st.c revision 1cba8b6cee3f680c2277b786670ad0c9ec746efd
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* SCSI SCSA-compliant and not-so-DDI-compliant Tape Driver
*/
#define DEBUG 1
#endif
#include <sys/ddidmareq.h>
#include <sys/byteorder.h>
/*
*/
kstat_function(IOSP); \
}
#define ST_DO_ERRSTATS(un, x) \
if (un->un_errstats) { \
struct st_errstats *stp; \
}
int _lun; \
DDI_PROP_DONTPASS, SCSI_ADDR_PROP_LUN, 0); \
if (_lun > 0) { \
_lun; \
} \
}
/*
* get an available contig mem header, cp.
* when big_enough is true, we will return NULL, if no big enough
* contig mem is found.
* when big_enough is false, we will try to find cp containing big
* enough contig mem. if not found, we will ruturn the last cp available.
*
* used by st_get_contig_mem()
*/
} else { \
} \
(un)->un_contig_mem_available_num--; \
break; \
} \
} \
}
#define ONE_K 1024
return (EINVAL); \
} else { \
return (EINVAL); \
} \
/*
* Global External Data Definitions
*/
extern struct scsi_key_strings scsi_cmds[];
extern uchar_t scsi_cdb_size[];
/*
* Local Static Data
*/
static void *st_state;
static char *const st_label = "st";
static volatile int st_recov_sz = sizeof (recov_info);
static const char mp_misconf[] = {
"St Tape is misconfigured, MPxIO enabled and "
"tape-command-recovery-disable set in st.conf\n"
};
#ifdef __x86
/*
* We need to use below DMA attr to alloc physically contiguous
* memory to do I/O in big block size
*/
static ddi_dma_attr_t st_contig_mem_dma_attr = {
DMA_ATTR_V0, /* version number */
0x0, /* lowest usable address */
0xFFFFFFFFull, /* high DMA address range */
0xFFFFFFFFull, /* DMA counter register */
1, /* DMA address alignment */
1, /* DMA burstsizes */
1, /* min effective DMA size */
0xFFFFFFFFull, /* max DMA xfer size */
0xFFFFFFFFull, /* segment boundary */
1, /* s/g list length */
1, /* granularity of device */
0 /* DMA transfer flags */
};
static ddi_device_acc_attr_t st_acc_attr = {
};
/* set limitation for the number of contig_mem */
static int st_max_contig_mem_num = ST_MAX_CONTIG_MEM_NUM;
#endif
/*
* Tunable parameters
*
* DISCLAIMER
* ----------
* These parameters are intended for use only in system testing; if you use
* them in production systems, you do so at your own risk. Altering any
* variable not listed below may cause unpredictable system behavior.
*
* st_check_media_time
*
* Three second state check
*
* st_allow_large_xfer
*
* Gated with ST_NO_RECSIZE_LIMIT
*
* 0 - Transfers larger than 64KB will not be allowed
* regardless of the setting of ST_NO_RECSIZE_LIMIT
* 1 - Transfers larger than 64KB will be allowed
* if ST_NO_RECSIZE_LIMIT is TRUE for the drive
*
* st_report_soft_errors_on_close
*
* Gated with ST_SOFT_ERROR_REPORTING
*
* 0 - Errors will not be reported on close regardless
* of the setting of ST_SOFT_ERROR_REPORTING
*
* 1 - Errors will be reported on close if
* ST_SOFT_ERROR_REPORTING is TRUE for the drive
*/
static int st_selection_retry_count = ST_SEL_RETRY_COUNT;
static int st_retry_count = ST_RETRY_COUNT;
static int st_io_time = ST_IO_TIME;
static int st_long_timeout_x = ST_LONG_TIMEOUT_X;
static int st_space_time = ST_SPACE_TIME;
static int st_long_space_time_x = ST_LONG_SPACE_TIME_X;
static int st_error_level = SCSI_ERR_RETRYABLE;
static int st_max_throttle = ST_MAX_THROTTLE;
static int st_allow_large_xfer = 1;
static int st_report_soft_errors_on_close = 1;
/*
* End of tunable parameters list
*/
/*
* Asynchronous I/O and persistent errors, refer to PSARC/1995/228
*
* Asynchronous I/O's main offering is that it is a non-blocking way to do
* reads and writes. The driver will queue up all the requests it gets and
* have them ready to transport to the HBA. Unfortunately, we cannot always
* just ship the I/O requests to the HBA, as there errors and exceptions
* that may happen when we don't want the HBA to continue. Therein comes
* the flush-on-errors capability. If the HBA supports it, then st will
* send in st_max_throttle I/O requests at the same time.
*
* Persistent errors : This was also reasonably simple. In the interrupt
* routines, if there was an error or exception (FM, LEOT, media error,
* transport error), the persistent error bits are set and shuts everything
* down, but setting the throttle to zero. If we hit and exception in the
* HBA, and flush-on-errors were set, we wait for all outstanding I/O's to
* come back (with CMD_ABORTED), then flush all bp's in the wait queue with
* the appropriate error, and this will preserve order. Of course, depending
* on the exception we have to show a zero read or write before we show
* errors back to the application.
*/
extern const int st_ndrivetypes; /* defined in st_conf.c */
extern const struct st_drivetype st_drivetypes[];
extern const char st_conf_version[];
#ifdef STDEBUG
static int st_soft_error_report_debug = 0;
volatile int st_debug = 0;
static volatile dev_info_t *st_lastdev;
static kmutex_t st_debug_mutex;
#endif
#define ST_MT02_NAME "Emulex MT02 QIC-11/24 "
static const struct vid_drivetype {
char *vid;
char type;
} st_vid_dt[] = {
{"LTO-CVE ", MT_LTO},
{"QUANTUM ", MT_ISDLT},
{"SONY ", MT_ISAIT},
{"STK ", MT_ISSTK9840}
};
static const struct driver_minor_data {
char *name;
int minor;
} st_minor_data[] = {
/*
* The top 4 entries are for the default densities,
* don't alter their position.
*/
{"", 0},
{"n", MT_NOREWIND},
{"b", MT_BSD},
{"l", MT_DENSITY1},
{"m", MT_DENSITY2},
{"h", MT_DENSITY3},
{"c", MT_DENSITY4},
{"u", MT_DENSITY4},
};
/* strings used in many debug and warning messages */
static const char wr_str[] = "write";
static const char rd_str[] = "read";
static const char wrg_str[] = "writing";
static const char rdg_str[] = "reading";
static const char *space_strs[] = {
"records",
"filemarks",
"sequential filemarks",
"eod",
"setmarks",
"sequential setmarks",
"Reserved",
"Reserved"
};
static const char *load_strs[] = {
"unload", /* LD_UNLOAD 0 */
"load", /* LD_LOAD 1 */
"retension", /* LD_RETEN 2 */
"load reten", /* LD_LOAD | LD_RETEN 3 */
"eod", /* LD_EOT 4 */
"load EOD", /* LD_LOAD | LD_EOT 5 */
"reten EOD", /* LD_RETEN | LD_EOT 6 */
"load reten EOD" /* LD_LOAD|LD_RETEN|LD_EOT 7 */
"hold", /* LD_HOLD 8 */
"load and hold" /* LD_LOAD | LD_HOLD 9 */
};
static const char *errstatenames[] = {
"COMMAND_DONE",
"COMMAND_DONE_ERROR",
"COMMAND_DONE_ERROR_RECOVERED",
"QUE_COMMAND",
"QUE_BUSY_COMMAND",
"QUE_SENSE",
"JUST_RETURN",
"COMMAND_DONE_EACCES",
"QUE_LAST_COMMAND",
"COMMAND_TIMEOUT",
"PATH_FAILED",
"DEVICE_RESET",
"DEVICE_TAMPER",
"ATTEMPT_RETRY"
};
const char *bogusID = "Unknown Media ID";
/* default density offsets in the table above */
#define DEF_BLANK 0
#define DEF_NOREWIND 1
#define DEF_BSD 2
#define DEF_BSD_NR 3
static struct tape_failure_code {
} st_tape_failure_code[] = {
{ 0xff}
};
/* clean bit position and mask */
static struct cln_bit_position {
} st_cln_bit_position[] = {
{ 21, 0x08},
{ 70, 0xc0},
{ 18, 0x81} /* 80 bit indicates in bit mode, 1 bit clean light is on */
};
/*
* architecture dependent allocation restrictions. For x86, we'll set
* dma_attr_addr_hi to st_max_phys_addr and dma_attr_sgllen to
* st_sgl_size during _init().
*/
#if defined(__sparc)
static ddi_dma_attr_t st_alloc_attr = {
DMA_ATTR_V0, /* version number */
0x0, /* lowest usable address */
0xFFFFFFFFull, /* high DMA address range */
0xFFFFFFFFull, /* DMA counter register */
1, /* DMA address alignment */
1, /* DMA burstsizes */
1, /* min effective DMA size */
0xFFFFFFFFull, /* max DMA xfer size */
0xFFFFFFFFull, /* segment boundary */
1, /* s/g list length */
512, /* granularity of device */
0 /* DMA transfer flags */
};
static ddi_dma_attr_t st_alloc_attr = {
DMA_ATTR_V0, /* version number */
0x0, /* lowest usable address */
0x0, /* high DMA address range [set in _init()] */
0xFFFFull, /* DMA counter register */
512, /* DMA address alignment */
1, /* DMA burstsizes */
1, /* min effective DMA size */
0xFFFFFFFFull, /* max DMA xfer size */
0xFFFFFFFFull, /* segment boundary */
0, /* s/g list length */
512, /* granularity of device [set in _init()] */
0 /* DMA transfer flags */
};
int st_sgl_size = 0xF;
#endif
/*
* Configuration Data:
*
* Device driver ops vector
*/
st_open, /* open */
st_close, /* close */
st_queued_strategy, /* strategy Not Block device but async checks */
nodev, /* print */
nodev, /* dump */
st_read, /* read */
st_write, /* write */
st_ioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
ddi_prop_op, /* cb_prop_op */
0, /* streamtab */
D_OPEN_RETURNS_EINTR, /* cb_flag */
CB_REV, /* cb_rev */
st_aread, /* async I/O read entry point */
st_awrite /* async I/O write entry point */
};
void **result);
DEVO_REV, /* devo_rev, */
0, /* refcnt */
st_info, /* info */
nulldev, /* identify */
st_probe, /* probe */
st_attach, /* attach */
st_detach, /* detach */
nodev, /* reset */
&st_cb_ops, /* driver operations */
(struct bus_ops *)0, /* bus operations */
nulldev, /* power */
ddi_quiesce_not_needed, /* devo_quiesce */
};
/*
* Local Function Declarations
*/
static char *st_print_scsi_cmd(char cmd);
static int st_get_conf_from_st_dot_conf(struct scsi_tape *, char *,
struct st_drivetype *);
static int st_get_conf_from_st_conf_dot_c(struct scsi_tape *, char *,
struct st_drivetype *);
static int st_get_conf_from_tape_drive(struct scsi_tape *, char *,
struct st_drivetype *);
static int st_get_densities_from_tape_drive(struct scsi_tape *,
struct st_drivetype *);
static int st_get_timeout_values_from_tape_drive(struct scsi_tape *,
struct st_drivetype *);
ushort_t);
static int st_get_default_conf(struct scsi_tape *, char *,
struct st_drivetype *);
tapepos_t *);
tapepos_t *);
struct scsi_arq_status *cmd);
struct scsi_arq_status *, tapepos_t *);
static void st_delayed_cv_broadcast(void *arg);
static void st_intr_restart(void *arg);
static void st_start_restart(void *arg);
struct read_blklim *read_blk);
int count);
int post_space);
int infront);
static void st_recover(void *arg);
int flag);
#ifdef __x86
/*
* routines for I/O in big block size
*/
int alloc_flags);
#endif
/*
*/
static int st_create_errstats(struct scsi_tape *, int);
#ifdef STDEBUG
#endif /* STDEBUG */
#if !defined(lint)
#endif
/*
* autoconfiguration routines.
*/
char _depends_on[] = "misc/scsi";
&mod_driverops, /* Type of module. This one is a driver */
"SCSI tape Driver", /* Name of the module. */
&st_ops /* driver ops */
};
static struct modlinkage modlinkage = {
};
/*
* Notes on Post Reset Behavior in the tape driver:
*
* When the tape drive is opened, the driver attempts to make sure that
* the tape head is positioned exactly where it was left when it was last
* closed provided the medium is not changed. If the tape drive is
* opened in O_NDELAY mode, the repositioning (if necessary for any loss
* of position due to reset) will happen when the first tape operation or
* I/O occurs. The repositioning (if required) may not be possible under
* certain situations such as when the device firmware not able to report
* the medium change in the REQUEST SENSE data because of a reset or a
* misbehaving bus not allowing the reposition to happen. In such
* extraordinary situations, where the driver fails to position the head
* at its original position, it will fail the open the first time, to
* save the applications from overwriting the data. All further attempts
* to open the tape device will result in the driver attempting to load
* the tape at BOT (beginning of tape). Also a warning message to
* indicate that further attempts to open the tape device may result in
* the tape being loaded at BOT will be printed on the console. If the
* tape device is opened in O_NDELAY mode, failure to restore the
* original tape head position, will result in the failure of the first
* tape operation or I/O, Further, the driver will invalidate its
* internal tape position which will necessitate the applications to
* validate the position by using either a tape positioning ioctl (such
* as MTREW) or closing and reopening the tape device.
*
*/
int
_init(void)
{
int e;
if (((e = ddi_soft_state_init(&st_state,
sizeof (struct scsi_tape), ST_MAXUNIT)) != 0)) {
return (e);
}
if ((e = mod_install(&modlinkage)) != 0) {
} else {
#ifdef STDEBUG
#endif
#if defined(__x86)
/* set the max physical address for iob allocs on x86 */
/*
* set the sgllen for iob allocs on x86. If this is set less
* than the number of pages the buffer will take
* (taking into account alignment), it would force the
* allocator to try and allocate contiguous pages.
*/
#endif
}
return (e);
}
int
_fini(void)
{
int e;
if ((e = mod_remove(&modlinkage)) != 0) {
return (e);
}
#ifdef STDEBUG
#endif
return (e);
}
int
{
}
static int
{
int instance;
struct scsi_device *devp;
int rval;
#if !defined(__sparc)
char *tape_prop;
int tape_prop_len;
#endif
/* If self identifying device */
return (DDI_PROBE_DONTCARE);
}
#if !defined(__sparc)
/*
* Since some x86 HBAs have devnodes that look like SCSI as
* far as we can tell but aren't really SCSI (DADK, like mlx)
* we check for the presence of the "tape" property.
*/
DDI_PROP_CANSLEEP, "tape",
return (DDI_PROBE_FAILURE);
}
return (DDI_PROBE_FAILURE);
}
#endif
return (DDI_PROBE_PARTIAL);
}
/*
* Turn around and call probe routine to see whether
* we actually have a tape at this SCSI nexus.
*/
/*
* In checking the whole inq_dtype byte we are looking at both
* the Peripheral Qualifier and the Peripheral Device Type.
* For this driver we are only interested in sequential devices
* that are connected or capable if connecting to this logical
* unit.
*/
(DTYPE_SEQUENTIAL | DPQ_POSSIBLE)) {
"probe exists\n");
} else {
}
} else {
"probe failure: nothing there\n");
}
return (rval);
}
static int
{
int instance;
int wide;
int dev_instance;
int ret_status;
struct scsi_device *devp;
int node_ix;
switch (cmd) {
case DDI_ATTACH:
"tape-command-recovery-disable", 0) != 0) {
st_recov_sz = sizeof (pkt_info);
}
return (DDI_FAILURE);
}
break;
case DDI_RESUME:
/*
*
* When the driver suspended, there might be
* outstanding cmds and therefore we need to
* reset the suspended flag and resume the scsi
* watch thread and restart commands and timeouts
*/
return (DDI_FAILURE);
}
un->un_tids_at_suspend = 0;
if (un->un_swr_token) {
}
/*
* Restart timeouts
*/
}
}
/*
* now check if we need to restore the tape position
*/
if (ret_status != 0) {
/*
* tape didn't get good TUR
* just print out error messages
*/
"st_attach-RESUME: tape failure "
" tape position will be lost");
} else {
/* this prints errors */
(void) st_validate_tapemarks(un,
}
/*
* there are no retries, if there is an error
* we don't know if the tape has changed
*/
}
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
"st_attach: instance=%x\n", instance);
/*
* Add a zero-length attribute to tell the world we support
* kernel ioctls (for layered drivers)
*/
DDI_KERNEL_IOCTL, NULL, 0);
/*
* If it's a SCSI-2 tape drive which supports wide,
* tell the host adapter to use wide.
*/
}
/*
* enable autorequest sense; keep the rq packet around in case
* the autorequest sense fails because of a busy condition
* do a getcap first in case the capability is not variable
*/
} else {
un->un_arq_enabled =
}
/*
* XXX - This is just for 2.6. to tell users that write buffering
* has gone away.
*/
"tape-driver-buffering", 0) != 0) {
"Write Data Buffering has been depricated. Your "
"applications should continue to work normally.\n"
" But, they should ported to use Asynchronous "
" I/O\n"
" For more information, read about "
" tape-driver-buffering "
"property in the st(7d) man page\n");
}
}
un->un_flush_on_errors = 0;
"throttle=%x, max_throttle = %x\n",
/* initialize persistent errors to nil */
un->un_persistence = 0;
un->un_persist_errors = 0;
/*
* Get dma-max from HBA driver. If it is not defined, use 64k
*/
"Received a value that looked like -1. Using 64k maxdma");
}
#ifdef __x86
/*
* for x86, the device may be able to DMA more than the system will
* allow under some circumstances. We need account for both the HBA's
* and system's contraints.
*
* Get the maximum DMA under worse case conditions. e.g. looking at the
* device constraints, the max copy buffer size, and the worse case
* fragmentation. NOTE: this may differ from dma-max since dma-max
* doesn't take the worse case framentation into account.
*
* e.g. a device may be able to DMA 16MBytes, but can only DMA 1MByte
* if none of the pages are contiguous. Keeping track of both of these
* values allows us to support larger tape block sizes on some devices.
*/
1);
/*
* If the dma-max-arch capability is not implemented, or the value
* comes back higher than what was reported in dma-max, use dma-max.
*/
}
#endif
/*
* Get the max allowable cdb size
*/
un->un_max_cdb_sz =
}
un->un_multipath = 0;
} else {
}
/*
* initialize kstats
*/
}
/*
* find the drive type for this target
*/
int minor;
char *name;
/*
* For default devices set the density to the
* preferred default density for this device.
*/
if (node_ix <= DEF_BSD_NR) {
}
continue;
}
#ifdef __x86
#endif
if (un->un_recov_taskq) {
}
}
if (un->un_recov_buf) {
}
if (un->un_uscsi_rqs_buf) {
}
}
if (un->un_dp_size) {
}
}
if (un->un_errstats) {
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* st_detach:
*
* we allow a detach if and only if:
* - no tape is currently inserted
* - tape position is at BOT or unknown
* (if it is not at BOT then a no rewind
* device was opened and we have to preserve state)
* - it must be in a closed state : no timeouts or scsi_watch requests
* will exist if it is closed, so we don't need to check for
* them here.
*/
/*ARGSUSED*/
static int
{
int instance;
int result;
struct scsi_device *devp;
return (DDI_FAILURE);
}
/*
* Clear error entry stack
*/
switch (cmd) {
case DDI_DETACH:
/*
* Undo what we did in st_attach & st_doattach,
* freeing resources and removing things we installed.
* The system framework guarantees we are not active
* with this devinfo node in any other entry points at
* this time.
*/
"st_detach: instance=%x, un=%p\n", instance,
(void *)un);
/*
* we cannot unload some targets because the
* inquiry returns junk unless immediately
* after a reset
*/
"cannot unload instance %x\n", instance);
return (DDI_FAILURE);
}
/*
* if the tape has been removed then we may unload;
* do a test unit ready and if it returns NOT READY
* then we assume that it is safe to unload.
* as a side effect, pmode may be set to invalid if the
* the test unit ready fails;
* also un_state may be set to non-closed, so reset it
*/
/*
* Send Test Unit Ready in the hopes that if
* the drive is not in the state we think it is.
* And the state will be changed so it can be detached.
* If the command fails to reach the device and
* the drive was not rewound or unloaded we want
* to fail the detach till a user command fails
* where after the detach will succead.
*/
/*
* After TUR un_state may be set to non-closed,
* so reset it back.
*/
}
"un_status=%x, fileno=%x, blkno=%x\n",
/*
* check again:
* if we are not at BOT then it is not safe to unload
*/
"cannot detach: pmode=%d fileno=0x%x, blkno=0x%x"
return (DDI_FAILURE);
}
/*
* Just To make sure that we have released the
* tape unit .
*/
}
/*
* now remove other data structures allocated in st_doattach()
*/
"destroying/freeing\n");
if (un->un_recov_taskq) {
}
if (un->un_hib_tid) {
un->un_hib_tid = 0;
}
if (un->un_delay_tid) {
un->un_delay_tid = 0;
}
#ifdef __x86
}
#endif
}
if (un->un_recov_buf) {
}
if (un->un_uscsi_rqs_buf) {
}
}
}
if (un->un_mkr_pkt) {
}
if (un->un_arq_enabled) {
}
if (un->un_dp_size) {
}
}
if (un->un_errstats) {
}
if (un->un_media_id_len) {
}
devp = ST_SCSI_DEVP;
return (DDI_SUCCESS);
case DDI_SUSPEND:
/*
*
* To process DDI_SUSPEND, we must do the following:
*
* - check ddi_removing_power to see if power will be turned
* off. if so, return DDI_FAILURE
* - check if we are already suspended,
* if so, return DDI_FAILURE
* - check if device state is CLOSED,
* if not, return DDI_FAILURE.
* - wait until outstanding operations complete
* - save tape state
* - block new operations
* - cancel pending timeouts
*
*/
if (ddi_removing_power(devi)) {
return (DDI_FAILURE);
}
/*
* Shouldn't already be suspended, if so return failure
*/
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* Wait for all outstanding I/O's to complete
*
* we wait on both ncmds and the wait queue for times
* when we are flushing after persistent errors are
* flagged, which is when ncmds can be 0, and the
* queue can still have I/O's. This way we preserve
* order of biodone's.
*/
wait_cmds_complete) == -1) {
/*
* Time expired then cancel the command
*/
if (un->un_last_throttle) {
un->un_throttle =
}
return (DDI_FAILURE);
} else {
break;
}
}
}
/*
* DDI_SUSPEND says that the system "may" power down, we
* remember the file and block number before rewinding.
* we also need to save state before issuing
* any WRITE_FILE_MARK command.
*/
/*
* Issue a zero write file fmk command to tell the drive to
* flush any buffered tape marks
*/
/*
* Because not all tape drives correctly implement buffer
* flushing with the zero write file fmk command, issue a
* synchronous rewind command to force data flushing.
* st_validate_tapemarks() will do a rewind during DDI_RESUME
* anyway.
*/
/* stop any new operations */
un->un_throttle = 0;
/*
* cancel any outstanding timeouts
*/
if (un->un_delay_tid) {
un->un_delay_tid = 0;
}
if (un->un_hib_tid) {
un->un_hib_tid = 0;
}
/*
* Suspend the scsi_watch_thread
*/
if (un->un_swr_token) {
} else {
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
/* ARGSUSED */
static int
{
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
return (DDI_FAILURE);
*result = (void *) ST_DEVINFO;
error = DDI_SUCCESS;
break;
case DDI_INFO_DEVT2INSTANCE:
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
}
return (error);
}
static int
{
recov_info *ri;
int instance;
/*
* Call the routine scsi_probe to do some of the dirty work.
* If the INQUIRY command succeeds, the field sd_inq in the
* device structure will be filled in.
*/
"st_doattach(): probing\n");
/*
* In checking the whole inq_dtype byte we are looking at both
* the Peripheral Qualifier and the Peripheral Device Type.
* For this driver we are only interested in sequential devices
* that are connected or capable if connecting to this logical
* unit.
*/
(DTYPE_SEQUENTIAL | DPQ_POSSIBLE)) {
"probe exists\n");
} else {
/* Something there but not a tape device */
return (DDI_FAILURE);
}
} else {
/* Nothing there */
"probe failure: nothing there\n");
return (DDI_FAILURE);
}
/*
* The actual unit is present.
* Now is the time to fill in the rest of our info..
*/
goto error;
}
goto error;
}
goto error;
}
SCMD_REQUEST_SENSE, 0, MAX_SENSE_LENGTH, 0);
if (st_recov_sz == sizeof (recov_info)) {
} else {
}
/*
* use i_ddi_mem_alloc() for now until we have an interface to allocate
* memory for DMA which doesn't require a DMA handle. ddi_iopb_alloc()
* is obsolete and we want more flexibility in controlling the DMA
* address constraints.
*/
"probe partial failure: no space\n");
goto error;
}
#ifdef __x86
#endif
/* Initialize power managemnet condition variable */
/*
* setting long a initial as it contains logical file info.
* support for long format is mandatory but many drive don't do it.
*/
#ifdef __x86
"allocation of contiguous memory dma handle failed!");
goto error;
}
#endif
/*
* Since this driver manages devices with "remote" hardware,
* i.e. the devices themselves have no "reg" properties,
* called by the power management framework unless we request
* it by creating a "pm-hardware-state" property and setting it
* to value "needs-suspend-resume".
*/
"pm-hardware-state", "needs-suspend-resume") !=
"ddi_prop_update(\"pm-hardware-state\") failed\n");
goto error;
}
"ddi_prop_create(\"no-involuntary-power-cycles\") "
"failed\n");
goto error;
}
return (DDI_SUCCESS);
if (un) {
}
if (un->un_read_pos_data) {
}
}
if (un->un_recov_buf) {
}
if (un->un_uscsi_rqs_buf) {
}
#ifdef __x86
}
#endif
}
}
}
}
return (DDI_FAILURE);
}
typedef int
static cfg_functp config_functs[] = {
};
/*
* determine tape type, using tape-config-list or built-in table or
* use a generic tape config entry
*/
static void
{
struct st_drivetype *dp;
: ST_RELEASE;
/*
* XXX: Emulex MT-02 (and emulators) predates SCSI-1 and has
* no vid & pid inquiry data. So, we provide one.
*/
if (ST_INQUIRY->inq_len == 0 ||
}
if (un->un_dp_size == 0) {
} else {
}
/*
* Loop through the configuration methods till one works.
*/
break;
}
}
int result;
/* try and enable TLR */
/*
* From attach command failed.
* Set dp type so is run again on open.
*/
} else if (result == 0) {
"tran-layer-retries", 1) == -1) {
} else {
}
} else {
}
}
/*
* If we didn't just make up this configuration and
* all the density codes are the same..
* Set Auto Density over ride.
*/
if (*config_funct != st_get_default_conf) {
/*
* If this device is one that is configured and all
* densities are the same, This saves doing gets and set
* that yield nothing.
*/
}
}
/*
* Store tape drive characteristics.
*/
/* setup operation time-outs based on options */
/* make sure if we are supposed to be variable, make it variable */
}
: ST_RELEASE)) {
}
}
typedef struct {
int mask;
int bottom;
int top;
char *name;
} conf_limit;
static const conf_limit conf_limits[] = {
-1, 1, 2, "conf version",
-1, 0, 0xffffff, "block size",
-1, 0, 4, "number of densities",
-1, 0, 3, "default density",
0, 0, 0, NULL
};
static int
const char *conf_name)
{
int dens;
int ndens;
int value;
int type;
int count;
"%s %s value invalid bits set: 0x%X\n",
"%s %s value too low: value = %d limit %d\n",
"%s %s value too high: value = %d limit %d\n",
} else {
"%s %s value = 0x%X\n",
}
/* If not the number of densities continue */
continue;
}
/* If number of densities is not in range can't use config */
return (-1);
}
"%s conf version 1 with %d densities has %d items"
" should have %d",
"%s conf version 2 with %d densities has %d items"
" should have %d",
}
limit++;
count--;
list++;
"%s density[%d] value too low: value ="
" 0x%X limit 0x%X\n",
"%s density[%d] value too high: value ="
" 0x%X limit 0x%X\n",
} else {
"%s density[%d] value = 0x%X\n",
}
}
}
return (0);
}
static int
struct st_drivetype *dp)
{
int *data_ptr;
int version;
int found = 0;
/*
* Determine type of tape controller. Type is determined by
* checking the vendor ids of the earlier inquiry command and
* comparing those with vids in tape-config-list defined in st.conf
*/
!= DDI_PROP_SUCCESS) {
return (found);
}
"st_get_conf_from_st_dot_conf(): st.conf has tape-config-list\n");
/*
* Compare vids in each triplet - if it matches, get value for
* data_name and contruct a st_drivetype struct
* tripletlen is not set yet!
*/
len > 0;
if (vidlen == 0) {
continue;
}
/*
* If inquiry vid dosen't match this triplets vid,
* try the next.
*/
continue;
}
/*
* if prettylen is zero then use the vid string
*/
if (prettylen == 0) {
}
"vid = %s, pretty=%s, dataname = %s\n",
/*
* get the data list
*/
&data_list_len) != DDI_PROP_SUCCESS) {
/*
* Error in getting property value
* print warning!
*/
"data property (%s) has no value\n",
continue;
}
/*
* now initialize the st_drivetype struct
*/
/*
* check if data is enough for version, type,
* bsize, options, # of densities, density1,
* density2, ..., default_density
*/
if ((data_list_len < 5 * sizeof (int)) ||
(data_list_len < 6 * sizeof (int) +
*(data_ptr + 4) * sizeof (int))) {
/*
* print warning and skip to next triplet.
*/
"data property (%s) incomplete\n",
continue;
}
data_list_len / sizeof (int), datanameptr)) {
"data property (%s) rejected\n",
continue;
}
/*
* check version
*/
/* print warning but accept it */
"Version # for data property (%s) "
"not set to 1 or 2\n", datanameptr);
}
for (i = 0; i < NDENSITIES; i++) {
if (i < len) {
}
}
if (version == 2 &&
data_ptr++;
}
found = 1;
"found in st.conf: vid = %s, pretty=%s\n",
break;
}
/*
* free up the memory allocated by ddi_getlongprop
*/
if (config_list) {
}
return (found);
}
static int
struct st_drivetype *dp)
{
int i;
/*
* Determine type of tape controller. Type is determined by
* checking the result of the earlier inquiry command and
* comparing vendor ids with strings in a table declared in st_conf.c.
*/
"st_get_conf_from_st_conf_dot_c(): looking at st_drivetypes\n");
for (i = 0; i < st_ndrivetypes; i++) {
if (st_drivetypes[i].length == 0) {
continue;
}
st_drivetypes[i].length)) {
continue;
}
return (1);
}
return (0);
}
static int
struct st_drivetype *dp)
{
int bsize;
struct st_drivetype *tem_dp;
struct read_blklim *blklim;
int rval;
int i;
/*
* Determine the type of tape controller. Type is determined by
* sending SCSI commands to tape drive and deriving the type from
* the returned data.
*/
"st_get_conf_from_tape_drive(): asking tape drive\n");
/*
* Make up a name
*/
/*
* 'clean' vendor and product strings of non-printing chars
*/
for (i = 0; i < VIDPIDLEN - 1; i ++) {
}
}
/*
* MODE SENSE to determine block size.
*/
if (rval) {
rval = 1;
} else {
rval = 0;
}
"st_get_conf_from_tape_drive(): fail to mode sense\n");
goto exit;
}
/* Can mode sense page 0x10 or 0xf */
if (bsize == 0) {
} else if (bsize > ST_MAXRECSIZE_FIXED) {
if (rval) {
rval = 1;
} else {
rval = 0;
"st_get_conf_from_tape_drive(): "
"Fixed record size is too large and"
"cannot switch to variable record size");
}
goto exit;
}
} else {
if (rval == 0) {
} else {
rval = 1;
goto exit;
}
}
/*
* If READ BLOCk LIMITS works and upper block size limit is
* more than 64K, ST_NO_RECSIZE_LIMIT is supported.
*/
if (rval) {
"st_get_conf_from_tape_drive(): "
"fail to read block limits.\n");
rval = 0;
goto exit;
}
if (maxbsize > ST_MAXRECSIZE_VARIABLE) {
}
/*
* Inquiry VPD page 0xb0 to see if the tape drive supports WORM
*/
if (rval) {
"st_get_conf_from_tape_drive(): "
"fail to read vitial inquiry.\n");
rval = 0;
goto exit;
}
}
/* Assume BSD BSR KNOWS_EOD */
/*
* Decide the densities supported by tape drive by sending
* REPORT DENSITY SUPPORT command.
*/
goto exit;
}
/*
* Decide the timeout values for several commands by sending
* REPORT SUPPORTED OPERATION CODES command.
*/
goto exit;
}
rval = 1;
exit:
return (rval);
}
static int
struct st_drivetype *dp)
{
int i, p;
struct report_density_desc *den_desc;
/*
* Since we have no idea how many densitiy support entries
* will be returned, we send the command firstly assuming
* there is only one. Then we can decide the number of
* entries by available density support length. If multiple
* entries exist, we will resend the command with enough
* buffer size.
*/
buflen = sizeof (struct report_density_header) +
sizeof (struct report_density_desc);
"st_get_conf_from_tape_drive(): fail to report density.\n");
return (0);
}
des_len =
if (num_den > 1) {
buflen = sizeof (struct report_density_header) +
sizeof (struct report_density_desc) * num_den;
"st_get_conf_from_tape_drive(): "
"fail to report density.\n");
return (0);
}
}
+ sizeof (struct report_density_header));
/*
* Decide the drive type by assigning organization
*/
for (i = 0; i < ST_NUM_MEMBERS(st_vid_dt); i ++) {
8) == 0) {
break;
}
}
if (i == ST_NUM_MEMBERS(st_vid_dt)) {
"st_get_conf_from_tape_drive(): "
"can't find match of assigned ort.\n");
return (0);
}
/*
* The tape drive may support many tape formats, but the st driver
* supports only the four highest densities. Since density code
* values are returned by ascending sequence, we start from the
* last entry of density support data block descriptor.
*/
p = 0;
if (p != 0) {
continue;
}
}
p ++;
}
}
switch (p) {
case 0:
break;
case 1:
break;
case 2:
if (deflt[0]) {
} else {
}
break;
case 3:
if (deflt[0]) {
} else if (deflt[1]) {
} else {
}
break;
default:
for (i = p; i > p - NDENSITIES; i --) {
}
if (deflt[0]) {
} else if (deflt[1]) {
} else if (deflt[2]) {
} else {
}
break;
}
return (1);
}
static int
struct st_drivetype *dp)
{
int rval;
if (rval) {
return (1);
}
return (0);
}
if (rval) {
return (1);
}
return (0);
}
if (rval) {
return (1);
}
return (0);
}
if (rval) {
return (1);
}
return (0);
}
if (rval) {
return (1);
}
return (0);
}
if (rval) {
return (1);
}
return (0);
}
if (rval) {
return (1);
}
return (0);
}
return (1);
}
static int
{
int rval;
buflen = sizeof (struct one_com_des) +
sizeof (struct com_timeout_des);
if (rval) {
"st_get_timeouts_value(): "
"fail to timeouts value for command %d.\n", option_code);
return (rval);
}
if ((support != SUPPORT_VALUES_SUPPORT_SCSI) &&
(support != SUPPORT_VALUES_SUPPORT_VENDOR)) {
"st_get_timeouts_value(): "
"command %d is not supported.\n", option_code);
return (ENOTSUP);
}
if (!ctdp) {
"st_get_timeouts_value(): "
"command timeout is not included.\n");
return (ENOTSUP);
}
/*
* Timeout value in seconds is 4 bytes, but we only support the lower 2
* bytes. If the higher 2 bytes are not zero, the timeout value is set
* to 0xFFFF.
*/
} else {
(*(timeouts + 11));
}
return (0);
}
static int
{
int i;
"st_get_default_conf(): making drivetype from INQ cmd\n");
/*
* Make up a name
*/
/*
* 'clean' vendor and product strings of non-printing chars
*/
for (i = 0; i < ST_NAMESIZE - 2; i++) {
}
}
return (1); /* Can Not Fail */
}
/*
* Regular Unix Entry points
*/
/* ARGSUSED */
static int
{
int rval = 0;
/*
* validate that we are addressing a sensible unit
*/
"st_open(node = %s dev = 0x%lx, flag = %d, otyp = %d)\n",
/*
* All device accesss go thru st_strategy() where we check
* suspend status
*/
if (!un->un_attached) {
if (!un->un_attached) {
goto exit;
}
}
/*
* Check for the case of the tape in the middle of closing.
* This isn't simply a check of the current state, because
* we could be in state of sensing with the previous state
* that of closing.
*
* And don't allow multiple opens.
*/
while (IS_CLOSING(un) ||
goto exit;
}
}
goto busy;
}
/*
* record current dev
*/
un->un_restore_pos = 0;
un->un_rqs_state = 0;
/*
* If we are opening O_NDELAY, or O_NONBLOCK, we don't check for
* anything, leave internal states alone, if fileno >= 0
*/
case invalid:
break;
case legacy:
/*
* If position is anything other than rewound.
*/
/*
* set un_read_only/write-protect status.
*
* If the tape is not bot we can assume
* that mspl->wp_status is set properly.
* else
* again to get the actual tape status.(since
* user might have replaced the tape)
* Hence make the st state OFFLINE so that
* we re-intialize the tape once again.
*/
un->un_read_only =
} else {
}
break;
case logical:
} else {
un->un_read_only =
}
break;
}
rval = 0;
} else {
/*
* Not opening O_NDELAY.
*/
/*
* Clear error entry stack
*/
rval = 0; /* so open doesn't fail */
} else if (rval) {
/*
* Release the tape unit, if reserved and not
* preserve reserve.
*/
if ((un->un_rsvd_status &
}
} else {
}
}
exit:
/*
* we don't want any uninvited guests scrogging our data when we're
* busy with something, so for successful opens or failed opens
* (except for EBUSY), reset these counters and state appropriately.
*/
if (rval) {
}
un->un_err_resid = 0;
un->un_retry_ct = 0;
}
busy:
return (rval);
}
static int
{
int err;
int rval = 0;
/*
* Clean up after any errors left by 'last' close.
* This also handles the case of the initial open.
*/
}
un->un_kbytes_xferred = 0;
/*
* do a throw away TUR to clear check condition
*/
/*
* If test unit ready fails because the drive is reserved
* by another host fail the open for no access.
*/
if (err) {
"st_tape_init: RESERVATION CONFLICT\n");
goto exit;
} else if ((un->un_rsvd_status &
ST_APPLICATION_RESERVATIONS) != 0) {
if ((ST_RQSENSE != NULL) &&
goto exit;
}
}
}
/*
* Tape self identification could fail if the tape drive is used by
* another host during attach time. We try to get the tape type
* again. This is also applied to any posponed configuration methods.
*/
}
/*
* If the tape type is still invalid, try to determine the generic
* configuration.
*/
if (rval) {
}
"st_tape_init: %s invalid type\n",
goto exit;
}
/*
* If this is a Unknown Type drive,
* Use the READ BLOCK LIMITS to determine if
* allow large xfer is approprate if not globally
* disabled with st_allow_large_xfer.
*/
} else {
/*
* If we allow_large_xfer (ie >64k) and have not yet found out
* the max block size supported by the drive,
* find it by issueing a READ_BLKLIM command.
* if READ_BLKLIM cmd fails, assume drive doesn't
*/
}
/*
* if maxbsize is unknown, set the maximum block size.
*/
/*
* Get the Block limits of the tape drive.
* if un->un_allow_large_xfer = 0 , then make sure
* that maxbsize is <= ST_MAXRECSIZE_FIXED.
*/
if (err) {
/* Retry */
}
if (!err) {
/*
* if cmd successful, use limit returned
*/
if ((un->un_maxbsize == 0) ||
(un->un_allow_large_xfer == 0 &&
/*
* Drive is not one that is configured, But the
* READ BLOCK LIMITS tells us it can do large
* xfers.
*/
}
/*
* If max and mimimum block limits are the
* same this is a fixed block size device.
*/
}
}
if (un->un_minbsize == 0) {
}
} else { /* error on read block limits */
"!st_tape_init: Error on READ BLOCK LIMITS,"
" errno = %d un_rsvd_status = 0x%X\n",
/*
* since read block limits cmd failed,
* do not allow large xfers.
* use old values in st_minphys
*/
} else {
un->un_allow_large_xfer = 0;
"!Disabling large transfers\n");
/*
* we guess maxbsize and minbsize
*/
} else {
}
/*
* Data Mod must be set,
* Even if read block limits fails.
* Prevents Divide By Zero in st_rw().
*/
}
}
}
if (rval) {
goto exit;
}
}
"maxdma = %d, maxbsize = %d, minbsize = %d, %s large xfer\n",
if (err != 0) {
goto exit;
}
/*
* Make sure the tape is ready
*/
/*
* allow open no media. Subsequent MTIOCSTATE
* with media present will complete the open
* logic.
*/
rval = 0;
goto exit;
} else {
"st_tape_init EIO no media, not opened "
"O_NONBLOCK|O_EXCL\n");
goto exit;
}
}
}
/*
* On each open, initialize block size from drivetype struct,
* as it could have been changed by MTSRSZ ioctl.
* Now, ST_VARIABLE simply means drive is capable of variable
* mode. All drives are assumed to support fixed records.
* Hence, un_bsize tells what mode the drive is in.
* un_bsize = 0 - variable record length
* = x - fixed record length is x
*/
/*
* If saved position is valid go there
*/
if (un->un_restore_pos) {
un->un_restore_pos = 0;
if (rval != 0) {
}
goto exit;
}
}
if (rval) {
}
"st_tape_init: %s can't open tape\n",
goto exit;
}
}
/*
* do a mode sense to pick up state of current write-protect,
* Could cause reserve and fail due to conflict.
*/
if (un->un_unit_attention_flags) {
goto exit;
}
}
/*
* If we are opening the tape for writing, check
* to make sure that the tape can be written.
*/
err = 0;
/*
* STK sets the wp bit if volsafe tape is loaded.
*/
} else {
goto exit;
}
} else {
}
} else {
}
"read_only = %d eof = %d oflag = %d\n",
}
}
/*
* If we're opening the tape write-only, we need to
* write 2 filemarks on the HP 1/2 inch drive, to
* create a null file.
*/
} else {
}
} else {
un->un_fmneeded = 0;
}
/*
* Make sure the density can be selected correctly.
* If WORM can only write at the append point which in most cases
* isn't BOP. st_determine_density() with a B_WRITE only attempts
* to set and try densities if a BOP.
*/
if (st_determine_density(un,
"st_tape_init: EIO can't determine density\n");
goto exit;
}
/*
* Destroy the knowledge that we have 'determined'
* density so that a later read at BOT comes along
* does the right density determination.
*/
un->un_density_known = 0;
/*
* Okay, the tape is loaded and either at BOT or somewhere past.
* Mark the state such that any I/O or tape space operations
*/
/*
* Set test append flag if writing.
* First write must check that tape is positioned correctly.
*/
/*
* if there are pending unit attention flags.
* Check that the media has not changed.
*/
if (un->un_unit_attention_flags) {
}
un->un_unit_attention_flags = 0;
}
exit:
un->un_err_resid = 0;
un->un_last_resid = 0;
un->un_last_count = 0;
"st_tape_init: return val = %x\n", rval);
return (rval);
}
/* ARGSUSED */
static int
{
int err = 0;
int count, last_state;
#ifdef __x86
#endif
/*
* wait till all cmds in the pipeline have been completed
*/
/* turn off persistent errors on close, as we want close to succeed */
/*
* set state to indicate that we are in process of closing
*/
/*
* BSD behavior:
* a close always causes a silent span to the next file if we've hit
* an EOF (but not yet read across it).
*/
}
}
/*
* SVR4 behavior for skipping to next file:
*
* If we have not seen a filemark, space to the next file
*
* If we have already seen the filemark we are physically in the next
* file and we only increment the filenumber
*/
case ST_NO_EOF:
/*
* if we were reading and did not read the complete file
* skip to the next file, leaving the tape correctly
* positioned to read the first record of the next file
* Check first for REEL if we are at EOT by trying to
* read a block
*/
"st_close : EIO can't space\n");
goto error_out;
}
break;
}
}
"st_close: EIO can't space #2\n");
goto error_out;
} else {
"st_close2: fileno=%x,blkno=%x,eof=%x\n",
}
break;
case ST_EOF_PENDING:
case ST_EOF:
break;
case ST_EOT:
case ST_EOT_PENDING:
case ST_EOM:
/* nothing to do */
break;
default:
}
}
/*
* For performance reasons (HP 88780), the driver should
* postpone writing the second tape mark until just before a file
* positioning ioctl is issued (e.g., rewind). This means that
* the user must not manually rewind the tape because the tape will
* be missing the second tape mark which marks EOM.
* However, this small performance improvement is not worth the risk.
*/
/*
* We need to back up over the filemark we inadvertently popped
* over doing a read in between the two filemarks that constitute
* logical eot for 1/2" tapes. Note that ST_EOT_PENDING is only
* set while reading.
*
* If we happen to be at physical eot (ST_EOM) (writing case),
* the writing of filemark(s) will clear the ST_EOM state, which
* we don't want, so we save this state and restore it later.
*/
"flag=%x, fmneeded=%x, lastop=%x, eof=%x\n",
if (minor & MT_NOREWIND) {
"st_close: EIO can't space #3\n");
goto error_out;
} else {
}
} else {
}
/*
* Do we need to write a file mark?
*
* only write filemarks if there are fmks to be written and
* - the last operation was a write
* or:
* - opened for wronly
* - no data was written
*/
(un->un_fmneeded > 0) &&
/* save ST_EOM state */
/*
* Note that we will write a filemark if we had opened
* the tape write only and no data was written, thus
* creating a null file.
*
* If the user already wrote one, we only have to write 1 more.
* If they wrote two, we don't have to write any.
*/
if (count > 0) {
"st_close : EIO can't wfm\n");
goto error_out;
}
(minor & MT_NOREWIND)) {
"st_close : EIO space fmk(-1)\n");
goto error_out;
}
/* fix up block number */
}
}
/*
* If we aren't going to be rewinding, and we were at
* physical eot, restore the state that indicates we
* are at physical eot. Once you have reached physical
* eot, and you close the tape, the only thing you can
* do on the next open is to rewind. Access to trailer
* records is only allowed without closing the device.
*/
}
}
/*
* report soft errors if enabled and available, if we never accessed
* the drive, don't get errors. This will prevent some DAT error
* messages upon LOG SENSE.
*/
(last_state != ST_STATE_OFFLINE)) {
goto error_out;
}
}
/*
* Do we need to rewind? Can we rewind?
*/
if ((minor & MT_NOREWIND) == 0 &&
/*
* We'd like to rewind with the
* 'immediate' bit set, but this
* causes problems on some drives
* where subsequent opens get a
* 'NOT READY' error condition
* back while the tape is rewinding,
* which is impossible to distinguish
* from the condition of 'no tape loaded'.
*
* Also, for some targets, if you disconnect
* with the 'immediate' bit set, you don't
* actually return right away, i.e., the
* target ignores your request for immediate
* return.
*
* Instead, we'll fire off an async rewind
* command. We'll mark the device as closed,
* and any subsequent open will stall on
* the first TEST_UNIT_READY until the rewind
* completes.
*/
/*
* Used to be if reserve was not supported we'd send an
* asynchronious rewind. Comments above may be slightly invalid
* as the immediate bit was never set. Doing an immedate rewind
* makes sense, I think fixes to not ready status might handle
* the problems described above.
*/
}
} else {
/* flush data for older drives per scsi spec. */
} else {
/* release the drive before rewind immediate */
if ((un->un_rsvd_status &
(ST_RESERVE | ST_PRESERVE_RESERVE)) ==
ST_RESERVE) {
st_uscsi_cmd)) {
}
}
/* send rewind with immediate bit set */
}
}
}
/*
* Setting positions invalid in case the rewind doesn't
* happen. Drives don't like to rewind if resets happen
* they will tend to move back to where the rewind was
* issued if a reset or something happens so that if a
* write happens the data doesn't get clobbered.
*
* Not a big deal if the position is invalid when the
* open occures it will do a read position.
*/
goto error_out;
}
}
/*
* eject tape if necessary
*/
if (un->un_eject_tape_on_failure) {
un->un_eject_tape_on_failure = 0;
"st_close : can't unload tape\n");
goto error_out;
} else {
"st_close : tape unloaded \n");
}
}
/*
* behaviour.
*/
if ((un->un_rsvd_status &
}
/*
* clear up state
*/
un->un_retry_ct = 0;
/* Restore the options to the init time settings */
} else {
}
} else {
}
} else {
}
/*
* Signal anyone awaiting a close operation to complete.
*/
/*
* any kind of error on closing causes all state to be tossed
*/
/*
* note that st_intr has already set
* un_pos.pmode to invalid.
*/
un->un_density_known = 0;
}
#ifdef __x86
/*
* free any contiguous mem alloc'ed for big block I/O
*/
while (cp) {
}
sizeof (struct contig_mem) + biosize());
}
un->un_contig_mem_total_num = 0;
un->un_max_contig_mem_len = 0;
#endif
"st_close3: return val = %x, fileno=%x, blkno=%x, eof=%x\n",
return (err);
}
/*
* These routines perform raw i/o operations.
*/
/* ARGSUSED2 */
static int
{
#ifdef STDEBUG
#endif
}
/* ARGSUSED2 */
static int
{
#ifdef STDEBUG
#endif
}
/* ARGSUSED */
static int
{
#ifdef STDEBUG
#endif
}
/* ARGSUSED */
static int
{
#ifdef STDEBUG
#endif
}
/*
* Due to historical reasons, old limits are: For variable-length devices:
* if greater than 64KB - 1 (ST_MAXRECSIZE_VARIABLE), block into 64 KB - 2
* ST_MAXRECSIZE_VARIABLE_LIMIT) requests; otherwise,
* (let it through unmodified. For fixed-length record devices:
* 63K (ST_MAXRECSIZE_FIXED) is max (default minphys).
*
* The new limits used are un_maxdma (retrieved using scsi_ifgetcap()
* from the HBA) and un_maxbsize (retrieved by sending SCMD_READ_BLKLIM
* command to the drive).
*
*/
static void
{
"st_minphys(bp = 0x%p): b_bcount = 0x%lx\n", (void *)bp,
if (un->un_allow_large_xfer) {
/*
* check un_maxbsize for variable length devices only
*/
}
/*
* can't go more that HBA maxdma limit in either fixed-length
* or variable-length tape drives.
*/
}
} else {
/*
* use old fixed limits
*/
}
} else {
}
}
}
/*
* For regular raw I/O and Fixed Block length devices, make sure
* the adjusted block count is a whole multiple of the device
* block size.
*/
}
}
static int
{
int rval = 0;
long len;
"st_rw(dev = 0x%lx, flag = %s)\n", dev,
/* get local copy of transfer length */
/*
* Clear error entry stack
*/
/*
* If in fixed block size mode and requested read or write
* is not an even multiple of that block size.
*/
"%s: not modulo %d block size\n",
}
/* If device has set granularity in the READ_BLKLIM we honor it. */
"%s: not modulo %d device granularity\n",
}
}
if (rval != 0) {
return (rval);
}
/*
* Reset this so it can be set if Berkeley and read over a filemark.
*/
un->un_silent_skip = 0;
/*
* if we have hit logical EOT during this xfer and there is not a
* full residue, then set eof back to ST_EOM to make sure that
* the user will see at least one zero write
* after this short write
*/
}
}
}
}
return (rval);
}
static int
{
int rval = 0;
long len;
"st_arw(dev = 0x%lx, flag = %s)\n", dev,
/* get local copy of transfer length */
/*
* If in fixed block size mode and requested read or write
* is not an even multiple of that block size.
*/
"%s: not modulo %d block size\n",
}
/* If device has set granularity in the READ_BLKLIM we honor it. */
"%s: not modulo %d device granularity\n",
}
}
if (rval != 0) {
return (rval);
}
rval =
/*
* if we have hit logical EOT during this xfer and there is not a
* full residue, then set eof back to ST_EOM to make sure that
* the user will see at least one zero write
* after this short write
*
* we keep this here just in case the application is not using
* persistent errors
*/
}
}
}
return (rval);
}
static int
{
int wasopening = 0;
/*
* validate arguments
*/
"st_queued_strategy: ENXIO error exit\n");
return (0);
}
}
"st_queued_strategy(): bcount=0x%lx, fileno=%d, blkno=%x, eof=%d\n",
/*
* If persistent errors have been flagged, just nix this one. We wait
* for any outstanding I/O's below, so we will be in order.
*/
goto exit;
}
/*
* If last command was non queued, wait till it finishes.
*/
while (un->un_sbuf_busy) {
/* woke up because of an error */
goto exit;
}
}
/*
* s_buf and recovery commands shouldn't come here.
*/
/*
* do it now.
*/
if ((un->un_rsvd_status &
(ST_RESERVE | ST_APPLICATION_RESERVATIONS)) == 0) {
goto exit;
}
/*
* Enter here to restore position for possible
* resets when the device was closed and opened
* in O_NDELAY mode subsequently
*/
0, SYNC_CMD);
}
}
/*
* If we are offline, we have to initialize everything first.
* This is to handle either when opened with O_NDELAY, or
* we just got a new tape in the drive, after an offline.
* We don't observe O_NDELAY past the open,
* as it will not make sense for tapes.
*/
/*
* reset state to avoid recursion
*/
if (st_tape_init(un)) {
"stioctl : OFFLINE init failure ");
goto b_done_err;
}
/* un_restore_pos make invalid */
un->un_restore_pos = 0;
}
/*
* Check for legal operations
*/
"strategy with un->un_pos.pmode invalid\n");
goto b_done_err;
}
"st_queued_strategy(): regular io\n");
/*
* Process this first. If we were reading, and we're pending
* logical eot, that means we've bumped one file mark too far.
*/
/*
* Recursion warning: st_cmd will route back through here.
* Not anymore st_cmd will go through st_strategy()!
*/
un->un_density_known = 0;
goto b_done_err;
}
}
/*
* If we are in the process of opening, we may have to
* to do a test_append (if QIC) to see whether we are
* in a position to append to the end of the tape.
*
* If we're already at logical eot, we transition
* to ST_NO_EOF. If we're at physical eot, we punt
* to the switch statement below to handle.
*/
goto b_done_err;
}
}
"pending_io@fileno %d rw %d qic %d eof %d\n",
/*
* st_test_append() will do it all
*/
return (0);
}
}
wasopening = 1;
}
}
/*
* Process rest of END OF FILE and END OF TAPE conditions
*/
"eof=%x, wasopening=%x\n",
case ST_EOM:
/*
* This allows writes to proceed past physical
* eot. We'll *really* be in trouble if the
* user continues blindly writing data too
* much past this point (unwind the tape).
* Physical eot really means 'early warning
* eot' in this context.
*
* Every other write from now on will succeed
* (if sufficient tape left).
* This write will return with resid == count
* but the next one should be successful
*
* Note that we only transition to logical EOT
* if the last state wasn't the OPENING state.
* We explicitly prohibit running up to physical
* eot, closing the device, and then re-opening
* to proceed. Trailer records may only be gotten
* at by keeping the tape open after hitting eot.
*
* Also note that ST_EOM cannot be set by reading-
* this can only be set during writing. Reading
* up to the end of the tape gets a blank check
* or a double-filemark indication (ST_EOT_PENDING),
* and we prohibit reading after that point.
*
*/
if (wasopening == 0) {
/*
* this allows st_rw() to reset it back to
* will see a zero write
*/
}
goto b_done;
case ST_WRITE_AFTER_EOM:
case ST_EOT:
if (SVR4_BEHAVIOR && reading) {
goto b_done_err;
}
if (reading) {
goto b_done;
}
break;
case ST_EOF_PENDING:
"EOF PENDING\n");
if (SVR4_BEHAVIOR) {
goto b_done;
}
/* FALLTHROUGH */
case ST_EOF:
if (SVR4_BEHAVIOR) {
goto b_done_err;
}
if (BSD_BEHAVIOR) {
}
if (reading) {
"now file %d (read)\n",
goto b_done;
}
break;
default:
break;
}
st_bioerror(bp, 0);
SET_BP_PKT(bp, 0);
"st_queued_strategy: cmd=0x%p count=%ld resid=%ld flags=0x%x"
" pkt=0x%p\n",
#ifdef __x86
/*
* We will replace bp with a new bp that can do big blk xfer
* if the requested xfer size is bigger than un->un_maxdma_arch
*
* Also, we need to make sure that we're handling real I/O
* by checking group 0/1 SCSI I/O commands, if needed
*/
}
#endif
/* put on wait queue */
"st_queued_strategy: un->un_quef = 0x%p, bp = 0x%p\n",
return (0);
"st_queued_strategy : EIO b_done_err\n");
"st_queued_strategy: b_done\n");
exit:
/*
* make sure no commands are outstanding or waiting before closing,
* so we can guarantee order
*/
/* override errno here, if persistent errors were flagged */
return (0);
}
static int
{
/*
* validate arguments
*/
"st_strategy: ENXIO error exit\n");
return (0);
}
}
"st_strategy(): bcount=0x%lx, fileno=%d, blkno=%x, eof=%d\n",
st_bioerror(bp, 0);
SET_BP_PKT(bp, 0);
"st_strategy: cmd=0x%x count=%ld resid=%ld flags=0x%x"
" pkt=0x%p\n",
return (0);
}
/*
* this routine spaces forward over filemarks
*/
static int
{
int rval = 0;
/*
* the risk with doing only one space operation is that we
* may accidentily jump in old data
* the exabyte 8500 reading 8200 tapes cannot use KNOWS_EOD
* because the 8200 does not append a marker; in order not to
* sacrifice the fast file skip, we do a slow skip if the low
* density device has been opened
*/
"space_fmks : EIO can't do space cmd #1\n");
}
} else {
while (count > 0) {
"space_fmks : EIO can't do space cmd #2\n");
break;
}
count -= 1;
/*
* read a block to see if we have reached
* end of medium (double filemark for reel or
* medium error for others)
*/
if (count > 0) {
"space_fmks : EIO can't do "
"space cmd #3\n");
break;
}
"space_fmks : EIO ST_REEL\n");
break;
count--;
"space_fmks, EIO > ST_EOF\n");
break;
}
}
}
}
return (rval);
}
/*
* this routine spaces to EOD
*
* it keeps track of the current filenumber and returns the filenumber after
* the last successful space operation, we keep the number high because as
* tapes are getting larger, the possibility of more and more files exist,
* 0x100000 (1 Meg of files) probably will never have to be changed any time
* soon
*/
static int
{
int result;
return (-1);
}
/*
* see if the drive is smart enough to do the skips in
* one operation; 1/2" use two filemarks
* the exabyte 8500 reading 8200 tapes cannot use KNOWS_EOD
* because the 8200 does not append a marker; in order not to
* sacrifice the fast file skip, we do a slow skip if the low
* density device has been opened
*/
} else {
}
} else {
}
for (;;) {
if (result == 0) {
}
if (result != 0) {
continue;
}
/*
* What we return will become the current file position.
* After completing the space command with the position
* mode that is not invalid a read position command will
* be automaticly issued. If the drive support the long
* read position format a valid file position can be
* returned.
*/
}
if (result != 0) {
break;
}
/*
* If we're not EOM smart, space a record
* to see whether we're now in the slot between
* the two sequential filemarks that logical
* EOM consists of (REEL) or hit nowhere land
* (8mm).
*/
/*
* no fast skipping, check a record
*/
break;
}
break;
}
}
break;
}
} else {
break;
}
}
}
}
}
/*
* this routine is frequently used in ioctls below;
* it determines whether we know the density and if not will
* determine it
* if we have written the tape before, one or more filemarks are written
*
* depending on the stepflag, the head is repositioned to where it was before
* the filemarks were written in order not to confuse step counts
*/
#define STEPBACK 0
#define NO_STEPBACK 1
static int
{
"st_check_density_or_wfm(dev= 0x%lx, wfm= %d, mode= %d, stpflg= %d)"
/*
* If we don't yet know the density of the tape we have inserted,
* we have to either unconditionally set it (if we're 'writing'),
* or we have to determine it. As side effects, check for any
* write-protect errors, and for the need to put out any file-marks
* before positioning a tape.
*
* If we are going to be spacing forward, and we haven't determined
* the tape density yet, we have to do so now...
*/
"check_density_or_wfm : EIO can't determine "
"density\n");
return (EIO);
}
/*
* Presumably we are at BOT. If we attempt to write, it will
* either work okay, or bomb. We don't do a st_test_append
* unless we're past BOT.
*/
/*
* We need to write one or two filemarks.
* In the case of the HP, we need to
* position the head between the two
* marks.
*/
un->un_fmneeded = 0;
}
un->un_density_known = 0;
"check_density_or_wfm : EIO can't write fm\n");
return (EIO);
}
"check_density_or_wfm : EIO can't space "
"(-wfm)\n");
return (EIO);
}
}
}
/*
* Whatever we do at this point clears the state of the eof flag.
*/
/*
* If writing, let's check that we're positioned correctly
* at the end of tape before issuing the next write.
*/
}
return (0);
}
/*
* Wait for all outstaning I/O's to complete
*
* we wait on both ncmds and the wait queue for times when we are flushing
* after persistent errors are flagged, which is when ncmds can be 0, and the
* queue can still have I/O's. This way we preserve order of biodone's.
*/
static void
{
}
}
/*
* This routine implements the ioctl calls. It is called
* from the device switch at normal priority.
*/
/*ARGSUSED*/
static int
int *rval_p)
{
"st_ioctl(): fileno=%x, blkno=%x, eof=%x, state = %d, "
"pe_flag = %d\n",
/*
* We don't want to block on these, so let them through
* and we don't care about setting driver states here.
*/
if ((cmd == MTIOCGETDRIVETYPE) ||
(cmd == MTIOCGUARANTEEDORDER) ||
(cmd == MTIOCPERSISTENTSTATUS)) {
goto check_commands;
}
/*
* We clear error entry stack except command
* MTIOCGETERROR and MTIOCGET
*/
if ((cmd != MTIOCGETERROR) &&
}
/*
* wait for all outstanding commands to complete, or be dequeued.
* And because ioctl's are synchronous commands, any return value
* after this, will be in order
*/
/*
* allow only a through clear errors and persistent status, and
* status
*/
if ((cmd == MTIOCLRERR) ||
(cmd == MTIOCPERSISTENT) ||
goto check_commands;
} else {
goto exit;
}
}
/*
* first and foremost, handle any ST_EOT_PENDING cases.
* That is, if a logical eot is pending notice, notice it.
*/
"stioctl : EIO can't space fmk(-1)\n");
goto exit;
}
if (status == SUN_KEY_EOF) {
} else {
}
/* fix up block number */
/* now we're at logical eot */
}
/*
* now, handle the rest of the situations
*/
switch (cmd) {
case MTIOCGET:
{
#ifdef _MULTI_DATAMODEL
/*
* For use when a 32 bit app makes a call into a
* 64 bit ioctl
*/
struct mtget32 mtg_local32;
#endif /* _MULTI_DATAMODEL */
/* Get tape status */
"st_ioctl: MTIOCGET\n");
} else {
}
/*
* If the value is positive fine.
* If its negative we need to return a value based on the
* old way if counting backwards from INF (1,000,000,000).
*/
} else {
}
}
} else { /* 1/4" cartridges */
/* Emulex cartridge tape */
case MT_ISMT02:
break;
default:
break;
}
}
/*
* If large transfers are allowed and drive options
* has no record size limit set. Calculate blocking
* factor from the lesser of maxbsize and maxdma.
*/
if ((un->un_allow_large_xfer) &&
}
}
/*
* In persistent error mode sending a non-queued can hang
* because this ioctl gets to be run without turning off
* persistense. Fake the answer based on previous info.
*/
if (un->un_persistence) {
rval = 0;
} else {
}
if (rval == 0) {
/*
* If zero is returned or in persistent mode,
* use the old data.
*/
!= TAPE_ALERT_NOT_SUPPORTED) {
}
}
} else {
rval = 0;
}
un->un_err_resid = 0;
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32:
/*
* Convert 64 bit back to 32 bit before doing
* copyout. This is what the ILP32 app expects.
*/
}
break;
case DDI_MODEL_NONE:
}
break;
}
#else /* ! _MULTI_DATAMODE */
}
#endif /* _MULTI_DATAMODE */
break;
}
case MTIOCGETERROR:
/*
* get error entry from error stack
*/
"st_ioctl: MTIOCGETERROR\n");
break;
case MTIOCSTATE:
{
/*
* return when media presence matches state
*/
enum mtio_state state;
"st_ioctl: MTIOCSTATE\n");
if (rval != 0) {
break;
}
sizeof (int), flag))
break;
}
case MTIOCGETDRIVETYPE:
{
#ifdef _MULTI_DATAMODEL
/*
* For use when a 32 bit app makes a call into a
* 64 bit ioctl
*/
struct mtdrivetype_request32 mtdtrq32;
#endif /* _MULTI_DATAMODEL */
/*
* return mtdrivetype
*/
struct mtdrivetype_request mtdtrq;
struct mtdrivetype mtdrtyp;
"st_ioctl: MTIOCGETDRIVETYPE\n");
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32:
{
sizeof (struct mtdrivetype_request32), flag)) {
break;
}
break;
}
case DDI_MODEL_NONE:
sizeof (struct mtdrivetype_request), flag)) {
break;
}
break;
}
#else /* ! _MULTI_DATAMODEL */
sizeof (struct mtdrivetype_request), flag)) {
break;
}
#endif /* _MULTI_DATAMODEL */
/*
* if requested size is < 0 then return
* error.
*/
break;
}
}
/*
* Speed hasn't been used since the hayday of reel tape.
* For all drives not setting the option ST_KNOWS_MEDIA
* the speed member renamed to mediatype are zeros.
* Those drives that have ST_KNOWS_MEDIA set use the
* new mediatype member which is used to figure the
* type of media loaded.
*
* So as to not break applications speed in the
* mtdrivetype structure is not renamed.
*/
}
/*
* Limit the maximum length of the result to
* sizeof (struct mtdrivetype).
*/
tmp = sizeof (struct mtdrivetype);
}
break;
}
case MTIOCPERSISTENT:
break;
}
if (tmp) {
} else {
}
"st_ioctl: MTIOCPERSISTENT : persistence = %d\n",
un->un_persistence);
break;
case MTIOCPERSISTENTSTATUS:
}
"st_ioctl: MTIOCPERSISTENTSTATUS:persistence = %d\n",
un->un_persistence);
break;
case MTIOCLRERR:
{
/* clear persistent errors */
"st_ioctl: MTIOCLRERR\n");
break;
}
case MTIOCGUARANTEEDORDER:
{
/*
* this is just a holder to make a valid ioctl and
* it won't be in any earlier release
*/
"st_ioctl: MTIOCGUARANTEEDORDER\n");
break;
}
case MTIOCRESERVE:
{
"st_ioctl: MTIOCRESERVE\n");
/*
*/
break;
}
if (rval == 0) {
}
break;
}
case MTIOCRELEASE:
{
"st_ioctl: MTIOCRELEASE\n");
/*
*/
break;
}
/*
* Used to just clear ST_PRESERVE_RESERVE which
* made the reservation release at next close.
* As the user may have opened and then done a
* persistant reservation we now need to drop
* the reservation without closing if the user
* attempts to do this.
*/
break;
}
case MTIOCFORCERESERVE:
{
"st_ioctl: MTIOCFORCERESERVE\n");
/*
*/
break;
}
/*
* allow only super user to run this.
*/
break;
}
/*
* Throw away reserve,
* not using test-unit-ready
* since reserve can succeed without tape being
* present in the drive.
*/
break;
}
case USCSICMD:
{
"st_ioctl: USCSICMD\n");
cr = ddi_get_cred();
} else {
}
break;
}
case MTIOCTOP:
"st_ioctl: MTIOCTOP\n");
break;
case MTIOCLTOP:
"st_ioctl: MTIOLCTOP\n");
break;
case MTIOCREADIGNOREILI:
{
int set_ili;
break;
}
break;
}
switch (set_ili) {
case 0:
break;
case 1:
break;
default:
break;
}
break;
}
case MTIOCREADIGNOREEOFS:
{
int ignore_eof;
sizeof (ignore_eof), flag)) {
break;
}
break;
}
switch (ignore_eof) {
case 0:
break;
case 1:
break;
default:
break;
}
break;
}
case MTIOCSHORTFMK:
{
int short_fmk;
break;
}
case ST_TYPE_EXB8500:
case ST_TYPE_EXABYTE:
if (!short_fmk) {
} else if (short_fmk == 1) {
} else {
}
break;
default:
break;
}
break;
}
case MTIOCGETPOS:
if (rval == 0) {
"MTIOCGETPOS copy out failed\n");
}
}
break;
case MTIOCRESTPOS:
{
flag) != 0) {
"MTIOCRESTPOS copy in failed\n");
break;
}
if (rval != 0) {
}
break;
}
default:
"st_ioctl: unknown ioctl\n");
}
exit:
}
return (rval);
}
/*
* do some MTIOCTOP tape operations
*/
static int
{
#ifdef _MULTI_DATAMODEL
/*
* For use when a 32 bit app makes a call into a
* 64 bit ioctl
*/
struct mtop32 mtop_32_for_64;
#endif /* _MULTI_DATAMODEL */
int rval = 0;
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32:
return (EFAULT);
}
break;
case DDI_MODEL_NONE:
return (EFAULT);
}
/* prevent sign extention */
break;
}
#else /* ! _MULTI_DATAMODEL */
return (EFAULT);
}
/* prevent sign extention */
#endif /* _MULTI_DATAMODEL */
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32:
break;
}
/*
* Convert 64 bit back to 32 bit before doing
* copyout. This is what the ILP32 app expects.
*/
}
break;
case DDI_MODEL_NONE:
}
break;
}
#else /* ! _MULTI_DATAMODE */
} else {
}
}
#endif /* _MULTI_DATAMODE */
un->un_density_known = 0;
}
return (rval);
}
static int
{
int rval;
return (EFAULT);
}
}
return (rval);
}
static int
{
int savefile;
int rval = 0;
"fileno=%x, blkno=%x, eof=%x\n",
/*
* if we are going to mess with a tape, we have to make sure we have
* one and are not offline (i.e. no tape is initialized). We let
* commands pass here that don't actually touch the tape, except for
* loading and initialization (rewinding).
*/
case MTLOAD:
case MTNOP:
/*
* We don't want strategy calling st_tape_init here,
* so, change state
*/
"st_do_mtioctop : OFFLINE state = %d\n",
break;
default:
/*
* reinitialize by normal means
*/
if (rval) {
"st_do_mtioctop : OFFLINE init failure ");
}
return (rval);
}
break;
}
}
/*
* If the file position is invalid, allow only those
* commands that properly position the tape and fail
* the rest with EIO
*/
case MTWEOF:
case MTRETEN:
case MTERASE:
case MTEOM:
case MTFSF:
case MTFSR:
case MTBSF:
case MTNBSF:
case MTBSR:
case MTSRSZ:
case MTGRSZ:
case MTSEEK:
case MTBSSF:
case MTFSSF:
return (EIO);
/* NOTREACHED */
case MTREW:
case MTLOAD:
case MTOFFL:
case MTNOP:
case MTTELL:
case MTLOCK:
case MTUNLOCK:
break;
default:
return (ENOTTY);
/* NOTREACHED */
}
}
case MTERASE:
/*
* MTERASE rewinds the tape, erase it completely, and returns
* to the beginning of the tape
*/
return (EACCES);
}
} else {
}
"st_do_mtioctop : EIO space or erase or "
"check den)\n");
} else {
/* QIC and helical scan rewind after erase */
}
}
break;
case MTWEOF:
/*
* write an end-of-file record
*/
return (EACCES);
}
/*
* zero count means just flush buffers
* negative count is not permitted
*/
return (EINVAL);
}
/* Not on worm */
}
"st_do_mtioctop : EIO : MTWEOF can't "
"determine density");
return (EIO);
}
}
/*
* Failure due to something other than illegal
* request results in loss of state (st_intr).
*/
"st_do_mtioctop : EIO : MTWEOF can't write "
"file mark");
}
break;
case MTRETEN:
/*
* retension the tape
*/
"st_do_mtioctop : EIO : MTRETEN ");
}
break;
case MTREW:
/*
* rewind the tape
*/
"st_do_mtioctop : EIO:MTREW check "
return (EIO);
}
"st_do_mtioctop : EIO : MTREW ");
}
break;
case MTOFFL:
/*
* rewinds, and, if appropriate, takes the device offline by
* unloading the tape
*/
"st_do_mtioctop :EIO:MTOFFL check "
return (EIO);
}
"st_do_mtioctop : EIO : MTOFFL");
return (EIO);
}
break;
case MTLOAD:
/*
* This is to load a tape into the drive
* Note that if the tape is not loaded, the device will have
* to be opened via O_NDELAY or O_NONBLOCK.
*/
/*
* Let's try and clean things up, if we are not
* initializing, and then send in the load command, no
* matter what.
*
* load after a media change by the user.
*/
}
/* Load command to a drive that doesn't support load */
/* Medium not present */
/* CSL not present */
break;
}
if (rval) {
"st_do_mtioctop : %s : MTLOAD\n",
/*
* If load tape fails, who knows what happened...
*/
break;
}
/*
* reset all counters appropriately using rewind, as if LOAD
* succeeds, we are at BOT
*/
rval = 0;
break;
}
if (rval != 0) {
"st_do_mtioctop : EIO : MTLOAD calls "
"st_tape_init\n");
}
break;
case MTNOP:
un->un_err_resid = 0;
break;
case MTEOM:
/*
* positions the tape at a location just after the last file
* written on the tape. For cartridge and 8 mm, this after
* the last file mark; for reel, this is inbetween the two
* last 2 file marks
*/
/*
* If the command wants to move to logical end
* of media, and we're already there, we're done.
* If we were at logical eot, we reset the state
* to be *not* at logical eot.
*
* If we're at physical or logical eot, we prohibit
* forward space operations (unconditionally).
*
* Also if the last operation was a write of any
* kind the tape is at EOD.
*/
return (0);
}
/*
* physical tape position may not be what we've been
* telling the user; adjust the request accordingly
*/
}
" failed");
return (EIO);
}
/*
* st_find_eod() returns the last fileno we knew about;
*/
"st_do_mtioctop : EIO : MTEOM status check failed");
} else {
/*
* For 1/2" reel tapes assume logical EOT marked
* by two file marks or we don't care that we may
* be extending the last file on the tape.
*/
"st_do_mtioctop : EIO : MTEOM space"
" cmd failed");
break;
}
/*
* Fix up the block number.
*/
}
un->un_err_resid = 0;
}
break;
case MTFSF:
break;
case MTFSR:
break;
case MTBSF:
break;
case MTNBSF:
break;
case MTBSR:
break;
case MTBSSF:
break;
case MTFSSF:
break;
case MTSRSZ:
/*
* Set record-size to that sent by user
* Check to see if there is reason that the requested
* block size should not be set.
*/
/* If requesting variable block size is it ok? */
return (ENOTTY);
}
/*
* If requested block size is not variable "0",
* is it less then minimum.
*/
return (EINVAL);
}
/* Is the requested block size more then maximum */
(un->un_maxbsize != 0)) {
return (EINVAL);
}
/* Is requested block size a modulus the device likes */
return (EINVAL);
}
"st_ioctl : MTSRSZ : EIO : cant set block size");
return (EIO);
}
return (0);
case MTGRSZ:
/*
* Get record-size to the user
*/
rval = 0;
break;
case MTTELL:
break;
case MTSEEK:
/*
* This bit of magic make mt print the actual position if
* the resulting position was not what was asked for.
*/
}
}
break;
case MTLOCK:
"st_do_mtioctop : EIO : MTLOCK");
}
break;
case MTUNLOCK:
"st_do_mtioctop : EIO : MTUNLOCK");
}
break;
default:
}
return (rval);
}
/*
* Run a command for uscsi ioctl.
*/
static int
{
int offline_state = 0;
int err = 0;
/*
* We really don't know what commands are coming in here and
* we don't want to limit the commands coming in.
*
* If st_tape_init() gets called from st_strategy(), then we
* will hang the process waiting for un->un_sbuf_busy to be cleared,
* which it never will, as we set it below. To prevent
* st_tape_init() from getting called, we have to set state to other
* than ST_STATE_OFFLINE, so we choose ST_STATE_INITIALIZING, which
* achieves this purpose already.
*
* We use offline_state to preserve the OFFLINE state, if it exists,
* so other entry points to the driver might have the chance to call
* st_tape_init().
*/
offline_state = 1;
}
if (err != 0) {
"st_uscsi_cmd: scsi_uscsi_alloc_and_copyin failed\n");
goto exit;
}
/* check to see if this command requires the drive to be reserved */
if (err) {
goto exit_free;
}
/*
* If this is a space command we need to save the starting
* point so we can retry from there if the command fails.
*/
}
}
/*
* Forground should not be doing anything while recovery is active.
*/
/*
* Get buffer resources...
*/
while (un->un_sbuf_busy)
#ifdef STDEBUG
if (uscmd->uscsi_buflen) {
"uscsi %s of %ld bytes %s %s space\n",
}
}
#endif /* STDEBUG */
/*
* Although st_uscsi_cmd() never makes use of these
* now, we are just being safe and consistent.
*/
}
/*
* If scsi reset successful, don't write any filemarks.
*/
un->un_fmneeded = 0;
}
/*
* Free resources
*/
un->un_sbuf_busy = 0;
/*
* If was a space command need to update logical block position.
* If the command failed such that positioning is invalid, Don't
* update the position as the user must do this to validate the
* position for data protection.
*/
/*
* Set running position to invalid so it updates on the
* next command.
*/
}
"st_uscsi_cmd returns 0x%x\n", err);
exit:
/* don't lose offline state */
if (offline_state) {
}
return (err);
}
static int
{
int i;
int rval;
/*
* write one filemark at the time after EOT
*/
for (i = 0; i < wfm; i++) {
return (rval);
}
if (rval != 0) {
"st_write_fm : EIO : write EOT file mark");
return (EIO);
}
}
} else {
return (rval);
}
if (rval) {
"st_write_fm : EIO : write file mark");
return (EIO);
}
}
return (0);
}
#ifdef STDEBUG
static void
{
return;
"st_start: cmd=0x%p count=%ld resid=%ld flags=0x%x pkt=0x%p\n",
"st_start: fileno=%d, blk=%d\n",
}
#endif
/*
* Command start && done functions
*/
/*
* st_start()
*
* Called from:
* st_strategy() to start a command.
* st_runout() to retry when scsi_pkt allocation fails on previous attempt(s).
* st_attach() when resuming from power down state.
* st_start_restart() to retry transport when device was previously busy.
* st_done_and_mutex_exit() to start the next command when previous is done.
*
* On entry:
* scsi_pkt may or may not be allocated.
*
*/
static void
{
int status;
int queued;
if (un->un_recov_buf_busy) {
/* recovery commands can happen anytime */
queued = 0;
} else if (un->un_sbuf_busy) {
/* sbuf commands should only happen with an empty queue. */
queued = 0;
return;
}
queued = 1;
} else {
"st_start() returning no buf found\n");
return;
}
/*
* Don't send more than un_throttle commands to the HBA
*/
/*
* if doing recovery we know there is outstanding commands.
*/
"st_start returning throttle = %d or ncmds = %d\n",
typedef void (*func)();
"Sending delayed start to st_runout()\n");
}
return;
}
}
/*
* If the buf has no scsi_pkt call st_make_cmd() to get one and
* build the command.
*/
/*
* Some HBA's don't call bioerror() to set an error.
* And geterror() returns zero if B_ERROR is not set.
* So if we get zero we must check b_error.
*/
}
/*
* Some HBA's convert DDI_DMA_NORESOURCES into ENOMEM.
* In tape ENOMEM has special meaning so we'll change it.
*/
status = 0;
}
/*
* Did it fail and is it retryable?
* If so return and wait for the callback through st_runout.
* Also looks like scsi_init_pkt() will setup a callback even
* if it isn't retryable.
*/
if (status == 0) {
/*
* If first attempt save state.
*/
}
"temp no resources for pkt\n");
"scsi_init_pkt rejected pkt as too big\n");
if (un->un_persistence) {
}
} else {
/*
* Unlikely that it would be retryable then not.
*/
}
"perm no resources for pkt errno = 0x%x\n",
status);
}
return;
}
/*
* Worked this time set the state back.
*/
}
}
if (queued) {
/*
* move from waitq to runq
*/
}
#ifdef STDEBUG
#endif
/* could not get here if throttle was zero */
if (un->un_last_throttle) {
}
if (status != TRAN_ACCEPT) {
"Unhappy transport packet status 0x%x\n", status);
/*
* If command recovery is enabled and this isn't
* a recovery command try command recovery.
*/
"Command Recovery called on busy send\n");
ATTEMPT_RETRY) == JUST_RETURN) {
return;
}
} else {
ST_TRAN_BUSY_TIMEOUT, queued) == 0) {
return;
}
/*
* if too many retries, fail the transport
*/
}
}
"transport rejected %d\n", status);
} else {
}
}
}
/*
* if the transport is busy, then put this bp back on the waitq
*/
static int
{
"st_handle_start_busy()\n");
/*
* Check to see if we hit the retry timeout and one last check for
* making sure this is the last on the runq, if it is not, we have
* to fail
*/
return (-1);
}
if (queued) {
/* put the bp back on the waitq */
}
/*
* Decrement un_ncmds so that this
* gets thru' st_start() again.
*/
if (queued) {
/*
* since this is an error case, we won't have to do this list
* walking much. We've already made sure this bp was the
* last on the runq
*/
/*
* send a marker pkt, if appropriate
*/
}
/*
* all queues are aligned, we are just waiting to
* transport, don't alloc any more buf p's, when
* st_start is reentered.
*/
return (0);
}
/*
* st_runout a callback that is called what a resource allocatation failed
*/
static int
{
int queued;
if (un->un_recov_buf_busy != 0) {
queued = 0;
} else if (un->un_sbuf_busy != 0) {
/* sbuf commands should only happen with an empty queue. */
queued = 0;
return (1);
}
queued = 1;
} else {
ASSERT(1 == 0);
return (1);
}
/*
* failed scsi_init_pkt(). If errno is zero its retryable.
*/
"errors after pkt alloc (b_flags=0x%x, b_error=0x%x)\n",
if (queued) {
bp);
}
/*
* Set resid, Error already set, then unblock calling thread.
*/
} else {
/*
* Try Again
*/
}
/*
* Comments courtesy of sd.c
* The scsi_init_pkt routine allows for the callback function to
* return a 0 indicating the callback should be rescheduled or a 1
* indicating not to reschedule. This routine always returns 1
* because the driver always provides a callback function to
* scsi_init_pkt. This results in a callback always being scheduled
* (via the scsi_init_pkt callback implementation) if a resource
* failure occurs.
*/
return (1);
}
/*
* st_done_and_mutex_exit()
* - remove bp from runq
* - start up the next request
* - if this was an asynch bp, clean up
* - exit with released mutex
*/
static void
{
int pe_flagged = 0;
#if !defined(lint)
#endif
"st_done_and_mutex_exit(): cmd=0x%x count=%ld resid=%ld flags="
/*
* update kstats with transfer count info
*/
} else {
}
}
/*
* Start the next one before releasing resources on this one, if
* there is something on the queue and persistent errors has not been
* flagged
*/
}
}
/*
* Since we marked this ourselves as ASYNC,
* there isn't anybody around waiting for
* completion any more.
*/
}
"st_done_and_mutex_exit(async): freeing pkt\n");
if (pkt) {
}
un->un_sbuf_busy = 0;
return;
}
/*
* Copy status from scsi_pkt to uscsi_cmd
* since st_uscsi_cmd needs it
*/
}
#ifdef STDEBUG
"st_d_a_m_exit(): ncmds = %d, thr = %d, "
"un_errno = %d, un_pe = %d\n",
}
#endif
"st_done_and_mutex_exit: freeing pkt\n");
if (pkt) {
}
/*
* now that we biodoned that command, if persistent errors have been
* flagged, flush the waitq
*/
if (pe_flagged)
}
/*
* Tape error, flush tape driver queue.
*/
static void
{
"st_flush(), ncmds = %d, quef = 0x%p\n",
/*
* if we still have commands outstanding, wait for them to come in
* before flushing the queue, and make sure there is a queue
*/
goto exit;
/*
* we have no more commands outstanding, so let's deal with special
* cases in the queue for EOM and FM. If we are here, and un_errno
* is 0, then we know there was no error and we return a 0 read or
* write before showing errors
*/
/* Flush the wait queue. */
"st_flush() : blkno=%d, err=%d, b_bcount=%ld\n",
/* it should have one, but check anyway */
}
}
/*
* It's not a bad practice to reset the
* waitq tail pointer to NULL.
*/
exit:
/* we mucked with the queue, so let others know about it */
}
/*
* Utility functions
*/
static int
{
int bsize;
static char *cart = "0.25 inch cartridge";
char *sizestr;
"st_determine_generic(un = 0x%p)\n", (void*)un);
if (st_modesense(un)) {
return (-1);
}
if (bsize == 0) {
} else if (bsize > ST_MAXRECSIZE_FIXED) {
/*
* record size of this device too big.
* try and convert it to variable record length.
*
*/
if (st_change_block_size(un, 0) != 0) {
"Fixed Record Size %d is too large\n", bsize);
"Cannot switch to variable record size\n");
return (-1);
}
} else if (st_change_block_size(un, 0) == 0) {
/*
* If the drive was set to a non zero block size,
* See if it can be set to a zero block size.
* If it works, ST_VARIABLE so user can set it as they want.
*/
} else {
}
default:
case 0x0:
/*
* default density, cannot determine any other
* information.
*/
sizestr = "Unknown type- assuming 0.25 inch cartridge";
break;
case 0x1:
case 0x2:
case 0x3:
case 0x6:
/*
* 1/2" reel
*/
sizestr = "0.50 inch reel";
break;
case 0x4:
case 0x5:
case 0x7:
case 0x0b:
/*
* Quarter inch.
*/
break;
case 0x0f:
case 0x10:
case 0x11:
case 0x12:
/*
* QIC-120, QIC-150, QIC-320, QIC-600
*/
break;
case 0x09:
case 0x0a:
case 0x0c:
case 0x0d:
/*
* 1/2" cartridge tapes. Include HI-TC.
*/
break;
case 0x13:
case 0x24:
case 0x25:
case 0x26:
sizestr = "DAT Data Storage (DDS)";
break;
case 0x14:
/*
* Helical Scan (Exabyte) devices
*/
sizestr = "8mm helical scan cartridge";
break;
}
/*
* Assume LONG ERASE, BSF and BSR
*/
/*
* Only if mode sense data says no buffered write, set NOBUF
*/
/*
* set up large read and write retry counts
*/
/*
* If this is a 0.50 inch reel tape, and
* it is *not* variable mode, try and
* set it to variable record length
* mode.
*/
if (st_change_block_size(un, 0) == 0) {
}
}
/*
* Write to console about type of device found
*/
sizestr);
"!Variable record length I/O\n");
} else {
"!Fixed record length (%d byte blocks) I/O\n",
}
return (0);
}
static int
{
int rval = 0;
"st_determine_density(un = 0x%p, rw = %s)\n",
/*
* If we're past BOT, density is determined already.
*/
goto exit;
}
/*
* XXX: put in a bitch message about attempting to
* XXX: change density past BOT.
*/
goto exit;
}
} else {
goto exit;
}
goto exit;
}
/*
* If we're going to be writing, we set the density
*/
/* un_curdens is used as an index into densities table */
if (st_set_density(un)) {
rval = -1;
}
goto exit;
}
/*
* If density is known already,
* we don't have to get it again.(?)
*/
if (!un->un_density_known) {
if (st_get_density(un)) {
rval = -1;
}
}
exit:
return (rval);
}
/*
* Try to determine density. We do this by attempting to read the
* first record off the tape, cycling through the available density
* codes as we go.
*/
static int
{
"st_get_density(un = 0x%p)\n", (void*)un);
/*
* If Auto Density override is enabled The drive has
* only one density and there is no point in attempting
* find the correct one.
*
* Since most modern drives auto detect the density
* and format of the recorded media before they come
* ready. What this function does is a legacy behavior
* and modern drives not only don't need it, The backup
* utilities that do positioning via uscsi find the un-
* expected rewinds problematic.
*
* The drives that need this are old reel to reel devices.
* I took a swag and said they must be scsi-1 or older.
* I don't beleave there will any of the newer devices
* that need this. There will be some scsi-1 devices that
* don't need this but I don't think they will be using the
* BIG aftermarket backup and restore utilitys.
*/
rval = 0;
goto exit;
}
/*
* This will only work on variable record length tapes
* if and only if all variable record length tapes autodensity
* select.
*/
/*
* Start at the specified density
*/
/*
* If we've done this density before,
* don't bother to do it again.
*/
continue;
"trying density 0x%x\n", dens);
if (st_set_density(un)) {
continue;
}
/*
* XXX - the creates lots of headaches and slowdowns - must
* fix.
*/
break;
}
if (succes) {
rval = 0;
break;
}
}
exit:
return (rval);
}
static int
{
int rval = 0;
"st_set_density(un = 0x%p): density = 0x%x\n", (void*)un,
/*
* If auto density override is not set, Use mode select
* to set density and compression.
*/
if (st_modeselect(un)) {
rval = -1;
}
/*
* If auto density and mode select compression are set,
* This is a drive with one density code but compression
* can be enabled or disabled.
* Set compression but no need to set density.
*/
rval = -1;
} else {
rval = 0;
}
}
if (rval == 0) {
}
return (rval);
}
static int
{
int rval;
"st_loadtape(un = 0x%p)\n", (void*) un);
return (rval);
}
/*
* 'LOAD' the tape to BOT by rewinding
*/
if (rval == 0) {
un->un_density_known = 0;
}
return (rval);
}
/*
* Note: QIC devices aren't so smart. If you try to append
* after EOM, the write can fail because the device doesn't know
* it's at EOM. In that case, issue a read. The read should fail
* because there's no data, but the device knows it's at EOM,
* so a subsequent write should succeed. To further confuse matters,
* the target returns the same error if the tape is positioned
* such that a write would overwrite existing data. That's why
* we have to do the append test. A read in the middle of
* recorded data would succeed, thus indicating we're attempting
* something illegal.
*/
static void
{
unsigned bcount;
un->un_test_append = 0;
/*
* first, map in the buffer, because we're doing a double write --
* first into the kernel, then onto the tape.
*/
/*
* get a copy of the data....
*/
/*
* attempt the write..
*/
"append write succeeded\n");
return;
}
/*
* The append failed. Do a short read. If that fails, we are at EOM
* so we can retry the write command. If that succeeds, than we're
* all screwed up (the controller reported a real error).
*
* XXX: should the dummy read be > SECSIZE? should it be the device's
* XXX: block size?
*
*/
"append at EOM\n");
/*
* Okay- the read failed. We should actually have confused
* the controller enough to allow writing. In any case, the
* i/o is on its own from here on out.
*/
SYNC_CMD) == 0) {
goto success;
}
}
"append write failed- not at EOM\n");
"st_test_append : EIO : append write failed - not at EOM");
/*
* backspace one record to get back to where we were
*/
}
/*
* Note: biodone will do a bp_mapout()
*/
}
/*
* Special command handler
*/
/*
* common st_cmd code. The fourth parameter states
* whether the caller wishes to await the results
* Note the release of the mutex during most of the function
*/
static int
{
int err;
#ifdef STDEBUG
if ((st_debug & 0x7)) {
}
#endif
/* check to see if this command requires the drive to be reserved */
if (err) {
return (err);
}
/*
* A space command is not recoverable if we don't know were we
* were when it was issued.
*/
}
/*
* Forground should not be doing anything while recovery is active.
*/
while (un->un_sbuf_busy)
un->un_sbuf_busy = 0;
/*
* If was a space command need to update logical block position.
* Only do this if the command was sucessful or it will mask the fact
* that the space command failed by promoting the pmode to logical.
*/
/*
* Set running position to invalid so it updates on the
* next command.
*/
}
return (err);
}
static int
{
int err;
/*
* Set count to the actual size of the data tranfer.
* For commands with no data transfer, set bp->b_bcount
* to the value to be used when constructing the
* cdb in st_make_cmd().
*/
switch (com) {
case SCMD_READ:
break;
case SCMD_WRITE:
break;
case SCMD_WRITE_FILE_MARK:
count = 0;
break;
case SCMD_REWIND:
count = 0;
break;
case SCMD_SPACE:
/*
* If the user could have entered a number that will
* not fit in the 12 bit count field of space(8),
* use space(16).
*/
com = SCMD_SPACE_G4;
}
count = 0;
break;
case SCMD_RESERVE:
count = 0;
break;
case SCMD_RELEASE:
count = 0;
break;
case SCMD_LOAD:
count = 0;
break;
case SCMD_ERASE:
"erase tape\n");
count = 0;
break;
case SCMD_MODE_SENSE:
"mode sense\n");
break;
case SCMD_MODE_SELECT:
"mode select\n");
break;
case SCMD_READ_BLKLIM:
"read block limits\n");
break;
case SCMD_TEST_UNIT_READY:
"test unit ready\n");
count = 0;
break;
case SCMD_DOORLOCK:
break;
case SCMD_READ_POSITION:
"read position\n");
switch (un->un_read_pos_type) {
case LONG_POS:
count = sizeof (tape_position_long_t);
break;
case EXT_POS:
break;
case SHORT_POS:
count = sizeof (tape_position_t);
break;
default:
"Unknown read position type 0x%x in "
}
break;
default:
"Unhandled scsi command 0x%x in st_setup_cmd()\n", com);
}
if (count > 0) {
/*
* We're going to do actual I/O.
* Set things up for physio.
*/
/*
* Let physio do the rest...
*/
"st_setup_cmd: physio returns %d\n", err);
} else {
/*
* Mimic physio
*/
(void) st_strategy(bp);
/*
* This is an async command- the caller won't wait
* and doesn't care about errors.
*/
return (0);
}
/*
* BugTraq #4260046
* ----------------
* Restore Solaris 2.5.1 behavior, namely call biowait
* unconditionally. The old comment said...
*
* "if strategy was flagged with persistent errors, we would
* have an error here, and the bp would never be sent, so we
* don't want to wait on a bp that was never sent...or hang"
*
* The new rationale, courtesy of Chitrank...
*
* "we should unconditionally biowait() here because
* st_strategy() will do a biodone() in the persistent error
* case and the following biowait() will return immediately.
* If not, in the case of "errors after pkt alloc" in
* st_start(), we will not biowait here which will cause the
* next biowait() to return immediately which will cause
* us to send out the next command. In the case where both of
* these use the sbuf, when the first command completes we'll
* free the packet attached to sbuf and the same pkt will
* get freed again when we complete the second command.
* see esc 518987. BTW, it is necessary to do biodone() in
* st_start() for the pkt alloc failure case because physio()
* does biowait() and will hang if we don't do biodone()"
*/
"st_setup_cmd: biowait returns %d\n", err);
}
return (err);
}
static int
{
int rval;
int turn_compression_on;
/*
* Drive either dosn't have compression or it is controlled with
* special density codes. Return ENOTTY so caller
* knows nothing was done.
*/
un->un_comp_page = 0;
return (ENOTTY);
}
/* set compression based on minor node opened */
/*
* If this the compression density or
* the drive has two densities and uses mode select for
* control of compression turn on compression for MT_DENSITY2
* as well.
*/
if ((minor == ST_COMPRESSION_DENSITY) ||
turn_compression_on = 1;
} else {
turn_compression_on = 0;
}
/*
* Need to determine which page does the device use for compression.
* First try the data compression page. If this fails try the device
* configuration page
*/
return (rval);
}
if (rval != 0) {
/*
* This device does not support data
* compression page
*/
} else {
rval = -1;
}
} else {
}
}
return (rval);
}
if (rval != 0) {
/*
* This device does not support
* compression at all advice the
* user and unset ST_MODE_SEL_COMP
*/
un->un_comp_page = 0;
"Device Does Not Support Compression\n");
} else {
rval = -1;
}
}
}
return (rval);
}
/*
* set or unset compression thru device configuration page.
*/
static int
{
unsigned char cflag;
int rval = 0;
/*
* if the mode sense page is not the correct one, load the correct one.
*/
if (rval)
return (rval);
}
/*
* Figure what to set compression flag to.
*/
if (compression_on) {
/* They have selected a compression node */
} else {
}
} else {
}
/*
* If compression is already set the way it was requested.
* And if this not the first time we has tried.
*/
return (EALREADY);
}
/*
* need to send mode select even if correct compression is
* already set since need to set density code
*/
#ifdef STDEBUG
"st_set_devconfig_page: sense data for mode select",
}
#endif
sizeof (struct seq_mode));
return (rval);
}
/*
*/
static int
{
int rval = 0;
/*
* if the mode sense page is not the correct one, load the correct one.
*/
if (rval)
return (rval);
}
/*
* If drive is not capable of compression (at this time)
* return EALREADY so caller doesn't think that this page
* is not supported. This check is for drives that can
* disable compression from the front panel or configuration.
* I doubt that a drive that supports this page is not really
* capable of compression.
*/
return (EALREADY);
}
/* See if compression currently turned on */
} else {
}
/*
* If compression is already set the way it was requested.
* And if this not the first time we has tried.
*/
if ((compression_on == compression_on_already) &&
return (EALREADY);
}
/*
* if we are already set to the appropriate compression
* mode, don't set it again
*/
if (compression_on) {
/* compression selected */
} else {
}
#ifdef STDEBUG
"st_set_datacomp_page: sense data for mode select",
}
#endif
sizeof (struct seq_mode));
return (rval);
}
static int
{
int rval;
switch (page) {
case ST_DEV_DATACOMP_PAGE:
case ST_DEV_CONFIG_PAGE: /* FALLTHROUGH */
sizeof (struct seq_mode));
break;
break;
}
break;
}
un->un_comp_page = 0;
} else {
un->un_comp_page = 0;
}
default: /* FALLTHROUGH */
}
return (rval);
}
static int
{
int rval = 0;
int ix;
"st_modeselect(dev = 0x%lx): density = 0x%x\n",
/*
* The parameter list should be the same for all of the
* cases that follow so set them here
*
* Try mode select first if if fails set fields manually
*/
if (rval != 0) {
"st_modeselect: First mode sense failed\n");
}
/*
* If configured to use a specific density code for a media type.
* curdens is previously set by the minor node opened.
* If the media type doesn't match the minor node we change it so it
* looks like the correct one was opened.
*/
/*
* It matches but it might not be the only one.
* Use the highest matching media type but not
* to exceed the density selected by the open.
*/
continue;
}
break;
}
}
/* If a match was found best will not be 0xff any more */
if (best < NDENSITIES) {
"found media 0x%X using density 0x%X\n",
} else {
/* Otherwise set density based on minor node opened */
}
} else {
}
} else {
}
/*
* If st_set_compression returned invalid or already it
* found no need to do the mode select.
* So do it here.
*/
/* Zero non-writeable fields */
/* need to set the density code */
if (rval != 0) {
"unable to set tape mode\n");
} else {
rval = -1;
}
}
}
/*
* The spec recommends to send a mode sense after a mode select
*/
(void) st_modesense(un);
return (rval);
}
/*
* st_gen_mode_sense
*
* generic mode sense.. it allows for any page
*/
static int
{
int r;
char cdb[CDB_GROUP0];
struct scsi_arq_status status;
cdb[0] = SCMD_MODE_SENSE;
return (r);
}
/*
* st_gen_mode_select
*
* generic mode select.. it allows for any page
*/
static int
{
int r;
char cdb[CDB_GROUP0];
struct scsi_arq_status status;
/* Zero non-writeable fields */
page_data->media_type = 0;
/*
* If mode select has any page data, zero the ps (Page Savable) bit.
*/
}
/*
* then, do a mode select to set what ever info
*/
cdb[0] = SCMD_MODE_SELECT;
return (r);
}
static int
{
int rval;
char cdb[CDB_GROUP0];
struct scsi_arq_status status;
cdb[0] = SCMD_READ_BLKLIM;
rval = -1;
}
return (rval);
}
static int
{
int rval;
char cdb[CDB_GROUP1];
struct scsi_arq_status status;
cdb[0] = SCMD_REPORT_DENSITIES;
rval = -1;
}
return (rval);
}
static int
{
int rval;
char cdb[CDB_GROUP5];
struct scsi_arq_status status;
allo_length = sizeof (struct one_com_des) +
sizeof (struct com_timeout_des);
cdb[0] = (char)SCMD_MAINTENANCE_IN;
if (service_action) {
} else {
0x80); /* RCTD */
}
if (com->uscsi_status) {
rval = -1;
}
return (rval);
}
/*
* Changes devices blocksize and bsize to requested blocksize nblksz.
* Returns returned value from first failed call or zero on success.
*/
static int
{
int rval;
/*
* If we haven't got the compression page yet, do that first.
*/
(void) st_modesense(un);
}
/* Read current settings */
if (rval != 0) {
"mode sense for change block size failed: rval = %d", rval);
goto finish;
}
/* Figure the current block size */
oldblksz =
/* If current block size is the same as requested were done */
rval = 0;
goto finish;
}
/* Change to requested block size */
/* Attempt to change block size */
if (rval != 0) {
"Set new block size failed: rval = %d", rval);
goto finish;
}
/* Read back and verify setting */
if (rval == 0) {
"Blocksize set does not equal requested blocksize"
}
}
return (rval);
}
static void
{
"st_init(): dev = 0x%lx, will reset fileno, blkno, eof\n",
if (st_error_level != SCSI_ERR_ALL) {
if (DEBUGGING) {
} else {
}
}
}
static void
{
recov_info *ri;
int tval = 0;
uint32_t additional = 0;
int flags = 0;
char fixbit;
char short_fm = 0;
int stat_size =
/*
* fixbit is for setting the Fixed Mode and Suppress Incorrect
* the Long bit on erase commands, and for setting the Code
* Field bits on space commands.
*/
/* regular raw I/O */
"Read Write scsi_init_pkt() failure\n");
goto exit;
}
#ifdef STDEBUG
#endif
fixbit = 0;
} else {
fixbit = 1;
}
fixbit = 2;
}
} else {
com = SCMD_WRITE;
}
/*
* For really large xfers, increase timeout
*/
/*
* uscsi - build command, allocate scsi resources
*/
goto exit;
} else { /* special I/O */
switch (com) {
case SCMD_READ:
} else {
}
break;
case SCMD_WRITE:
} else {
fixbit = 0;
}
break;
case SCMD_WRITE_FILE_MARK:
fixbit = 0;
/*
* If ST_SHORT_FILEMARKS bit is ON for EXABYTE
* device, set the Vendor Unique bit to
* write Short File Mark.
*/
/*
* Now the Vendor Unique bit 7 in Byte 5 of CDB
* is set to to write Short File Mark
*/
short_fm = 1;
}
break;
case SCMD_REWIND:
/*
* In the case of rewind we're gona do the rewind with
* the immediate bit set so status will be retured when
* the command is accepted by the device. We clear the
* B_ASYNC flag so we wait for that acceptance.
*/
fixbit = 0;
if (count) {
fixbit = 1;
}
}
count = 0;
"rewind\n");
break;
case SCMD_SPACE_G4:
count &= 0xffff;
count <<= 16;
break;
case SCMD_SPACE:
count &= 0xffffffff;
break;
case SCMD_LOAD:
fixbit = 0;
/* Loading or Unloading */
} else {
}
/* Is Retension requested */
}
break;
case SCMD_ERASE:
"erase tape\n");
if (count == 1) {
/*
* do long erase
*/
/* Drive might not honor immidiate bit */
} else {
/* Short Erase */
fixbit = 0;
}
count = 0;
break;
case SCMD_MODE_SENSE:
"mode sense\n");
fixbit = 0;
break;
case SCMD_MODE_SELECT:
"mode select\n");
fixbit = 0;
break;
case SCMD_RESERVE:
"reserve\n");
fixbit = 0;
break;
case SCMD_RELEASE:
"release\n");
fixbit = 0;
break;
case SCMD_READ_BLKLIM:
"read block limits\n");
break;
case SCMD_TEST_UNIT_READY:
"test unit ready\n");
fixbit = 0;
break;
case SCMD_DOORLOCK:
fixbit = 0;
break;
case SCMD_READ_POSITION:
"read position\n");
switch (un->un_read_pos_type) {
case LONG_POS:
count = 0;
break;
case EXT_POS:
count = sizeof (tape_position_ext_t);
break;
case SHORT_POS:
count = 0;
break;
default:
"Unknown read position type 0x%x in "
}
break;
default:
"Unhandled scsi command 0x%x in st_make_cmd()\n",
com);
}
"generic command scsi_init_pkt() failure\n");
goto exit;
}
#ifdef STDEBUG
#endif
if (allocbp) {
}
}
/*
*/
} else {
}
/*
* If we just write data to tape and did a command that doesn't
* change position, we still need to write a filemark.
*/
cmd_attribute const *atrib;
} else {
}
}
}
exit:
}
/*
* Build a command based on a uscsi command;
*/
static void
{
recov_info *ri;
int cdblen;
int stat_size = 1;
int flags = 0;
if (un->un_arq_enabled) {
sizeof (struct scsi_arq_status) -
sizeof (struct scsi_extended_sense);
} else {
stat_size = sizeof (struct scsi_arq_status);
}
}
}
"st_make_uscsi_cmd: buflen=%ld bcount=%ld\n",
"uscsi command scsi_init_pkt() failure\n");
goto exit;
}
#ifdef STDEBUG
#endif
#ifdef STDEBUG
}
#endif
}
} else {
}
exit:
}
/*
* restart cmd currently at the head of the runq
*
* If scsi_transport() succeeds or the retries
* count exhausted, restore the throttle that was
* zeroed out in st_handle_intr_busy().
*
*/
static void
st_intr_restart(void *arg)
{
int queued;
int status = TRAN_ACCEPT;
"st_intr_restart(), un = 0x%p\n", (void *)un);
un->un_hib_tid = 0;
if (un->un_recov_buf_busy != 0) {
queued = 0;
} else if (un->un_sbuf_busy != 0) {
queued = 0;
queued = 1;
} else {
return;
}
/*
* Here we know :
* throttle = 0, via st_handle_intr_busy
*/
if (queued) {
/*
* move from waitq to runq, if there is anything on the waitq
*/
/*
* not good, we don't want to requeue something after
* another.
*/
goto done_error;
} else {
}
}
if (status != TRAN_ACCEPT) {
un->un_unit_attention_flags = 0;
"Command Recovery called on busy resend\n");
ATTEMPT_RETRY) == JUST_RETURN) {
return;
}
}
ST_TRAN_BUSY_TIMEOUT) == 0)
return; /* timeout is setup again */
}
"restart transport rejected\n");
if (un->un_last_throttle) {
}
if (status != TRAN_ACCEPT) {
}
"busy restart aborted\n");
} else {
if (un->un_last_throttle) {
}
}
}
/*
* st_check_media():
* Periodically check the media state using scsi_watch service;
* this service calls back after TUR and possibly request sense
* the callback handler (st_media_watch_cb()) decodes the request sense
* data (if any)
*/
static int
{
int rval = 0;
enum mtio_state prev_state;
"st_check_media:state=%x, mediastate=%x\n",
/*
* is there anything to do?
*/
/*
* submit the request to the scsi_watch service;
* scsi_media_watch_cb() does the real work
*/
goto done;
}
/*
* now wait for media change
* we will not be signalled unless mediastate == state but it
* still better to test for this condition, since there
* is a 5 sec cv_broadcast delay when
* mediastate == MTIO_INSERTED
*/
"st_check_media:waiting for media state change\n");
"st_check_media:waiting for media state "
"was interrupted\n");
goto done;
}
"st_check_media:received signal, state=%x\n",
un->un_mediastate);
}
}
/*
* if we transitioned to MTIO_INSERTED, media has really been
* inserted. If TUR fails, it is probably a exabyte slow spin up.
* Reset and retry the state change. If everything is ok, replay
* the open() logic.
*/
"st_check_media: calling st_cmd to confirm inserted\n");
/*
* set this early so that TUR will make it through strategy
* without triggering a st_tape_init(). We needed it set
* before calling st_tape_init() ourselves anyway. If TUR
* fails, set it back
*/
/*
* If not reserved fail as getting reservation conflict
* will make this hang forever.
*/
if ((un->un_rsvd_status &
(ST_RESERVE | ST_APPLICATION_RESERVATIONS)) == 0) {
goto done;
}
"st_check_media: TUR got Reservation Conflict\n");
goto done;
}
if (rval) {
"st_check_media: TUR failed, going to retry\n");
goto retry;
}
"st_check_media: media inserted\n");
/* this also rewinds the tape */
if (rval != 0) {
"st_check_media : OFFLINE init failure ");
} else {
}
/*
* supported devices must be rewound before ejection
* rewind resets fileno & blkno
*/
}
done:
if (token) {
(void) scsi_watch_request_terminate(token,
}
return (rval);
}
/*
* st_media_watch_cb() is called by scsi_watch_thread for
* verifying the request sense data (if any)
*/
static int
{
int instance;
return (-1);
}
"st_media_watch_cb: status=%x, sensep=%p, len=%x\n",
/*
* if there was a check condition then sensep points to valid
* sense data
* if status was not a check condition but a reservation or busy
* status then the new state is MTIO_NONE
*/
if (sensep) {
"st_media_watch_cb: KEY=%x, ASC=%x, ASCQ=%x\n",
default:
"st_media_watch_cb: unknown drive type %d, "
/* FALLTHROUGH */
case ST_TYPE_STC3490: /* STK 4220 1/2" cartridge */
case ST_TYPE_FUJI: /* 1/2" cartridge */
case ST_TYPE_HP: /* HP 88780 1/2" reel */
"st_media_watch_cb: ST_TYPE_FUJI\n");
} else {
"st_media_watch_cb: ST_TYPE_HP\n");
}
case KEY_UNIT_ATTENTION:
/* not ready to ready transition */
/* hp/es_qual_code == 80 on>off>on */
/* hp/es_qual_code == 0 on>off>unld>ld>on */
}
break;
case KEY_NOT_READY:
/* in process, rewinding or loading */
}
break;
}
break;
case ST_TYPE_EXB8500: /* Exabyte 8500 */
"st_media_watch_cb: ST_TYPE_EXB8500\n");
case KEY_UNIT_ATTENTION:
/* operator medium removal request */
/* not ready to ready transition */
}
break;
case KEY_NOT_READY:
/* medium not present */
}
break;
}
break;
case ST_TYPE_EXABYTE: /* Exabyte 8200 */
"st_media_watch_cb: ST_TYPE_EXABYTE\n");
case KEY_NOT_READY:
/* volume not mounted? */
}
break;
case KEY_UNIT_ATTENTION:
break;
}
break;
case ST_TYPE_DLT: /* quantum DLT4xxx */
case KEY_UNIT_ATTENTION:
}
break;
case KEY_NOT_READY:
/* in transition but could be either */
}
break;
}
break;
}
} else if (*((char *)statusp) == STATUS_GOOD) {
}
"st_media_watch_cb:state=%x, specified=%x\n",
/*
* now signal the waiting thread if this is *not* the specified state;
* delay the signal if the state is MTIO_INSERTED
* to allow the target to recover
*/
if (state == MTIO_INSERTED) {
/*
* delay the signal to give the drive a chance
* to do what it apparently needs to do
*/
"st_media_watch_cb:delayed cv_broadcast\n");
} else {
"st_media_watch_cb:immediate cv_broadcast\n");
}
}
return (0);
}
/*
* delayed cv_broadcast to allow for target to recover
* from media insertion
*/
static void
st_delayed_cv_broadcast(void *arg)
{
"st_delayed_cv_broadcast:delayed cv_broadcast\n");
}
/*
* restart cmd currently at the start of the waitq
*/
static void
st_start_restart(void *arg)
{
}
/*
* Command completion processing
*
*/
static void
{
int status;
"Unhappy packet status reason = %s statistics = 0x%x\n",
/* If device has gone away not much else to do */
} else {
}
/*
* At this point we know that the command was successfully
* completed. Now what?
*/
/*
* okay. We were running a REQUEST SENSE. Find
* out what to do next.
*/
/*
* Make rqs isn't going to be retied.
*/
/*
* set pkt back to original packet in case we will have
* to requeue it
*/
/*
* some actions are based on un_state, hence
* restore the state st was in before ST_STATE_SENSING.
*/
}
/*
* the transport layer successfully completed an autorqsense
*/
/*
* Okay, we weren't running a REQUEST SENSE. Call a routine
* to see if the status bits we're okay. If a request sense
* is to be run, that will happen.
*/
}
switch (action) {
case QUE_COMMAND:
/*
* return cmd to head to the queue
* since we are suspending so that
* it gets restarted during resume
*/
break;
case QUE_SENSE:
break;
default:
break;
}
}
/*
* check for undetected path failover.
*/
if (un->un_multipath) {
int pkt_valid = 0;
if (ucmd) {
/*
* Also copies path instance to the uscsi structure.
*/
/*
* scsi_uscsi_pktfini() zeros pkt_path_instance.
*/
} else {
}
/*
* If the scsi_pkt was not allocated correctly the
* pkt_path_instance is not even there.
*/
if ((pkt_valid != 0) &&
/*
* Don't recover the path change if it was done
* intentionally or if the device has not completely
* opened yet.
*/
"Failover detected, action is %s\n",
if (action == COMMAND_DONE) {
}
}
}
}
/*
* Restore old state if we were sensing.
*/
}
"st_intr: pkt=%p, bp=%p, action=%s, status=%x\n",
switch (action) {
case COMMAND_DONE_EACCES:
/* this is to report a reservation conflict */
"Reservation Conflict \n");
/*FALLTHROUGH*/
case COMMAND_DONE_ERROR:
/*
* all errors set state of the tape to 'unknown'
* unless we're at EOT or are doing append testing.
* If sense key was illegal request, preserve state.
*/
}
}
/*
* since we have an error (COMMAND_DONE_ERROR), we want to
* make sure an error ocurrs, so make sure at least EIO is
* returned
*/
}
break;
"st_intr(): COMMAND_DONE_ERROR_RECOVERED");
}
}
/*FALLTHROUGH*/
case COMMAND_DONE:
break;
case QUE_SENSE:
goto sense_error;
}
/*
* zero the sense data.
*/
/*
* If this is not a retry on QUE_SENSE point to the original
* bp of the command that got us here.
*/
}
if (un->un_throttle) {
un->un_throttle = 0;
}
/*
* never retry this, some other command will have nuked the
* sense, anyway
*/
if (un->un_last_throttle) {
}
if (status == TRAN_ACCEPT) {
return;
}
break;
case QUE_BUSY_COMMAND:
/* longish timeout */
goto que_it_up;
case QUE_COMMAND:
/* short timeout */
/*
* let st_handle_intr_busy put this bp back on waitq and make
* checks to see if it is ok to requeue the command.
*/
/*
* Save the throttle before setting up the timeout
*/
if (un->un_throttle) {
}
return; /* timeout is setup again */
break;
case QUE_LAST_COMMAND:
goto last_command_error;
}
return;
break;
case COMMAND_TIMEOUT:
case DEVICE_RESET:
case DEVICE_TAMPER:
case ATTEMPT_RETRY:
case PATH_FAILED:
"Command Recovery called on %s status\n",
goto again;
default:
ASSERT(0);
/* FALLTHRU */
case JUST_RETURN:
return;
}
}
static errstate
{
static char *fail = "SCSI transport failed: reason '%s': %s\n";
int result;
switch (pkt->pkt_reason) {
case CMD_INCOMPLETE: /* tran stopped with not normal state */
/*
* this occurs when accessing a powered down drive, no
* need to complain; just fail the open
*/
/*
* if we have commands outstanding in HBA, and a command
* comes back incomplete, we're hosed, so reset target
* If we have the bus, but cmd_incomplete, we probably just
* have a failed selection, so don't reset the target, just
* requeue the command and try again
*/
goto reset_target;
}
/*
* Retry selection a couple more times if we're
* open. If opening, we only try just once to
* reduce probe time for nonexistant devices.
*/
/* XXX check retriable? */
rval = QUE_COMMAND;
}
break;
case CMD_ABORTED:
/*
* most likely this is caused by flush-on-error support. If
* it was not there, the we're in trouble.
*/
if (!un->un_flush_on_errors) {
goto reset_target;
}
return (COMMAND_DONE_ERROR);
else
return (COMMAND_DONE);
case CMD_TIMEOUT: /* Command timed out */
return (COMMAND_TIMEOUT);
case CMD_TRAN_ERR:
case CMD_RESET:
if ((un->un_rsvd_status &
ST_RESERVE) {
"Lost Reservation\n");
}
rval = DEVICE_RESET;
return (rval);
}
rval = DEVICE_RESET;
return (rval);
}
/*FALLTHROUGH*/
default:
"Unhandled packet status reason = %s statistics = 0x%x\n",
"transport completed with %s\n",
STAT_ABORTED)) == 0)) {
/*
* If we haven't reserved the drive don't reset it.
*/
if ((un->un_rsvd_status &
(ST_RESERVE | ST_APPLICATION_RESERVATIONS)) == 0) {
return (rval);
}
/*
* if we aren't lost yet we will be soon.
*/
/* no hope left to recover */
"recovery by resets failed\n");
return (rval);
}
}
}
rval = QUE_COMMAND;
/*
* These commands can be rerun
* with impunity
*/
rval = QUE_COMMAND;
}
} else {
cmd_attribute const *attrib;
attrib =
rval = QUE_COMMAND;
}
}
}
} else {
}
(rval == COMMAND_DONE_ERROR)?
"giving up" : "retrying command");
}
return (rval);
}
/*
* if the device is busy, then put this bp back on the waitq, on the
* interrupt thread, where we want the head of the queue and not the
* end
*
* The callers of this routine should take measures to save the
* un_throttle in un_last_throttle which will be restored in
* st_intr_restart(). The only exception should be st_intr_restart()
* calling this routine for which the saving is already done.
*/
static int
{
int queued;
int rval = 0;
"st_handle_intr_busy(), un = 0x%p\n", (void *)un);
queued = 1;
} else {
queued = 0;
}
/*
* Check to see if we hit the retry timeout. We check to make sure
* this is the first one on the runq and make sure we have not
* queued up any more, so this one has to be the last on the list
* also. If it is not, we have to fail. If it is not the first, but
* is the last we are in trouble anyway, as we are in the interrupt
* context here.
*/
rval = -1;
goto exit;
}
/* put the bp back on the waitq */
if (queued) {
}
/*
* We don't want any other commands being started in the mean time.
* If start had just released mutex after putting something on the
* runq, we won't even get here.
*/
un->un_throttle = 0;
/*
* send a marker pkt, if appropriate
*/
/*
* all queues are aligned, we are just waiting to
* transport
*/
exit:
return (rval);
}
/*
* To get one error entry from error stack
*/
static int
{
#ifdef _MULTI_DATAMODEL
/*
* For use when a 32 bit app makes a call into a
* 64 bit ioctl
*/
struct mterror_entry32 err_entry32;
#endif /* _MULTI_DATAMODEL */
int rval = 0;
struct mterror_entry err_entry;
struct mterror_entry_stack *err_link_entry_p;
"st_get_error_entry()\n");
/*
* if error record stack empty, return ENXIO
*/
"st_get_error_entry: Error Entry Stack Empty!\n");
goto ret;
}
/*
* get the top entry from stack
*/
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32:
goto ret;
}
if (ddi_copyout(
"st_get_error_entry: Copy cdb buffer error!");
}
if (ddi_copyout(
"st_get_error_entry: copy arq status error!");
}
"st_get_error_entry: copy arq status out error!");
}
break;
case DDI_MODEL_NONE:
goto ret;
}
if (ddi_copyout(
"st_get_error_entry: Copy cdb buffer error!");
}
if (ddi_copyout(
"st_get_error_entry: copy arq status error!");
}
"st_get_error_entry: copy arq status out error!");
}
break;
}
#else /* _MULTI_DATAMODEL */
goto ret;
}
if (ddi_copyout(
"st_get_error_entry: Copy cdb buffer error!");
}
if (ddi_copyout(
"st_get_error_entry: copy arq status buffer error!");
}
"st_get_error_entry: copy arq status out error!");
}
#endif /* _MULTI_DATAMODEL */
/*
* update stack
*/
ret:
return (rval);
}
/*
* MTIOCGETERROR ioctl needs to retrieve the current sense data along with
* the scsi CDB command which causes the error and generates sense data and
* the scsi status.
*
* error-record stack
*
*
* TOP BOTTOM
* ------------------------------------------
* | 0 | 1 | 2 | ... | n |
* ------------------------------------------
* ^
* |
* pointer to error entry
*
* when st driver generates one sense data record, it creates a error-entry
* and pushes it onto the stack.
*
*/
static void
struct scsi_arq_status *cmd)
{
struct mterror_entry_stack *err_entry_tmp;
"st_update_error_stack()\n");
if (cdblen == 0) {
"st_update_error_stack: CDB length error!\n");
return;
}
/*
* copy cdb command & length to current error entry
*/
/*
* copy scsi status length to current error entry
*/
/*
* copy sense data and scsi status to current error entry
*/
}
/*
* Empty all the error entry in stack
*/
static void
{
struct mterror_entry_stack *linkp;
"st_empty_entry_stack()\n");
}
}
static errstate
{
struct scsi_arq_status arqstat;
int amt;
"st_handle_sense()\n");
"Attempt recovery of busy unit on request sense\n");
"Retry busy unit on request sense\n");
}
return (rval);
"Check Condition on REQUEST SENSE\n");
return (rval);
}
/*
* Make sure there is sense data to look at.
*/
return (rval);
}
/* was there enough data? */
(amt < SUN_MIN_SENSE_LENGTH)) {
"REQUEST SENSE couldn't get sense data\n");
return (rval);
}
sizeof (struct scsi_status));
sizeof (struct scsi_status));
/*
* copy one arqstat entry in the sense data buffer
*/
}
static errstate
{
struct scsi_arq_status *arqstat =
int amt;
"st_handle_autosense()\n");
"busy unit on request sense\n");
/*
* we return QUE_SENSE so st_intr will setup the SENSE cmd.
* the disadvantage is that we do not have any delay for the
* second retry of rqsense and we have to keep a packet around
*/
return (QUE_SENSE);
"transport error on REQUEST SENSE\n");
"recovery by resets failed\n");
}
}
return (rval);
"Check Condition on REQUEST SENSE\n");
return (rval);
}
/* was there enough data? */
} else {
} else {
}
}
(amt < SUN_MIN_SENSE_LENGTH)) {
"REQUEST SENSE couldn't get sense data\n");
return (rval);
}
} else {
}
/*
* copy one arqstat entry in the sense data buffer
*/
}
static errstate
{
cmd_attribute const *attrib;
long resid;
int severity;
int get_error;
"st_decode_sense()\n");
/*
* For uscsi commands, squirrel away a copy of the
* results of the Request Sense.
*/
"st_decode_sense: stat=0x%x resid=0x%x\n",
}
}
} else {
}
/*
* If the drive is an MT-02, reposition the
* secondary error code into the proper place.
*
* XXX MT-02 is non-CCS tape, so secondary error code
* is in byte 8. However, in SCSI-2, tape has CCS definition
* so it's in byte 12.
*/
}
sizeof (*statusp));
/* for normal I/O check extract the resid values. */
resid =
/* If fixed block */
}
} else {
}
"st_decode_sense (rw): xferred bit = %d, resid=%ld (%d), "
/*
* The problem is, what should we believe?
*/
}
} else {
/*
* If the command is SCMD_SPACE, we need to get the
* residual as returned in the sense data, to adjust
* our idea of current tape position correctly
*/
resid =
"st_decode_sense(other): resid=%ld\n", resid);
} else {
/*
* If the special command is SCMD_READ,
* the correct resid will be set later.
*/
} else {
}
"st_decode_sense(special read): resid=%ld\n",
resid);
}
}
"count 0x%lx resid 0x%lx pktresid 0x%lx\n",
}
case KEY_NO_SENSE:
/*
* Erase, locate or rewind operation in progress, retry
* ASC ASCQ
* 00 18 Erase operation in progress
* 00 19 Locate operation in progress
* 00 1A Rewind operation in progress
*/
if (sensep->es_add_code == 0 &&
break;
}
goto common;
case KEY_RECOVERABLE_ERROR:
sensep);
"Command will be retried\n");
} else {
sensep);
}
break;
}
/*
* XXX only want reads to be stopped by filemarks.
* Don't want them to be stopped by EOT. EOT matters
* only on write.
*/
rval = COMMAND_DONE;
rval = COMMAND_DONE;
/*
* Fun with variable length record devices:
* for specifying larger blocks sizes than the
* actual physical record size.
*/
/*
* XXX! Ugly.
* The requested blocksize is > tape blocksize,
* so this is ok, so we just return the
* actual size xferred.
*/
rval = COMMAND_DONE;
/*
* The requested blocksize is < tape blocksize,
* so this is not ok, so we err with ENOMEM
*/
} else {
}
} else {
/*
* we hope and pray for this just being
* something we can ignore (ie. a
* truly recoverable soft error)
*/
rval = COMMAND_DONE;
}
"filemark\n");
}
/*
* ignore eom when reading, a fmk should terminate reading
*/
if ((sensep->es_add_code == 0) &&
"bot\n");
} else {
"eom\n");
}
}
break;
case KEY_ILLEGAL_REQUEST:
} else {
}
break;
case KEY_MEDIUM_ERROR:
/*
* attempt to process the keys in the presence of
* other errors
*/
/*
* Fun with variable length record devices:
* for specifying larger blocks sizes than the
* actual physical record size.
*/
/*
* XXX! Ugly
*/
} else {
}
}
"filemark\n");
}
/*
* ignore eom when reading, a fmk should terminate reading
*/
}
break;
case KEY_VOLUME_OVERFLOW:
goto check_keys;
case KEY_HARDWARE_ERROR:
break;
case KEY_BLANK_CHECK:
/*
* if not a special request and some data was xferred then it
* it is not an error yet
*/
/*
* no error for read with or without data xferred
*/
goto check_keys;
rval = COMMAND_DONE;
} else {
}
"blank check\n");
}
/*
* we were doing a fast forward by skipping
* multiple fmk at the time
*/
rval = COMMAND_DONE;
}
goto check_keys;
case KEY_WRITE_PROTECT:
if (st_wrongtapetype(un)) {
"wrong tape for writing- use DC6150 tape "
"(or equivalent)\n");
} else {
}
break;
case KEY_UNIT_ATTENTION:
/*
* If we have detected a Bus Reset and the tape
* drive has been reserved.
*/
rval = DEVICE_RESET;
if ((un->un_rsvd_status &
ST_RESERVE) {
"st_decode_sense: Lost Reservation\n");
}
}
/*
* If this is a recovery command and retrable, retry.
*/
rval = QUE_COMMAND;
} else {
}
break; /* Don't set position invalid */
}
/*
* If ST_APPLICATION_RESERVATIONS is set,
* has been cleared just allow the write to continue
* which would force a scsi 2 reserve.
* If preempted that persistent reservation
* the scsi 2 reserve would get a reservation conflict.
*/
if ((un->un_rsvd_status &
ST_APPLICATION_RESERVATIONS) != 0) {
/*
* RESERVATIONS PREEMPTED
* With MPxIO this could be a fail over? XXX
*/
break;
/*
* RESERVATIONS RELEASED
*/
rval = COMMAND_DONE;
break;
}
}
/*
* Look, the tape isn't open yet, now determine
* if the cause is a BUS RESET, Save the file
* and Block positions for the callers to
* recover from the loss of position.
*/
(rval == DEVICE_RESET) &&
}
rval = QUE_COMMAND;
} else if (rval == DEVICE_RESET) {
break;
} else {
}
/*
* Means it thinks the mode parameters have changed.
* This is the result of a reset clearing settings or
* another initiator changing what we set.
*/
}
/* Error recovery will modeselect and retry. */
break; /* don't set position invalid */
}
}
/*
* Not Ready to Ready change, Media may have changed.
*/
} else {
if (rval != DEVICE_RESET) {
} else {
/*
* Returning DEVICE_RESET will call
* error recovery.
*/
break; /* don't set position invalid */
}
/*
* Check if it is an Unexpected Unit Attention.
* If state is >= ST_STATE_OPEN, we have
* already done the initialization .
* In this case it is Fatal Error
* can be done with fileno set to < 0.
*/
} else {
}
}
break;
case KEY_NOT_READY:
/*
* If in process of getting ready retry.
*/
switch (sensep->es_qual_code) {
case 0x07:
/*
* We get here when the tape is rewinding.
* QUE_BUSY_COMMAND retries every 10 seconds.
*/
if (ri->pkt_retry_cnt++ <
} else {
/* give up */
}
break;
case 0x01:
rval = QUE_COMMAND;
break;
}
default: /* FALLTHRU */
/* give up */
}
} else {
/* give up */
}
/*
* If this was an error and after device opened
* do error stats.
*/
if (rval == COMMAND_DONE_ERROR &&
}
if (st_error_level >= SCSI_ERR_FATAL)
"Tape not inserted in drive\n");
}
(rval != QUE_COMMAND))
break;
case KEY_ABORTED_COMMAND:
/* XXX Do drives return this when they see a lost light? */
/* Testing would say yes */
goto check_keys;
}
/*
* Probably a parity error...
* if we retry here then this may cause data to be
* written twice or data skipped during reading
*/
goto check_keys;
default:
/*
* Undecoded sense key. Try retries and hope
* that will fix the problem. Otherwise, we're
* dead.
*/
"Unhandled Sense Key '%s'\n",
goto check_keys;
}
(severity >= st_error_level))) {
"File Mark Detected\n");
}
"End-of-Media Detected\n");
}
"Incorrect Length Indicator Set\n");
}
}
if (((rval == COMMAND_DONE_ERROR) ||
(rval == COMMAND_DONE_ERROR_RECOVERED)) &&
} else {
}
}
return (rval);
}
static int
{
int status = TRAN_ACCEPT;
"st_handle_intr_rtr_lcmd(), un = 0x%p\n", (void *)un);
/*
* Check to see if we hit the retry timeout. We check to make sure
* this is the first one on the runq and make sure we have not
* queued up any more, so this one has to be the last on the list
* also. If it is not, we have to fail. If it is not the first, but
* is the last we are in trouble anyway, as we are in the interrupt
* context here.
*/
goto exit;
}
if (un->un_throttle) {
un->un_throttle = 0;
}
/*
* Here we know : bp is the first and last one on the runq
* it is not necessary to put it back on the head of the
* waitq and then move from waitq to runq. Save this queuing
* and call scsi_transport.
*/
if (status == TRAN_ACCEPT) {
if (un->un_last_throttle) {
}
"restart transport \n");
return (0);
}
return (0);
}
}
"restart transport rejected\n");
if (un->un_last_throttle) {
}
exit:
return (-1);
}
static int
{
/*
* Hack to handle 600A, 600XTD, 6150 && 660 vs. 300XL tapes...
*/
case ST_TYPE_WANGTEK:
case ST_TYPE_ARCHIVE:
/*
* If this really worked, we could go off of
* the density codes set in the modesense
* page. For this drive, 0x10 == QIC-120,
* 0xf == QIC-150, and 0x5 should be for
* both QIC-24 and, maybe, QIC-11. However,
* the h/w doesn't do what the manual says
* that it should, so we'll key off of
* getting a WRITE PROTECT error AND wp *not*
* set in the mode sense information.
*/
/*
* XXX but we already know that status is
* write protect, so don't check it again.
*/
return (1);
}
break;
default:
break;
}
}
return (0);
}
static errstate
{
/*
* Command recovery is enabled, not just opening,
* we had the drive reserved and we thing its ours.
* Call recovery to attempt to take it back.
*/
ST_APPLICATION_RESERVATIONS)) != 0)) {
} else {
}
break;
case STATUS_BUSY:
/*
* Status returned by scsi_vhci indicating path
* has failed over.
*/
break;
}
/* FALLTHRU */
case STATUS_QFULL:
/*
* If recovery is inabled use it instead of
* blind reties.
*/
} else if ((un->un_rsvd_status &
(ST_RESERVE | ST_APPLICATION_RESERVATIONS)) == 0) {
/*
* If this is a command done before reserve is done
* don't reset.
*/
} else {
"unit busy too long\n");
}
break;
case STATUS_CHECK:
case STATUS_TERMINATED:
/*
* we should only get here if the auto rqsense failed
* thru a uscsi cmd without autorequest sense
* so we just try again
*/
if (un->un_arq_enabled &&
STATE_GOT_STATUS)) {
"Really got sense data\n");
} else {
"Trying to queue sense command\n");
}
break;
case STATUS_TASK_ABORT:
/*
* This is an aborted task. This can be a reset on the other
* port of a multiport drive. Lets try and recover it.
*/
break;
default:
}
return (action);
}
static void
{
int nblks;
int nfiles;
long count;
cmd_attribute const *attrib;
} else {
}
/* Command reads or writes data */
if (count == 0) {
/* failed writes should not make it here */
nblks = 0;
nfiles = 1;
/*
* If variable block mode.
* Fixed bit in CBD should be zero.
*/
nblks = 1;
nfiles = 0;
} else {
/*
* If fixed block mode.
* Fixed bit in CBD should be one.
*/
nfiles = 0;
}
/*
* So its possable to read some blocks and hit a filemark.
* Example reading in fixed block mode where more then one
* block at a time is requested. In this case because the
* filemark is hit something less then the requesed number
* of blocks is read.
*/
nfiles = 1;
}
} else {
nblks = 0;
}
/*
* If some command failed after this one started and it seems
* to have finshed without error count the position.
*/
}
} else {
ASSERT(0);
}
/* recovery disabled */
return;
}
/*
* If we didn't just read a filemark.
*/
/*
* If Previously calulated expected position does not match
* debug the expected position.
*/
#ifdef STDEBUG
#endif
}
} else {
/*
* blkno and lgclblkno already counted in
* st_add_recovery_info_to_pkt(). Since a block was not
* read and a filemark was.
*/
}
}
}
}
static void
{
"st_set_state(): eof=%x fmneeded=%x pkt_resid=0x%lx (%ld)\n",
#ifdef STDEBUG
"pkt_resid %ld bcount %ld\n",
}
#endif
}
un->un_fmneeded = 0;
} else {
} else {
}
}
/*
* all is honky dory at this point, so let's
* readjust the throttle, to increase speed, if we
* have not throttled down.
*/
if (un->un_throttle) {
}
} else {
switch (cmd) {
case SCMD_WRITE:
case SCMD_WRITE_G4:
break;
}
} else {
}
break;
case SCMD_READ:
case SCMD_READ_G4:
break;
}
un->un_fmneeded = 0;
break;
case SCMD_WRITE_FILE_MARK_G4:
case SCMD_WRITE_FILE_MARK:
{
int fmdone;
}
if (fmdone > 0) {
} else {
"Flushed buffer\n");
}
un->un_fmneeded = 0;
} else {
}
break;
}
case SCMD_REWIND:
un->un_restore_pos = 0;
break;
case SCMD_SPACE:
case SCMD_SPACE_G4:
{
cmd_attribute const *attrib;
} else {
attrib =
}
if (count >= 0) {
} else {
}
if (done > 0) {
} else {
}
"space cmd: cdb[1] = %s\n"
"space data: = 0x%lx\n"
"fileno before = %d\n"
"blkno before = %d\n",
case SPACE_TYPE(SP_FLM):
/* Space file forward */
if (count >= 0) {
}
break;
}
/* Space file backward */
} else {
}
break;
case SPACE_TYPE(SP_BLK):
/* Space block forward */
if (count >= 0) {
break;
}
/* Space block backward */
/*
* we stepped back into
* a previous file; we are not
* making an effort to pretend that
* we are still in the current file
* ie. logical == physical position
* and leave it to st_ioctl to correct
*/
} else {
}
} else {
}
break;
case SPACE_TYPE(SP_SQFLM):
break;
case SPACE_TYPE(SP_EOD):
break;
default:
"Unsupported space cmd: %s\n",
}
break;
}
case SCMD_LOAD:
} else {
}
/*
* If we are loading or unloading we expect the media id
* to change. Lets make it unknown.
*/
un->un_media_id_len = 0;
}
un->un_density_known = 0;
break;
case SCMD_ERASE:
break;
case SCMD_RESERVE:
un->un_rsvd_status &=
~(ST_RELEASE | ST_LOST_RESERVE |
break;
case SCMD_RELEASE:
un->un_rsvd_status &=
~(ST_RESERVE | ST_LOST_RESERVE |
break;
"PGR_IN command\n");
break;
case ST_SA_SCSI3_RESERVE:
case ST_SA_SCSI3_PREEMPT:
un->un_rsvd_status |=
"PGR Reserve and set: entering"
" ST_APPLICATION_RESERVATIONS mode");
break;
case ST_SA_SCSI3_REGISTER:
"PGR Reserve register key");
break;
case ST_SA_SCSI3_CLEAR:
/* FALLTHROUGH */
case ST_SA_SCSI3_RELEASE:
un->un_rsvd_status &=
"PGR Release and reset: exiting"
" ST_APPLICATION_RESERVATIONS mode");
break;
}
break;
case SCMD_TEST_UNIT_READY:
case SCMD_READ_BLKLIM:
case SCMD_REQUEST_SENSE:
case SCMD_INQUIRY:
case SCMD_RECOVER_BUF:
case SCMD_MODE_SELECT:
case SCMD_MODE_SENSE:
case SCMD_DOORLOCK:
case SCMD_READ_BUFFER:
case SCMD_REPORT_DENSITIES:
case SCMD_LOG_SELECT_G1:
case SCMD_LOG_SENSE_G1:
case SCMD_REPORT_LUNS:
case SCMD_READ_ATTRIBUTE:
case SCMD_WRITE_ATTRIBUTE:
case SCMD_SVC_ACTION_IN_G5:
break;
case SCMD_READ_POSITION:
/*
* Only if the buf used was un_sbufp.
* Among other things the prevents read positions used
* as part of error recovery from messing up our
* current position as they will use un_recov_buf.
*/
}
break;
case SCMD_LOCATE:
case SCMD_LOCATE_G4:
/* Locate makes position mode no longer legacy */
break;
case SCMD_MAINTENANCE_IN:
break;
}
if (new_lastop != ST_OP_NIL) {
break;
}
default:
/*
* Unknown command, If was USCSI and USCSI_SILENT
* flag was not set, set position to unknown.
*/
"unknown cmd 0x%X caused loss of state\n",
cmd);
} else {
/*
* keep the old agreement to allow unknown
* commands with the USCSI_SILENT set.
* This prevents ASSERT below.
*/
break;
}
/* FALLTHROUGH */
case SCMD_WRITE_BUFFER: /* Writes new firmware to device */
break;
}
/* new_lastop should have been changed */
/* If un_lastop should copy new_lastop */
new_lastop != ST_OP_CTL) {
}
}
/*
* In the st driver we have a logical and physical file position.
* Under BSD behavior, when you get a zero read, the logical position
* is before the filemark but after the last record of the file.
* The physical position is after the filemark. MTIOCGET should always
* return the logical file position.
*
* The next read gives a silent skip to the next file.
* Under SVR4, the logical file position remains before the filemark
* until the file is closed or a space operation is performed.
* Hence set err_resid and err_file before changing fileno if case
* BSD Behaviour.
*/
/*
* If we've seen a filemark via the last read operation
* advance the file counter, but mark things such that
* the next read operation gets a zero count. We have
* to put this here to handle the case of sitting right
* at the end of a tape file having seen the file mark,
* but the tape is closed and then re-opened without
* any further i/o. That is, the position information
* must be updated before a close.
*/
/*
* If we're a 1/2" tape, and we get a filemark
* right on block 0, *AND* we were not in the
* first file on the tape, and we've hit logical EOM.
* We'll mark the state so that later we do the
* right thing (in st_close(), st_strategy() or
* st_ioctl()).
*
*/
"eot pending\n");
} else if (BSD_BEHAVIOR) {
/*
* If the read of the filemark was a side effect
* of reading some blocks (i.e., data was actually
* read), then the EOF mark is pending and the
* bump into the next file awaits the next read
* operation (which will return a zero count), or
* a close or a space operation, else the bump
* into the next file occurs now.
*/
"resid=%lx, bcount=%lx\n",
} else {
}
"eof of file %d, eof=%d\n",
} else if (SVR4_BEHAVIOR) {
/*
* If the read of the filemark was a side effect
* of reading some blocks (i.e., data was actually
* read), then the next read should return 0
*/
"resid=%lx, bcount=%lx\n",
}
"eof of file=%d, eof=%d\n",
}
}
}
/*
* set the correct un_errno, to take corner cases into consideration
*/
static void
{
/* if errno is already set, don't reset it */
return;
/* here un_errno == 0 */
/*
* if the last transfer before flushing all the
* waiting I/O's, was 0 (resid = count), then we
* want to give the user an error on all the rest,
* so here. If there was a transfer, we set the
* resid and counts to 0, and let it drop through,
* giving a zero return. the next I/O will then
* give an error.
*/
case ST_EOM:
break;
case ST_EOT:
case ST_EOF:
break;
}
} else {
/*
* we know they did not have a zero, so make
* sure they get one
*/
}
}
/*
* send in a marker pkt to terminate flushing of commands by BBA (via
* flush-on-errors) property. The HBA will always return TRAN_ACCEPT
*/
static void
{
if (!un->un_flush_on_errors)
return;
#ifdef FLUSH_ON_ERRORS
if (!un->un_mkr_pkt) {
/* we slept, so it must be there */
}
#endif
}
static char *
st_print_scsi_cmd(char cmd)
{
char tmp[64];
char *cpnt;
/* tmp goes out of scope on return and caller sees garbage */
cpnt = "Unknown Command";
}
return (cpnt);
}
static void
{
char buf[256];
/* force one line output so repeated commands are printed once */
return;
}
/* force one line output so repeated CDB's are printed once */
} else {
st_print_scsi_cmd(*cdb));
}
}
static void
{
int i;
int c;
char *format;
char buf[256];
if (title) {
}
for (i = 0; i < len; ) {
buf[0] = 0;
for (c = 0; c < 8 && i < len; c++, i++) {
if (byte < 0x10)
format = "0x0%x ";
else
format = "0x%x ";
}
}
}
/*
* Conditionally enabled debugging
*/
#ifdef STDEBUG
static void
{
char tmpbuf[64];
"cmd=%s count=0x%x (%d) %ssync\n",
}
#endif /* STDEBUG */
/*
* Returns pointer to name of minor node name of device 'dev'.
*/
static char *
{
static char name[32];
int instance;
int nprt = 0;
if (un) {
}
}
if (minor & MT_NOREWIND) {
}
/* NULL terminator */
return (name);
}
/*
* Soft error reporting, so far unique to each drive
*
* Currently supported: exabyte and DAT soft error reporting
*/
static int
{
int amt;
int rval = 0;
"st_report_exabyte_soft_errors(dev = 0x%lx, flag = %d)\n",
*c++ = SCMD_REQUEST_SENSE;
*c++ = 0;
*c++ = 0;
*c++ = 0;
*c++ = TAPE_SENSE_LENGTH;
/*
* set CLRCNT (byte 5, bit 7 which clears the error counts)
*/
*c = (char)0x80;
goto done;
}
/*
* was there enough data?
*/
"Periodic head cleaning required");
}
goto done;
}
/*
* check if soft error reporting needs to be done.
*/
count &= 0xffffff;
#ifdef STDEBUG
if (st_soft_error_report_debug) {
"Exabyte Soft Error Report:\n");
"number of bytes transferred: %dK\n",
"error_rate: %d%%\n", error_rate);
if (amt >= 22) {
"unit sense: 0x%b 0x%b 0x%b\n",
}
if (amt >= 27) {
"tracking retry counter: %d\n",
sensep[26]);
sensep[27]);
}
}
#endif
} else {
}
if (error_rate >= rate) {
"Soft error rate (%d%%) during %s was too high",
"Please, replace tape cartridge\n");
}
}
done:
if (rval != 0) {
"exabyte soft error reporting failed\n");
}
return (rval);
}
/*
* this is very specific to Archive 4mm dat
*/
static int
{
int amt, i;
int rval = 0;
struct scsi_arq_status status;
*c++ = SCMD_LOG_SENSE_G1;
*c++ = 0;
*c++ = 0;
*c++ = 0;
*c++ = 0;
*c++ = 2;
*c++ = 0;
*c++ = (char)LOG_SENSE_LENGTH;
*c = 0;
if (rval) {
"DAT soft error reporting failed\n");
}
goto done;
}
/*
* was there enough data?
*/
total = -1;
retries = -1;
#ifdef STDEBUG
if (st_soft_error_report_debug) {
(void) printf("logsense:");
for (i = 0; i < MIN_LOG_SENSE_LENGTH; i++) {
if (i % 16 == 0) {
(void) printf("\t\n");
}
}
(void) printf("\n");
}
#endif
/*
* parse the param_codes
*/
for (i = 4; i < amt; i++) {
param_code += sensep[i++];
i++; /* skip control byte */
if (param_code == 5) {
if (sensep[i++] == 4) {
}
} else if (param_code == 0x8007) {
if (sensep[i++] == 2) {
}
} else {
i += sensep[i];
}
}
}
/*
* if the log sense returned valid numbers then determine
* the read and write error thresholds based on the amount of
* data transferred
*/
short normal_retries = 0;
"total xferred (%s) =%x, retries=%x\n",
if (total <=
} else {
}
} else {
if (total <=
} else {
}
}
"normal retries=%d\n", normal_retries);
if (retries >= normal_retries) {
"Soft error rate (retries = %d) during "
"%s was too high", retries,
"Periodic head cleaning required "
}
"log sense parameter code does not make sense\n");
}
}
/*
* reset all values
*/
c = cdb;
*c++ = SCMD_LOG_SELECT_G1;
*c++ = 2; /* this resets all values */
*c++ = (char)0xc0;
*c++ = 0;
*c++ = 0;
*c++ = 0;
*c++ = 0;
*c++ = 0;
*c++ = 0;
*c = 0;
com->uscsi_buflen = 0;
if (rval) {
"DAT soft error reset failed\n");
}
done:
return (rval);
}
static int
{
case ST_TYPE_EXB8500:
case ST_TYPE_EXABYTE:
/*NOTREACHED*/
case ST_TYPE_PYTHON:
/*NOTREACHED*/
default:
return (-1);
}
}
/*
* persistent error routines
*/
/*
* enable persistent errors, and set the throttle appropriately, checking
* for flush-on-errors capability
*/
static void
{
/*
* only use flush-on-errors if auto-request-sense and untagged-qing are
* enabled. This will simplify the error handling for request senses
*/
1 : 0;
} else {
un->un_flush_on_errors = 0;
}
if (un->un_flush_on_errors)
else
/* this will send a marker pkt */
}
/*
* This turns persistent errors permanently off
*/
static void
{
/* turn it off for good */
un->un_persistence = 0;
/* this will send a marker pkt */
/* turn off flush on error capability, if enabled */
if (un->un_flush_on_errors) {
}
un->un_flush_on_errors = 0;
}
/*
* This clear persistent errors, allowing more commands through, and also
* sending a marker packet.
*/
static void
{
un->un_persist_errors = 0;
}
/*
* This will flag persistent errors, shutting everything down, if the
* application had enabled persistent errors via MTIOCPERSISTENT
*/
static void
{
if (un->un_persistence) {
}
}
static int
{
int rval;
/*
* Issue a Throw-Away reserve command to clear the
* check condition.
* hold reservation across opens , and if a Bus reset
* has been issued between opens then this command
* would set the ST_LOST_RESERVE flags in rsvd_status.
* In this case return an EACCES so that user knows that
* reservation has been lost in between opens.
* If this error is not returned and we continue with
* successful open , then user may think position of the
* tape is still the same but inreality we would rewind the
* tape and continue from BOT.
*/
if (rval) {
return (EACCES);
}
}
if (rval == 0) {
}
if (was_lost) {
}
return (rval);
}
static int
{
int rval;
cmd_attribute const *attrib;
/*
* If already reserved no need to do it again.
* Also if Reserve and Release are disabled Just return.
*/
"st_check_cdb_for_need_to_reserve() reserve unneeded %s",
return (0);
}
/* See if command is on the list */
} else if ((attrib->requires_reserve) != 0) {
rval = 1;
} else if ((attrib->reserve_byte) != 0) {
/*
* cmd is on list.
* if byte is zero always allowed.
*/
rval = 1;
(attrib->reserve_mask)) != 0) {
rval = 1;
} else {
rval = 0;
}
if (rval) {
"Command %s requires reservation",
st_print_scsi_cmd(cdb[0]));
}
return (rval);
}
static int
{
int rval;
cmd_attribute const *attrib;
/*
* Do not reserve when already reserved, when not supported or when
* auto-rewinding on device closure.
*/
"st_check_cmd_for_need_to_reserve() reserve unneeded %s",
return (0);
}
/* search for this command on the list */
} else if ((attrib->requires_reserve) != 0) {
rval = 1;
} else if ((attrib->reserve_byte) != 0) {
/*
* cmd is on list.
* if byte is zero always allowed.
*/
rval = 1;
rval = 1;
} else {
rval = 0;
}
if (rval) {
}
return (rval);
}
static int
{
int rval;
char cdb[CDB_GROUP0];
struct scsi_arq_status stat;
"st_reserve_release: %s \n",
if (cmd == ST_RELEASE) {
cdb[0] = SCMD_RELEASE;
} else {
cdb[0] = SCMD_RESERVE;
}
"st_reserve_release: rval(1)=%d\n", rval);
if (rval) {
}
/*
* in case of drives which do not support
*/
"Tape unit does not support "
}
rval = 0;
}
}
return (rval);
}
static int
{
int rval;
"st_take_ownership: Entering ...\n");
/*
* XXX -> Should reset be done only if we get EACCES.
* .
*/
if (rval) {
return (EIO);
}
un->un_rsvd_status &=
/*
* remove the check condition.
*/
if (rval != 0) {
!= 0) {
return (rval);
}
}
/*
* Set tape state to ST_STATE_OFFLINE , in case if
* the user wants to continue and start using
* the tape.
*/
}
return (rval);
}
static int
{
char kstatname[KSTAT_STRLEN];
/*
* Create device error kstats
*/
"device_error", KSTAT_TYPE_NAMED,
sizeof (struct st_errstats) / sizeof (kstat_named_t),
if (un->un_errstats) {
struct st_errstats *stp;
/*
* Fill in the static data
*/
/*
* XXX: Emulex MT-02 (and emulators) predates
* SCSI-1 and has no vid & pid inquiry data.
*/
if (ST_INQUIRY->inq_len != 0) {
}
}
}
return (0);
}
static int
{
int rval;
/* Can't restore an invalid position */
return (4);
}
/*
* Assumtions:
* If a position was read and is in logical position mode.
* If a drive supports read position it supports locate.
* If the read position type is not NO_POS. even though
* a read position make not have been attemped yet.
*
* The drive can locate to the position.
*/
/*
* If position mode is logical or legacy mode try
* to locate there as it is faster.
* If it fails try the old way.
*/
/* Assume we are there copy rest of position back */
}
return (0);
}
/*
* If logical block locate failed to restore a logical
* position, can't recover.
*/
return (-1);
}
}
"Restoring tape position at fileno=%x, blkno=%x....",
/*
* Rewind ? Oh yeah, Fidelity has got the STK F/W changed
* so as not to rewind tape on RESETS: Gee, Has life ever
* been simple in tape land ?
*/
if (rval) {
"Failed to restore the last file and block position: In"
" this state, Tape will be loaded at BOT during next open");
return (rval);
}
/* If the position was as the result of back space file */
/* Go one extra file forward */
/* Figure how many blocks to back into the previous file */
}
/* Go to requested fileno */
if (rval) {
"Failed to restore the last file position: In this "
" state, Tape will be loaded at BOT during next"
" open %d", __LINE__);
return (rval);
}
}
/*
* If backing into a file we already did an extra file forward.
* Now we have to back over the filemark to get to the end of
* the previous file. The blkno has been ajusted to a negative
* value so we will get to the expected location.
*/
if (rval) {
"Failed to restore the last file position: In this "
" state, Tape will be loaded at BOT during next"
" open %d", __LINE__);
return (rval);
}
}
/*
* The position mode, block and fileno should be correct,
* This updates eof and logical position information.
*/
return (0);
}
/*
* check sense key, ASC, ASCQ in order to determine if the tape needs
* to be ejected
*/
static int
{
struct tape_failure_code *code;
return (1);
}
return (0);
}
/*
* st_logpage_supported() sends a Log Sense command with
* page code = 0 = Supported Log Pages Page to the device,
* to see whether the page 'page' is supported.
* Return values are:
* -1 if the Log Sense command fails
* 0 if page is not supported
* 1 if page is supported
*/
static int
{
unsigned length;
struct scsi_arq_status status;
int rval;
char cdb[CDB_GROUP1] = {
0,
0,
0,
0,
0,
0,
(char)LOG_SENSE_LENGTH,
0
};
com->uscsi_flags =
/* uscsi-command failed */
rval = -1;
} else {
rval = 1;
break;
}
}
}
return (rval);
}
/*
* st_check_clean_bit() gets the status of the tape's cleaning bit.
*
* If the device does support the TapeAlert log page, then the cleaning bit
* information will be read from this page. Otherwise we will see if one of
* ST_CLN_TYPE_1, ST_CLN_TYPE_2 or ST_CLN_TYPE_3 is set in the properties of
* the device, which means, that we can get the cleaning bit information via
* a RequestSense command.
* If both methods of getting cleaning bit information are not supported
* st_check_clean_bit() will return with 0. Otherwise st_check_clean_bit()
* returns with
* - MTF_TAPE_CLN_SUPPORTED if cleaning bit is not set or
* - MTF_TAPE_CLN_SUPPORTED | MTF_TAPE_HEAD_DIRTY if cleaning bit is set.
* If the call to st_uscsi_cmd() to do the Log Sense or the Request Sense
* command fails, or if the amount of Request Sense data is not enough, then
* st_check_clean_bit() returns with -1.
*/
static int
{
int rval = 0;
return (rval);
}
if (rval == -1) {
return (0);
}
if (rval == 1) {
}
if (rval == -1) {
return (0);
}
if (rval == 1) {
}
}
}
rval = 0;
if (rval == -1) {
return (0);
}
}
if (rval == -1) {
return (0);
}
}
if (rval == -1) {
return (0);
}
}
/*
* If found a supported means to check need to clean.
*/
if (rval & MTF_TAPE_CLN_SUPPORTED) {
/*
* head needs to be cleaned.
*/
if (rval & MTF_TAPE_HEAD_DIRTY) {
/*
* Print log message only first time
* found needing cleaned.
*/
"Periodic head cleaning required");
}
} else {
}
}
return (rval);
}
static int
{
int rval;
int ix;
struct log_sequential_page *sp;
struct log_sequential_page_parameter *prm;
struct scsi_arq_status status;
char cdb[CDB_GROUP1] = {
0,
0,
0,
0,
0,
(char)(sizeof (struct log_sequential_page) >> 8),
(char)(sizeof (struct log_sequential_page)),
0
};
cmd->uscsi_flags =
rval = -1;
rval = -1;
}
break;
}
if (parameter == SEQUENTIAL_NEED_CLN) {
"sequential log says head dirty\n");
}
}
prm = (struct log_sequential_page_parameter *)
}
return (rval);
}
static int
{
struct st_tape_alert *ta;
struct scsi_arq_status status;
int rval;
char cdb[CDB_GROUP1] = {
0,
0,
0,
0,
0,
(char)(sizeof (struct st_tape_alert) >> 8),
(char)(sizeof (struct st_tape_alert)),
0
};
com->uscsi_flags =
rval = -1;
}
if (length != TAPE_ALERT_PARAMETER_LENGTH) {
"TapeAlert length %d\n", length);
}
/*
* if rval is bad before the first pass don't bother
*/
break;
}
continue;
}
/*
* check to see if current parameter is of interest.
* CLEAN_FOR_ERRORS is vendor specific to 9840 9940 stk's.
*/
if ((flag == TAF_CLEAN_NOW) ||
(flag == TAF_CLEAN_PERIODIC) ||
((flag == CLEAN_FOR_ERRORS) &&
"alert_page drive needs clean %d\n", flag);
} else if (flag == TAF_CLEANING_MEDIA) {
"alert_page drive was cleaned\n");
}
}
/*
* Report it as dirty till we see it cleaned
*/
"alert_page still dirty\n");
}
return (rval);
}
static int
{
char cdb[CDB_GROUP0];
unsigned length;
int index;
int rval;
/*
* Since this tape does not support Tape Alert,
* we now try to get the cleanbit status via
* Request Sense.
*/
index = 0;
index = 1;
index = 2;
} else {
return (-1);
}
cdb[0] = SCMD_REQUEST_SENSE;
cdb[1] = 0;
cdb[2] = 0;
cdb[3] = 0;
cdb[5] = 0;
com->uscsi_flags =
rval = -1;
} else {
"sense data says head dirty\n");
}
}
return (rval);
}
/*
* st_clear_unit_attention
*
* run test unit ready's to clear out outstanding
* unit attentions.
* returns zero for SUCCESS or the errno from st_cmd call
*/
static int
{
int i = 0;
int rval;
do {
return (rval);
}
static void
{
} else {
}
}
} else {
}
}
} else {
}
}
} else {
}
}
} else {
}
}
} else {
}
}
} else {
}
}
}
static writablity
{
return (RDWR);
}
static writablity
{
/* Mode sense should be current */
"Drive has WORM media loaded\n");
} else {
"Drive has non WORM media loaded\n");
}
return (wrt);
}
#define HP_DAT_INQUIRY 0x4A
static writablity
{
char *buf;
int result;
if (result != 0) {
"Read Standard Inquiry for WORM support failed");
"Drive is NOT WORMable\n");
/* This drive doesn't support it so don't check again */
} else {
}
/*
* If drive doesn't support it no point in checking further.
*/
return (wrt);
}
static writablity
{
/* Mode sense should be current */
case 0x00:
case 0x40:
"Drive has standard Gen I media loaded\n");
break;
case 0x42:
"Drive has standard Gen II media loaded\n");
break;
case 0x44:
"Drive has standard Gen III media loaded\n");
break;
case 0x46:
"Drive has standard Gen IV media loaded\n");
break;
default:
"Drive has standard unknown 0x%X media loaded\n",
}
break;
case 0x01:
"Drive has WORM medium loaded\n");
break;
case 0x80:
"Drive has CD-ROM emulation medium loaded\n");
break;
default:
"Drive has an unexpected medium type 0x%X loaded\n",
}
return (wrt);
}
#define LTO_REQ_INQUIRY 44
static writablity
{
char *buf;
int result;
if (result != 0) {
"Read Standard Inquiry for WORM support failed");
"Drive is NOT WORMable\n");
/* This drive doesn't support it so don't check again */
} else {
}
/*
* If drive doesn't support it no point in checking further.
*/
return (wrt);
}
static writablity
{
"Drive has WORM media loaded\n");
} else {
"Drive has non WORM media loaded\n");
}
return (wrt);
}
#define SEQ_CAP_PAGE (char)0xb0
static writablity
{
char *buf;
int result;
if (result != 0) {
"Read Vitial Inquiry for Sequental Capability"
" WORM support failed %x", result);
"Drive is NOT WORMable\n");
/* This drive doesn't support it so don't check again */
} else {
"Drive supports WORM\n");
}
return (wrt);
}
#define STK_REQ_SENSE 26
static writablity
{
struct scsi_extended_sense *sense;
char *buf;
int result;
cmd->uscsi_rqlen = 0;
"Request Sense for WORM failed");
"Drive didn't send enough sense data for WORM byte %d\n",
"Drive has WORM tape loaded\n");
} else {
"Drive has normal tape loaded\n");
}
return (wrt);
}
#define DLT_INQ_SZ 44
static writablity
{
int result;
/* Read Attribute Media Type */
/*
* If this quantum drive is attached via an HBA that cannot
* support thr read attributes command return error in the
* hope that someday they will support the t10 method.
*/
"Read Attribute Command for WORM Media detection is not "
"supported on the HBA that this drive is attached to.");
goto out;
}
if (result != 0) {
"Read Attribute Command for WORM Media returned 0x%x",
result);
goto out;
}
"Drive media is WORM\n");
} else {
}
out:
return (wrt);
}
static writablity
{
int result;
if (result != 0) {
"Read Vendor Specific Inquiry for WORM support failed");
goto out;
}
"Drive is not WORMable\n");
goto out;
} else {
"Drive is WORMable\n");
}
out:
return (wrt);
}
typedef struct {
struct modeheader_seq header;
#if defined(_BIT_FIELDS_LTOH) /* X86 */
:2;
device :1,
abs :1,
ulpbot :1,
prth :1,
ponej :1,
ait :1;
uchar_t :6,
worm :1,
mic :1;
:7;
uint32_t :32;
#else /* SPARC */
uchar_t :2,
pagecode :6;
device :1,
abs :1,
ulpbot :1,
prth :1,
ponej :1,
syslogalive :2;
worm :1,
:6;
uchar_t :7,
worm_cap :1;
uint32_t :32;
#endif
#define AIT_DEV_PAGE 0x31
static writablity
{
int result;
if (result == 0) {
"returned page 0x%x not 0x%x AIT_DEV_PAGE\n",
"Drives is WORMable\n");
"Media is WORM\n");
} else {
"Media is not WORM\n");
}
} else {
"Drives not is WORMable\n");
/* No further checking required */
}
} else {
"AIT device config mode sense page read command failed"
" result = %d ", result);
}
return (wrt);
}
static writablity
{
case MT_ISDLT:
break;
case MT_ISSTK9840:
break;
case MT_IS8MM:
case MT_ISAIT:
break;
case MT_LTO:
} else {
}
break;
case MT_ISDAT:
} else {
}
break;
default:
break;
}
/*
* If any of the above failed try the t10 standard method.
*/
}
/*
* Unknown method for detecting WORM media.
*/
"Unknown method for WORM media detection\n");
}
return (wrt);
}
static int
{
char cdb[CDB_GROUP4];
int result;
struct scsi_arq_status status;
return (ENOTTY);
}
cdb[0] = (char)SCMD_READ_ATTRIBUTE;
cdb[1] = 0;
cdb[2] = 0;
cdb[3] = 0;
cdb[4] = 0;
cdb[5] = 0;
cdb[6] = 0;
cdb[7] = 0;
cdb[14] = 0;
cdb[15] = 0;
"st_read_attribute failed: result %d status %d\n",
/*
* If this returns invalid operation code don't try again.
*/
} else if (result == 0) {
}
} else {
/*
* The attribute retured should match the attribute requested.
*/
"st_read_attribute got wrong data back expected "
}
}
return (result);
}
static int
{
char cdb[CDB_GROUP0];
struct scsi_extended_sense *sense;
int result;
cdb[0] = SCMD_INQUIRY;
cdb[3] = 0;
cdb[5] = 0;
"st_get_special_inquiry() failed for page %x", page);
if (result == 0) {
}
}
return (result);
}
static int
{
/*
* If read position command returned good status
* Parse the data to see if the position can be interpreted.
*/
if ((rval == 0) &&
post_space)) == 0)) {
/*
* Update the running position as well if un_pos was
* ok. But only if recovery is enabled.
*/
if (st_recov_sz != sizeof (recov_info)) {
break;
}
break;
continue;
"st_update_block_pos() read position cmd 0x%x"
" returned 0x%x un_status = %d",
/* ENOTTY means it read garbage. try something else. */
} else {
break;
}
} else {
"st_update_block_pos() read position cmd %x"
}
switch (un->un_read_pos_type) {
case SHORT_POS:
break;
case LONG_POS:
break;
case EXT_POS:
break;
default:
"Unexpected read position type 0x%x",
}
}
return (rval);
}
static int
{
int result;
return (0);
}
"bp_mapin_common() failed");
return (EIO);
}
if (d_sz == 0) {
return (EIO);
}
/*
* Copy the buf to a double-word aligned memory that can hold the
* tape_position_t data structure.
*/
"kmem_alloc() failed");
return (EIO);
}
#ifdef STDEBUG
"st_get_read_pos() position info",
}
#endif
return (result);
}
#if defined(_BIG_ENDIAN)
#define FIX_ENDIAN16(x)
#define FIX_ENDIAN32(x)
#define FIX_ENDIAN64(x)
#elif defined(_LITTLE_ENDIAN)
static void
{
}
static void
{
}
static void
{
}
#define FIX_ENDIAN16(x) st_swap16(x)
#define FIX_ENDIAN32(x) st_swap32(x)
#define FIX_ENDIAN64(x) st_swap64(x)
#endif
/*
* st_interpret_read_pos()
*
* Returns:
* 0 If secsessful.
* EIO If read postion responce data was unuseable or invalid.
* ERANGE If the position of the drive is too large for the read_p_type.
* ENOTTY If the responce data looks invalid for the read position type.
*/
static int
{
int rval = 0;
int flag = 0;
/*
* We expect the position value to change after a space command.
* So if post_space is set we don't print out what has changed.
*/
(st_recov_sz == sizeof (recov_info))) {
flag = 1;
}
/*
* See what kind of read position was requested.
*/
switch (type) {
case SHORT_POS: /* Short data format */
{
/* If reserved fields are non zero don't use the data */
"Invalid Read Short Position Data returned\n");
break;
}
/*
* Position is to large to use this type of read position.
*/
"Drive reported position error\n");
break;
}
/*
* If your at the begining of partition and end at the same
* time it's very small partition or bad data.
*/
"SHORT_POS returned begin and end of"
" partition\n");
break;
}
if (pos_info->blk_posi_unkwn == 0) {
/*
* If the tape is rewound the host blcok should be 0.
*/
(value != 0)) {
"SHORT_POS returned begin of partition"
" but host block was 0x%x\n", value);
break;
}
if (flag)
flag++;
}
/*
* If the begining of partition is true and the
* block number is zero we will beleive that it is
* rewound. Promote the pmode to legacy.
*/
(value == 0)) {
/*
* otherwise if the pmode was invalid,
* promote it to logical.
*/
}
if (flag)
flag++;
"SHORT_POS current partition %d read %d\n",
}
} else {
"Tape drive reported block position as unknown\n");
}
break;
}
case LONG_POS: /* Long data format */
{
/* If reserved fields are non zero don't use the data */
if ((long_pos_info->reserved0) ||
(long_pos_info->reserved1) ||
(long_pos_info->reserved2)) {
"Invalid Read Long Position Data returned\n");
break;
}
/* Is position Valid */
if (long_pos_info->blk_posi_unkwn == 0) {
/*
* If it says we are at the begining of partition
* the block value better be 0.
*/
(value != 0)) {
"LONG_POS returned begin of partition but"
break;
}
/*
* Can't be at the start and the end of the partition
* at the same time if the partition is larger the 0.
*/
if (long_pos_info->begin_of_part &&
"LONG_POS returned begin and end of"
" partition\n");
break;
}
/*
* If the logical block number is not what we expected.
*/
if (flag)
flag++;
"LONG_POS current logical 0x%"PRIx64
}
/*
* If the begining of partition is true and the
* block number is zero we will beleive that it is
* rewound. Promote the pmode to legacy.
*/
(long_pos_info->block_number == 0)) {
/*
* otherwise if the pmode was invalid,
* promote it to logical.
*/
}
FIX_ENDIAN32(&part);
if (flag)
flag++;
"LONG_POS current partition %d"
}
} else {
/*
* If the drive doesn't know location,
* we don't either.
*/
"Tape drive reported block position as unknown\n");
}
/* Is file position valid */
if (long_pos_info->mrk_posi_unkwn == 0) {
/*
* If it says we are at the begining of partition
* the block value better be 0.
*/
(value != 0)) {
"LONG_POS returned begin of partition but"
break;
}
if (flag)
flag++;
"LONG_POS fileno 0x%"PRIx64
" not un_pos %x\n", value,
}
}
}
break;
}
case EXT_POS: /* Extended data format */
{
/* Make sure that there is enough data there */
if (data_sz < 16) {
break;
}
/* If reserved fields are non zero don't use the data */
"EXT_POS reserved fields not zero\n");
break;
}
/*
* In the unlikely event of overflowing 64 bits of position.
*/
if (ext_pos_info->posi_err != 0) {
break;
}
FIX_ENDIAN16(&len);
if (len != 0x1c) {
"EXT_POS parameter_len should be 0x1c was 0x%x\n",
len);
break;
}
/* Is block position information valid */
if (ext_pos_info->blk_posi_unkwn == 0) {
(value != 0)) {
"EXT_POS returned begining of partition but"
break;
}
if (flag)
flag++;
"EXT_POS current logical 0x%"PRIx64
}
/*
* If the begining of partition is true and the
* block number is zero we will beleive that it is
* rewound. Promote the pmode to legacy.
*/
(ext_pos_info->host_block == 0)) {
}
/*
* otherwise if the pmode was invalid,
* promote it to logical.
*/
}
if (flag)
flag++;
"EXT_POS current partition %d read %d\n",
}
} else {
}
break;
}
default:
"Got unexpected SCMD_READ_POSITION type %d\n", type);
}
"position read in", &org);
"position read out", dest);
}
return (rval);
}
static int
{
int rval;
char cdb[CDB_GROUP4];
struct scsi_extended_sense sense;
/*
* Not sure what to do when doing recovery and not wanting
* to update un_pos
*/
cdb[0] = SCMD_LOCATE;
cdb[2] = 0;
cdb[7] = 0;
cdb[9] = 0;
} else {
/*
* If the drive doesn't give a 64 bit read position data
* it is unlikely it will accept 64 bit locates.
*/
return (ERANGE);
}
cdb[0] = (char)SCMD_LOCATE_G4;
cdb[2] = 0;
cdb[12] = 0;
cdb[13] = 0;
cdb[14] = 0;
cdb[15] = 0;
}
/*
* XXX This is a work around till we handle Descriptor format
* sense data. Since we are sending a command where the standard
* sense data can not correctly represent a correct residual in
* 4 bytes.
*/
"Big LOCATE ILLEGAL_REQUEST: rval = %d\n", rval);
/* Doesn't like big locate command */
/* Aborted big locate command */
"Big LOCATE resulted in invalid pos: rval = %d\n",
rval);
/* read position failed */
"Big LOCATE and read pos: rval = %d\n", rval);
/* read position worked but position was not expected */
"Big LOCATE and recover read less then desired 0x%"
/* read position was what was expected */
"Big LOCATE and recover seems to have worked\n");
un->un_err_resid = 0;
rval = 0;
} else {
"BIGLOCATE end up going backwards");
}
} else if (rval == 0) {
/* Worked as requested */
(cmd->uscsi_resid != 0)) {
/* Got part way there but wasn't enough blocks on tape */
/* Got part way there but drive didn't tell what we missed by */
} else {
"Failed LOCATE and recover pos: rval = %d status = %d\n",
}
return (rval);
}
static int
{
int rval;
#if 0
return (0);
}
#endif
/* pmode == invalid already handled */
/*
* forward space over filemark
*
* For ASF we allow a count of 0 on fsf which means
* we just want to go to beginning of current file.
* Equivalent to "nbsf(0)" or "bsf(1) + fsf".
* Allow stepping over double fmk with reel
*/
(files > 0) &&
/* we're at EOM */
"st_mtfsf_ioctl: EIO : MTFSF at EOM");
return (EIO);
}
/*
* physical tape position may not be what we've been
* telling the user; adjust the request accordingly
*/
/*
* For positive direction case, we're now covered.
* For zero or negative direction, we're covered
* (almost)
*/
files--;
}
}
return (EIO);
}
/*
* Forward space file marks.
* We leave ourselves at block zero
* of the target file number.
*/
if (files < 0) {
} else {
}
return (rval);
}
static int
{
int rval;
/*
* A space with a count of zero means take me to the start of file.
*/
if (count == 0) {
/* Hay look were already there */
un->un_err_resid = 0;
return (0);
}
/*
* Well we are in the first file.
* A rewind will get to the start.
*/
/*
* Can we backspace to get there?
* This should work in logical mode.
*/
/*
* Can't back space but current file number is known,
* So rewind and space from the begining of the partition.
*/
/*
* pmode is logical and ST_BSF is not set.
* The LONG_POS read position contains the fileno.
* If the read position works, rewind and space.
*/
if (rval) {
/*
* We didn't get the file position from the
* read position command.
* We are going to trust the drive to backspace
* and then position after the filemark.
*/
}
} else {
}
} else {
}
/*
* If something didn't work we are lost
*/
if (rval != 0) {
"st_mtioctop : EIO : fspace pmode invalid");
}
} else {
}
/*
* we came here with a count < 0; we now need
* to skip back to end up before the filemark
*/
}
return (rval);
}
static int
{
int rval;
}
return (rval);
}
static int
{
int rval;
/*
* Back space of the file at the begining of the file.
*/
if (rval) {
return (rval);
}
/*
* Other interesting answers might be crashed BOT which isn't bad.
*/
return (rval);
}
/*
* Now we are on the BOP side of the filemark. Forward space to
* the EOM side and we are at the begining of the file.
*/
if (rval) {
}
return (rval);
}
static int
{
/*
* forward space to inter-record gap
*
*/
/*
* If were are at end of tape and count is forward.
* Return blank check.
*/
/* we're at EOM */
"st_mtfsr_ioctl: EIO : MTFSR eof > ST_EOT");
return (EIO);
}
/*
* If count is zero there is nothing to do.
*/
if (count == 0) {
un->un_err_resid = 0;
}
return (0);
}
/*
* physical tape position may not be what we've been
* telling the user; adjust the position accordingly
*/
== -1) {
"st_mtfsr_ioctl:EIO:MTFSR count && IN_EOF");
return (EIO);
}
}
}
"st_mtfsr_ioctl: EIO : MTFSR st_check_den");
return (EIO);
}
}
static int
{
int rval = 0;
if (rval != 0) {
}
return (rval);
}
/* Already there */
un->un_err_resid = 0;
return (0);
}
/*
* If the destination block is forward
* or the drive will backspace records.
*/
/*
* If we're spacing forward, or the device can
* backspace records, we can just use the SPACE
* command.
*/
"st_space_records:EIO:space_records can't spc");
/*
*/
} else if (dblk < 0 &&
/*
* we skipped over a filemark
* and need to go forward again
*/
SCSI_DEBUG, "st_space_records: EIO"
" : can't space #2");
}
}
if (rval == 0) {
"st_space_records: EIO : space_rec rval"
" == 0");
}
}
} else {
/*
* else we rewind, space forward across filemarks to
* the desired file, and then space records to the
* desired block.
*/
if (dblk < 0) {
/*
* Wups - we're backing up over a filemark
*/
}
}
"st_space_records:EIO:space_records : dblk < 0");
"st_space_records: EIO :space_records : rewind "
"and space failed");
}
}
return (rval);
}
static int
{
/*
* backward space of file filemark (1/2" and 8mm)
* tape position will end on the beginning of tape side
* of the desired file mark
*/
return (ENOTTY);
}
/*
* If a negative count (which implies a forward space op)
* is specified, and we're at logical or physical eot,
* bounce the request.
*/
"st_ioctl_mt_bsf : EIO : MTBSF : eof > ST_EOF");
return (EIO);
}
/*
* physical tape position may not be what we've been
* telling the user; adjust the request accordingly
*/
files++;
}
}
"st_ioctl : EIO : MTBSF : check den wfm");
return (EIO);
}
if (files <= 0) {
/*
* for a negative count, we need to step forward
* first and then step back again
*/
}
}
static int
{
int rval = 0;
/*
* Backspace files (MTNBSF): infront == 0
*
* For tapes that can backspace, backspace
* count+1 filemarks and then run forward over
* a filemark
*
* For tapes that can't backspace,
* calculate desired filenumber
* (un->un_pos.fileno - count), rewind,
* and then space forward this amount
*
* Backspace filemarks (MTBSF) infront == 1
*
* For tapes that can backspace, backspace count
* filemarks
*
* For tapes that can't backspace, calculate
* desired filenumber (un->un_pos.fileno - count),
* add 1, rewind, space forward this amount,
* and mark state as ST_EOF_PENDING appropriately.
*/
"st_backward_space_files: mt_op=%x count=%"PRIx64
/* In case a drive that won't back space gets in logical mode */
return (rval);
}
if ((infront == 1) &&
return (rval);
} else if ((infront == 0) &&
return (rval);
}
return (rval);
}
"st_backward_space_files: mt_op=%x count=%"PRIx64
"fileno=%x blkno=%x\n",
/*
* Handle the simple case of BOT
* playing a role in these cmds.
* We do this by calculating the
* ending file number. If the ending
* file is < BOT, rewind and set an
* error and mark resid appropriately.
* If we're backspacing a file (not a
* filemark) and the target file is
* the first file on the tape, just
* rewind.
*/
/* figure expected destination of this SPACE command */
/*
* Would the end effect of this SPACE be the same as rewinding?
* If so just rewind instead.
*/
if ((infront != 0) && (end_fileno < 0) ||
(infront == 0) && (end_fileno <= 0)) {
"st_backward_space_files: EIO : "
"rewind in lou of BSF failed\n");
}
if (end_fileno < 0) {
"st_backward_space_files: EIO : "
"back space file greater then fileno\n");
}
return (rval);
}
/*
* If we are going to end up at the beginning
* of the file, we have to space one extra file
* first, and then space forward later.
*/
"st_backward_space_files:EIO:back space fm failed");
}
} else {
} else {
}
}
/*
* If we have to space forward, do so...
*/
"st_backward_space_files:EIO:space fm skip count");
} else if (infront) {
/*
* If we had to space forward, and we're
* not a tape that can backspace, mark state
* as if we'd just seen a filemark during a
* a read.
*/
}
}
}
if (rval != 0) {
}
return (rval);
}
static int
{
int rval;
/*
* backward space file to beginning of file
*
* If a negative count (which implies a forward space op)
* is specified, and we're at logical or physical eot,
* bounce the request.
*/
"st_ioctl : EIO : > EOT and count < 0");
return (EIO);
}
/*
* physical tape position may not be what we've been
* telling the user; adjust the request accordingly
*/
count++;
}
}
"st_ioctl : EIO : MTNBSF check den and wfm");
return (EIO);
}
if (count <= 0) {
} else {
}
return (rval);
}
static int
{
/*
* backward space into inter-record gap
*
* If a negative count (which implies a forward space op)
* is specified, and we're at logical or physical eot,
* bounce the request.
*/
"st_ioctl : EIO : MTBSR > EOT");
return (EIO);
}
if (num == 0) {
un->un_err_resid = 0;
}
return (0);
}
/*
* physical tape position may not be what we've been
* telling the user; adjust the position accordingly.
* bsr can not skip filemarks and continue to skip records
* therefore if we are logically before the filemark but
* physically at the EOT side of the filemark, we need to step
* back; this allows fsr N where N > number of blocks in file
* followed by bsr 1 to position at the beginning of last block
*/
"st_mtbsr_ioctl: EIO : MTBSR can't space");
return (EIO);
}
}
}
"st_ioctl : EIO : MTBSR : can't set density or wfm");
return (EIO);
}
}
static int
{
int rval;
if (rval == 0) {
/*
* Drive says invalid field in cdb.
* Doesn't like space multiple. Position isn't lost.
*/
} else {
}
return (rval);
}
static int
{
int rval;
if (rval == 0) {
/*
* Drive says invalid field in cdb.
* Doesn't like space multiple. Position isn't lost.
*/
} else {
}
return (rval);
}
#ifdef __x86
/*
* release contig_mem and wake up waiting thread, if any
*/
static void
{
}
/*
* St_get_contig_mem will return a contig_mem if there is one available
* in current system. Otherwise, it will try to alloc one, if the total
* number of contig_mem is within st_max_contig_mem_num.
* It will sleep, if allowed by caller or return NULL, if no contig_mem
* is available for now.
*/
static struct contig_mem *
{
int big_enough = 0;
/* Try to get one available contig_mem */
if (un->un_contig_mem_available_num > 0) {
/*
* we failed to get one. we're going to
* alloc one more contig_mem for this I/O
*/
sizeof (struct contig_mem) + biosize(),
"alloc contig_mem failure\n");
return (NULL); /* cannot get one */
}
} else {
/*
* we failed to get one and we're NOT allowed to
* alloc more contig_mem
*/
if (alloc_flags == KM_SLEEP) {
while (un->un_contig_mem_available_num <= 0) {
}
} else {
"alloc contig_mem failure\n");
return (NULL); /* cannot get one */
}
}
/* We need to check if this block of mem is big enough for this I/O */
/* not big enough, need to alloc a new one */
"alloc contig_mem failure: not enough mem\n");
} else {
/* release previous one before attach new one */
}
/* attach new mem to this cp */
goto alloc_ok; /* get one usable cp */
}
} else {
goto alloc_ok; /* get one usable cp */
}
(alloc_flags != KM_SLEEP)) {
return (NULL);
}
/*
* we're allowed to sleep, and there is one big enough
* contig mem in the system, which is currently in use,
* wait for it...
*/
big_enough = 1;
do {
/* we get the big enough contig mem, finally */
/* init bp attached to this cp */
return (cp);
}
/*
* this is the biodone func for the bp used in big block I/O
*/
static int
{
struct contig_mem *cp;
int ioerr;
/* sanity check */
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/* special handling for special I/O */
if (cp->cm_use_sbuf) {
#ifndef __lock_lint
#endif
cp->cm_use_sbuf = 0;
}
if (ioerr != 0) {
/* copy data back to original bp */
}
return (DDI_SUCCESS);
}
/*
* We use this func to replace original bp that may not be able to do I/O
* in big block size with one that can
*/
static struct buf *
{
struct contig_mem *cp;
return (bp);
}
/* try to get one contig_mem */
if (!cp) {
"Cannot alloc contig buf for I/O for %lu blk size",
return (bp);
}
/* make sure that we "are" using un_sbufp for special I/O */
#ifndef __lock_lint
#endif
}
/* clone bp */
/* get data in original bp */
}
return (cont_bp);
}
#else
#ifdef __lock_lint
static int
{
return (0);
}
#endif
#endif
static const char *eof_status[] =
{
"NO_EOF",
"EOF_PENDING",
"EOF",
"EOT_PENDING",
"EOT",
"EOM",
"AFTER_EOM"
};
static const char *mode[] = {
"invalid",
"legacy",
"logical"
};
static void
{
"%s Position data:\n", comment);
}
static int
{
int result = 0;
int i;
/*
* find non alpha numeric working from the end.
*/
for (i = size - 1; i >= 0; i--) {
data[i] = 0;
size = i;
}
}
if (size == 1) {
/*
* Drive seems to think its returning useful data
* but it looks like all junk
*/
return (result);
}
size++;
/*
* Actually got a valid serial number.
* If never stored one before alloc space for it.
*/
if (un->un_media_id_len == 0) {
}
"Longer Media Id old ID:%s new ID:%s\n",
"Old Media Id %s length = %d New %s length = %d\n",
} else {
}
return (result);
}
#define ID_SIZE 32
typedef struct
{
#ifdef _BIT_FIELDS_LTOH
: 5,
read_only : 1;
#else
: 5,
format : 2;
#endif
typedef struct {
char data[1];
static int
{
int result;
int i;
char *format;
for (i = 0; i < size; i++) {
if (byte < 0x10)
format = "0%x";
else
format = "%x";
}
return (result);
}
static int
{
int result;
int size;
int newsize;
if (result == 0) {
"resizing read attribute data from %d to %d format"
goto again;
}
result =
} else {
newsize);
}
"Read Attribute Command for Media Identification is not "
"supported on the HBA that this drive is attached to.");
}
return (result);
}
static int
{
char cdb[CDB_GROUP5];
struct scsi_extended_sense sense;
int rval;
return (ENOTTY);
}
cdb[0] = (char)SCMD_SVC_ACTION_IN_G5;
cdb[2] = 0;
cdb[3] = 0;
cdb[4] = 0;
cdb[5] = 0;
cdb[10] = 0;
cdb[11] = 0;
"media serial command returned %d scsi_status %d"
/*
* If this returns invalid operation code don't try again.
*/
} else if (rval == 0) {
}
} else {
int act_size;
/*
* get reported size.
*/
/* documentation says mod 4. */
while (act_size & 3) {
act_size++;
}
/*
* If reported size is larger that we our buffer.
* Free the old one and allocate one that is larger
* enough and re-issuse the command.
*/
goto upsize;
}
if (act_size == 0) {
"media serial number is not available");
rval = 0;
} else {
/*
* set data pointer to point to the start
* of that serial number.
*/
rval =
}
}
return (rval);
}
/* ARGSUSED */
static int
{
un->un_media_id_len = 0;
return (0);
}
};
static int
{
int result = 0;
int i;
for (i = 0; i < ST_NUM_MEMBERS(media_chk_functions); i++) {
/*
* Last operation type not supported by this device.
* Make so next time it doesn`t do that again.
*/
continue;
}
/*
* If result indicates the function was successful or
* that the media is not the same as last known, break.
*/
break;
}
}
return (result);
}
static errstate
{
int ret;
/*
* Don't try and recover a reset that this device sent.
*/
onentry == DEVICE_RESET) {
return (COMMAND_DONE_ERROR);
}
/*
* See if expected position was passed with scsi_pkt.
*/
/*
* Not for this command.
*/
return (COMMAND_DONE_ERROR);
}
/*
* Create structure to hold all error state info.
*/
} else {
/* disabled */
return (COMMAND_DONE_ERROR);
}
if (ret != DDI_SUCCESS) {
return (COMMAND_DONE_ERROR);
}
return (JUST_RETURN); /* release calling thread */
}
static void
{
int error_number;
#if !defined(lint)
#endif
switch (err) {
case JUST_RETURN:
return;
case COMMAND_DONE:
error_number = 0;
break;
default:
"st_recov_ret with unhandled errstat %d\n", err);
/* FALLTHROUGH */
case COMMAND_DONE_ERROR:
/* FALLTHROUGH */
case COMMAND_DONE_EACCES:
error_number = EIO;
break;
}
}
static void
st_recover(void *arg)
{
int rval;
sizeof (struct scsi_arq_status));
"starting position for recovery command" :
"expected position for recovery command",
"st_recover called with %s, TUR returned %d\n",
/*
* If the drive responed to the TUR lets try and get it to sync
* any data it might have in the buffer.
*/
if (rval) {
"st_recover failed to flush, returned %d\n", rval);
return;
}
}
switch (errinfo->ei_error_type) {
case ATTEMPT_RETRY:
case COMMAND_TIMEOUT:
case DEVICE_RESET:
case PATH_FAILED:
/*
* For now if we can't talk to the device we are done.
* If the drive is reserved we can try to get it back.
*/
return;
}
/*
* If reservation conflict and do a preempt, fail it.
*/
if ((un->un_rsvd_status &
(ST_APPLICATION_RESERVATIONS | ST_RESERVE)) != 0) {
return;
}
}
/*
* If we have already set a scsi II reserve and get a
* conflict on a scsi III type reserve fail without
* any attempt to recover.
*/
return;
}
/*
* If scsi II lost reserve try and get it back.
*/
if ((((un->un_rsvd_status &
ST_LOST_RESERVE)) &&
if (rval != 0) {
return;
}
}
}
if (rval) {
return;
}
break;
case DEVICE_TAMPER:
/*
*/
0x2a) &&
0x01)) {
/*
* See if mode sense changed.
*/
if (rval) {
return;
}
}
/*
* if we have a media id and its not bogus.
* Check to see if it the same.
*/
return;
}
}
break;
default:
"Unhandled error type %s in st_recover() 0x%x\n",
}
/*
* if command is retriable retry it.
* Special case here. The command attribute for SCMD_REQUEST_SENSE
* does not say that it is retriable. That because if you reissue a
* request sense and the target responds the sense data will have
* been consumed and no long be valid. If we get a busy status on
* request sense while the state is ST_STATE_SENSING this will
* reissue that pkt.
*
* XXX If this request sense gets sent to a different port then
* the original command that failed was sent on it will not get
* valid sense data for that command.
*/
/*
* if drive doesn't support read position we are done
*/
/*
* If this command results in a changed tape position,
* lets see where we are.
*/
/*
* XXX May be a reason to choose a different type here.
* Long format has file position information.
* Short and Extended have information about whats
* in the buffer. St's positioning assumes in the buffer
* to be the same as on tape.
*/
if (rval == 0) {
&errinfo->ei_failed_pkt);
} else {
}
} else {
ASSERT(0);
}
}
static void
{
/*
* Get the buf from the packet.
*/
/*
* get the unit from the buf.
*/
switch (pkt->pkt_reason) {
case CMD_CMPLT:
} else {
}
break;
case CMD_TIMEOUT:
break;
case CMD_TRAN_ERR:
break;
case CMD_DEV_GONE:
if (un->un_multipath)
else
break;
default:
"pkt_reason not handled yet %s",
}
/*
* check for undetected path failover.
*/
if (un->un_multipath) {
if (scsi_pkt_allocated_correctly(pkt) &&
"Failover detected in recovery, action is "
}
}
}
"Recovery call back got %s status on %s\n",
switch (action) {
case COMMAND_DONE:
break;
case COMMAND_DONE_EACCES:
break;
case COMMAND_DONE_ERROR_RECOVERED: /* XXX maybe wrong */
ASSERT(0);
break;
case COMMAND_TIMEOUT:
case COMMAND_DONE_ERROR:
break;
case DEVICE_RESET:
case QUE_BUSY_COMMAND:
case PATH_FAILED:
/* longish timeout */
/* FALLTHRU */
case QUE_COMMAND:
case DEVICE_TAMPER:
case ATTEMPT_RETRY:
/*
* let st_handle_intr_busy put this bp back on waitq and make
* checks to see if it is ok to requeue the command.
*/
/*
* Save the throttle before setting up the timeout
*/
if (un->un_throttle) {
}
return; /* timeout is setup again */
}
break;
default:
"Unhandled recovery state 0x%x\n", action);
break;
}
}
static int
{
int err;
#ifdef STDEBUG
if ((st_debug & 0x7)) {
}
#endif
while (un->un_recov_buf_busy)
un->un_recov_buf_busy = 0;
return (err);
}
/* args used */
static int
{
int rval;
/*
* Get buffer resources...
*/
while (un->un_recov_buf_busy)
/*
* Free resources
*/
un->un_recov_buf_busy = 0;
return (rval);
}
/*
* Add data to scsi_pkt to help know what to do if the command fails.
*/
static void
{
return;
}
/*
* lookup the command attributes and add them to the recovery info.
*/
/*
* For commands that there is no way to figure the expected position
* once completed, we save the position the command was started from
* so that if they fail we can position back and try again.
* This has already been done in st_cmd() or st_iscsi_cmd().
*/
/* save current position as the starting position. */
return;
}
/*
* Don't want to update the running position for recovery.
*/
return;
}
/*
* If running position is invalid copy the current position.
* Running being set invalid means we are not in a read, write
* or write filemark sequence.
* We'll copy the current position and start from there.
*/
} else {
/*
* Always should be more logical blocks then
* data blocks and files marks.
*/
}
}
/*
* If the command is not expected to change the drive position
* then the running position should be the expected position.
*/
return;
}
/*
* This is a user generated CDB.
*/
/*
* See if this CDB will generate a locate or change
* partition.
*/
}
} else {
}
return;
}
/* should not have got an invalid position from running. */
}
/* should have either a get count or or get lba function */
/* only explicit commands have both and they're handled above */
/* if it has a get count function */
if (count == 0) {
return;
}
/*
* Changes position but doesn't transfer data.
* i.e. rewind, write_file_mark and load.
*/
case DIR_NONE: /* Erase */
break;
case DIR_FORW: /* write_file_mark */
break;
case DIR_REVC: /* rewind */
break;
case DIR_EITH: /* Load unload */
case LD_UNLOAD:
case LD_RETEN:
case LD_HOLD:
break;
case LD_EOT:
break;
case LD_LOAD:
break;
default:
ASSERT(0);
}
break;
default:
ASSERT(0);
break;
}
} else {
/*
* Changes position and does transfer data.
* i.e. read or write.
*/
case DIR_FORW:
break;
case DIR_REVC:
break;
default:
ASSERT(0);
break;
}
}
/* Have a get LBA fuction. i.e. Locate */
DIR_EITH);
} else {
ASSERT(0);
}
return;
}
}
static int
{
int rval;
/*
* check to see if mode data has changed.
*/
if (rval) {
sizeof (struct seq_mode));
}
}
return (rval);
}
static int
{
int rval;
int i;
/* recovery called with mode tamper before mode selection */
"Mode Select not done yet");
return (0);
}
sizeof (struct seq_mode));
if (rval != 0) {
"Mode Sense for mode verification failed");
return (rval);
}
if (rval == 0) {
"Found no changes in mode data");
}
#ifdef STDEBUG
else {
for (i = 1; i < sizeof (struct seq_mode); i++) {
"sense data changed at byte %d was "
"0x%x now 0x%x", i,
}
}
}
#endif
return (rval);
}
static int
{
int rval = 0;
int limit = st_retry_count;
/*
* XXX Newer drives may not RESEVATION CONFLICT a TUR.
*/
do {
if (rval != 0) {
}
"ping TUR returned 0x%x", rval);
limit--;
rval = 0;
return (rval);
}
/*
* Does read position using recov_buf and doesn't update un_pos.
* Does what ever kind of read position you want.
*/
static int
{
int rval;
struct scsi_arq_status status;
char cdb[CDB_GROUP1];
cdb[0] = SCMD_READ_POSITION;
cdb[2] = 0;
cdb[3] = 0;
cdb[4] = 0;
cdb[5] = 0;
cdb[6] = 0;
cdb[7] = 0;
cdb[9] = 0;
switch (type) {
case SHORT_POS:
break;
case LONG_POS:
break;
case EXT_POS:
break;
default:
ASSERT(0);
}
if (cmd.uscsi_status) {
}
return (rval);
}
static int
{
int rval;
do {
if (rval != 0) {
switch (type) {
case SHORT_POS:
break;
case LONG_POS:
break;
case EXT_POS:
break;
default:
break;
}
} else {
}
break;
}
if (rval == 0) {
}
return (rval);
}
/*
* based on the command do we retry, continue or give up?
* possable return values?
* zero do nothing looks fine.
* EAGAIN retry.
* EIO failed makes no sense.
*/
static int
{
int rval;
if (rval != 0) {
return (EIO);
}
return (EIO);
}
/*
* Command that changes tape position and have an expected position
* if it were to chave completed sucessfully.
*/
uchar_t reposition = 0;
"Got count from CDB and it was %d\n", count);
/*
* At expected?
*/
"Found drive to be at expected position\n");
/*
* If the command should move tape and it got a busy
* it shouldn't be in the expected position.
*/
reposition = 1;
/*
* If the command doesn't transfer data should be good.
*/
return (0); /* Good */
/*
* Command transfers data, should have done so.
*/
return (0); /* Good */
} else {
reposition = 1;
}
}
"difference between expected and actual is %"
"Found failed FORW command, retrying\n");
return (EAGAIN);
}
/*
* If rewound or somewhere between the starting position
* and the expected position (partial read or write).
* Locate to the starting position and try the whole
* thing over again.
*/
if (rval == 0) {
CE_NOTE, "reestablished FORW"
" command retrying\n");
return (EAGAIN);
}
/*
* This handles flushed read ahead on the drive or
* an aborted read that presents as a busy and advanced
* the tape position.
*/
if (rval == 0) {
CE_NOTE, "reestablished FORW"
" read command retrying\n");
return (EAGAIN);
}
/*
* XXX swag seeing difference of 2 on write filemark.
* If the space to the starting position works on a
* write that means the previous write made it to tape.
* If not we lost data and have to give up.
*
* The plot thickens. Now I am attempting to cover a
* count of 1 and a differance of 2 on a write.
*/
if (rval == 0) {
CE_NOTE, "reestablished FORW"
" write command retrying\n");
return (EAGAIN);
}
rval);
} else {
"Not expected transfers_data = %d "
"difference = %"PRId64,
}
return (EIO);
/* Don't think we can write backwards */
"difference between expected and actual is %"
"Found failed REVC command, retrying\n");
return (EAGAIN);
}
if (rval == 0) {
CE_NOTE, "reestablished REVC"
" command retrying\n");
return (EAGAIN);
}
/* This handles read ahead in reverse direction */
if (rval == 0) {
CE_NOTE, "reestablished REVC"
" read command retrying\n");
return (EAGAIN);
}
} else {
"Not expected transfers_data = %d "
"difference = %"PRId64,
}
return (EIO);
} else {
/*
* Commands that change tape position either
* direction or don't change position should not
* get here.
*/
ASSERT(0);
}
"Didn't find a recoverable position, Failing\n");
/*
* Command that changes tape position and can only be recovered
* by going back to the point of origin and retrying.
*
* Example SCMD_SPACE.
*/
/*
* This type of command stores the starting position.
* If the read position is the starting position,
* reissue the command.
*/
"Found Space command at starting position, "
"Reissuing\n");
return (EAGAIN);
}
/*
* Not in the position that the command was originally issued,
* Attempt to locate to that position.
*/
if (rval) {
"Found Space at an unexpected position and locate "
"back to starting position failed\n");
return (EIO);
}
"Found Space at an unexpected position and locate "
"back to starting position worked, Reissuing\n");
return (EAGAIN);
}
"Read position above did not make sense", read);
ASSERT(0);
return (EIO);
}
static errstate
{
cmd_attribute const *attrib;
int queued = 0;
int rval;
int flags = 0;
int stat_size =
} else {
}
/*
* Some non-uscsi commands use the b_bcount for values that
* have nothing to do with how much data is transfered.
* In those cases we need to hide the buf_t from scsi_init_pkt().
*/
} else {
}
/*
* if this is a queued command make sure it the only one in the
* run queue.
*/
queued = 1;
}
flags |= PKT_CONSISTENT;
stat_size = 1;
}
"Reissue pkt scsi_init_pkt() failure\n");
return (COMMAND_DONE_ERROR);
}
st_bioerror(bp, 0);
newpkt->pkt_statistics = 0;
/*
* oldpkt passed in was a copy of the original.
* to distroy we need the address of the original.
*/
}
if (rval == TRAN_ACCEPT) {
return (JUST_RETURN);
}
"Reissue pkt st_transport(0x%x) failure\n", rval);
return (COMMAND_DONE_ERROR);
}
if (rval) {
return (COMMAND_DONE_ERROR);
}
return (JUST_RETURN);
}
static int
{
int status;
return (status);
}
/*
* Removed the buf_t bp from the queue referenced to by head and tail.
* Returns the buf_t pointer if it is found in the queue.
* Returns NULL if it is not found.
*/
static buf_t *
{
/* found it, is it at the head? */
} else {
}
}
return (bp); /* found and removed */
}
}
return (NULL);
}
/*
* Adds a buf_t to the queue pointed to by head and tail.
* Adds it either to the head end or the tail end based on which
* the passed variable end (head or tail) points at.
*/
static void
{
if (*head) {
/* Queue is not empty */
/* Add at front of queue */
/* Add at end of queue */
} else {
ASSERT(0);
}
} else {
/* Queue is empty */
}
}
static uint64_t
{
/* fixed block mode, the count is the number of blocks */
count =
cdb[4];
} else {
/* variable block mode, the count is the block size */
count = 1;
}
return (count);
}
static uint64_t
{
count =
cdb[4];
/*
* If the sign bit of the 3 byte value is set, extended it.
*/
if (count & 0x800000) {
count |= 0xffffffffff000000;
}
return (count);
}
static uint64_t
{
count =
cdb[4];
return (count);
}
static uint64_t
{
/* fixed block mode */
count =
cdb[14];
} else {
/* variable block mode */
count = 1;
}
return (count);
}
static uint64_t
{
}
static uint64_t
{
}
static uint64_t
{
}
static uint64_t
{
lba =
cdb[6];
return (lba);
}
static uint64_t
{
cdb[14];
return (count);
}
static uint64_t
{
lba =
return (lba);
}
static const cmd_attribute cmd_attributes[] = {
{ SCMD_READ,
0, 0, 0, st_get_cdb_g0_rw_count },
{ SCMD_WRITE,
0, 0, 0, st_get_cdb_g0_rw_count },
0, 0, 0 },
{ SCMD_REWIND,
0, 0, 0, st_get_no_count },
0, 0, 0 },
0, 0, 0 },
{ SCMD_READ_G4,
0, 0, 0, st_get_cdb_g5_rw_cnt, st_get_cdb_g4g5_cnt },
0, 0, 0, st_get_cdb_g5_rw_cnt, st_get_cdb_g4g5_cnt },
0, 0, 0, st_get_cdb_g0_rw_count },
0, 0, 0, st_get_cdb_g5_rw_cnt, st_get_cdb_g4g5_cnt },
0, 0, 0, st_get_cdb_g0_count },
0, 0, 0, st_get_cdb_g5_count, st_get_cdb_g4g5_cnt },
{ SCMD_SPACE,
0, 0, 0, st_get_cdb_g0_sign_count },
0, 0, 0, st_get_cdb_g4g5_cnt },
{ SCMD_INQUIRY,
0, 0, 0 },
0, 0, 0, st_get_cdb_g0_rw_count },
0, 0, 0, st_get_cdb_g5_rw_cnt, st_get_cdb_g4g5_cnt },
0, 0, 0 },
0, 0, 0 },
{ SCMD_RESERVE,
0, 0, 0 },
{ SCMD_RELEASE,
0, 0, 0 },
{ SCMD_ERASE,
0, 0, 0, st_get_erase_options },
0, 0, 0 },
{ SCMD_LOAD,
0, 0, 0, st_get_load_options },
{ SCMD_GDIAG,
1, 0, 0 },
{ SCMD_SDIAG,
1, 0, 0 },
0, 4, 3 },
{ SCMD_LOCATE,
0, 0, 0, NULL, st_get_cdb_g1_lba },
0, 0, 0 },
1, 0, 0 },
1, 0, 0 },
0, 0, 0 },
0, 0, 0 },
0, 0, 0 },
{ SCMD_PRIN,
0, 0, 0 },
{ SCMD_PROUT,
0, 0, 0 },
0, 0, 0 },
0, 0, 0 },
0, 0, 0, NULL, st_get_cdb_g4g5_cnt },
0, 0, 0 },
0, 0, 0 },
0, 0, 0 },
0, 0, 0 },
{ 0xff, /* Default attribute for unsupported commands */
};
static const cmd_attribute *
st_lookup_cmd_attribute(unsigned char cmd)
{
int i;
cmd_attribute const *attribute;
for (i = 0; i < ST_NUM_MEMBERS(cmd_attributes); i++) {
attribute = &cmd_attributes[i];
return (attribute);
}
}
return (attribute);
}
static int
{
int rval;
do {
if (rval == 0) {
switch (reset_type) {
case RESET_LUN:
"LUN reset failed trying target reset");
break;
case RESET_TARGET:
"target reset failed trying bus reset");
break;
case RESET_BUS:
"bus reset failed trying all reset");
default:
return (rval);
}
}
} while (rval == 0);
return (rval);
}
#define SAS_TLR_MOD_LEN sizeof (struct seq_mode)
static int
{
int ret;
int amount = SAS_TLR_MOD_LEN;
if (ret != DDI_SUCCESS) {
goto out;
}
}
/* Must be SAS protocol */
goto out;
}
goto out;
} else {
goto out;
}
if (ret != DDI_SUCCESS) {
} else {
else
}
#ifdef STDEBUG
#endif
out:
return (ret);
}
static void
{
ST_RESERVE) {
"Lost Reservation notification");
} else {
"reset notification");
}
if ((un->un_restore_pos == 0) &&
}
}