esp.c revision 04580fdfa39e6e9d80ef4c60bfcf6e8461687692
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* esp - Emulex SCSI Processor host adapter driver with FAS101/236,
* tagged and non-tagged queuing support
*/
#define DEBUG 1
#define ESP_CHECK
#endif
/*
* these are non-ddi compliant:
*/
/*
* private
*/
/*
* External references
*/
extern uchar_t scsi_cdb_size[];
/*
* tunables
*/
#ifdef ESP_KSTATS
static int esp_do_kstats = 1;
static int esp_do_bus_kstats = 1;
#endif
#ifdef ESPDEBUG
static int espdebug = 0;
static int esp_no_sync_backoff = 0;
static int esp_test_stop;
#endif /* ESPDEBUG */
/*
* Local static data
* the global mutex protects some of these esp driver variables
*/
static kmutex_t esp_global_mutex;
static int esp_watchdog_running = 0;
static int esp_scsi_watchdog_tick; /* in sec */
static timeout_id_t esp_reset_watch;
static timeout_id_t esp_timeout_id = 0;
static int esp_timeout_initted = 0;
static int esp_n_esps = 0;
static void *esp_state;
static kmutex_t esp_log_mutex;
static char esp_log_buf[256];
/*
* linked list while being traversed (or updated).
*/
static krwlock_t esp_global_rwlock;
/*
* variables & prototypes for torture testing
*/
#ifdef ESP_TEST_RQSENSE
static int esp_test_rqsense;
#endif /* ESP_TEST_RQSENSE */
#ifdef ESP_TEST_PARITY
static int esp_ptest_emsgin;
static int esp_ptest_msgin;
static int esp_ptest_msg = -1;
static int esp_ptest_status;
static int esp_ptest_data_in;
#endif /* ESP_TEST_PARITY */
#ifdef ESP_TEST_ABORT
static int esp_atest;
static int esp_atest_disc;
static int esp_atest_reconn;
#endif /* ESP_TEST_ABORT */
#ifdef ESP_TEST_RESET
static int esp_rtest;
static int esp_rtest_type;
#endif /* ESP_TEST_RESET */
#ifdef ESP_TEST_TIMEOUT
static int esp_force_timeout;
#endif /* ESP_TEST_TIMEOUT */
#ifdef ESP_TEST_BUS_RESET
static int esp_btest;
#endif /* ESP_TEST_BUS_RESET */
#ifdef ESP_TEST_UNTAGGED
static int esp_test_untagged;
static int esp_enable_untagged;
static int esp_test_stop;
#endif /* ESP_TEST_UNTAGGED */
#ifdef ESP_PERF
/*
* these should really be protected but it is not really important
* to be very accurate
*/
static int esp_request_count;
static int esp_sample_time = 0;
static int esp_intr_count;
static int esp_ncmds;
static int esp_ndisc;
static int esp_ncmds_per_esp[MAX_ESPS];
#endif
/*
* function prototypes
*
* scsa functions are exported by means of the transport table
*/
int (*waitfunc)(void));
scsi_hba_tran_t *, struct scsi_device *);
/*
* internal functions
*/
static void esp_restart_cmd(void *);
static void esp_watch(void *);
/*PRINTFLIKE3*/
__KPRINTFLIKE(3);
/*PRINTFLIKE2*/
__KPRINTFLIKE(2);
int slot);
int slot);
int size);
int slot);
static void esp_destroy_callback_thread(struct esp *);
static void esp_start_watch_reset_delay(struct esp *);
static void esp_watch_reset_delay(void *arg);
int n, int what);
short *throttle);
short *throttle);
#ifdef ESP_CHECK
#else
#endif
/*
* esp DMA attr for all supported dma engines:
*/
static ddi_dma_attr_t dma1_espattr = {
DMA_ATTR_V0, (unsigned long long)0,
(unsigned long long)0xffffffff, (unsigned long long)((1<<24)-1),
(unsigned long long)0xffffffff, (unsigned long long)((1<<24)-1),
1, 512, 0
};
/*
* ESC1 esp dma attr
*/
static ddi_dma_attr_t esc1_espattr = {
DMA_ATTR_V0, (unsigned long long)0,
(unsigned long long)0xffffffff, (unsigned long long)((1<<24)-1),
(unsigned long long)0xffffffff, (unsigned long long)((1<<24)-1),
1, 512, 0
};
/*
* DMA2 esp dma attr
*/
static ddi_dma_attr_t dma2_espattr = {
DMA_ATTR_V0, (unsigned long long)0,
(unsigned long long)0xffffffff, (unsigned long long)((1<<24)-1),
(unsigned long long)0xffffffff, (unsigned long long)((1<<24)-1),
1, 512, 0
};
/*
* DMA3 esp dma attr
*/
static ddi_dma_attr_t dma3_espattr = {
DMA_ATTR_V0, (unsigned long long)0x0,
(unsigned long long)0xffffffff, (unsigned long long)((1<<24)-1),
(unsigned long long)0xffffffff, (unsigned long long)((1<<24)-1),
1, 512, 0
};
/*
* autoconfiguration routines.
*/
DEVO_REV, /* devo_rev, */
0, /* refcnt */
ddi_no_info, /* info */
nulldev, /* identify */
nulldev, /* probe */
esp_attach, /* attach */
esp_detach, /* detach */
nodev, /* reset */
NULL, /* cb ops */
NULL, /* bus operations */
ddi_power /* power */
};
char _depends_on[] = "misc/scsi";
&mod_driverops, /* Type of module. This one is a driver */
"ESP SCSI HBA Driver v%I%", /* Name of the module. */
&esp_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
};
int
_init(void)
{
int i;
/* CONSTCOND */
if (i != 0)
return (i);
if ((i = scsi_hba_init(&modlinkage)) != 0) {
return (i);
}
if ((i = mod_install(&modlinkage)) != 0) {
return (i);
}
return (i);
}
int
_fini(void)
{
int i;
/* CONSTCOND */
if ((i = mod_remove(&modlinkage)) == 0) {
}
return (i);
}
int
{
}
static int
int (*waitfunc)(void))
{
int rval = SCSIPROBE_FAILURE;
/*
* force renegotiation since Inquiry cmds do not cause
* check conditions
*/
/*
* the scsi-options precedence is:
* target-scsi-options highest
* device-type-scsi-options
* per bus scsi-options
* global scsi-options lowest
*/
if ((rval == SCSIPROBE_EXISTS) &&
int options;
if (options != -1) {
"?target%x-scsi-options = 0x%x\n", tgt,
if (options & SCSI_OPTIONS_FAST) {
} else {
}
}
}
IPRINTF2("target%x-scsi-options = 0x%x\n",
return (rval);
}
/*ARGSUSED*/
static int
{
}
static char *prop_cfreq = "clock-frequency";
/*ARGSUSED*/
static int
{
int instance, i;
char buf[64];
int mutex_initialized = 0;
int add_intr_done = 0;
int bound_handle = 0;
char *prop_template = "target%d-scsi-options";
char prop_str[32];
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
case DDI_PM_RESUME:
return (DDI_FAILURE);
if (!esp) {
return (DDI_FAILURE);
}
/*
* Reset hardware and softc to "no outstanding commands"
* Note that a check condition can result on first command
* to a target.
*/
(void) esp_reset_bus(esp);
/*
* esp_watchdog_running was reset at checkpoint time,
* enable it at resume time
*/
esp_watchdog_running = 1;
esp->e_suspended = 0;
if (esp_timeout_id == 0) {
esp_timeout_initted = 1;
}
/* make sure that things get started */
(void) esp_istart(esp);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/*
* Since we know that some instantiations of this device can
* be plugged into slave-only SBus slots, check to see whether
* this is one such.
*/
"esp%d: device in slave-only slot", instance);
return (DDI_FAILURE);
}
if (ddi_intr_hilevel(dip, 0)) {
/*
* Interrupt number '0' is a high-level interrupt.
* At this point you either add a special interrupt
* handler that triggers a soft interrupt at a lower level,
* or - more simply and appropriately here - you just
* fail the attach.
*/
"esp%d: Device is using a hilevel intr", instance);
return (DDI_FAILURE);
}
/*
* Allocate softc information.
*/
"esp%d: cannot allocate soft state", instance);
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* map in device registers
*/
goto exit;
}
"esp%d: cannot find dma controller", instance);
goto unmap;
}
/*
* Initialize state of DMA gate array.
* Must clear DMAGA_RESET on the ESC before accessing the esp.
*/
case DMA_REV2:
break;
case ESC1_REV1:
break;
case DMA_REV3:
break;
case DMA_REV1:
default:
break;
}
"esp%d: cannot alloc dma handle", instance);
goto fail;
}
"esp%d: cannot alloc cmd area", instance);
goto fail;
}
"esp%d: cannot bind cmdarea", instance);
goto fail;
}
bound_handle++;
/*
* Allocate a transport structure
*/
/*
* the ESC has a rerun bug and the workaround is
* to round up the ESC count; rather than
* doing this on each xfer we do it once here
* for the cmd area read xfers
*/
}
/*
* By default we assume embedded devices and save time
* checking for timeouts in esp_watch() by skipping the rest of luns
* If we're talking to any non-embedded devices, we can't cheat
* and skip over non-zero luns anymore in esp_watch().
*/
#ifdef ESPDEBUG
/*
* Initialize last state log.
*/
for (i = 0; i < NPHASE; i++) {
}
esp->e_phase_index = 0;
#endif /* ESPDEBUG */
/*
* Initialize throttles.
*/
/*
* initialize transport structure
*/
/* XXX need tran_quiesce and tran_unquiesce for hotplugging */
if (i == -1) {
"scsi-initiator-id", -1);
}
if (i != DEFAULT_HOSTID && i >= 0 && i < NTARGETS) {
}
for (i = 0; i < NTARGETS; i++) {
esp->e_qfull_retry_interval[i] =
}
IPRINTF1("esp_dma_attr burstsize=%x\n",
/*
* Attach this instance of the hba
*/
DDI_SUCCESS) {
goto fail;
}
/*
* if scsi-options property exists, use it;
* otherwise use the global variable
*/
"scsi-options", SCSI_OPTIONS_DR);
/* we don't support wide */
}
}
/*
* if scsi-selection-timeout property exists, use it
*/
#ifdef ESPDEBUG
espdebug = 1;
}
#endif
/*
* if target<n>-scsi-options property exists, use it;
* otherwise use the e_scsi_options
*/
for (i = 0; i < NTARGETS; i++) {
"?target%d_scsi_options=0x%x\n",
i, esp->e_target_scsi_options[i]);
} else {
}
"Disabled TQ since disconnects are disabled\n");
}
}
IPRINTF2("esp tag age limit=%d, global=%d\n",
}
"scsi-reset-delay", scsi_reset_delay);
IPRINTF2("esp scsi_reset_delay=%x, global=%x\n",
if (esp->e_scsi_reset_delay == 0) {
"scsi_reset_delay of 0 is not recommended,"
" resetting to SCSI_DEFAULT_RESET_DELAY\n");
}
}
esp->e_force_async = 0;
/*
* disable tagged queuing for all targets
* (will be enabled by target driver if necessary)
*/
/*
* get iblock cookie and initialize mutexes
*/
!= DDI_SUCCESS) {
goto fail;
}
/*
* initialize mutex for startQ
*/
/*
* add this esp to the linked list of esp's
*/
} else {
}
/*
* kstat_intr support
*/
if (esp->e_intr_kstat)
goto fail;
}
/*
* now we are ready to take the reset interrupt
*/
/*
* start off one watchdog for all esp's now we are fully initialized
*/
"scsi-watchdog-tick", scsi_watchdog_tick);
if (esp_scsi_watchdog_tick != scsi_watchdog_tick) {
}
esp_scsi_watchdog_tick * 1000000);
IPRINTF2("esp scsi watchdog tick=%x, esp_tick=%lx\n",
if (esp_timeout_id == 0) {
esp_timeout_initted = 1;
}
}
/*
* Initialize power management bookkeeping; components are
* created idle
*/
/*
* Since as of now, there is no power management done in
* scsi-HBA drivers, there is no need to create a pm_component.
* BUT esp is a special case with GYPSY. In gypsy, the
* PM_SUSPEND/PM_RESUME is used. So, the following few lines
* of code will be there until Gypsy machines are supported.
*/
} else {
goto fail;
}
#ifdef ESP_KSTATS
/*
* kstats to measure scsi bus busy time
*/
if (esp_do_bus_kstats) {
KSTAT_FLAG_PERSISTENT)) != NULL) {
}
}
#endif /* ESP_KSTATS */
/*
* create a possibly shared callback thread which will empty the
* callback queue
*/
/*
* create kmem cache for packets
*/
sizeof (struct esp_cmd), 8,
goto fail;
}
return (DDI_SUCCESS);
fail:
if (esp) {
/* remove this esp from the linked list */
} else {
}
}
break;
}
}
if (mutex_initialized) {
}
if (esp->e_intr_kstat) {
}
if (add_intr_done) {
}
if (tran) {
}
if (esp->e_kmem_cache) {
}
if (bound_handle) {
}
}
if (esp->e_dmahandle) {
}
}
if (dmar)
if (esp->e_regs_acc_handle)
exit:
if (esp) {
}
return (DDI_FAILURE);
}
/*ARGSUSED*/
static int
{
switch (cmd) {
case DDI_DETACH:
return (esp_dr_detach(dip));
case DDI_SUSPEND:
case DDI_PM_SUSPEND:
return (DDI_FAILURE);
if (!esp) {
return (DDI_FAILURE);
}
esp_watchdog_running = 0;
(void) esp_reset_bus(esp);
}
/*
* In the current implementation of esp power management, the
* SCSI active terminators are turned off and so the bus
* signals can wander everywhere - including generating false
* interrupts, so they need to be disabled. This should also
* be done for a full SUSPEND in theory, but since CPR writes
* out the state file....
*/
if (cmd == DDI_PM_SUSPEND) {
}
if (esp->e_restart_cmd_timeid) {
esp->e_restart_cmd_timeid = 0;
}
/* Last esp? */
if (!nesp->e_suspended) {
return (DDI_SUCCESS);
}
}
if (esp_timeout_initted) {
esp_timeout_initted = 0;
esp_timeout_id = 0; /* don't resched */
}
if (esp_reset_watch) {
(void) untimeout(esp_reset_watch);
esp_reset_watch = 0;
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/* NOTREACHED */
}
static int
{
int i, j;
return (DDI_FAILURE);
if (!esp) {
return (DDI_FAILURE);
}
/*
* Force interrupts OFF
*/
#ifdef ESP_KSTATS
/*
* Remove kstats if any i.e., if pointer non-NULL.
* Note: pointer NOT explicitly NULL'ed. But buffer zalloc'd
*/
}
#endif /* ESP_KSTATS */
/*
* deallocate reset notify callback list
*/
/*
* Remove device instance from the global linked list
*/
} else {
break;
}
}
/*
* Instance not in softc list. Since the
* instance is not there in softc list, don't
* enable interrupts, the instance is effectively
* unusable.
*/
" in softc list!");
return (DDI_FAILURE);
}
}
esp_tail = e;
if (esp->e_intr_kstat)
/*
* disallow timeout thread rescheduling
*/
/*
* last esp? ... if active, CANCEL watch threads.
*/
if (esp_timeout_initted) {
esp_timeout_initted = 0;
esp_timeout_id = 0; /* don't resched */
}
if (esp_reset_watch) {
(void) untimeout(esp_reset_watch);
esp_reset_watch = 0;
}
}
if (esp->e_restart_cmd_timeid) {
esp->e_restart_cmd_timeid = 0;
}
/*
* destroy outstanding ARQ pkts
*/
for (i = 0; i < NTARGETS; i++) {
for (j = 0; j < NLUNS_PER_TARGET; j++) {
int slot = i * NLUNS_PER_TARGET | j;
struct scsi_address sa;
}
}
}
/*
* destroy any outstanding tagged command info
*/
for (i = 0; i < N_SLOTS; i++) {
if (active) {
for (j = 0; j < NTAGS; j++) {
if (sp) {
if (pkt) {
}
/* sp freed in esp_scsi_destroy_pkt */
}
}
}
}
/*
* Remove device MT locks
*/
/*
* Release miscellaneous device resources
*/
if (esp->e_kmem_cache) {
}
}
/*
* Process shared callback resources, as required.
* Update callback_thread bookkeeping.
*/
/*
* Remove properties created during attach()
*/
/*
* Delete the DMA limits, transport vectors and remove the device
* links to the scsi_transport layer.
* -- ddi_set_driver_private(dip, NULL)
*/
(void) scsi_hba_detach(dev);
/*
* Free the scsi_transport structure for this device.
*/
return (DDI_SUCCESS);
}
/*
* Hardware and Software internal reset routines
*/
static void
{
int i;
/*
* Determine clock frequency of attached ESP chip.
*/
/*
* Valid clock freqs. are between 10 and 40 MHz. Otherwise
* presume 20 MHz. and complain. (Notice, that we wrap to
* zero at 40 MHz. Ick!) This test should NEVER fail!
*
* freq (MHz) clock conversion factor
* 10 2
* 10.01-15 3
* 15.01-20 4
* 20.01-25 5
* 25.01-30 6
* 30.01-35 7
* 35.01-40 8 (0)
*/
if (i > FIVE_MEG) {
} else {
clock_conv = 0;
}
"Bad clock frequency- setting 20mhz, asynchronous mode");
i = TWENTY_MEG;
}
IPRINTF5("%d mhz, clock_conv %d, clock_cycle %d, ticks %ld, stval %d\n",
for (i = 0; i < NTARGETS; i++) {
esp->e_espconf3[i] = 0;
}
if (clock_conv > CLOCK_25MHZ) {
/*
* do not enable FENABLE when using
* stacked cmds
* esp->e_espconf2 |= ESP_CONF2_FENABLE;
*/
IPRINTF("found FAST\n");
} else {
}
} else {
}
} else {
}
for (i = 0; i < NTARGETS; i++) {
} else {
esp->e_default_period[i] =
}
}
/*
* Avoid resetting the scsi bus since this causes a few seconds
* delay per esp in boot and also causes busy conditions in some
* tape devices.
* we assume that with FAS devices, we probably have OBP 2.0 or
* higher which resets the bus before booting.
* worst case, we hang during the first probe and reset then
*/
} else {
}
}
static void
{
int i;
for (i = 0; i < 1000; i++) {
break;
}
drv_usecwait(1);
}
if (i >= 1000) {
}
}
}
static void
{
if (reset_action & ESP_RESET_HW) {
}
if (reset_action & ESP_RESET_SOFTC) {
/*
* esp->e_weak && esp->e_nodisc && ncmds && ndiscs are
* preserved across softc resets.
*/
}
}
static void
{
int sbus_reruns;
/*
* never reset the dmaga while a request pending; this
* may cause a hang in xbox if there was a rerun pending
*/
if (action & ESP_RESET_SCSIBUS) {
if (esp_watchdog_running && !panicstr) {
int i;
for (i = 0; i < NTARGETS; i++) {
esp->e_reset_delay[i] =
}
} else {
}
}
if (action & ESP_RESET_DMA) {
switch (ESP_DMAGA_REV(esp)) {
case ESC1_REV1:
"reruns");
if (sbus_reruns) {
}
if (!(burstsizes & BURST32)) {
IPRINTF("16 byte burstsize\n");
}
break;
case DMA_REV2:
break;
case DMA_REV3:
if (burstsizes & BURST32) {
IPRINTF("32 byte burstsize\n");
}
break;
default:
break;
}
}
if (action & ESP_RESET_ESP) {
/*
* according to Emulex, 2 NOPs with DMA are required here
* (essential for FAS101; id_code is unreliable if we don't
* do this)
*/
/*
* Re-load chip configurations
*/
ep->esp_sync_period = 0;
ep->esp_sync_offset = 0;
/*
* enable default configurations
*/
fcode =
(uchar_t)3;
if (fcode == ESP_FAS236) {
} else {
}
IPRINTF2("Family code %d, revision %d\n",
}
case FAS236:
/*
* used on DSBE, FSBE, galaxies
*/
IPRINTF("type is FAS236\n");
for (i = 0; i < NTARGETS; i++) {
}
/*
* check if differential scsi bus; if so then no
*/
DDI_PROP_DONTPASS, "differential", 0)) {
IPRINTF("differential scsibus\n");
esp->e_req_ack_delay = 0;
} else {
}
> (uchar_t)2) {
IPRINTF1("FAS236 rev=%x Stack_cmds DISABLED\n",
| ESP_OPT_FAS;
} else {
IPRINTF1("FAS236 rev=%x Stack_cmds ENABLED\n",
}
break;
case FAS100A:
/*
* used on all desktop sun4m machines (macio)
*/
IPRINTF("type is FAS100A or 101A\n");
for (i = 0; i < NTARGETS; i++) {
}
break;
case ESP236:
/*
* used on galaxies, SBE
*/
IPRINTF("type is ESP236\n");
break;
case ESP100A:
/*
* used on SS2, IPX, sport8
*/
IPRINTF("type is ESP100A\n");
break;
case ESP100:
/*
* used on SS1, SS1+, IPC
*/
IPRINTF("type is ESP100\n");
IPRINTF("disable sync mode\n");
break;
default:
IPRINTF("type is ???\n");
break;
}
/*
* look up esp-options property
*/
/*
* Just in case...
* clear interrupt
*/
IPRINTF2("conf2 = %x (%x)\n",
}
EPRINTF4("conf3 (for target 0 - 3) = %x %x %x %x\n",
EPRINTF3("conf3 (for target 4 - 6) = %x %x %x\n",
EPRINTF2("req_ack_delay (0x%p) = %x\n",
(void *)&esp->e_req_ack_delay,
}
}
#ifdef lint
#endif /* lint */
}
/*
* create a thread that performs the callbacks and init associated cv and mutex
*
* callback tunables:
*/
static int esp_n_esps_per_callback_thread = 4;
static int esp_cb_load_count = 25;
static int esp_n_callback_threads = 0;
static struct callback_info *last_esp_callback_info;
static void
{
if ((esp_n_esps++ % esp_n_esps_per_callback_thread) == 0) {
kthread_t *t;
struct callback_info *cb_info;
/*
* create another thread
*/
if (last_esp_callback_info) {
}
} else {
IPRINTF1("sharing callback thread %d\n",
}
}
static void
{
struct esp *e;
/*
* Remove callback
*
* We have to see if we are the last one using this cb thread
* before deleting it.
* Check the list for others referencing this cb. We are off
* the list, so finding one other reference indicates shared.
*/
break;
}
}
/*
* we couldn't find another esp sharing this cb
*/
if (!e) {
IPRINTF2("esp_destroy_callback_thread: "
"killing callback 0x%p thread %d\n",
IPRINTF2("esp_destroy_callback_thread: spawned %d max %d\n",
IPRINTF1("esp_destroy_callback_thread:%p wakeup\n",
(void *)ci);
}
IPRINTF("esp_destroy_callback_thread: all threads killed\n");
for (pci = &last_esp_callback_info;
/* take it out of list */
/* destroy it */
IPRINTF1("esp_destroy_callback_thread:%p freed\n",
(void *)ci);
break;
}
}
} else {
IPRINTF1("esp_destroy_callback_thread: callback 0x%p shared\n",
(void *)esp->e_callback_info);
}
esp_n_esps--;
}
/*
* this is the function executed by the callback thread; it
* empties the callback queue by calling the completion function of each
* packet; note the release of the mutex before
* calling the completion function
* the cv_wait is at the end of the loop because by the time this thread
* comes alive, there is already work to do.
*/
void
{
/*
* callback now?
*/
}
}
/*
* if the queue is too long then do
* a wakeup for *all* callback threads
*/
} else if (cb_info->c_signal_needed) {
}
}
cb_info->c_signal_needed = 0;
}
/*
* Warlock has a problem when we use different locks
* on the same type of structure in different contexts.
* We use callb_cpr_t in both scsi_watch and esp_callback threads.
* we use different mutex's in different threads. And
* this is not acceptable to warlock. To avoid this
* problem we use the same name for the mutex in
* both scsi_watch & esp_callback. when __lock_lint is not defined
* esp_callback uses the mutex on the stack and in scsi_watch
* a static variable. But when __lock_lint is defined
* we make a mutex which is global in esp_callback and
* a external mutex for scsi_watch.
*/
#ifdef __lock_lint
#endif
static void
{
int serviced = 0;
int wakeups = 0;
int hiload = 0;
int loload = 0;
#ifndef __lock_lint
#endif
int n = 0;
#ifdef ESP_PERF
#endif
for (;;) {
"esp_callback_start");
}
serviced++;
n++;
}
/*
* check load
* if the load is consistently too high, create another
* thread to help out
* if the load is consistently too low, exit thread
* If the load is so high that we never exit the
* above while loop then esp_n_esps_per_callback_thread is
* too high; we are not going to deal with that condition
* here
*/
if (wakeups) {
} else {
load = 0;
}
EPRINTF2("esp_callback: thread %d 0x%p exit set\n",
break;
} else if (load > esp_hi_cb_load) {
/*
* load is too high
*/
if ((hiload++ > esp_cb_load_count) &&
/*
* create another thread
*/
/*
* from now on do not allow immediate
* callback
*/
cb_info->c_cb_now_qlen = 0;
}
} else if (load < esp_lo_cb_load) {
/*
* load is too low
*/
if (loload++ > esp_cb_load_count) {
/*
* if this is not the first thread, exit
*/
if (id != 0) {
/*
* exit while loop and esp_callback
* function which destroys the
* thread
*/
break;
} else {
/*
* if only 1 thread left then set
* back cb_now_qlen
*/
}
}
}
} else {
/*
* always use deferred callback from now on
*/
cb_info->c_cb_now_qlen = 0;
}
"esp_callback_end: (%d)", serviced);
/*
* reset serviced and wakeups; if these numbers get too high
* then we don't adjust to bursts very well
*/
if (serviced >= 20000) {
#ifdef ESP_PERF
"esp cb%d.%d: svced=%d, wkup=%d, ld=%d, spwn=%d, now_qlen=%d\n",
#endif
serviced = 0;
wakeups = 0;
}
cb_info->c_signal_needed = 0;
wakeups++;
}
#ifdef ESP_PERF
#endif
"esp_callback_end: (%d)", n);
#ifndef __lock_lint
#endif
thread_exit();
}
/*
* Interface functions
*
* Visible to the external world via the transport structure.
*
* These functions have been grouped together to reduce cache misses.
*
*/
/*ARGSUSED*/
static void
{
"esp_scsi_dmafree_start");
/*
* Free the mapping.
*/
}
"esp_scsi_dmafree_end");
}
/*ARGSUSED*/
static void
{
int i;
if (i != DDI_SUCCESS) {
}
}
}
static struct scsi_pkt *
{
int kf;
int failure = 0;
int rval;
/* #define ESP_TEST_EXTRN_ALLOC */
#ifdef ESP_TEST_EXTRN_ALLOC
#endif
/*
* If we've already allocated a pkt once,
* this request is for dma allocation only.
*/
/*
* First step of esp_scsi_init_pkt: pkt allocation
*/
"esp_scsi_pktalloc_start");
failure = 0;
if (cmd) {
} else {
failure++;
}
(tgtlen > PKT_PRIV_LEN) ||
(statuslen > EXTCMDS_STATUS_SIZE)) {
if (failure == 0) {
}
if (failure) {
"esp_scsi_pktalloc_end");
return (NULL);
}
}
"esp_scsi_pktalloc_end");
} else {
}
/*
* Second step of esp_scsi_init_pkt: dma allocation
* Set up dma info
*/
"esp_scsi_dmaget_start");
cmd_flags &= ~CFLAG_DMASEND;
} else {
}
if (flags & PKT_CONSISTENT) {
}
switch (rval) {
case DDI_DMA_NORESOURCES:
break;
case DDI_DMA_BADATTR:
case DDI_DMA_NOMAPPING:
break;
case DDI_DMA_TOOBIG:
default:
break;
}
if (new_cmd) {
}
"esp_scsi_dmaget_end");
}
"esp_scsi_dmaget_end");
}
}
/*
* allocate and deallocate external space (ie. not part of esp_cmd) for
* non-standard length cdb, pkt_private, status areas
*/
/* ARGSUSED */
static int
{
int failure = 0;
failure++;
} else {
}
}
if (tgtlen > PKT_PRIV_LEN) {
failure++;
} else {
}
}
if (statuslen > EXTCMDS_STATUS_SIZE) {
failure++;
} else {
}
}
if (failure) {
}
return (failure);
}
/* ARGSUSED */
static void
{
/*
* esp_scsi_dmafree inline to speed things up
*/
"esp_scsi_dmafree_start");
/*
* Free the mapping.
*/
}
"esp_scsi_dmafree_end");
"esp_scsi_pktfree_start");
/*
* first test the most common case
*/
CFLAG_SCBEXTERN)) == 0) {
} else {
}
"esp_scsi_pktfree_end");
}
/* ARGSUSED */
static void
{
panic("esp_pkt_destroy(_extern): freeing free packet");
/*NOTREACHED*/
}
}
}
}
}
/*
* kmem cache constructor and destructor.
* When constructing, we bzero the cmd and allocate a handle
* When destructing, just free the dma handle
*/
static int
{
&cmd->cmd_dmahandle) != 0) {
return (-1);
}
return (0);
}
/* ARGSUSED */
static void
{
if (cmd->cmd_dmahandle) {
}
}
/*
* esp_prepare_pkt():
* initialize the packet and do some sanity checks
* before taking the lock
*/
static int
{
#ifdef ESPDEBUG
switch (ESP_DMAGA_REV(esp)) {
default:
case DMA_REV1:
case DMA_REV2:
case ESC1_REV1:
break;
case DMA_REV3:
break;
}
IPRINTF("prepare pkt: dma count too high\n");
return (TRAN_BADPKT);
}
}
#endif
/*
* Reinitialize some fields that need it; the packet may
* have been resubmitted
*/
/*
* Copy the cdb and scb pointers to the esp_cmd area as we
* modify these parameters.
*/
}
/*
* if the pkt was resubmitted then the
* window may be at the wrong number
*/
if (sp->cmd_cur_win) {
sp->cmd_cur_win = 0;
IPRINTF("cannot reset window\n");
return (TRAN_BADPKT);
}
}
/*
* the common case is just one window, we worry
* about multiple windows when we run out of the
* current window
*/
(CFLAG_CMDIOPB | CFLAG_DMASEND)) {
}
}
/*
* The ESP chip only will automatically send 6, 10 or 12 byte
* cdb's. Setting cmd_cdblen to a non-zero value signals this.
* Otherwise, we have to do it manually and send them out one at
* a time. Setting cmd_cdblen to zero signals this condition.
* For non-group{0,1,2,5} cmds we use the cmdlen specified by
* the target driver if it is 6, 10, or 12.
*/
sp->cmd_cdblen = 0;
IPRINTF("cdblen = 0\n");
} else if (size != 0) {
}
#ifdef ESP_TEST_UNTAGGED
#ifndef __lock_lint
if (esp_test_untagged > 0) {
"starting untagged cmd, target=%d,"
" tcmds=%d, sp=0x%p, throttle=%d\n",
esp_test_untagged = -10;
}
}
#endif
#endif
#ifdef ESPDEBUG
IPRINTF2("tagged packet for non-tagged target %d.%d\n",
}
/*
* the scsa spec states that it is an error to have no
* completion function when FLAG_NOINTR is not set
*/
IPRINTF("intr packet with pkt_comp == 0\n");
"esp_prepare_pkt_end (tran_badpkt)");
return (TRAN_BADPKT);
}
#endif /* ESPDEBUG */
/*
* no need to reset tag bits since tag queuing will
* not be enabled if disconnects are disabled
*/
}
"esp_prepare_pkt_end (tran_accept)");
return (TRAN_ACCEPT);
}
/*
* when the startQ is emptied, we cannot tolerate TRAN_BUSY.
* if the queue is not empty when the next request comes in esp_start
* the order of requests is not preserved
* if a transport busy condition occurs, we queue up startQ pkts in the ready
* queue; the disadvantage is that the target driver has initially
* the wrong value (too high) for the target queue but eventually
* it should get it right; there is not really a big performance hit here
*/
static void
{
int rval;
"esp_empty_startQ_start");
}
/*
* the request should have been accepted but if not,
* put it back on the head of startQ
* If the packet was rejected for other reasons then
* complete it here
*/
if (rval != TRAN_ACCEPT) {
}
}
continue;
}
} else {
}
break;
}
}
"esp_empty_startQ_end");
}
/*
* emptying the startQ just before releasing ESP_MUTEX is
* tricky; there is a small window where we checked the
* startQ and emptied it but possibly due to a
* a kernel preemption, we don't release the ESP_MUTEX soon enough and
* esp_start() will not be able to get the ESP_MUTEX and exit
* The next cmd coming in or the next interrupt or esp_watch() would eventually
* empty the startQ, though
* Therefore, by releasing the ESP_MUTEX before releasing the startQ mutex,
* we prevent that esp_start() fills the startQ and then cannot get the
* ESP_MUTEX for emptying the startQ
*
* esp_start() - accept a esp_cmd
*/
static int
{
int rval;
#ifdef ESP_PERF
#endif
#ifdef ESP_CHECK
#endif
/*
* prepare packet before taking the mutex
*/
if (rval != TRAN_ACCEPT) {
"esp_start_end (prepare_pkt)");
return (rval);
}
/*
* esp mutex can be held for a long time; therefore, if mutex is
* held, we queue the packet in a startQ; we now need to check
* the startQ on every mutex_exit(ESP_MUTEX);
* Don't put NOINTR cmds in startQ! Proxy cmds go directly
* to _esp_start
*/
} else {
} else {
}
/*
* check again the ESP_MUTEX
*/
if (mutex_tryenter(ESP_MUTEX)) {
}
goto done;
}
}
done:
(void *)esp);
return (rval);
}
/*
* _esp_start()
* the flag argument is to force _esp_start to accept the pkt; pkts that were
* on startQ cannot be bounced back with TRAN_BUSY
*/
static int
{
short slot;
int rval = TRAN_ACCEPT;
if (lun) {
EPRINTF("_esp_start: switching target and lun slot scan\n");
}
/*
* prepare (init) packet if this hasn't been done yet and do some checks
*/
if (rval != TRAN_ACCEPT) {
#ifdef ESPDEBUG
#endif
goto done;
}
}
/*
* At this point we are not going to reject the packet.
* we let proxy packets go thru because these packets don't call a
* target driver completion routine
*/
#ifdef ESP_KSTATS
/*
* create kstats if not done already
*/
if (esp_do_kstats) {
/*
* don't create e_slot_stats if this is an NOINTR cmd; this
* may be just a probing
*/
char buf[32];
KSTAT_TYPE_IO, 1,
KSTAT_FLAG_PERSISTENT)) != NULL) {
}
}
}
}
#endif /* ESP_KSTATS */
#ifdef ESP_PERF
#endif
/*
* we accepted the command; increment the count
* (we may reject later if TRAN_BUSY!; we test this later because
* we don't want to incur the extra overhead here)
*/
/*
* if it is a nointr packet, start it now
* (NO_INTR pkts are not queued in the startQ)
*/
EPRINTF("starting a nointr cmd\n");
#ifdef ESPDEBUG
#endif
goto done;
}
/*
* accept the command:
* If no ready que and free slot, run cmd immediately.
* If FLAG_HEAD mode set, run cmd as soon as free slot
* available. if first cmd in ready Q is request sense then insert
* after this cmd (there shouldn't be more than one request sense).
* Queue up the command in the ready queue if this queue is non-empty
* or if we had a queue full condition
*/
EPRINTF("que head\n");
} else {
}
}
(flag == TRAN_BUSY_OK)) {
"transport busy, slot=%x, ncmds=%x\n",
#ifdef ESPDEBUG
#endif
#ifdef ESP_PERF
#endif
goto done;
} else {
EPRINTF("que tail\n");
}
}
/*
* just in case that the bus is free and we haven't
* been able to restart for some reason
* XXX this shouldn't really be necessary
*/
}
} else {
/*
* for tagged targets with no cmds outstanding and currently
* draining, reset throttle now
* for non-tagged targets and currently draining, always reset
* throttle now (t_cmds is always zero for non-tagged)
*/
DRAIN_THROTTLE)) {
IPRINTF("reset throttle\n");
}
EPRINTF("start cmd (maybe)\n");
} else {
"cmd not started: e_slot=0x%p, throttle=%x\n",
}
}
done:
return (rval);
}
static char esp_tag_lookup[] =
static int
{
int rval = 0;
"esp_alloc_tag_start");
/*
* allocate tag
* Optimize for the common case, ie. success
*/
/* Validate tag, should never fail. */
/*
* Store assigned tag and tag queue type.
* Note, in case
* of multiple choice, default to simple queue.
*/
done:
"esp_alloc_tag_end");
return (rval);
} else {
int age, i;
/*
* Check tag age. If timeouts enabled and
* tag age greater than 1, print warning msg.
* If timeouts enabled and tag age greater than
* age limit, begin draining tag que to check for
* lost tag cmd.
*/
IPRINTF("draining tag queue\n");
}
}
/* If tag in use, scan until a free one is found. */
for (i = 0; i < NTAGS; i++) {
break;
}
}
/* If no free tags, we're in serious trouble. */
"slot %x: All tags in use!!!\n", slot);
rval = -1;
goto done;
}
goto alloc;
}
} else {
EPRINTF2("Target %d.%d allocating tag que\n",
/*
* Couldn't get space for tagged que. Complain
* and disable tagged queuing. It beats
* dying...Seriously, this should not
* happen.
*/
"Target %d.%d cannot alloc tag queue\n",
goto done;
}
goto alloc;
}
/* NOTREACHED */
}
/*
* Internal Search Routine.
*
* Search for a command to start.
*/
static int
{
"esp_istart_start");
EPRINTF("esp_istart:\n");
}
"esp_istart_end");
return (ACTION_RETURN);
}
static int
{
short slot;
switch (flag) {
case NEW_CMD:
{
int found = 0;
#ifdef ESPDEBUG
if (dslot == NLUNS_PER_TARGET) {
}
#endif /* ESPDEBUG */
/*
* check each std slot; if it is empty (ie. target not currently
* connected), then check the ready queue for packets
*/
do {
}
found++;
} else {
}
if (!found) {
EPRINTF("esp_ustart: no cmds to start\n");
"esp_ustart_end (not_found)");
return (FALSE);
}
break;
}
case SAME_CMD:
break;
default:
"esp_ustart_end (default)");
return (FALSE);
}
}
/*
* Start a command off
*/
#ifdef ESPDEBUG
static int esp_cmd_len;
#endif
static int
{
EPRINTF2("esp_startcmd: sp=0x%p flags=%x\n",
#ifdef ESPDEBUG
debug_enter("esp_startcmd");
}
#endif
/*
* if a non-tagged cmd is submitted to an active tagged target
* then drain before submitting this cmd; SCSI-2 allows RQSENSE
* to be untagged
*/
IPRINTF("untagged cmd, start draining\n");
}
}
}
return (FALSE);
}
/*
* The only reason that this should happen
* is if we have a re-selection attempt starting.
*/
if (INTPENDING(esp)) {
int slot;
"esp_startcmd_preempt_call");
/*
* put request back in the ready queue
* runpoll will retry NOINTR cmds so no need to put
* those on ready Q
*/
}
}
"esp_startcmd_end (re_selection)");
return (FALSE);
}
/*
* allocate a tag; if no tag available then put request back
* on the ready queue and return; eventually a cmd completes and we
* get going again
*/
int slot;
}
"esp_startcmd_end (alloc_tag2)");
return (FALSE);
}
} else {
}
}
}
#ifdef ESPDEBUG
#endif /* ESPDEBUG */
/*
* The ESP chip will only automatically
* send 6, 10 or 12 byte SCSI cmds.
* NOTE: if cmd_len is 0, we xfer cmd bytes one at the time
* Also note that the "SELECT with ATN and STOP" stops with ATN
* asserted; if no msg is available, we send a NOP. Some targets
* may not like this.
*/
#ifdef ESPDEBUG
if (esp_cmd_len)
cmd_len = 0;
#endif
EPRINTF("tag cmd\n");
if (cmd_len) {
} else {
EPRINTF2("tag %d, omsglen=%x\n",
cmd_len = 0;
}
IPRINTF2("proxy cmd, len=%x, msg=%x\n",
/*
* This is a proxy command. It will have
* a message to send as part of post-selection
* (e.g, MSG_ABORT or MSG_DEVICE_RESET)
* XXX: We should check to make sure that
* this is a valid PROXY command, i.e,
* a valid message length.
*/
esp->e_cur_msgout[i] =
}
cmd_len = 0;
} else {
}
/*
* Set up to send synch. negotiating message. This is getting
* a bit tricky as we dma out the identify message and
* send the other messages via the fifo buffer.
*/
/* First the tag message bytes */
i = 0;
}
} else {
int offset = DEFAULT_OFFSET;
offset = 0;
}
offset = 0;
}
(int)offset);
cmd_len = 0;
}
/*
* XXX: Set sync known here because the Sony CDrom
* ignores the synch negotiation msg. Net effect
* is we negotiate on every I/O request forever.
*/
} else {
} else {
}
/* Send cmd. */
if (cmd_len) {
EPRINTF("std. cmd\n");
/*
* XXX: Things get a bit complicated for cdb's the esp
* chip doesn't understand. We have to send them out
* one byte at a time. This is not a fast process!
*/
} else {
IPRINTF("sending special cmd\n");
}
}
/*
* Now load cdb (if any)
*/
for (i = 0; i < cmd_len; i++) {
}
if (cmd_len) {
nstate);
}
/*
* calculate total dma amount:
*/
/*
* load rest of chip registers, if needed
*/
}
}
#ifdef ESPDEBUG
if (DEBUGGING) {
auto char buf[256];
buf[0] = '\0';
for (i = 0; i < (int)sp->cmd_cdblen; i++) {
}
}
#endif /* ESPDEBUG */
/*
* set up timeout here; there is a risk of preemption in which
* case we don't adjust the timeout. So, we hope that this
* cmd gets started fairly quickly after a preemption.
*/
if (i == 0) {
EPRINTF("dup timeout\n");
} else if (i > 0) {
EPRINTF("new timeout\n");
}
}
#ifdef ESP_KSTATS
}
#endif /* ESP_KSTATS */
return (TRUE);
}
/*
* Autovector Interrupt Entry Point.
*
*/
static uint_t
{
int kstat_updated = 0;
int rval = DDI_INTR_UNCLAIMED;
#ifdef ESP_PERF
#endif
do {
while (INTPENDING(esp)) {
}
if (esp->e_polled_intr) {
esp->e_polled_intr = 0;
}
rval == DDI_INTR_CLAIMED) {
}
/*
* check and empty the startQ
*/
} while (INTPENDING(esp));
return (rval);
}
/*
* General interrupt service routine.
*/
static char *dmaga_bits = DMAGA_BITS;
static void
{
};
int action;
int i = 0;
/*
* A read of ESP interrupt register clears interrupt,
* so any other volatile information needs to be latched
* up prior to reading the interrupt register.
*/
/*
* unclear what could cause a gross error;
* most of the time we get a data overrun after this.
*/
IPRINTF5("esp_cmd=%x, stat=%x, intr=%x, step=%x, fifoflag=%x\n",
ep->esp_fifo_flag);
}
} else {
goto start_action;
}
}
#ifdef ESPDEBUG
if (esp_check_dma_error(esp)) {
goto start_action;
}
#endif
/*
* the esp may post an interrupt even though we have just reset
* the bus and blown away the targets; therefore, check on
* reset state first and deal with reset recovery immediately
*/
goto start_action;
}
/*
* While some documentation claims that the
* ESP100A's msb in the stat register is an
* INTERRUPT PENDING bit, an errata sheet
* warned that you shouldn't depend on that
* being so (unless you're an ESP-236)
*/
goto start_action;
}
if (esp_check_dma_error(esp)) {
goto start_action;
}
goto exit;
}
/*
* now it is finally safe to read the interrupt register
*/
#ifdef ESPDEBUG
if (DEBUGGING) {
}
#endif /* ESPDEBUG */
/*
* Based upon the current state of the host adapter driver
* we should be able to figure out what to do with an interrupt.
* We have several possible interrupt sources, some of them
* modified by various status conditions.
*
* Basically, we'll get an interrupt through the dma gate array
* for one or more of the following three conditions:
*
* 1. The ESP is asserting an interrupt request.
*
* 2. There has been a memory exception of some kind.
*
* In the latter case we are either in one of the SCSI
* DATA phases or are using dma in sending a command to a
* target. We will let the various handlers for these kind
* of states decode any error conditions in the gate array.
*
* The ESP asserts an interrupt with one or more of 8 possible
* bits set in its interrupt register. These conditions are
* SCSI bus reset detected, an illegal command fed to the ESP,
* one of DISCONNECT, BUS SERVICE, FUNCTION COMPLETE conditions
* for the ESP, a Reselection interrupt, or one of Selection
* or Selection with Attention.
*
* Of these possible interrupts, we can deal with some right
* here and now, irrespective of the current state of the driver.
*
* take care of the most likely interrupts first and call the action
* immediately
*/
ESP_INT_RESEL)) == 0) {
/*
* The rest of the reasons for an interrupt, including
* interrupts just from the dma gate array itself, can
* be handled based purely on the state that the driver
* is currently in now.
*/
} else {
#ifdef ESPDEBUG
#endif /* ESPDEBUG */
}
/*
* this 'cannot happen'.
*/
} else {
}
} else if (intr & ESP_INT_RESET) {
/*
* If we detect a SCSI reset, we blow away the current
* command (if there is one) and all disconnected commands
* because we now don't know the state of them at all.
*/
} else if (intr & ESP_INT_ILLEGAL) {
/*
* This should not happen. The one situation where
* we can get an ILLEGAL COMMAND interrupt is due to
* a bug in the ESP100 during reselection which we
* should be handling in esp_reconnect().
*/
}
while (action != ACTION_RETURN) {
"espsvc call: esp 0x%p, action %d (%d)",
i++;
}
exit:
}
/*
* Manage phase transitions.
*/
static int
{
int action;
};
int i = 0;
do {
EPRINTF1("esp_phasemanage: %s\n",
"esp_phasemanage_call: esp 0x%p (%d)", (void *)esp, i);
i++;
if (state == ACTS_UNKNOWN) {
} else {
}
} while (action == ACTION_PHASEMANAGE);
return (action);
}
/*
* remove a tagged cmd from t_slot list and if timeout is set, then
* adjust timeouts; if a the same cmd will be resubmitted soon, don't
* bother to adjust timeouts
*/
static void
int new_timeout_flag)
{
EPRINTF4("remove tag %d slot %d for target %d.%d\n",
}
/*
* If all cmds drained from tag Q, clear throttle and
* start queuing up new cmds again.
*/
IPRINTF("reset throttle\n");
}
if (new_timeout_flag != NEW_TIMEOUT) {
return;
}
/*
* Figure out what to set tag Q timeout for...
*
* Optimize: If we have duplicate's of same timeout
* we're using, then we'll use it again until we run
* out of duplicates. This should be the normal case
* for block and raw I/O.
* If no duplicates, we have to scan through tag que and
* find the longest timeout value and use it. This is
* going to take a while...
*/
uint_t n = 0;
int i;
/*
* This crude check assumes we don't do
* this too often which seems reasonable
* for block and raw I/O.
*/
for (i = 0; i < NTAGS; i++) {
continue;
}
} else if (
}
}
tag_slots->e_timebase = n;
} else {
tag_slots->e_timebase = 0;
}
}
}
}
}
/*
* Most commonly called phase handlers:
*
* Finish routines
*/
static int
{
short last_slot;
int action = ACTION_SEARCH;
struct scsi_status *status =
"esp_finish_start");
EPRINTF("esp_finish\n");
}
#ifdef ESP_TEST_UNTAGGED
debug_enter("untagged cmd completed");
}
#endif
#ifdef ESPDEBUG
debug_enter("parity errors");
}
if (DEBUGGING) {
esp->e_last_msgin);
}
#endif /* ESPDEBUG */
/*
* In the case that we are getting a check condition
* clear our knowledge of synchronous capabilities.
* This will unambiguously force a renegotiation
* prior to any possible data transfer (we hope),
* including the data transfer for a UNIT ATTENTION
* condition generated by somebody powering on and
* off a target.
* Note: only renegotiate if we were running sync mode
* with this target
*/
}
}
/*
* backoff sync if there were parity errors
*/
}
/*
* go to state free and try to start a new cmd now
* don't start the next cmd if the current cmd was a RQSENSE; this
* will give the target driver a chance to do some recovery
*/
/*
* Free tagged slot
*/
/*
* we used to always set action to ACTION_RETURN
* this leaves a small window where the
* ready queue is non-empty
* and doesn't get started
*/
}
}
#ifdef ESP_TEST_RQSENSE
esp_test_rqsense = 0;
}
#endif
#ifdef ESPDEBUG
debug_enter("esp_finish with check condition");
}
#endif
}
#ifdef ESPDEBUG
}
#endif /* ESPDEBUG */
}
#ifdef ESP_KSTATS
/*
* update kstats
*/
} else {
}
}
}
#endif /* ESP_KSTATS */
/*
* NO_INTR pkts shouldn't have a pkt_comp callback
* but we call esp_call_pkt_comp() just to clean up
*/
} else {
/*
* start an autorequest sense if there was a check condition
*/
/*
* auto request sense failed
* let the target driver handle it
*/
} else {
}
} else {
}
}
return (action);
}
/*
* Request sense commands are priority commands and can't get
* QFULL condition.
*/
static void
{
/*
* We have exhausted the retries on QFULL, or,
* the target driver has indicated that it
* wants to handle QFULL itself by setting
* qfull-retries capability to 0. In either case
* we want the target driver's QFULL handling
* to kick in. We do this by having pkt_reason
* as CMD_CMPLT and pkt_scbp as STATUS_QFULL.
*/
IPRINTF2("%d.%d: status queue full, retries over\n",
} else {
}
IPRINTF3("%d.%d: status queue full, new throttle = %d, "
/*
* By setting throttle to QFULL_THROTTLE, we
* avoid submitting new commands and in
* esp_restart_cmd find out slots which need
* their throttles to be cleared.
*/
}
}
}
}
static void
esp_restart_cmd(void *esp_arg)
{
int i;
IPRINTF("esp_restart_cmd:\n");
esp->e_restart_cmd_timeid = 0;
for (i = 0; i < N_SLOTS; i += NLUNS_PER_TARGET) {
continue;
}
}
}
(void) esp_istart(esp);
}
#ifdef ESP_CHECK
/*
* this function checks whether a cmd is already queued
* and also checks the counts (which are not always accurate but
* usually on completion of the error recovery are OK again
*/
static int esp_do_check = 0;
static void
{
if (sp) {
for (i = 0; i < NTAGS; i++) {
}
}
while (qsp) {
}
/*
* command has not been started yet and is still
* in the ready queue
*/
== 0);
== 0);
}
}
}
/* count the number of cmds */
ncmds++;
ndiscs++;
}
}
if (qsp) {
ncmds++;
== 0);
== 0);
}
}
for (i = 0; i < NTAGS; i++) {
ncmds++;
ndiscs++;
}
CFLAG_COMPLETED) == 0);
== 0);
CFLAG_FINISHED) == 0);
}
}
}
}
if (esp_do_check)
debug_enter("ncmds problem");
}
}
#endif
/*
* esp_call_pkt_comp does sanity checking to ensure that we don't
* call completion twice on the same packet or a packet that has been freed.
* if there is a completion function specified, the packet is queued
* up and it is left to the esp_callback thread to empty the queue at
* a lower priority; note that there is one callback queue per esp
*
* we use a separate thread for calling back into the target driver
* this thread unqueues packets from the callback queue
*/
static void
{
"esp_call_pkt_comp_start");
sp->cmd_qfull_retries = 0;
/*
* if there is a completion function and this is not an arq pkt
* or immediate callback pkt then queue up the callback
*/
IPRINTF6("completion for %d.%d, sp=0x%p, "
"reason=%s, stats=%x, state=%x\n",
} else {
EPRINTF2("completion queued for %d.%d\n",
}
/*
* append the packet or start a new queue
*/
/*
* add to tail
*/
} else {
/*
* start new queue
*/
}
/*
* the callback will be redone later
*/
/*
* this recurses!
*/
} else {
EPRINTF2("No completion routine for 0x%p reason %x\n",
}
"esp_call_pkt_comp_end");
}
/*
* Complete the process of selecting a target
*/
static int
{
int target;
"esp_finish_select_start");
EPRINTF("esp_finish_select:\n");
/*
* this shouldn't happen but sometimes does after a
* device reset
*/
return (ACTION_RESET);
}
/*
* Check for DMA gate array errors
*/
/*
* It would be desirable to set the ATN* line and attempt to
* do the whole schmear of INITIATOR DETECTED ERROR here,
* but that is too hard to do at present.
*/
"Unrecoverable DMA error during selection");
"esp_finish_select_end (ACTION_RESET1)");
return (ACTION_RESET);
}
/*
* Latch up fifo count
*/
/*
* How far did we go (by the DMA gate array's reckoning)?
*/
/*
* If the NEXTBYTE value is non-zero (and we have the
* rev 1 DMA gate array), we went one longword further
* less 4 minus the NEXTBYTE value....
*/
int i;
if ((i = DMAGA_NEXTBYTE(dmar)) != 0) {
cmdamt -= (4-i);
}
}
/*
* Shut off DMA gate array
*/
/*
* Now adjust cmdamt by the amount of data left in the fifo
*/
/*
* Be a bit defensive...
*/
cmdamt = 0;
}
#ifdef ESPDEBUG
if (DEBUGGING) {
"finsel: state %s, step %d; did %d of %d; fifo %d\n",
}
#endif /* ESPDEBUG */
/*
* Did something respond to selection?
*/
/*
* We successfully selected a target (we think).
* Now we figure out how botched things are
* based upon the kind of selection we were
* doing and the state of the step register.
*/
switch (step) {
case ESP_STEP_ARBSEL:
/*
* In this case, we selected the target, but went
* neither into MESSAGE OUT nor COMMAND phase.
* However, this isn't a fatal error, so we just
* drive on.
*
* This might be a good point to note that we have
* a target that appears to not accommodate
* disconnecting,
* but it really isn't worth the effort to distinguish
* such targets especially from others.
*/
/* FALLTHROUGH */
case ESP_STEP_SENTID:
/*
* In this case, we selected the target and sent
* message byte and have stopped with ATN* still on.
* This case should only occur if we use the SELECT
* AND STOP command.
*/
/* FALLTHROUGH */
case ESP_STEP_NOTCMD:
/*
* In this case, we either didn't transition to command
* phase, or,
* if we were using the SELECT WITH ATN3 command,
* we possibly didn't send all message bytes.
*/
cmdamt = 0;
break;
case ESP_STEP_PCMD:
/*
* In this case, not all command bytes transferred.
*/
/* FALLTHROUGH */
case ESP_STEP_DONE:
/*
* This is the usual 'good' completion point.
* If we we sent message byte(s), we subtract
* off the number of message bytes that were
* ahead of the command.
*/
if (state == STATE_SELECT_NORMAL)
cmdamt -= 1;
break;
case ESP_STEP_DONE5:
case ESP_STEP_DONE6:
case ESP_STEP_DONE7:
/*
* this happens on some sun4m boards; probably a hw bug
*/
goto step_done;
}
/* FALLTHROUGH */
default:
"bad sequence step (0x%x) in selection", step);
"esp_finish_select_end (ACTION_RESET3)");
return (ACTION_RESET);
}
/*
* If we sent any messages or sent a command, as
* per ESP errata sheets, we have to hit the
* chip with a CMD_NOP in order to unlatch the
* fifo counter.
*/
/*
* *Carefully* dump out any cruft left in the fifo.
* If this target has shifted to synchronous DATA IN
* phase, then the ESP has already flushed the fifo
* for us.
*/
if (fifoamt != 0 &&
}
}
/*
* OR in common state...
*/
/*
* advance command pointer
*/
if (cmdamt > 0) {
}
/*
* data pointer initialization has already been done
*/
"esp_finish_select_end (action3)");
return (esp_handle_unknown(esp));
} else if (intr == ESP_INT_DISCON) {
/*
* This takes care of getting the bus, but no
* target responding to selection. Clean up the
* chip state.
*/
/*
* There is a definite problem where the MT02
* drops BSY if you use the SELECT && STOP command,
* which leaves ATN asserted after sending an identify
* message.
*/
if (step != 0 &&
(state == STATE_SELECT_N_SENDMSG ||
state == STATE_SELECT_N_TAG ||
state == STATE_SELECT_N_STOP)) {
if ((state == STATE_SELECT_N_SENDMSG ||
(state == STATE_SELECT_N_STOP)) &&
IPRINTF("esp_finish_sel: sync neg. failed\n");
/*
* Rerun the command again.
* if not a proxy cmd
*/
#ifdef ESP_KSTATS
/*
* update kstats
*/
if (esp_do_kstats &&
}
#endif /* ESP_KSTATS */
"esp_finish_select_end (RETURN1)");
return (ACTION_RETURN);
}
/*
* target rejected tag and dropped off the
* bus
* clear tag slot and tag
*/
IPRINTF("esp_finish_sel: tag asking failed\n");
/*
* Rerun the command again.
*/
#ifdef ESP_KSTATS
/*
* update kstats
*/
if (esp_do_kstats &&
}
#endif /* ESP_KSTATS */
/* esp_runpoll() will retry nointr cmds */
FLAG_NOINTR) == 0) {
}
"esp_finish_select_end (ACTION_RETURN2)");
return (ACTION_RETURN);
}
}
"esp_finish_select_end (ACTION_FINISH)");
return (ACTION_FINISH);
/*
* A reselection attempt glotzed our selection attempt.
* If we were running w/o checking parity on this
* command, restore parity checking.
* we put request back in the ready queue
*/
int slot;
#ifdef ESP_KSTATS
}
#endif /* ESP_KSTATS */
}
/*
* runpoll will try again so no need to put it
* on ready Q
*/
"esp_finish_select_end (action1)");
return (esp_reconnect(esp));
}
}
}
"esp_finish_select_end (action2)");
return (esp_reconnect(esp));
#ifdef ESPDEBUG
#endif
"esp_finish_select_end (ACTION_RESET2)");
return (ACTION_RESET);
}
/* NOTREACHED */
return (ACTION_FINSEL);
}
/*
* Handle the reconnection of a target
*/
static char *botched_tag =
"Target %d.%d botched tagged queuing msg (0x%x, 0x%x)";
/*
* Identify msg. to target number conversion table.
* Note, id's > 64 are multi-bit and thus invalid so we don't
* need bigger table.
*/
static char scsi_targetid[] = {
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
-1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1,
04, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
05, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
06, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
7
};
static int
{
short slot = -1;
int msg_accept_issued = 0;
char *bad_reselect = NULL;
"esp_reconnect_start");
EPRINTF("esp_reconnect:\n");
default:
/*
* normal initial reconnect; we get another interrupt later
* for the tag
*/
/*
* Pick up target id from fifo
*
* There should only be the reselecting target's id
* and an identify message in the fifo.
*/
bad_reselect = "bad reselect bytes";
goto bad;
}
/*
* Our SCSI id is missing. This 'cannot happen'.
*/
bad_reselect = "scsi id is missing";
goto bad;
}
/*
* Turn off our id
*/
if (tmp == 0) {
/*
* There is no other SCSI id, therefore we cannot
* tell who is reselecting us. This 'cannot happen'.
*/
bad_reselect = "no other scsi id";
goto bad;
}
if (target == -1) {
/*
* There is more than one reselection id on the bus.
* This 'cannot happen'.
*/
bad_reselect = ">2 reselection IDs on the bus";
goto bad;
}
/*
* Now pick up identify message byte, and acknowledge it.
*/
/*
* If we aren't in MESSAGE IN phase,
* things are really screwed up.
*/
bad_reselect = "not in msg-in phase";
goto bad;
}
/*
* XXX: Oh boy. We have problems. What happens
* XXX: if we have a parity error on the IDENTIFY
* XXX: message? We cannot know which lun is
* XXX: reconnecting, but we really need to know
* XXX: that in order to go through all the
* XXX: rigamarole of sending a MSG_PARITY_ERR
* XXX: message back to the target.
* XXX:
* XXX: In order to minimize a panic situation,
* XXX: we'll assume a lun of zero (i.e., synthesize
* XXX: the IDENTIFY message), and only panic
* XXX: if there is more than one active lun on
* XXX: this target.
*/
tmp = MSG_IDENTIFY;
}
/*
* Check sanity of message.
*/
bad_reselect = "bad message";
goto bad;
}
/*
* As per the ESP100 errata sheets, if a selection
* attempt is preempted by a reselection coming in,
* we'll get a spurious ILLEGAL COMMAND error
* interrupt from the ESP100.
* Instead of trying to figure out whether we were
* preempted or not, just gate off of whether
* we are an ESP100 or not.
*/
if (tmp & ESP_INT_RESET) {
"esp_reconnect_end (_F1)");
return (ACTION_FINRST);
}
}
/*
* I believe that this needs to be done to
* unlatch the ESP.
*/
}
/*
* If this target is synchronous, here is the
* place to set it up during a reconnect.
* Must setup for sync xfers because once identify msg ack'ed,
* we can go into data in phase and begin transferring data.
*/
}
/*
* If tag queuing in use, DMA in tag.
* Otherwise, we're ready to go.
* XXX make this non-polled, interrupt driven
*/
volatile uchar_t *c =
/*
* accept the identify msg
*/
/*
* If we've been doing tagged queuing and this
* request doesn't do it,
* maybe it was disabled for this one. This is rather
* dangerous as it blows all pending tagged cmds away.
* But if target is confused, then we'll blow up
* shortly.
*/
*c++ = INVALID_MSG;
*c = INVALID_MSG;
/*
* For tagged queuing, we should still be in msgin
* phase.
* If not, then either we aren't running tagged
* queuing like we thought or the target died.
*/
if (INTPENDING(esp) == 0) {
"esp_reconnect_end (_RETURN1)");
return (ACTION_RETURN);
}
"esp_reconnect_end (_F2)");
return (ACTION_FINRST);
}
bad_reselect = "not in msgin phase";
goto NO_TAG_MSG;
}
bad_reselect = "unexpected bus free";
goto bad;
}
} else {
break;
}
/*FALLTHROUGH*/
case ACTS_RESEL:
{
volatile uchar_t *c =
uint_t i;
IPRINTF1("no tag for slot %x\n",
esp->e_cur_slot);
ESP_INT_FCMP)) {
"esp_reconnect_end (_PHASEMANAGE)");
return (ACTION_PHASEMANAGE);
} else {
bad_reselect = "not in msgin phase";
break;
}
}
msg_accept_issued = 1;
for (i = 0; i < (uint_t)RECONNECT_TAG_RCV_TIMEOUT;
i++) {
/*
* timeout is not very accurate but this
* should take no time at all
*/
if (INTPENDING(esp)) {
"esp_reconnect_end (_F3)");
return (ACTION_FINRST);
}
break;
}
}
drv_usecwait(1);
}
if (i == (uint_t)RECONNECT_TAG_RCV_TIMEOUT) {
bad_reselect = "timeout on tag byte";
goto NO_TAG_MSG;
}
/*
* XXX we should really do a sync here but that
* hurts performance too much; we'll just hang
* around till the tag byte flips
* This is necessary on any system with an
* XBox
*/
if (*c == INVALID_MSG) {
"esp_reconnect: invalid msg, polling\n");
for (i = 0; i < 1000000; i++) {
if (*c != INVALID_MSG)
break;
}
}
bad_reselect = "parity error in tag msg";
goto NO_TAG_MSG;
}
(id = *c++) < MSG_SIMPLE_QTAG ||
id > MSG_ORDERED_QTAG) {
/*
* Target agreed to do tagged queuing
* and lied!
* This problem implies the drive firmware is
* broken.
*/
bad_reselect = "botched tag";
goto NO_TAG_MSG;
}
tag = *c;
/* Set ptr to reconnecting scsi pkt */
} else {
EPRINTF2("Invalid tag, que= 0x%x tag= %d\n",
bad_reselect = "invalid tag";
}
}
}
bad_reselect = "parity error";
sp = 0;
}
#ifdef ESP_TEST_ABORT
#endif
/*
* this shouldn't really happen, so it is better
* to reset the bus; some disks accept the abort
* and then still reconnect
*/
#ifdef ESP_TEST_ABORT
esp_atest_reconn = 0;
#endif
if (bad_reselect == NULL) {
bad_reselect = "no valid cmd";
}
goto bad;
/*
* If we got here, we were already attempting to
* run a polled proxy command for this target.
* Set ATN and, copy in the message, and drive
* on (ignoring any parity error on the identify).
* XXX this may not be very useful....
*/
IPRINTF2("esp_reconnect: fielding proxy cmd for %d.%d\n",
tmp = 0;
tmp++;
}
/*
* pretend that the disconnected cmd is still disconnected
* (this prevents ndisc from going negative)
*/
/*
* If we are doing PARITY checking, check for a parity
* error on the IDENTIFY message.
*/
/*
* If we had detected a parity error
* on the IDENTIFY message, and this
* command is being run without checking,
* act as if we didn't get a parity
* error. The assumption here is that
* we only disable parity checking for
* targets that don't generate parity.
*/
}
}
/*
* Accept the last message if we haven't done so
*/
if (msg_accept_issued == 0) {
}
/*
* A reconnect may imply a restore pointers operation
* Note that some older disks (Micropolis in Pbox) do not
* send a save data ptr on disconnect if all data has been
* xferred. So, we cannot restore ptrs yet here.
*/
}
/*
* And zero out the SYNC negotiation counter
*/
/*
* Return to await the FUNCTION COMPLETE interrupt we
* should get out of accepting the IDENTIFY message.
*/
"esp_reconnect_end (_RETURN2)");
return (ACTION_RETURN);
bad:
#ifdef ESPDEBUG
#endif
"esp_reconnect_end (_RESET5)");
return (ACTION_RESET);
}
static int
{
"esp_handle_unknown_start: esp 0x%p", (void *)esp);
EPRINTF("esp_handle_unknown:\n");
/*
* we call actions here rather than returning to phasemanage
* (this is the most frequently called action)
*/
case ESP_PHASE_DATA_IN:
case ESP_PHASE_DATA_OUT:
"esp_handle_unknown_end (phase_data)");
return (esp_handle_data(esp));
case ESP_PHASE_MSG_OUT:
"esp_handle_unknown_end (phase_msg_out)");
return (esp_handle_msg_out(esp));
case ESP_PHASE_MSG_IN:
"esp_handle_unknown_end (phase_msg_in)");
return (esp_handle_msg_in(esp));
case ESP_PHASE_STATUS:
#ifdef ESP_TEST_PARITY
}
#endif /* ESP_TEST_PARITY */
/*
* use a stacked cmd to complete
* and accept the msg
*
* stacked cmds sometimes fail with FAS101
* and some slow disks; they are only
* reliable on FAS236
*/
volatile uchar_t *c =
*c++ = INVALID_MSG;
*c = INVALID_MSG;
/*
* no back to back accesses to esp
*/
} else {
}
"esp_handle_unknown_end (phase_status)");
return (esp_handle_c_cmplt(esp));
case ESP_PHASE_COMMAND:
"esp_handle_unknown_end (phase_cmd)");
return (esp_handle_cmd_start(esp));
}
"esp_handle_unknown_end (reset)");
return (ACTION_RESET);
} else {
/*
* Okay. What to do now? Let's try (for the time being)
* assuming that the target went south and dropped busy,
* as a disconnect implies that either we received
* a completion or a disconnect message, or that we
* had sent an ABORT OPERATION or BUS DEVICE RESET
* message. In either case, we expected the disconnect
* and should have fielded it elsewhere.
*
* If we see a chip disconnect here, this is an unexpected
* loss of BSY*. Clean up the state of the chip and return.
*
*/
}
EPRINTF4("msgout: %x %x %x, last_msgout=%x\n",
msgout == MSG_DEVICE_RESET) {
IPRINTF2("Successful %s message to target %d\n",
}
}
} else if (msgout == MSG_DEVICE_RESET) {
}
}
} else {
#ifdef ESPDEBUG
#endif
}
"esp_handle_unknown_end (int_discon)");
return (ACTION_FINISH);
}
/* NOTREACHED */
}
static int
{
"esp_handle_clearing_start");
EPRINTF("esp_handle_clearing:\n");
if (INTPENDING(esp)) {
} else {
}
"esp_handle_clearing_end (ACTION_FINRST)");
return (ACTION_FINRST);
}
} else {
/*
* change e_laststate for the next time around
*/
"esp_handle_clearing_end (ACTION_RETURN1)");
return (ACTION_RETURN);
}
}
/*
* At this point the ESP chip has disconnected. The bus should
* be either quiet or someone may be attempting a reselection
* of us (or somebody else). Call the routine that sets the
* chip back to a correct and known state.
* If the last message in was a disconnect, search
* for new work to do, else return to call esp_finish()
*/
}
}
/*
* start a cmd here to save time
*/
"esp_handle_clearing_end (ACTION_RETURN2)");
return (ACTION_RETURN);
}
"esp_handle_clearing_end (ACTION_RETURN3)");
return (ACTION_RETURN);
} else {
"esp_handle_clearing_end");
return (esp_finish(esp));
}
} else {
/*
* bus, that is a gross fatal error.
*/
"Target %d didn't disconnect after sending %s",
#ifdef ESPDEBUG
IPRINTF4("msgout: %x %x %x, last_msgout=%x\n",
#endif
"esp_handle_clearing_end (ACTION_ABORT_CURCMD)");
return (ACTION_ABORT_ALLCMDS);
}
}
static int
{
int sending;
"esp_handle_data_start");
EPRINTF2("esp_handle_data: sp=0x%p, flags=%x\n",
}
/*
* XXX: This isn't the right reason
*/
bad:
"esp_handle_data_end (ACTION_ABORT_CURCMD1)");
return (ACTION_ABORT_CURCMD);
} else {
}
return (ACTION_ABORT_CURCMD);
}
}
/*
* make sure our DMA pointers are in good shape.
*
* Because SCSI is SCSI, the current DMA pointer has got to be
* greater than or equal to our DMA base address. All other cases
* that might have affected this always set curaddr to be >=
* to the DMA base address.
*/
EPRINTF5("cmd_data_count=%x, dmacount=%x, cur_addr=%x, end=%"
PRIx64 ", nwin=%x\n",
EPRINTF2("dmac_address=%x, dmac_size=%lx\n",
goto bad;
}
IPRINTF2("dmac_address=%x, dmac_size=%lx\n",
}
if (ESP_MAX_DMACOUNT < amt) {
}
#ifdef ESPDEBUG
/*
* Make sure that we don't cross a boundary we can't handle
* This is probably checked as well by the DMA framework
*/
goto bad;
}
}
#endif
}
#ifdef ESPDEBUG
#endif /* ESPDEBUG */
if (!sending) {
"esp_handle_data_end (ACTION_ABORT_CURCMD2)");
return (ACTION_ABORT_CURCMD);
}
} else {
if (sending) {
"esp_handle_data_end (ACTION_ABORT_CURCMD3)");
return (ACTION_ABORT_CURCMD);
}
}
#ifdef ESP_TEST_PARITY
}
#endif /* ESP_TEST_PARITY */
/*
* XXX DON't change the order of these two statements, see 1162008
*/
"esp_handle_data_end (ACTION_RETURN)");
return (ACTION_RETURN);
}
static int
{
"esp_handle_data_done_start");
EPRINTF("esp_handle_data_done:\n");
spurious_data = do_drain_fifo = 0;
/*
* Check for DMAGA errors (parity or memory fault)
*/
/*
* It would be desirable to set the ATN* line and attempt to
* do the whole schmear of INITIATOR DETECTED ERROR here,
* but that is too hard to do at present.
*/
"esp_handle_data_done_end (ACTION_RESET)");
return (ACTION_RESET);
}
/*
* Data Receive conditions:
*
* Check for parity errors. If we have a parity error upon
* receive, the ESP chip has asserted ATN* for us already.
*
* For Rev-1 and Rev-2 dma gate arrays,
* make sure the last bytes have flushed.
*/
if (!was_sending) {
#ifdef ESP_TEST_PARITY
esp_ptest_data_in = 0;
stat |= ESP_STAT_PERR;
}
#endif /* ESP_TEST_PARITY */
if (stat & ESP_STAT_PERR) {
"SCSI bus DATA IN phase parity error");
}
} else {
/*
* clear state of dma gate array
*/
}
/*
* Check to make sure we're still connected to the target.
* If the target dropped the bus, that is a fatal error.
* We don't even attempt to count what we were transferring
* here. Let esp_handle_unknown clean up for us.
*/
"esp_handle_data_done_end (ACTION_PHASEMANAGE)");
return (ACTION_PHASEMANAGE);
}
/*
* Figure out how far we got.
* Latch up fifo amount first.
*/
if (stat & ESP_STAT_XZERO) {
} else {
}
/*
* Unconditionally knock off by the amount left
* in the fifo if we were sending out the SCSI bus.
*
* If we were receiving from the SCSI bus, believe
* what the chip told us (either XZERO or by the
* value calculated from the counter register).
* The reason we don't look at the fifo for
* incoming data is that in synchronous mode
* the fifo may have further data bytes, and
* for async mode we assume that all data in
* the fifo will have been transferred before
* the esp asserts an interrupt.
*/
if (was_sending) {
}
/*
* If this was a synchronous transfer, flag it.
* Also check for the errata condition of long
* last REQ/ pulse for some synchronous targets
*/
/*
* flag that a synchronous data xfer took place
*/
static char *spur =
"Spurious %s phase from target %d\n";
/*
* Okay, latch up new status register value
*/
/*
* Get a new stat from the esp chip register.
*/
phase &= ESP_PHASE_MASK;
/*
* Now, if we're still (maybe) in a data phase,
* check to be real sure that we are...
*/
if (phase == ESP_PHASE_DATA_IN) {
spurious_data = 1;
} else if (phase == ESP_PHASE_DATA_OUT) {
spurious_data = -1;
}
if (spurious_data) {
spur, (spurious_data < 0) ?
/*
* It turns out that this can also
* come about if the target resets
* (and goes back to async SCSI mode)
* and we don't know about it.
*
* The degenerate case for this is
* turning off a lunchbox- this clears
* it's state. The trouble is is that
* we'll get a check condition (likely)
* on the next command after a power-cycle
* for this target, but we'll have to
* go into a DATA IN phase to pick up
* the sense information for the Request
* Sense that will likely follow that
* Check Condition.
*
* As a temporary fix, I'll clear
* the 'sync_known' flag for this
* target so that the next selection
* for this target will renegotiate
* the sync protocol to be followed.
*/
}
if (spurious_data == 0 && was_sending)
do_drain_fifo = 1;
} else {
/*
* The need to handle for the ESP100A the case
* it's sync. setting is covered in esp_finish()
* where a CHECK CONDITION status causes the
* esp->e_sync_known flag to be cleared.
*
* If we are doing synchronous DATA OUT,
* we should probably drain the fifo.
* If we are doing synchronous DATA IN,
* we really don't dare do that (in case
* we are going from data phase to data
* phase).
*/
if (was_sending)
do_drain_fifo = 1;
}
} else {
/*
* If we aren't doing Synchronous Data Transfers,
* definitely offload the fifo.
*/
do_drain_fifo = 1;
}
/*
* Drain the fifo here of any left over
* that weren't transferred (if desirable).
*/
if (do_drain_fifo) {
}
/*
* adjust pointers...
*/
#ifdef ESPDEBUG
"DATA %s phase for %d.%d did 0x%x of 0x%x bytes\n",
}
#endif /* ESPDEBUG */
if (spurious_data == 0) {
stat &= ESP_PHASE_MASK;
"esp_handle_data_done_end (action1)");
return (esp_handle_data(esp));
} else {
"esp_handle_data_done_end (action2)");
return (esp_handle_unknown(esp));
}
} else {
"esp_handle_data_done_end (ACTION_RETURN)");
return (ACTION_RETURN);
}
}
static char *msginperr = "SCSI bus MESSAGE IN phase parity error\n";
static int
{
"esp_handle_c_cmplt_start");
EPRINTF("esp_handle_c_cmplt:\n");
/*
* if target is fast, we can get cmd. completion by the time we get
* here. Otherwise, we'll have to taken an interrupt.
*/
if (INTPENDING(esp)) {
} else {
}
if (intr & ESP_INT_RESET) {
"esp_handle_c_cmplt_end (ACTION_FINRST)");
return (ACTION_FINRST);
}
} else {
/*
* change e_laststate for the next time around
*/
"esp_handle_c_cmplt_end (ACTION_RETURN1)");
return (ACTION_RETURN);
}
} else {
}
#ifdef ESP_TEST_PARITY
esp_ptest_status = 0;
esp_ptest_msgin = 0;
esp_ptest_msg = -1;
}
#endif /* ESP_TEST_PARITY */
if (intr == ESP_INT_DISCON) {
"esp_handle_c_cmplt_end (action1)");
return (esp_handle_unknown(esp));
}
}
/*
* we really need a ddi_dma_sync() here but that is too
* expensive; this loop is necessary for xbox, see
* also in esp_reconnect()
*/
if (*c == INVALID_MSG) {
int i;
EPRINTF("esp_handle_c_cmplt: invalid msg\n");
for (i = 0; i < 1000000; i++) {
if (*c != INVALID_MSG) {
break;
}
}
}
} else {
/*
* if we haven't done a stacked cmd with a MSG_ACPT,
* do a msg accept now and read the fifo data
*/
if (intr & ESP_INT_FCMP) {
*c = ep->esp_fifo_data;
}
}
msgout = 0;
/*
* The ESP manuals state that this sequence completes
* with a BUS SERVICE interrupt if just the status
* byte was received, else a FUNCTION COMPLETE interrupt
* if both status and a message was received.
*
* The manuals also state that ATN* is asserted if
* bad parity is detected.
*
* The one case that we cannot handle is where we detect
* bad parity for the status byte, but the target refuses
* to go to MESSAGE OUT phase right away. This means that
* if that happens, we will misconstrue the parity error
* to be for the completion message, not the status byte.
*/
if (intr & ESP_INT_FCMP) {
sts = *c++;
if (perr) {
}
} else if (intr == ESP_INT_BUS) {
/*
* We only got the status byte.
*/
sts = *c;
if (perr) {
/*
* If we get a parity error on a status byte
* assume that it was a CHECK CONDITION
*/
sts = STATUS_CHECK;
"SCSI bus STATUS phase parity error");
}
} else {
IPRINTF("esp_handle_cmd_cmplt: unexpected int\n");
"esp_handle_c_cmplt_end (action2)");
return (esp_handle_unknown(esp));
}
if (sts != INVALID_MSG) {
}
if (msgout == 0) {
if (msg == MSG_COMMAND_COMPLETE) {
/*
* Actually, if the message was a 'linked command
* complete' message, the target isn't going to be
* clearing the bus.
*/
} else {
"esp_handle_c_cmplt_end (action3)");
return (esp_handle_msg_in_done(esp));
}
} else {
}
if (intr != ESP_INT_BUS) {
"esp_handle_c_cmplt_end (action4)");
return (esp_handle_clearing(esp));
}
"esp_handle_c_cmplt_end (ACTION_RETURN2)");
return (ACTION_RETURN);
} else {
"esp_handle_c_cmplt_end (action5)");
return (esp_handle_unknown(esp));
}
"esp_handle_c_cmplt_end (ACTION_PHASEMANAGE)");
return (ACTION_PHASEMANAGE);
}
}
static int
{
"esp_handle_msg_in_start");
EPRINTF("esp_handle_msg_in\n");
/*
* Pick up a message byte.
* Clear the FIFO so we
* don't get confused.
*/
}
esp->e_imsgindex = 0;
/*
* give a little extra time by returning to phasemanage
*/
"esp_handle_msg_in_end (ACTION_PHASEMANAGE)");
return (ACTION_PHASEMANAGE);
}
/*
* We come here after issuing a MSG_ACCEPT
* command and are expecting more message bytes.
* The ESP should be asserting a BUS SERVICE
* interrupt status, but may have asserted
* a different interrupt in the case that
* the target disconnected and dropped BSY*.
*
* In the case that we are eating up message
* bytes (and throwing them away unread) because
* we have ATN* asserted (we are trying to send
* a message), we do not consider it an error
* if the phase has changed out of MESSAGE IN.
*/
static int
{
"esp_handle_more_msgin_start");
EPRINTF("esp_handle_more_msgin\n");
/*
* Fetch another byte of a message in.
*/
"esp_handle_more_msgin_end (ACTION_RETURN)");
return (ACTION_RETURN);
}
/*
* If we were gobbling up a message and we have
* changed phases, handle this silently, else
* complain. In either case, we return to let
* esp_phasemanage() handle things.
*
* If it wasn't a BUS SERVICE interrupt,
* let esp_phasemanage() find out if the
* chip disconnected.
*/
"Premature end of extended message");
}
}
"esp_handle_more_msgin_end (action)");
return (esp_handle_unknown(esp));
}
static int
{
int sndmsg = 0;
"esp_handle_msg_in_done_start");
EPRINTF("esp_handle_msg_in_done:\n");
if (INTPENDING(esp)) {
} else {
}
"esp_handle_msg_in_done_end (_FINRST)");
return (ACTION_FINRST);
}
} else {
/*
* change e_laststate for the next time around
*/
"esp_handle_msg_in_done_end (ACTION_RETURN1)");
return (ACTION_RETURN);
}
}
/*
* We can be called here for both the case where
* we had requested the ESP chip to fetch a message
* byte from the target (at the target's request).
* We can also be called in the case where we had
* been using the CMD_COMP_SEQ command to pick up
* both a status byte and a completion message from
* a target, but where the message wasn't one of
* COMMAND COMPLETE, LINKED COMMAND COMPLETE, or
* LINKED COMMAND COMPLETE (with flag). This is a
* legal (albeit extremely unusual) SCSI bus trans-
* -ition, so we have to handle it.
*/
#ifdef ESP_TEST_PARITY
#endif /* ESP_TEST_PARITY */
"premature end of input message");
"esp_handle_msg_in_done_end (ACTION_PHASEMANAGE)");
return (ACTION_PHASEMANAGE);
}
/*
* Note that if e_imsglen is zero, then we are skipping
* input message bytes, so there is no reason to look for
* parity errors.
*/
/*
* If we have got more than one byte in the fifo,
* that is a gross screwup, and we should let the
* target know that we have completely fouled up.
*/
/*
* If we are in the middle of gobbling up and throwing
* away a message (due to a previous message input
* error), drive on.
*/
} else {
}
} else {
/*
* In this case, we have been called (from
* esp_handle_c_cmplt()) with the message
* already stored in the message array.
*/
}
/*
* Process this message byte (but not if we are
* going to be trying to send back some error
* anyway)
*/
EPRINTF2("message byte %d: 0x%x\n",
#ifdef ESP_TEST_PARITY
esp_ptest_msg == msgin) {
esp_ptest_msgin = 0;
esp_ptest_msg = -1;
goto reloop;
}
#endif /* ESP_TEST_PARITY */
#ifdef ESP_TEST_PARITY
esp_ptest_emsgin = 0;
goto reloop;
}
#endif /* ESP_TEST_PARITY */
static char *tool =
"Extended message 0x%x is too long";
/*
* Is the incoming message too long
* to be stored in our local array?
*/
sndmsg = MSG_REJECT;
} else {
}
} else {
}
} else {
}
}
if (sndmsg < 0) {
/*
* If sndmsg is less than zero, one of the subsidiary
* routines needs to return some other state than
* ACTION_RETURN.
*/
"esp_handle_msg_in_done_end (-sndmsg)");
return (-sndmsg);
} else if (sndmsg > 0) {
if (IS_1BYTE_MSG(sndmsg)) {
}
/*
* The target is not guaranteed to go to message out
* phase, period. Moreover, until the entire incoming
* message is transferred, the target may (and likely
* will) continue to transfer message bytes (which
* we will have to ignore).
*
* In order to do this, we'll go to 'infinite'
* message in handling by setting the current input
* message length to a sentinel of zero.
*
* This works regardless of the message we are trying
* to send out. At the point in time which we want
* to send a message in response to an incoming message
* we do not care any more about the incoming message.
*
* If we are sending a message in response to detecting
* a parity error on input, the ESP chip has already
* set ATN* for us, but it doesn't hurt to set it here
* again anyhow.
*/
}
/*
* do not give a MSG_ACPT if we are not in msg phase anymore
* and the target already dropped off the bus
* this is not worth the extra PIO read on viking based machines
* with FAS chips
*/
(ESP_STAT_MSG | ESP_STAT_CD)) {
}
} else {
}
"esp_handle_msg_in_done_end (action)");
return (esp_handle_clearing(esp));
}
"esp_handle_msg_in_done_end (ACTION_RETURN2)");
return (ACTION_RETURN);
}
static int
{
int msgout = 0;
EPRINTF("esp_onebyte_msg\n");
if (msgin & MSG_IDENTIFY) {
/*
* How did we get here? We should only see identify
* messages on a reconnection, but we'll handle this
* fine here (just in case we get this) as long as
* we believe that this is a valid identify message.
*
* For this to be a valid incoming message,
* bits 6-4 must must be zero. Also, the
* bit that says that I'm an initiator and
* can support disconnection cannot possibly
* be set here.
*/
if (garbled) {
/*
* If it's a garbled message,
* try and tell the target...
*/
} else {
}
return (msgout);
return (0);
}
switch (msgin) {
case MSG_DISCONNECT:
/*
* If we 'cannot' disconnect- reject this message.
* Note that we only key off of the pkt_flags here-
* it would be inappropriate to test against esp->e_scsi_options
* or esp->e_nodisc here (they might have been changed
* after this command started). I realize that this
* isn't complete coverage against this error, but it
* is the best we can do. I thought briefly about setting
* the FLAG_NODISCON bit in a packet
* if either of esp->e_scsi_options or esp->e_nodisc indicated
* that disconnect/reconnect has been turned off, but
* that might really bolix up the true owner of the
* packet (the target driver) who has really only
* *loaned* us this packet during transport.
*/
msgout = MSG_REJECT;
break;
}
/* FALLTHROUGH */
case MSG_COMMAND_COMPLETE:
/* FALLTHROUGH */
case MSG_LINK_CMPLT:
/* FALLTHROUGH */
case MSG_LINK_CMPLT_FLAG:
break;
/* This has been taken care of above */
/* case MSG_EXTENDED: */
case MSG_NOP:
break;
case MSG_REJECT:
{
/*
* The target is rejecting the last message we sent.
*
* If the last message we attempted to send out was an
* extended message, we were trying to negotiate sync
* xfers- and we're okay.
*
* Otherwise, a target has rejected a message that
* it should have handled. We will abort the operation
* in progress and set the pkt_reason value here to
* show why we have completed. The process of aborting
* may be via a message or may be via a bus reset (as
* a last resort).
*/
switch (lastmsg) {
case MSG_EXTENDED:
msgout = 0;
break;
case MSG_NOP:
break;
case MSG_INITIATOR_ERROR:
break;
case MSG_MSG_PARITY:
break;
case MSG_REJECT:
break;
case MSG_SIMPLE_QTAG:
case MSG_ORDERED_QTAG:
case MSG_HEAD_QTAG:
break;
case MSG_DEVICE_RESET:
case MSG_ABORT:
case MSG_ABORT_TAG:
/*
* it is time to yank the chain on the bus...
*/
break;
default:
if (IS_IDENTIFY_MSG(lastmsg)) {
/*
* this often happens when the
* target rejected our tag
*/
} else {
}
} else {
}
break;
}
if (msgout) {
"Target %d rejects our message '%s'",
IPRINTF2("sp=0x%p, pkt_reason=%x\n",
}
}
break;
}
case MSG_RESTORE_PTRS:
}
}
break;
case MSG_SAVE_DATA_PTR:
break;
/* These don't make sense for us, and */
/* will be rejected */
/* case MSG_INITIATOR_ERROR */
/* case MSG_ABORT */
/* case MSG_MSG_PARITY */
/* case MSG_DEVICE_RESET */
default:
msgout = MSG_REJECT;
"Rejecting message '%s' from Target %d",
break;
}
return (msgout);
}
/*
* phase handlers that are rarely used
*/
static int
{
"esp_handle_cmd_start_start");
EPRINTF("esp_handle_cmd:\n");
/*
* If the cmd is a defined scsi-2 cdb and it'll fit in our dma buffer,
* we'll use dma. If not, we send it one byte at a time and take
* forever!
*/
if (amt > 0) {
int i;
EPRINTF("esp_handle_cmd: send cmd\n");
for (i = 0; i < amt; i++) {
}
} else {
/*
* Check for command overflow.
*/
"esp_handle_cmd_start_end (abort_cmd)");
return (ACTION_ABORT_CURCMD);
}
if (cmd_distance == 0) {
}
/*
* Stuff next command byte into fifo
*/
/* delay here: prevents problems with CDROM, see 1068706 */
}
"esp_handle_cmd_start_end");
return (ACTION_RETURN);
}
static int
{
"esp_handle_cmd_done_start");
EPRINTF("esp_handle_cmd_done\n");
/*
* The NOP command is required following a COMMAND
* or MESSAGE OUT phase in order to unlatch the
* FIFO flags register. This is needed for all
* ESP chip variants.
*/
/*
* We should have gotten a BUS SERVICE interrupt.
* If it isn't that, and it isn't a DISCONNECT
* interrupt, we have a "cannot happen" situation.
*/
if ((intr & ESP_INT_BUS) == 0) {
if ((intr & ESP_INT_DISCON) == 0) {
"esp_handle_cmd_done_end (abort1)");
return (ACTION_ABORT_CURCMD);
}
} else {
}
/*
* If we dma'ed out the cdb, we have a little cleanup to do...
*/
if (sp->cmd_cdblen > 0) {
int amt, i;
if ((i = DMAGA_NEXTBYTE(dmar)) != 0) {
amt -= (4-i);
}
}
"esp_handle_cmd_done_end (abort2)");
return (ACTION_ABORT_CURCMD);
}
}
"esp_handle_cmd_done_end");
return (esp_handle_unknown(esp));
}
/*
* Begin to send a message out
*/
static int
{
"esp_handle_msg_out_start");
EPRINTF("esp_handle_msg_out\n");
/*
* Check to make *sure* that we are really
* in MESSAGE OUT phase. If the last state
* was ACTS_MSG_OUT_DONE, then we are trying
* to resend a message that the target stated
* had a parity error in it.
*
* If this is the case, and mark completion reason as CMD_NOMSGOUT.
* XXX: Right now, we just *drive* on. Should we abort the command?
*/
"esp_handle_msg_out_end (ACTION_PHASEMANAGE)");
return (ACTION_PHASEMANAGE);
}
/*
* Clean the fifo.
*/
/*
* If msg only 1 byte, just dump it in the fifo and go. For
* multi-byte msgs, dma them to save time. If we have no
* msg to send and we're in msg out phase, send a NOP.
*
* XXX: If target rejects synch. negotiate, we'll end up
* having to send a nop msg because the esp chip doesn't
* drop ATN* fast enough.
*/
if (amt == 1) {
} else if (amt > 1) {
char i;
for (i = 0; i < amt; i++)
} else {
/*
* this happens when the target reject the first byte
* of an extended msg such as synch negotiate
* (see also comment above)
*/
}
"esp_handle_msg_out_end");
return (ACTION_RETURN);
}
static int
{
int action;
"esp_handle_msg_out_done_start");
}
EPRINTF4("msgout: %x %x %x, last_msgout=%x\n",
/*
* If we dma'ed out the msg, we have a little cleanup to do...
*/
if (amt > 1) {
int i;
if ((i = DMAGA_NEXTBYTE(dmar)) != 0) {
amt -= (4-i);
}
}
}
/*
* If the ESP disconnected, then the message we sent caused
* the target to decide to drop BSY* and clear the bus.
*/
msgout == MSG_ABORT_TAG) {
/*
* If we sent a device reset msg, then we need to do
* a synch negotiate again unless we have already
* inhibited synch.
*/
}
} else if (msgout == MSG_DEVICE_RESET) {
}
}
EPRINTF2("Successful %s message to target %d\n",
}
"esp_handle_msg_out_done_end (ACTION_FINISH)");
return (ACTION_FINISH);
}
/*
* If the target dropped busy on any other message, it
* wasn't expected. We will let the code in esp_phasemanage()
* handle this unexpected bus free event.
*/
goto out;
}
/*
* What phase have we transitioned to?
*/
/*
* Save current fifo count
*/
/*
* As per the ESP errata sheets, this must be done for
* all ESP chip variants.
*
* This releases the FIFO counter from its latched state.
* Note that we read the fifo counter above prior to doing
* this.
*/
/*
* Clean the fifo? Yes, if and only if we haven't
* transitioned to Synchronous DATA IN phase.
* The ESP chip manual notes that in the case
* that the target has shifted to Synchronous
* DATA IN phase, that while the FIFO count
* register stays latched up with the number
* of bytes not transferred out, that the fifo
* itself is cleared and will contain only
* the incoming data bytes.
*
* The manual doesn't state what happens in
* other receive cases (transition to STATUS,
* MESSAGE IN, or asynchronous DATA IN phase),
* but I'll assume that there is probably
* a single-byte pad between the fifo and
* the SCSI bus which the ESP uses to hold
* the currently asserted data on the bus
* (known valid by a true REQ* signal). In
* the case of synchronous data in, up to
* 15 bytes of data could arrive, so the
* ESP must have to make room for by clearing
* the fifo, but in other cases it can just
* hold the current byte until the next
* ESP chip command that would cause a
* data transfer.
* XXX STILL NEEDED????
*/
}
/*
* If we finish sending a message out, and we are
* still in message out phase, then the target has
* detected one or more parity errors in the message
* we just sent and it is asking us to resend the
* previous message.
*/
/*
* As per SCSI-2 specification, if the message to
* be re-sent is greater than one byte, then we
* have to set ATN*.
*/
if (amt > 1) {
}
"SCSI bus MESSAGE OUT phase parity error");
"esp_handle_msg_out_done_end (ACTION_PHASEMANAGE)");
return (ACTION_PHASEMANAGE);
}
/*
* Count that we sent a SYNCHRONOUS DATA TRANSFER message.
* (allow for a tag message before the sdtr msg)
*/
}
out:
"esp_handle_msg_out_done_end");
return (action);
}
static int
{
"Two byte message '%s' 0x%x rejected",
return (MSG_REJECT);
}
/*
* If offset is 0 then asynchronous mode is assumed and the property
* is removed
*/
static void
{
static char *prop_template = "target%d-sync-speed";
char property[32];
if (offset) {
/*
* Convert input clock cycle per
* byte to nanoseconds per byte.
* (ns/b), and convert that to
*/
} else {
tickval = 0;
}
/*
* We cannot hold any mutex at this point because the call to
* ddi_prop_update_int, ddi_prop_remove may block.
*/
if (offset == 0) {
/*
* if target was switched back to async mode,
* remove property
*/
IPRINTF1("cannot remove %s property\n",
property);
}
return;
}
}
if (offset != 0) {
(int)tickval) != DDI_PROP_SUCCESS) {
}
}
}
static int
{
#ifdef ESPDEBUG
/*
* XXX: Should be able to use %d.03%d instead of three different messages.
*/
static char *mbs =
"Target %d now Synchronous at %d.%d MB/s max transmit rate\n";
static char *mbs1 =
"Target %d now Synchronous at %d.0%d MB/s max transmit rate\n";
static char *mbs2 =
"Target %d now Synchronous at %d.00%d MB/s max transmit rate\n";
#endif
int msgout = 0;
EPRINTF("esp_multibyte_msg:\n");
if (emsg == MSG_SYNCHRONOUS) {
EPRINTF3("received period %d offset %d from tgt %d\n",
EPRINTF3("calculated minsync %d, maxsync %d for tgt %d\n",
/*
* In cases where the target negotiates synchronous
* mode before we do, and we either have sync mode
* disbled, or this target is known to be a weak
* signal target, we send back a message indicating
* a desire to stay in asynchronous mode (the SCSI-2
* spec states that if we have synchronous capability
* that we cannot reject a SYNCHRONOUS DATA TRANSFER
* REQUEST message).
*/
SCSI_OPTIONS_SYNC) == 0) {
/*
* Only zero out the offset. Don't change
* the period.
*/
IPRINTF("sending async back\n");
goto out;
}
offset = 0;
}
}
regval = 0;
/*
* If the target's offset is bigger than ours,
* the target has violated the scsi protocol.
*/
if (offset > DEFAULT_OFFSET) {
msgout = MSG_REJECT;
goto out;
}
/*
* We cannot transmit data in synchronous
* mode this slow, so convert to asynchronous
* mode.
*/
goto out;
/*
* If the target's period is less than ours,
* the target has violated the scsi protocol.
*/
msgout = MSG_REJECT;
goto out;
} else if (offset) {
/*
* Conversion method for received PERIOD value
* to the number of input clock ticks to the ESP.
*
* We adjust the input period value such that
* we always will transmit data *not* faster
* than the period value received.
*/
/*
*/
if (period >= FASTSCSI_THRESHOLD) {
regval--;
}
}
/*
* Strictly paranoia!
*/
goto out;
}
}
if (offset) {
/*
* fastscsi in conf3
*/
if (period < FASTSCSI_THRESHOLD) {
} else {
}
}
"sending period %d (%d), offset %d to tgt %d\n",
#ifdef ESPDEBUG
{
/*
* Convert input clock cycle per
* byte to nanoseconds per byte.
* (ns/b), and convert that to
*/
if (xfer_mod > 99) {
} else if (xfer_mod > 9) {
} else {
}
}
#endif /* ESPDEBUG */
} else {
/*
* We are converting back to async mode.
*/
}
if (msgout) {
}
} else if (emsg == MSG_MODIFY_DATA_PTR) {
msgout = MSG_REJECT;
} else {
if (emsg != MSG_WIDE_DATA_XFER) {
"Rejecting message %s 0x%x from Target %d",
} else {
"Rejecting message %s 0x%x from Target %d",
}
msgout = MSG_REJECT;
}
out:
return (msgout);
}
static int
{
return (ACTION_RETURN);
}
/*
* dma window handling
*/
static int
{
return (-1);
}
}
}
return (0);
}
static int
{
return (-1);
}
IPRINTF4("new window %x: off=%lx, len=%lx, count=%x\n",
return (0);
}
static int
{
/* are there more windows? */
}
"cmd_data_count=%x, dmacount=%x, curaddr=%x, nwin=%x\n",
/*
* if there are no more windows, we have a data overrun condition
*/
}
/*
* A fix for bug id 1048141- if we get data transfer
* overruns, assume we have a weak scsi bus. Note that
* this won't catch consistent underruns or other
* noise related syndromes.
*/
return (-1);
} else {
sp->cmd_cur_win--;
return (-1);
}
}
return (0);
}
/*
* dma error checking
*/
static int
{
/*
* was there a dmaga error that caused espsvc() to be called?
*/
/*
* It would be desirable to set the ATN* line and attempt to
* do the whole schmear of INITIATOR DETECTED ERROR here,
* but that is too hard to do at present.
*/
}
return (-1);
}
return (0);
}
/*
* run a polled cmd
*/
static void
{
int limit, i, n;
int timeout = 0;
IPRINTF4("runpoll: slot=%x, cmd=%x, e_slots=0x%p, tcmds=%x\n",
/*
* wait for cmd to complete
* don't start new cmds so set throttles to HOLD_THROTTLE
*/
if (savesp) {
}
IPRINTF("runpoll: timeout on draining\n");
goto bad;
}
}
/*
* if this is not a proxy cmd, don't start the cmd
* without draining the active cmd(s)
* for proxy cmds, we zap the active cmd and assume
* that the caller will take care of this
* For tagged cmds, wait with submitting a non-tagged
* cmd until the queue has been drained
* If the cmd is a request sense, then draining won't
* help since we are in contingence allegiance condition
* XXX this shouldn't really be necessary but it is
* safer
*/
(*cmdp != SCMD_REQUEST_SENSE)) {
if (timeout < POLL_TIMEOUT) {
timeout += 100;
drv_usecwait(100);
continue;
} else {
"polled cmd failed (target busy)");
goto cleanup;
}
}
}
/*
* If the draining of active commands killed the
* the current polled command, we're done..
* XXX this is not very likely
*/
break;
}
/*
* ensure we are not accessing a target too quickly
* after a reset. the throttles get set back later
* by the reset delay watch; hopefully, we don't go
* thru this loop more than once
*/
for (i = 0; i < NTARGETS; i++) {
if (esp->e_reset_delay[i]) {
int s = i * NLUNS_PER_TARGET;
int e = s + NLUNS_PER_TARGET;
esp->e_reset_delay[i] = 0;
for (; s < e; s++) {
esp->e_throttle[s] =
}
}
}
}
/*
* the draining should have cleaned everything up
*/
/* adjust the counts since this cmd is now gone */
/*
* set finished flag so the counts won't get
* decremented again for this cmd
*/
}
/* make sure the throttles are still on hold */
/*
* esp_startcmd() will return false if preempted and will
* not service the interrupt if NOINTR cmd
* if this cmd was a non-tagged cmd for a tagged cmd
* esp_startcmd will also return false
*/
continue;
}
/*
* We're now 'running' this command.
*
* esp_dopoll will always return when
* esp->e_state is STATE_FREE, and
*/
if (limit == 0) {
}
/*
* if the cmd disconnected, the first call to esp_dopoll
* will return with bus free; we go thru the loop one more
* time and wait limit usec for the target to reconnect
*/
for (i = 0; i <= POLL_TIMEOUT; i += 100) {
IPRINTF("runpoll: timeout on polling\n");
goto bad;
}
/*
* If a preemption occurred that caused this
* command to actually not start, go around
* the loop again. If CFLAG_COMPLETED is set, the
* command completed
*/
break;
}
/*
* the bus may have gone free because the target
* disconnected; go thru the loop again
*/
if (n == 0) {
/*
* bump i, we have waited limit usecs in
* esp_dopoll
*/
i += limit - 100;
}
}
if (i > POLL_TIMEOUT) {
IPRINTF("polled timeout on disc. cmd\n");
goto bad;
}
/*
* don't go thru the loop again; the cmd
* was already started
*/
IPRINTF("esp_runpoll: cmd started??\n");
goto bad;
}
}
}
/*
* blindly restore throttles which is preferable over
* leaving throttle hanging at HOLD_THROTTLE and none to clear it
*/
/*
* If we stored up commands to do, start them off now.
*/
}
exit:
return;
bad:
#ifdef ESPDEBUG
#endif /* ESPDEBUG */
/*
* clean up all traces of this sp because esp_runpoll will return
* before esp_reset_recovery() cleans up
*/
}
}
(void) esp_abort_allcmds(esp);
}
goto exit;
}
/*
* Interrupt Service Section:
* Poll for command completion (i.e., no interrupts)
* limit is in usec (and will not be very accurate)
*/
static int
{
int i, n;
/*
* timeout is not very accurate since we don't know how
* long the poll takes
* also if the packet gets started fairly late, we may
* timeout prematurely
* esp_dopoll always returns if e_state transitions to STATE_FREE
*/
if (limit == 0) {
}
for (n = i = 0; i < limit; i += 100) {
if (INTPENDING(esp)) {
n++;
break;
}
drv_usecwait(100);
}
n = -1;
}
return (n);
}
static void
{
int i;
for (i = 0; i < nmsgs; i++) {
}
}
static int
{
return (TRUE);
} else {
"Proxy %s failed for %d.%d, result=%x, reason=%x\n", what,
}
return (FALSE);
}
}
static void
{
*p++ = (uchar_t)MSG_EXTENDED;
*p++ = (uchar_t)3;
*p++ = (uchar_t)MSG_SYNCHRONOUS;
}
/*
* Command watchdog routines
*/
/*ARGSUSED*/
static void
{
int i;
ushort_t props_update = 0;
#ifdef ESP_PERF
if (esp_request_count >= 20000) {
for (i = 0; i < MAX_ESPS; i++) {
if (esp_ncmds_per_esp[i] == 0) {
continue;
}
"esp%d: ncmds = %d\n", i, esp_ncmds_per_esp[i]);
esp_ncmds_per_esp[i] = 0;
}
}
#endif
if (esp_watchdog_running == 0) {
}
/*
* reset throttle. the throttle may have been
* too low if queue full was caused by
* another initiator
* Only reset throttle if no cmd active in e_slots
*/
#ifdef ESP_TEST_UNTAGGED
if (esp_enable_untagged) {
}
#endif
for (i = 0; i < N_SLOTS; i++) {
if ((esp->e_throttle[i] > 0) &&
}
}
}
#ifdef ESP_PERF
#endif
if (esp->e_props_update) {
int i;
/*
* e_mutex will be released and reentered in
* esp_props_update().
* Hence we save the esp->e_props_update now and
* set to 0 indicating that property has been
* updated. This will avoid a race condition with
* any thread that runs in interrupt context that
* attempts to set the e_props_update to non-zero value
*/
esp->e_props_update = 0;
for (i = 0; i < NTARGETS; i++) {
if (props_update & (1<<i)) {
esp_update_props(esp, i);
}
}
}
}
if (esp_timeout_initted && esp_timeout_id) {
}
}
static void
{
short slot;
#ifdef ESP_TEST_BUS_RESET
if (esp_btest) {
esp_btest = 0;
(void) esp_abort_allcmds(esp);
return;
}
#endif /* ESP_TEST_BUS_RESET */
#ifdef ESP_TEST_TIMEOUT
if (esp_force_timeout &&
esp_force_timeout = 0;
return;
}
#endif /* ESP_TEST_TIMEOUT */
#ifdef ESP_TEST_RESET
#endif /* ESP_TEST_RESET */
#ifdef ESP_TEST_ABORT
#endif /* ESP_TEST_ABORT */
/*
* check tagged cmds first
*/
"esp_watchsubr: slot %x: tcmds=%x, timeout=%x\n",
if (INTPENDING(esp)) {
/*
* A pending interrupt
* defers the sentence
* of death.
*/
break;
}
break;
}
int i;
IPRINTF1("pending timeout on slot=%x\n",
slot);
IPRINTF("draining all tag queues\n");
for (i = 0; i < N_SLOTS; i += d) {
NLUNS_PER_TARGET] == 0)) {
esp->e_throttle[i] =
}
}
}
} else {
/*
* reset timeouts since there aren't
* any cmds outstanding for this slot
*/
tag_slots->e_timebase = 0;
}
continue;
}
continue;
}
/*
* This command hasn't officially been started yet- drive on
*/
continue;
}
/*
* This command not to be watched- drive on
*/
continue;
}
/*
* Else, knock off the timer if any time left.
*/
if (sp->cmd_timeout > 0) {
continue;
}
/*
* No time left for this command. Last check
* before killing it.
*/
if (INTPENDING(esp)) {
/*
* A pending interrupt
* defers the sentence
* of death.
*/
break;
}
}
}
static void
int slot)
{
int i;
for (i = 0; i < N_SLOTS; i += d) {
}
}
/*
* if no interrupt pending for next second then the current
* cmd must be stuck; switch slot and sp to current slot and cmd.
* we used to call esp_dopoll() here but this causes more
* polled cmd timeout messages. We are really only interested
* in whether we are stuck or not
*/
drv_usecwait(100);
}
if ((INTPENDING(esp) == 0) &&
IPRINTF2("timeout is not slot %x but %x\n",
}
}
/*
* dump all we know about this timeout
*/
if (sp) {
"Disconnected command timeout for Target %d.%d",
} else {
"Connected command timeout for Target %d.%d",
}
} else {
"Disconnected tagged cmds (%d) timeout for Target %d.%d",
}
#ifdef ESPDEBUG
if (sp) {
auto char buf[128];
int i;
buf[0] = '\0';
for (i = 0; i < (int)sp->cmd_cdblen; i++) {
break;
}
}
if (INFORMATIVE) {
int dma_enabled = 0;
} else {
}
/*
* disable DVMA to avoid a timeout on SS1
*/
dma_enabled++;
}
} else {
}
if (dma_enabled) {
}
}
#endif /* ESPDEBUG */
/*
* Current command timeout appears to relate often to noisy SCSI
* in synchronous mode.
*/
}
if (sp) {
}
int tag;
if (sp) {
}
}
sp = 0;
}
/*
* clear reset delay to prevent a deadlock
*/
}
}
static void
int slot)
{
#ifdef ESPDEBUG
if (esp_no_sync_backoff) {
return;
}
#endif
/*
* Only process data phase hangs. Also, ignore any data phase
* hangs caused by request sense cmds as it's possible they could
* be caused by target reverting to asynch.
*
* Allow sync backoff for parity errors detected and called from
* esp_finish.
*/
IPRINTF3("esp_sync_backoff: target %d: state=%x, phase=%x\n",
IPRINTF2("Target %d.%d hang state not in data phase\n",
return;
} else if (
IPRINTF2("Target %d.%d hang bus not in data phase\n",
return;
} else if (
IPRINTF2("Target %d.%d ignoring request sense hang\n",
return;
}
}
/*
* First we reduce xfer rate 100% and always enable slow cable mode
* and if that fails we revert to async with slow cable mode
*/
#ifdef ESPDEBUG
IPRINTF4("regval %d maxreg %d backoff %d for tgt %d\n",
/*
* Compute sync transfer limits for later compensation.
*/
#endif
"Target %d.%d reverting to async. mode",
} else {
"Target %d.%d reducing sync. transfer rate",
/* increase period by 100% */
}
/*
* Paranoia: Force sync. renegotiate
*/
}
/*
* always enable slow cable mode
*/
}
}
/*
* Abort routines
*/
static int
{
} else {
return (ACTION_RETURN);
}
}
static int
{
struct scsi_address ap;
if (sp) {
}
/*
* attempting to abort a connected cmd is usually fruitless, so
* only try disconnected cmds (sp == NULL indicates a bunch of
* tagged cmds are disconnected and timed out)
* a reset is preferable over an abort (see 1161701)
*/
IPRINTF2("attempting to reset target %d.%d\n",
return (ACTION_RETURN);
}
}
/*
* if the target won't listen, then a retry is useless
* there is also the possibility that the cmd still completed while
* we were trying to reset and the target driver may have done a
* device reset which has blown away this sp.
* well, we've tried, now pull the chain
*/
IPRINTF("aborting all cmds by bus reset\n");
return (esp_abort_allcmds(esp));
}
static int
{
/*
* Last resort: Reset everything.
* wait here for the reset recovery; this makes nested error
* recovery more manageable
*/
(void) esp_reset_bus(esp);
return (ACTION_SEARCH);
}
/*
* auto request sense handling:
* for arq, we create a pkt per slot and save it in e_arq_pkt list. the
* original pkt is always saved in e_save_pkt list. Only one arq
* can be in progress at any point in time
*/
static int
{
/*
* Allocate a request sense packet using get_pktiopb
*/
int rval = 0;
if (create == 0) {
/*
* if there is still a pkt saved or no rqpkt
* then we cannot deallocate or there is nothing to do
*/
rval = -1;
}
} else {
/*
* it would be nicer if we could allow the target driver
* to specify the size but this is easier and OK for most
* drivers to use SENSE_LENGTH
* Allocate a request sense packet.
*/
/*
* if one exists, don't create another
*/
return (rval);
}
(char)SCMD_REQUEST_SENSE, 0, (char)SENSE_LENGTH);
/*
* delay callbacks; esp_call_pkt_comp() calls
* esp_complete_arq_pkt() directly without releasing the lock
*/
#ifndef __lock_lint
(void (*)(struct scsi_pkt *))esp_complete_arq_pkt;
#endif
}
return (rval);
}
/*
* complete an arq packet by copying over transport info and the actual
* request sense data; called with mutex held from esp_call_pkt_comp()
*/
static void
{
struct scsi_arq_status *arqstat;
}
/*
* now we can finally complete the original packet
*/
if (ssp) {
}
}
/*
* start an arq packet
*/
static int
{
IPRINTF("no arq packet or cannot arq on arq pkt\n");
return (-1);
}
}
return (-1);
}
/*
* copy the timeout from the original packet by lack of a better
* value
* we could take the residue of the timeout but that could cause
* premature timeouts perhaps
*/
/*
* set throttle to full throttle so the request sense
* can be submitted even if there was a queue full condition
*/
}
IPRINTF("arq packet has not been accepted\n");
return (-1);
}
return (0);
}
/*
* esp_abort: abort a current cmd or all cmds for a target
*/
static int
{
int rval;
return (rval);
}
/*
* _esp_abort() assumes that we already have the mutex
* during the abort, we hold the mutex and prevent callbacks by setting
* completion pointer to NULL. this will also avoid that a target driver
* attempts to do a scsi_abort/reset while we are aborting.
* because the completion pointer is NULL we can still update the
* packet after completion
* the throttle for this slot is cleared either by esp_abort_connected_cmd
* or esp_runpoll which prevents new cmds from starting while aborting
*/
static int
{
int abort_msg;
int abort_disconnected = 0;
short throttles[1];
/*
* If no specific command was passed, all cmds here will be aborted
* If a specific command was passed as an argument (to be aborted)
* only the specified command will be aborted
*/
IPRINTF4("esp_abort for slot %x, sp=0x%p, pkt_flags=%x, cur_sp=0x%p\n",
if (cur_sp) {
/*
* prevent completion on current cmd
*/
}
if (sp) {
IPRINTF3("aborting one command 0x%p for %d.%d\n",
if (rval) {
IPRINTF("aborted one ready cmd\n");
if (cur_sp) {
}
goto exit;
}
IPRINTF("cmd doesn't exist here\n");
if (cur_sp) {
}
goto exit;
}
/*
* the cmd exists here. is it connected or disconnected?
* if connected but still selecting then can't abort now.
* the selection may be preempted and we may then attempt
* to abort a pkt that is yet issued to the target. On
* completion of the successful proxy msg, the cmd may
* be submitted while we think it has been aborted
*
* prevent completion on this cmd
*/
/* connected but not selecting? */
}
/* disconnected? */
if ((rval == 0) &&
}
if (rval) {
}
}
} else {
IPRINTF2("aborting all commands for %d.%d\n",
/* active and not selecting ? */
}
if (rval == 0) {
}
}
/*
* complete the sp passed as 2nd arg now otherwise the
* the check_in_transport will fail because the cmd has been
* completed but not yet removed from the queues
*/
}
/*
* a proxy cmd zapped the active slot for non-tagged tgts.
* regardless whether it actually aborted, it has to be
* completed here; otherwise it is lost forever.
*/
}
}
/* complete the current sp */
if (cur_sp) {
/* is this packet still on the lists but not completed? */
if (!cur_tagged_flag ||
}
}
/*
* make sure it is not on the ready list
*/
}
}
/* clean up all cmds for this slot */
/*
* mark all commands here as aborted
* abort msg has been accepted, now cleanup queues;
*/
}
exit:
}
#ifdef ESPDEBUG
if (rval && esp_test_stop)
debug_enter("abort done");
#endif
return (rval);
}
/*
* mark all packets with new reason and update statistics
*/
static void
{
}
while (sp != 0) {
}
int n = 0;
int tag;
n++;
}
}
}
}
/*
* delete specified packet from the ready queue
*/
static int
{
/*
* command has not been started yet and is still in the ready queue
*/
return (FALSE);
}
IPRINTF3("aborting sp=0x%p %d.%d (not yet started)\n",
/*
* find packet on the ready queue and remove it
*/
} else {
}
}
return (TRUE);
}
}
return (FALSE);
}
/*
* cleanup cmds in ready queue
*/
static void
{
return;
}
while (sp != 0) {
/*
* save the forward pointer before calling the completion
* routine
*/
}
}
/*
* cleanup the tag queue
* preserve some order by starting with the oldest tag
*/
static void
{
/*
* is there a non-tagged cmd?
*/
}
return;
}
IPRINTF2("flushing entire tag queue, slot=%x, tcmds=%x\n",
#ifdef ESPDEBUG
{
int n = 0;
n++;
0) {
debug_enter("esp_flush_tagQ");
}
}
}
}
}
#endif
do {
}
}
/*
* cleanup one active command
*/
static void
{
}
}
}
/*
* decrement e_ncmds and e_disc for this cmd before completing
* during nested error recovery, our counts may get somewhat inaccurate;
* therefore, ensure that both counts remain >= 0
*/
static void
{
}
}
}
}
/*
* assert ATN to force the target to go to msg out phase.
* need to disable DVMA to avoid watchdogs and bus timeouts on sun4c
*/
static void
{
} else {
}
}
/*
* abort a connected command by sending an abort msg; hold off on
* starting new cmds by setting throttles to HOLD_THROTTLE
*/
static int
{
/*
* if reset delay active we cannot access the target
*/
return (rval);
}
IPRINTF3("Sending abort message %s to connected %d.%d\n",
/*
* now check if the msg was taken
* e_abort is set in esp_handle_msg_out_done when the abort
* msg has actually gone out (ie. msg out phase occurred
*/
IPRINTF2("target %d.%d aborted\n",
} else {
IPRINTF2("target %d.%d did not abort\n",
}
return (rval);
}
/*
* abort a disconnected command; if it is a tagged command, we need
* to include the tag
*/
static int
{
/*
* if reset delay is active, we cannot start a selection
* and there shouldn't be a cmd outstanding
*/
return (FALSE);
}
IPRINTF1("aborting disconnected tagged cmd(s) with %s\n",
scsi_mname(msg));
} else if (sp) {
} else {
return (FALSE);
}
} else {
}
}
/*
* set throttles for all luns of this target
*/
static void
{
int i;
if (n == NLUNS_PER_TARGET) {
}
} else {
}
}
}
static void
{
/*
* esp_set_throttles adjusts slot to starting at LUN0
*/
}
/*
* restore throttle unless reset delay in progress
*/
static void
{
int i;
if (n == NLUNS_PER_TARGET) {
}
} else {
}
} else {
}
}
}
/*
* save throttles
*/
static void
{
if (n == NLUNS_PER_TARGET) {
}
}
/*
* reset handling
*/
static int
{
int rval;
IPRINTF3("esp_reset: target %d.%d, level %d\n",
return (rval);
}
/*
* _esp_reset assumes that we have already entered the mutex
*/
static int
{
IPRINTF3("esp_reset for slot %x, level=%x, tcmds=%x\n",
/*
* horrible hack for esp100, see bugid 1172190
* do not reset the bus during dumping for esp100
*/
return (TRUE);
}
/*
* We know that esp_reset_bus() returns ACTION_RETURN.
*/
(void) esp_reset_bus(esp);
/*
* Now call esp_dopoll() to field the reset interrupt
* which will then call esp_reset_recovery which will
* call the completion function for all commands.
*/
/*
* reset esp
*/
(void) esp_reset_bus(esp);
CE_WARN, "reset scsi bus failed");
} else {
}
} else {
}
} else {
/*
* if reset delay active we cannot access the target
*/
return (rval);
}
/*
* zero pkt_comp so it won't complete during the reset and
* we can still update the packet after the reset.
*/
if (cur_sp) {
}
/*
* is this a connected cmd but not selecting?
*/
}
/*
* if not connected or esp_reset_connected_cmd() failed,
* attempt a reset_disconnected_cmd
* NOTE: a proxy cmd zaps the currently disconnected
* non-tagged cmd; also this could cause a failed reselection
* if the target reselects just after zapping
*/
cur_sp));
}
}
/*
* a proxy cmd zapped the active slot for non-tagged tgts.
*
* regardless whether the devices was actually reset,
* it has to be completed here; otherwise it is lost forever.
* tagged cmds will just timeout eventually
*
* don't set completed for cmds that are in arq.
*/
}
/* blow everything away */
}
/*
* cleanup if reset was successful
* complete the current sp first.
* unless it is currently in auto request sense
*/
if (cur_sp) {
/*
* the packet shouldn't be on readyQ but
* just in case, check for it
*/
}
}
if (cur_sp) {
}
} else {
}
NEW_CMD);
}
}
exit:
#ifdef ESPDEBUG
if (rval && esp_test_stop)
debug_enter("reset done");
#endif
return (rval);
}
/*
* reset delay is now handled by a separate watchdog; this ensures that
* regardless of esp_scsi_watchdog_tick, the reset delay will not change
*/
static void
{
if ((esp_reset_watch == 0) && ESP_CAN_SCHED) {
}
}
/*ARGSUSED*/
static void
esp_watch_reset_delay(void *arg)
{
int not_done;
esp_reset_watch = 0;
continue;
}
if (not_done) {
EPRINTF("\trestart watch reset delay\n");
} else {
EPRINTF("\tno more reset delay watching\n");
}
}
}
static int
{
short slot, s;
int start_slot = -1;
int done = 0;
/*
* check if a reset delay is active; if so clear throttle
* which will unleash the cmds in the ready Q
*/
s = slot/NLUNS_PER_TARGET;
if (esp->e_reset_delay[s] != 0) {
EPRINTF2("target%d: reset delay=%d\n", s,
esp->e_reset_delay[s]);
if (esp->e_reset_delay[s] <= 0) {
/*
* clear throttle for all luns on this target
*/
esp->e_reset_delay[s] = 0;
IPRINTF1("reset delay completed, slot=%x\n",
slot);
if (start_slot == -1) {
start_slot = slot;
}
} else {
done = -1;
}
}
}
/*
* start a cmd if a reset delay expired
*/
}
return (done);
}
static void
{
/*
* reset msg has been accepted, now cleanup queues;
* for all luns of this target
*/
IPRINTF4("esp_reset_cleanup: slot %x, start=%x, end=%x, tcmds=%x\n",
/*
* if the watchdog is running, set up a reset delay for this target
* a throttle of HOLD_THROTTLE forces all new requests into the ready Q
* if the watchdog is not running then delay here
*/
if (esp_watchdog_running && !panicstr) {
} else {
}
esp_flush_tagQ(esp, i);
esp_flush_readyQ(esp, i);
}
}
/*
* reset a currently disconnected target
*/
static int
{
/*
* if reset delay active we cannot access the target
*/
return (FALSE);
}
}
/*
* reset a target with a currently connected command
* Assert ATN and send MSG_DEVICE_RESET, clear throttles temporarily
* to prevent new cmds from starting regardless of the outcome
*/
static int
{
/*
* if reset delay active we cannot access the target
*/
return (rval);
}
IPRINTF2("Sending reset message to connected %d.%d\n",
/*
* now check if the msg was taken
* e_reset is set in esp_handle_msg_out_done when
* msg has actually gone out (ie. msg out phase occurred)
*/
} else {
IPRINTF2("target %d.%d did not reset\n",
}
return (rval);
}
/*
* error handling, reset and abort stuff
*/
static int
{
IPRINTF("esp_reset_bus:\n");
/*
* Now that we've reset the SCSI bus, we'll take a SCSI RESET
* interrupt and use that to clean up the state of things.
*/
return (ACTION_RETURN);
}
static int
{
short slot, start_slot;
int i;
IPRINTF("esp_reset_recovery:\n");
/*
* this reset was not expected, so probably external reset
*/
IPRINTF("external reset recovery\n");
if (esp_watchdog_running && !panicstr) {
int i;
for (i = 0; i < N_SLOTS; i++) {
}
for (i = 0; i < NTARGETS; i++) {
esp->e_reset_delay[i] =
}
} else {
}
}
}
/*
* Renegotiate sync immediately on next command
*/
esp->e_sync_known = 0;
/*
* Flush DMA, clear interrupts until they go away, and clear fifo
*/
while (INTPENDING(esp)) {
}
return (ACTION_RETURN);
}
start_slot = 0;
}
/*
* for right now just claim that all
* commands have been destroyed by a SCSI reset
* and let already set reason fields or callers
* decide otherwise for specific commands.
*
* We're blowing it all away. Remove any dead wood to the
* side so that completion routines don't get confused.
*/
for (i = 0; i < N_SLOTS; i++) {
}
}
/*
* Call this routine to completely reset the state of the softc data.
*/
/*
* Hold the state of the host adapter open
*/
slot = start_slot;
do {
}
} while (slot != start_slot);
/*
* Move the state back to free...
*/
/*
* there might be cmds in the ready list again because
* for immediate callback cmds, the mutex has been released
* Therefore, do not check on ncdms == 0
*/
/*
* perform the reset notification callbacks that are registered.
*/
return (ACTION_RETURN);
}
/*
* routine for reset notification setup, to register or cancel.
*/
static int
{
}
/*
* torture test functions
*/
#ifdef ESP_TEST_RESET
static void
{
struct scsi_address ap;
if ((esp_rtest_type == 1) &&
esp_rtest = 0;
}
} else if ((esp_rtest_type == 2) &&
esp_rtest = 0;
}
} else {
esp_rtest = 0;
}
}
}
}
#endif
#ifdef ESP_TEST_ABORT
static void
{
struct scsi_address ap;
if ((esp_atest_disc == 0) && sp &&
int tag;
/*
* find the oldest tag
*/
break;
}
if (sp) {
}
} else if (esp_atest_disc == 4 &&
} else if ((esp_atest_disc == 6) &&
} else if (esp_atest_disc == 7) {
esp_atest = 0;
return;
} else {
}
return;
}
}
esp_atest = 0;
}
}
}
#endif
/*
* capability interface
*/
static int
{
int cidx;
if (cap == (char *)0) {
goto exit;
}
if (cidx == -1) {
} else if (doset) {
switch (cidx) {
case SCSI_CAP_DMA_MAX:
case SCSI_CAP_MSG_OUT:
case SCSI_CAP_PARITY:
case SCSI_CAP_INITIATOR_ID:
case SCSI_CAP_LINKED_CMDS:
case SCSI_CAP_UNTAGGED_QING:
/*
* None of these are settable via
* the capability interface.
*/
break;
case SCSI_CAP_DISCONNECT:
SCSI_OPTIONS_DR) == 0) {
break;
} else if (tgtonly) {
if (val)
else
} else {
}
break;
case SCSI_CAP_SYNCHRONOUS:
SCSI_OPTIONS_SYNC) == 0) {
break;
} else if (tgtonly) {
"target %d.%d: can't set sync cap!\n",
break;
}
if (val) {
esp->e_force_async &=
} else {
esp->e_force_async |=
}
} else {
"can't set sync cap!\n");
break;
}
esp->e_sync_known = 0;
}
break;
case SCSI_CAP_TAGGED_QING:
SCSI_OPTIONS_DR) == 0) ||
SCSI_OPTIONS_TAG) == 0) ||
break;
} else if (tgtonly) {
if (val) {
/*
* allocate the tagQ area later
*/
IPRINTF1("target %d: TQ enabled\n",
} else {
IPRINTF1("target %d: TQ disabled\n",
target);
/*
* free all tagQ space
*/
size);
}
}
}
} else {
}
/*
* update TQ properties
*/
if (tgtonly) {
} else {
int i;
for (i = 0; i < NTARGETS; i++) {
}
}
break;
case SCSI_CAP_ARQ:
}
break;
case SCSI_CAP_QFULL_RETRIES:
if (tgtonly) {
} else {
int i;
for (i = 0; i < NTARGETS; i++) {
}
}
break;
if (tgtonly) {
} else {
int i;
for (i = 0; i < NTARGETS; i++) {
esp->e_qfull_retry_interval[i] =
}
}
break;
default:
break;
}
} else if (doset == 0) {
switch (cidx) {
case SCSI_CAP_DMA_MAX:
/*
* very high limit because of multiple dma windows
* The return value can not be 0xFFFFFFFF (-1)
* as it is the value returned for error.
*/
break;
case SCSI_CAP_MSG_OUT:
break;
case SCSI_CAP_DISCONNECT:
SCSI_OPTIONS_DR) &&
}
break;
case SCSI_CAP_SYNCHRONOUS:
}
break;
case SCSI_CAP_PARITY:
break;
case SCSI_CAP_INITIATOR_ID:
break;
case SCSI_CAP_TAGGED_QING:
SCSI_OPTIONS_DR) == 0) ||
SCSI_OPTIONS_TAG) == 0) ||
break;
break;
}
break;
case SCSI_CAP_UNTAGGED_QING:
break;
case SCSI_CAP_ARQ:
{
}
}
break;
case SCSI_CAP_LINKED_CMDS:
break;
break;
case SCSI_CAP_QFULL_RETRIES:
break;
rval = drv_hztousec(
1000;
break;
default:
break;
}
}
exit:
if (doset) {
"esp_commoncap:tgt=%x,cap=%s,tgtonly=%x,doset=%x,val=%x,rval=%x\n",
}
return (rval);
}
static int
{
}
static int
{
}
static void
{
static char *prop_template = "target%d-TQ";
char property[32];
if (value == 0) {
IPRINTF1("cannot remove %s property\n",
property);
}
}
} else if (value) {
/*
* create a boolean property (not supported with the
* new property interfaces)
*/
NULL, 0) != DDI_PROP_SUCCESS) {
}
}
}
/*
* Error logging, printing, and debug print routines
*/
static char *esp_label = "esp";
/*PRINTFLIKE3*/
static void
{
if (esp) {
} else {
dev = 0;
}
}
/*PRINTFLIKE2*/
static void
{
if (esp) {
} else {
dev = 0;
}
#ifdef ESPDEBUG
#else
{
char label[32];
if (dev) {
} else {
}
}
#endif
}
static char *esp_int_bits = ESP_INT_BITS;
static char *esp_stat_bits = ESP_STAT_BITS;
#ifdef ESPDEBUG
static void
{
}
#endif /* ESPDEBUG */
static void
{
/*
* disable DVMA to avoid a timeout on SS1
*/
} else {
}
"\tlast msg out: %s; last msg in: %s\n",
}
#ifdef ESPDEBUG
if (espdebug)
#endif /* ESPDEBUG */
}
#ifdef ESPDEBUG
static void
{
short x, z;
auto char buf[128];
z = esp->e_phase_index;
for (x = 1; x <= NPHASE; x++) {
short y;
if (y == STATE_FREE)
break;
y, esp_state_name((ushort_t)y));
}
}
}
}
#endif /* ESPDEBUG */
static void
{
int i;
auto char buf[128];
buf[0] = '\0';
for (i = 0; i < (int)sp->cmd_cdblen; i++) {
}
} else {
}
"\tpkt_state=0x%b pkt_flags=0x%x pkt_statistics=0x%x\n",
sp->cmd_timeout);
}
static char *
{
if (state == STATE_FREE) {
return ("FREE");
} else if ((state & STATE_SELECTING) &&
if (state == STATE_SELECT_NORMAL)
return ("SELECT");
else if (state == STATE_SELECT_N_STOP)
return ("SEL&STOP");
else if (state == STATE_SELECT_N_SENDMSG)
return ("SELECT_SNDMSG");
else if (state == STATE_SELECT_N_TAG)
return ("STATE_SELECT_N_TAG");
else
return ("SEL_NO_ATN");
} else {
static struct {
char *sname;
char state;
} names[] = {
"CMD_START", ACTS_CMD_START,
"CMD_DONE", ACTS_CMD_DONE,
"MSG_OUT", ACTS_MSG_OUT,
"MSG_OUT_DONE", ACTS_MSG_OUT_DONE,
"MSG_IN", ACTS_MSG_IN,
"MSG_IN_MORE", ACTS_MSG_IN_MORE,
"MSG_IN_DONE", ACTS_MSG_IN_DONE,
"CLEARING", ACTS_CLEARING,
"DATA", ACTS_DATA,
"DATA_DONE", ACTS_DATA_DONE,
"CMD_CMPLT", ACTS_C_CMPLT,
"UNKNOWN", ACTS_UNKNOWN,
"RESEL", ACTS_RESEL,
"ENDVEC", ACTS_ENDVEC,
"RESET", ACTS_RESET,
"ABORTING", ACTS_ABORTING,
"SPANNING", ACTS_SPANNING,
"FROZEN", ACTS_FROZEN,
"PREEMPTED", ACTS_PREEMPTED,
"PROXY", ACTS_PROXY,
"SYNCHOUT", ACTS_SYNCHOUT,
"CMD_LOST", ACTS_CMD_LOST,
"DATAOUT", ACTS_DATAOUT,
"DATAIN", ACTS_DATAIN,
"STATUS", ACTS_STATUS,
"DISCONNECT", ACTS_DISCONNECT,
"NOP", ACTS_NOP,
"REJECT", ACTS_REJECT,
"RESTOREDP", ACTS_RESTOREDP,
"SAVEDP", ACTS_SAVEDP,
"BAD_RESEL", ACTS_BAD_RESEL,
"LOG", ACTS_LOG,
"TAG", ACTS_TAG,
"CMD", ACTS_CMD,
"SELECT", ACTS_SELECT,
0
};
int i;
}
}
return ("<BAD>");
}