sgen.c revision a204de77cd937c018f628c7dc0357c2cdc90a07e
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright Siemens 1999
* All rights reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* sgen - SCSI generic device driver
*
* The sgen driver provides user programs access to SCSI devices that
* are not supported by other drivers by providing the USCSI(7I) interface.
*/
/* The name of the driver, established from the module name in _init. */
static char *sgen_label = NULL;
#define DDI_NT_SGEN "ddi_generic:scsi"
static char *sgen_devtypes[] = {
"direct", /* 0x00 -- disks */
"sequential", /* 0x01 */
"printer", /* 0x02 */
"processor", /* 0x03 */
"worm", /* 0x04 */
"rodirect", /* 0x05 */
"scanner", /* 0x06 */
"optical", /* 0x07 */
"changer", /* 0x08 */
"comm", /* 0x09 */
"prepress1", /* 0x0a -- reserved for prepress (ASC IT8) */
"prepress2", /* 0x0b -- reserved for prepress (ASC IT8) */
"array_ctrl", /* 0x0c -- storage array */
"ses", /* 0x0d -- enclosure services */
"rbc", /* 0x0e -- simplified block */
"bridge", /* 0x10 -- reserved for bridging expanders */
"type_0x11", /* 0x11 */
"type_0x12", /* 0x12 */
"type_0x13", /* 0x13 */
"type_0x14", /* 0x14 */
"type_0x15", /* 0x15 */
"type_0x16", /* 0x16 */
"type_0x17", /* 0x17 */
"type_0x18", /* 0x18 */
"type_0x19", /* 0x19 */
"type_0x1a", /* 0x1a */
"type_0x1b", /* 0x1b */
"type_0x1c", /* 0x1c */
"type_0x1d", /* 0x1d */
"type_0x1e", /* 0x1e */
"type_unknown" /* 0x1f is "no device type" or "unknown" */
};
#define SGEN_NDEVTYPES ((sizeof (sgen_devtypes) / sizeof (char *)))
#define SGEN_INQSTRLEN 24
#define SGEN_VENDID_MAX 8
#define SGEN_PRODID_MAX 16
int _lun; \
DDI_PROP_DONTPASS, SCSI_ADDR_PROP_LUN, 0); \
if (_lun > 0) { \
_lun; \
} \
}
#define SGEN_DO_ERRSTATS(sg_state, x) \
if (sg_state->sgen_kstats) { \
struct sgen_errstats *sp; \
}
/*
* Standard entrypoints
*/
static int sgen_probe(dev_info_t *);
/*
* Configuration routines
*/
static int sgen_do_attach(dev_info_t *);
static int sgen_setup_sense(sgen_state_t *);
static void sgen_create_errstats(sgen_state_t *, int);
static int sgen_do_suspend(dev_info_t *);
static int sgen_do_detach(dev_info_t *);
static void sgen_setup_binddb(dev_info_t *);
static void sgen_cleanup_binddb();
/*
* Packet transport routines
*/
static int sgen_start(struct buf *);
static int sgen_hold_cmdbuf(sgen_state_t *);
static void sgen_rele_cmdbuf(sgen_state_t *);
static void sgen_restart(void *);
static void sgen_callback(struct scsi_pkt *);
static int sgen_handle_sense(sgen_state_t *);
static int sgen_initiate_sense(sgen_state_t *, int);
static int sgen_scsi_transport(struct scsi_pkt *);
/*
*/
static void sgen_log(sgen_state_t *, int, const char *, ...);
static int sgen_diag_ok(sgen_state_t *, int);
int sgen_diag = 0;
int sgen_sporadic_failures = 0;
int sgen_force_manual_sense = 0;
struct sgen_binddb sgen_binddb;
static struct cb_ops sgen_cb_ops = {
sgen_open, /* open */
sgen_close, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
nodev, /* read */
nodev, /* write */
sgen_ioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
ddi_prop_op, /* cb_prop_op */
0, /* streamtab */
};
static struct dev_ops sgen_dev_ops = {
DEVO_REV, /* devo_rev, */
0, /* refcnt */
sgen_getinfo, /* info */
nodev, /* identify */
sgen_probe, /* probe */
sgen_attach, /* attach */
sgen_detach, /* detach */
nodev, /* reset */
&sgen_cb_ops, /* driver operations */
(struct bus_ops *)0, /* bus operations */
NULL /* power */
};
static void *sgen_soft_state = NULL;
};
static struct modlinkage modlinkage = {
};
int
_init(void)
{
int err;
/* establish driver name from module name */
sizeof (sgen_state_t), SGEN_ESTIMATED_NUM_DEVS)) != 0) {
goto done;
}
goto done;
}
done:
return (err);
}
int
_fini(void)
{
int err;
goto done;
}
done:
return (err);
}
int
{
}
/*
* sgen_typename()
* return a device type's name by looking it up in the sgen_devtypes table.
*/
static char *
{
if (typeno >= SGEN_NDEVTYPES)
return ("type_unknown");
return (sgen_devtypes[typeno]);
}
/*
* sgen_typenum()
* return a device type's number by looking it up in the sgen_devtypes
* table.
*/
static int
{
int i;
for (i = 0; i < SGEN_NDEVTYPES; i++) {
return (0);
}
}
return (-1);
}
/*
* sgen_setup_binddb()
* initialize a data structure which stores all of the information about
* which devices and device types the driver should bind to.
*/
static void
{
/*
* for each device type specifier make a copy and put it into a
* node in the binddb.
*/
"unknown device type '%s', "
"device unit-address @%s",
continue;
}
KM_SLEEP);
"'%s' in device-type-config-list, "
"device unit-address @%s",
}
}
/*
* into the the binddb.
*/
"device unit-address @%s",
nelems--;
}
/*
* Grab vendor and product ID.
*/
"Invalid vendor ID '%s', "
"device unit-address @%s",
continue;
}
"Invalid product ID '%s', "
"device unit-address @%s",
continue;
}
KM_SLEEP);
"'%s' '%s' in device-type-config-list, "
"device unit-address @%s",
}
}
}
/*
* sgen_cleanup_binddb()
* deallocate data structures for binding database.
*/
static void
{
if (sgen_binddb.sdb_init == 0) {
return;
}
}
}
}
/*
* sgen_bind_byinq()
* lookup a device in the binding database by its inquiry data.
*/
static int
{
struct scsi_device *scsidevp;
/*
* inq_vid and inq_pid are laid out by the protocol in order in the
* inquiry structure, and are not delimited by \0.
*/
/*
* Allow the "*" wildcard to match all vendor IDs.
*/
continue;
}
}
/*
* Using strncasecmp() with the key length allows substring
* matching for product data.
*/
return (0);
}
}
return (-1);
}
/*
* sgen_bind_bytype()
* lookup a device type in the binding database; if found, return a
* format string corresponding to the string in the .conf file.
*/
static int
{
struct scsi_device *scsidevp;
return (0);
}
}
return (-1);
}
/*
* sgen_get_binding()
* Check to see if the device in question matches the criteria for
* sgen to bind.
*
* Either the .conf file must specify a device_type entry which
* matches the SCSI device type of this device, or the inquiry
* string provided by the device must match an inquiry string specified
* in the .conf file. Inquiry data is matched first.
*/
static int
{
int retval = 0;
if (sgen_binddb.sdb_init == 0)
/*
* Check device-type-config-list for a match by device type.
*/
if (sgen_bind_bytype(dip) == 0)
goto done;
/*
*/
if (sgen_bind_byinq(dip) == 0)
goto done;
retval = -1;
done:
return (retval);
}
/*
* sgen_attach()
* attach(9e) entrypoint.
*/
static int
{
int err;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
err = DDI_SUCCESS;
break;
case DDI_PM_RESUME:
default:
err = DDI_FAILURE;
break;
}
done:
return (err);
}
/*
* sgen_do_attach()
* handle the nitty details of attach.
*/
static int
{
int instance;
struct scsi_device *scsidevp;
struct scsi_inquiry *inq;
/*
* Probe the device in order to get its device type to name the minor
* node.
*/
return (DDI_FAILURE);
}
"sgen_do_attach: failed to allocate softstate, "
return (DDI_FAILURE);
}
/*
* Now that sg_state->sgen_scsidev is initialized, it's ok to
* call sgen_log with sg_state instead of NULL.
*/
/*
* If the user specified the sgen_diag property, override the global
* sgen_diag setting by setting sg_state's sgen_diag value. If the
* user gave a value out of range, default to '0'.
*/
"sgen-diag", -1);
}
"sgen_do_attach: sgen_soft_state=0x%p, instance=%d, "
"device unit-address @%s",
/*
* For simplicity, the minor number == the instance number
*/
"sgen_do_attach: minor node creation failed, "
return (DDI_FAILURE);
}
/*
* Allocate the command buffer, then create a condition variable for
* managing it; mark the command buffer as free.
*/
/*
* If the hba and the target both support wide xfers, enable them.
*/
int wide = 0;
wide = 1;
"sgen_attach: wide xfer %s, "
"device unit-address @%s",
}
}
/*
* This is a little debugging code-- since the codepath for auto-sense
* and 'manual' sense is split, toggling this variable will make
* sgen act as though the adapter in question can't do auto-sense.
*/
if (sgen_force_manual_sense) {
0, 1) == 1) {
sg_state->sgen_arq_enabled = 0;
} else {
}
} else {
/*
* Enable autorequest sense, if supported
*/
"auto-rqsense", 1) != 1) {
"auto-rqsense", 1, 1) == 1) {
"sgen_attach: auto-request-sense enabled, "
"device unit-address @%s",
} else {
sg_state->sgen_arq_enabled = 0;
"sgen_attach: auto-request-sense disabled, "
"device unit-address @%s",
}
} else {
"sgen_attach: auto-request-sense enabled, "
}
}
/*
* Allocate plumbing for manually fetching sense.
*/
if (sgen_setup_sense(sg_state) != 0) {
"sgen_do_attach: failed to setup request-sense, "
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* sgen_setup_sense()
* Allocate a request sense packet so that if sgen needs to fetch sense
* data for the user, it will have a pkt ready to send.
*/
static int
{
return (-1);
}
return (-1);
}
/*
* Make the results of running a SENSE available by filling out the
* sd_sense field of the scsi device (sgen_sense is just an alias).
*/
SCMD_REQUEST_SENSE, 0, MAX_SENSE_LENGTH, 0);
return (0);
}
/*
* sgen_create_errstats()
* create named kstats for tracking occurrence of errors.
*/
static void
{
char kstatname[KSTAT_STRLEN];
struct sgen_errstats *stp;
sizeof (struct sgen_errstats) / sizeof (kstat_named_t),
return;
}
/*
* sgen_detach()
* detach(9E) entrypoint
*/
static int
{
int instance;
"sgen_detach: failed, no softstate found (%d), "
"device unit-address @%s",
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_DETACH:
return (sgen_do_detach(dip));
case DDI_SUSPEND:
return (sgen_do_suspend(dip));
case DDI_PM_SUSPEND:
default:
return (DDI_FAILURE);
}
}
/*
* sgen_do_detach()
* detach the driver, tearing down resources.
*/
static int
{
int instance;
struct scsi_device *devp;
if (SGEN_IS_BUSY(sg_state)) {
"device is busy, device unit-address @%s",
return (DDI_FAILURE);
}
/*
* Final approach for detach. Free data allocated by scsi_probe()
* in attach.
*/
if (sg_state->sgen_restart_timeid)
sg_state->sgen_restart_timeid = 0;
/*
* Free auto-request plumbing.
*/
if (sg_state->sgen_kstats) {
}
/*
* Free command buffer and clean up
*/
return (DDI_SUCCESS);
}
/*
* sgen_do_suspend()
* suspend the driver. This sets the "suspend" bit for this target if it
* is currently open; once resumed, the suspend bit will cause
* reopen the device to acknowledge that they need to reexamine its
* state and do the right thing.
*/
static int
{
int instance;
if (sg_state->sgen_restart_timeid) {
}
sg_state->sgen_restart_timeid = 0;
if (SGEN_IS_OPEN(sg_state))
return (DDI_SUCCESS);
}
/*
* sgen_getinfo()
* getinfo(9e) entrypoint.
*/
/*ARGSUSED*/
static int
{
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
== NULL)
return (DDI_FAILURE);
error = DDI_SUCCESS;
break;
case DDI_INFO_DEVT2INSTANCE:
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
}
return (error);
}
/*
* sgen_probe()
* probe(9e) entrypoint. sgen *never* returns DDI_PROBE_PARTIAL, in
* order to avoid leaving around extra devinfos. If sgen's binding
* rules indicate that it should bind, it returns DDI_PROBE_SUCCESS.
*/
static int
{
struct scsi_device *scsidevp;
int instance;
int rval;
return (DDI_PROBE_DONTCARE);
return (DDI_PROBE_FAILURE);
if (sgen_binddb.sdb_init == 0) {
}
/*
* A small optimization: if it's impossible for sgen to bind to
* any devices, don't bother probing, just fail.
*/
return (DDI_PROBE_FAILURE);
}
if (sgen_get_binding(dip) == 0) {
}
} else {
}
return (rval);
}
/*
* sgen_open()
* open(9e) entrypoint. sgen enforces a strict exclusive open policy per
* target.
*/
/*ARGSUSED1*/
static int
{
int instance;
return (ENXIO);
instance);
/*
* Don't allow new opens of a suspended device until the last close has
* happened. This is rather simplistic, but keeps the implementation
* straightforward.
*/
if (SGEN_IS_SUSP(sg_state)) {
return (EIO);
}
/*
* Enforce exclusive access.
*/
if (SGEN_IS_EXCL(sg_state) ||
return (EBUSY);
}
return (0);
}
/*
* sgen_close()
* close(9e) entrypoint.
*/
/*ARGSUSED1*/
static int
{
int instance;
return (ENXIO);
instance);
return (0);
}
/*
* sgen_ioctl()
* sgen supports the USCSI(7I) ioctl interface.
*/
/*ARGSUSED4*/
static int
{
int retval = 0;
int instance;
return (ENXIO);
instance);
/*
* If the driver has been suspended since the last open, fail all
* subsequent IO's so that the userland consumer reinitializes state.
*/
if (SGEN_IS_SUSP(sg_state)) {
"driver instance %d was previously suspended", instance);
return (EIO);
}
switch (cmd) {
case SGEN_IOC_DIAG: {
if (arg > 3) {
arg = 0;
}
retval = 0;
break;
}
case SGEN_IOC_READY: {
} else {
retval = 0;
}
break;
}
case USCSICMD:
break;
default:
}
retval);
return (retval);
}
/*
* sgen_uscsi_cmd()
* Setup, configuration and teardown for a uscsi(7I) command
*/
/*ARGSUSED*/
static int
{
int instance;
int flags;
int err;
instance);
/*
* At this point, we start affecting state relevant to the target,
* so access needs to be serialized.
*/
if (sgen_hold_cmdbuf(sg_state) != 0) {
return (EINTR);
}
if (err != 0) {
"scsi_uscsi_alloc_and_copyin failed\n");
return (err);
}
/*
* Clear out undesirable command flags
*/
}
}
/*
* Stash the sense buffer into sgen_rqs_sen for convenience.
*/
} else {
uscmd->uscsi_status = 0;
}
}
}
/*
* After this point, we can't touch per-target state.
*/
return (err);
}
/*
* sgen_hold_cmdbuf()
* Acquire a lock on the command buffer for the given target. Returns
* non-zero if interrupted.
*/
static int
{
while (SGEN_IS_BUSY(sg_state)) {
&sg_state->sgen_mutex)) {
return (-1);
}
}
return (0);
}
/*
* sgen_rele_cmdbuf()
* release the command buffer for a particular target.
*/
static void
{
}
/*
* sgen_start()
* Transport a uscsi command; this is invoked by physio() or directly
* by sgen_uscsi_cmd().
*/
static int
{
int trans_err;
return (ENXIO);
}
/*
* Sanity checks - command should not be complete, no packet should
* be allocated, and there ought to be a uscsi cmd in b_private
*/
return (EFAULT);
}
/*
* Clear out the residual and error fields
*/
switch (trans_err) {
case TRAN_ACCEPT:
break;
case TRAN_BUSY:
"sgen_start: scsi_transport() returned TRAN_BUSY");
break;
default:
/*
* Indicate there has been an I/O transfer error.
* Be done with the command.
*/
"returned %d", trans_err);
return (EIO);
}
return (0);
}
/*
* sgen_scsi_transport()
* a simple scsi_transport() wrapper which can be configured to inject
* sporadic errors for testing.
*/
static int
{
int trans_err;
static int cnt = 0;
if (sgen_sporadic_failures == 0) {
return (scsi_transport(pkt));
}
"injecting sporadic BUSY");
"injecting sporadic BADPKT");
} else {
/*
* Most of the time we take the normal path
*/
}
return (trans_err);
}
/*
* sgen_make_uscsi_cmd()
* Initialize a SCSI packet usable for USCSI.
*/
static int
{
int stat_size = 1;
int flags = 0;
sizeof (struct scsi_arq_status) -
sizeof (struct scsi_extended_sense);
} else {
stat_size = sizeof (struct scsi_arq_status);
}
}
NULL, /* in_pkt - null so it'll be alloc'd */
stat_size, /* statuslen */
0, /* privatelen */
flags, /* flags */
SLEEP_FUNC, /* callback */
return (-1);
}
/*
* We *don't* call scsi_setup_cdb here, as is customary, since the
* user could specify a command from one group, but pass cdblen
* as something totally different. If cdblen is smaller than expected,
* this results in scsi_setup_cdb writing past the end of the cdb.
*/
}
if (ucmd->uscsi_timeout > 0)
else
/*
* Set packet options
*/
}
/* Transfer uscsi information to scsi_pkt */
return (0);
}
/*
* sgen_restart()
* sgen_restart() is called after a timeout, when a command has been
* postponed due to a TRAN_BUSY response from the HBA.
*/
static void
sgen_restart(void *arg)
{
/*
* If the packet is marked with the sensing flag, sgen is off running
* a request sense, and *that packet* is what needs to be restarted.
*/
"sgen_restart: restarting REQUEST SENSE");
}
}
}
/*
* sgen_callback()
* Command completion processing
*
* sgen's completion processing is very pessimistic-- it does not retry
* failed commands; instead, it allows the user application to make
* decisions about what has gone wrong.
*/
static void
{
int action;
/*
* bp should always be the command buffer regardless of whether
* this is a command completion or a request-sense completion.
* This is because there is no need to biodone() the sense buf
* when it completes-- we want to biodone() the actual command buffer!
*/
"in sgen_callback() (SENSE completion callback)");
} else {
"in sgen_callback() (command completion callback)");
}
/* Transfer scsi_pkt information to uscsi */
/*
* The command did not complete.
*/
"sgen_callback: command did not complete");
} else if (sg_state->sgen_arq_enabled &&
/*
* The auto-rqsense happened, and the packet has a filled-in
* scsi_arq_status structure, pointed to by pkt_scbp.
*/
"sgen_callback: received auto-requested sense");
/*
* sgen was running a REQUEST SENSE. Decode the sense data and
* decide what to do next.
*
* Clear FLAG_SENSING on the original packet for completeness.
*/
} else {
/*
* Command completed and we're not getting sense. Check for
* errors and decide what to do next.
*/
"sgen_callback: command appears complete");
}
switch (action) {
case FETCH_SENSE:
/*
* If there is sense to fetch, break out to prevent biodone'ing
* until the sense fetch is complete.
*/
pkt->pkt_path_instance : 0) == 0)
break;
/*FALLTHROUGH*/
case COMMAND_DONE_ERROR:
/*FALLTHROUGH*/
case COMMAND_DONE:
break;
default:
ASSERT(0);
break;
}
}
/*
* sgen_initiate_sense()
* Send the sgen_rqspkt to the target, thereby requesting sense data.
*/
static int
{
/* use same path_instance as command */
case TRAN_ACCEPT:
"sense fetch transport accepted.");
return (0);
case TRAN_BUSY:
"sense fetch transport busy, setting timeout.");
return (0);
default:
"sense fetch transport failed or busy.");
return (-1);
}
}
/*
* sgen_handle_incomplete()
* sgen is pessimistic, but also careful-- it doesn't try to retry
* incomplete commands, but it also doesn't go resetting devices;
* it is hard to tell if the device will be tolerant of that sort
* of prodding.
*
* This routine has been left as a guide for the future--- the
* current administration's hands-off policy may need modification.
*/
/*ARGSUSED*/
static int
{
return (COMMAND_DONE_ERROR);
}
/*
* sgen_handle_autosense()
* Deal with SENSE data acquired automatically via the auto-request-sense
* facility.
*
* Sgen takes a pessimistic view of things-- it doesn't retry commands,
* and unless the device recovered from the problem, this routine returns
* COMMAND_DONE_ERROR.
*/
static int
{
struct scsi_arq_status *arqstat;
int amt;
"failed to complete.");
return (COMMAND_DONE_ERROR);
}
} else {
} else {
}
}
"uscsi_rqstatus=0x%x uscsi_rqresid=%d\n",
}
"check condition on auto request sense!");
return (COMMAND_DONE_ERROR);
}
(amt == 0)) {
"auto-sense, but it contains no data!");
return (COMMAND_DONE_ERROR);
}
/*
* Stuff the sense data pointer into sgen_sense for later retrieval
*/
/*
* Now, check to see whether we got enough sense data to make any
* sense out if it (heh-heh).
*/
if (amt < SUN_MIN_SENSE_LENGTH) {
"enough auto sense data");
return (COMMAND_DONE_ERROR);
}
case KEY_RECOVERABLE_ERROR:
break;
case KEY_NO_SENSE:
break;
default:
break;
}
return (COMMAND_DONE);
}
/*
* sgen_handle_sense()
* Examine sense data that was manually fetched from the target.
*/
static int
{
int amt;
"uscsi_rqstatus=0x%x uscsi_rqresid=%d\n",
}
"on request sense");
return (COMMAND_DONE_ERROR);
}
"condition on request sense!");
return (COMMAND_DONE_ERROR);
}
"sense, but it contains no data");
return (COMMAND_DONE_ERROR);
}
/*
* Now, check to see whether we got enough sense data to make any
* sense out if it (heh-heh).
*/
if (amt < SUN_MIN_SENSE_LENGTH) {
"enough sense data");
return (COMMAND_DONE_ERROR);
}
/*
* Decode the sense data-- this was deposited here for us by the
* setup in sgen_do_attach(). (note that sgen_sense is an alias for
* the sd_sense field in the scsi_device).
*/
case KEY_RECOVERABLE_ERROR:
break;
case KEY_NO_SENSE:
break;
default:
break;
}
return (COMMAND_DONE);
}
/*
* sgen_check_error()
* examine the command packet for abnormal completion.
*
* sgen_check_error should only be called at the completion of the
* command packet.
*/
static int
{
"sgen_check_error: target is busy");
return (COMMAND_DONE_ERROR);
}
/*
* pkt_resid will reflect, at this point, a residual of how many bytes
* were not transferred; a non-zero pkt_resid is an error.
*/
}
if (sg_state->sgen_arq_enabled) {
"sgen_check_error: strange: target "
"indicates CHECK CONDITION with auto-sense "
"enabled.");
}
"target ready for sense fetch");
return (FETCH_SENSE);
} else {
"target indicates CHECK CONDITION");
}
}
return (COMMAND_DONE);
}
/*
* sgen_tur()
* test if a target is ready to operate by sending it a TUR command.
*/
static int
{
char cmdblk[CDB_GROUP0];
scmd.uscsi_bufaddr = 0;
scmd.uscsi_buflen = 0;
cmdblk[0] = (char)SCMD_TEST_UNIT_READY;
}
/*
* sgen_diag_ok()
* given an sg_state and a desired diagnostic level, return true if
* it is acceptable to output a message.
*/
/*ARGSUSED*/
static int
{
int diag_lvl;
switch (level) {
case CE_WARN:
case CE_NOTE:
case CE_CONT:
case CE_PANIC:
return (1);
case SGEN_DIAG1:
case SGEN_DIAG2:
case SGEN_DIAG3:
if (sg_state) {
/*
* Check to see if user overrode the diagnostics level
* for this instance (either via SGEN_IOC_DIAG or via
* .conf file). If not, fall back to the global diag
* level.
*/
else
} else {
}
return (1);
} else {
return (0);
}
default:
return (1);
}
}
/*PRINTFLIKE3*/
static void
{
char buf[256];
return;
switch (level) {
case CE_NOTE:
case CE_CONT:
case CE_WARN:
case CE_PANIC:
} else {
"%s", buf);
}
break;
case SGEN_DIAG1:
case SGEN_DIAG2:
case SGEN_DIAG3:
default:
} else {
"%s", buf);
}
}
}
/*
* sgen_dump_cdb()
* dump out the contents of a cdb. Take care that 'label' is not too
* large, or 'buf' could overflow.
*/
static void
{
static char hex[] = "0123456789abcdef";
char *buf, *p;
int i;
/*
* fastpath-- if we're not able to print out, don't do all of this
* extra work.
*/
return;
/*
* 3 characters for each byte (because of the ' '), plus the size of
* the label, plus the trailing ']' and the null character.
*/
if (i > 0)
*p++ = ' ';
}
*p++ = ']';
*p = 0;
}
static void
{
static char hex[] = "0123456789abcdef";
char *buf, *p;
int i;
/*
* fastpath-- if we're not able to print out, don't do all of this
* extra work.
*/
return;
/*
* 3 characters for each byte (because of the ' '), plus the size of
* the label, plus the trailing ']' and the null character.
*/
if (i > 0)
*p++ = ' ';
}
*p++ = ']';
*p = 0;
}