19a016c63Sstevel /* 29a016c63Sstevel * CDDL HEADER START 39a016c63Sstevel * 49a016c63Sstevel * The contents of this file are subject to the terms of the 59a016c63Sstevel * Common Development and Distribution License (the "License"). 69a016c63Sstevel * You may not use this file except in compliance with the License. 79a016c63Sstevel * 89a016c63Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 99a016c63Sstevel * or http://www.opensolaris.org/os/licensing. 109a016c63Sstevel * See the License for the specific language governing permissions 119a016c63Sstevel * and limitations under the License. 129a016c63Sstevel * 139a016c63Sstevel * When distributing Covered Code, include this CDDL HEADER in each 149a016c63Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 159a016c63Sstevel * If applicable, add the following below this CDDL HEADER, with the 169a016c63Sstevel * fields enclosed by brackets "[]" replaced with your own identifying 179a016c63Sstevel * information: Portions Copyright [yyyy] [name of copyright owner] 189a016c63Sstevel * 199a016c63Sstevel * CDDL HEADER END 209a016c63Sstevel */ 219a016c63Sstevel 229a016c63Sstevel /* 23275c9da8Seschrock * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 249a016c63Sstevel * Use is subject to license terms. 259a016c63Sstevel */ 269a016c63Sstevel 279a016c63Sstevel /* 289a016c63Sstevel * Copyright Siemens 1999 299a016c63Sstevel * All rights reserved. 309a016c63Sstevel */ 319a016c63Sstevel 329a016c63Sstevel 339a016c63Sstevel /* 349a016c63Sstevel * sgen - SCSI generic device driver 359a016c63Sstevel * 369a016c63Sstevel * The sgen driver provides user programs access to SCSI devices that 379a016c63Sstevel * are not supported by other drivers by providing the USCSI(7I) interface. 389a016c63Sstevel */ 399a016c63Sstevel 409a016c63Sstevel #include <sys/modctl.h> 419a016c63Sstevel #include <sys/file.h> 429a016c63Sstevel #include <sys/scsi/scsi.h> 439a016c63Sstevel #include <sys/scsi/targets/sgendef.h> 449a016c63Sstevel 45a204de77Scth /* The name of the driver, established from the module name in _init. */ 46a204de77Scth static char *sgen_label = NULL; 47a204de77Scth 489a016c63Sstevel #define DDI_NT_SGEN "ddi_generic:scsi" 499a016c63Sstevel 509a016c63Sstevel static char *sgen_devtypes[] = { 519a016c63Sstevel "direct", /* 0x00 -- disks */ 529a016c63Sstevel "sequential", /* 0x01 */ 539a016c63Sstevel "printer", /* 0x02 */ 549a016c63Sstevel "processor", /* 0x03 */ 559a016c63Sstevel "worm", /* 0x04 */ 569a016c63Sstevel "rodirect", /* 0x05 */ 579a016c63Sstevel "scanner", /* 0x06 */ 589a016c63Sstevel "optical", /* 0x07 */ 599a016c63Sstevel "changer", /* 0x08 */ 609a016c63Sstevel "comm", /* 0x09 */ 619a016c63Sstevel "prepress1", /* 0x0a -- reserved for prepress (ASC IT8) */ 629a016c63Sstevel "prepress2", /* 0x0b -- reserved for prepress (ASC IT8) */ 639a016c63Sstevel "array_ctrl", /* 0x0c -- storage array */ 649a016c63Sstevel "ses", /* 0x0d -- enclosure services */ 659a016c63Sstevel "rbc", /* 0x0e -- simplified block */ 669a016c63Sstevel "ocrw", /* 0x0f -- optical card read/write */ 679a016c63Sstevel "bridge", /* 0x10 -- reserved for bridging expanders */ 689a016c63Sstevel "type_0x11", /* 0x11 */ 699a016c63Sstevel "type_0x12", /* 0x12 */ 709a016c63Sstevel "type_0x13", /* 0x13 */ 719a016c63Sstevel "type_0x14", /* 0x14 */ 729a016c63Sstevel "type_0x15", /* 0x15 */ 739a016c63Sstevel "type_0x16", /* 0x16 */ 749a016c63Sstevel "type_0x17", /* 0x17 */ 759a016c63Sstevel "type_0x18", /* 0x18 */ 769a016c63Sstevel "type_0x19", /* 0x19 */ 779a016c63Sstevel "type_0x1a", /* 0x1a */ 789a016c63Sstevel "type_0x1b", /* 0x1b */ 799a016c63Sstevel "type_0x1c", /* 0x1c */ 809a016c63Sstevel "type_0x1d", /* 0x1d */ 819a016c63Sstevel "type_0x1e", /* 0x1e */ 829a016c63Sstevel "type_unknown" /* 0x1f is "no device type" or "unknown" */ 839a016c63Sstevel }; 849a016c63Sstevel 859a016c63Sstevel #define SGEN_NDEVTYPES ((sizeof (sgen_devtypes) / sizeof (char *))) 869a016c63Sstevel 879a016c63Sstevel #define SGEN_INQSTRLEN 24 889a016c63Sstevel #define SGEN_VENDID_MAX 8 899a016c63Sstevel #define SGEN_PRODID_MAX 16 909a016c63Sstevel 919a016c63Sstevel #define FILL_SCSI1_LUN(devp, pkt) \ 929a016c63Sstevel if ((devp)->sd_inq->inq_ansi == 0x1) { \ 939a016c63Sstevel int _lun; \ 949a016c63Sstevel _lun = ddi_prop_get_int(DDI_DEV_T_ANY, (devp)->sd_dev, \ 959a016c63Sstevel DDI_PROP_DONTPASS, SCSI_ADDR_PROP_LUN, 0); \ 969a016c63Sstevel if (_lun > 0) { \ 979a016c63Sstevel ((union scsi_cdb *)(pkt)->pkt_cdbp)->scc_lun = \ 989a016c63Sstevel _lun; \ 999a016c63Sstevel } \ 1009a016c63Sstevel } 1019a016c63Sstevel 1029a016c63Sstevel #define SGEN_DO_ERRSTATS(sg_state, x) \ 1039a016c63Sstevel if (sg_state->sgen_kstats) { \ 1049a016c63Sstevel struct sgen_errstats *sp; \ 1059a016c63Sstevel sp = (struct sgen_errstats *)sg_state->sgen_kstats->ks_data; \ 1069a016c63Sstevel sp->x.value.ui32++; \ 1079a016c63Sstevel } 1089a016c63Sstevel 1099a016c63Sstevel #define SCBP_C(pkt) ((*(pkt)->pkt_scbp) & STATUS_MASK) 1109a016c63Sstevel 1119a016c63Sstevel /* 1129a016c63Sstevel * Standard entrypoints 1139a016c63Sstevel */ 1149a016c63Sstevel static int sgen_attach(dev_info_t *, ddi_attach_cmd_t); 1159a016c63Sstevel static int sgen_detach(dev_info_t *, ddi_detach_cmd_t); 1169a016c63Sstevel static int sgen_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 1179a016c63Sstevel static int sgen_probe(dev_info_t *); 1189a016c63Sstevel static int sgen_open(dev_t *, int, int, cred_t *); 1199a016c63Sstevel static int sgen_close(dev_t, int, int, cred_t *); 1209a016c63Sstevel static int sgen_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 1219a016c63Sstevel 1229a016c63Sstevel /* 1239a016c63Sstevel * Configuration routines 1249a016c63Sstevel */ 1259a016c63Sstevel static int sgen_do_attach(dev_info_t *); 1269a016c63Sstevel static int sgen_setup_sense(sgen_state_t *); 1279a016c63Sstevel static void sgen_create_errstats(sgen_state_t *, int); 1289a016c63Sstevel static int sgen_do_suspend(dev_info_t *); 1299a016c63Sstevel static int sgen_do_detach(dev_info_t *); 1309a016c63Sstevel static void sgen_setup_binddb(dev_info_t *); 1319a016c63Sstevel static void sgen_cleanup_binddb(); 1329a016c63Sstevel 1339a016c63Sstevel /* 1349a016c63Sstevel * Packet transport routines 1359a016c63Sstevel */ 1366567eb0aSlh195018 static int sgen_uscsi_cmd(dev_t, struct uscsi_cmd *, int); 1379a016c63Sstevel static int sgen_start(struct buf *); 138275c9da8Seschrock static int sgen_hold_cmdbuf(sgen_state_t *); 1399a016c63Sstevel static void sgen_rele_cmdbuf(sgen_state_t *); 1409a016c63Sstevel static int sgen_make_uscsi_cmd(sgen_state_t *, struct buf *); 1419a016c63Sstevel static void sgen_restart(void *); 1429a016c63Sstevel static void sgen_callback(struct scsi_pkt *); 1439a016c63Sstevel static int sgen_handle_autosense(sgen_state_t *, struct scsi_pkt *); 1449a016c63Sstevel static int sgen_handle_sense(sgen_state_t *); 1459a016c63Sstevel static int sgen_handle_incomplete(sgen_state_t *, struct scsi_pkt *); 1469a016c63Sstevel static int sgen_check_error(sgen_state_t *, struct buf *); 147602ca9eaScth static int sgen_initiate_sense(sgen_state_t *, int); 1489a016c63Sstevel static int sgen_scsi_transport(struct scsi_pkt *); 1499a016c63Sstevel static int sgen_tur(dev_t); 1509a016c63Sstevel 1519a016c63Sstevel /* 1529a016c63Sstevel * Logging/debugging routines 1539a016c63Sstevel */ 1549a016c63Sstevel static void sgen_log(sgen_state_t *, int, const char *, ...); 1559a016c63Sstevel static int sgen_diag_ok(sgen_state_t *, int); 1569a016c63Sstevel static void sgen_dump_cdb(sgen_state_t *, const char *, union scsi_cdb *, int); 1579a016c63Sstevel static void sgen_dump_sense(sgen_state_t *, size_t, uchar_t *); 1589a016c63Sstevel 1599a016c63Sstevel int sgen_diag = 0; 1609a016c63Sstevel int sgen_sporadic_failures = 0; 1619a016c63Sstevel int sgen_force_manual_sense = 0; 1629a016c63Sstevel struct sgen_binddb sgen_binddb; 1639a016c63Sstevel 1649a016c63Sstevel static struct cb_ops sgen_cb_ops = { 1659a016c63Sstevel sgen_open, /* open */ 1669a016c63Sstevel sgen_close, /* close */ 1679a016c63Sstevel nodev, /* strategy */ 1689a016c63Sstevel nodev, /* print */ 1699a016c63Sstevel nodev, /* dump */ 1709a016c63Sstevel nodev, /* read */ 1719a016c63Sstevel nodev, /* write */ 1729a016c63Sstevel sgen_ioctl, /* ioctl */ 1739a016c63Sstevel nodev, /* devmap */ 1749a016c63Sstevel nodev, /* mmap */ 1759a016c63Sstevel nodev, /* segmap */ 1769a016c63Sstevel nochpoll, /* poll */ 1779a016c63Sstevel ddi_prop_op, /* cb_prop_op */ 1789a016c63Sstevel 0, /* streamtab */ 1799a016c63Sstevel D_MP | D_NEW | D_HOTPLUG /* Driver compatibility flag */ 1809a016c63Sstevel }; 1819a016c63Sstevel 1829a016c63Sstevel static struct dev_ops sgen_dev_ops = { 1839a016c63Sstevel DEVO_REV, /* devo_rev, */ 1849a016c63Sstevel 0, /* refcnt */ 1859a016c63Sstevel sgen_getinfo, /* info */ 1869a016c63Sstevel nodev, /* identify */ 1879a016c63Sstevel sgen_probe, /* probe */ 1889a016c63Sstevel sgen_attach, /* attach */ 1899a016c63Sstevel sgen_detach, /* detach */ 1909a016c63Sstevel nodev, /* reset */ 1919a016c63Sstevel &sgen_cb_ops, /* driver operations */ 1929a016c63Sstevel (struct bus_ops *)0, /* bus operations */ 193*19397407SSherry Moore NULL, /* power */ 194*19397407SSherry Moore ddi_quiesce_not_supported, /* devo_quiesce */ 1959a016c63Sstevel }; 1969a016c63Sstevel 1979a016c63Sstevel static void *sgen_soft_state = NULL; 1989a016c63Sstevel 1999a016c63Sstevel static struct modldrv modldrv = { 200*19397407SSherry Moore &mod_driverops, "SCSI generic driver", &sgen_dev_ops 2019a016c63Sstevel }; 2029a016c63Sstevel 2039a016c63Sstevel static struct modlinkage modlinkage = { 2049a016c63Sstevel MODREV_1, &modldrv, NULL 2059a016c63Sstevel }; 2069a016c63Sstevel 2079a016c63Sstevel int 2089a016c63Sstevel _init(void) 2099a016c63Sstevel { 2109a016c63Sstevel int err; 2119a016c63Sstevel 212a204de77Scth /* establish driver name from module name */ 213a204de77Scth sgen_label = (char *)mod_modname(&modlinkage); 214a204de77Scth 2159a016c63Sstevel sgen_log(NULL, SGEN_DIAG2, "in sgen_init()"); 2169a016c63Sstevel if ((err = ddi_soft_state_init(&sgen_soft_state, 2179a016c63Sstevel sizeof (sgen_state_t), SGEN_ESTIMATED_NUM_DEVS)) != 0) { 2189a016c63Sstevel goto done; 2199a016c63Sstevel } 2209a016c63Sstevel 2219a016c63Sstevel if ((err = mod_install(&modlinkage)) != 0) { 2229a016c63Sstevel ddi_soft_state_fini(&sgen_soft_state); 2239a016c63Sstevel goto done; 2249a016c63Sstevel } 2259a016c63Sstevel 2269a016c63Sstevel done: 2279a016c63Sstevel sgen_log(NULL, SGEN_DIAG2, "%s sgen_init()", err ? "failed" : "done"); 2289a016c63Sstevel return (err); 2299a016c63Sstevel } 2309a016c63Sstevel 2319a016c63Sstevel int 2329a016c63Sstevel _fini(void) 2339a016c63Sstevel { 2349a016c63Sstevel int err; 2359a016c63Sstevel sgen_log(NULL, SGEN_DIAG2, "in sgen_fini()"); 2369a016c63Sstevel 2379a016c63Sstevel if ((err = mod_remove(&modlinkage)) != 0) { 2389a016c63Sstevel goto done; 2399a016c63Sstevel } 2409a016c63Sstevel 2419a016c63Sstevel ddi_soft_state_fini(&sgen_soft_state); 2429a016c63Sstevel sgen_cleanup_binddb(); 2439a016c63Sstevel 2449a016c63Sstevel done: 2459a016c63Sstevel sgen_log(NULL, SGEN_DIAG2, "%s sgen_fini()", err ? "failed" : "done"); 2469a016c63Sstevel return (err); 2479a016c63Sstevel } 2489a016c63Sstevel 2499a016c63Sstevel int 2509a016c63Sstevel _info(struct modinfo *modinfop) 2519a016c63Sstevel { 2529a016c63Sstevel return (mod_info(&modlinkage, modinfop)); 2539a016c63Sstevel } 2549a016c63Sstevel 2559a016c63Sstevel /* 2569a016c63Sstevel * sgen_typename() 2579a016c63Sstevel * return a device type's name by looking it up in the sgen_devtypes table. 2589a016c63Sstevel */ 2599a016c63Sstevel static char * 2609a016c63Sstevel sgen_typename(uchar_t typeno) 2619a016c63Sstevel { 2629a016c63Sstevel if (typeno >= SGEN_NDEVTYPES) 2639a016c63Sstevel return ("type_unknown"); 2649a016c63Sstevel return (sgen_devtypes[typeno]); 2659a016c63Sstevel } 2669a016c63Sstevel 2679a016c63Sstevel /* 2689a016c63Sstevel * sgen_typenum() 2699a016c63Sstevel * return a device type's number by looking it up in the sgen_devtypes 2709a016c63Sstevel * table. 2719a016c63Sstevel */ 2729a016c63Sstevel static int 2739a016c63Sstevel sgen_typenum(const char *typename, uchar_t *typenum) 2749a016c63Sstevel { 2759a016c63Sstevel int i; 2769a016c63Sstevel for (i = 0; i < SGEN_NDEVTYPES; i++) { 2779a016c63Sstevel if (strcasecmp(sgen_devtypes[i], typename) == 0) { 2789a016c63Sstevel *typenum = (uchar_t)i; 2799a016c63Sstevel return (0); 2809a016c63Sstevel } 2819a016c63Sstevel } 2829a016c63Sstevel return (-1); 2839a016c63Sstevel } 2849a016c63Sstevel 2859a016c63Sstevel /* 2869a016c63Sstevel * sgen_setup_binddb() 2879a016c63Sstevel * initialize a data structure which stores all of the information about 2889a016c63Sstevel * which devices and device types the driver should bind to. 2899a016c63Sstevel */ 2909a016c63Sstevel static void 2919a016c63Sstevel sgen_setup_binddb(dev_info_t *dip) 2929a016c63Sstevel { 2939a016c63Sstevel char **strs = NULL, *cp, *pcp, *vcp; 2949a016c63Sstevel uint_t nelems, pcplen, vcplen, idx; 2959a016c63Sstevel 2969a016c63Sstevel ASSERT(sgen_binddb.sdb_init == 0); 2979a016c63Sstevel ASSERT(MUTEX_HELD(&sgen_binddb.sdb_lock)); 2989a016c63Sstevel 2999a016c63Sstevel if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 3009a016c63Sstevel "device-type-config-list", &strs, &nelems) == DDI_PROP_SUCCESS) { 3019a016c63Sstevel /* 3029a016c63Sstevel * for each device type specifier make a copy and put it into a 3039a016c63Sstevel * node in the binddb. 3049a016c63Sstevel */ 3059a016c63Sstevel for (idx = 0; idx < nelems; idx++) { 3069a016c63Sstevel sgen_type_node_t *nodep; 3079a016c63Sstevel uchar_t devtype; 3089a016c63Sstevel cp = strs[idx]; 3099a016c63Sstevel if (sgen_typenum(cp, &devtype) != 0) { 3109a016c63Sstevel sgen_log(NULL, CE_WARN, 3119a016c63Sstevel "unknown device type '%s', " 3129a016c63Sstevel "device unit-address @%s", 3139a016c63Sstevel cp, ddi_get_name_addr(dip)); 3149a016c63Sstevel continue; 3159a016c63Sstevel } 3169a016c63Sstevel nodep = kmem_zalloc(sizeof (sgen_type_node_t), 3179a016c63Sstevel KM_SLEEP); 3189a016c63Sstevel nodep->node_type = devtype; 3199a016c63Sstevel nodep->node_next = sgen_binddb.sdb_type_nodes; 3209a016c63Sstevel sgen_binddb.sdb_type_nodes = nodep; 3219a016c63Sstevel 3229a016c63Sstevel sgen_log(NULL, SGEN_DIAG2, "found device type " 3239a016c63Sstevel "'%s' in device-type-config-list, " 3249a016c63Sstevel "device unit-address @%s", 3259a016c63Sstevel cp, ddi_get_name_addr(dip)); 3269a016c63Sstevel } 3279a016c63Sstevel ddi_prop_free(strs); 3289a016c63Sstevel } 3299a016c63Sstevel 3309a016c63Sstevel /* 3319a016c63Sstevel * for each Vendor/Product inquiry pair, build a node and put it 3329a016c63Sstevel * into the the binddb. 3339a016c63Sstevel */ 3349a016c63Sstevel if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 3359a016c63Sstevel "inquiry-config-list", &strs, &nelems) == DDI_PROP_SUCCESS) { 3369a016c63Sstevel 3379a016c63Sstevel if (nelems % 2 == 1) { 3389a016c63Sstevel sgen_log(NULL, CE_WARN, "inquiry-config-list must " 3399a016c63Sstevel "contain Vendor/Product pairs, " 3409a016c63Sstevel "device unit-address @%s", 3419a016c63Sstevel ddi_get_name_addr(dip)); 3429a016c63Sstevel nelems--; 3439a016c63Sstevel } 3449a016c63Sstevel for (idx = 0; idx < nelems; idx += 2) { 3459a016c63Sstevel sgen_inq_node_t *nodep; 3469a016c63Sstevel /* 3479a016c63Sstevel * Grab vendor and product ID. 3489a016c63Sstevel */ 3499a016c63Sstevel vcp = strs[idx]; 3509a016c63Sstevel vcplen = strlen(vcp); 3519a016c63Sstevel if (vcplen == 0 || vcplen > SGEN_VENDID_MAX) { 3529a016c63Sstevel sgen_log(NULL, CE_WARN, 3539a016c63Sstevel "Invalid vendor ID '%s', " 3549a016c63Sstevel "device unit-address @%s", 3559a016c63Sstevel vcp, ddi_get_name_addr(dip)); 3569a016c63Sstevel continue; 3579a016c63Sstevel } 3589a016c63Sstevel 3599a016c63Sstevel pcp = strs[idx + 1]; 3609a016c63Sstevel pcplen = strlen(pcp); 3619a016c63Sstevel if (pcplen == 0 || pcplen > SGEN_PRODID_MAX) { 3629a016c63Sstevel sgen_log(NULL, CE_WARN, 3639a016c63Sstevel "Invalid product ID '%s', " 3649a016c63Sstevel "device unit-address @%s", 3659a016c63Sstevel pcp, ddi_get_name_addr(dip)); 3669a016c63Sstevel continue; 3679a016c63Sstevel } 3689a016c63Sstevel 3699a016c63Sstevel nodep = kmem_zalloc(sizeof (sgen_inq_node_t), 3709a016c63Sstevel KM_SLEEP); 3719a016c63Sstevel nodep->node_vendor = kmem_alloc(vcplen + 1, KM_SLEEP); 3729a016c63Sstevel (void) strcpy(nodep->node_vendor, vcp); 3739a016c63Sstevel nodep->node_product = kmem_alloc(pcplen + 1, KM_SLEEP); 3749a016c63Sstevel (void) strcpy(nodep->node_product, pcp); 3759a016c63Sstevel 3769a016c63Sstevel nodep->node_next = sgen_binddb.sdb_inq_nodes; 3779a016c63Sstevel sgen_binddb.sdb_inq_nodes = nodep; 3789a016c63Sstevel 3799a016c63Sstevel sgen_log(NULL, SGEN_DIAG2, "found inquiry string " 3809a016c63Sstevel "'%s' '%s' in device-type-config-list, " 3819a016c63Sstevel "device unit-address @%s", 3829a016c63Sstevel nodep->node_vendor, nodep->node_product, 3839a016c63Sstevel ddi_get_name_addr(dip)); 3849a016c63Sstevel } 3859a016c63Sstevel ddi_prop_free(strs); 3869a016c63Sstevel } 3879a016c63Sstevel 3889a016c63Sstevel sgen_binddb.sdb_init = 1; 3899a016c63Sstevel } 3909a016c63Sstevel 3919a016c63Sstevel /* 3929a016c63Sstevel * sgen_cleanup_binddb() 3939a016c63Sstevel * deallocate data structures for binding database. 3949a016c63Sstevel */ 3959a016c63Sstevel static void 3969a016c63Sstevel sgen_cleanup_binddb() 3979a016c63Sstevel { 3989a016c63Sstevel sgen_inq_node_t *inqp, *inqnextp; 3999a016c63Sstevel sgen_type_node_t *typep, *typenextp; 4009a016c63Sstevel 4019a016c63Sstevel mutex_enter(&sgen_binddb.sdb_lock); 4029a016c63Sstevel if (sgen_binddb.sdb_init == 0) { 4039a016c63Sstevel mutex_exit(&sgen_binddb.sdb_lock); 4049a016c63Sstevel return; 4059a016c63Sstevel } 4069a016c63Sstevel 4079a016c63Sstevel for (inqp = sgen_binddb.sdb_inq_nodes; inqp != NULL; inqp = inqnextp) { 4089a016c63Sstevel inqnextp = inqp->node_next; 4099a016c63Sstevel ASSERT(inqp->node_vendor && inqp->node_product); 4109a016c63Sstevel kmem_free(inqp->node_vendor, 4119a016c63Sstevel strlen(inqp->node_vendor) + 1); 4129a016c63Sstevel kmem_free(inqp->node_product, 4139a016c63Sstevel strlen(inqp->node_product) + 1); 4149a016c63Sstevel kmem_free(inqp, sizeof (sgen_inq_node_t)); 4159a016c63Sstevel } 4169a016c63Sstevel 4179a016c63Sstevel for (typep = sgen_binddb.sdb_type_nodes; typep != NULL; 4189a016c63Sstevel typep = typenextp) { 4199a016c63Sstevel typenextp = typep->node_next; 4209a016c63Sstevel kmem_free(typep, sizeof (sgen_type_node_t)); 4219a016c63Sstevel } 4229a016c63Sstevel mutex_exit(&sgen_binddb.sdb_lock); 4239a016c63Sstevel } 4249a016c63Sstevel 4259a016c63Sstevel /* 4269a016c63Sstevel * sgen_bind_byinq() 4279a016c63Sstevel * lookup a device in the binding database by its inquiry data. 4289a016c63Sstevel */ 4299a016c63Sstevel static int 4309a016c63Sstevel sgen_bind_byinq(dev_info_t *dip) 4319a016c63Sstevel { 4329a016c63Sstevel sgen_inq_node_t *nodep; 4339a016c63Sstevel char vend_str[SGEN_VENDID_MAX+1]; 4349a016c63Sstevel char prod_str[SGEN_PRODID_MAX+1]; 4359a016c63Sstevel struct scsi_device *scsidevp; 4369a016c63Sstevel 4379a016c63Sstevel scsidevp = ddi_get_driver_private(dip); 4389a016c63Sstevel 4399a016c63Sstevel /* 4409a016c63Sstevel * inq_vid and inq_pid are laid out by the protocol in order in the 4419a016c63Sstevel * inquiry structure, and are not delimited by \0. 4429a016c63Sstevel */ 4439a016c63Sstevel bcopy(scsidevp->sd_inq->inq_vid, vend_str, SGEN_VENDID_MAX); 4449a016c63Sstevel vend_str[SGEN_VENDID_MAX] = '\0'; 4459a016c63Sstevel bcopy(scsidevp->sd_inq->inq_pid, prod_str, SGEN_PRODID_MAX); 4469a016c63Sstevel prod_str[SGEN_PRODID_MAX] = '\0'; 4479a016c63Sstevel 4489a016c63Sstevel for (nodep = sgen_binddb.sdb_inq_nodes; nodep != NULL; 4499a016c63Sstevel nodep = nodep->node_next) { 4509a016c63Sstevel /* 4519a016c63Sstevel * Allow the "*" wildcard to match all vendor IDs. 4529a016c63Sstevel */ 4539a016c63Sstevel if (strcmp(nodep->node_vendor, "*") != 0) { 4549a016c63Sstevel if (strncasecmp(nodep->node_vendor, vend_str, 4559a016c63Sstevel strlen(nodep->node_vendor)) != 0) { 4569a016c63Sstevel continue; 4579a016c63Sstevel } 4589a016c63Sstevel } 4599a016c63Sstevel 4609a016c63Sstevel /* 4619a016c63Sstevel * Using strncasecmp() with the key length allows substring 4629a016c63Sstevel * matching for product data. 4639a016c63Sstevel */ 4649a016c63Sstevel if (strncasecmp(nodep->node_product, prod_str, 4659a016c63Sstevel strlen(nodep->node_product)) == 0) { 4669a016c63Sstevel return (0); 4679a016c63Sstevel } 4689a016c63Sstevel } 4699a016c63Sstevel return (-1); 4709a016c63Sstevel } 4719a016c63Sstevel 4729a016c63Sstevel /* 4739a016c63Sstevel * sgen_bind_bytype() 4749a016c63Sstevel * lookup a device type in the binding database; if found, return a 4759a016c63Sstevel * format string corresponding to the string in the .conf file. 4769a016c63Sstevel */ 4779a016c63Sstevel static int 4789a016c63Sstevel sgen_bind_bytype(dev_info_t *dip) 4799a016c63Sstevel { 4809a016c63Sstevel sgen_type_node_t *nodep; 4819a016c63Sstevel struct scsi_device *scsidevp; 4829a016c63Sstevel 4839a016c63Sstevel scsidevp = ddi_get_driver_private(dip); 4849a016c63Sstevel 4859a016c63Sstevel for (nodep = sgen_binddb.sdb_type_nodes; nodep != NULL; 4869a016c63Sstevel nodep = nodep->node_next) { 4879a016c63Sstevel if (nodep->node_type == scsidevp->sd_inq->inq_dtype) { 4889a016c63Sstevel return (0); 4899a016c63Sstevel } 4909a016c63Sstevel } 4919a016c63Sstevel return (-1); 4929a016c63Sstevel } 4939a016c63Sstevel 4949a016c63Sstevel /* 4959a016c63Sstevel * sgen_get_binding() 4969a016c63Sstevel * Check to see if the device in question matches the criteria for 4979a016c63Sstevel * sgen to bind. 4989a016c63Sstevel * 4999a016c63Sstevel * Either the .conf file must specify a device_type entry which 5009a016c63Sstevel * matches the SCSI device type of this device, or the inquiry 5019a016c63Sstevel * string provided by the device must match an inquiry string specified 5029a016c63Sstevel * in the .conf file. Inquiry data is matched first. 5039a016c63Sstevel */ 5049a016c63Sstevel static int 5059a016c63Sstevel sgen_get_binding(dev_info_t *dip) 5069a016c63Sstevel { 5079a016c63Sstevel int retval = 0; 5089a016c63Sstevel 5099a016c63Sstevel mutex_enter(&sgen_binddb.sdb_lock); 5109a016c63Sstevel if (sgen_binddb.sdb_init == 0) 5119a016c63Sstevel sgen_setup_binddb(dip); 5129a016c63Sstevel mutex_exit(&sgen_binddb.sdb_lock); 5139a016c63Sstevel 5149a016c63Sstevel 5159a016c63Sstevel /* 5169a016c63Sstevel * Check device-type-config-list for a match by device type. 5179a016c63Sstevel */ 5189a016c63Sstevel if (sgen_bind_bytype(dip) == 0) 5199a016c63Sstevel goto done; 5209a016c63Sstevel 5219a016c63Sstevel /* 5229a016c63Sstevel * Check inquiry-config-list for a match by Vendor/Product ID. 5239a016c63Sstevel */ 5249a016c63Sstevel if (sgen_bind_byinq(dip) == 0) 5259a016c63Sstevel goto done; 5269a016c63Sstevel 5279a016c63Sstevel retval = -1; 5289a016c63Sstevel done: 5299a016c63Sstevel return (retval); 5309a016c63Sstevel } 5319a016c63Sstevel 5329a016c63Sstevel /* 5339a016c63Sstevel * sgen_attach() 5349a016c63Sstevel * attach(9e) entrypoint. 5359a016c63Sstevel */ 5369a016c63Sstevel static int 5379a016c63Sstevel sgen_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 5389a016c63Sstevel { 5399a016c63Sstevel int err; 5409a016c63Sstevel 5419a016c63Sstevel sgen_log(NULL, SGEN_DIAG2, "in sgen_attach(), device unit-address @%s", 5429a016c63Sstevel ddi_get_name_addr(dip)); 5439a016c63Sstevel 5449a016c63Sstevel switch (cmd) { 5459a016c63Sstevel case DDI_ATTACH: 5469a016c63Sstevel err = sgen_do_attach(dip); 5479a016c63Sstevel break; 5489a016c63Sstevel case DDI_RESUME: 5499a016c63Sstevel err = DDI_SUCCESS; 5509a016c63Sstevel break; 5519a016c63Sstevel case DDI_PM_RESUME: 5529a016c63Sstevel default: 5539a016c63Sstevel err = DDI_FAILURE; 5549a016c63Sstevel break; 5559a016c63Sstevel } 5569a016c63Sstevel 5579a016c63Sstevel done: 5589a016c63Sstevel sgen_log(NULL, SGEN_DIAG2, "%s sgen_attach(), device unit-address @%s", 5599a016c63Sstevel err == DDI_SUCCESS ? "done" : "failed", ddi_get_name_addr(dip)); 5609a016c63Sstevel return (err); 5619a016c63Sstevel } 5629a016c63Sstevel 5639a016c63Sstevel /* 5649a016c63Sstevel * sgen_do_attach() 5659a016c63Sstevel * handle the nitty details of attach. 5669a016c63Sstevel */ 5679a016c63Sstevel static int 5689a016c63Sstevel sgen_do_attach(dev_info_t *dip) 5699a016c63Sstevel { 5709a016c63Sstevel int instance; 5719a016c63Sstevel struct scsi_device *scsidevp; 5729a016c63Sstevel sgen_state_t *sg_state; 5739a016c63Sstevel uchar_t devtype; 5749a016c63Sstevel struct scsi_inquiry *inq; 5759a016c63Sstevel 5769a016c63Sstevel instance = ddi_get_instance(dip); 5779a016c63Sstevel 5789a016c63Sstevel scsidevp = ddi_get_driver_private(dip); 5799a016c63Sstevel ASSERT(scsidevp); 5809a016c63Sstevel 5819a016c63Sstevel sgen_log(NULL, SGEN_DIAG2, "sgen_do_attach: instance = %d, " 5829a016c63Sstevel "device unit-address @%s", instance, ddi_get_name_addr(dip)); 5839a016c63Sstevel 5849a016c63Sstevel /* 5859a016c63Sstevel * Probe the device in order to get its device type to name the minor 5869a016c63Sstevel * node. 5879a016c63Sstevel */ 5889a016c63Sstevel if (scsi_probe(scsidevp, NULL_FUNC) != SCSIPROBE_EXISTS) { 5899a016c63Sstevel scsi_unprobe(scsidevp); 5909a016c63Sstevel return (DDI_FAILURE); 5919a016c63Sstevel } 5929a016c63Sstevel 5939a016c63Sstevel if (ddi_soft_state_zalloc(sgen_soft_state, instance) != DDI_SUCCESS) { 5949a016c63Sstevel sgen_log(NULL, SGEN_DIAG1, 5959a016c63Sstevel "sgen_do_attach: failed to allocate softstate, " 5969a016c63Sstevel "device unit-address @%s", ddi_get_name_addr(dip)); 5979a016c63Sstevel scsi_unprobe(scsidevp); 5989a016c63Sstevel return (DDI_FAILURE); 5999a016c63Sstevel } 6009a016c63Sstevel 6019a016c63Sstevel inq = scsidevp->sd_inq; /* valid while device is probed... */ 6029a016c63Sstevel devtype = inq->inq_dtype; 6039a016c63Sstevel 6049a016c63Sstevel sg_state = ddi_get_soft_state(sgen_soft_state, instance); 6059a016c63Sstevel sg_state->sgen_scsidev = scsidevp; 6069a016c63Sstevel scsidevp->sd_dev = dip; 6079a016c63Sstevel 6089a016c63Sstevel /* 6099a016c63Sstevel * Now that sg_state->sgen_scsidev is initialized, it's ok to 6109a016c63Sstevel * call sgen_log with sg_state instead of NULL. 6119a016c63Sstevel */ 6129a016c63Sstevel 6139a016c63Sstevel /* 6149a016c63Sstevel * If the user specified the sgen_diag property, override the global 6159a016c63Sstevel * sgen_diag setting by setting sg_state's sgen_diag value. If the 6169a016c63Sstevel * user gave a value out of range, default to '0'. 6179a016c63Sstevel */ 6189a016c63Sstevel sg_state->sgen_diag = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 6199a016c63Sstevel "sgen-diag", -1); 6209a016c63Sstevel 6219a016c63Sstevel if (sg_state->sgen_diag != -1) { 6229a016c63Sstevel if (sg_state->sgen_diag < 0 || sg_state->sgen_diag > 3) 6239a016c63Sstevel sg_state->sgen_diag = 0; 6249a016c63Sstevel } 6259a016c63Sstevel 6269a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, 6279a016c63Sstevel "sgen_do_attach: sgen_soft_state=0x%p, instance=%d, " 6289a016c63Sstevel "device unit-address @%s", 6299a016c63Sstevel sgen_soft_state, instance, ddi_get_name_addr(dip)); 6309a016c63Sstevel 6319a016c63Sstevel /* 6329a016c63Sstevel * For simplicity, the minor number == the instance number 6339a016c63Sstevel */ 6349a016c63Sstevel if (ddi_create_minor_node(dip, sgen_typename(devtype), S_IFCHR, 6359a016c63Sstevel instance, DDI_NT_SGEN, NULL) == DDI_FAILURE) { 6369a016c63Sstevel scsi_unprobe(scsidevp); 6379a016c63Sstevel ddi_prop_remove_all(dip); 6389a016c63Sstevel sgen_log(sg_state, SGEN_DIAG1, 6399a016c63Sstevel "sgen_do_attach: minor node creation failed, " 6409a016c63Sstevel "device unit-address @%s", ddi_get_name_addr(dip)); 6419a016c63Sstevel ddi_soft_state_free(sgen_soft_state, instance); 6429a016c63Sstevel return (DDI_FAILURE); 6439a016c63Sstevel } 6449a016c63Sstevel 6459a016c63Sstevel /* 6469a016c63Sstevel * Allocate the command buffer, then create a condition variable for 6479a016c63Sstevel * managing it; mark the command buffer as free. 6489a016c63Sstevel */ 6499a016c63Sstevel sg_state->sgen_cmdbuf = getrbuf(KM_SLEEP); 6509a016c63Sstevel cv_init(&sg_state->sgen_cmdbuf_cv, NULL, CV_DRIVER, NULL); 6519a016c63Sstevel 6529a016c63Sstevel SGEN_CLR_BUSY(sg_state); 6539a016c63Sstevel SGEN_CLR_OPEN(sg_state); 6549a016c63Sstevel SGEN_CLR_SUSP(sg_state); 6559a016c63Sstevel 6569a016c63Sstevel /* 6579a016c63Sstevel * If the hba and the target both support wide xfers, enable them. 6589a016c63Sstevel */ 6599a016c63Sstevel if (scsi_ifgetcap(&sg_state->sgen_scsiaddr, "wide-xfer", 1) != -1) { 6609a016c63Sstevel int wide = 0; 6619a016c63Sstevel if ((inq->inq_rdf == RDF_SCSI2) && 6629a016c63Sstevel (inq->inq_wbus16 || inq->inq_wbus32)) 6639a016c63Sstevel wide = 1; 6649a016c63Sstevel if (scsi_ifsetcap(&sg_state->sgen_scsiaddr, "wide-xfer", 6659a016c63Sstevel wide, 1) == 1) { 6669a016c63Sstevel sgen_log(sg_state, SGEN_DIAG1, 6679a016c63Sstevel "sgen_attach: wide xfer %s, " 6689a016c63Sstevel "device unit-address @%s", 6699a016c63Sstevel wide ? "enabled" : "disabled", 6709a016c63Sstevel ddi_get_name_addr(dip)); 6719a016c63Sstevel } 6729a016c63Sstevel } 6739a016c63Sstevel 6749a016c63Sstevel /* 6759a016c63Sstevel * This is a little debugging code-- since the codepath for auto-sense 6769a016c63Sstevel * and 'manual' sense is split, toggling this variable will make 6779a016c63Sstevel * sgen act as though the adapter in question can't do auto-sense. 6789a016c63Sstevel */ 6799a016c63Sstevel if (sgen_force_manual_sense) { 6809a016c63Sstevel if (scsi_ifsetcap(&sg_state->sgen_scsiaddr, "auto-rqsense", 6819a016c63Sstevel 0, 1) == 1) { 6829a016c63Sstevel sg_state->sgen_arq_enabled = 0; 6839a016c63Sstevel } else { 6849a016c63Sstevel sg_state->sgen_arq_enabled = 1; 6859a016c63Sstevel } 6869a016c63Sstevel } else { 6879a016c63Sstevel /* 6889a016c63Sstevel * Enable autorequest sense, if supported 6899a016c63Sstevel */ 6909a016c63Sstevel if (scsi_ifgetcap(&sg_state->sgen_scsiaddr, 6919a016c63Sstevel "auto-rqsense", 1) != 1) { 6929a016c63Sstevel if (scsi_ifsetcap(&sg_state->sgen_scsiaddr, 6939a016c63Sstevel "auto-rqsense", 1, 1) == 1) { 6949a016c63Sstevel sg_state->sgen_arq_enabled = 1; 6959a016c63Sstevel sgen_log(sg_state, SGEN_DIAG1, 6969a016c63Sstevel "sgen_attach: auto-request-sense enabled, " 6979a016c63Sstevel "device unit-address @%s", 6989a016c63Sstevel ddi_get_name_addr(dip)); 6999a016c63Sstevel } else { 7009a016c63Sstevel sg_state->sgen_arq_enabled = 0; 7019a016c63Sstevel sgen_log(sg_state, SGEN_DIAG1, 7029a016c63Sstevel "sgen_attach: auto-request-sense disabled, " 7039a016c63Sstevel "device unit-address @%s", 7049a016c63Sstevel ddi_get_name_addr(dip)); 7059a016c63Sstevel } 7069a016c63Sstevel } else { 7079a016c63Sstevel sg_state->sgen_arq_enabled = 1; /* already enabled */ 7089a016c63Sstevel sgen_log(sg_state, SGEN_DIAG1, 7099a016c63Sstevel "sgen_attach: auto-request-sense enabled, " 7109a016c63Sstevel "device unit-address @%s", ddi_get_name_addr(dip)); 7119a016c63Sstevel } 7129a016c63Sstevel } 7139a016c63Sstevel 7149a016c63Sstevel /* 7159a016c63Sstevel * Allocate plumbing for manually fetching sense. 7169a016c63Sstevel */ 7179a016c63Sstevel if (sgen_setup_sense(sg_state) != 0) { 7189a016c63Sstevel freerbuf(sg_state->sgen_cmdbuf); 7199a016c63Sstevel ddi_prop_remove_all(dip); 7209a016c63Sstevel ddi_remove_minor_node(dip, NULL); 7219a016c63Sstevel scsi_unprobe(scsidevp); 7229a016c63Sstevel sgen_log(sg_state, SGEN_DIAG1, 7239a016c63Sstevel "sgen_do_attach: failed to setup request-sense, " 7249a016c63Sstevel "device unit-address @%s", ddi_get_name_addr(dip)); 7259a016c63Sstevel ddi_soft_state_free(sgen_soft_state, instance); 7269a016c63Sstevel return (DDI_FAILURE); 7279a016c63Sstevel } 7289a016c63Sstevel 7299a016c63Sstevel sgen_create_errstats(sg_state, instance); 7309a016c63Sstevel 7319a016c63Sstevel ddi_report_dev(dip); 7329a016c63Sstevel 7339a016c63Sstevel return (DDI_SUCCESS); 7349a016c63Sstevel } 7359a016c63Sstevel 7369a016c63Sstevel /* 7379a016c63Sstevel * sgen_setup_sense() 7389a016c63Sstevel * Allocate a request sense packet so that if sgen needs to fetch sense 7399a016c63Sstevel * data for the user, it will have a pkt ready to send. 7409a016c63Sstevel */ 7419a016c63Sstevel static int 7429a016c63Sstevel sgen_setup_sense(sgen_state_t *sg_state) 7439a016c63Sstevel { 7449a016c63Sstevel struct buf *bp; 7459a016c63Sstevel struct scsi_pkt *rqpkt; 7469a016c63Sstevel 7479a016c63Sstevel if ((bp = scsi_alloc_consistent_buf(&sg_state->sgen_scsiaddr, NULL, 74830ab6db6Slh195018 MAX_SENSE_LENGTH, B_READ, SLEEP_FUNC, NULL)) == NULL) { 7499a016c63Sstevel return (-1); 7509a016c63Sstevel } 7519a016c63Sstevel 7529a016c63Sstevel if ((rqpkt = scsi_init_pkt(&sg_state->sgen_scsiaddr, NULL, bp, 7539a016c63Sstevel CDB_GROUP0, 1, 0, PKT_CONSISTENT, SLEEP_FUNC, NULL)) == NULL) { 7549a016c63Sstevel scsi_free_consistent_buf(bp); 7559a016c63Sstevel return (-1); 7569a016c63Sstevel } 7579a016c63Sstevel 7589a016c63Sstevel /* 7599a016c63Sstevel * Make the results of running a SENSE available by filling out the 7609a016c63Sstevel * sd_sense field of the scsi device (sgen_sense is just an alias). 7619a016c63Sstevel */ 7629a016c63Sstevel sg_state->sgen_sense = (struct scsi_extended_sense *)bp->b_un.b_addr; 7639a016c63Sstevel 7649a016c63Sstevel (void) scsi_setup_cdb((union scsi_cdb *)rqpkt->pkt_cdbp, 76530ab6db6Slh195018 SCMD_REQUEST_SENSE, 0, MAX_SENSE_LENGTH, 0); 7669a016c63Sstevel FILL_SCSI1_LUN(sg_state->sgen_scsidev, rqpkt); 7679a016c63Sstevel 7689a016c63Sstevel rqpkt->pkt_comp = sgen_callback; 7699a016c63Sstevel rqpkt->pkt_time = SGEN_IO_TIME; 7709a016c63Sstevel rqpkt->pkt_flags |= FLAG_SENSING; 7719a016c63Sstevel rqpkt->pkt_private = sg_state; 7729a016c63Sstevel 7739a016c63Sstevel sg_state->sgen_rqspkt = rqpkt; 7749a016c63Sstevel sg_state->sgen_rqsbuf = bp; 7759a016c63Sstevel 7769a016c63Sstevel return (0); 7779a016c63Sstevel } 7789a016c63Sstevel 7799a016c63Sstevel /* 7809a016c63Sstevel * sgen_create_errstats() 781275c9da8Seschrock * create named kstats for tracking occurrence of errors. 7829a016c63Sstevel */ 7839a016c63Sstevel static void 7849a016c63Sstevel sgen_create_errstats(sgen_state_t *sg_state, int instance) 7859a016c63Sstevel { 7869a016c63Sstevel char kstatname[KSTAT_STRLEN]; 7879a016c63Sstevel struct sgen_errstats *stp; 7889a016c63Sstevel 789a204de77Scth (void) snprintf(kstatname, KSTAT_STRLEN, "%s%d,err", 790a204de77Scth sgen_label, instance); 7919a016c63Sstevel sg_state->sgen_kstats = kstat_create("sgenerr", instance, 7929a016c63Sstevel kstatname, "device_error", KSTAT_TYPE_NAMED, 7939a016c63Sstevel sizeof (struct sgen_errstats) / sizeof (kstat_named_t), 7949a016c63Sstevel KSTAT_FLAG_PERSISTENT); 7959a016c63Sstevel 7969a016c63Sstevel if (sg_state->sgen_kstats == NULL) 7979a016c63Sstevel return; 7989a016c63Sstevel 7999a016c63Sstevel stp = (struct sgen_errstats *)sg_state->sgen_kstats->ks_data; 8009a016c63Sstevel kstat_named_init(&stp->sgen_trans_err, "transport_errors", 8019a016c63Sstevel KSTAT_DATA_UINT32); 8029a016c63Sstevel kstat_named_init(&stp->sgen_restart, "command_restarts", 8039a016c63Sstevel KSTAT_DATA_UINT32); 8049a016c63Sstevel kstat_named_init(&stp->sgen_incmp_err, "incomplete_commands", 8059a016c63Sstevel KSTAT_DATA_UINT32); 8069a016c63Sstevel kstat_named_init(&stp->sgen_autosen_rcv, "autosense_occurred", 8079a016c63Sstevel KSTAT_DATA_UINT32); 8089a016c63Sstevel kstat_named_init(&stp->sgen_autosen_bad, "autosense_undecipherable", 8099a016c63Sstevel KSTAT_DATA_UINT32); 8109a016c63Sstevel kstat_named_init(&stp->sgen_sense_rcv, "sense_fetches", 8119a016c63Sstevel KSTAT_DATA_UINT32); 8129a016c63Sstevel kstat_named_init(&stp->sgen_sense_bad, "sense_data_undecipherable", 8139a016c63Sstevel KSTAT_DATA_UINT32); 8149a016c63Sstevel kstat_named_init(&stp->sgen_recov_err, "recoverable_error", 8159a016c63Sstevel KSTAT_DATA_UINT32); 8169a016c63Sstevel kstat_named_init(&stp->sgen_nosen_err, "NO_SENSE_sense_key", 8179a016c63Sstevel KSTAT_DATA_UINT32); 8189a016c63Sstevel kstat_named_init(&stp->sgen_unrecov_err, "unrecoverable_sense_error", 8199a016c63Sstevel KSTAT_DATA_UINT32); 8209a016c63Sstevel sg_state->sgen_kstats->ks_private = sg_state; 8219a016c63Sstevel sg_state->sgen_kstats->ks_update = nulldev; 8229a016c63Sstevel kstat_install(sg_state->sgen_kstats); 8239a016c63Sstevel } 8249a016c63Sstevel 8259a016c63Sstevel /* 8269a016c63Sstevel * sgen_detach() 8279a016c63Sstevel * detach(9E) entrypoint 8289a016c63Sstevel */ 8299a016c63Sstevel static int 8309a016c63Sstevel sgen_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 8319a016c63Sstevel { 8329a016c63Sstevel int instance; 8339a016c63Sstevel sgen_state_t *sg_state; 8349a016c63Sstevel 8359a016c63Sstevel instance = ddi_get_instance(dip); 8369a016c63Sstevel sg_state = ddi_get_soft_state(sgen_soft_state, instance); 8379a016c63Sstevel 8389a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "in sgen_detach(), " 8399a016c63Sstevel "device unit-address @%s", ddi_get_name_addr(dip)); 8409a016c63Sstevel 8419a016c63Sstevel if (sg_state == NULL) { 8429a016c63Sstevel sgen_log(NULL, SGEN_DIAG1, 8439a016c63Sstevel "sgen_detach: failed, no softstate found (%d), " 8449a016c63Sstevel "device unit-address @%s", 8459a016c63Sstevel instance, ddi_get_name_addr(dip)); 8469a016c63Sstevel return (DDI_FAILURE); 8479a016c63Sstevel } 8489a016c63Sstevel 8499a016c63Sstevel switch (cmd) { 8509a016c63Sstevel case DDI_DETACH: 8519a016c63Sstevel return (sgen_do_detach(dip)); 8529a016c63Sstevel case DDI_SUSPEND: 8539a016c63Sstevel return (sgen_do_suspend(dip)); 8549a016c63Sstevel case DDI_PM_SUSPEND: 8559a016c63Sstevel default: 8569a016c63Sstevel return (DDI_FAILURE); 8579a016c63Sstevel } 8589a016c63Sstevel } 8599a016c63Sstevel 8609a016c63Sstevel /* 8619a016c63Sstevel * sgen_do_detach() 8629a016c63Sstevel * detach the driver, tearing down resources. 8639a016c63Sstevel */ 8649a016c63Sstevel static int 8659a016c63Sstevel sgen_do_detach(dev_info_t *dip) 8669a016c63Sstevel { 8679a016c63Sstevel int instance; 8689a016c63Sstevel sgen_state_t *sg_state; 8699a016c63Sstevel struct scsi_device *devp; 8709a016c63Sstevel 8719a016c63Sstevel instance = ddi_get_instance(dip); 8729a016c63Sstevel sg_state = ddi_get_soft_state(sgen_soft_state, instance); 8739a016c63Sstevel ASSERT(sg_state); 8749a016c63Sstevel 8759a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "in sgen_do_detach(), " 8769a016c63Sstevel "device unit-address @%s", ddi_get_name_addr(dip)); 8779a016c63Sstevel devp = ddi_get_driver_private(dip); 8789a016c63Sstevel 8799a016c63Sstevel mutex_enter(&sg_state->sgen_mutex); 8809a016c63Sstevel if (SGEN_IS_BUSY(sg_state)) { 8819a016c63Sstevel mutex_exit(&sg_state->sgen_mutex); 8829a016c63Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_do_detach: failed because " 8839a016c63Sstevel "device is busy, device unit-address @%s", 8849a016c63Sstevel ddi_get_name_addr(dip)); 8859a016c63Sstevel return (DDI_FAILURE); 8869a016c63Sstevel } 8879a016c63Sstevel mutex_exit(&sg_state->sgen_mutex); 8889a016c63Sstevel 8899a016c63Sstevel /* 8909a016c63Sstevel * Final approach for detach. Free data allocated by scsi_probe() 8919a016c63Sstevel * in attach. 8929a016c63Sstevel */ 8939a016c63Sstevel if (sg_state->sgen_restart_timeid) 8949a016c63Sstevel (void) untimeout(sg_state->sgen_restart_timeid); 8959a016c63Sstevel sg_state->sgen_restart_timeid = 0; 8969a016c63Sstevel scsi_unprobe(devp); 8979a016c63Sstevel 8989a016c63Sstevel /* 8999a016c63Sstevel * Free auto-request plumbing. 9009a016c63Sstevel */ 9019a016c63Sstevel scsi_free_consistent_buf(sg_state->sgen_rqsbuf); 9029a016c63Sstevel scsi_destroy_pkt(sg_state->sgen_rqspkt); 9039a016c63Sstevel 9049a016c63Sstevel if (sg_state->sgen_kstats) { 9059a016c63Sstevel kstat_delete(sg_state->sgen_kstats); 9069a016c63Sstevel sg_state->sgen_kstats = NULL; 9079a016c63Sstevel } 9089a016c63Sstevel 9099a016c63Sstevel /* 9109a016c63Sstevel * Free command buffer and clean up 9119a016c63Sstevel */ 9129a016c63Sstevel freerbuf(sg_state->sgen_cmdbuf); 9139a016c63Sstevel cv_destroy(&sg_state->sgen_cmdbuf_cv); 9149a016c63Sstevel 9159a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "done sgen_do_detach(), " 9169a016c63Sstevel "device unit-address @%s", ddi_get_name_addr(dip)); 9179a016c63Sstevel 9189a016c63Sstevel ddi_soft_state_free(sgen_soft_state, instance); 9199a016c63Sstevel ddi_prop_remove_all(dip); 9209a016c63Sstevel ddi_remove_minor_node(dip, NULL); 9219a016c63Sstevel return (DDI_SUCCESS); 9229a016c63Sstevel } 9239a016c63Sstevel 9249a016c63Sstevel /* 9259a016c63Sstevel * sgen_do_suspend() 9269a016c63Sstevel * suspend the driver. This sets the "suspend" bit for this target if it 9279a016c63Sstevel * is currently open; once resumed, the suspend bit will cause 9289a016c63Sstevel * subsequent I/Os to fail. We want user programs to close and 9299a016c63Sstevel * reopen the device to acknowledge that they need to reexamine its 9309a016c63Sstevel * state and do the right thing. 9319a016c63Sstevel */ 9329a016c63Sstevel static int 9339a016c63Sstevel sgen_do_suspend(dev_info_t *dip) 9349a016c63Sstevel { 9359a016c63Sstevel int instance; 9369a016c63Sstevel sgen_state_t *sg_state; 9379a016c63Sstevel 9389a016c63Sstevel instance = ddi_get_instance(dip); 9399a016c63Sstevel sg_state = ddi_get_soft_state(sgen_soft_state, instance); 9409a016c63Sstevel ASSERT(sg_state); 9419a016c63Sstevel 9429a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "in sgen_do_suspend(), " 9439a016c63Sstevel "device unit-address @%s", ddi_get_name_addr(dip)); 9449a016c63Sstevel 9459a016c63Sstevel if (sg_state->sgen_restart_timeid) { 9469a016c63Sstevel (void) untimeout(sg_state->sgen_restart_timeid); 9479a016c63Sstevel } 9489a016c63Sstevel sg_state->sgen_restart_timeid = 0; 9499a016c63Sstevel 9509a016c63Sstevel mutex_enter(&sg_state->sgen_mutex); 9519a016c63Sstevel if (SGEN_IS_OPEN(sg_state)) 9529a016c63Sstevel SGEN_SET_SUSP(sg_state); 9539a016c63Sstevel mutex_exit(&sg_state->sgen_mutex); 9549a016c63Sstevel 9559a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "done sgen_do_suspend(), " 9569a016c63Sstevel "device unit-address @%s", ddi_get_name_addr(dip)); 9579a016c63Sstevel return (DDI_SUCCESS); 9589a016c63Sstevel } 9599a016c63Sstevel 9609a016c63Sstevel /* 9619a016c63Sstevel * sgen_getinfo() 9629a016c63Sstevel * getinfo(9e) entrypoint. 9639a016c63Sstevel */ 9649a016c63Sstevel /*ARGSUSED*/ 9659a016c63Sstevel static int 9669a016c63Sstevel sgen_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 9679a016c63Sstevel { 9689a016c63Sstevel dev_t dev; 9699a016c63Sstevel sgen_state_t *sg_state; 9709a016c63Sstevel int instance, error; 9719a016c63Sstevel switch (infocmd) { 9729a016c63Sstevel case DDI_INFO_DEVT2DEVINFO: 9739a016c63Sstevel dev = (dev_t)arg; 9749a016c63Sstevel instance = getminor(dev); 9759a016c63Sstevel if ((sg_state = ddi_get_soft_state(sgen_soft_state, instance)) 9769a016c63Sstevel == NULL) 9779a016c63Sstevel return (DDI_FAILURE); 9789a016c63Sstevel *result = (void *) sg_state->sgen_scsidev->sd_dev; 9799a016c63Sstevel error = DDI_SUCCESS; 9809a016c63Sstevel break; 9819a016c63Sstevel case DDI_INFO_DEVT2INSTANCE: 9829a016c63Sstevel dev = (dev_t)arg; 9839a016c63Sstevel instance = getminor(dev); 9849a016c63Sstevel *result = (void *)(uintptr_t)instance; 9859a016c63Sstevel error = DDI_SUCCESS; 9869a016c63Sstevel break; 9879a016c63Sstevel default: 9889a016c63Sstevel error = DDI_FAILURE; 9899a016c63Sstevel } 9909a016c63Sstevel return (error); 9919a016c63Sstevel } 9929a016c63Sstevel 9939a016c63Sstevel /* 9949a016c63Sstevel * sgen_probe() 9959a016c63Sstevel * probe(9e) entrypoint. sgen *never* returns DDI_PROBE_PARTIAL, in 9969a016c63Sstevel * order to avoid leaving around extra devinfos. If sgen's binding 9979a016c63Sstevel * rules indicate that it should bind, it returns DDI_PROBE_SUCCESS. 9989a016c63Sstevel */ 9999a016c63Sstevel static int 10009a016c63Sstevel sgen_probe(dev_info_t *dip) 10019a016c63Sstevel { 10029a016c63Sstevel struct scsi_device *scsidevp; 10039a016c63Sstevel int instance; 10049a016c63Sstevel int rval; 10059a016c63Sstevel 10069a016c63Sstevel scsidevp = ddi_get_driver_private(dip); 10079a016c63Sstevel instance = ddi_get_instance(dip); 10089a016c63Sstevel sgen_log(NULL, SGEN_DIAG2, "in sgen_probe(): instance = %d, " 10099a016c63Sstevel "device unit-address @%s", instance, ddi_get_name_addr(dip)); 10109a016c63Sstevel 10119a016c63Sstevel if (ddi_dev_is_sid(dip) == DDI_SUCCESS) 10129a016c63Sstevel return (DDI_PROBE_DONTCARE); 10139a016c63Sstevel 10149a016c63Sstevel if (ddi_get_soft_state(sgen_soft_state, instance) != NULL) 10159a016c63Sstevel return (DDI_PROBE_FAILURE); 10169a016c63Sstevel 10179a016c63Sstevel mutex_enter(&sgen_binddb.sdb_lock); 10189a016c63Sstevel if (sgen_binddb.sdb_init == 0) { 10199a016c63Sstevel sgen_setup_binddb(dip); 10209a016c63Sstevel } 10219a016c63Sstevel mutex_exit(&sgen_binddb.sdb_lock); 10229a016c63Sstevel 10239a016c63Sstevel /* 10249a016c63Sstevel * A small optimization: if it's impossible for sgen to bind to 10259a016c63Sstevel * any devices, don't bother probing, just fail. 10269a016c63Sstevel */ 10279a016c63Sstevel if ((sgen_binddb.sdb_inq_nodes == NULL) && 10289a016c63Sstevel (sgen_binddb.sdb_type_nodes == NULL)) { 10299a016c63Sstevel return (DDI_PROBE_FAILURE); 10309a016c63Sstevel } 10319a016c63Sstevel 10329a016c63Sstevel if (scsi_probe(scsidevp, NULL_FUNC) == SCSIPROBE_EXISTS) { 10339a016c63Sstevel if (sgen_get_binding(dip) == 0) { 10349a016c63Sstevel rval = DDI_PROBE_SUCCESS; 10359a016c63Sstevel } 10369a016c63Sstevel } else { 10379a016c63Sstevel rval = DDI_PROBE_FAILURE; 10389a016c63Sstevel } 10399a016c63Sstevel scsi_unprobe(scsidevp); 10409a016c63Sstevel 10419a016c63Sstevel sgen_log(NULL, SGEN_DIAG2, "sgen_probe() %s, device unit-address @%s", 10429a016c63Sstevel rval == DDI_PROBE_SUCCESS ? "succeeded" : "failed", 10439a016c63Sstevel ddi_get_name_addr(dip)); 10449a016c63Sstevel return (rval); 10459a016c63Sstevel } 10469a016c63Sstevel 10479a016c63Sstevel /* 10489a016c63Sstevel * sgen_open() 10499a016c63Sstevel * open(9e) entrypoint. sgen enforces a strict exclusive open policy per 10509a016c63Sstevel * target. 10519a016c63Sstevel */ 10529a016c63Sstevel /*ARGSUSED1*/ 10539a016c63Sstevel static int 10549a016c63Sstevel sgen_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p) 10559a016c63Sstevel { 10569a016c63Sstevel dev_t dev = *dev_p; 10579a016c63Sstevel sgen_state_t *sg_state; 10589a016c63Sstevel int instance; 10599a016c63Sstevel 10609a016c63Sstevel instance = getminor(dev); 10619a016c63Sstevel 10629a016c63Sstevel if ((sg_state = ddi_get_soft_state(sgen_soft_state, instance)) == NULL) 10639a016c63Sstevel return (ENXIO); 10649a016c63Sstevel 10659a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "in sgen_open(): instance = %d", 10669a016c63Sstevel instance); 10679a016c63Sstevel 10689a016c63Sstevel mutex_enter(&sg_state->sgen_mutex); 10699a016c63Sstevel 1070275c9da8Seschrock /* 1071275c9da8Seschrock * Don't allow new opens of a suspended device until the last close has 1072275c9da8Seschrock * happened. This is rather simplistic, but keeps the implementation 1073275c9da8Seschrock * straightforward. 1074275c9da8Seschrock */ 1075275c9da8Seschrock if (SGEN_IS_SUSP(sg_state)) { 1076275c9da8Seschrock mutex_exit(&sg_state->sgen_mutex); 1077275c9da8Seschrock return (EIO); 1078275c9da8Seschrock } 1079275c9da8Seschrock 1080275c9da8Seschrock /* 1081275c9da8Seschrock * Enforce exclusive access. 1082275c9da8Seschrock */ 1083275c9da8Seschrock if (SGEN_IS_EXCL(sg_state) || 1084275c9da8Seschrock (SGEN_IS_OPEN(sg_state) && (flag & FEXCL))) { 10859a016c63Sstevel mutex_exit(&sg_state->sgen_mutex); 10869a016c63Sstevel return (EBUSY); 10879a016c63Sstevel } 10889a016c63Sstevel 1089275c9da8Seschrock if (flag & FEXCL) 1090275c9da8Seschrock SGEN_SET_EXCL(sg_state); 1091275c9da8Seschrock 10929a016c63Sstevel SGEN_SET_OPEN(sg_state); 10939a016c63Sstevel 10949a016c63Sstevel mutex_exit(&sg_state->sgen_mutex); 10959a016c63Sstevel 10969a016c63Sstevel return (0); 10979a016c63Sstevel } 10989a016c63Sstevel 10999a016c63Sstevel /* 11009a016c63Sstevel * sgen_close() 11019a016c63Sstevel * close(9e) entrypoint. 11029a016c63Sstevel */ 11039a016c63Sstevel /*ARGSUSED1*/ 11049a016c63Sstevel static int 11059a016c63Sstevel sgen_close(dev_t dev, int flag, int otyp, cred_t *cred_p) 11069a016c63Sstevel { 11079a016c63Sstevel sgen_state_t *sg_state; 11089a016c63Sstevel int instance; 11099a016c63Sstevel 11109a016c63Sstevel instance = getminor(dev); 11119a016c63Sstevel 11129a016c63Sstevel if ((sg_state = ddi_get_soft_state(sgen_soft_state, instance)) == NULL) 11139a016c63Sstevel return (ENXIO); 11149a016c63Sstevel 11159a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "in sgen_close(): instance = %d", 11169a016c63Sstevel instance); 11179a016c63Sstevel 11189a016c63Sstevel mutex_enter(&sg_state->sgen_mutex); 11199a016c63Sstevel SGEN_CLR_OPEN(sg_state); 1120275c9da8Seschrock SGEN_CLR_EXCL(sg_state); 11219a016c63Sstevel SGEN_CLR_SUSP(sg_state); /* closing clears the 'I was suspended' bit */ 11229a016c63Sstevel mutex_exit(&sg_state->sgen_mutex); 11239a016c63Sstevel 11249a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "done sgen_close()"); 11259a016c63Sstevel 11269a016c63Sstevel return (0); 11279a016c63Sstevel } 11289a016c63Sstevel 11299a016c63Sstevel /* 11309a016c63Sstevel * sgen_ioctl() 11319a016c63Sstevel * sgen supports the USCSI(7I) ioctl interface. 11329a016c63Sstevel */ 11339a016c63Sstevel /*ARGSUSED4*/ 11349a016c63Sstevel static int 11359a016c63Sstevel sgen_ioctl(dev_t dev, 11369a016c63Sstevel int cmd, intptr_t arg, int flag, cred_t *cred_p, int *rval_p) 11379a016c63Sstevel { 11389a016c63Sstevel int retval = 0; 11399a016c63Sstevel sgen_state_t *sg_state; 11409a016c63Sstevel int instance; 11419a016c63Sstevel 11429a016c63Sstevel instance = getminor(dev); 11439a016c63Sstevel 11449a016c63Sstevel if ((sg_state = ddi_get_soft_state(sgen_soft_state, instance)) == NULL) 11459a016c63Sstevel return (ENXIO); 11469a016c63Sstevel 11479a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "in sgen_ioctl(): instance = %d", 11489a016c63Sstevel instance); 11499a016c63Sstevel 11509a016c63Sstevel /* 11519a016c63Sstevel * If the driver has been suspended since the last open, fail all 11529a016c63Sstevel * subsequent IO's so that the userland consumer reinitializes state. 11539a016c63Sstevel */ 11549a016c63Sstevel mutex_enter(&sg_state->sgen_mutex); 11559a016c63Sstevel if (SGEN_IS_SUSP(sg_state)) { 11569a016c63Sstevel mutex_exit(&sg_state->sgen_mutex); 11579a016c63Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_ioctl: returning EIO: " 11589a016c63Sstevel "driver instance %d was previously suspended", instance); 11599a016c63Sstevel return (EIO); 11609a016c63Sstevel } 11619a016c63Sstevel mutex_exit(&sg_state->sgen_mutex); 11629a016c63Sstevel 11639a016c63Sstevel switch (cmd) { 11649a016c63Sstevel case SGEN_IOC_DIAG: { 11659a016c63Sstevel if (arg > 3) { 11669a016c63Sstevel arg = 0; 11679a016c63Sstevel } 11689a016c63Sstevel sg_state->sgen_diag = (int)arg; 11699a016c63Sstevel retval = 0; 11709a016c63Sstevel break; 11719a016c63Sstevel } 11729a016c63Sstevel 11739a016c63Sstevel case SGEN_IOC_READY: { 11749a016c63Sstevel if (sgen_tur(dev) != 0) { 11759a016c63Sstevel retval = EIO; 11769a016c63Sstevel } else { 11779a016c63Sstevel retval = 0; 11789a016c63Sstevel } 11799a016c63Sstevel break; 11809a016c63Sstevel } 11819a016c63Sstevel 11826567eb0aSlh195018 case USCSICMD: 11836567eb0aSlh195018 retval = sgen_uscsi_cmd(dev, (struct uscsi_cmd *)arg, flag); 11846567eb0aSlh195018 break; 11859a016c63Sstevel 11869a016c63Sstevel default: 11879a016c63Sstevel retval = ENOTTY; 11889a016c63Sstevel } 11899a016c63Sstevel 11909a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "done sgen_ioctl(), returning %d", 11919a016c63Sstevel retval); 11929a016c63Sstevel 11939a016c63Sstevel return (retval); 11949a016c63Sstevel } 11959a016c63Sstevel 11969a016c63Sstevel /* 11979a016c63Sstevel * sgen_uscsi_cmd() 11989a016c63Sstevel * Setup, configuration and teardown for a uscsi(7I) command 11999a016c63Sstevel */ 12009a016c63Sstevel /*ARGSUSED*/ 12019a016c63Sstevel static int 12026567eb0aSlh195018 sgen_uscsi_cmd(dev_t dev, struct uscsi_cmd *ucmd, int flag) 12039a016c63Sstevel { 12046567eb0aSlh195018 struct uscsi_cmd *uscmd; 12059a016c63Sstevel struct buf *bp; 12069a016c63Sstevel sgen_state_t *sg_state; 12076567eb0aSlh195018 enum uio_seg uioseg; 12086567eb0aSlh195018 int instance; 12096567eb0aSlh195018 int flags; 12106567eb0aSlh195018 int err; 12119a016c63Sstevel 12129a016c63Sstevel instance = getminor(dev); 12139a016c63Sstevel 12149a016c63Sstevel sg_state = ddi_get_soft_state(sgen_soft_state, instance); 12159a016c63Sstevel ASSERT(sg_state); 12169a016c63Sstevel 12179a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "in sgen_uscsi_cmd(): instance = %d", 12189a016c63Sstevel instance); 12199a016c63Sstevel 1220275c9da8Seschrock /* 1221275c9da8Seschrock * At this point, we start affecting state relevant to the target, 1222275c9da8Seschrock * so access needs to be serialized. 1223275c9da8Seschrock */ 1224275c9da8Seschrock if (sgen_hold_cmdbuf(sg_state) != 0) { 1225275c9da8Seschrock sgen_log(sg_state, SGEN_DIAG1, "sgen_uscsi_cmd: interrupted"); 1226275c9da8Seschrock return (EINTR); 1227275c9da8Seschrock } 1228275c9da8Seschrock 12296567eb0aSlh195018 err = scsi_uscsi_alloc_and_copyin((intptr_t)ucmd, flag, 12306567eb0aSlh195018 &sg_state->sgen_scsiaddr, &uscmd); 12316567eb0aSlh195018 if (err != 0) { 1232275c9da8Seschrock sgen_rele_cmdbuf(sg_state); 12336567eb0aSlh195018 sgen_log(sg_state, SGEN_DIAG1, "sgen_uscsi_cmd: " 12346567eb0aSlh195018 "scsi_uscsi_alloc_and_copyin failed\n"); 12356567eb0aSlh195018 return (err); 12366567eb0aSlh195018 } 12379a016c63Sstevel 12389a016c63Sstevel /* 12399a016c63Sstevel * Clear out undesirable command flags 12409a016c63Sstevel */ 12416567eb0aSlh195018 flags = (uscmd->uscsi_flags & ~(USCSI_NOINTR | USCSI_NOPARITY | 12429a016c63Sstevel USCSI_OTAG | USCSI_HTAG | USCSI_HEAD)); 12436567eb0aSlh195018 if (flags != uscmd->uscsi_flags) { 12449a016c63Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_uscsi_cmd: cleared " 1245ad72f872Slh195018 "unsafe uscsi_flags 0x%x", uscmd->uscsi_flags & ~flags); 12466567eb0aSlh195018 uscmd->uscsi_flags = flags; 12479a016c63Sstevel } 12489a016c63Sstevel 12496567eb0aSlh195018 if (uscmd->uscsi_cdb != NULL) { 12509a016c63Sstevel sgen_dump_cdb(sg_state, "sgen_uscsi_cmd: ", 12516567eb0aSlh195018 (union scsi_cdb *)uscmd->uscsi_cdb, uscmd->uscsi_cdblen); 12526567eb0aSlh195018 } 12539a016c63Sstevel 12549a016c63Sstevel /* 12559a016c63Sstevel * Stash the sense buffer into sgen_rqs_sen for convenience. 12569a016c63Sstevel */ 12576567eb0aSlh195018 sg_state->sgen_rqs_sen = uscmd->uscsi_rqbuf; 12589a016c63Sstevel 12596567eb0aSlh195018 bp = sg_state->sgen_cmdbuf; 12609a016c63Sstevel bp->av_back = NULL; 12619a016c63Sstevel bp->av_forw = NULL; 12626567eb0aSlh195018 bp->b_private = (struct buf *)uscmd; 12636567eb0aSlh195018 uioseg = (flag & FKIOCTL) ? UIO_SYSSPACE : UIO_USERSPACE; 12649a016c63Sstevel 12656567eb0aSlh195018 err = scsi_uscsi_handle_cmd(dev, uioseg, uscmd, sgen_start, bp, NULL); 12669a016c63Sstevel 12679a016c63Sstevel if (sg_state->sgen_cmdpkt != NULL) { 12686567eb0aSlh195018 uscmd->uscsi_status = SCBP_C(sg_state->sgen_cmdpkt); 12699a016c63Sstevel } else { 12706567eb0aSlh195018 uscmd->uscsi_status = 0; 12719a016c63Sstevel } 12729a016c63Sstevel 12739a016c63Sstevel sgen_log(sg_state, SGEN_DIAG3, "sgen_uscsi_cmd: awake from waiting " 12746567eb0aSlh195018 "for command. Status is 0x%x", uscmd->uscsi_status); 12759a016c63Sstevel 12766567eb0aSlh195018 if (uscmd->uscsi_rqbuf != NULL) { 12776567eb0aSlh195018 int rqlen = uscmd->uscsi_rqlen - uscmd->uscsi_rqresid; 12789a016c63Sstevel sgen_dump_sense(sg_state, rqlen, 12796567eb0aSlh195018 (uchar_t *)uscmd->uscsi_rqbuf); 12809a016c63Sstevel } 12819a016c63Sstevel 12826567eb0aSlh195018 (void) scsi_uscsi_copyout_and_free((intptr_t)ucmd, uscmd); 12836567eb0aSlh195018 12849a016c63Sstevel if (sg_state->sgen_cmdpkt != NULL) { 12859a016c63Sstevel scsi_destroy_pkt(sg_state->sgen_cmdpkt); 12869a016c63Sstevel sg_state->sgen_cmdpkt = NULL; 12879a016c63Sstevel } 12889a016c63Sstevel 12899a016c63Sstevel /* 12909a016c63Sstevel * After this point, we can't touch per-target state. 12919a016c63Sstevel */ 12929a016c63Sstevel sgen_rele_cmdbuf(sg_state); 12939a016c63Sstevel 12949a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "done sgen_uscsi_cmd()"); 12959a016c63Sstevel 12969a016c63Sstevel return (err); 12979a016c63Sstevel } 12989a016c63Sstevel 12999a016c63Sstevel /* 13009a016c63Sstevel * sgen_hold_cmdbuf() 1301275c9da8Seschrock * Acquire a lock on the command buffer for the given target. Returns 1302275c9da8Seschrock * non-zero if interrupted. 13039a016c63Sstevel */ 1304275c9da8Seschrock static int 13059a016c63Sstevel sgen_hold_cmdbuf(sgen_state_t *sg_state) 13069a016c63Sstevel { 13079a016c63Sstevel mutex_enter(&sg_state->sgen_mutex); 1308275c9da8Seschrock while (SGEN_IS_BUSY(sg_state)) { 1309275c9da8Seschrock if (!cv_wait_sig(&sg_state->sgen_cmdbuf_cv, 1310275c9da8Seschrock &sg_state->sgen_mutex)) { 1311275c9da8Seschrock mutex_exit(&sg_state->sgen_mutex); 1312275c9da8Seschrock return (-1); 1313275c9da8Seschrock } 1314275c9da8Seschrock } 13159a016c63Sstevel SGEN_SET_BUSY(sg_state); 13169a016c63Sstevel mutex_exit(&sg_state->sgen_mutex); 1317275c9da8Seschrock return (0); 13189a016c63Sstevel } 13199a016c63Sstevel 13209a016c63Sstevel /* 13219a016c63Sstevel * sgen_rele_cmdbuf() 13229a016c63Sstevel * release the command buffer for a particular target. 13239a016c63Sstevel */ 13249a016c63Sstevel static void 13259a016c63Sstevel sgen_rele_cmdbuf(sgen_state_t *sg_state) 13269a016c63Sstevel { 13279a016c63Sstevel mutex_enter(&sg_state->sgen_mutex); 13289a016c63Sstevel SGEN_CLR_BUSY(sg_state); 13299a016c63Sstevel cv_signal(&sg_state->sgen_cmdbuf_cv); 13309a016c63Sstevel mutex_exit(&sg_state->sgen_mutex); 13319a016c63Sstevel } 13329a016c63Sstevel 13339a016c63Sstevel /* 13349a016c63Sstevel * sgen_start() 13359a016c63Sstevel * Transport a uscsi command; this is invoked by physio() or directly 13369a016c63Sstevel * by sgen_uscsi_cmd(). 13379a016c63Sstevel */ 13389a016c63Sstevel static int 13399a016c63Sstevel sgen_start(struct buf *bp) 13409a016c63Sstevel { 13419a016c63Sstevel sgen_state_t *sg_state; 13429a016c63Sstevel dev_t dev = bp->b_edev; 13439a016c63Sstevel int trans_err; 13449a016c63Sstevel 13459a016c63Sstevel if ((sg_state = ddi_get_soft_state(sgen_soft_state, 13469a016c63Sstevel getminor(dev))) == NULL) { 13479a016c63Sstevel bp->b_resid = bp->b_bcount; 13489a016c63Sstevel bioerror(bp, ENXIO); 13499a016c63Sstevel biodone(bp); 13509a016c63Sstevel return (ENXIO); 13519a016c63Sstevel } 13529a016c63Sstevel 13539a016c63Sstevel /* 13549a016c63Sstevel * Sanity checks - command should not be complete, no packet should 13559a016c63Sstevel * be allocated, and there ought to be a uscsi cmd in b_private 13569a016c63Sstevel */ 13579a016c63Sstevel ASSERT(bp == sg_state->sgen_cmdbuf && sg_state->sgen_cmdpkt == NULL); 13589a016c63Sstevel ASSERT((bp->b_flags & B_DONE) == 0); 13599a016c63Sstevel ASSERT(bp->b_private); 13609a016c63Sstevel if (sgen_make_uscsi_cmd(sg_state, bp) != 0) { 13619a016c63Sstevel bp->b_resid = bp->b_bcount; 13629a016c63Sstevel bioerror(bp, EFAULT); 13639a016c63Sstevel biodone(bp); 13649a016c63Sstevel return (EFAULT); 13659a016c63Sstevel } 13669a016c63Sstevel 13679a016c63Sstevel ASSERT(sg_state->sgen_cmdpkt != NULL); 13689a016c63Sstevel 13699a016c63Sstevel /* 13709a016c63Sstevel * Clear out the residual and error fields 13719a016c63Sstevel */ 13729a016c63Sstevel bp->b_resid = 0; 13739a016c63Sstevel bp->b_error = 0; 13749a016c63Sstevel 13759a016c63Sstevel trans_err = sgen_scsi_transport(sg_state->sgen_cmdpkt); 13769a016c63Sstevel switch (trans_err) { 13779a016c63Sstevel case TRAN_ACCEPT: 13789a016c63Sstevel break; 13799a016c63Sstevel case TRAN_BUSY: 13809a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, 13819a016c63Sstevel "sgen_start: scsi_transport() returned TRAN_BUSY"); 13829a016c63Sstevel sg_state->sgen_restart_timeid = timeout(sgen_restart, sg_state, 13839a016c63Sstevel SGEN_BSY_TIMEOUT); 13849a016c63Sstevel break; 13859a016c63Sstevel default: 13869a016c63Sstevel /* 13879a016c63Sstevel * Indicate there has been an I/O transfer error. 13889a016c63Sstevel * Be done with the command. 13899a016c63Sstevel */ 13909a016c63Sstevel mutex_enter(&sg_state->sgen_mutex); 13919a016c63Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_trans_err); 13929a016c63Sstevel mutex_exit(&sg_state->sgen_mutex); 13939a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "sgen_start: scsi_transport() " 13949a016c63Sstevel "returned %d", trans_err); 13959a016c63Sstevel bioerror(bp, EIO); 13969a016c63Sstevel biodone(bp); 13979a016c63Sstevel return (EIO); 13989a016c63Sstevel } 13999a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "sgen_start: b_flags 0x%x", bp->b_flags); 14009a016c63Sstevel return (0); 14019a016c63Sstevel } 14029a016c63Sstevel 14039a016c63Sstevel /* 14049a016c63Sstevel * sgen_scsi_transport() 14059a016c63Sstevel * a simple scsi_transport() wrapper which can be configured to inject 14069a016c63Sstevel * sporadic errors for testing. 14079a016c63Sstevel */ 14089a016c63Sstevel static int 14099a016c63Sstevel sgen_scsi_transport(struct scsi_pkt *pkt) 14109a016c63Sstevel { 14119a016c63Sstevel int trans_err; 14129a016c63Sstevel static int cnt = 0; 14139a016c63Sstevel sgen_state_t *sg_state = pkt->pkt_private; 14149a016c63Sstevel 14159a016c63Sstevel if (sgen_sporadic_failures == 0) { 14169a016c63Sstevel return (scsi_transport(pkt)); 14179a016c63Sstevel } 14189a016c63Sstevel 14199a016c63Sstevel cnt = (cnt * 2416 + 374441) % 1771875; /* borrowed from kmem.c */ 14209a016c63Sstevel if (cnt % 40 == 1) { 14219a016c63Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_scsi_transport: " 14229a016c63Sstevel "injecting sporadic BUSY"); 14239a016c63Sstevel trans_err = TRAN_BUSY; 14249a016c63Sstevel } else if (cnt % 40 == 2) { 14259a016c63Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_scsi_transport: " 14269a016c63Sstevel "injecting sporadic BADPKT"); 14279a016c63Sstevel trans_err = TRAN_BADPKT; 14289a016c63Sstevel } else { 14299a016c63Sstevel /* 14309a016c63Sstevel * Most of the time we take the normal path 14319a016c63Sstevel */ 14329a016c63Sstevel trans_err = scsi_transport(pkt); 14339a016c63Sstevel } 14349a016c63Sstevel return (trans_err); 14359a016c63Sstevel } 14369a016c63Sstevel 14379a016c63Sstevel /* 14389a016c63Sstevel * sgen_make_uscsi_cmd() 14399a016c63Sstevel * Initialize a SCSI packet usable for USCSI. 14409a016c63Sstevel */ 14419a016c63Sstevel static int 14429a016c63Sstevel sgen_make_uscsi_cmd(sgen_state_t *sg_state, struct buf *bp) 14439a016c63Sstevel { 14449a016c63Sstevel struct scsi_pkt *pkt; 14459a016c63Sstevel struct uscsi_cmd *ucmd; 144630ab6db6Slh195018 int stat_size = 1; 144730ab6db6Slh195018 int flags = 0; 14489a016c63Sstevel 14499a016c63Sstevel ASSERT(bp); 14509a016c63Sstevel 14519a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "in sgen_make_uscsi_cmd()"); 14529a016c63Sstevel 14539a016c63Sstevel ucmd = (struct uscsi_cmd *)bp->b_private; 14549a016c63Sstevel 14559a016c63Sstevel if (ucmd->uscsi_flags & USCSI_RQENABLE) { 145630ab6db6Slh195018 if (ucmd->uscsi_rqlen > SENSE_LENGTH) { 145730ab6db6Slh195018 stat_size = (int)(ucmd->uscsi_rqlen) + 145830ab6db6Slh195018 sizeof (struct scsi_arq_status) - 145930ab6db6Slh195018 sizeof (struct scsi_extended_sense); 146030ab6db6Slh195018 flags = PKT_XARQ; 14613a27445bSdm120769 } else { 146230ab6db6Slh195018 stat_size = sizeof (struct scsi_arq_status); 146330ab6db6Slh195018 } 14649a016c63Sstevel } 14659a016c63Sstevel 14669a016c63Sstevel sgen_log(sg_state, SGEN_DIAG3, "sgen_make_uscsi_cmd: b_bcount = %ld", 14679a016c63Sstevel bp->b_bcount); 14689a016c63Sstevel pkt = scsi_init_pkt(&sg_state->sgen_scsiaddr, 14699a016c63Sstevel NULL, /* in_pkt - null so it'll be alloc'd */ 14709a016c63Sstevel bp->b_bcount ? bp : NULL, /* buf structure for data xfer */ 14719a016c63Sstevel ucmd->uscsi_cdblen, /* cmdlen */ 14729a016c63Sstevel stat_size, /* statuslen */ 14739a016c63Sstevel 0, /* privatelen */ 147430ab6db6Slh195018 flags, /* flags */ 14759a016c63Sstevel SLEEP_FUNC, /* callback */ 14769a016c63Sstevel (caddr_t)sg_state); /* callback_arg */ 14779a016c63Sstevel 14789a016c63Sstevel if (pkt == NULL) { 14799a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "failed sgen_make_uscsi_cmd()"); 14809a016c63Sstevel return (-1); 14819a016c63Sstevel } 14829a016c63Sstevel 14839a016c63Sstevel pkt->pkt_comp = sgen_callback; 14849a016c63Sstevel pkt->pkt_private = sg_state; 14859a016c63Sstevel sg_state->sgen_cmdpkt = pkt; 14869a016c63Sstevel 14879a016c63Sstevel /* 14889a016c63Sstevel * We *don't* call scsi_setup_cdb here, as is customary, since the 14899a016c63Sstevel * user could specify a command from one group, but pass cdblen 14909a016c63Sstevel * as something totally different. If cdblen is smaller than expected, 14919a016c63Sstevel * this results in scsi_setup_cdb writing past the end of the cdb. 14929a016c63Sstevel */ 14939a016c63Sstevel bcopy(ucmd->uscsi_cdb, pkt->pkt_cdbp, ucmd->uscsi_cdblen); 14949a016c63Sstevel if (ucmd->uscsi_cdblen >= CDB_GROUP0) { 14959a016c63Sstevel FILL_SCSI1_LUN(sg_state->sgen_scsidev, pkt); 14969a016c63Sstevel } 14979a016c63Sstevel 14989a016c63Sstevel if (ucmd->uscsi_timeout > 0) 14999a016c63Sstevel pkt->pkt_time = ucmd->uscsi_timeout; 15009a016c63Sstevel else 15019a016c63Sstevel pkt->pkt_time = SGEN_IO_TIME; 15029a016c63Sstevel 15039a016c63Sstevel /* 15049a016c63Sstevel * Set packet options 15059a016c63Sstevel */ 15069a016c63Sstevel if (ucmd->uscsi_flags & USCSI_SILENT) 15079a016c63Sstevel pkt->pkt_flags |= FLAG_SILENT; 15089a016c63Sstevel if (ucmd->uscsi_flags & USCSI_ISOLATE) 15099a016c63Sstevel pkt->pkt_flags |= FLAG_ISOLATE; 15109a016c63Sstevel if (ucmd->uscsi_flags & USCSI_DIAGNOSE) 15119a016c63Sstevel pkt->pkt_flags |= FLAG_DIAGNOSE; 15129a016c63Sstevel if (ucmd->uscsi_flags & USCSI_RENEGOT) { 15139a016c63Sstevel pkt->pkt_flags |= FLAG_RENEGOTIATE_WIDE_SYNC; 15149a016c63Sstevel } 15159a016c63Sstevel 1516602ca9eaScth /* Transfer uscsi information to scsi_pkt */ 1517602ca9eaScth (void) scsi_uscsi_pktinit(ucmd, pkt); 1518602ca9eaScth 15199a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "done sgen_make_uscsi_cmd()"); 15209a016c63Sstevel return (0); 15219a016c63Sstevel } 15229a016c63Sstevel 15239a016c63Sstevel 15249a016c63Sstevel /* 15259a016c63Sstevel * sgen_restart() 15269a016c63Sstevel * sgen_restart() is called after a timeout, when a command has been 15279a016c63Sstevel * postponed due to a TRAN_BUSY response from the HBA. 15289a016c63Sstevel */ 15299a016c63Sstevel static void 15309a016c63Sstevel sgen_restart(void *arg) 15319a016c63Sstevel { 15329a016c63Sstevel sgen_state_t *sg_state = (sgen_state_t *)arg; 15339a016c63Sstevel struct scsi_pkt *pkt; 15349a016c63Sstevel struct buf *bp; 15359a016c63Sstevel 15369a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "in sgen_restart()"); 15379a016c63Sstevel 15389a016c63Sstevel bp = sg_state->sgen_cmdbuf; 15399a016c63Sstevel pkt = sg_state->sgen_cmdpkt; 15409a016c63Sstevel ASSERT(bp && pkt); 15419a016c63Sstevel 15429a016c63Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_restart); 15439a016c63Sstevel 15449a016c63Sstevel /* 15459a016c63Sstevel * If the packet is marked with the sensing flag, sgen is off running 15469a016c63Sstevel * a request sense, and *that packet* is what needs to be restarted. 15479a016c63Sstevel */ 15489a016c63Sstevel if (pkt->pkt_flags & FLAG_SENSING) { 15499a016c63Sstevel sgen_log(sg_state, SGEN_DIAG3, 15509a016c63Sstevel "sgen_restart: restarting REQUEST SENSE"); 15519a016c63Sstevel pkt = sg_state->sgen_rqspkt; 15529a016c63Sstevel } 15539a016c63Sstevel 15549a016c63Sstevel if (sgen_scsi_transport(pkt) != TRAN_ACCEPT) { 15559a016c63Sstevel bp->b_resid = bp->b_bcount; 15569a016c63Sstevel bioerror(bp, EIO); 15579a016c63Sstevel biodone(bp); 15589a016c63Sstevel } 15599a016c63Sstevel } 15609a016c63Sstevel 15619a016c63Sstevel /* 15629a016c63Sstevel * sgen_callback() 15639a016c63Sstevel * Command completion processing 15649a016c63Sstevel * 15659a016c63Sstevel * sgen's completion processing is very pessimistic-- it does not retry 15669a016c63Sstevel * failed commands; instead, it allows the user application to make 15679a016c63Sstevel * decisions about what has gone wrong. 15689a016c63Sstevel */ 15699a016c63Sstevel static void 15709a016c63Sstevel sgen_callback(struct scsi_pkt *pkt) 15719a016c63Sstevel { 15729a016c63Sstevel sgen_state_t *sg_state; 1573602ca9eaScth struct uscsi_cmd *ucmd; 15749a016c63Sstevel struct buf *bp; 15759a016c63Sstevel int action; 15769a016c63Sstevel 15779a016c63Sstevel sg_state = pkt->pkt_private; 15789a016c63Sstevel /* 15799a016c63Sstevel * bp should always be the command buffer regardless of whether 15809a016c63Sstevel * this is a command completion or a request-sense completion. 15819a016c63Sstevel * This is because there is no need to biodone() the sense buf 15829a016c63Sstevel * when it completes-- we want to biodone() the actual command buffer! 15839a016c63Sstevel */ 15849a016c63Sstevel bp = sg_state->sgen_cmdbuf; 15859a016c63Sstevel if (pkt->pkt_flags & FLAG_SENSING) { 15869a016c63Sstevel ASSERT(pkt == sg_state->sgen_rqspkt); 15879a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, 15889a016c63Sstevel "in sgen_callback() (SENSE completion callback)"); 15899a016c63Sstevel } else { 15909a016c63Sstevel ASSERT(pkt == sg_state->sgen_cmdpkt); 15919a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, 15929a016c63Sstevel "in sgen_callback() (command completion callback)"); 15939a016c63Sstevel } 1594602ca9eaScth ucmd = (struct uscsi_cmd *)bp->b_private; 15959a016c63Sstevel 15969a016c63Sstevel sgen_log(sg_state, SGEN_DIAG3, "sgen_callback: reason=0x%x resid=%ld " 15979a016c63Sstevel "state=0x%x", pkt->pkt_reason, pkt->pkt_resid, pkt->pkt_state); 15989a016c63Sstevel 1599602ca9eaScth /* Transfer scsi_pkt information to uscsi */ 1600602ca9eaScth (void) scsi_uscsi_pktfini(pkt, ucmd); 1601602ca9eaScth 16029a016c63Sstevel if (pkt->pkt_reason != CMD_CMPLT) { 16039a016c63Sstevel /* 16049a016c63Sstevel * The command did not complete. 16059a016c63Sstevel */ 16069a016c63Sstevel sgen_log(sg_state, SGEN_DIAG3, 16079a016c63Sstevel "sgen_callback: command did not complete"); 16089a016c63Sstevel action = sgen_handle_incomplete(sg_state, pkt); 16099a016c63Sstevel } else if (sg_state->sgen_arq_enabled && 16109a016c63Sstevel (pkt->pkt_state & STATE_ARQ_DONE)) { 16119a016c63Sstevel /* 16129a016c63Sstevel * The auto-rqsense happened, and the packet has a filled-in 16139a016c63Sstevel * scsi_arq_status structure, pointed to by pkt_scbp. 16149a016c63Sstevel */ 16159a016c63Sstevel sgen_log(sg_state, SGEN_DIAG3, 16169a016c63Sstevel "sgen_callback: received auto-requested sense"); 16179a016c63Sstevel action = sgen_handle_autosense(sg_state, pkt); 16189a016c63Sstevel ASSERT(action != FETCH_SENSE); 16199a016c63Sstevel } else if (pkt->pkt_flags & FLAG_SENSING) { 16209a016c63Sstevel /* 16219a016c63Sstevel * sgen was running a REQUEST SENSE. Decode the sense data and 16229a016c63Sstevel * decide what to do next. 16239a016c63Sstevel * 16249a016c63Sstevel * Clear FLAG_SENSING on the original packet for completeness. 16259a016c63Sstevel */ 16269a016c63Sstevel sgen_log(sg_state, SGEN_DIAG3, "sgen_callback: received sense"); 16279a016c63Sstevel sg_state->sgen_cmdpkt->pkt_flags &= ~FLAG_SENSING; 16289a016c63Sstevel action = sgen_handle_sense(sg_state); 16299a016c63Sstevel ASSERT(action != FETCH_SENSE); 16309a016c63Sstevel } else { 16319a016c63Sstevel /* 16329a016c63Sstevel * Command completed and we're not getting sense. Check for 16339a016c63Sstevel * errors and decide what to do next. 16349a016c63Sstevel */ 16359a016c63Sstevel sgen_log(sg_state, SGEN_DIAG3, 16369a016c63Sstevel "sgen_callback: command appears complete"); 16379a016c63Sstevel action = sgen_check_error(sg_state, bp); 16389a016c63Sstevel } 16399a016c63Sstevel 16409a016c63Sstevel switch (action) { 16419a016c63Sstevel case FETCH_SENSE: 16429a016c63Sstevel /* 16439a016c63Sstevel * If there is sense to fetch, break out to prevent biodone'ing 16449a016c63Sstevel * until the sense fetch is complete. 16459a016c63Sstevel */ 1646602ca9eaScth if (sgen_initiate_sense(sg_state, 1647602ca9eaScth scsi_pkt_allocated_correctly(pkt) ? 1648602ca9eaScth pkt->pkt_path_instance : 0) == 0) 16499a016c63Sstevel break; 16509a016c63Sstevel /*FALLTHROUGH*/ 16519a016c63Sstevel case COMMAND_DONE_ERROR: 16529a016c63Sstevel bp->b_resid = bp->b_bcount; 16539a016c63Sstevel bioerror(bp, EIO); 16549a016c63Sstevel /*FALLTHROUGH*/ 16559a016c63Sstevel case COMMAND_DONE: 16569a016c63Sstevel biodone(bp); 16579a016c63Sstevel break; 16589a016c63Sstevel default: 16599a016c63Sstevel ASSERT(0); 16609a016c63Sstevel break; 16619a016c63Sstevel } 16629a016c63Sstevel 16639a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "done sgen_callback()"); 16649a016c63Sstevel } 16659a016c63Sstevel 16669a016c63Sstevel /* 16679a016c63Sstevel * sgen_initiate_sense() 16689a016c63Sstevel * Send the sgen_rqspkt to the target, thereby requesting sense data. 16699a016c63Sstevel */ 16709a016c63Sstevel static int 1671602ca9eaScth sgen_initiate_sense(sgen_state_t *sg_state, int path_instance) 16729a016c63Sstevel { 1673602ca9eaScth /* use same path_instance as command */ 1674602ca9eaScth if (scsi_pkt_allocated_correctly(sg_state->sgen_rqspkt)) 1675602ca9eaScth sg_state->sgen_rqspkt->pkt_path_instance = path_instance; 1676602ca9eaScth 16779a016c63Sstevel switch (sgen_scsi_transport(sg_state->sgen_rqspkt)) { 16789a016c63Sstevel case TRAN_ACCEPT: 16799a016c63Sstevel sgen_log(sg_state, SGEN_DIAG3, "sgen_initiate_sense: " 16809a016c63Sstevel "sense fetch transport accepted."); 16819a016c63Sstevel return (0); 16829a016c63Sstevel case TRAN_BUSY: 16839a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "sgen_initiate_sense: " 16849a016c63Sstevel "sense fetch transport busy, setting timeout."); 16859a016c63Sstevel sg_state->sgen_restart_timeid = timeout(sgen_restart, sg_state, 16869a016c63Sstevel SGEN_BSY_TIMEOUT); 16879a016c63Sstevel return (0); 16889a016c63Sstevel default: 16899a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "sgen_initiate_sense: " 16909a016c63Sstevel "sense fetch transport failed or busy."); 16919a016c63Sstevel return (-1); 16929a016c63Sstevel } 16939a016c63Sstevel } 16949a016c63Sstevel 16959a016c63Sstevel /* 16969a016c63Sstevel * sgen_handle_incomplete() 16979a016c63Sstevel * sgen is pessimistic, but also careful-- it doesn't try to retry 16989a016c63Sstevel * incomplete commands, but it also doesn't go resetting devices; 16999a016c63Sstevel * it is hard to tell if the device will be tolerant of that sort 17009a016c63Sstevel * of prodding. 17019a016c63Sstevel * 17029a016c63Sstevel * This routine has been left as a guide for the future--- the 17039a016c63Sstevel * current administration's hands-off policy may need modification. 17049a016c63Sstevel */ 17059a016c63Sstevel /*ARGSUSED*/ 17069a016c63Sstevel static int 17079a016c63Sstevel sgen_handle_incomplete(sgen_state_t *sg_state, struct scsi_pkt *pkt) 17089a016c63Sstevel { 17099a016c63Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_incmp_err); 17109a016c63Sstevel return (COMMAND_DONE_ERROR); 17119a016c63Sstevel } 17129a016c63Sstevel 17139a016c63Sstevel /* 17149a016c63Sstevel * sgen_handle_autosense() 17159a016c63Sstevel * Deal with SENSE data acquired automatically via the auto-request-sense 17169a016c63Sstevel * facility. 17179a016c63Sstevel * 17189a016c63Sstevel * Sgen takes a pessimistic view of things-- it doesn't retry commands, 17199a016c63Sstevel * and unless the device recovered from the problem, this routine returns 17209a016c63Sstevel * COMMAND_DONE_ERROR. 17219a016c63Sstevel */ 17229a016c63Sstevel static int 17239a016c63Sstevel sgen_handle_autosense(sgen_state_t *sg_state, struct scsi_pkt *pkt) 17249a016c63Sstevel { 17259a016c63Sstevel struct scsi_arq_status *arqstat; 17269a016c63Sstevel struct uscsi_cmd *ucmd = 17279a016c63Sstevel (struct uscsi_cmd *)sg_state->sgen_cmdbuf->b_private; 17289a016c63Sstevel int amt; 17299a016c63Sstevel 17309a016c63Sstevel arqstat = (struct scsi_arq_status *)(pkt->pkt_scbp); 17319a016c63Sstevel 17329a016c63Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_autosen_rcv); 17339a016c63Sstevel 17349a016c63Sstevel if (arqstat->sts_rqpkt_reason != CMD_CMPLT) { 17359a016c63Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_autosense: ARQ" 17369a016c63Sstevel "failed to complete."); 17379a016c63Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_autosen_bad); 17389a016c63Sstevel return (COMMAND_DONE_ERROR); 17399a016c63Sstevel } 17409a016c63Sstevel 174130ab6db6Slh195018 if (pkt->pkt_state & STATE_XARQ_DONE) { 174230ab6db6Slh195018 amt = MAX_SENSE_LENGTH - arqstat->sts_rqpkt_resid; 174330ab6db6Slh195018 } else { 174430ab6db6Slh195018 if (arqstat->sts_rqpkt_resid > SENSE_LENGTH) { 174530ab6db6Slh195018 amt = MAX_SENSE_LENGTH - arqstat->sts_rqpkt_resid; 174630ab6db6Slh195018 } else { 174730ab6db6Slh195018 amt = SENSE_LENGTH - arqstat->sts_rqpkt_resid; 174830ab6db6Slh195018 } 174930ab6db6Slh195018 } 175030ab6db6Slh195018 17519a016c63Sstevel if (ucmd->uscsi_flags & USCSI_RQENABLE) { 17529a016c63Sstevel ucmd->uscsi_rqstatus = *((char *)&arqstat->sts_rqpkt_status); 175330ab6db6Slh195018 uchar_t rqlen = min((uchar_t)amt, ucmd->uscsi_rqlen); 175430ab6db6Slh195018 ucmd->uscsi_rqresid = ucmd->uscsi_rqlen - rqlen; 17559a016c63Sstevel ASSERT(ucmd->uscsi_rqlen && sg_state->sgen_rqs_sen); 175630ab6db6Slh195018 bcopy(&(arqstat->sts_sensedata), sg_state->sgen_rqs_sen, rqlen); 17579a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "sgen_handle_autosense: " 17589a016c63Sstevel "uscsi_rqstatus=0x%x uscsi_rqresid=%d\n", 17599a016c63Sstevel ucmd->uscsi_rqstatus, ucmd->uscsi_rqresid); 17609a016c63Sstevel } 17619a016c63Sstevel 17629a016c63Sstevel if (arqstat->sts_rqpkt_status.sts_chk) { 17639a016c63Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_autosense: got " 17649a016c63Sstevel "check condition on auto request sense!"); 17659a016c63Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_autosen_bad); 17669a016c63Sstevel return (COMMAND_DONE_ERROR); 17679a016c63Sstevel } 17689a016c63Sstevel 17699a016c63Sstevel if (((arqstat->sts_rqpkt_state & STATE_XFERRED_DATA) == 0) || 17709a016c63Sstevel (amt == 0)) { 17719a016c63Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_autosense: got " 17729a016c63Sstevel "auto-sense, but it contains no data!"); 17739a016c63Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_autosen_bad); 17749a016c63Sstevel return (COMMAND_DONE_ERROR); 17759a016c63Sstevel } 17769a016c63Sstevel 17779a016c63Sstevel /* 17789a016c63Sstevel * Stuff the sense data pointer into sgen_sense for later retrieval 17799a016c63Sstevel */ 17809a016c63Sstevel sg_state->sgen_sense = &arqstat->sts_sensedata; 17819a016c63Sstevel 17829a016c63Sstevel /* 17839a016c63Sstevel * Now, check to see whether we got enough sense data to make any 17849a016c63Sstevel * sense out if it (heh-heh). 17859a016c63Sstevel */ 17869a016c63Sstevel if (amt < SUN_MIN_SENSE_LENGTH) { 17879a016c63Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_autosense: not " 17889a016c63Sstevel "enough auto sense data"); 17899a016c63Sstevel return (COMMAND_DONE_ERROR); 17909a016c63Sstevel } 17919a016c63Sstevel 17929a016c63Sstevel switch (arqstat->sts_sensedata.es_key) { 17939a016c63Sstevel case KEY_RECOVERABLE_ERROR: 17949a016c63Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_recov_err); 17959a016c63Sstevel break; 17969a016c63Sstevel case KEY_NO_SENSE: 17979a016c63Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_nosen_err); 17989a016c63Sstevel break; 17999a016c63Sstevel default: 18009a016c63Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_unrecov_err); 18019a016c63Sstevel break; 18029a016c63Sstevel } 18039a016c63Sstevel 18049a016c63Sstevel return (COMMAND_DONE); 18059a016c63Sstevel } 18069a016c63Sstevel 18079a016c63Sstevel /* 18089a016c63Sstevel * sgen_handle_sense() 18099a016c63Sstevel * Examine sense data that was manually fetched from the target. 18109a016c63Sstevel */ 18119a016c63Sstevel static int 18129a016c63Sstevel sgen_handle_sense(sgen_state_t *sg_state) 18139a016c63Sstevel { 18149a016c63Sstevel struct scsi_pkt *rqpkt = sg_state->sgen_rqspkt; 18159a016c63Sstevel struct scsi_status *rqstatus = (struct scsi_status *)rqpkt->pkt_scbp; 18169a016c63Sstevel struct uscsi_cmd *ucmd = 18179a016c63Sstevel (struct uscsi_cmd *)sg_state->sgen_cmdbuf->b_private; 18189a016c63Sstevel int amt; 18199a016c63Sstevel 18209a016c63Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_sense_rcv); 18219a016c63Sstevel 182230ab6db6Slh195018 amt = MAX_SENSE_LENGTH - rqpkt->pkt_resid; 182330ab6db6Slh195018 18249a016c63Sstevel if (ucmd->uscsi_flags & USCSI_RQENABLE) { 18259a016c63Sstevel ucmd->uscsi_rqstatus = *((char *)rqstatus); 182630ab6db6Slh195018 uchar_t rqlen = min((uchar_t)amt, ucmd->uscsi_rqlen); 182730ab6db6Slh195018 ucmd->uscsi_rqresid = ucmd->uscsi_rqlen - rqlen; 18289a016c63Sstevel ASSERT(ucmd->uscsi_rqlen && sg_state->sgen_rqs_sen); 182930ab6db6Slh195018 bcopy(sg_state->sgen_sense, sg_state->sgen_rqs_sen, rqlen); 18309a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "sgen_handle_sense: " 18319a016c63Sstevel "uscsi_rqstatus=0x%x uscsi_rqresid=%d\n", 18329a016c63Sstevel ucmd->uscsi_rqstatus, ucmd->uscsi_rqresid); 18339a016c63Sstevel } 18349a016c63Sstevel 18359a016c63Sstevel if (rqstatus->sts_busy) { 18369a016c63Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_sense: got busy " 18379a016c63Sstevel "on request sense"); 18389a016c63Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_sense_bad); 18399a016c63Sstevel return (COMMAND_DONE_ERROR); 18409a016c63Sstevel } 18419a016c63Sstevel 18429a016c63Sstevel if (rqstatus->sts_chk) { 18439a016c63Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_sense: got check " 18449a016c63Sstevel "condition on request sense!"); 18459a016c63Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_sense_bad); 18469a016c63Sstevel return (COMMAND_DONE_ERROR); 18479a016c63Sstevel } 18489a016c63Sstevel 18499a016c63Sstevel if ((rqpkt->pkt_state & STATE_XFERRED_DATA) == 0 || amt == 0) { 18509a016c63Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_sense: got " 18519a016c63Sstevel "sense, but it contains no data"); 18529a016c63Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_sense_bad); 18539a016c63Sstevel return (COMMAND_DONE_ERROR); 18549a016c63Sstevel } 18559a016c63Sstevel 18569a016c63Sstevel /* 18579a016c63Sstevel * Now, check to see whether we got enough sense data to make any 18589a016c63Sstevel * sense out if it (heh-heh). 18599a016c63Sstevel */ 18609a016c63Sstevel if (amt < SUN_MIN_SENSE_LENGTH) { 18619a016c63Sstevel sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_sense: not " 18629a016c63Sstevel "enough sense data"); 18639a016c63Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_sense_bad); 18649a016c63Sstevel return (COMMAND_DONE_ERROR); 18659a016c63Sstevel } 18669a016c63Sstevel 18679a016c63Sstevel /* 18689a016c63Sstevel * Decode the sense data-- this was deposited here for us by the 18699a016c63Sstevel * setup in sgen_do_attach(). (note that sgen_sense is an alias for 18709a016c63Sstevel * the sd_sense field in the scsi_device). 18719a016c63Sstevel */ 18729a016c63Sstevel sgen_log(sg_state, SGEN_DIAG1, "Sense key is %s [0x%x]", 18739a016c63Sstevel scsi_sname(sg_state->sgen_sense->es_key), 18749a016c63Sstevel sg_state->sgen_sense->es_key); 18759a016c63Sstevel switch (sg_state->sgen_sense->es_key) { 18769a016c63Sstevel case KEY_RECOVERABLE_ERROR: 18779a016c63Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_recov_err); 18789a016c63Sstevel break; 18799a016c63Sstevel case KEY_NO_SENSE: 18809a016c63Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_nosen_err); 18819a016c63Sstevel break; 18829a016c63Sstevel default: 18839a016c63Sstevel SGEN_DO_ERRSTATS(sg_state, sgen_unrecov_err); 18849a016c63Sstevel break; 18859a016c63Sstevel } 18869a016c63Sstevel 18879a016c63Sstevel return (COMMAND_DONE); 18889a016c63Sstevel } 18899a016c63Sstevel 18909a016c63Sstevel /* 18919a016c63Sstevel * sgen_check_error() 18929a016c63Sstevel * examine the command packet for abnormal completion. 18939a016c63Sstevel * 18949a016c63Sstevel * sgen_check_error should only be called at the completion of the 18959a016c63Sstevel * command packet. 18969a016c63Sstevel */ 18979a016c63Sstevel static int 18989a016c63Sstevel sgen_check_error(sgen_state_t *sg_state, struct buf *bp) 18999a016c63Sstevel { 19009a016c63Sstevel struct scsi_pkt *pkt = sg_state->sgen_cmdpkt; 19019a016c63Sstevel struct scsi_status *status = (struct scsi_status *)pkt->pkt_scbp; 19029a016c63Sstevel struct uscsi_cmd *ucmd = 19039a016c63Sstevel (struct uscsi_cmd *)sg_state->sgen_cmdbuf->b_private; 19049a016c63Sstevel 19059a016c63Sstevel if (status->sts_busy) { 19069a016c63Sstevel sgen_log(sg_state, SGEN_DIAG1, 19079a016c63Sstevel "sgen_check_error: target is busy"); 19089a016c63Sstevel return (COMMAND_DONE_ERROR); 19099a016c63Sstevel } 19109a016c63Sstevel 19119a016c63Sstevel /* 19129a016c63Sstevel * pkt_resid will reflect, at this point, a residual of how many bytes 19139a016c63Sstevel * were not transferred; a non-zero pkt_resid is an error. 19149a016c63Sstevel */ 19159a016c63Sstevel if (pkt->pkt_resid) { 19169a016c63Sstevel bp->b_resid += pkt->pkt_resid; 19179a016c63Sstevel } 19189a016c63Sstevel 19199a016c63Sstevel if (status->sts_chk) { 19209a016c63Sstevel if (ucmd->uscsi_flags & USCSI_RQENABLE) { 19219a016c63Sstevel if (sg_state->sgen_arq_enabled) { 19229a016c63Sstevel sgen_log(sg_state, SGEN_DIAG1, 19239a016c63Sstevel "sgen_check_error: strange: target " 19249a016c63Sstevel "indicates CHECK CONDITION with auto-sense " 19259a016c63Sstevel "enabled."); 19269a016c63Sstevel } 19279a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "sgen_check_error: " 19289a016c63Sstevel "target ready for sense fetch"); 19299a016c63Sstevel return (FETCH_SENSE); 19309a016c63Sstevel } else { 19319a016c63Sstevel sgen_log(sg_state, SGEN_DIAG2, "sgen_check_error: " 19329a016c63Sstevel "target indicates CHECK CONDITION"); 19339a016c63Sstevel } 19349a016c63Sstevel } 19359a016c63Sstevel 19369a016c63Sstevel return (COMMAND_DONE); 19379a016c63Sstevel } 19389a016c63Sstevel 19399a016c63Sstevel /* 19409a016c63Sstevel * sgen_tur() 19419a016c63Sstevel * test if a target is ready to operate by sending it a TUR command. 19429a016c63Sstevel */ 19439a016c63Sstevel static int 19449a016c63Sstevel sgen_tur(dev_t dev) 19459a016c63Sstevel { 19469a016c63Sstevel char cmdblk[CDB_GROUP0]; 19479a016c63Sstevel struct uscsi_cmd scmd; 19489a016c63Sstevel 19499a016c63Sstevel bzero(&scmd, sizeof (scmd)); 19509a016c63Sstevel scmd.uscsi_bufaddr = 0; 19519a016c63Sstevel scmd.uscsi_buflen = 0; 19529a016c63Sstevel bzero(cmdblk, CDB_GROUP0); 19539a016c63Sstevel cmdblk[0] = (char)SCMD_TEST_UNIT_READY; 19549a016c63Sstevel scmd.uscsi_flags = USCSI_DIAGNOSE | USCSI_SILENT | USCSI_WRITE; 19559a016c63Sstevel scmd.uscsi_cdb = cmdblk; 19569a016c63Sstevel scmd.uscsi_cdblen = CDB_GROUP0; 19579a016c63Sstevel 19586567eb0aSlh195018 return (sgen_uscsi_cmd(dev, &scmd, FKIOCTL)); 19599a016c63Sstevel } 19609a016c63Sstevel 19619a016c63Sstevel /* 19629a016c63Sstevel * sgen_diag_ok() 19639a016c63Sstevel * given an sg_state and a desired diagnostic level, return true if 19649a016c63Sstevel * it is acceptable to output a message. 19659a016c63Sstevel */ 19669a016c63Sstevel /*ARGSUSED*/ 19679a016c63Sstevel static int 19689a016c63Sstevel sgen_diag_ok(sgen_state_t *sg_state, int level) 19699a016c63Sstevel { 19709a016c63Sstevel int diag_lvl; 19719a016c63Sstevel 19729a016c63Sstevel switch (level) { 19739a016c63Sstevel case CE_WARN: 19749a016c63Sstevel case CE_NOTE: 19759a016c63Sstevel case CE_CONT: 19769a016c63Sstevel case CE_PANIC: 19779a016c63Sstevel return (1); 19789a016c63Sstevel case SGEN_DIAG1: 19799a016c63Sstevel case SGEN_DIAG2: 19809a016c63Sstevel case SGEN_DIAG3: 19819a016c63Sstevel if (sg_state) { 19829a016c63Sstevel /* 19839a016c63Sstevel * Check to see if user overrode the diagnostics level 19849a016c63Sstevel * for this instance (either via SGEN_IOC_DIAG or via 19859a016c63Sstevel * .conf file). If not, fall back to the global diag 19869a016c63Sstevel * level. 19879a016c63Sstevel */ 19889a016c63Sstevel if (sg_state->sgen_diag != -1) 19899a016c63Sstevel diag_lvl = sg_state->sgen_diag; 19909a016c63Sstevel else 19919a016c63Sstevel diag_lvl = sgen_diag; 19929a016c63Sstevel } else { 19939a016c63Sstevel diag_lvl = sgen_diag; 19949a016c63Sstevel } 19959a016c63Sstevel if (((diag_lvl << 8) | CE_CONT) >= level) { 19969a016c63Sstevel return (1); 19979a016c63Sstevel } else { 19989a016c63Sstevel return (0); 19999a016c63Sstevel } 20009a016c63Sstevel default: 20019a016c63Sstevel return (1); 20029a016c63Sstevel } 20039a016c63Sstevel } 20049a016c63Sstevel 20059a016c63Sstevel /*PRINTFLIKE3*/ 20069a016c63Sstevel static void 20079a016c63Sstevel sgen_log(sgen_state_t *sg_state, int level, const char *fmt, ...) 20089a016c63Sstevel { 20099a016c63Sstevel va_list ap; 20109a016c63Sstevel char buf[256]; 20119a016c63Sstevel 20129a016c63Sstevel if (!sgen_diag_ok(sg_state, level)) 20139a016c63Sstevel return; 20149a016c63Sstevel 20159a016c63Sstevel va_start(ap, fmt); 20169a016c63Sstevel (void) vsnprintf(buf, sizeof (buf), fmt, ap); 20179a016c63Sstevel va_end(ap); 20189a016c63Sstevel 20199a016c63Sstevel switch (level) { 20209a016c63Sstevel case CE_NOTE: 20219a016c63Sstevel case CE_CONT: 20229a016c63Sstevel case CE_WARN: 20239a016c63Sstevel case CE_PANIC: 20249a016c63Sstevel if (sg_state == (sgen_state_t *)NULL) { 20259a016c63Sstevel cmn_err(level, "%s", buf); 20269a016c63Sstevel } else { 2027a204de77Scth scsi_log(sg_state->sgen_devinfo, sgen_label, level, 20289a016c63Sstevel "%s", buf); 20299a016c63Sstevel } 20309a016c63Sstevel break; 20319a016c63Sstevel case SGEN_DIAG1: 20329a016c63Sstevel case SGEN_DIAG2: 20339a016c63Sstevel case SGEN_DIAG3: 20349a016c63Sstevel default: 20359a016c63Sstevel if (sg_state == (sgen_state_t *)NULL) { 2036a204de77Scth scsi_log(NULL, sgen_label, CE_CONT, "%s", buf); 20379a016c63Sstevel } else { 2038a204de77Scth scsi_log(sg_state->sgen_devinfo, sgen_label, CE_CONT, 20399a016c63Sstevel "%s", buf); 20409a016c63Sstevel } 20419a016c63Sstevel } 20429a016c63Sstevel } 20439a016c63Sstevel 20449a016c63Sstevel /* 20459a016c63Sstevel * sgen_dump_cdb() 20469a016c63Sstevel * dump out the contents of a cdb. Take care that 'label' is not too 20479a016c63Sstevel * large, or 'buf' could overflow. 20489a016c63Sstevel */ 20499a016c63Sstevel static void 20509a016c63Sstevel sgen_dump_cdb(sgen_state_t *sg_state, const char *label, 20519a016c63Sstevel union scsi_cdb *cdb, int cdblen) 20529a016c63Sstevel { 20539a016c63Sstevel static char hex[] = "0123456789abcdef"; 20549a016c63Sstevel char *buf, *p; 20559a016c63Sstevel size_t nbytes; 20569a016c63Sstevel int i; 20579a016c63Sstevel uchar_t *cdbp = (uchar_t *)cdb; 20589a016c63Sstevel 20599a016c63Sstevel /* 20609a016c63Sstevel * fastpath-- if we're not able to print out, don't do all of this 20619a016c63Sstevel * extra work. 20629a016c63Sstevel */ 20639a016c63Sstevel if (!sgen_diag_ok(sg_state, SGEN_DIAG3)) 20649a016c63Sstevel return; 20659a016c63Sstevel 20669a016c63Sstevel /* 20679a016c63Sstevel * 3 characters for each byte (because of the ' '), plus the size of 20689a016c63Sstevel * the label, plus the trailing ']' and the null character. 20699a016c63Sstevel */ 20709a016c63Sstevel nbytes = 3 * cdblen + strlen(label) + strlen(" CDB = [") + 2; 20719a016c63Sstevel buf = kmem_alloc(nbytes, KM_SLEEP); 20729a016c63Sstevel (void) sprintf(buf, "%s CDB = [", label); 20739a016c63Sstevel p = &buf[strlen(buf)]; 20749a016c63Sstevel for (i = 0; i < cdblen; i++, cdbp++) { 20759a016c63Sstevel if (i > 0) 20769a016c63Sstevel *p++ = ' '; 20779a016c63Sstevel *p++ = hex[(*cdbp >> 4) & 0x0f]; 20789a016c63Sstevel *p++ = hex[*cdbp & 0x0f]; 20799a016c63Sstevel } 20809a016c63Sstevel *p++ = ']'; 20819a016c63Sstevel *p = 0; 20829a016c63Sstevel sgen_log(sg_state, SGEN_DIAG3, buf); 20839a016c63Sstevel kmem_free(buf, nbytes); 20849a016c63Sstevel } 20859a016c63Sstevel 20869a016c63Sstevel static void 20879a016c63Sstevel sgen_dump_sense(sgen_state_t *sg_state, size_t rqlen, uchar_t *rqbuf) 20889a016c63Sstevel { 20899a016c63Sstevel static char hex[] = "0123456789abcdef"; 20909a016c63Sstevel char *buf, *p; 20919a016c63Sstevel size_t nbytes; 20929a016c63Sstevel int i; 20939a016c63Sstevel 20949a016c63Sstevel /* 20959a016c63Sstevel * fastpath-- if we're not able to print out, don't do all of this 20969a016c63Sstevel * extra work. 20979a016c63Sstevel */ 20989a016c63Sstevel if (!sgen_diag_ok(sg_state, SGEN_DIAG3)) 20999a016c63Sstevel return; 21009a016c63Sstevel 21019a016c63Sstevel /* 21029a016c63Sstevel * 3 characters for each byte (because of the ' '), plus the size of 21039a016c63Sstevel * the label, plus the trailing ']' and the null character. 21049a016c63Sstevel */ 21059a016c63Sstevel nbytes = 3 * rqlen + strlen(" SENSE = [") + 2; 21069a016c63Sstevel buf = kmem_alloc(nbytes, KM_SLEEP); 21079a016c63Sstevel (void) sprintf(buf, "SENSE = ["); 21089a016c63Sstevel p = &buf[strlen(buf)]; 21099a016c63Sstevel for (i = 0; i < rqlen; i++, rqbuf++) { 21109a016c63Sstevel if (i > 0) 21119a016c63Sstevel *p++ = ' '; 21129a016c63Sstevel *p++ = hex[(*rqbuf >> 4) & 0x0f]; 21139a016c63Sstevel *p++ = hex[*rqbuf & 0x0f]; 21149a016c63Sstevel } 21159a016c63Sstevel *p++ = ']'; 21169a016c63Sstevel *p = 0; 21179a016c63Sstevel sgen_log(sg_state, SGEN_DIAG3, buf); 21189a016c63Sstevel kmem_free(buf, nbytes); 21199a016c63Sstevel } 2120