/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2011 Bayard G. Bell. All rights reserved.
*/
/*
* ISSUES
*
* - more consistent error messages
* - report name of device on errors?
* - if wide target renegotiates sync, back to narrow?
* - last_msgout is not accurate ????
* - resolve XXXX
* - improve msg reject code (use special msg reject handler)
* - better use of IDE message
* - keep track if ATN remains asserted and target not going into
* a msg-out phase
* - improve comments
* - no slave accesses when start address is odd and dma hasn't started
* this affect asserting ATN
*/
/*
* tagged and non-tagged queueing support
*/
#define FASDEBUG
#endif
/*
* standard header files
*/
/*
* private header files
*/
/*
* tunables
*/
/*
* needed for presto support, do not remove
*/
#ifdef FASDEBUG
int fasdebug = 0;
static int fas_no_sync_wide_backoff = 0;
#endif /* FASDEBUG */
/*
* Local static data protected by global mutex
*/
/* instances */
static int fas_timeout_initted = 0;
/*
* dma attribute structure for scsi engine
*/
DMA_ATTR_V0, (unsigned long long)0,
(unsigned long long)0xffffffff, (unsigned long long)((1<<24)-1),
(unsigned long long)0xffffffff, (unsigned long long)0xffffffff,
1, 512, 0
};
/*
* optional torture test stuff
*/
#ifdef FASDEBUG
#define FAS_TEST
static int fas_ptest_emsgin;
static int fas_ptest_msgin;
static int fas_ptest_status;
static int fas_ptest_data_in;
static int fas_atest;
static int fas_atest_disc;
static int fas_atest_reconn;
static int fas_rtest;
static int fas_rtest_type;
static int fas_force_timeout;
static int fas_btest;
static int fas_test_stop;
static int fas_transport_busy;
static int fas_transport_busy_rqs;
static int fas_transport_reject;
static int fas_arqs_failure;
static int fas_tran_err;
static int fas_test_untagged;
static int fas_enable_untagged;
#endif
/*
* warlock directives
*/
/*
* function prototypes
*
* scsa functions are exported by means of the transport table:
*/
int (*waitfunc)(void));
scsi_hba_tran_t *, struct scsi_device *);
int whom);
/*
* internal functions:
*/
static void fas_restart_cmd(void *);
int width);
int slot);
/*PRINTFLIKE3*/
/*PRINTFLIKE2*/
int slot);
static void fas_start_watch_reset_delay(struct fas *);
static void fas_watch_reset_delay(void *arg);
int n, int what);
static void fas_ncmds_checkdrain(void *arg);
#ifdef FASDEBUG
#else
#endif
/*
* autoconfiguration data and routines.
*/
DEVO_REV, /* devo_rev, */
0, /* refcnt */
ddi_no_info, /* info */
nulldev, /* identify */
nulldev, /* probe */
fas_attach, /* attach */
fas_detach, /* detach */
nodev, /* reset */
NULL, /* driver operations */
NULL, /* bus operations */
NULL, /* power */
ddi_quiesce_not_supported, /* devo_quiesce */
};
&mod_driverops, /* Type of module. This one is a driver */
"FAS SCSI HBA Driver", /* Name of the module. */
&fas_ops, /* driver ops */
};
};
int
_init(void)
{
int rval;
/* CONSTCOND */
if (rval != 0) {
return (rval);
}
return (rval);
}
return (rval);
}
return (rval);
}
int
_fini(void)
{
int rval;
/* CONSTCOND */
}
return (rval);
}
int
{
/* CONSTCOND */
}
static int
int (*waitfunc)(void))
{
/*
* 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,
}
}
IPRINTF2("target%x-scsi-options= 0x%x\n",
return (rval);
}
/*ARGSUSED*/
static int
{
}
/*ARGSUSED*/
static int
{
char intr_added = 0;
char mutex_init_done = 0;
char hba_attached = 0;
char bound_handle = 0;
/* CONSTCOND */
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_FAILURE);
if (!fas) {
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) fas_reset_bus(fas);
fas->f_suspended = 0;
/* make sure that things get started */
(void) fas_istart(fas);
if (fas_timeout_id == 0) {
fas_timeout_initted = 1;
}
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.
*/
"fas%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.
*/
"fas%d: Device is using a hilevel intr", instance);
return (DDI_FAILURE);
}
/*
* Allocate softc information.
*/
"fas%d: cannot allocate soft state", instance);
goto fail;
}
goto fail;
}
/*
* map in device registers
*/
goto fail;
}
"fas%d: unable to map fas366 registers", instance);
goto fail;
}
"fas%d: cannot alloc dma handle", instance);
goto fail;
}
/*
* allocate cmdarea and its dma handle
*/
"fas%d: cannot alloc cmd area", instance);
goto fail;
}
"fas%d: cannot bind cmdarea", instance);
goto fail;
}
bound_handle++;
/*
* Allocate a transport structure
*/
/* Indicate that we are 'sizeof (scsi_*(9S))' clean. */
/*
* initialize transport structure
*/
fas->f_force_async = 0;
/*
* disable tagged queuing and wide for all targets
* (will be enabled by target driver if required)
* sync is enabled by default
*/
/*
* By default we assume embedded devices and save time
* checking for timeouts in fas_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 fas_watch() and fas_ustart().
*/
/*
* f_active is used for saving disconnected cmds;
* For tagged targets, we need to increase the size later
* Only allocate for Lun == 0, if we probe a lun > 0 then
* we allocate an active structure
* If TQ gets enabled then we need to increase the size
* to hold 256 cmds
*/
}
/*
* initialize the qfull retry counts
*/
for (i = 0; i < NTARGETS_WIDE; i++) {
fas->f_qfull_retry_interval[i] =
}
/*
* Initialize throttles.
*/
/*
* Initialize mask of deferred property updates
*/
fas->f_props_update = 0;
/*
* set host ID
*/
if (id == -1) {
"scsi-initiator-id", -1);
}
}
/*
* find the burstsize and reduce ours if necessary
*/
#ifdef FASDEBUG
#endif
/*
* Attach this instance of the hba
*/
DDI_SUCCESS) {
goto fail;
}
hba_attached++;
/*
* if scsi-options property exists, use it
*/
/*
* if scsi-selection-timeout property exists, use it
*/
/*
* if hm-rev property doesn't exist, use old scheme for rev
*/
"hm-rev", -1);
"obsolete rev 2.0 FEPS chip, "
"possible data corruption");
} else {
"obsolete and unsupported rev 1.0 FEPS chip");
goto fail;
}
} else if (hm_rev == 0x20) {
} else {
}
}
}
/*
* if target<n>-scsi-options property exists, use it;
* otherwise use the f_scsi_options
*/
for (i = 0; i < NTARGETS_WIDE; i++) {
i, fas->f_target_scsi_options[i]);
} else {
}
if (((fas->f_target_scsi_options[i] &
SCSI_OPTIONS_DR) == 0) &&
"Disabled TQ since disconnects are disabled");
}
}
if (fas->f_scsi_reset_delay == 0) {
"scsi_reset_delay of 0 is not recommended,"
" resetting to SCSI_DEFAULT_RESET_DELAY\n");
}
/*
* get iblock cookie and initialize mutexes
*/
!= DDI_SUCCESS) {
goto fail;
}
/*
* initialize mutex for waitQ
*/
/*
* initialize callback mechanism (immediate callback)
*/
if (fas_init_callbacks(fas)) {
goto fail;
}
/*
* kstat_intr support
*/
if (fas->f_intr_kstat)
/*
* install interrupt handler
*/
goto fail;
}
intr_added++;
/*
* initialize fas chip
*/
goto fail;
}
/*
* create kmem cache for packets
*/
EXTCMD_SIZE, 8,
goto fail;
}
/*
* at this point, we are not going to fail the attach
* so there is no need to undo the rest:
*
* add this fas to the list, this makes debugging easier
* and fas_watch() needs it to walk thru all fas's
*/
} else {
}
/*
* there is one watchdog handler for all driver instances.
* start the watchdog if it hasn't been done yet
*/
if (fas_scsi_watchdog_tick == 0) {
if (fas_scsi_watchdog_tick != DEFAULT_WD_TICK) {
}
fas_scsi_watchdog_tick * 1000000);
IPRINTF2("fas scsi watchdog tick=%x, fas_tick=%lx\n",
if (fas_timeout_id == 0) {
fas_timeout_initted = 1;
}
}
return (DDI_SUCCESS);
fail:
if (fas) {
if (active) {
}
}
if (mutex_init_done) {
}
if (intr_added) {
}
/*
* kstat_intr support
*/
if (fas->f_intr_kstat) {
}
if (hba_attached) {
(void) scsi_hba_detach(dip);
}
if (tran) {
}
if (fas->f_kmem_cache) {
}
if (bound_handle) {
}
}
if (fas->f_dmahandle) {
}
if (fas->f_regs_acc_handle) {
}
if (fas->f_dmar_acc_handle) {
}
}
return (DDI_FAILURE);
}
/*ARGSUSED*/
static int
{
/* CONSTCOND */
switch (cmd) {
case DDI_DETACH:
return (fas_dr_detach(dip));
case DDI_SUSPEND:
return (DDI_FAILURE);
if (!fas) {
return (DDI_FAILURE);
}
(void) fas_reset_bus(fas);
}
/*
* disable dma and fas interrupt
*/
if (fas->f_quiesce_timeid) {
fas->f_quiesce_timeid = 0;
}
if (fas->f_restart_cmd_timeid) {
fas->f_restart_cmd_timeid = 0;
}
/* Last fas? */
if (!nfas->f_suspended) {
return (DDI_SUCCESS);
}
}
if (fas_timeout_id != 0) {
fas_timeout_id = 0;
fas_timeout_initted = 0;
} else {
}
if (fas_reset_watch) {
fas_reset_watch = 0;
} else {
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/* NOTREACHED */
}
static int
{
short slot;
int i, j;
return (DDI_FAILURE);
if (!fas) {
return (DDI_FAILURE);
}
/*
* disable interrupts
*/
/*
* 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);
}
}
fas_tail = f;
if (fas->f_intr_kstat)
/*
* destroy any outstanding tagged command info
*/
if (active) {
if (sp) {
if (pkt) {
(void) fas_scsi_destroy_pkt(
}
/* sp freed in fas_scsi_destroy_pkt */
}
}
}
}
/*
* disallow timeout thread rescheduling
*/
if (fas->f_quiesce_timeid) {
}
/*
* last fas? ... if active, CANCEL watch threads.
*/
if (fas_timeout_initted) {
fas_timeout_initted = 0;
fas_timeout_id = 0; /* don't resched */
}
if (fas_reset_watch) {
(void) untimeout(fas_reset_watch);
fas_reset_watch = 0;
}
}
if (fas->f_restart_cmd_timeid) {
fas->f_restart_cmd_timeid = 0;
}
/*
* destroy outstanding ARQ pkts
*/
for (i = 0; i < NTARGETS_WIDE; i++) {
for (j = 0; j < NLUNS_PER_TARGET; j++) {
}
}
}
/*
* Remove device MT locks and CV
*/
/*
* Release miscellaneous device resources
*/
if (fas->f_kmem_cache) {
}
}
}
if (fas->f_regs_acc_handle) {
}
if (fas->f_dmar_acc_handle) {
}
/*
* 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(dip);
/*
* Free the scsi_transport structure for this device.
*/
return (DDI_SUCCESS);
}
static int
{
IPRINTF("fas_quiesce: QUIESCEing\n");
IPRINTF3("fas_quiesce: ncmds (%d) ndisc (%d) state (%d)\n",
if (fas_check_outstanding(fas)) {
/*
* quiesce has been interrupted.
*/
IPRINTF("fas_quiesce: abort QUIESCE\n");
(void) fas_istart(fas);
if (fas->f_quiesce_timeid != 0) {
#ifndef __lock_lint /* warlock complains but there is a NOTE on this */
fas->f_quiesce_timeid = 0;
#endif
return (-1);
}
return (-1);
} else {
IPRINTF("fas_quiesce: bus is QUIESCED\n");
return (0);
}
}
IPRINTF("fas_quiesce: bus was not busy QUIESCED\n");
return (0);
}
static int
{
(void) fas_istart(fas);
IPRINTF("fas_quiesce: bus has been UNQUIESCED\n");
return (0);
}
/*
* invoked from timeout() to check the number of outstanding commands
*/
static void
{
IPRINTF3("fas_checkdrain: ncmds (%d) ndisc (%d) state (%d)\n",
fas->f_quiesce_timeid = 0;
if (fas_check_outstanding(fas) == 0) {
IPRINTF("fas_drain: bus has drained\n");
} else {
/*
* throttle may have been reset by a bus reset
* or fas_runpoll()
* XXX shouldn't be necessary
*/
IPRINTF("fas_drain: rescheduling timeout\n");
}
}
}
static int
{
int ncmds = 0;
return (ncmds);
}
#ifdef FASDEBUG
/*
*/
static void
{
fas->f_reg_trace_index = 0;
}
}
static void
{
fas->f_reg_cmds++;
}
static void
{
*p = what;
fas->f_reg_writes++;
}
static uint8_t
{
what = *p;
fas->f_reg_reads++;
return (what);
}
/*
* dma register access routines
*/
static void
{
*p = what;
fas->f_reg_dma_writes++;
#ifdef DMA_REG_TRACING
{
}
#endif
}
static uint32_t
{
fas->f_reg_dma_reads++;
#ifdef DMA_REG_TRACING
{
}
#endif
return (what);
}
#endif
#ifdef FASDEBUG
static void
{
#ifdef FAS_TEST
if (fas_test_stop > 1)
debug_enter("asserted atn");
#endif
}
#else
#endif
/*
* DMA macros; we use a shadow copy of the dma_csr to save unnecessary
* reads
*/
}
}
}
static void
{
}
/*
* FAS_FLUSH_DMA_HARD checks on REQPEND before taking away the reset
*/
static void
{
;
}
/*
* update period, conf3, offset reg, if necessary
*/
{ \
} \
}
/*
* always read the fifo bytes before reading the interrupt register
*/
static void
{
int i;
while (i-- > 0) {
&fasreg->fas_fifo_data);
&fasreg->fas_fifo_data);
}
/* write pad byte */
&fasreg->fas_fifo_data);
/* flush pad byte */
}
EPRINTF2("fas_read_fifo: fifo len=%x, stat2=%x\n",
} /* fas_read_fifo */
static void
{
int i;
for (i = 0; i < length; i++) {
if (pad) {
}
}
}
/*
* Hardware and Software internal reset routines
*/
static int
{
int i;
/*
* Determine clock frequency of attached FAS chip.
*/
if (clock_conv != CLOCK_40MHZ) {
return (-1);
}
DPRINTF5("%d mhz, clock_conv %d, clock_cycle %d, ticks %d, stval %d\n",
/*
* set up conf registers
*/
if (initiator_id < NTARGETS) {
} else {
}
for (i = 0; i < NTARGETS_WIDE; i++) {
}
/*
* Avoid resetting the scsi bus since this causes a few seconds
* delay per fas in boot and also causes busy conditions in some
* tape devices.
*/
/*
* initialize period and offset for each target
*/
for (i = 0; i < NTARGETS_WIDE; i++) {
} else {
}
fas->f_neg_period[i] =
} else {
fas->f_neg_period[i] =
}
}
return (0);
}
/*
* reset bus, chip, dma, or soft state
*/
static void
{
if (reset_action & FAS_RESET_SCSIBUS) {
}
/*
* NOTE: if dma is aborted while active, indefinite hangs
* may occur; it is preferable to stop the target first before
* flushing the dma
*/
if (reset_action & FAS_RESET_DMA) {
if (burstsizes & BURST64) {
IPRINTF("64 byte burstsize\n");
} else if (burstsizes & BURST32) {
IPRINTF("32 byte burstsize\n");
} else {
IPRINTF("16 byte burstsize\n");
}
DDI_SUCCESS)) {
IPRINTF("enabled 64 bit sbus\n");
}
}
if (reset_action & FAS_RESET_FAS) {
/*
* 2 NOPs with DMA are required here
* id_code is unreliable if we don't do this)
*/
int dmarev;
/*
* Re-load chip configurations
* Only load registers which are not loaded in fas_startcmd()
*/
/*
* enable default configurations
*/
IPRINTF2("Family code %d, revision %d\n",
/*
* Just in case... clear interrupt
*/
}
if (reset_action & FAS_RESET_SOFTC) {
fas->f_next_slot = 0;
}
}
#ifdef FASDEBUG
/*
* check if ncmds still reflects the truth
* count all cmds for this driver instance and compare with ncmds
*/
static void
{
int slot = 0;
int n, total = 0;
do {
while (sp != 0) {
total++;
}
n++;
total++;
}
}
}
} while (slot != 0);
IPRINTF2("fas_check_ncmds: total=%x, ncmds=%x\n",
}
}
#else
#endif
/*
* SCSA Interface functions
*
* Visible to the external world via the transport structure.
*
* fas_scsi_abort: abort a current cmd or all cmds for a target
*/
/*ARGSUSED*/
static int
{
int rval;
return (rval);
}
/*
* reset handling: reset bus or target
*/
/*ARGSUSED*/
static int
{
int rval;
IPRINTF3("fas_scsi_reset: target %d.%d, level %d\n",
return (rval);
}
/*
* entry point for reset notification setup, to register or to cancel.
*/
static int
{
}
/*
* capability interface
*/
/*ARGSUSED*/
static int
{
DPRINTF3("fas_scsi_getcap: tgt=%x, cap=%s, whom=%x\n",
}
/*ARGSUSED*/
static int
{
IPRINTF4("fas_scsi_setcap: tgt=%x, cap=%s, value=%x, whom=%x\n",
}
/*
* pkt and dma allocation and deallocation
*/
/*ARGSUSED*/
static void
{
"fas_scsi_dmafree_start");
/*
* Free the mapping.
*/
}
"fas_scsi_dmafree_end");
}
/*ARGSUSED*/
static void
{
DDI_SUCCESS) {
"sync of pkt (%p) failed", (void *)pkt);
}
}
}
/*
* initialize pkt and allocate DVMA resources
*/
static struct scsi_pkt *
{
int kf;
int rval;
/* #define FAS_TEST_EXTRN_ALLOC */
#ifdef FAS_TEST_EXTRN_ALLOC
#endif
/*
* if no pkt was passed then allocate a pkt first
*/
"fas_scsi_impl_pktalloc_start");
/*
* only one size of pkt (with arq).
*/
if (cmd) {
sizeof (struct fas_cmd));
failure = 0;
}
(tgtlen > PKT_PRIV_LEN) ||
(statuslen > EXTCMDS_STATUS_SIZE)) {
if (failure == 0) {
/*
* if extern alloc fails, all will be
* deallocated, including cmd
*/
}
if (failure) {
/*
* nothing to deallocate so just return
*/
"fas_scsi_impl_pktalloc_end");
return (NULL);
}
}
"fas_scsi_impl_pktalloc_end");
} else {
}
/*
* Second step of fas_scsi_init_pkt:
* bind the buf to the handle
*/
"fas_scsi_impl_dmaget_start");
cmd_flags &= ~CFLAG_DMASEND;
} else {
}
if (flags & PKT_CONSISTENT) {
}
/*
* bind the handle to the buf
*/
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) {
}
"fas_scsi_impl_dmaget_end");
}
"fas_scsi_impl_dmaget_end");
}
return (pkt);
}
/*
* unbind dma resources and deallocate the pkt
*/
static void
{
/*
* fas_scsi_impl_dmafree inline to speed things up
*/
"fas_scsi_impl_dmafree_start");
/*
* Free the mapping.
*/
}
"fas_scsi_impl_dmafree_end");
"fas_scsi_impl_pktfree_start");
CFLAG_SCBEXTERN)) == 0) {
} else {
}
"fas_scsi_impl_pktfree_end");
}
/*
* allocate and deallocate external pkt space (ie. not part of fas_cmd) for
* non-standard length cdb, pkt_private, status areas
* if allocation fails, then deallocate all external space and the pkt
*/
/* 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);
}
/*
* deallocate external pkt space and deallocate the pkt
*/
static void
{
panic("fas_pkt_destroy_extern: freeing free packet");
/* NOTREACHED */
}
}
}
}
}
/*
* kmem cache constructor and destructor:
* When constructing, we bzero the cmd and allocate the dma handle
* When destructing, just free the dma handle
*/
static int
{
/*
* allocate a dma handle
*/
return (-1);
}
return (0);
}
/*ARGSUSED*/
static void
{
if (cmd->cmd_dmahandle) {
}
}
/*
* fas_scsi_start - Accept commands for transport
*/
static int
{
int rval;
int intr = 0;
#ifdef FAS_TEST
if (fas_transport_busy > 0) {
return (TRAN_BUSY);
}
if ((fas_transport_busy_rqs > 0) &&
return (TRAN_BUSY);
}
if (fas_transport_reject > 0) {
return (TRAN_BADPKT);
}
#endif
/*
* prepare packet before taking the mutex
*/
if (rval != TRAN_ACCEPT) {
"fas_scsi_start_end (prepare_pkt)");
return (rval);
}
/*
* fas mutex can be held for a long time; therefore, if the mutex is
* held, we queue the packet in a waitQ; we now should check
* the waitQ on every mutex_exit(FAS_MUTEX(fas)) but we really only
* need to do this when the bus is free
* don't put NOINTR cmds including proxy cmds in waitQ! These
* cmds are handled by fas_runpoll()
* if the waitQ is non-empty, queue the pkt anyway to preserve
* order
* the goal is to queue in waitQ as much as possible so at
* interrupt time, we can move the packets to readyQ or start
* a packet immediately. It helps to do this at interrupt
* time because we can then field more interrupts
*/
/*
* if the bus is not free, we will get an interrupt shortly
* so we don't want to take the fas mutex but queue up
* the packet in the waitQ
* also, if the waitQ is non-empty or there is an interrupt
* pending then queue up the packet in the waitQ and let the
* interrupt handler empty the waitQ
*/
goto queue_in_waitQ;
}
/*
* we didn't queue up in the waitQ, so now try to accept
* the packet. if we fail to get the fas mutex, go back to
* the waitQ again
* do not release the waitQ mutex yet because that
* leaves a window where the interrupt handler has
* emptied the waitQ but not released the fas mutex yet
*
* the interrupt handler gets the locks in opposite order
* but because we do a tryenter, there is no deadlock
*
* if another thread has the fas mutex then either this
* thread or the other may find the bus free and
* empty the waitQ
*/
} else {
/*
* we didn't get the fas mutex so
* the packet has to go in the waitQ now
*/
goto queue_in_waitQ;
}
} else {
/*
* for polled cmds, we have to take the mutex and
* start the packet using fas_runpoll()
*/
}
/*
* if the bus is free then empty waitQ and release the mutex
* (this should be unlikely that the bus is still free after
* accepting the packet. it may be the relatively unusual case
* that we are throttling)
*/
} else {
}
done:
"fas_scsi_start_end: fas 0x%p", fas);
return (rval);
} else {
}
/*
* check again the fas mutex
* if there was an interrupt then the interrupt
* handler will eventually empty the waitQ
*/
/*
* double check if the bus is still free
* (this actually reduced mutex contention a bit)
*/
}
}
"fas_scsi_start_end: fas 0x%p", fas);
return (rval);
}
/*
* prepare the pkt:
* the pkt may have been resubmitted or just reused so
* initialize some fields, reset the dma window, and do some checks
*/
static int
{
/*
* Reinitialize some fields that need it; the packet may
* have been resubmitted
*/
pkt->pkt_statistics = 0;
/*
* Copy the cdb pointer to the pkt wrapper area as we
* might modify this pointer. Zero status byte
*/
/*
* if the pkt was resubmitted then the
* windows 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
*/
/*
* consistent packets need to be sync'ed first
* (only for data going out)
*/
(CFLAG_CMDIOPB | CFLAG_DMASEND)) {
}
}
#ifdef FAS_TEST
#ifndef __lock_lint
if (fas_test_untagged > 0) {
"starting untagged cmd, target=%d,"
" tcmds=%d, sp=0x%p, throttle=%d\n",
fas_test_untagged = -10;
}
}
#endif
#endif
#ifdef FASDEBUG
IPRINTF2("tagged packet for non-tagged target %d.%d\n",
"fas_prepare_pkt_end (tran_badpkt)");
return (TRAN_BADPKT);
}
/*
* 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");
"fas_prepare_pkt_end (tran_badpkt)");
return (TRAN_BADPKT);
}
#endif /* FASDEBUG */
/*
* no need to reset tag bits since tag queueing will
* not be enabled if disconnects are disabled
*/
}
"fas_prepare_pkt_end (tran_accept)");
return (TRAN_ACCEPT);
}
/*
* emptying the waitQ just before releasing FAS_MUTEX is a bit
* tricky; if we release the waitQ mutex and then the FAS_MUTEX,
* another thread could queue a cmd in the waitQ, just before
* the FAS_MUTEX is released. This cmd is then stuck in the waitQ unless
* another cmd comes in or fas_intr() or fas_watch() checks the waitQ.
* Therefore, by releasing the FAS_MUTEX before releasing the waitQ mutex,
* we prevent fas_scsi_start() filling the waitQ
*
* By setting NO_TRAN_BUSY, we force fas_accept_pkt() to queue up
* the waitQ pkts in the readyQ.
* If a QFull condition occurs, the target driver may set its throttle
* too high because of the requests queued up in the readyQ but this
* is not a big problem. The throttle should be periodically reset anyway.
*/
static void
{
int rval;
"fas_empty_waitQ_start");
/* copy waitQ, zero the waitQ and release the mutex */
do {
}
/*
* If the packet was rejected for other reasons then
* complete it here
*/
if (rval != TRAN_ACCEPT) {
}
}
if (INTPENDING(fas)) {
/*
* stop processing the waitQ and put back
* the remaining packets on the waitQ
*/
if (waitf) {
}
}
return;
}
} while (waitf);
}
"fas_empty_waitQ_end");
}
static void
{
/*
* this may actually start cmds but it is most likely
* that if waitQ is not empty that the bus is not free
*/
}
/*
* function wrapper for two frequently used macros. for the non-critical
* path we use the function
*/
static void
{
}
/*
* fas_accept_pkt():
* the flag argument is to force fas_accept_pkt to accept the pkt;
* the caller cannot take the pkt back and it has to be queued up in
* the readyQ
*/
static int
{
/*
* prepare packet for transport if this hasn't been done yet and
* do some checks
*/
if (rval != TRAN_ACCEPT) {
goto done;
}
}
EPRINTF("fas_accept_pkt: switching target and lun slot scan\n");
}
IPRINTF("fatal error on non-zero lun pkt\n");
return (TRAN_FATAL_ERROR);
}
}
/*
* we accepted the command; increment the count
* (we may still reject later if TRAN_BUSY_OK)
*/
/*
* if it is a nointr packet, start it now
* (NO_INTR pkts are not queued in the waitQ)
*/
EPRINTF("starting a nointr cmd\n");
goto done;
}
/*
* reset the throttle if we were draining
*/
DPRINTF("reset throttle\n");
}
/*
* accept the command:
* If no readyQ and no bus free, and throttle is OK,
* run cmd immediately.
*/
#ifdef FASDEBUG
fas->f_total_cmds++;
#endif
goto exit;
} else {
/*
* If FLAG_HEAD is set, run cmd if target and bus are
* available. if first cmd in ready Q is request sense
* then insert after this command, there shouldn't be more
* than one request sense.
*/
EPRINTF("que head\n");
if (ssp &&
} else if (ssp) {
}
} else {
}
/*
* for tagged targets, check for qfull condition and
* return TRAN_BUSY (if permitted), if throttle has been
* exceeded
*/
(flag == TRAN_BUSY_OK)) {
"transport busy, slot=%x, ncmds=%x\n",
goto done;
/*
* append to readyQ or start a new readyQ
*/
} else {
}
}
done:
/*
* just in case that the bus is free and we haven't
* been able to restart for some reason
*/
(void) fas_istart(fas);
}
exit:
return (rval);
}
/*
* allocate a tag byte and check for tag aging
*/
static char fas_tag_lookup[] =
static int
{
int tag;
/*
* we reserve tag 0 for non-tagged cmds
*/
}
/* Validate tag, should never fail. */
/*
* Store assigned tag and tag queue type.
* Note, in case of multiple choice, default to simple queue.
*/
FLAG_TAGMASK) >> 12)];
"fas_alloc_tag_end");
return (0);
} 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.
*/
DPRINTF("draining tag queue\n");
}
}
/* If tag in use, scan until a free one is found. */
for (i = 1; i < NTAGS; i++) {
break;
}
/*
* we reserve tag 0 for non-tagged cmds
*/
}
}
/*
* If no free tags, we're in serious trouble.
* the target driver submitted more than 255
* requests
*/
goto fail;
}
goto alloc_tag;
}
fail:
"fas_alloc_tag_end");
return (-1);
}
/*
* Internal Search Routine.
*
* Search for a command to start.
*/
static int
{
"fas_istart_start");
EPRINTF("fas_istart:\n");
(void) fas_ustart(fas);
}
"fas_istart_end");
return (ACTION_RETURN);
}
static int
{
if (dslot == NLUNS_PER_TARGET) {
}
/*
* if readyQ not empty and we are not draining, then we
* can start another cmd
*/
do {
/*
* If all cmds drained from tag Q, back to full throttle and
* start queueing up new cmds again.
*/
}
}
"fas_ustart_end");
} else {
}
} while (slot != start_slot);
EPRINTF("fas_ustart: no cmds to start\n");
"fas_ustart_end (not_found)");
return (FALSE);
}
/*
* Start a command off
*/
static int
{
int i, cdb_len;
EPRINTF2("fas_startcmd: sp=0x%p flags=%x\n",
}
/*
* 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);
}
/*
* allocate a tag; if no tag available then put request back
* on the ready queue and return; eventually a cmd returns and we
* get going again or we timeout
*/
return (FALSE);
}
} else {
/*
* tag slot 0 is reserved for non-tagged cmds
* and should be empty because we have drained
*/
/*
* don't start any other cmd until this
* one is finished. The throttle is reset
* later in fas_watch()
*/
}
}
}
}
/*
* first send identify message, with or without disconnect priv.
*/
} else {
}
/*
* normal case, tagQ and we have negotiated wide and sync
* or we don't need to renegotiate because wide and sync
* have been disabled
* (proxy msg's don't have tag flag set)
*/
EPRINTF("tag cmd\n");
/*
* is this a proxy message
*/
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)
*/
fas->f_cur_msgout[i] =
}
cdb_len = 0;
/*
* always negotiate wide first and sync after wide
*/
int i = 0;
/* First the tag message bytes */
}
/*
* Set up to send wide negotiating message. This is getting
* a bit tricky as we dma out the identify message and
* send the other messages via the fifo buffer.
*/
cdb_len = 0;
/*
* negotiate sync xfer rate
*/
int i = 0;
/*
* Set up to send sync negotiating message. This is getting
* a bit tricky as we dma out the identify message and
* send the other messages via the fifo buffer.
*/
}
cdb_len = 0;
/*
* normal cmds, no negotiations and not a proxy and no TQ
*/
} else {
EPRINTF("std. cmd\n");
}
/*
* Now load cdb (if any)
*/
for (i = 0; i < cdb_len; i++) {
}
/*
* calculate total dma amount:
*/
/*
* load target id and enable bus id encoding and 32 bit counter
*/
#ifdef FASDEBUG
if (DDEBUGGING) {
}
#endif /* FASDEBUG */
/*
* if timeout == 0, then it has no effect on the timeout
* handling; we deal with this when an actual timeout occurs.
*/
}
if (i == 0) {
EPRINTF("dup timeout\n");
} else if (i > 0) {
EPRINTF("new timeout\n");
}
return (TRUE);
}
/*
* Interrupt Entry Point.
* Poll interrupts until they go away
*/
static uint_t
{
int kstat_updated = 0;
do {
do {
if (fas_intr_svc(fas)) {
/*
* do not return immediately here because
* we have to guarantee to always empty
* the waitQ and callbackQ in the interrupt
* handler
*/
if (fas->f_polled_intr) {
fas->f_polled_intr = 0;
}
} else {
}
} while (INTPENDING(fas));
rval == DDI_INTR_CLAIMED) {
}
/*
* check and empty the waitQ and the callbackQ
*/
} while (INTPENDING(fas));
return (rval);
}
/*
* General interrupt service routine.
*/
static int
{
};
int action;
int i = 0;
/*
* A read of FAS interrupt register clears interrupt,
* so any other volatile information needs to be latched
* up prior to reading the interrupt register.
*/
/*
* this wasn't our interrupt?
*/
if (fas_check_dma_error(fas)) {
goto start_action;
}
return (-1);
}
/*
* if we are reset state, handle this first
*/
goto start_action;
}
/*
* check for gross error. fas366 hardware seems to register
* the gross error bit when a parity error is found. Make sure
* to ignore the gross error bit when a parity error is detected.
*/
goto start_action;
}
/*
* now it is finally safe to read the interrupt register
* if we haven't done so yet
* Note: we don't read step register here but only in
* fas_finish_select(). It is not entirely safe but saves
* redundant PIOs or extra code in this critical path
*/
/*
* read the fifo if there is something there or still in the
* input shuttle
*/
if ((intr & FAS_INT_RESEL) ||
}
}
/*
* Based upon the current state of the host adapter driver
* we should be able to figure out what to do with an interrupt.
*
* The FAS 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 FAS,
* one of DISCONNECT, BUS SERVICE, FUNCTION COMPLETE conditions
* for the FAS, 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
*/
FAS_INT_RESEL)) == 0) {
/*
* The rest of the reasons for an interrupt can
* be handled based purely on the state that the driver
* is currently in now.
*/
} else {
}
} else {
}
}
while (action != ACTION_RETURN) {
"fas_intr_svc call: fas 0x%p, action %d (%d)",
i++;
}
exit:
return (0);
}
/*
* Manage phase transitions.
*/
static int
{
int action;
};
int i = 0;
"fas_phasemanage_start");
do {
EPRINTF1("fas_phasemanage: %s\n",
"fas_phasemanage_call: fas 0x%p (%d)", fas, i++);
} else {
}
} while (action == ACTION_PHASEMANAGE);
"fas_phasemanage_end");
return (action);
}
/*
* remove a cmd from active list and if timeout flag is set, then
* adjust timeouts; if a the same cmd will be resubmitted soon, don't
* bother to adjust timeouts (ie. don't set this flag)
*/
static void
{
EPRINTF4("remove tag %d slot %d for target %d.%d\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;
ushort_t 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 < t; i++) {
if (ssp &&
} else if (ssp &&
}
}
tag_slots->f_timebase = n;
EPRINTF1("searching, new_timeout= %d\n", n);
} else {
tag_slots->f_timebase = 0;
}
}
}
}
/*
* decrement f_ncmds and f_ndisc for this cmd before completing
*/
static void
{
}
}
}
/*
* Most commonly called phase handlers:
*
* Finish routines
*/
static int
{
"fas_finish_start");
EPRINTF("fas_finish\n");
#ifdef FAS_TEST
debug_enter("untagged cmd completed");
}
#endif
/*
* immediately enable reselects
*/
/*
* 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.
*/
}
/*
*/
#ifdef FAS_TEST
if (fas_test_stop) {
debug_enter("parity error");
}
#endif
}
/*
* Free from active list and update counts
* We need to clean up this cmd now, just in case fas_ustart()
* hits a reset or other fatal transport error
*/
/*
* go to state free and try to start a new cmd now
*/
(INTPENDING(fas) == 0)) {
if (fas_ustart(fas)) {
}
}
/*
* if there was a data xfer then calculate residue and
* sync data for consistent memory xfers
*/
}
IPRINTF3("%d.%d finishes with %ld resid\n",
}
}
} else {
/*
* start an autorequest sense if there was a check condition.
* if arq has not been enabled, fas_handle_sts_chk will do
* do the callback
*/
/*
* we can't start an arq because one is
* already in progress. the target is
* probably confused
*/
}
} else if ((*((char *)status) & STATUS_MASK) ==
STATUS_QFULL) {
} else {
#ifdef FAS_TEST
arqstat = (struct scsi_arq_status *)
fas_arqs_failure = 0;
}
if (fas_tran_err) {
fas_tran_err = 0;
}
#endif
}
}
return (action);
}
/*
* Complete the process of selecting a target
*/
static int
{
"fas_finish_select_start");
EPRINTF("fas_finish_select:\n");
/*
* Check for DMA gate array errors
*/
& DMA_ERRPEND) {
/*
* 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");
"fas_finish_select_end (ACTION_RESET1)");
return (ACTION_RESET);
}
/*
* Shut off DMA gate array
*/
/*
* Did something respond to selection?
*/
/*
* We succesfully 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 FAS_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 accomodate
* disconnecting,
* but it really isn't worth the effort to distinguish
* such targets fasecially from others.
*/
/* FALLTHROUGH */
case FAS_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 FAS_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.
*/
break;
case FAS_STEP_PCMD:
/*
* In this case, not all command bytes transferred.
*/
/* FALLTHROUGH */
case FAS_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.
*/
break;
default:
"bad sequence step (0x%x) in selection", step);
"fas_finish_select_end (ACTION_RESET3)");
return (ACTION_RESET);
}
/*
* OR in common state...
*/
/*
* data pointer initialization has already been done
*/
"fas_finish_select_end (action3)");
return (fas_handle_unknown(fas));
} else if (intr == FAS_INT_DISCON) {
/*
* make sure we negotiate when this target comes
* on line later on
*/
/*
* Set the throttle to DRAIN_THROTTLE to make
* sure any disconnected commands will get timed out
* incase the drive dies
*/
}
"fas_finish_select_end (ACTION_FINISH)");
return (ACTION_FINISH);
} else {
"fas_finish_select_end (ACTION_RESET2)");
return (ACTION_RESET);
}
/* NOTREACHED */
}
/*
* a selection got preempted by a reselection; shut down dma
* and put back cmd in the ready queue unless NOINTR
*/
static int
{
int rval;
/*
* A reselection attempt glotzed our selection attempt.
* we put request back in the ready queue
*/
/*
* Shut off DMA gate array
*/
/*
* service the reconnect now and clean up later
*/
/*
* If selection for a non-tagged command is preempted, the
* command could be stuck because throttle was set to DRAIN,
* and a disconnected command timeout follows.
*/
}
/*
* if we attempted to renegotiate on this cmd, undo this now
*/
if (fas->f_wdtr_sent) {
fas->f_wdtr_sent = 0;
}
if (fas->f_sdtr_sent) {
fas->f_sdtr_sent = 0;
}
return (rval);
}
/*
* Handle the reconnection of a target
*/
static int
{
"fas_reconnect_start");
EPRINTF("fas_reconnect:\n");
default:
/*
* Pick up target id from fifo
*
* There should only be the reselecting target's id
* and an identify message in the fifo.
*/
/*
* we know the target so update period, conf3,
* offset reg, if necessary, and accept the msg
*/
/*
* now we can accept the message. an untagged
* target will go immediately into data phase so
* updated before accepting the message
*/
bad_reselect = "bad reselect bytes";
break;
}
/*
* normal initial reconnect; we get another interrupt later
* for the tag
*/
break;
}
/*
* Check sanity of message.
*/
bad_reselect = "bad identify msg";
break;
}
EPRINTF2("fas_reconnect: target=%x, idmsg=%x\n",
/*
* If tag queueing in use, DMA in tag.
* Otherwise, we're ready to go.
* if tag 0 slot is non-empty, a non-tagged cmd is
* reconnecting
*/
volatile uchar_t *c =
/*
* If we've been doing tagged queueing 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
* queueing like we thought or the target died.
*/
if (INTPENDING(fas) == 0) {
"fas_reconnect_end (_RETURN1)");
return (ACTION_RETURN);
}
return (fas_illegal_cmd_or_bus_reset(fas));
}
bad_reselect = "not in msgin phase";
break;
}
bad_reselect = "unexpected bus free";
break;
}
} else {
break;
}
/*FALLTHROUGH*/
case ACTS_RESEL:
{
volatile uchar_t *c =
uint_t i;
FAS_INT_FCMP)) {
"fas_reconnect_end (_PHASEMANAGE)");
return (ACTION_PHASEMANAGE);
} else {
bad_reselect = "not in msgin phase";
break;
}
}
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(fas)) {
FAS_INT_ILLEGAL)) {
return (
(fas));
}
break;
}
}
}
if (i == (uint_t)RECONNECT_TAG_RCV_TIMEOUT) {
bad_reselect = "timeout on receiving tag msg";
break;
}
/*
* 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) {
"fas_reconnect: invalid msg, polling\n");
for (i = 0; i < 1000000; i++) {
if (*c != INVALID_MSG)
break;
}
}
break;
}
(id = *c++) < MSG_SIMPLE_QTAG ||
id > MSG_ORDERED_QTAG) {
/*
* Target agreed to do tagged queueing
* and lied!
* This problem implies the drive firmware is
* broken.
*/
bad_reselect = "botched tag";
break;
}
tag = *c;
/* Set ptr to reconnecting scsi pkt */
} else {
bad_reselect = "Invalid tag";
break;
}
}
}
bad_reselect = "Parity error in reconnect msg's";
}
#ifdef FAS_TEST
#endif
/*
* this shouldn't really happen, so it is better
* to reset the bus; some disks accept the abort
* and then still reconnect
*/
if (bad_reselect == NULL) {
bad_reselect = "no command";
}
#ifdef FAS_TEST
debug_enter("bad reconnect");
} else {
fas_atest_reconn = 0;
}
#endif
goto bad;
/*
* XXX remove this case or make it an ASSERT
*/
/*
* 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).
*/
IPRINTF1("fas_reconnect: fielding proxy cmd for %d\n",
target);
tmp = 0;
tmp++;
}
/*
* pretend that the disconnected cmd is still disconnected
* (this prevents ndisc from going negative)
*/
}
/*
* 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.
*/
}
/*
* Return to await the FUNCTION COMPLETE interrupt we
* should get out of accepting the IDENTIFY message.
*/
"fas_reconnect_end (_RETURN2)");
return (ACTION_RETURN);
bad:
}
#ifdef FASDEBUG
#endif
"fas_reconnect_end (_RESET5)");
return (ACTION_RESET);
}
/*
* handle unknown bus phase
* we don't know what to expect so check status register for current
* phase
*/
int
{
"fas_handle_unknown_start: fas 0x%p", fas);
EPRINTF("fas_handle_unknown:\n");
/*
* we call actions here rather than returning to phasemanage
* (this is the most frequently called action)
*/
case FAS_PHASE_DATA_IN:
case FAS_PHASE_DATA_OUT:
"fas_handle_unknown_end (phase_data)");
return (fas_handle_data_start(fas));
case FAS_PHASE_MSG_OUT:
"fas_handle_unknown_end (phase_msg_out)");
return (fas_handle_msg_out_start(fas));
case FAS_PHASE_MSG_IN:
"fas_handle_unknown_end (phase_msg_in)");
return (fas_handle_msg_in_start(fas));
case FAS_PHASE_STATUS:
#ifdef FAS_TEST
}
#endif /* FAS_TEST */
"fas_handle_unknown_end (phase_status)");
return (fas_handle_c_cmplt(fas));
case FAS_PHASE_COMMAND:
"fas_handle_unknown_end (phase_cmd)");
return (fas_handle_cmd_start(fas));
}
"fas_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",
}
fas->f_abort_msg_sent++;
}
} else if (msgout == MSG_DEVICE_RESET) {
fas->f_reset_msg_sent++;
}
}
} else {
/*
* the target rejected the negotiations,
* is now set)
*/
"fas_handle_unknown_end (int_discon)");
return (ACTION_SEARCH);
/*
* target dropped off the bus during
* negotiations
*/
}
#ifdef FASDEBUG
#endif
}
"fas_handle_unknown_end (int_discon)");
return (ACTION_FINISH);
}
/* NOTREACHED */
}
/*
* handle target disconnecting
*/
static int
{
"fas_handle_clearing_start");
EPRINTF("fas_handle_clearing:\n");
if (INTPENDING(fas)) {
return (fas_illegal_cmd_or_bus_reset(fas));
}
} else {
/*
* change e_laststate for the next time around
*/
"fas_handle_clearing_end (ACTION_RETURN1)");
return (ACTION_RETURN);
}
}
/*
* At this point the FAS 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 fas_finish()
*/
}
/*
* start a cmd here to save time
*/
"fas_handle_clearing_end (ACTION_RETURN2)");
return (ACTION_RETURN);
}
"fas_handle_clearing_end (ACTION_RETURN3)");
return (ACTION_RETURN);
} else {
"fas_handle_clearing_end");
return (fas_finish(fas));
}
} else {
/*
* If the target didn't disconnect from the
* bus, that is a gross fatal error.
* XXX this can be caused by asserting ATN
* XXX check bus phase and if msgout, send a message
*/
"Target %d didn't disconnect after sending %s",
#ifdef FASDEBUG
IPRINTF4("msgout: %x %x %x, last_msgout=%x\n",
#endif
"fas_handle_clearing_end (ACTION_ABORT_CURCMD)");
return (ACTION_ABORT_ALLCMDS);
}
}
/*
* handle data phase start
*/
static int
{
"fas_handle_data_start");
EPRINTF("fas_handle_data_start:\n");
bad:
"fas_handle_data_end (ACTION_ABORT_CURCMD1)");
return (ACTION_ABORT_CURCMD);
} else {
}
return (ACTION_ABORT_CURCMD);
}
}
/*
* And 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.
*/
"cmd_data_count=%x, dmacount=%x, curaddr=%x, end=%"
PRIx64 ", nwin=%x\n",
DPRINTF2("dmac_address = %x, dmac_size=%lx\n",
goto bad;
}
DPRINTF2("dmac_address=%x, dmac_size=%lx\n",
}
}
#ifdef FASDEBUG
/*
* Make sure that we don't cross a boundary we can't handle
*/
goto bad;
}
}
#endif
}
} else {
"fas_handle_data_end (ACTION_ABORT_CURCMD2)");
return (ACTION_ABORT_CURCMD);
}
#ifdef FAS_TEST
}
#endif /* FAS_TEST */
"fas_handle_data_end (ACTION_RETURN)");
return (ACTION_RETURN);
}
static int
{
char was_sending;
"fas_handle_data_done_start");
EPRINTF("fas_handle_data_done\n");
/*
* Check for DMA errors (parity or memory fault)
*/
DMA_ERRPEND) {
/*
* 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.
*/
"fas_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 FAS chip has asserted ATN* for us already.
*/
if (!was_sending) {
#ifdef FAS_TEST
fas_ptest_data_in = 0;
stat |= FAS_STAT_PERR;
if (fas_test_stop > 1) {
debug_enter("ptest_data_in");
}
}
#endif /* FAS_TEST */
if (stat & FAS_STAT_PERR) {
"SCSI bus DATA IN phase parity error");
}
}
/*
* 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 fas_handle_unknown clean up for us.
*/
"fas_handle_data_done_end (ACTION_PHASEMANAGE)");
return (ACTION_PHASEMANAGE);
}
/*
* Figure out how far we got.
* Latch up fifo amount first and double if wide has been enabled
*/
}
if (stat & FAS_STAT_XZERO) {
} else {
}
DPRINTF4("fifoamt=%x, xfer_amt=%x, lastcount=%x, stat=%x\n",
/*
* 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 fas asserts an interrupt.
*/
if (was_sending) {
}
#ifdef FASDEBUG
{
(phase != FAS_PHASE_DATA_IN) &&
(phase != FAS_PHASE_DATA_OUT) &&
"input shuttle not empty at end of data phase");
"fas_handle_data_done_end (ACTION_RESET)");
return (ACTION_RESET);
}
}
#endif /* FASDEBUG */
/*
* 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
*/
if (was_sending)
} else {
/*
* If we aren't doing Synchronous Data Transfers,
* definitely offload the fifo.
*/
}
/*
* adjust pointers...
*/
DPRINTF3("before:cmd_data_count=%x, cmd_cur_addr=%x, xfer_amt=%x\n",
DPRINTF3("after:cmd_data_count=%x, cmd_cur_addr=%x, xfer_amt=%x\n",
stat &= FAS_PHASE_MASK;
"fas_handle_data_done_end (action1)");
return (fas_handle_data_start(fas));
}
"fas_handle_data_done_end (action2)");
return (fas_handle_unknown(fas));
}
static int
{
"fas_handle_c_cmplt_start");
EPRINTF("fas_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(fas)) {
return (fas_illegal_cmd_or_bus_reset(fas));
}
} else {
/*
* change f_laststate for the next time around
*/
"fas_handle_c_cmplt_end (ACTION_RETURN1)");
return (ACTION_RETURN);
}
} else {
}
#ifdef FAS_TEST
fas_ptest_status = 0;
if (fas_test_stop > 1) {
debug_enter("ptest_status");
}
fas_ptest_msgin = 0;
fas_ptest_msg = -1;
if (fas_test_stop > 1) {
debug_enter("ptest_completion");
}
}
#endif /* FAS_TEST */
if (intr == FAS_INT_DISCON) {
"fas_handle_c_cmplt_end (action1)");
return (fas_handle_unknown(fas));
}
}
/*
* do a msg accept now and read the fifo data
*/
if (intr & FAS_INT_FCMP) {
/*
* The FAS 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.
*
* if we give the MSG_ACT before reading the msg byte
* we get the status byte again and if the status is zero
* then we won't detect a failure
*/
/*
* 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 (perr) {
"fas_handle_c_cmplt_end (action5)");
return (ACTION_RETURN);
}
} else if (intr == FAS_INT_BUS) {
/*
* We only got the status byte.
*/
msg = INVALID_MSG;
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");
"fas_handle_c_cmplt_end (action5)");
return (fas_handle_unknown(fas));
}
} else {
IPRINTF("fas_handle_cmd_cmplt: unexpected intr\n");
"fas_handle_c_cmplt_end (action2)");
return (fas_handle_unknown(fas));
}
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.
*/
"fas_handle_c_cmplt_end (action4)");
return (fas_handle_clearing(fas));
} else {
"fas_handle_c_cmplt_end (action3)");
return (fas_handle_msg_in_done(fas));
}
}
/*
* prepare for accepting a message byte from the fifo
*/
static int
{
"fas_handle_msg_in_start");
EPRINTF("fas_handle_msg_in_start\n");
/*
* Pick up a message byte.
* Clear the FIFO so we
* don't get confused.
*/
if (!FIFO_EMPTY(fas)) {
}
fas->f_imsgindex = 0;
/*
* give a little extra time by returning to phasemanage
*/
"fas_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 FAS 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
{
"fas_handle_more_msgin_start");
EPRINTF("fas_handle_more_msgin\n");
/*
* Fetch another byte of a message in.
*/
"fas_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
* fas_phasemanage() handle things.
*
* If it wasn't a BUS SERVICE interrupt,
* let fas_phasemanage() find out if the
* chip disconnected.
*/
"Premature end of extended message");
}
}
"fas_handle_more_msgin_end (action)");
return (fas_handle_unknown(fas));
}
static int
{
int sndmsg = 0;
"fas_handle_msg_in_done_start");
EPRINTF("fas_handle_msg_in_done:\n");
if (INTPENDING(fas)) {
return (fas_illegal_cmd_or_bus_reset(fas));
}
} else {
/*
* change f_laststate for the next time around
*/
"fas_handle_msg_in_done_end (ACTION_RETURN1)");
return (ACTION_RETURN);
}
}
/*
* the most common case is a disconnect message. we do
* a fast path for this condition and if it fails then
* we go for the detailed error handling
*/
#ifndef FAS_TEST
"fas_handle_msg_in_done_end (action)");
return (fas_handle_clearing(fas));
}
}
#endif /* not FAS_TEST */
/*
* We can be called here for both the case where
* we had requested the FAS 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 FAS_TEST
#endif /* FAS_TEST */
"premature end of input message");
"fas_handle_msg_in_done_end (ACTION_PHASEMANAGE)");
return (ACTION_PHASEMANAGE);
}
/*
* Note that if f_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 or 0 bytes 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
* fas_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 FAS_TEST
fas_ptest_msg == msgin) {
fas_ptest_msgin = 0;
fas_ptest_msg = -1;
if (fas_test_stop > 1) {
debug_enter("ptest msgin");
}
goto reloop;
}
#endif /* FAS_TEST */
#ifdef FAS_TEST
fas_ptest_emsgin = 0;
if (fas_test_stop > 1) {
debug_enter("ptest emsgin");
}
goto reloop;
}
#endif /* FAS_TEST */
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.
*/
"fas_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 FAS chip has already
* set ATN* for us, but it doesn't hurt to set it here
* again anyhow.
*/
}
"fas_handle_msg_in_done_end (action)");
return (fas_handle_clearing(fas));
}
"fas_handle_msg_in_done_end (ACTION_RETURN2)");
return (ACTION_RETURN);
}
static int
{
int msgout = 0;
EPRINTF("fas_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-
* the FLAG_NODISCON was set in fas_accept_pkt() if
* no disconnect was enabled in scsi_options
*/
msgout = MSG_REJECT;
break;
}
/* FALLTHROUGH */
case MSG_COMMAND_COMPLETE:
break;
case MSG_NOP:
break;
/* XXX Make it a MSG_REJECT handler */
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:
if (fas->f_wdtr_sent) {
/*
* Disable wide, Target rejected
* out WDTR message
*/
fas->f_wdtr_sent = 0;
/*
* we still want to negotiate sync
*/
}
} else if (fas->f_sdtr_sent) {
fas->f_sdtr_sent = 0;
}
msgout = 0;
break;
case MSG_NOP:
break;
case MSG_INITIATOR_ERROR:
break;
case MSG_MSG_PARITY:
break;
case MSG_REJECT:
break;
/* XXX - abort not good, queue full handling or drain (?) */
case MSG_SIMPLE_QTAG:
case MSG_ORDERED_QTAG:
case MSG_HEAD_QTAG:
break;
case MSG_DEVICE_RESET:
break;
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'",
}
break;
}
case MSG_RESTORE_PTRS:
(sp->cmd_cur_win == 0) &&
(sp->cmd_data_count == 0)) {
}
}
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
{
int i;
"fas_handle_cmd_start_start");
EPRINTF("fas_handle_cmd: send cmd\n");
for (i = 0; i < amt; i++) {
}
"fas_handle_cmd_start_end");
return (ACTION_RETURN);
}
static int
{
"fas_handle_cmd_done_start");
EPRINTF("fas_handle_cmd_done\n");
/*
* 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 & FAS_INT_BUS) == 0) {
if ((intr & FAS_INT_DISCON) == 0) {
"fas_handle_cmd_done_end (abort1)");
return (ACTION_ABORT_CURCMD);
}
} else {
}
"fas_handle_cmd_done_end");
return (fas_handle_unknown(fas));
}
/*
* Begin to send a message out
*/
static int
{
"fas_handle_msg_out_start");
EPRINTF("fas_handle_msg_out_start\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?
*/
"fas_handle_msg_out_end (ACTION_PHASEMANAGE)");
return (ACTION_PHASEMANAGE);
}
/*
* Clean the fifo.
*/
if (amt == 0) {
/*
* no msg to send
*/
}
/*
* 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.
*/
/*
* There is a bug in the fas366 that occasionaly
* deasserts the ATN signal prematurely when we send
* workaround here is to send the negotiation bytes out
* using PIO
*/
"fas_handle_msg_out_end");
return (ACTION_RETURN);
}
static int
{
int action;
"fas_handle_msg_out_done_start");
}
EPRINTF4("msgout: %x %x %x, last_msgout=%x\n",
/*
* flush fifo, just in case some bytes were not sent
*/
/*
* If the FAS 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.
*/
fas->f_abort_msg_sent++;
}
} else if (msgout == MSG_DEVICE_RESET) {
fas->f_reset_msg_sent++;
}
}
EPRINTF2("Successful %s message to target %d\n",
}
"fas_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 fas_phasemanage()
* handle this unexpected bus free event.
*/
goto out;
}
/*
* What phase have we transitioned to?
*/
/*
* 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");
"fas_handle_msg_out_done_end (ACTION_PHASEMANAGE)");
return (ACTION_PHASEMANAGE);
}
out:
"fas_handle_msg_out_done_end");
return (action);
}
static int
{
int xfer_amt;
/*
* Knock off one byte if there
* is a last transfer and is even number of bytes
*/
sp->cmd_data_count--;
sp->cmd_cur_addr--;
}
return (0);
}
"Two byte message '%s' 0x%x rejected",
return (MSG_REJECT);
}
/*
* handle receiving extended messages
*/
static int
{
#ifdef FASDEBUG
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("fas_multibyte_msg:\n");
if (emsg == MSG_SYNCHRONOUS) {
DPRINTF5("sync msg received: %x %x %x %x %x\n",
DPRINTF3("received period %d offset %d from tgt %d\n",
DPRINTF3("calculated minsync %d, maxsync %d for tgt %d\n",
DPRINTF2("sync period %d, neg period %d\n",
/*
* In cases where the target negotiates synchronous
* mode before we do, and we either have sync mode
* disabled, 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
* then we cannot reject a SYNCHRONOUS DATA TRANSFER
* REQUEST message).
*/
IPRINTF1("SYNC negotiation initiated by target %d\n",
tgt);
period =
}
}
/*
* If the target's offset is bigger than ours,
* the target has violated the scsi protocol.
*/
if (offset > fas_default_offset) {
msgout = MSG_REJECT;
}
/*
* We cannot transmit data in synchronous
* mode this slow, so convert to asynchronous
* mode.
*/
/*
* If the target's period is less than ours,
* the target has violated the scsi protocol.
*/
msgout = MSG_REJECT;
} else if (offset) {
/*
* Conversion method for received PERIOD value
* to the number of input clock ticks to the FAS.
*
* We adjust the input period value such that
* we always will transmit data *not* faster
* than the period value received.
*/
/*
* XXX do we need this?
*/
regval--;
}
}
/*
* Is is now safe to produce a responce to a target
* initiated sdtr. period and offset have been checked.
*/
if (msgout == MSG_EXTENDED) {
}
if (offset) {
/*
* fastscsi in conf3
*/
if (period < FASTSCSI_THRESHOLD) {
} else {
}
DPRINTF4("period %d (%d), offset %d to tgt %d\n",
#ifdef FASDEBUG
/*
* 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
} else {
/*
* We are converting back to async mode.
*/
}
/*
* If this target violated the scsi spec, reject the
* sdtr msg and don't negotiate sdtr again.
*/
if (msgout == MSG_REJECT) {
}
} else if (emsg == MSG_WIDE_DATA_XFER) {
DPRINTF4("wide msg received: %x %x %x %x\n",
/* always renegotiate sync after wide */
IPRINTF1("Wide negotiation initiated by target %d\n",
tgt);
/*
* allow wide neg even if the target driver hasn't
* enabled wide yet.
*/
/*
* Let us go back to async mode(SCSI spec)
* and depend on target to do sync
* after wide negotiations.
* If target does not do a sync neg and enters
* async mode we will negotiate sync on next command
*/
} else {
/*
* renegotiate sync after wide
*/
fas->f_wdtr_sent = 0;
} else {
msgout = 0;
}
}
} else if (emsg == MSG_MODIFY_DATA_PTR) {
msgout = MSG_REJECT;
} else {
"Rejecting message %s 0x%x from Target %d",
msgout = MSG_REJECT;
}
out:
return (msgout);
}
/*
* Back off sync negotiation
* and got to async mode
*/
static void
{
}
/*
* handle an unexpected selection attempt
* XXX look for better way: msg reject, drop off the bus
*/
static int
{
return (ACTION_RETURN);
}
/*
* dma window handling
*/
static int
{
return (-1);
}
}
}
return (0);
}
static int
{
return (-1);
}
DPRINTF4("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, end=%lx, nwin=%x\n",
sp->cmd_cur_win++;
sp->cmd_cur_win--;
return (-1);
}
/*
* if there are no more windows, we have a data overrun condition
*/
} else {
/*
* 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);
}
return (0);
}
/*
* dma error handler
*/
static int
{
/*
* was there a dma error that caused fas_intr_svc() 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);
}
/*
* check for gross error or spurious interrupt
*/
static int
{
IPRINTF5("fas_cmd=%x, stat=%x, intr=%x, step=%x, fifoflag=%x\n",
return (ACTION_RESET);
}
/*
* handle illegal cmd interrupt or (external) bus reset cleanup
*/
static int
{
/*
* 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.
*/
return (ACTION_FINRST);
}
/*
* Illegal cmd to fas:
* This should not happen. The one situation where
* we can get an ILLEGAL COMMAND interrupt is due to
* a bug in the FAS366 during reselection which we
* should be handling in fas_reconnect().
*/
return (ACTION_RESET);
}
/*NOTREACHED*/
return (ACTION_RETURN);
}
/*
* set throttles for all luns of this target
*/
static void
{
int i;
/*
* are allowed. Not allowing change of throttles during draining
* limits error recovery but will reduce draining time
*
* all throttles should have been set to HOLD_THROTTLE
*/
return;
}
if (n == NLUNS_PER_TARGET) {
}
if (what == HOLD_THROTTLE) {
if (what == MAX_THROTTLE) {
fas->f_throttle[i] = (short)
} else {
}
}
}
}
static void
{
/*
* fas_set_throttle will adjust slot to starting at LUN 0
*/
}
static void
{
}
/*
* run a polled cmd
*/
static void
{
int limit, i, n;
int timeout = 0;
DPRINTF4("runpoll: slot=%x, cmd=%x, current_sp=0x%p, tcmds=%x\n",
/*
* wait for cmd to complete
* don't start new cmds so set throttles to HOLD_THROTTLE
*/
}
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
*/
(*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..
*/
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_WIDE; i++) {
if (fas->f_reset_delay[i]) {
int s = i * NLUNS_PER_TARGET;
int e = s + NLUNS_PER_TARGET;
fas->f_reset_delay[i] = 0;
for (; s < e; s++) {
fas_full_throttle(fas, s);
}
}
}
}
/*
* fas_startcmd() will return false if preempted
* or draining
*/
IPRINTF("runpoll: cannot start new cmds\n");
continue;
}
/*
* We're now 'running' this command.
*
* fas_dopoll will always return when
* fas->f_state is STATE_FREE, and
*/
if (limit == 0) {
}
/*
* if the cmd disconnected, the first call to fas_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
* fas_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("fas_runpoll: cmd started??\n");
goto bad;
}
}
}
/*
* blindly restore throttles which is preferable over
* leaving throttle hanging at 0 and noone to clear it
*/
}
/*
* ensure that the cmd is completely removed
*/
/*
* If we stored up commands to do, start them off now.
*/
(void) fas_ustart(fas);
}
exit:
return;
bad:
#ifdef FASDEBUG
#endif /* FASDEBUG */
/*
* clean up all traces of this sp because fas_runpoll will return
* before fas_reset_recovery() cleans up
*/
(void) fas_reset_bus(fas);
}
goto exit;
}
/*
* Poll for command completion (i.e., no interrupts)
* limit is in usec (and will not be very accurate)
*
* the assumption is that we only run polled cmds in interrupt context
* as scsi_transport will filter out FLAG_NOINTR
*/
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
* fas_dopoll always returns if e_state transitions to STATE_FREE
*/
if (limit == 0) {
}
for (n = i = 0; i < limit; i += 100) {
if (INTPENDING(fas)) {
n++;
(void) fas_intr_svc(fas);
break;
}
drv_usecwait(100);
}
n = -1;
}
"fas_dopoll_end: rval %x", n);
return (n);
}
/*
* prepare a sync negotiation message
*/
static void
{
/*
* If this target experienced a sync backoff use the
* target's sync speed that was adjusted in
* fas_sync_wide_backoff. For second sync backoff,
* offset will be ajusted below in sanity checks.
*/
}
/*
* If this is a responce to a target initiated sdtr,
* use the agreed upon values.
*/
}
/*
* If the target driver disabled
* sync then make offset = 0
*/
offset = 0;
}
/*
* sanity check of period and offset
*/
}
}
} else {
}
offset = 0;
}
}
*p++ = (uchar_t)MSG_EXTENDED;
*p++ = (uchar_t)3;
*p++ = (uchar_t)MSG_SYNCHRONOUS;
*p++ = period;
*p++ = offset & 0xf;
IPRINTF2("fas_make_sdtr: period = %x, offset = %x\n",
/*
* increment sdtr flag, odd value indicates that we initiated
* the negotiation
*/
fas->f_sdtr_sent++;
/*
* the target may reject the optional sync message so
* to avoid negotiating on every cmd, set sync known here
* we should not negotiate wide after sync again
*/
}
/*
* prepare a wide negotiation message
*/
static void
{
width = 0;
}
width = 0;
}
*p++ = (uchar_t)MSG_EXTENDED;
*p++ = (uchar_t)2;
*p++ = (uchar_t)MSG_WIDE_DATA_XFER;
/*
* increment wdtr flag, odd value indicates that we initiated
* the negotiation
*/
fas->f_wdtr_sent++;
/*
* the target may reject the optional wide message so
* to avoid negotiating on every cmd, set wide known here
*/
}
/*
* auto request sense support
* create or destroy an auto request sense packet
*/
static int
{
/*
* Allocate a request sense packet using get_pktiopb
*/
/*
* if one exists, don't create another
*/
return (0);
}
/*
* 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.
*/
arq_data =
(char)SCMD_REQUEST_SENSE, 0, (char)SENSE_LENGTH);
/*
* defer callbacks; fas_call_pkt_comp() calls
* fas_complete_arq_pkt() directly without releasing the lock
* However, since we are not calling back directly thru
* pkt_comp, don't check this with warlock
*/
#ifndef __lock_lint
(void (*)(struct scsi_pkt *))fas_complete_arq_pkt;
#endif
return (0);
}
static int
{
/*
* if there is still a pkt saved or no rqpkt
* then we cannot deallocate or there is nothing to do
*/
/*
* is arq pkt in use?
*/
if (arq_data->arq_save_sp) {
return (-1);
}
}
return (0);
}
/*
* complete an arq packet by copying over transport info and the actual
* request sense data; called with mutex held from fas_call_pkt_comp()
*/
void
{
/*
* ASC=0x47 is parity error
*/
}
}
/*
* handle check condition and start an arq packet
*/
static int
{
IPRINTF("no arq packet or cannot arq on arq pkt\n");
return (0);
}
DPRINTF3("start arq for slot=%x, arqsp=0x%p, rqpkt=0x%p\n",
IPRINTF("auto request sense already in progress\n");
goto fail;
}
/*
* 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
*/
/*
* make sure that auto request sense always goes out
* after queue full and after throttle was set to draining
*/
return (0);
fail:
return (-1);
}
/*
* handle 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, "
/*
* when target gives queue full status with no commands
* outstanding (f_tcmds[] == 0), throttle is set to 0
* (HOLD_THROTTLE), and the queue full handling starts
* (see psarc/1994/313); if there are commands outstanding,
* the throttle is set to (f_tcmds[] - 2)
*/
/*
* By setting throttle to QFULL_THROTTLE, we
* avoid submitting new commands and in
* fas_restart_cmd find out slots which need
* their throttles to be cleared.
*/
if (fas->f_restart_cmd_timeid == 0) {
}
}
}
}
/*
* invoked from timeout() to restart qfull cmds with throttle == 0
*/
static void
{
int i;
IPRINTF("fas_restart_cmd:\n");
fas->f_restart_cmd_timeid = 0;
for (i = 0; i < N_SLOTS; i += NLUNS_PER_TARGET) {
i, MAX_THROTTLE);
}
}
}
(void) fas_ustart(fas);
}
/*
* Timeout handling:
* Command watchdog routines
*/
/*ARGSUSED*/
static void
{
#ifdef FAS_PIO_COUNTS
if (fas->f_total_cmds) {
int n = fas->f_total_cmds;
"total=%d, cmds=%d fas-rd=%d, fas-wrt=%d, dma-rd=%d, dma-wrt=%d\n",
fas->f_reg_cmds/n,
}
#endif
int i;
/*
* 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 slot 0
* (untagged cmd)
*/
#ifdef FAS_TEST
if (fas_enable_untagged) {
}
#endif
for (i = 0; i < N_SLOTS; i++) {
fas_full_throttle(fas, i);
}
}
}
if (fas->f_props_update) {
int i;
/*
* f_mutex will be released and reentered in
* fas_props_update().
* Hence we save the fas->f_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 f_props_update to non-zero value
*/
fas->f_props_update = 0;
for (i = 0; i < NTARGETS_WIDE; i++) {
if (props_update & (1<<i)) {
fas_update_props(fas, i);
}
}
}
}
if (fas_timeout_initted && fas_timeout_id) {
}
}
static void
{
short slot;
#ifdef FAS_TEST
if (fas_btest) {
fas_btest = 0;
(void) fas_reset_bus(fas);
return;
}
fas_force_timeout = 0;
return;
}
#endif /* FAS_TEST */
/*
* check tagged cmds first
*/
"fas_watchsubr: slot %x: tcmds=%x, timeout=%x\n",
if (tag_slots->f_timebase <=
tag_slots->f_timebase +=
continue;
}
return;
}
IPRINTF1("pending timeout on slot=%x\n",
slot);
IPRINTF("draining all queues\n");
}
}
}
}
/*
* timeout recovery
*/
static void
{
#ifdef FAS_TEST
if (fas_test_stop) {
debug_enter("timeout");
}
#endif
/*
* set throttle back; no more draining necessary
*/
for (i = 0; i < N_SLOTS; i += d) {
fas_full_throttle(fas, i);
}
}
}
/*
* if no interrupt pending for next second then the current
* cmd must be stuck; switch slot and sp to current slot and cmd
*/
drv_usecwait(100);
}
if (INTPENDING(fas) == 0) {
}
}
/*
* update all outstanding pkts for this slot
*/
ncmds++;
}
}
/*
* no timed-out cmds here?
*/
if (ncmds == 0) {
return;
}
/*
* dump all we know about this timeout
*/
if (sp) {
"Disconnected command timeout for Target %d.%d",
} else {
"Connected command timeout for Target %d.%d",
/*
* Current command timeout appears to relate often
* to noisy SCSI in synchronous mode.
*/
}
}
#ifdef FASDEBUG
#endif
} else {
"Disconnected tagged cmd(s) (%d) timeout for Target %d.%d",
}
(void) fas_istart(fas);
}
}
/*
* fas_sync_wide_backoff() increases sync period and enables slow
* cable mode.
* we count on a bus reset to disable wide in the target and will
* never renegotiate wide again
*/
static void
int slot)
{
char phase;
phase &= FAS_PHASE_MASK;
"fas_sync_wide_backoff: target %d: state=%x, phase=%x, sp=0x%p\n",
#ifdef FASDEBUG
if (fas_no_sync_wide_backoff) {
return;
}
#endif
/*
* if this not the first time or sync is disabled
* thru scsi_options then disable wide
*/
/*
* disable wide for just this target
*/
"Target %d disabled wide SCSI mode", tgt);
}
/*
* do not reset the bit in f_nowide because that
* would not force a renegotiation of wide
* and do not change any register value yet because
* we may have reconnects before the renegotiations
*/
}
/*
* reduce xfer rate. if this is the first time, reduce by
* 100%. second time, disable sync and wide.
*/
/*
* do not reset the bit in f_nosync because that
* would not force a renegotiation of sync
*/
"Target %d reverting to async. mode",
tgt);
}
} else {
/* increase period by 100% */
"Target %d reducing sync. transfer rate", tgt);
}
}
/*
* always enable slow cable mode, if not already enabled
*/
IPRINTF("Reverting to slow SCSI cable mode\n");
}
/*
* Force sync renegotiation and update properties
*/
}
/*
* handle failed negotiations (either reject or bus free condition)
*/
static void
{
if (fas->f_wdtr_sent) {
IPRINTF("wide neg message rejected or bus free\n");
/*
* clear offset just in case it goes to
* data phase
*/
} else if (fas->f_sdtr_sent) {
IPRINTF("sync neg message rejected or bus free\n");
}
}
/*
* force wide and sync renegotiation
*/
static void
{
}
/*
* update conf3 register for wide negotiation
*/
static void
{
switch (width) {
case 0:
break;
case 1:
break;
}
}
/*
* Abort command handling
*
* abort current cmd, either by device reset or immediately with bus reset
* (usually an abort msg doesn't completely solve the problem, therefore
* a device or bus reset is recommended)
*/
static int
{
if (fas->f_current_sp) {
} else {
return (fas_reset_bus(fas));
}
}
static int
{
/*
* attempting to abort a connected cmd is usually fruitless, so
* only try disconnected cmds
* a reset is preferable over an abort (see 1161701)
*/
IPRINTF2("attempting to reset target %d.%d\n",
return (ACTION_SEARCH);
}
}
/*
* 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 (fas_reset_bus(fas));
}
/*
* fas_do_scsi_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 fas_abort_connected_cmd
* or fas_runpoll which prevents new cmds from starting while aborting
*/
static int
{
short slot;
if (pkt) {
} else {
}
/*
* 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("fas_scsi_abort for slot %x, "
"sp=0x%p, pkt_flags=%x, cur_sp=0x%p\n",
/*
* first check if the cmd is in the ready queue or
* in the active queue
*/
if (sp) {
IPRINTF3("aborting one command 0x%p for %d.%d\n",
if (rval) {
IPRINTF("aborted one ready cmd\n");
goto exit;
} else if ((sp !=
IPRINTF("cmd doesn't exist here\n");
goto exit;
}
}
/*
* hold off any new commands while attempting to abort
* an active cmd
*/
if (cur_sp) {
/*
* prevent completion on current cmd
*/
}
if (sp) {
/*
* the cmd exists here. is it connected or disconnected?
* if connected but still selecting then can't abort now.
* prevent completion on this cmd
*/
/* connected but not selecting? */
}
/* if abort connected cmd failed, try abort disconnected */
if ((rval == 0) &&
}
if (rval) {
}
} else {
IPRINTF2("aborting all commands for %d.%d\n",
/* active and not selecting ? */
}
if (rval == 0) {
}
}
done:
/* complete the current sp */
if (cur_sp) {
}
}
/* complete the sp passed as 2nd arg */
}
/* clean up all cmds for this slot */
/*
* mark all commands here as aborted
* abort msg has been accepted, now cleanup queues;
*/
}
exit:
(void) fas_ustart(fas);
}
#ifdef FASDEBUG
if (rval && fas_test_stop) {
debug_enter("abort succeeded");
}
#endif
return (rval);
}
/*
* mark all packets with new reason and update statistics
*/
static void
{
while (sp != 0) {
}
int n = 0;
n++;
}
}
}
}
/*
* set pkt_reason and OR in pkt_statistics flag
*/
static void
{
if (sp) {
}
IPRINTF3("sp=0x%p, pkt_reason=%x, pkt_stat=%x\n",
}
}
/*
* delete specified cmd from the ready queue
*/
static int
{
/*
* command has not been started yet and is still in the ready queue
*/
if (sp) {
/*
* find packet on the ready queue and remove it
*/
} else {
}
}
return (TRUE);
}
}
}
return (FALSE);
}
/*
* add cmd to to head of the readyQ
* due to tag allocation failure or preemption we have to return
* this cmd to the readyQ
*/
static void
{
/*
* never return a NOINTR pkt to the readyQ
* (fas_runpoll will resubmit)
*/
}
}
}
/*
* flush cmds in ready queue
*/
static void
{
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
{
return;
}
DPRINTF2("flushing entire tag queue, slot=%x, tcmds=%x\n",
#ifdef FASDEBUG
{
int n = 0;
n++;
0) {
debug_enter("fas_flush_tagQ");
}
}
}
}
}
#endif
do {
}
}
/*
* cleanup one active command
*/
static void
{
}
/*
* prepare a proxy cmd (a cmd sent on behalf of the target driver,
*/
static void
{
int i;
for (i = 0; i < nmsgs; i++) {
}
}
/*
* send a proxy cmd and check the result
*/
static int
{
int rval;
} else {
"Proxy %s failed for %d.%d, result=%x, reason=%x\n", what,
}
return (rval);
}
/*
* 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);
}
/*
* only abort while in data phase; otherwise we mess up msg phase
*/
return (rval);
}
IPRINTF3("Sending abort message %s to connected %d.%d\n",
fas->f_abort_msg_sent = 0;
/*
* now check if the msg was taken
* e_abort is set in fas_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
{
int rval;
/*
* if reset delay is active, we cannot start a selection
* and there shouldn't be a cmd outstanding
*/
return (FALSE);
}
if (sp)
IPRINTF1("aborting disconnected tagged cmd(s) with %s\n",
scsi_mname(msg));
} else {
}
return (rval);
}
/*
* reset handling:
* fas_do_scsi_reset assumes that we have already entered the mutex
*/
static int
{
IPRINTF3("fas_scsi_reset for slot %x, level=%x, tcmds=%x\n",
/*
* We know that fas_reset_bus() returns ACTION_RETURN.
*/
(void) fas_reset_bus(fas);
/*
* Now call fas_dopoll() to field the reset interrupt
* which will then call fas_reset_recovery which will
* call the completion function for all commands.
*/
/*
* reset fas
*/
(void) fas_reset_bus(fas);
CE_WARN, "reset scsi bus failed");
} else {
}
} else {
}
} else {
/*
* prevent new commands from starting
*/
/*
* 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 fas_reset_connected_cmd() failed,
* attempt a reset_disconnected_cmd
*/
}
/*
* cleanup if reset was successful
* complete the current sp first.
*/
if (cur_sp) {
}
}
}
} else {
/*
* restore throttles to max throttle, regardless
* of what it was (fas_set_throttles() will deal
* with reset delay active)
* restoring to the old throttle is not
* a such a good idea
*/
}
(void) fas_ustart(fas);
}
}
exit:
#ifdef FASDEBUG
if (rval && fas_test_stop) {
debug_enter("reset succeeded");
}
#endif
return (rval);
}
/*
* reset delay is handled by a separate watchdog; this ensures that
* regardless of fas_scsi_watchdog_tick, the reset delay will not change
*/
static void
{
if ((fas_reset_watch == 0) && FAS_CAN_SCHED) {
}
}
/*
*/
static void
{
if (!ddi_in_panic()) {
int i;
for (i = 0; i < NTARGETS_WIDE; i++) {
}
} else {
}
}
/*
* fas_watch_reset_delay(_subr) is invoked by timeout() and checks every
* fas instance for active reset delays
*/
/*ARGSUSED*/
static void
{
int not_done = 0;
fas_reset_watch = 0;
continue;
}
}
if (not_done) {
}
}
static int
{
short slot, s;
int done = 0;
/*
* check if a reset delay is active; if so back to full throttle
* which will unleash the cmds in the ready Q
*/
s = slot/NLUNS_PER_TARGET;
if (fas->f_reset_delay[s] != 0) {
EPRINTF2("target%d: reset delay=%d\n", s,
fas->f_reset_delay[s]);
if (fas->f_reset_delay[s] <= 0) {
/*
* clear throttle for all luns on this target
*/
fas->f_reset_delay[s] = 0;
slot, MAX_THROTTLE);
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
*/
(void) fas_ustart(fas);
}
return (done);
}
/*
* cleanup after a device reset. this affects all target's luns
*/
static void
{
/*
* reset msg has been accepted, now cleanup queues;
* for all luns of this target
*/
IPRINTF4("fas_reset_cleanup: slot %x, start=%x, end=%x, tcmds=%x\n",
/*
* if we are not in panic set up a reset delay for this target,
* a zero throttle forces all new requests into the ready Q
*/
if (!ddi_in_panic()) {
} else {
}
fas_flush_tagQ(fas, i);
fas_flush_readyQ(fas, i);
(struct arq_private_data *)
}
}
}
}
/*
* reset a currently disconnected target
*/
static int
{
int rval;
return (rval);
}
/*
* reset a target with a currently connected command
* Assert ATN and send MSG_DEVICE_RESET, zero throttles temporarily
* to prevent new cmds from starting regardless of the outcome
*/
static int
{
/*
* only attempt to reset in data phase; during other phases
* asserting ATN may just cause confusion
*/
return (rval);
}
IPRINTF2("Sending reset message to connected %d.%d\n",
fas->f_reset_msg_sent = 0;
/*
* poll for interrupts until bus free
*/
/*
* now check if the msg was taken
* f_reset is set in fas_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);
}
/*
* reset the scsi bus to blow all commands away
*/
static int
{
IPRINTF("fas_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);
}
/*
* fas_reset_recovery is called on the reset interrupt and cleans
* up all cmds (active or waiting)
*/
static int
{
int i;
int max_loop = 0;
IPRINTF("fas_reset_recovery:\n");
/*
* renegotiate wide and sync for all targets
*/
/*
* reset dma engine
*/
/*
* set throttles and reset delay
*/
/*
* clear interrupts until they go away
*/
max_loop++;
}
if (max_loop >= FAS_RESET_SPIN_MAX_LOOP) {
}
/*
* reset the chip, this shouldn't be necessary but sometimes
* we get a hang in the next data in phase
*/
/*
* reset was expected? if not, it must be external bus reset
*/
}
}
goto done;
}
/*
* completely reset the state of the softc data.
*/
/*
* Hold the state of the host adapter open
*/
/*
* 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.
*/
slot = start_slot;
do {
(struct arq_private_data *)
}
}
} while (slot != start_slot);
/*
* reset timeouts
*/
for (i = 0; i < N_SLOTS; i++) {
}
}
done:
/*
* Move the state back to free...
*/
/*
* perform the reset notification callbacks that are registered.
*/
/*
* if reset delay is still active a search is meaningless
* but do it anyway
*/
return (rval);
}
/*
* hba_tran ops for quiesce and unquiesce
*/
static int
{
return (-1);
}
return (fas_quiesce_bus(fas));
}
static int
{
return (-1);
}
return (fas_unquiesce_bus(fas));
}
#ifdef FAS_TEST
/*
* torture test functions
*/
static void
{
if ((fas_rtest_type == 1) &&
fas_rtest = 0;
}
} else if ((fas_rtest_type == 2) &&
fas_rtest = 0;
}
} else {
fas_rtest = 0;
}
}
}
}
static void
{
if ((fas_atest_disc == 0) && sp &&
}
int tag;
/*
* find the oldest tag
*/
!= 0)
break;
}
if (sp) {
} else {
return;
}
} else if (fas_atest_disc == 4 &&
} else if (fas_atest_disc == 7) {
if (fas_do_scsi_reset(&ap,
RESET_TARGET)) {
fas_atest = 0;
}
}
}
return;
} else {
return;
}
fas_atest = 0;
}
}
}
#endif /* FAS_TEST */
/*
* capability interface
*/
static int
{
int cidx;
if (cap == (char *)0) {
goto exit;
}
if (cidx == -1) {
} else if (doset) {
/*
* we usually don't allow setting capabilities for
* other targets!
*/
if (!tgtonly) {
goto exit;
}
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:
if (val)
else
break;
case SCSI_CAP_SYNCHRONOUS:
if (val) {
} else {
}
break;
case SCSI_CAP_TAGGED_QING:
{
/* do not allow with active tgt */
break;
}
if (val) {
IPRINTF1("target %d: TQ enabled\n",
target);
} else {
break;
}
} else {
IPRINTF1("target %d: TQ disabled\n",
target);
}
KM_NOSLEEP)) {
break;
}
break;
}
case SCSI_CAP_WIDE_XFER:
if (val) {
} else {
break;
}
} else {
}
break;
case SCSI_CAP_ARQ:
if (val) {
break;
}
} else {
break;
}
}
break;
case SCSI_CAP_QFULL_RETRIES:
break;
break;
default:
break;
}
} else if (doset == 0) {
switch (cidx) {
case SCSI_CAP_DMA_MAX:
/* very high limit because of multiple dma windows */
break;
case SCSI_CAP_MSG_OUT:
break;
case SCSI_CAP_DISCONNECT:
if (tgtonly &&
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:
}
break;
case SCSI_CAP_WIDE_XFER:
}
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) {
"fas_commoncap:tgt=%x,cap=%s,tgtonly=%x,doset=%x,val=%x,rval=%x\n",
}
return (rval);
}
/*
* property management
* fas_update_props:
*/
static void
{
}
}
static void
{
/*
* We cannot hold any mutex at this point because the call to
* ddi_prop_update_int() may block.
*/
}
}
/*
* allocate active slots array, size is dependent on whether tagQ enabled
*/
static int
{
IPRINTF("cannot change size of active slots array\n");
return (rval);
}
"fas_alloc_active_slots: target=%x size=%x, old=0x%p, oldsize=%x\n",
if (new_active == NULL) {
IPRINTF("new active alloc failed\n");
} else {
/*
* reserve tag 0 for non-tagged cmds to tagged targets
*/
}
if (old_active) {
}
rval = 0;
}
return (rval);
}
/*
* Error logging, printing, and debug print routines
*/
/*PRINTFLIKE3*/
static void
{
if (fas) {
} else {
dev = 0;
}
} else {
}
}
/*PRINTFLIKE2*/
static void
{
if (fas) {
} else {
}
}
#ifdef FASDEBUG
/*PRINTFLIKE2*/
void
{
if (fas) {
}
}
#endif
static void
{
"addr=%x dmacnt=%x test=%x last=%x last_cnt=%x",
"\tstep=%x fifoflag=%x conf=%x test=%x conf2=%x conf3=%x",
if (fas->f_current_sp) {
}
}
/*
* dump all we know about a cmd
*/
static void
{
int i;
buf[0] = '\0';
for (i = 0; i < (int)sp->cmd_actual_cdblen; i++) {
}
"pkt_state=0x%b pkt_flags=0x%x pkt_statistics=0x%x",
}
}
/*ARGSUSED*/
static void
{
int i;
buf[0] = '\0';
for (i = 0; i < (int)sp->cmd_actual_cdblen; i++) {
}
}
/*
* state decoding for error messages
*/
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
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,
"FROZEN", ACTS_FROZEN,
0
};
int i;
}
}
return ("<BAD>");
}