12518Sstevel /*
22518Sstevel * CDDL HEADER START
32518Sstevel *
42518Sstevel * The contents of this file are subject to the terms of the
52518Sstevel * Common Development and Distribution License (the "License").
62518Sstevel * You may not use this file except in compliance with the License.
72518Sstevel *
82518Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92518Sstevel * or http://www.opensolaris.org/os/licensing.
102518Sstevel * See the License for the specific language governing permissions
112518Sstevel * and limitations under the License.
122518Sstevel *
132518Sstevel * When distributing Covered Code, include this CDDL HEADER in each
142518Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152518Sstevel * If applicable, add the following below this CDDL HEADER, with the
162518Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
172518Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
182518Sstevel *
192518Sstevel * CDDL HEADER END
202518Sstevel */
212518Sstevel
222518Sstevel /*
236316Seschrock * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
242518Sstevel * Use is subject to license terms.
252518Sstevel */
262518Sstevel
272518Sstevel /*
282518Sstevel * Copyright Siemens 1999
292518Sstevel * All rights reserved.
302518Sstevel */
312518Sstevel
322518Sstevel
332518Sstevel /*
342518Sstevel * sgen - SCSI generic device driver
352518Sstevel *
362518Sstevel * The sgen driver provides user programs access to SCSI devices that
372518Sstevel * are not supported by other drivers by providing the USCSI(7I) interface.
382518Sstevel */
392518Sstevel
402518Sstevel #include <sys/modctl.h>
412518Sstevel #include <sys/file.h>
422518Sstevel #include <sys/scsi/scsi.h>
432518Sstevel #include <sys/scsi/targets/sgendef.h>
442518Sstevel
457009Scth /* The name of the driver, established from the module name in _init. */
467009Scth static char *sgen_label = NULL;
477009Scth
482518Sstevel #define DDI_NT_SGEN "ddi_generic:scsi"
492518Sstevel
502518Sstevel static char *sgen_devtypes[] = {
512518Sstevel "direct", /* 0x00 -- disks */
522518Sstevel "sequential", /* 0x01 */
532518Sstevel "printer", /* 0x02 */
542518Sstevel "processor", /* 0x03 */
552518Sstevel "worm", /* 0x04 */
562518Sstevel "rodirect", /* 0x05 */
572518Sstevel "scanner", /* 0x06 */
582518Sstevel "optical", /* 0x07 */
592518Sstevel "changer", /* 0x08 */
602518Sstevel "comm", /* 0x09 */
612518Sstevel "prepress1", /* 0x0a -- reserved for prepress (ASC IT8) */
622518Sstevel "prepress2", /* 0x0b -- reserved for prepress (ASC IT8) */
632518Sstevel "array_ctrl", /* 0x0c -- storage array */
642518Sstevel "ses", /* 0x0d -- enclosure services */
652518Sstevel "rbc", /* 0x0e -- simplified block */
662518Sstevel "ocrw", /* 0x0f -- optical card read/write */
672518Sstevel "bridge", /* 0x10 -- reserved for bridging expanders */
682518Sstevel "type_0x11", /* 0x11 */
692518Sstevel "type_0x12", /* 0x12 */
702518Sstevel "type_0x13", /* 0x13 */
712518Sstevel "type_0x14", /* 0x14 */
722518Sstevel "type_0x15", /* 0x15 */
732518Sstevel "type_0x16", /* 0x16 */
742518Sstevel "type_0x17", /* 0x17 */
752518Sstevel "type_0x18", /* 0x18 */
762518Sstevel "type_0x19", /* 0x19 */
772518Sstevel "type_0x1a", /* 0x1a */
782518Sstevel "type_0x1b", /* 0x1b */
792518Sstevel "type_0x1c", /* 0x1c */
802518Sstevel "type_0x1d", /* 0x1d */
812518Sstevel "type_0x1e", /* 0x1e */
822518Sstevel "type_unknown" /* 0x1f is "no device type" or "unknown" */
832518Sstevel };
842518Sstevel
852518Sstevel #define SGEN_NDEVTYPES ((sizeof (sgen_devtypes) / sizeof (char *)))
862518Sstevel
872518Sstevel #define SGEN_INQSTRLEN 24
882518Sstevel #define SGEN_VENDID_MAX 8
892518Sstevel #define SGEN_PRODID_MAX 16
902518Sstevel
912518Sstevel #define FILL_SCSI1_LUN(devp, pkt) \
922518Sstevel if ((devp)->sd_inq->inq_ansi == 0x1) { \
932518Sstevel int _lun; \
942518Sstevel _lun = ddi_prop_get_int(DDI_DEV_T_ANY, (devp)->sd_dev, \
952518Sstevel DDI_PROP_DONTPASS, SCSI_ADDR_PROP_LUN, 0); \
962518Sstevel if (_lun > 0) { \
972518Sstevel ((union scsi_cdb *)(pkt)->pkt_cdbp)->scc_lun = \
982518Sstevel _lun; \
992518Sstevel } \
1002518Sstevel }
1012518Sstevel
1022518Sstevel #define SGEN_DO_ERRSTATS(sg_state, x) \
1032518Sstevel if (sg_state->sgen_kstats) { \
1042518Sstevel struct sgen_errstats *sp; \
1052518Sstevel sp = (struct sgen_errstats *)sg_state->sgen_kstats->ks_data; \
1062518Sstevel sp->x.value.ui32++; \
1072518Sstevel }
1082518Sstevel
1092518Sstevel #define SCBP_C(pkt) ((*(pkt)->pkt_scbp) & STATUS_MASK)
1102518Sstevel
1112518Sstevel /*
1122518Sstevel * Standard entrypoints
1132518Sstevel */
1142518Sstevel static int sgen_attach(dev_info_t *, ddi_attach_cmd_t);
1152518Sstevel static int sgen_detach(dev_info_t *, ddi_detach_cmd_t);
1162518Sstevel static int sgen_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
1172518Sstevel static int sgen_probe(dev_info_t *);
1182518Sstevel static int sgen_open(dev_t *, int, int, cred_t *);
1192518Sstevel static int sgen_close(dev_t, int, int, cred_t *);
1202518Sstevel static int sgen_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1212518Sstevel
1222518Sstevel /*
1232518Sstevel * Configuration routines
1242518Sstevel */
1252518Sstevel static int sgen_do_attach(dev_info_t *);
1262518Sstevel static int sgen_setup_sense(sgen_state_t *);
1272518Sstevel static void sgen_create_errstats(sgen_state_t *, int);
1282518Sstevel static int sgen_do_suspend(dev_info_t *);
1292518Sstevel static int sgen_do_detach(dev_info_t *);
1302518Sstevel static void sgen_setup_binddb(dev_info_t *);
1312518Sstevel static void sgen_cleanup_binddb();
1322518Sstevel
1332518Sstevel /*
1342518Sstevel * Packet transport routines
1352518Sstevel */
1363368Slh195018 static int sgen_uscsi_cmd(dev_t, struct uscsi_cmd *, int);
1372518Sstevel static int sgen_start(struct buf *);
1386316Seschrock static int sgen_hold_cmdbuf(sgen_state_t *);
1392518Sstevel static void sgen_rele_cmdbuf(sgen_state_t *);
1402518Sstevel static int sgen_make_uscsi_cmd(sgen_state_t *, struct buf *);
1412518Sstevel static void sgen_restart(void *);
1422518Sstevel static void sgen_callback(struct scsi_pkt *);
1432518Sstevel static int sgen_handle_autosense(sgen_state_t *, struct scsi_pkt *);
1442518Sstevel static int sgen_handle_sense(sgen_state_t *);
1452518Sstevel static int sgen_handle_incomplete(sgen_state_t *, struct scsi_pkt *);
1462518Sstevel static int sgen_check_error(sgen_state_t *, struct buf *);
1476640Scth static int sgen_initiate_sense(sgen_state_t *, int);
1482518Sstevel static int sgen_scsi_transport(struct scsi_pkt *);
1492518Sstevel static int sgen_tur(dev_t);
1502518Sstevel
1512518Sstevel /*
1522518Sstevel * Logging/debugging routines
1532518Sstevel */
1542518Sstevel static void sgen_log(sgen_state_t *, int, const char *, ...);
1552518Sstevel static int sgen_diag_ok(sgen_state_t *, int);
1562518Sstevel static void sgen_dump_cdb(sgen_state_t *, const char *, union scsi_cdb *, int);
1572518Sstevel static void sgen_dump_sense(sgen_state_t *, size_t, uchar_t *);
1582518Sstevel
1592518Sstevel int sgen_diag = 0;
1602518Sstevel int sgen_sporadic_failures = 0;
1612518Sstevel int sgen_force_manual_sense = 0;
1622518Sstevel struct sgen_binddb sgen_binddb;
1632518Sstevel
1642518Sstevel static struct cb_ops sgen_cb_ops = {
1652518Sstevel sgen_open, /* open */
1662518Sstevel sgen_close, /* close */
1672518Sstevel nodev, /* strategy */
1682518Sstevel nodev, /* print */
1692518Sstevel nodev, /* dump */
1702518Sstevel nodev, /* read */
1712518Sstevel nodev, /* write */
1722518Sstevel sgen_ioctl, /* ioctl */
1732518Sstevel nodev, /* devmap */
1742518Sstevel nodev, /* mmap */
1752518Sstevel nodev, /* segmap */
1762518Sstevel nochpoll, /* poll */
1772518Sstevel ddi_prop_op, /* cb_prop_op */
1782518Sstevel 0, /* streamtab */
1792518Sstevel D_MP | D_NEW | D_HOTPLUG /* Driver compatibility flag */
1802518Sstevel };
1812518Sstevel
1822518Sstevel static struct dev_ops sgen_dev_ops = {
1832518Sstevel DEVO_REV, /* devo_rev, */
1842518Sstevel 0, /* refcnt */
1852518Sstevel sgen_getinfo, /* info */
1862518Sstevel nodev, /* identify */
1872518Sstevel sgen_probe, /* probe */
1882518Sstevel sgen_attach, /* attach */
1892518Sstevel sgen_detach, /* detach */
1902518Sstevel nodev, /* reset */
1912518Sstevel &sgen_cb_ops, /* driver operations */
1922518Sstevel (struct bus_ops *)0, /* bus operations */
193*7656SSherry.Moore@Sun.COM NULL, /* power */
194*7656SSherry.Moore@Sun.COM ddi_quiesce_not_supported, /* devo_quiesce */
1952518Sstevel };
1962518Sstevel
1972518Sstevel static void *sgen_soft_state = NULL;
1982518Sstevel
1992518Sstevel static struct modldrv modldrv = {
200*7656SSherry.Moore@Sun.COM &mod_driverops, "SCSI generic driver", &sgen_dev_ops
2012518Sstevel };
2022518Sstevel
2032518Sstevel static struct modlinkage modlinkage = {
2042518Sstevel MODREV_1, &modldrv, NULL
2052518Sstevel };
2062518Sstevel
2072518Sstevel int
_init(void)2082518Sstevel _init(void)
2092518Sstevel {
2102518Sstevel int err;
2112518Sstevel
2127009Scth /* establish driver name from module name */
2137009Scth sgen_label = (char *)mod_modname(&modlinkage);
2147009Scth
2152518Sstevel sgen_log(NULL, SGEN_DIAG2, "in sgen_init()");
2162518Sstevel if ((err = ddi_soft_state_init(&sgen_soft_state,
2175597Slh195018 sizeof (sgen_state_t), SGEN_ESTIMATED_NUM_DEVS)) != 0) {
2182518Sstevel goto done;
2192518Sstevel }
2202518Sstevel
2212518Sstevel if ((err = mod_install(&modlinkage)) != 0) {
2222518Sstevel ddi_soft_state_fini(&sgen_soft_state);
2232518Sstevel goto done;
2242518Sstevel }
2252518Sstevel
2262518Sstevel done:
2272518Sstevel sgen_log(NULL, SGEN_DIAG2, "%s sgen_init()", err ? "failed" : "done");
2282518Sstevel return (err);
2292518Sstevel }
2302518Sstevel
2312518Sstevel int
_fini(void)2322518Sstevel _fini(void)
2332518Sstevel {
2342518Sstevel int err;
2352518Sstevel sgen_log(NULL, SGEN_DIAG2, "in sgen_fini()");
2362518Sstevel
2372518Sstevel if ((err = mod_remove(&modlinkage)) != 0) {
2382518Sstevel goto done;
2392518Sstevel }
2402518Sstevel
2412518Sstevel ddi_soft_state_fini(&sgen_soft_state);
2422518Sstevel sgen_cleanup_binddb();
2432518Sstevel
2442518Sstevel done:
2452518Sstevel sgen_log(NULL, SGEN_DIAG2, "%s sgen_fini()", err ? "failed" : "done");
2462518Sstevel return (err);
2472518Sstevel }
2482518Sstevel
2492518Sstevel int
_info(struct modinfo * modinfop)2502518Sstevel _info(struct modinfo *modinfop)
2512518Sstevel {
2522518Sstevel return (mod_info(&modlinkage, modinfop));
2532518Sstevel }
2542518Sstevel
2552518Sstevel /*
2562518Sstevel * sgen_typename()
2572518Sstevel * return a device type's name by looking it up in the sgen_devtypes table.
2582518Sstevel */
2592518Sstevel static char *
sgen_typename(uchar_t typeno)2602518Sstevel sgen_typename(uchar_t typeno)
2612518Sstevel {
2622518Sstevel if (typeno >= SGEN_NDEVTYPES)
2632518Sstevel return ("type_unknown");
2642518Sstevel return (sgen_devtypes[typeno]);
2652518Sstevel }
2662518Sstevel
2672518Sstevel /*
2682518Sstevel * sgen_typenum()
2692518Sstevel * return a device type's number by looking it up in the sgen_devtypes
2702518Sstevel * table.
2712518Sstevel */
2722518Sstevel static int
sgen_typenum(const char * typename,uchar_t * typenum)2732518Sstevel sgen_typenum(const char *typename, uchar_t *typenum)
2742518Sstevel {
2752518Sstevel int i;
2762518Sstevel for (i = 0; i < SGEN_NDEVTYPES; i++) {
2772518Sstevel if (strcasecmp(sgen_devtypes[i], typename) == 0) {
2782518Sstevel *typenum = (uchar_t)i;
2792518Sstevel return (0);
2802518Sstevel }
2812518Sstevel }
2822518Sstevel return (-1);
2832518Sstevel }
2842518Sstevel
2852518Sstevel /*
2862518Sstevel * sgen_setup_binddb()
2872518Sstevel * initialize a data structure which stores all of the information about
2882518Sstevel * which devices and device types the driver should bind to.
2892518Sstevel */
2902518Sstevel static void
sgen_setup_binddb(dev_info_t * dip)2912518Sstevel sgen_setup_binddb(dev_info_t *dip)
2922518Sstevel {
2932518Sstevel char **strs = NULL, *cp, *pcp, *vcp;
2942518Sstevel uint_t nelems, pcplen, vcplen, idx;
2952518Sstevel
2962518Sstevel ASSERT(sgen_binddb.sdb_init == 0);
2972518Sstevel ASSERT(MUTEX_HELD(&sgen_binddb.sdb_lock));
2982518Sstevel
2992518Sstevel if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
3002518Sstevel "device-type-config-list", &strs, &nelems) == DDI_PROP_SUCCESS) {
3012518Sstevel /*
3022518Sstevel * for each device type specifier make a copy and put it into a
3032518Sstevel * node in the binddb.
3042518Sstevel */
3052518Sstevel for (idx = 0; idx < nelems; idx++) {
3062518Sstevel sgen_type_node_t *nodep;
3072518Sstevel uchar_t devtype;
3082518Sstevel cp = strs[idx];
3092518Sstevel if (sgen_typenum(cp, &devtype) != 0) {
3102518Sstevel sgen_log(NULL, CE_WARN,
3112518Sstevel "unknown device type '%s', "
3122518Sstevel "device unit-address @%s",
3132518Sstevel cp, ddi_get_name_addr(dip));
3142518Sstevel continue;
3152518Sstevel }
3162518Sstevel nodep = kmem_zalloc(sizeof (sgen_type_node_t),
3172518Sstevel KM_SLEEP);
3182518Sstevel nodep->node_type = devtype;
3192518Sstevel nodep->node_next = sgen_binddb.sdb_type_nodes;
3202518Sstevel sgen_binddb.sdb_type_nodes = nodep;
3212518Sstevel
3222518Sstevel sgen_log(NULL, SGEN_DIAG2, "found device type "
3232518Sstevel "'%s' in device-type-config-list, "
3242518Sstevel "device unit-address @%s",
3252518Sstevel cp, ddi_get_name_addr(dip));
3262518Sstevel }
3272518Sstevel ddi_prop_free(strs);
3282518Sstevel }
3292518Sstevel
3302518Sstevel /*
3312518Sstevel * for each Vendor/Product inquiry pair, build a node and put it
3322518Sstevel * into the the binddb.
3332518Sstevel */
3342518Sstevel if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
3352518Sstevel "inquiry-config-list", &strs, &nelems) == DDI_PROP_SUCCESS) {
3362518Sstevel
3372518Sstevel if (nelems % 2 == 1) {
3382518Sstevel sgen_log(NULL, CE_WARN, "inquiry-config-list must "
3392518Sstevel "contain Vendor/Product pairs, "
3402518Sstevel "device unit-address @%s",
3412518Sstevel ddi_get_name_addr(dip));
3422518Sstevel nelems--;
3432518Sstevel }
3442518Sstevel for (idx = 0; idx < nelems; idx += 2) {
3452518Sstevel sgen_inq_node_t *nodep;
3462518Sstevel /*
3472518Sstevel * Grab vendor and product ID.
3482518Sstevel */
3492518Sstevel vcp = strs[idx];
3502518Sstevel vcplen = strlen(vcp);
3512518Sstevel if (vcplen == 0 || vcplen > SGEN_VENDID_MAX) {
3522518Sstevel sgen_log(NULL, CE_WARN,
3532518Sstevel "Invalid vendor ID '%s', "
3542518Sstevel "device unit-address @%s",
3552518Sstevel vcp, ddi_get_name_addr(dip));
3562518Sstevel continue;
3572518Sstevel }
3582518Sstevel
3592518Sstevel pcp = strs[idx + 1];
3602518Sstevel pcplen = strlen(pcp);
3612518Sstevel if (pcplen == 0 || pcplen > SGEN_PRODID_MAX) {
3622518Sstevel sgen_log(NULL, CE_WARN,
3632518Sstevel "Invalid product ID '%s', "
3642518Sstevel "device unit-address @%s",
3652518Sstevel pcp, ddi_get_name_addr(dip));
3662518Sstevel continue;
3672518Sstevel }
3682518Sstevel
3692518Sstevel nodep = kmem_zalloc(sizeof (sgen_inq_node_t),
3702518Sstevel KM_SLEEP);
3712518Sstevel nodep->node_vendor = kmem_alloc(vcplen + 1, KM_SLEEP);
3722518Sstevel (void) strcpy(nodep->node_vendor, vcp);
3732518Sstevel nodep->node_product = kmem_alloc(pcplen + 1, KM_SLEEP);
3742518Sstevel (void) strcpy(nodep->node_product, pcp);
3752518Sstevel
3762518Sstevel nodep->node_next = sgen_binddb.sdb_inq_nodes;
3772518Sstevel sgen_binddb.sdb_inq_nodes = nodep;
3782518Sstevel
3792518Sstevel sgen_log(NULL, SGEN_DIAG2, "found inquiry string "
3802518Sstevel "'%s' '%s' in device-type-config-list, "
3812518Sstevel "device unit-address @%s",
3822518Sstevel nodep->node_vendor, nodep->node_product,
3832518Sstevel ddi_get_name_addr(dip));
3842518Sstevel }
3852518Sstevel ddi_prop_free(strs);
3862518Sstevel }
3872518Sstevel
3882518Sstevel sgen_binddb.sdb_init = 1;
3892518Sstevel }
3902518Sstevel
3912518Sstevel /*
3922518Sstevel * sgen_cleanup_binddb()
3932518Sstevel * deallocate data structures for binding database.
3942518Sstevel */
3952518Sstevel static void
sgen_cleanup_binddb()3962518Sstevel sgen_cleanup_binddb()
3972518Sstevel {
3982518Sstevel sgen_inq_node_t *inqp, *inqnextp;
3992518Sstevel sgen_type_node_t *typep, *typenextp;
4002518Sstevel
4012518Sstevel mutex_enter(&sgen_binddb.sdb_lock);
4022518Sstevel if (sgen_binddb.sdb_init == 0) {
4032518Sstevel mutex_exit(&sgen_binddb.sdb_lock);
4042518Sstevel return;
4052518Sstevel }
4062518Sstevel
4072518Sstevel for (inqp = sgen_binddb.sdb_inq_nodes; inqp != NULL; inqp = inqnextp) {
4082518Sstevel inqnextp = inqp->node_next;
4092518Sstevel ASSERT(inqp->node_vendor && inqp->node_product);
4102518Sstevel kmem_free(inqp->node_vendor,
4112518Sstevel strlen(inqp->node_vendor) + 1);
4122518Sstevel kmem_free(inqp->node_product,
4132518Sstevel strlen(inqp->node_product) + 1);
4142518Sstevel kmem_free(inqp, sizeof (sgen_inq_node_t));
4152518Sstevel }
4162518Sstevel
4172518Sstevel for (typep = sgen_binddb.sdb_type_nodes; typep != NULL;
4182518Sstevel typep = typenextp) {
4192518Sstevel typenextp = typep->node_next;
4202518Sstevel kmem_free(typep, sizeof (sgen_type_node_t));
4212518Sstevel }
4222518Sstevel mutex_exit(&sgen_binddb.sdb_lock);
4232518Sstevel }
4242518Sstevel
4252518Sstevel /*
4262518Sstevel * sgen_bind_byinq()
4272518Sstevel * lookup a device in the binding database by its inquiry data.
4282518Sstevel */
4292518Sstevel static int
sgen_bind_byinq(dev_info_t * dip)4302518Sstevel sgen_bind_byinq(dev_info_t *dip)
4312518Sstevel {
4322518Sstevel sgen_inq_node_t *nodep;
4332518Sstevel char vend_str[SGEN_VENDID_MAX+1];
4342518Sstevel char prod_str[SGEN_PRODID_MAX+1];
4352518Sstevel struct scsi_device *scsidevp;
4362518Sstevel
4372518Sstevel scsidevp = ddi_get_driver_private(dip);
4382518Sstevel
4392518Sstevel /*
4402518Sstevel * inq_vid and inq_pid are laid out by the protocol in order in the
4412518Sstevel * inquiry structure, and are not delimited by \0.
4422518Sstevel */
4432518Sstevel bcopy(scsidevp->sd_inq->inq_vid, vend_str, SGEN_VENDID_MAX);
4442518Sstevel vend_str[SGEN_VENDID_MAX] = '\0';
4452518Sstevel bcopy(scsidevp->sd_inq->inq_pid, prod_str, SGEN_PRODID_MAX);
4462518Sstevel prod_str[SGEN_PRODID_MAX] = '\0';
4472518Sstevel
4482518Sstevel for (nodep = sgen_binddb.sdb_inq_nodes; nodep != NULL;
4492518Sstevel nodep = nodep->node_next) {
4502518Sstevel /*
4512518Sstevel * Allow the "*" wildcard to match all vendor IDs.
4522518Sstevel */
4532518Sstevel if (strcmp(nodep->node_vendor, "*") != 0) {
4542518Sstevel if (strncasecmp(nodep->node_vendor, vend_str,
4552518Sstevel strlen(nodep->node_vendor)) != 0) {
4562518Sstevel continue;
4572518Sstevel }
4582518Sstevel }
4592518Sstevel
4602518Sstevel /*
4612518Sstevel * Using strncasecmp() with the key length allows substring
4622518Sstevel * matching for product data.
4632518Sstevel */
4642518Sstevel if (strncasecmp(nodep->node_product, prod_str,
4652518Sstevel strlen(nodep->node_product)) == 0) {
4662518Sstevel return (0);
4672518Sstevel }
4682518Sstevel }
4692518Sstevel return (-1);
4702518Sstevel }
4712518Sstevel
4722518Sstevel /*
4732518Sstevel * sgen_bind_bytype()
4742518Sstevel * lookup a device type in the binding database; if found, return a
4752518Sstevel * format string corresponding to the string in the .conf file.
4762518Sstevel */
4772518Sstevel static int
sgen_bind_bytype(dev_info_t * dip)4782518Sstevel sgen_bind_bytype(dev_info_t *dip)
4792518Sstevel {
4802518Sstevel sgen_type_node_t *nodep;
4812518Sstevel struct scsi_device *scsidevp;
4822518Sstevel
4832518Sstevel scsidevp = ddi_get_driver_private(dip);
4842518Sstevel
4852518Sstevel for (nodep = sgen_binddb.sdb_type_nodes; nodep != NULL;
4862518Sstevel nodep = nodep->node_next) {
4872518Sstevel if (nodep->node_type == scsidevp->sd_inq->inq_dtype) {
4882518Sstevel return (0);
4892518Sstevel }
4902518Sstevel }
4912518Sstevel return (-1);
4922518Sstevel }
4932518Sstevel
4942518Sstevel /*
4952518Sstevel * sgen_get_binding()
4962518Sstevel * Check to see if the device in question matches the criteria for
4972518Sstevel * sgen to bind.
4982518Sstevel *
4992518Sstevel * Either the .conf file must specify a device_type entry which
5002518Sstevel * matches the SCSI device type of this device, or the inquiry
5012518Sstevel * string provided by the device must match an inquiry string specified
5022518Sstevel * in the .conf file. Inquiry data is matched first.
5032518Sstevel */
5042518Sstevel static int
sgen_get_binding(dev_info_t * dip)5052518Sstevel sgen_get_binding(dev_info_t *dip)
5062518Sstevel {
5072518Sstevel int retval = 0;
5082518Sstevel
5092518Sstevel mutex_enter(&sgen_binddb.sdb_lock);
5102518Sstevel if (sgen_binddb.sdb_init == 0)
5112518Sstevel sgen_setup_binddb(dip);
5122518Sstevel mutex_exit(&sgen_binddb.sdb_lock);
5132518Sstevel
5142518Sstevel
5152518Sstevel /*
5162518Sstevel * Check device-type-config-list for a match by device type.
5172518Sstevel */
5182518Sstevel if (sgen_bind_bytype(dip) == 0)
5192518Sstevel goto done;
5202518Sstevel
5212518Sstevel /*
5222518Sstevel * Check inquiry-config-list for a match by Vendor/Product ID.
5232518Sstevel */
5242518Sstevel if (sgen_bind_byinq(dip) == 0)
5252518Sstevel goto done;
5262518Sstevel
5272518Sstevel retval = -1;
5282518Sstevel done:
5292518Sstevel return (retval);
5302518Sstevel }
5312518Sstevel
5322518Sstevel /*
5332518Sstevel * sgen_attach()
5342518Sstevel * attach(9e) entrypoint.
5352518Sstevel */
5362518Sstevel static int
sgen_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)5372518Sstevel sgen_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
5382518Sstevel {
5392518Sstevel int err;
5402518Sstevel
5412518Sstevel sgen_log(NULL, SGEN_DIAG2, "in sgen_attach(), device unit-address @%s",
5422518Sstevel ddi_get_name_addr(dip));
5432518Sstevel
5442518Sstevel switch (cmd) {
5452518Sstevel case DDI_ATTACH:
5462518Sstevel err = sgen_do_attach(dip);
5472518Sstevel break;
5482518Sstevel case DDI_RESUME:
5492518Sstevel err = DDI_SUCCESS;
5502518Sstevel break;
5512518Sstevel case DDI_PM_RESUME:
5522518Sstevel default:
5532518Sstevel err = DDI_FAILURE;
5542518Sstevel break;
5552518Sstevel }
5562518Sstevel
5572518Sstevel done:
5582518Sstevel sgen_log(NULL, SGEN_DIAG2, "%s sgen_attach(), device unit-address @%s",
5592518Sstevel err == DDI_SUCCESS ? "done" : "failed", ddi_get_name_addr(dip));
5602518Sstevel return (err);
5612518Sstevel }
5622518Sstevel
5632518Sstevel /*
5642518Sstevel * sgen_do_attach()
5652518Sstevel * handle the nitty details of attach.
5662518Sstevel */
5672518Sstevel static int
sgen_do_attach(dev_info_t * dip)5682518Sstevel sgen_do_attach(dev_info_t *dip)
5692518Sstevel {
5702518Sstevel int instance;
5712518Sstevel struct scsi_device *scsidevp;
5722518Sstevel sgen_state_t *sg_state;
5732518Sstevel uchar_t devtype;
5742518Sstevel struct scsi_inquiry *inq;
5752518Sstevel
5762518Sstevel instance = ddi_get_instance(dip);
5772518Sstevel
5782518Sstevel scsidevp = ddi_get_driver_private(dip);
5792518Sstevel ASSERT(scsidevp);
5802518Sstevel
5812518Sstevel sgen_log(NULL, SGEN_DIAG2, "sgen_do_attach: instance = %d, "
5822518Sstevel "device unit-address @%s", instance, ddi_get_name_addr(dip));
5832518Sstevel
5842518Sstevel /*
5852518Sstevel * Probe the device in order to get its device type to name the minor
5862518Sstevel * node.
5872518Sstevel */
5882518Sstevel if (scsi_probe(scsidevp, NULL_FUNC) != SCSIPROBE_EXISTS) {
5892518Sstevel scsi_unprobe(scsidevp);
5902518Sstevel return (DDI_FAILURE);
5912518Sstevel }
5922518Sstevel
5932518Sstevel if (ddi_soft_state_zalloc(sgen_soft_state, instance) != DDI_SUCCESS) {
5942518Sstevel sgen_log(NULL, SGEN_DIAG1,
5952518Sstevel "sgen_do_attach: failed to allocate softstate, "
5962518Sstevel "device unit-address @%s", ddi_get_name_addr(dip));
5972518Sstevel scsi_unprobe(scsidevp);
5982518Sstevel return (DDI_FAILURE);
5992518Sstevel }
6002518Sstevel
6012518Sstevel inq = scsidevp->sd_inq; /* valid while device is probed... */
6022518Sstevel devtype = inq->inq_dtype;
6032518Sstevel
6042518Sstevel sg_state = ddi_get_soft_state(sgen_soft_state, instance);
6052518Sstevel sg_state->sgen_scsidev = scsidevp;
6062518Sstevel scsidevp->sd_dev = dip;
6072518Sstevel
6082518Sstevel /*
6092518Sstevel * Now that sg_state->sgen_scsidev is initialized, it's ok to
6102518Sstevel * call sgen_log with sg_state instead of NULL.
6112518Sstevel */
6122518Sstevel
6132518Sstevel /*
6142518Sstevel * If the user specified the sgen_diag property, override the global
6152518Sstevel * sgen_diag setting by setting sg_state's sgen_diag value. If the
6162518Sstevel * user gave a value out of range, default to '0'.
6172518Sstevel */
6182518Sstevel sg_state->sgen_diag = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
6192518Sstevel "sgen-diag", -1);
6202518Sstevel
6212518Sstevel if (sg_state->sgen_diag != -1) {
6222518Sstevel if (sg_state->sgen_diag < 0 || sg_state->sgen_diag > 3)
6232518Sstevel sg_state->sgen_diag = 0;
6242518Sstevel }
6252518Sstevel
6262518Sstevel sgen_log(sg_state, SGEN_DIAG2,
6272518Sstevel "sgen_do_attach: sgen_soft_state=0x%p, instance=%d, "
6282518Sstevel "device unit-address @%s",
6292518Sstevel sgen_soft_state, instance, ddi_get_name_addr(dip));
6302518Sstevel
6312518Sstevel /*
6322518Sstevel * For simplicity, the minor number == the instance number
6332518Sstevel */
6342518Sstevel if (ddi_create_minor_node(dip, sgen_typename(devtype), S_IFCHR,
6352518Sstevel instance, DDI_NT_SGEN, NULL) == DDI_FAILURE) {
6362518Sstevel scsi_unprobe(scsidevp);
6372518Sstevel ddi_prop_remove_all(dip);
6382518Sstevel sgen_log(sg_state, SGEN_DIAG1,
6395597Slh195018 "sgen_do_attach: minor node creation failed, "
6405597Slh195018 "device unit-address @%s", ddi_get_name_addr(dip));
6412518Sstevel ddi_soft_state_free(sgen_soft_state, instance);
6422518Sstevel return (DDI_FAILURE);
6432518Sstevel }
6442518Sstevel
6452518Sstevel /*
6462518Sstevel * Allocate the command buffer, then create a condition variable for
6472518Sstevel * managing it; mark the command buffer as free.
6482518Sstevel */
6492518Sstevel sg_state->sgen_cmdbuf = getrbuf(KM_SLEEP);
6502518Sstevel cv_init(&sg_state->sgen_cmdbuf_cv, NULL, CV_DRIVER, NULL);
6512518Sstevel
6522518Sstevel SGEN_CLR_BUSY(sg_state);
6532518Sstevel SGEN_CLR_OPEN(sg_state);
6542518Sstevel SGEN_CLR_SUSP(sg_state);
6552518Sstevel
6562518Sstevel /*
6572518Sstevel * If the hba and the target both support wide xfers, enable them.
6582518Sstevel */
6592518Sstevel if (scsi_ifgetcap(&sg_state->sgen_scsiaddr, "wide-xfer", 1) != -1) {
6602518Sstevel int wide = 0;
6612518Sstevel if ((inq->inq_rdf == RDF_SCSI2) &&
6622518Sstevel (inq->inq_wbus16 || inq->inq_wbus32))
6632518Sstevel wide = 1;
6642518Sstevel if (scsi_ifsetcap(&sg_state->sgen_scsiaddr, "wide-xfer",
6652518Sstevel wide, 1) == 1) {
6662518Sstevel sgen_log(sg_state, SGEN_DIAG1,
6672518Sstevel "sgen_attach: wide xfer %s, "
6682518Sstevel "device unit-address @%s",
6692518Sstevel wide ? "enabled" : "disabled",
6702518Sstevel ddi_get_name_addr(dip));
6712518Sstevel }
6722518Sstevel }
6732518Sstevel
6742518Sstevel /*
6752518Sstevel * This is a little debugging code-- since the codepath for auto-sense
6762518Sstevel * and 'manual' sense is split, toggling this variable will make
6772518Sstevel * sgen act as though the adapter in question can't do auto-sense.
6782518Sstevel */
6792518Sstevel if (sgen_force_manual_sense) {
6802518Sstevel if (scsi_ifsetcap(&sg_state->sgen_scsiaddr, "auto-rqsense",
6812518Sstevel 0, 1) == 1) {
6822518Sstevel sg_state->sgen_arq_enabled = 0;
6832518Sstevel } else {
6842518Sstevel sg_state->sgen_arq_enabled = 1;
6852518Sstevel }
6862518Sstevel } else {
6872518Sstevel /*
6882518Sstevel * Enable autorequest sense, if supported
6892518Sstevel */
6902518Sstevel if (scsi_ifgetcap(&sg_state->sgen_scsiaddr,
6912518Sstevel "auto-rqsense", 1) != 1) {
6922518Sstevel if (scsi_ifsetcap(&sg_state->sgen_scsiaddr,
6932518Sstevel "auto-rqsense", 1, 1) == 1) {
6942518Sstevel sg_state->sgen_arq_enabled = 1;
6952518Sstevel sgen_log(sg_state, SGEN_DIAG1,
6962518Sstevel "sgen_attach: auto-request-sense enabled, "
6972518Sstevel "device unit-address @%s",
6982518Sstevel ddi_get_name_addr(dip));
6992518Sstevel } else {
7002518Sstevel sg_state->sgen_arq_enabled = 0;
7012518Sstevel sgen_log(sg_state, SGEN_DIAG1,
7022518Sstevel "sgen_attach: auto-request-sense disabled, "
7032518Sstevel "device unit-address @%s",
7042518Sstevel ddi_get_name_addr(dip));
7052518Sstevel }
7062518Sstevel } else {
7072518Sstevel sg_state->sgen_arq_enabled = 1; /* already enabled */
7082518Sstevel sgen_log(sg_state, SGEN_DIAG1,
7092518Sstevel "sgen_attach: auto-request-sense enabled, "
7102518Sstevel "device unit-address @%s", ddi_get_name_addr(dip));
7112518Sstevel }
7122518Sstevel }
7132518Sstevel
7142518Sstevel /*
7152518Sstevel * Allocate plumbing for manually fetching sense.
7162518Sstevel */
7172518Sstevel if (sgen_setup_sense(sg_state) != 0) {
7182518Sstevel freerbuf(sg_state->sgen_cmdbuf);
7192518Sstevel ddi_prop_remove_all(dip);
7202518Sstevel ddi_remove_minor_node(dip, NULL);
7212518Sstevel scsi_unprobe(scsidevp);
7222518Sstevel sgen_log(sg_state, SGEN_DIAG1,
7232518Sstevel "sgen_do_attach: failed to setup request-sense, "
7242518Sstevel "device unit-address @%s", ddi_get_name_addr(dip));
7252518Sstevel ddi_soft_state_free(sgen_soft_state, instance);
7262518Sstevel return (DDI_FAILURE);
7272518Sstevel }
7282518Sstevel
7292518Sstevel sgen_create_errstats(sg_state, instance);
7302518Sstevel
7312518Sstevel ddi_report_dev(dip);
7322518Sstevel
7332518Sstevel return (DDI_SUCCESS);
7342518Sstevel }
7352518Sstevel
7362518Sstevel /*
7372518Sstevel * sgen_setup_sense()
7382518Sstevel * Allocate a request sense packet so that if sgen needs to fetch sense
7392518Sstevel * data for the user, it will have a pkt ready to send.
7402518Sstevel */
7412518Sstevel static int
sgen_setup_sense(sgen_state_t * sg_state)7422518Sstevel sgen_setup_sense(sgen_state_t *sg_state)
7432518Sstevel {
7442518Sstevel struct buf *bp;
7452518Sstevel struct scsi_pkt *rqpkt;
7462518Sstevel
7472518Sstevel if ((bp = scsi_alloc_consistent_buf(&sg_state->sgen_scsiaddr, NULL,
7485597Slh195018 MAX_SENSE_LENGTH, B_READ, SLEEP_FUNC, NULL)) == NULL) {
7492518Sstevel return (-1);
7502518Sstevel }
7512518Sstevel
7522518Sstevel if ((rqpkt = scsi_init_pkt(&sg_state->sgen_scsiaddr, NULL, bp,
7532518Sstevel CDB_GROUP0, 1, 0, PKT_CONSISTENT, SLEEP_FUNC, NULL)) == NULL) {
7542518Sstevel scsi_free_consistent_buf(bp);
7552518Sstevel return (-1);
7562518Sstevel }
7572518Sstevel
7582518Sstevel /*
7592518Sstevel * Make the results of running a SENSE available by filling out the
7602518Sstevel * sd_sense field of the scsi device (sgen_sense is just an alias).
7612518Sstevel */
7622518Sstevel sg_state->sgen_sense = (struct scsi_extended_sense *)bp->b_un.b_addr;
7632518Sstevel
7642518Sstevel (void) scsi_setup_cdb((union scsi_cdb *)rqpkt->pkt_cdbp,
7655597Slh195018 SCMD_REQUEST_SENSE, 0, MAX_SENSE_LENGTH, 0);
7662518Sstevel FILL_SCSI1_LUN(sg_state->sgen_scsidev, rqpkt);
7672518Sstevel
7682518Sstevel rqpkt->pkt_comp = sgen_callback;
7692518Sstevel rqpkt->pkt_time = SGEN_IO_TIME;
7702518Sstevel rqpkt->pkt_flags |= FLAG_SENSING;
7712518Sstevel rqpkt->pkt_private = sg_state;
7722518Sstevel
7732518Sstevel sg_state->sgen_rqspkt = rqpkt;
7742518Sstevel sg_state->sgen_rqsbuf = bp;
7752518Sstevel
7762518Sstevel return (0);
7772518Sstevel }
7782518Sstevel
7792518Sstevel /*
7802518Sstevel * sgen_create_errstats()
7816316Seschrock * create named kstats for tracking occurrence of errors.
7822518Sstevel */
7832518Sstevel static void
sgen_create_errstats(sgen_state_t * sg_state,int instance)7842518Sstevel sgen_create_errstats(sgen_state_t *sg_state, int instance)
7852518Sstevel {
7862518Sstevel char kstatname[KSTAT_STRLEN];
7872518Sstevel struct sgen_errstats *stp;
7882518Sstevel
7897009Scth (void) snprintf(kstatname, KSTAT_STRLEN, "%s%d,err",
7907009Scth sgen_label, instance);
7912518Sstevel sg_state->sgen_kstats = kstat_create("sgenerr", instance,
7922518Sstevel kstatname, "device_error", KSTAT_TYPE_NAMED,
7932518Sstevel sizeof (struct sgen_errstats) / sizeof (kstat_named_t),
7942518Sstevel KSTAT_FLAG_PERSISTENT);
7952518Sstevel
7962518Sstevel if (sg_state->sgen_kstats == NULL)
7972518Sstevel return;
7982518Sstevel
7992518Sstevel stp = (struct sgen_errstats *)sg_state->sgen_kstats->ks_data;
8002518Sstevel kstat_named_init(&stp->sgen_trans_err, "transport_errors",
8012518Sstevel KSTAT_DATA_UINT32);
8022518Sstevel kstat_named_init(&stp->sgen_restart, "command_restarts",
8032518Sstevel KSTAT_DATA_UINT32);
8042518Sstevel kstat_named_init(&stp->sgen_incmp_err, "incomplete_commands",
8052518Sstevel KSTAT_DATA_UINT32);
8062518Sstevel kstat_named_init(&stp->sgen_autosen_rcv, "autosense_occurred",
8072518Sstevel KSTAT_DATA_UINT32);
8082518Sstevel kstat_named_init(&stp->sgen_autosen_bad, "autosense_undecipherable",
8092518Sstevel KSTAT_DATA_UINT32);
8102518Sstevel kstat_named_init(&stp->sgen_sense_rcv, "sense_fetches",
8112518Sstevel KSTAT_DATA_UINT32);
8122518Sstevel kstat_named_init(&stp->sgen_sense_bad, "sense_data_undecipherable",
8132518Sstevel KSTAT_DATA_UINT32);
8142518Sstevel kstat_named_init(&stp->sgen_recov_err, "recoverable_error",
8152518Sstevel KSTAT_DATA_UINT32);
8162518Sstevel kstat_named_init(&stp->sgen_nosen_err, "NO_SENSE_sense_key",
8172518Sstevel KSTAT_DATA_UINT32);
8182518Sstevel kstat_named_init(&stp->sgen_unrecov_err, "unrecoverable_sense_error",
8192518Sstevel KSTAT_DATA_UINT32);
8202518Sstevel sg_state->sgen_kstats->ks_private = sg_state;
8212518Sstevel sg_state->sgen_kstats->ks_update = nulldev;
8222518Sstevel kstat_install(sg_state->sgen_kstats);
8232518Sstevel }
8242518Sstevel
8252518Sstevel /*
8262518Sstevel * sgen_detach()
8272518Sstevel * detach(9E) entrypoint
8282518Sstevel */
8292518Sstevel static int
sgen_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)8302518Sstevel sgen_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
8312518Sstevel {
8322518Sstevel int instance;
8332518Sstevel sgen_state_t *sg_state;
8342518Sstevel
8352518Sstevel instance = ddi_get_instance(dip);
8362518Sstevel sg_state = ddi_get_soft_state(sgen_soft_state, instance);
8372518Sstevel
8382518Sstevel sgen_log(sg_state, SGEN_DIAG2, "in sgen_detach(), "
8392518Sstevel "device unit-address @%s", ddi_get_name_addr(dip));
8402518Sstevel
8412518Sstevel if (sg_state == NULL) {
8422518Sstevel sgen_log(NULL, SGEN_DIAG1,
8432518Sstevel "sgen_detach: failed, no softstate found (%d), "
8442518Sstevel "device unit-address @%s",
8452518Sstevel instance, ddi_get_name_addr(dip));
8462518Sstevel return (DDI_FAILURE);
8472518Sstevel }
8482518Sstevel
8492518Sstevel switch (cmd) {
8502518Sstevel case DDI_DETACH:
8512518Sstevel return (sgen_do_detach(dip));
8522518Sstevel case DDI_SUSPEND:
8532518Sstevel return (sgen_do_suspend(dip));
8542518Sstevel case DDI_PM_SUSPEND:
8552518Sstevel default:
8562518Sstevel return (DDI_FAILURE);
8572518Sstevel }
8582518Sstevel }
8592518Sstevel
8602518Sstevel /*
8612518Sstevel * sgen_do_detach()
8622518Sstevel * detach the driver, tearing down resources.
8632518Sstevel */
8642518Sstevel static int
sgen_do_detach(dev_info_t * dip)8652518Sstevel sgen_do_detach(dev_info_t *dip)
8662518Sstevel {
8672518Sstevel int instance;
8682518Sstevel sgen_state_t *sg_state;
8692518Sstevel struct scsi_device *devp;
8702518Sstevel
8712518Sstevel instance = ddi_get_instance(dip);
8722518Sstevel sg_state = ddi_get_soft_state(sgen_soft_state, instance);
8732518Sstevel ASSERT(sg_state);
8742518Sstevel
8752518Sstevel sgen_log(sg_state, SGEN_DIAG2, "in sgen_do_detach(), "
8762518Sstevel "device unit-address @%s", ddi_get_name_addr(dip));
8772518Sstevel devp = ddi_get_driver_private(dip);
8782518Sstevel
8792518Sstevel mutex_enter(&sg_state->sgen_mutex);
8802518Sstevel if (SGEN_IS_BUSY(sg_state)) {
8812518Sstevel mutex_exit(&sg_state->sgen_mutex);
8822518Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_do_detach: failed because "
8832518Sstevel "device is busy, device unit-address @%s",
8842518Sstevel ddi_get_name_addr(dip));
8852518Sstevel return (DDI_FAILURE);
8862518Sstevel }
8872518Sstevel mutex_exit(&sg_state->sgen_mutex);
8882518Sstevel
8892518Sstevel /*
8902518Sstevel * Final approach for detach. Free data allocated by scsi_probe()
8912518Sstevel * in attach.
8922518Sstevel */
8932518Sstevel if (sg_state->sgen_restart_timeid)
8942518Sstevel (void) untimeout(sg_state->sgen_restart_timeid);
8952518Sstevel sg_state->sgen_restart_timeid = 0;
8962518Sstevel scsi_unprobe(devp);
8972518Sstevel
8982518Sstevel /*
8992518Sstevel * Free auto-request plumbing.
9002518Sstevel */
9012518Sstevel scsi_free_consistent_buf(sg_state->sgen_rqsbuf);
9022518Sstevel scsi_destroy_pkt(sg_state->sgen_rqspkt);
9032518Sstevel
9042518Sstevel if (sg_state->sgen_kstats) {
9052518Sstevel kstat_delete(sg_state->sgen_kstats);
9062518Sstevel sg_state->sgen_kstats = NULL;
9072518Sstevel }
9082518Sstevel
9092518Sstevel /*
9102518Sstevel * Free command buffer and clean up
9112518Sstevel */
9122518Sstevel freerbuf(sg_state->sgen_cmdbuf);
9132518Sstevel cv_destroy(&sg_state->sgen_cmdbuf_cv);
9142518Sstevel
9152518Sstevel sgen_log(sg_state, SGEN_DIAG2, "done sgen_do_detach(), "
9162518Sstevel "device unit-address @%s", ddi_get_name_addr(dip));
9172518Sstevel
9182518Sstevel ddi_soft_state_free(sgen_soft_state, instance);
9192518Sstevel ddi_prop_remove_all(dip);
9202518Sstevel ddi_remove_minor_node(dip, NULL);
9212518Sstevel return (DDI_SUCCESS);
9222518Sstevel }
9232518Sstevel
9242518Sstevel /*
9252518Sstevel * sgen_do_suspend()
9262518Sstevel * suspend the driver. This sets the "suspend" bit for this target if it
9272518Sstevel * is currently open; once resumed, the suspend bit will cause
9282518Sstevel * subsequent I/Os to fail. We want user programs to close and
9292518Sstevel * reopen the device to acknowledge that they need to reexamine its
9302518Sstevel * state and do the right thing.
9312518Sstevel */
9322518Sstevel static int
sgen_do_suspend(dev_info_t * dip)9332518Sstevel sgen_do_suspend(dev_info_t *dip)
9342518Sstevel {
9352518Sstevel int instance;
9362518Sstevel sgen_state_t *sg_state;
9372518Sstevel
9382518Sstevel instance = ddi_get_instance(dip);
9392518Sstevel sg_state = ddi_get_soft_state(sgen_soft_state, instance);
9402518Sstevel ASSERT(sg_state);
9412518Sstevel
9422518Sstevel sgen_log(sg_state, SGEN_DIAG2, "in sgen_do_suspend(), "
9432518Sstevel "device unit-address @%s", ddi_get_name_addr(dip));
9442518Sstevel
9452518Sstevel if (sg_state->sgen_restart_timeid) {
9462518Sstevel (void) untimeout(sg_state->sgen_restart_timeid);
9472518Sstevel }
9482518Sstevel sg_state->sgen_restart_timeid = 0;
9492518Sstevel
9502518Sstevel mutex_enter(&sg_state->sgen_mutex);
9512518Sstevel if (SGEN_IS_OPEN(sg_state))
9522518Sstevel SGEN_SET_SUSP(sg_state);
9532518Sstevel mutex_exit(&sg_state->sgen_mutex);
9542518Sstevel
9552518Sstevel sgen_log(sg_state, SGEN_DIAG2, "done sgen_do_suspend(), "
9562518Sstevel "device unit-address @%s", ddi_get_name_addr(dip));
9572518Sstevel return (DDI_SUCCESS);
9582518Sstevel }
9592518Sstevel
9602518Sstevel /*
9612518Sstevel * sgen_getinfo()
9622518Sstevel * getinfo(9e) entrypoint.
9632518Sstevel */
9642518Sstevel /*ARGSUSED*/
9652518Sstevel static int
sgen_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)9662518Sstevel sgen_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
9672518Sstevel {
9682518Sstevel dev_t dev;
9692518Sstevel sgen_state_t *sg_state;
9702518Sstevel int instance, error;
9712518Sstevel switch (infocmd) {
9722518Sstevel case DDI_INFO_DEVT2DEVINFO:
9732518Sstevel dev = (dev_t)arg;
9742518Sstevel instance = getminor(dev);
9752518Sstevel if ((sg_state = ddi_get_soft_state(sgen_soft_state, instance))
9762518Sstevel == NULL)
9772518Sstevel return (DDI_FAILURE);
9782518Sstevel *result = (void *) sg_state->sgen_scsidev->sd_dev;
9792518Sstevel error = DDI_SUCCESS;
9802518Sstevel break;
9812518Sstevel case DDI_INFO_DEVT2INSTANCE:
9822518Sstevel dev = (dev_t)arg;
9832518Sstevel instance = getminor(dev);
9842518Sstevel *result = (void *)(uintptr_t)instance;
9852518Sstevel error = DDI_SUCCESS;
9862518Sstevel break;
9872518Sstevel default:
9882518Sstevel error = DDI_FAILURE;
9892518Sstevel }
9902518Sstevel return (error);
9912518Sstevel }
9922518Sstevel
9932518Sstevel /*
9942518Sstevel * sgen_probe()
9952518Sstevel * probe(9e) entrypoint. sgen *never* returns DDI_PROBE_PARTIAL, in
9962518Sstevel * order to avoid leaving around extra devinfos. If sgen's binding
9972518Sstevel * rules indicate that it should bind, it returns DDI_PROBE_SUCCESS.
9982518Sstevel */
9992518Sstevel static int
sgen_probe(dev_info_t * dip)10002518Sstevel sgen_probe(dev_info_t *dip)
10012518Sstevel {
10022518Sstevel struct scsi_device *scsidevp;
10032518Sstevel int instance;
10042518Sstevel int rval;
10052518Sstevel
10062518Sstevel scsidevp = ddi_get_driver_private(dip);
10072518Sstevel instance = ddi_get_instance(dip);
10082518Sstevel sgen_log(NULL, SGEN_DIAG2, "in sgen_probe(): instance = %d, "
10092518Sstevel "device unit-address @%s", instance, ddi_get_name_addr(dip));
10102518Sstevel
10112518Sstevel if (ddi_dev_is_sid(dip) == DDI_SUCCESS)
10122518Sstevel return (DDI_PROBE_DONTCARE);
10132518Sstevel
10142518Sstevel if (ddi_get_soft_state(sgen_soft_state, instance) != NULL)
10152518Sstevel return (DDI_PROBE_FAILURE);
10162518Sstevel
10172518Sstevel mutex_enter(&sgen_binddb.sdb_lock);
10182518Sstevel if (sgen_binddb.sdb_init == 0) {
10192518Sstevel sgen_setup_binddb(dip);
10202518Sstevel }
10212518Sstevel mutex_exit(&sgen_binddb.sdb_lock);
10222518Sstevel
10232518Sstevel /*
10242518Sstevel * A small optimization: if it's impossible for sgen to bind to
10252518Sstevel * any devices, don't bother probing, just fail.
10262518Sstevel */
10272518Sstevel if ((sgen_binddb.sdb_inq_nodes == NULL) &&
10282518Sstevel (sgen_binddb.sdb_type_nodes == NULL)) {
10292518Sstevel return (DDI_PROBE_FAILURE);
10302518Sstevel }
10312518Sstevel
10322518Sstevel if (scsi_probe(scsidevp, NULL_FUNC) == SCSIPROBE_EXISTS) {
10332518Sstevel if (sgen_get_binding(dip) == 0) {
10342518Sstevel rval = DDI_PROBE_SUCCESS;
10352518Sstevel }
10362518Sstevel } else {
10372518Sstevel rval = DDI_PROBE_FAILURE;
10382518Sstevel }
10392518Sstevel scsi_unprobe(scsidevp);
10402518Sstevel
10412518Sstevel sgen_log(NULL, SGEN_DIAG2, "sgen_probe() %s, device unit-address @%s",
10422518Sstevel rval == DDI_PROBE_SUCCESS ? "succeeded" : "failed",
10432518Sstevel ddi_get_name_addr(dip));
10442518Sstevel return (rval);
10452518Sstevel }
10462518Sstevel
10472518Sstevel /*
10482518Sstevel * sgen_open()
10492518Sstevel * open(9e) entrypoint. sgen enforces a strict exclusive open policy per
10502518Sstevel * target.
10512518Sstevel */
10522518Sstevel /*ARGSUSED1*/
10532518Sstevel static int
sgen_open(dev_t * dev_p,int flag,int otyp,cred_t * cred_p)10542518Sstevel sgen_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
10552518Sstevel {
10562518Sstevel dev_t dev = *dev_p;
10572518Sstevel sgen_state_t *sg_state;
10582518Sstevel int instance;
10592518Sstevel
10602518Sstevel instance = getminor(dev);
10612518Sstevel
10622518Sstevel if ((sg_state = ddi_get_soft_state(sgen_soft_state, instance)) == NULL)
10632518Sstevel return (ENXIO);
10642518Sstevel
10652518Sstevel sgen_log(sg_state, SGEN_DIAG2, "in sgen_open(): instance = %d",
10662518Sstevel instance);
10672518Sstevel
10682518Sstevel mutex_enter(&sg_state->sgen_mutex);
10692518Sstevel
10706316Seschrock /*
10716316Seschrock * Don't allow new opens of a suspended device until the last close has
10726316Seschrock * happened. This is rather simplistic, but keeps the implementation
10736316Seschrock * straightforward.
10746316Seschrock */
10756316Seschrock if (SGEN_IS_SUSP(sg_state)) {
10766316Seschrock mutex_exit(&sg_state->sgen_mutex);
10776316Seschrock return (EIO);
10786316Seschrock }
10796316Seschrock
10806316Seschrock /*
10816316Seschrock * Enforce exclusive access.
10826316Seschrock */
10836316Seschrock if (SGEN_IS_EXCL(sg_state) ||
10846316Seschrock (SGEN_IS_OPEN(sg_state) && (flag & FEXCL))) {
10852518Sstevel mutex_exit(&sg_state->sgen_mutex);
10862518Sstevel return (EBUSY);
10872518Sstevel }
10882518Sstevel
10896316Seschrock if (flag & FEXCL)
10906316Seschrock SGEN_SET_EXCL(sg_state);
10916316Seschrock
10922518Sstevel SGEN_SET_OPEN(sg_state);
10932518Sstevel
10942518Sstevel mutex_exit(&sg_state->sgen_mutex);
10952518Sstevel
10962518Sstevel return (0);
10972518Sstevel }
10982518Sstevel
10992518Sstevel /*
11002518Sstevel * sgen_close()
11012518Sstevel * close(9e) entrypoint.
11022518Sstevel */
11032518Sstevel /*ARGSUSED1*/
11042518Sstevel static int
sgen_close(dev_t dev,int flag,int otyp,cred_t * cred_p)11052518Sstevel sgen_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
11062518Sstevel {
11072518Sstevel sgen_state_t *sg_state;
11082518Sstevel int instance;
11092518Sstevel
11102518Sstevel instance = getminor(dev);
11112518Sstevel
11122518Sstevel if ((sg_state = ddi_get_soft_state(sgen_soft_state, instance)) == NULL)
11132518Sstevel return (ENXIO);
11142518Sstevel
11152518Sstevel sgen_log(sg_state, SGEN_DIAG2, "in sgen_close(): instance = %d",
11162518Sstevel instance);
11172518Sstevel
11182518Sstevel mutex_enter(&sg_state->sgen_mutex);
11192518Sstevel SGEN_CLR_OPEN(sg_state);
11206316Seschrock SGEN_CLR_EXCL(sg_state);
11212518Sstevel SGEN_CLR_SUSP(sg_state); /* closing clears the 'I was suspended' bit */
11222518Sstevel mutex_exit(&sg_state->sgen_mutex);
11232518Sstevel
11242518Sstevel sgen_log(sg_state, SGEN_DIAG2, "done sgen_close()");
11252518Sstevel
11262518Sstevel return (0);
11272518Sstevel }
11282518Sstevel
11292518Sstevel /*
11302518Sstevel * sgen_ioctl()
11312518Sstevel * sgen supports the USCSI(7I) ioctl interface.
11322518Sstevel */
11332518Sstevel /*ARGSUSED4*/
11342518Sstevel static int
sgen_ioctl(dev_t dev,int cmd,intptr_t arg,int flag,cred_t * cred_p,int * rval_p)11352518Sstevel sgen_ioctl(dev_t dev,
11362518Sstevel int cmd, intptr_t arg, int flag, cred_t *cred_p, int *rval_p)
11372518Sstevel {
11382518Sstevel int retval = 0;
11392518Sstevel sgen_state_t *sg_state;
11402518Sstevel int instance;
11412518Sstevel
11422518Sstevel instance = getminor(dev);
11432518Sstevel
11442518Sstevel if ((sg_state = ddi_get_soft_state(sgen_soft_state, instance)) == NULL)
11452518Sstevel return (ENXIO);
11462518Sstevel
11472518Sstevel sgen_log(sg_state, SGEN_DIAG2, "in sgen_ioctl(): instance = %d",
11482518Sstevel instance);
11492518Sstevel
11502518Sstevel /*
11512518Sstevel * If the driver has been suspended since the last open, fail all
11522518Sstevel * subsequent IO's so that the userland consumer reinitializes state.
11532518Sstevel */
11542518Sstevel mutex_enter(&sg_state->sgen_mutex);
11552518Sstevel if (SGEN_IS_SUSP(sg_state)) {
11562518Sstevel mutex_exit(&sg_state->sgen_mutex);
11572518Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_ioctl: returning EIO: "
11582518Sstevel "driver instance %d was previously suspended", instance);
11592518Sstevel return (EIO);
11602518Sstevel }
11612518Sstevel mutex_exit(&sg_state->sgen_mutex);
11622518Sstevel
11632518Sstevel switch (cmd) {
11642518Sstevel case SGEN_IOC_DIAG: {
11652518Sstevel if (arg > 3) {
11662518Sstevel arg = 0;
11672518Sstevel }
11682518Sstevel sg_state->sgen_diag = (int)arg;
11692518Sstevel retval = 0;
11702518Sstevel break;
11712518Sstevel }
11722518Sstevel
11732518Sstevel case SGEN_IOC_READY: {
11742518Sstevel if (sgen_tur(dev) != 0) {
11752518Sstevel retval = EIO;
11762518Sstevel } else {
11772518Sstevel retval = 0;
11782518Sstevel }
11792518Sstevel break;
11802518Sstevel }
11812518Sstevel
11823368Slh195018 case USCSICMD:
11833368Slh195018 retval = sgen_uscsi_cmd(dev, (struct uscsi_cmd *)arg, flag);
11843368Slh195018 break;
11852518Sstevel
11862518Sstevel default:
11872518Sstevel retval = ENOTTY;
11882518Sstevel }
11892518Sstevel
11902518Sstevel sgen_log(sg_state, SGEN_DIAG2, "done sgen_ioctl(), returning %d",
11912518Sstevel retval);
11922518Sstevel
11932518Sstevel return (retval);
11942518Sstevel }
11952518Sstevel
11962518Sstevel /*
11972518Sstevel * sgen_uscsi_cmd()
11982518Sstevel * Setup, configuration and teardown for a uscsi(7I) command
11992518Sstevel */
12002518Sstevel /*ARGSUSED*/
12012518Sstevel static int
sgen_uscsi_cmd(dev_t dev,struct uscsi_cmd * ucmd,int flag)12023368Slh195018 sgen_uscsi_cmd(dev_t dev, struct uscsi_cmd *ucmd, int flag)
12032518Sstevel {
12043368Slh195018 struct uscsi_cmd *uscmd;
12053368Slh195018 struct buf *bp;
12063368Slh195018 sgen_state_t *sg_state;
12073368Slh195018 enum uio_seg uioseg;
12083368Slh195018 int instance;
12093368Slh195018 int flags;
12103368Slh195018 int err;
12112518Sstevel
12122518Sstevel instance = getminor(dev);
12132518Sstevel
12142518Sstevel sg_state = ddi_get_soft_state(sgen_soft_state, instance);
12152518Sstevel ASSERT(sg_state);
12162518Sstevel
12172518Sstevel sgen_log(sg_state, SGEN_DIAG2, "in sgen_uscsi_cmd(): instance = %d",
12182518Sstevel instance);
12192518Sstevel
12206316Seschrock /*
12216316Seschrock * At this point, we start affecting state relevant to the target,
12226316Seschrock * so access needs to be serialized.
12236316Seschrock */
12246316Seschrock if (sgen_hold_cmdbuf(sg_state) != 0) {
12256316Seschrock sgen_log(sg_state, SGEN_DIAG1, "sgen_uscsi_cmd: interrupted");
12266316Seschrock return (EINTR);
12276316Seschrock }
12286316Seschrock
12293368Slh195018 err = scsi_uscsi_alloc_and_copyin((intptr_t)ucmd, flag,
12303368Slh195018 &sg_state->sgen_scsiaddr, &uscmd);
12313368Slh195018 if (err != 0) {
12326316Seschrock sgen_rele_cmdbuf(sg_state);
12333368Slh195018 sgen_log(sg_state, SGEN_DIAG1, "sgen_uscsi_cmd: "
12343368Slh195018 "scsi_uscsi_alloc_and_copyin failed\n");
12353368Slh195018 return (err);
12363368Slh195018 }
12372518Sstevel
12382518Sstevel /*
12392518Sstevel * Clear out undesirable command flags
12402518Sstevel */
12413368Slh195018 flags = (uscmd->uscsi_flags & ~(USCSI_NOINTR | USCSI_NOPARITY |
12422518Sstevel USCSI_OTAG | USCSI_HTAG | USCSI_HEAD));
12433368Slh195018 if (flags != uscmd->uscsi_flags) {
12442518Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_uscsi_cmd: cleared "
12453455Slh195018 "unsafe uscsi_flags 0x%x", uscmd->uscsi_flags & ~flags);
12463368Slh195018 uscmd->uscsi_flags = flags;
12472518Sstevel }
12482518Sstevel
12493368Slh195018 if (uscmd->uscsi_cdb != NULL) {
12503368Slh195018 sgen_dump_cdb(sg_state, "sgen_uscsi_cmd: ",
12513368Slh195018 (union scsi_cdb *)uscmd->uscsi_cdb, uscmd->uscsi_cdblen);
12522518Sstevel }
12532518Sstevel
12542518Sstevel /*
12552518Sstevel * Stash the sense buffer into sgen_rqs_sen for convenience.
12562518Sstevel */
12573368Slh195018 sg_state->sgen_rqs_sen = uscmd->uscsi_rqbuf;
12582518Sstevel
12593368Slh195018 bp = sg_state->sgen_cmdbuf;
12602518Sstevel bp->av_back = NULL;
12612518Sstevel bp->av_forw = NULL;
12623368Slh195018 bp->b_private = (struct buf *)uscmd;
12633368Slh195018 uioseg = (flag & FKIOCTL) ? UIO_SYSSPACE : UIO_USERSPACE;
12642518Sstevel
12653368Slh195018 err = scsi_uscsi_handle_cmd(dev, uioseg, uscmd, sgen_start, bp, NULL);
12662518Sstevel
12672518Sstevel if (sg_state->sgen_cmdpkt != NULL) {
12683368Slh195018 uscmd->uscsi_status = SCBP_C(sg_state->sgen_cmdpkt);
12692518Sstevel } else {
12703368Slh195018 uscmd->uscsi_status = 0;
12712518Sstevel }
12722518Sstevel
12732518Sstevel sgen_log(sg_state, SGEN_DIAG3, "sgen_uscsi_cmd: awake from waiting "
12743368Slh195018 "for command. Status is 0x%x", uscmd->uscsi_status);
12752518Sstevel
12763368Slh195018 if (uscmd->uscsi_rqbuf != NULL) {
12773368Slh195018 int rqlen = uscmd->uscsi_rqlen - uscmd->uscsi_rqresid;
12783368Slh195018 sgen_dump_sense(sg_state, rqlen,
12793368Slh195018 (uchar_t *)uscmd->uscsi_rqbuf);
12802518Sstevel }
12812518Sstevel
12823368Slh195018 (void) scsi_uscsi_copyout_and_free((intptr_t)ucmd, uscmd);
12833368Slh195018
12842518Sstevel if (sg_state->sgen_cmdpkt != NULL) {
12852518Sstevel scsi_destroy_pkt(sg_state->sgen_cmdpkt);
12862518Sstevel sg_state->sgen_cmdpkt = NULL;
12872518Sstevel }
12882518Sstevel
12892518Sstevel /*
12902518Sstevel * After this point, we can't touch per-target state.
12912518Sstevel */
12926316Seschrock sgen_rele_cmdbuf(sg_state);
12932518Sstevel
12942518Sstevel sgen_log(sg_state, SGEN_DIAG2, "done sgen_uscsi_cmd()");
12952518Sstevel
12962518Sstevel return (err);
12972518Sstevel }
12982518Sstevel
12992518Sstevel /*
13002518Sstevel * sgen_hold_cmdbuf()
13016316Seschrock * Acquire a lock on the command buffer for the given target. Returns
13026316Seschrock * non-zero if interrupted.
13032518Sstevel */
13046316Seschrock static int
sgen_hold_cmdbuf(sgen_state_t * sg_state)13052518Sstevel sgen_hold_cmdbuf(sgen_state_t *sg_state)
13062518Sstevel {
13072518Sstevel mutex_enter(&sg_state->sgen_mutex);
13086316Seschrock while (SGEN_IS_BUSY(sg_state)) {
13096316Seschrock if (!cv_wait_sig(&sg_state->sgen_cmdbuf_cv,
13106316Seschrock &sg_state->sgen_mutex)) {
13116316Seschrock mutex_exit(&sg_state->sgen_mutex);
13126316Seschrock return (-1);
13136316Seschrock }
13146316Seschrock }
13152518Sstevel SGEN_SET_BUSY(sg_state);
13162518Sstevel mutex_exit(&sg_state->sgen_mutex);
13176316Seschrock return (0);
13182518Sstevel }
13192518Sstevel
13202518Sstevel /*
13212518Sstevel * sgen_rele_cmdbuf()
13222518Sstevel * release the command buffer for a particular target.
13232518Sstevel */
13242518Sstevel static void
sgen_rele_cmdbuf(sgen_state_t * sg_state)13252518Sstevel sgen_rele_cmdbuf(sgen_state_t *sg_state)
13262518Sstevel {
13272518Sstevel mutex_enter(&sg_state->sgen_mutex);
13282518Sstevel SGEN_CLR_BUSY(sg_state);
13292518Sstevel cv_signal(&sg_state->sgen_cmdbuf_cv);
13302518Sstevel mutex_exit(&sg_state->sgen_mutex);
13312518Sstevel }
13322518Sstevel
13332518Sstevel /*
13342518Sstevel * sgen_start()
13352518Sstevel * Transport a uscsi command; this is invoked by physio() or directly
13362518Sstevel * by sgen_uscsi_cmd().
13372518Sstevel */
13382518Sstevel static int
sgen_start(struct buf * bp)13392518Sstevel sgen_start(struct buf *bp)
13402518Sstevel {
13412518Sstevel sgen_state_t *sg_state;
13422518Sstevel dev_t dev = bp->b_edev;
13432518Sstevel int trans_err;
13442518Sstevel
13452518Sstevel if ((sg_state = ddi_get_soft_state(sgen_soft_state,
13462518Sstevel getminor(dev))) == NULL) {
13472518Sstevel bp->b_resid = bp->b_bcount;
13482518Sstevel bioerror(bp, ENXIO);
13492518Sstevel biodone(bp);
13502518Sstevel return (ENXIO);
13512518Sstevel }
13522518Sstevel
13532518Sstevel /*
13542518Sstevel * Sanity checks - command should not be complete, no packet should
13552518Sstevel * be allocated, and there ought to be a uscsi cmd in b_private
13562518Sstevel */
13572518Sstevel ASSERT(bp == sg_state->sgen_cmdbuf && sg_state->sgen_cmdpkt == NULL);
13582518Sstevel ASSERT((bp->b_flags & B_DONE) == 0);
13592518Sstevel ASSERT(bp->b_private);
13602518Sstevel if (sgen_make_uscsi_cmd(sg_state, bp) != 0) {
13612518Sstevel bp->b_resid = bp->b_bcount;
13622518Sstevel bioerror(bp, EFAULT);
13632518Sstevel biodone(bp);
13642518Sstevel return (EFAULT);
13652518Sstevel }
13662518Sstevel
13672518Sstevel ASSERT(sg_state->sgen_cmdpkt != NULL);
13682518Sstevel
13692518Sstevel /*
13702518Sstevel * Clear out the residual and error fields
13712518Sstevel */
13722518Sstevel bp->b_resid = 0;
13732518Sstevel bp->b_error = 0;
13742518Sstevel
13752518Sstevel trans_err = sgen_scsi_transport(sg_state->sgen_cmdpkt);
13762518Sstevel switch (trans_err) {
13772518Sstevel case TRAN_ACCEPT:
13782518Sstevel break;
13792518Sstevel case TRAN_BUSY:
13802518Sstevel sgen_log(sg_state, SGEN_DIAG2,
13812518Sstevel "sgen_start: scsi_transport() returned TRAN_BUSY");
13822518Sstevel sg_state->sgen_restart_timeid = timeout(sgen_restart, sg_state,
13832518Sstevel SGEN_BSY_TIMEOUT);
13842518Sstevel break;
13852518Sstevel default:
13862518Sstevel /*
13872518Sstevel * Indicate there has been an I/O transfer error.
13882518Sstevel * Be done with the command.
13892518Sstevel */
13902518Sstevel mutex_enter(&sg_state->sgen_mutex);
13912518Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_trans_err);
13922518Sstevel mutex_exit(&sg_state->sgen_mutex);
13932518Sstevel sgen_log(sg_state, SGEN_DIAG2, "sgen_start: scsi_transport() "
13942518Sstevel "returned %d", trans_err);
13952518Sstevel bioerror(bp, EIO);
13962518Sstevel biodone(bp);
13972518Sstevel return (EIO);
13982518Sstevel }
13992518Sstevel sgen_log(sg_state, SGEN_DIAG2, "sgen_start: b_flags 0x%x", bp->b_flags);
14002518Sstevel return (0);
14012518Sstevel }
14022518Sstevel
14032518Sstevel /*
14042518Sstevel * sgen_scsi_transport()
14052518Sstevel * a simple scsi_transport() wrapper which can be configured to inject
14062518Sstevel * sporadic errors for testing.
14072518Sstevel */
14082518Sstevel static int
sgen_scsi_transport(struct scsi_pkt * pkt)14092518Sstevel sgen_scsi_transport(struct scsi_pkt *pkt)
14102518Sstevel {
14112518Sstevel int trans_err;
14122518Sstevel static int cnt = 0;
14132518Sstevel sgen_state_t *sg_state = pkt->pkt_private;
14142518Sstevel
14152518Sstevel if (sgen_sporadic_failures == 0) {
14162518Sstevel return (scsi_transport(pkt));
14172518Sstevel }
14182518Sstevel
14192518Sstevel cnt = (cnt * 2416 + 374441) % 1771875; /* borrowed from kmem.c */
14202518Sstevel if (cnt % 40 == 1) {
14212518Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_scsi_transport: "
14222518Sstevel "injecting sporadic BUSY");
14232518Sstevel trans_err = TRAN_BUSY;
14242518Sstevel } else if (cnt % 40 == 2) {
14252518Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_scsi_transport: "
14262518Sstevel "injecting sporadic BADPKT");
14272518Sstevel trans_err = TRAN_BADPKT;
14282518Sstevel } else {
14292518Sstevel /*
14302518Sstevel * Most of the time we take the normal path
14312518Sstevel */
14322518Sstevel trans_err = scsi_transport(pkt);
14332518Sstevel }
14342518Sstevel return (trans_err);
14352518Sstevel }
14362518Sstevel
14372518Sstevel /*
14382518Sstevel * sgen_make_uscsi_cmd()
14392518Sstevel * Initialize a SCSI packet usable for USCSI.
14402518Sstevel */
14412518Sstevel static int
sgen_make_uscsi_cmd(sgen_state_t * sg_state,struct buf * bp)14422518Sstevel sgen_make_uscsi_cmd(sgen_state_t *sg_state, struct buf *bp)
14432518Sstevel {
14442518Sstevel struct scsi_pkt *pkt;
14452518Sstevel struct uscsi_cmd *ucmd;
14465597Slh195018 int stat_size = 1;
14475597Slh195018 int flags = 0;
14482518Sstevel
14492518Sstevel ASSERT(bp);
14502518Sstevel
14512518Sstevel sgen_log(sg_state, SGEN_DIAG2, "in sgen_make_uscsi_cmd()");
14522518Sstevel
14532518Sstevel ucmd = (struct uscsi_cmd *)bp->b_private;
14542518Sstevel
14552518Sstevel if (ucmd->uscsi_flags & USCSI_RQENABLE) {
14565597Slh195018 if (ucmd->uscsi_rqlen > SENSE_LENGTH) {
14575597Slh195018 stat_size = (int)(ucmd->uscsi_rqlen) +
14585597Slh195018 sizeof (struct scsi_arq_status) -
14595597Slh195018 sizeof (struct scsi_extended_sense);
14605597Slh195018 flags = PKT_XARQ;
14615597Slh195018 } else {
14625597Slh195018 stat_size = sizeof (struct scsi_arq_status);
14635597Slh195018 }
14642518Sstevel }
14652518Sstevel
14662518Sstevel sgen_log(sg_state, SGEN_DIAG3, "sgen_make_uscsi_cmd: b_bcount = %ld",
14672518Sstevel bp->b_bcount);
14682518Sstevel pkt = scsi_init_pkt(&sg_state->sgen_scsiaddr,
14692518Sstevel NULL, /* in_pkt - null so it'll be alloc'd */
14702518Sstevel bp->b_bcount ? bp : NULL, /* buf structure for data xfer */
14712518Sstevel ucmd->uscsi_cdblen, /* cmdlen */
14722518Sstevel stat_size, /* statuslen */
14732518Sstevel 0, /* privatelen */
14745597Slh195018 flags, /* flags */
14752518Sstevel SLEEP_FUNC, /* callback */
14762518Sstevel (caddr_t)sg_state); /* callback_arg */
14772518Sstevel
14782518Sstevel if (pkt == NULL) {
14792518Sstevel sgen_log(sg_state, SGEN_DIAG2, "failed sgen_make_uscsi_cmd()");
14802518Sstevel return (-1);
14812518Sstevel }
14822518Sstevel
14832518Sstevel pkt->pkt_comp = sgen_callback;
14842518Sstevel pkt->pkt_private = sg_state;
14852518Sstevel sg_state->sgen_cmdpkt = pkt;
14862518Sstevel
14872518Sstevel /*
14882518Sstevel * We *don't* call scsi_setup_cdb here, as is customary, since the
14892518Sstevel * user could specify a command from one group, but pass cdblen
14902518Sstevel * as something totally different. If cdblen is smaller than expected,
14912518Sstevel * this results in scsi_setup_cdb writing past the end of the cdb.
14922518Sstevel */
14932518Sstevel bcopy(ucmd->uscsi_cdb, pkt->pkt_cdbp, ucmd->uscsi_cdblen);
14942518Sstevel if (ucmd->uscsi_cdblen >= CDB_GROUP0) {
14952518Sstevel FILL_SCSI1_LUN(sg_state->sgen_scsidev, pkt);
14962518Sstevel }
14972518Sstevel
14982518Sstevel if (ucmd->uscsi_timeout > 0)
14992518Sstevel pkt->pkt_time = ucmd->uscsi_timeout;
15002518Sstevel else
15012518Sstevel pkt->pkt_time = SGEN_IO_TIME;
15022518Sstevel
15032518Sstevel /*
15042518Sstevel * Set packet options
15052518Sstevel */
15062518Sstevel if (ucmd->uscsi_flags & USCSI_SILENT)
15072518Sstevel pkt->pkt_flags |= FLAG_SILENT;
15082518Sstevel if (ucmd->uscsi_flags & USCSI_ISOLATE)
15092518Sstevel pkt->pkt_flags |= FLAG_ISOLATE;
15102518Sstevel if (ucmd->uscsi_flags & USCSI_DIAGNOSE)
15112518Sstevel pkt->pkt_flags |= FLAG_DIAGNOSE;
15122518Sstevel if (ucmd->uscsi_flags & USCSI_RENEGOT) {
15132518Sstevel pkt->pkt_flags |= FLAG_RENEGOTIATE_WIDE_SYNC;
15142518Sstevel }
15152518Sstevel
15166640Scth /* Transfer uscsi information to scsi_pkt */
15176640Scth (void) scsi_uscsi_pktinit(ucmd, pkt);
15186640Scth
15192518Sstevel sgen_log(sg_state, SGEN_DIAG2, "done sgen_make_uscsi_cmd()");
15202518Sstevel return (0);
15212518Sstevel }
15222518Sstevel
15232518Sstevel
15242518Sstevel /*
15252518Sstevel * sgen_restart()
15262518Sstevel * sgen_restart() is called after a timeout, when a command has been
15272518Sstevel * postponed due to a TRAN_BUSY response from the HBA.
15282518Sstevel */
15292518Sstevel static void
sgen_restart(void * arg)15302518Sstevel sgen_restart(void *arg)
15312518Sstevel {
15322518Sstevel sgen_state_t *sg_state = (sgen_state_t *)arg;
15332518Sstevel struct scsi_pkt *pkt;
15342518Sstevel struct buf *bp;
15352518Sstevel
15362518Sstevel sgen_log(sg_state, SGEN_DIAG2, "in sgen_restart()");
15372518Sstevel
15382518Sstevel bp = sg_state->sgen_cmdbuf;
15392518Sstevel pkt = sg_state->sgen_cmdpkt;
15402518Sstevel ASSERT(bp && pkt);
15412518Sstevel
15422518Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_restart);
15432518Sstevel
15442518Sstevel /*
15452518Sstevel * If the packet is marked with the sensing flag, sgen is off running
15462518Sstevel * a request sense, and *that packet* is what needs to be restarted.
15472518Sstevel */
15482518Sstevel if (pkt->pkt_flags & FLAG_SENSING) {
15492518Sstevel sgen_log(sg_state, SGEN_DIAG3,
15502518Sstevel "sgen_restart: restarting REQUEST SENSE");
15512518Sstevel pkt = sg_state->sgen_rqspkt;
15522518Sstevel }
15532518Sstevel
15542518Sstevel if (sgen_scsi_transport(pkt) != TRAN_ACCEPT) {
15552518Sstevel bp->b_resid = bp->b_bcount;
15562518Sstevel bioerror(bp, EIO);
15572518Sstevel biodone(bp);
15582518Sstevel }
15592518Sstevel }
15602518Sstevel
15612518Sstevel /*
15622518Sstevel * sgen_callback()
15632518Sstevel * Command completion processing
15642518Sstevel *
15652518Sstevel * sgen's completion processing is very pessimistic-- it does not retry
15662518Sstevel * failed commands; instead, it allows the user application to make
15672518Sstevel * decisions about what has gone wrong.
15682518Sstevel */
15692518Sstevel static void
sgen_callback(struct scsi_pkt * pkt)15702518Sstevel sgen_callback(struct scsi_pkt *pkt)
15712518Sstevel {
15722518Sstevel sgen_state_t *sg_state;
15736640Scth struct uscsi_cmd *ucmd;
15742518Sstevel struct buf *bp;
15752518Sstevel int action;
15762518Sstevel
15772518Sstevel sg_state = pkt->pkt_private;
15782518Sstevel /*
15792518Sstevel * bp should always be the command buffer regardless of whether
15802518Sstevel * this is a command completion or a request-sense completion.
15812518Sstevel * This is because there is no need to biodone() the sense buf
15822518Sstevel * when it completes-- we want to biodone() the actual command buffer!
15832518Sstevel */
15842518Sstevel bp = sg_state->sgen_cmdbuf;
15852518Sstevel if (pkt->pkt_flags & FLAG_SENSING) {
15862518Sstevel ASSERT(pkt == sg_state->sgen_rqspkt);
15872518Sstevel sgen_log(sg_state, SGEN_DIAG2,
15882518Sstevel "in sgen_callback() (SENSE completion callback)");
15892518Sstevel } else {
15902518Sstevel ASSERT(pkt == sg_state->sgen_cmdpkt);
15912518Sstevel sgen_log(sg_state, SGEN_DIAG2,
15922518Sstevel "in sgen_callback() (command completion callback)");
15932518Sstevel }
15946640Scth ucmd = (struct uscsi_cmd *)bp->b_private;
15952518Sstevel
15962518Sstevel sgen_log(sg_state, SGEN_DIAG3, "sgen_callback: reason=0x%x resid=%ld "
15972518Sstevel "state=0x%x", pkt->pkt_reason, pkt->pkt_resid, pkt->pkt_state);
15982518Sstevel
15996640Scth /* Transfer scsi_pkt information to uscsi */
16006640Scth (void) scsi_uscsi_pktfini(pkt, ucmd);
16016640Scth
16022518Sstevel if (pkt->pkt_reason != CMD_CMPLT) {
16032518Sstevel /*
16042518Sstevel * The command did not complete.
16052518Sstevel */
16062518Sstevel sgen_log(sg_state, SGEN_DIAG3,
16072518Sstevel "sgen_callback: command did not complete");
16082518Sstevel action = sgen_handle_incomplete(sg_state, pkt);
16092518Sstevel } else if (sg_state->sgen_arq_enabled &&
16102518Sstevel (pkt->pkt_state & STATE_ARQ_DONE)) {
16112518Sstevel /*
16122518Sstevel * The auto-rqsense happened, and the packet has a filled-in
16132518Sstevel * scsi_arq_status structure, pointed to by pkt_scbp.
16142518Sstevel */
16152518Sstevel sgen_log(sg_state, SGEN_DIAG3,
16162518Sstevel "sgen_callback: received auto-requested sense");
16172518Sstevel action = sgen_handle_autosense(sg_state, pkt);
16182518Sstevel ASSERT(action != FETCH_SENSE);
16192518Sstevel } else if (pkt->pkt_flags & FLAG_SENSING) {
16202518Sstevel /*
16212518Sstevel * sgen was running a REQUEST SENSE. Decode the sense data and
16222518Sstevel * decide what to do next.
16232518Sstevel *
16242518Sstevel * Clear FLAG_SENSING on the original packet for completeness.
16252518Sstevel */
16262518Sstevel sgen_log(sg_state, SGEN_DIAG3, "sgen_callback: received sense");
16272518Sstevel sg_state->sgen_cmdpkt->pkt_flags &= ~FLAG_SENSING;
16282518Sstevel action = sgen_handle_sense(sg_state);
16292518Sstevel ASSERT(action != FETCH_SENSE);
16302518Sstevel } else {
16312518Sstevel /*
16322518Sstevel * Command completed and we're not getting sense. Check for
16332518Sstevel * errors and decide what to do next.
16342518Sstevel */
16352518Sstevel sgen_log(sg_state, SGEN_DIAG3,
16362518Sstevel "sgen_callback: command appears complete");
16372518Sstevel action = sgen_check_error(sg_state, bp);
16382518Sstevel }
16392518Sstevel
16402518Sstevel switch (action) {
16412518Sstevel case FETCH_SENSE:
16422518Sstevel /*
16432518Sstevel * If there is sense to fetch, break out to prevent biodone'ing
16442518Sstevel * until the sense fetch is complete.
16452518Sstevel */
16466640Scth if (sgen_initiate_sense(sg_state,
16476640Scth scsi_pkt_allocated_correctly(pkt) ?
16486640Scth pkt->pkt_path_instance : 0) == 0)
16492518Sstevel break;
16502518Sstevel /*FALLTHROUGH*/
16512518Sstevel case COMMAND_DONE_ERROR:
16522518Sstevel bp->b_resid = bp->b_bcount;
16532518Sstevel bioerror(bp, EIO);
16542518Sstevel /*FALLTHROUGH*/
16552518Sstevel case COMMAND_DONE:
16562518Sstevel biodone(bp);
16572518Sstevel break;
16582518Sstevel default:
16592518Sstevel ASSERT(0);
16602518Sstevel break;
16612518Sstevel }
16622518Sstevel
16632518Sstevel sgen_log(sg_state, SGEN_DIAG2, "done sgen_callback()");
16642518Sstevel }
16652518Sstevel
16662518Sstevel /*
16672518Sstevel * sgen_initiate_sense()
16682518Sstevel * Send the sgen_rqspkt to the target, thereby requesting sense data.
16692518Sstevel */
16702518Sstevel static int
sgen_initiate_sense(sgen_state_t * sg_state,int path_instance)16716640Scth sgen_initiate_sense(sgen_state_t *sg_state, int path_instance)
16722518Sstevel {
16736640Scth /* use same path_instance as command */
16746640Scth if (scsi_pkt_allocated_correctly(sg_state->sgen_rqspkt))
16756640Scth sg_state->sgen_rqspkt->pkt_path_instance = path_instance;
16766640Scth
16772518Sstevel switch (sgen_scsi_transport(sg_state->sgen_rqspkt)) {
16782518Sstevel case TRAN_ACCEPT:
16792518Sstevel sgen_log(sg_state, SGEN_DIAG3, "sgen_initiate_sense: "
16802518Sstevel "sense fetch transport accepted.");
16812518Sstevel return (0);
16822518Sstevel case TRAN_BUSY:
16832518Sstevel sgen_log(sg_state, SGEN_DIAG2, "sgen_initiate_sense: "
16842518Sstevel "sense fetch transport busy, setting timeout.");
16852518Sstevel sg_state->sgen_restart_timeid = timeout(sgen_restart, sg_state,
16862518Sstevel SGEN_BSY_TIMEOUT);
16872518Sstevel return (0);
16882518Sstevel default:
16892518Sstevel sgen_log(sg_state, SGEN_DIAG2, "sgen_initiate_sense: "
16902518Sstevel "sense fetch transport failed or busy.");
16912518Sstevel return (-1);
16922518Sstevel }
16932518Sstevel }
16942518Sstevel
16952518Sstevel /*
16962518Sstevel * sgen_handle_incomplete()
16972518Sstevel * sgen is pessimistic, but also careful-- it doesn't try to retry
16982518Sstevel * incomplete commands, but it also doesn't go resetting devices;
16992518Sstevel * it is hard to tell if the device will be tolerant of that sort
17002518Sstevel * of prodding.
17012518Sstevel *
17022518Sstevel * This routine has been left as a guide for the future--- the
17032518Sstevel * current administration's hands-off policy may need modification.
17042518Sstevel */
17052518Sstevel /*ARGSUSED*/
17062518Sstevel static int
sgen_handle_incomplete(sgen_state_t * sg_state,struct scsi_pkt * pkt)17072518Sstevel sgen_handle_incomplete(sgen_state_t *sg_state, struct scsi_pkt *pkt)
17082518Sstevel {
17092518Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_incmp_err);
17102518Sstevel return (COMMAND_DONE_ERROR);
17112518Sstevel }
17122518Sstevel
17132518Sstevel /*
17142518Sstevel * sgen_handle_autosense()
17152518Sstevel * Deal with SENSE data acquired automatically via the auto-request-sense
17162518Sstevel * facility.
17172518Sstevel *
17182518Sstevel * Sgen takes a pessimistic view of things-- it doesn't retry commands,
17192518Sstevel * and unless the device recovered from the problem, this routine returns
17202518Sstevel * COMMAND_DONE_ERROR.
17212518Sstevel */
17222518Sstevel static int
sgen_handle_autosense(sgen_state_t * sg_state,struct scsi_pkt * pkt)17232518Sstevel sgen_handle_autosense(sgen_state_t *sg_state, struct scsi_pkt *pkt)
17242518Sstevel {
17252518Sstevel struct scsi_arq_status *arqstat;
17262518Sstevel struct uscsi_cmd *ucmd =
17272518Sstevel (struct uscsi_cmd *)sg_state->sgen_cmdbuf->b_private;
17282518Sstevel int amt;
17292518Sstevel
17302518Sstevel arqstat = (struct scsi_arq_status *)(pkt->pkt_scbp);
17312518Sstevel
17322518Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_autosen_rcv);
17332518Sstevel
17342518Sstevel if (arqstat->sts_rqpkt_reason != CMD_CMPLT) {
17352518Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_autosense: ARQ"
17362518Sstevel "failed to complete.");
17372518Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_autosen_bad);
17382518Sstevel return (COMMAND_DONE_ERROR);
17392518Sstevel }
17402518Sstevel
17415597Slh195018 if (pkt->pkt_state & STATE_XARQ_DONE) {
17425597Slh195018 amt = MAX_SENSE_LENGTH - arqstat->sts_rqpkt_resid;
17435597Slh195018 } else {
17445597Slh195018 if (arqstat->sts_rqpkt_resid > SENSE_LENGTH) {
17455597Slh195018 amt = MAX_SENSE_LENGTH - arqstat->sts_rqpkt_resid;
17465597Slh195018 } else {
17475597Slh195018 amt = SENSE_LENGTH - arqstat->sts_rqpkt_resid;
17485597Slh195018 }
17495597Slh195018 }
17505597Slh195018
17512518Sstevel if (ucmd->uscsi_flags & USCSI_RQENABLE) {
17522518Sstevel ucmd->uscsi_rqstatus = *((char *)&arqstat->sts_rqpkt_status);
17535597Slh195018 uchar_t rqlen = min((uchar_t)amt, ucmd->uscsi_rqlen);
17545597Slh195018 ucmd->uscsi_rqresid = ucmd->uscsi_rqlen - rqlen;
17552518Sstevel ASSERT(ucmd->uscsi_rqlen && sg_state->sgen_rqs_sen);
17565597Slh195018 bcopy(&(arqstat->sts_sensedata), sg_state->sgen_rqs_sen, rqlen);
17572518Sstevel sgen_log(sg_state, SGEN_DIAG2, "sgen_handle_autosense: "
17582518Sstevel "uscsi_rqstatus=0x%x uscsi_rqresid=%d\n",
17592518Sstevel ucmd->uscsi_rqstatus, ucmd->uscsi_rqresid);
17602518Sstevel }
17612518Sstevel
17622518Sstevel if (arqstat->sts_rqpkt_status.sts_chk) {
17632518Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_autosense: got "
17642518Sstevel "check condition on auto request sense!");
17652518Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_autosen_bad);
17662518Sstevel return (COMMAND_DONE_ERROR);
17672518Sstevel }
17682518Sstevel
17692518Sstevel if (((arqstat->sts_rqpkt_state & STATE_XFERRED_DATA) == 0) ||
17702518Sstevel (amt == 0)) {
17712518Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_autosense: got "
17722518Sstevel "auto-sense, but it contains no data!");
17732518Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_autosen_bad);
17742518Sstevel return (COMMAND_DONE_ERROR);
17752518Sstevel }
17762518Sstevel
17772518Sstevel /*
17782518Sstevel * Stuff the sense data pointer into sgen_sense for later retrieval
17792518Sstevel */
17802518Sstevel sg_state->sgen_sense = &arqstat->sts_sensedata;
17812518Sstevel
17822518Sstevel /*
17832518Sstevel * Now, check to see whether we got enough sense data to make any
17842518Sstevel * sense out if it (heh-heh).
17852518Sstevel */
17862518Sstevel if (amt < SUN_MIN_SENSE_LENGTH) {
17872518Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_autosense: not "
17882518Sstevel "enough auto sense data");
17892518Sstevel return (COMMAND_DONE_ERROR);
17902518Sstevel }
17912518Sstevel
17922518Sstevel switch (arqstat->sts_sensedata.es_key) {
17932518Sstevel case KEY_RECOVERABLE_ERROR:
17942518Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_recov_err);
17952518Sstevel break;
17962518Sstevel case KEY_NO_SENSE:
17972518Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_nosen_err);
17982518Sstevel break;
17992518Sstevel default:
18002518Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_unrecov_err);
18012518Sstevel break;
18022518Sstevel }
18032518Sstevel
18042518Sstevel return (COMMAND_DONE);
18052518Sstevel }
18062518Sstevel
18072518Sstevel /*
18082518Sstevel * sgen_handle_sense()
18092518Sstevel * Examine sense data that was manually fetched from the target.
18102518Sstevel */
18112518Sstevel static int
sgen_handle_sense(sgen_state_t * sg_state)18122518Sstevel sgen_handle_sense(sgen_state_t *sg_state)
18132518Sstevel {
18142518Sstevel struct scsi_pkt *rqpkt = sg_state->sgen_rqspkt;
18152518Sstevel struct scsi_status *rqstatus = (struct scsi_status *)rqpkt->pkt_scbp;
18162518Sstevel struct uscsi_cmd *ucmd =
18172518Sstevel (struct uscsi_cmd *)sg_state->sgen_cmdbuf->b_private;
18182518Sstevel int amt;
18192518Sstevel
18202518Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_sense_rcv);
18212518Sstevel
18225597Slh195018 amt = MAX_SENSE_LENGTH - rqpkt->pkt_resid;
18235597Slh195018
18242518Sstevel if (ucmd->uscsi_flags & USCSI_RQENABLE) {
18252518Sstevel ucmd->uscsi_rqstatus = *((char *)rqstatus);
18265597Slh195018 uchar_t rqlen = min((uchar_t)amt, ucmd->uscsi_rqlen);
18275597Slh195018 ucmd->uscsi_rqresid = ucmd->uscsi_rqlen - rqlen;
18282518Sstevel ASSERT(ucmd->uscsi_rqlen && sg_state->sgen_rqs_sen);
18295597Slh195018 bcopy(sg_state->sgen_sense, sg_state->sgen_rqs_sen, rqlen);
18302518Sstevel sgen_log(sg_state, SGEN_DIAG2, "sgen_handle_sense: "
18312518Sstevel "uscsi_rqstatus=0x%x uscsi_rqresid=%d\n",
18322518Sstevel ucmd->uscsi_rqstatus, ucmd->uscsi_rqresid);
18332518Sstevel }
18342518Sstevel
18352518Sstevel if (rqstatus->sts_busy) {
18362518Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_sense: got busy "
18372518Sstevel "on request sense");
18382518Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_sense_bad);
18392518Sstevel return (COMMAND_DONE_ERROR);
18402518Sstevel }
18412518Sstevel
18422518Sstevel if (rqstatus->sts_chk) {
18432518Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_sense: got check "
18442518Sstevel "condition on request sense!");
18452518Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_sense_bad);
18462518Sstevel return (COMMAND_DONE_ERROR);
18472518Sstevel }
18482518Sstevel
18492518Sstevel if ((rqpkt->pkt_state & STATE_XFERRED_DATA) == 0 || amt == 0) {
18502518Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_sense: got "
18512518Sstevel "sense, but it contains no data");
18522518Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_sense_bad);
18532518Sstevel return (COMMAND_DONE_ERROR);
18542518Sstevel }
18552518Sstevel
18562518Sstevel /*
18572518Sstevel * Now, check to see whether we got enough sense data to make any
18582518Sstevel * sense out if it (heh-heh).
18592518Sstevel */
18602518Sstevel if (amt < SUN_MIN_SENSE_LENGTH) {
18612518Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_sense: not "
18622518Sstevel "enough sense data");
18632518Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_sense_bad);
18642518Sstevel return (COMMAND_DONE_ERROR);
18652518Sstevel }
18662518Sstevel
18672518Sstevel /*
18682518Sstevel * Decode the sense data-- this was deposited here for us by the
18692518Sstevel * setup in sgen_do_attach(). (note that sgen_sense is an alias for
18702518Sstevel * the sd_sense field in the scsi_device).
18712518Sstevel */
18722518Sstevel sgen_log(sg_state, SGEN_DIAG1, "Sense key is %s [0x%x]",
18732518Sstevel scsi_sname(sg_state->sgen_sense->es_key),
18742518Sstevel sg_state->sgen_sense->es_key);
18752518Sstevel switch (sg_state->sgen_sense->es_key) {
18762518Sstevel case KEY_RECOVERABLE_ERROR:
18772518Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_recov_err);
18782518Sstevel break;
18792518Sstevel case KEY_NO_SENSE:
18802518Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_nosen_err);
18812518Sstevel break;
18822518Sstevel default:
18832518Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_unrecov_err);
18842518Sstevel break;
18852518Sstevel }
18862518Sstevel
18872518Sstevel return (COMMAND_DONE);
18882518Sstevel }
18892518Sstevel
18902518Sstevel /*
18912518Sstevel * sgen_check_error()
18922518Sstevel * examine the command packet for abnormal completion.
18932518Sstevel *
18942518Sstevel * sgen_check_error should only be called at the completion of the
18952518Sstevel * command packet.
18962518Sstevel */
18972518Sstevel static int
sgen_check_error(sgen_state_t * sg_state,struct buf * bp)18982518Sstevel sgen_check_error(sgen_state_t *sg_state, struct buf *bp)
18992518Sstevel {
19002518Sstevel struct scsi_pkt *pkt = sg_state->sgen_cmdpkt;
19012518Sstevel struct scsi_status *status = (struct scsi_status *)pkt->pkt_scbp;
19022518Sstevel struct uscsi_cmd *ucmd =
19032518Sstevel (struct uscsi_cmd *)sg_state->sgen_cmdbuf->b_private;
19042518Sstevel
19052518Sstevel if (status->sts_busy) {
19062518Sstevel sgen_log(sg_state, SGEN_DIAG1,
19072518Sstevel "sgen_check_error: target is busy");
19082518Sstevel return (COMMAND_DONE_ERROR);
19092518Sstevel }
19102518Sstevel
19112518Sstevel /*
19122518Sstevel * pkt_resid will reflect, at this point, a residual of how many bytes
19132518Sstevel * were not transferred; a non-zero pkt_resid is an error.
19142518Sstevel */
19152518Sstevel if (pkt->pkt_resid) {
19162518Sstevel bp->b_resid += pkt->pkt_resid;
19172518Sstevel }
19182518Sstevel
19192518Sstevel if (status->sts_chk) {
19202518Sstevel if (ucmd->uscsi_flags & USCSI_RQENABLE) {
19212518Sstevel if (sg_state->sgen_arq_enabled) {
19222518Sstevel sgen_log(sg_state, SGEN_DIAG1,
19232518Sstevel "sgen_check_error: strange: target "
19242518Sstevel "indicates CHECK CONDITION with auto-sense "
19252518Sstevel "enabled.");
19262518Sstevel }
19272518Sstevel sgen_log(sg_state, SGEN_DIAG2, "sgen_check_error: "
19282518Sstevel "target ready for sense fetch");
19292518Sstevel return (FETCH_SENSE);
19302518Sstevel } else {
19312518Sstevel sgen_log(sg_state, SGEN_DIAG2, "sgen_check_error: "
19322518Sstevel "target indicates CHECK CONDITION");
19332518Sstevel }
19342518Sstevel }
19352518Sstevel
19362518Sstevel return (COMMAND_DONE);
19372518Sstevel }
19382518Sstevel
19392518Sstevel /*
19402518Sstevel * sgen_tur()
19412518Sstevel * test if a target is ready to operate by sending it a TUR command.
19422518Sstevel */
19432518Sstevel static int
sgen_tur(dev_t dev)19442518Sstevel sgen_tur(dev_t dev)
19452518Sstevel {
19462518Sstevel char cmdblk[CDB_GROUP0];
19472518Sstevel struct uscsi_cmd scmd;
19482518Sstevel
19492518Sstevel bzero(&scmd, sizeof (scmd));
19502518Sstevel scmd.uscsi_bufaddr = 0;
19512518Sstevel scmd.uscsi_buflen = 0;
19522518Sstevel bzero(cmdblk, CDB_GROUP0);
19532518Sstevel cmdblk[0] = (char)SCMD_TEST_UNIT_READY;
19542518Sstevel scmd.uscsi_flags = USCSI_DIAGNOSE | USCSI_SILENT | USCSI_WRITE;
19552518Sstevel scmd.uscsi_cdb = cmdblk;
19562518Sstevel scmd.uscsi_cdblen = CDB_GROUP0;
19572518Sstevel
19583368Slh195018 return (sgen_uscsi_cmd(dev, &scmd, FKIOCTL));
19592518Sstevel }
19602518Sstevel
19612518Sstevel /*
19622518Sstevel * sgen_diag_ok()
19632518Sstevel * given an sg_state and a desired diagnostic level, return true if
19642518Sstevel * it is acceptable to output a message.
19652518Sstevel */
19662518Sstevel /*ARGSUSED*/
19672518Sstevel static int
sgen_diag_ok(sgen_state_t * sg_state,int level)19682518Sstevel sgen_diag_ok(sgen_state_t *sg_state, int level)
19692518Sstevel {
19702518Sstevel int diag_lvl;
19712518Sstevel
19722518Sstevel switch (level) {
19732518Sstevel case CE_WARN:
19742518Sstevel case CE_NOTE:
19752518Sstevel case CE_CONT:
19762518Sstevel case CE_PANIC:
19772518Sstevel return (1);
19782518Sstevel case SGEN_DIAG1:
19792518Sstevel case SGEN_DIAG2:
19802518Sstevel case SGEN_DIAG3:
19812518Sstevel if (sg_state) {
19822518Sstevel /*
19832518Sstevel * Check to see if user overrode the diagnostics level
19842518Sstevel * for this instance (either via SGEN_IOC_DIAG or via
19852518Sstevel * .conf file). If not, fall back to the global diag
19862518Sstevel * level.
19872518Sstevel */
19882518Sstevel if (sg_state->sgen_diag != -1)
19892518Sstevel diag_lvl = sg_state->sgen_diag;
19902518Sstevel else
19912518Sstevel diag_lvl = sgen_diag;
19922518Sstevel } else {
19932518Sstevel diag_lvl = sgen_diag;
19942518Sstevel }
19952518Sstevel if (((diag_lvl << 8) | CE_CONT) >= level) {
19962518Sstevel return (1);
19972518Sstevel } else {
19982518Sstevel return (0);
19992518Sstevel }
20002518Sstevel default:
20012518Sstevel return (1);
20022518Sstevel }
20032518Sstevel }
20042518Sstevel
20052518Sstevel /*PRINTFLIKE3*/
20062518Sstevel static void
sgen_log(sgen_state_t * sg_state,int level,const char * fmt,...)20072518Sstevel sgen_log(sgen_state_t *sg_state, int level, const char *fmt, ...)
20082518Sstevel {
20092518Sstevel va_list ap;
20102518Sstevel char buf[256];
20112518Sstevel
20122518Sstevel if (!sgen_diag_ok(sg_state, level))
20132518Sstevel return;
20142518Sstevel
20152518Sstevel va_start(ap, fmt);
20162518Sstevel (void) vsnprintf(buf, sizeof (buf), fmt, ap);
20172518Sstevel va_end(ap);
20182518Sstevel
20192518Sstevel switch (level) {
20202518Sstevel case CE_NOTE:
20212518Sstevel case CE_CONT:
20222518Sstevel case CE_WARN:
20232518Sstevel case CE_PANIC:
20242518Sstevel if (sg_state == (sgen_state_t *)NULL) {
20252518Sstevel cmn_err(level, "%s", buf);
20262518Sstevel } else {
20277009Scth scsi_log(sg_state->sgen_devinfo, sgen_label, level,
20282518Sstevel "%s", buf);
20292518Sstevel }
20302518Sstevel break;
20312518Sstevel case SGEN_DIAG1:
20322518Sstevel case SGEN_DIAG2:
20332518Sstevel case SGEN_DIAG3:
20342518Sstevel default:
20352518Sstevel if (sg_state == (sgen_state_t *)NULL) {
20367009Scth scsi_log(NULL, sgen_label, CE_CONT, "%s", buf);
20372518Sstevel } else {
20387009Scth scsi_log(sg_state->sgen_devinfo, sgen_label, CE_CONT,
20392518Sstevel "%s", buf);
20402518Sstevel }
20412518Sstevel }
20422518Sstevel }
20432518Sstevel
20442518Sstevel /*
20452518Sstevel * sgen_dump_cdb()
20462518Sstevel * dump out the contents of a cdb. Take care that 'label' is not too
20472518Sstevel * large, or 'buf' could overflow.
20482518Sstevel */
20492518Sstevel static void
sgen_dump_cdb(sgen_state_t * sg_state,const char * label,union scsi_cdb * cdb,int cdblen)20502518Sstevel sgen_dump_cdb(sgen_state_t *sg_state, const char *label,
20512518Sstevel union scsi_cdb *cdb, int cdblen)
20522518Sstevel {
20532518Sstevel static char hex[] = "0123456789abcdef";
20542518Sstevel char *buf, *p;
20552518Sstevel size_t nbytes;
20562518Sstevel int i;
20572518Sstevel uchar_t *cdbp = (uchar_t *)cdb;
20582518Sstevel
20592518Sstevel /*
20602518Sstevel * fastpath-- if we're not able to print out, don't do all of this
20612518Sstevel * extra work.
20622518Sstevel */
20632518Sstevel if (!sgen_diag_ok(sg_state, SGEN_DIAG3))
20642518Sstevel return;
20652518Sstevel
20662518Sstevel /*
20672518Sstevel * 3 characters for each byte (because of the ' '), plus the size of
20682518Sstevel * the label, plus the trailing ']' and the null character.
20692518Sstevel */
20702518Sstevel nbytes = 3 * cdblen + strlen(label) + strlen(" CDB = [") + 2;
20712518Sstevel buf = kmem_alloc(nbytes, KM_SLEEP);
20722518Sstevel (void) sprintf(buf, "%s CDB = [", label);
20732518Sstevel p = &buf[strlen(buf)];
20742518Sstevel for (i = 0; i < cdblen; i++, cdbp++) {
20752518Sstevel if (i > 0)
20762518Sstevel *p++ = ' ';
20772518Sstevel *p++ = hex[(*cdbp >> 4) & 0x0f];
20782518Sstevel *p++ = hex[*cdbp & 0x0f];
20792518Sstevel }
20802518Sstevel *p++ = ']';
20812518Sstevel *p = 0;
20822518Sstevel sgen_log(sg_state, SGEN_DIAG3, buf);
20832518Sstevel kmem_free(buf, nbytes);
20842518Sstevel }
20852518Sstevel
20862518Sstevel static void
sgen_dump_sense(sgen_state_t * sg_state,size_t rqlen,uchar_t * rqbuf)20872518Sstevel sgen_dump_sense(sgen_state_t *sg_state, size_t rqlen, uchar_t *rqbuf)
20882518Sstevel {
20892518Sstevel static char hex[] = "0123456789abcdef";
20902518Sstevel char *buf, *p;
20912518Sstevel size_t nbytes;
20922518Sstevel int i;
20932518Sstevel
20942518Sstevel /*
20952518Sstevel * fastpath-- if we're not able to print out, don't do all of this
20962518Sstevel * extra work.
20972518Sstevel */
20982518Sstevel if (!sgen_diag_ok(sg_state, SGEN_DIAG3))
20992518Sstevel return;
21002518Sstevel
21012518Sstevel /*
21022518Sstevel * 3 characters for each byte (because of the ' '), plus the size of
21032518Sstevel * the label, plus the trailing ']' and the null character.
21042518Sstevel */
21052518Sstevel nbytes = 3 * rqlen + strlen(" SENSE = [") + 2;
21062518Sstevel buf = kmem_alloc(nbytes, KM_SLEEP);
21072518Sstevel (void) sprintf(buf, "SENSE = [");
21082518Sstevel p = &buf[strlen(buf)];
21092518Sstevel for (i = 0; i < rqlen; i++, rqbuf++) {
21102518Sstevel if (i > 0)
21112518Sstevel *p++ = ' ';
21122518Sstevel *p++ = hex[(*rqbuf >> 4) & 0x0f];
21132518Sstevel *p++ = hex[*rqbuf & 0x0f];
21142518Sstevel }
21152518Sstevel *p++ = ']';
21162518Sstevel *p = 0;
21172518Sstevel sgen_log(sg_state, SGEN_DIAG3, buf);
21182518Sstevel kmem_free(buf, nbytes);
21192518Sstevel }
2120