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