1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 /*
  30  * This file contains routines for sending and receiving SCSI commands.  The
  31  * higher level logic is contained in ds_scsi.c.
  32  */
  33 
  34 #include <assert.h>
  35 #include <sys/types.h>
  36 #include <sys/param.h>
  37 #include <inttypes.h>
  38 #include <stdio.h>
  39 #include <stdlib.h>
  40 #include <string.h>
  41 #include <errno.h>
  42 #include <stdarg.h>
  43 #include <limits.h>
  44 #include <utility.h>
  45 #include <unistd.h>
  46 #include <stropts.h>
  47 #include <alloca.h>
  48 
  49 #include "ds_scsi.h"
  50 #include "ds_scsi_uscsi.h"
  51 
  52 #define MSGBUFLEN 64
  53 #define USCSI_DEFAULT_TIMEOUT   45
  54 #define USCSI_TIMEOUT_MAX       INT_MAX
  55 
  56 static diskaddr_t scsi_extract_sense_info_descr(
  57     struct scsi_descr_sense_hdr *sdsp, int rqlen);
  58 static void scsi_print_extended_sense(struct scsi_extended_sense *rq,
  59     int rqlen);
  60 static void scsi_print_descr_sense(struct scsi_descr_sense_hdr *rq, int rqlen);
  61 
  62 typedef struct slist {
  63         char    *str;
  64         int     value;
  65 } slist_t;
  66 
  67 static char *
  68 find_string(slist_t *slist, int match_value)
  69 {
  70         for (; slist->str != NULL; slist++) {
  71                 if (slist->value == match_value) {
  72                         return (slist->str);
  73                 }
  74         }
  75 
  76         return ((char *)NULL);
  77 }
  78 
  79 /*
  80  * Strings for printing mode sense page control values
  81  */
  82 static slist_t page_control_strings[] = {
  83         { "current",    PC_CURRENT },
  84         { "changeable", PC_CHANGEABLE },
  85         { "default",    PC_DEFAULT },
  86         { "saved",      PC_SAVED },
  87         { NULL,         0 }
  88 };
  89 
  90 /*
  91  * Strings for printing the mode select options
  92  */
  93 static slist_t mode_select_strings[] = {
  94         { "",           0 },
  95         { "(pf)",       MODE_SELECT_PF },
  96         { "(sp)",       MODE_SELECT_SP },
  97         { "(pf,sp)",    MODE_SELECT_PF|MODE_SELECT_SP },
  98         { NULL,         0 }
  99 };
 100 
 101 static slist_t sensekey_strings[] = {
 102         { "No sense error",     KEY_NO_SENSE            },
 103         { "Recoverable error",  KEY_RECOVERABLE_ERROR   },
 104         { "Not ready error",    KEY_NOT_READY           },
 105         { "Medium error",       KEY_MEDIUM_ERROR        },
 106         { "Hardware error",     KEY_HARDWARE_ERROR      },
 107         { "Illegal request",    KEY_ILLEGAL_REQUEST     },
 108         { "Unit attention error", KEY_UNIT_ATTENTION    },
 109         { "Write protect error", KEY_WRITE_PROTECT      },
 110         { "Blank check error",  KEY_BLANK_CHECK         },
 111         { "Vendor unique error", KEY_VENDOR_UNIQUE      },
 112         { "Copy aborted error", KEY_COPY_ABORTED        },
 113         { "Aborted command",    KEY_ABORTED_COMMAND     },
 114         { "Equal error",        KEY_EQUAL               },
 115         { "Volume overflow",    KEY_VOLUME_OVERFLOW     },
 116         { "Miscompare error",   KEY_MISCOMPARE          },
 117         { "Reserved error",     KEY_RESERVED            },
 118         { NULL,                 0                       }
 119 };
 120 
 121 static slist_t scsi_cmdname_strings[] = {
 122         { "mode select",        SCMD_MODE_SELECT        },
 123         { "mode sense",         SCMD_MODE_SENSE         },
 124         { "mode select(10)",    SCMD_MODE_SELECT_G1     },
 125         { "mode sense(10)",     SCMD_MODE_SENSE_G1      },
 126         { "log sense",          SCMD_LOG_SENSE_G1       },
 127         { "request sense",      SCMD_REQUEST_SENSE      },
 128         { NULL,                 0                       }
 129 };
 130 
 131 static struct _scsi_asq_key_strings {
 132         uint_t asc;
 133         uint_t ascq;
 134         const char *message;
 135 } extended_sense_list[] = {
 136         { 0x00, 0x00, "no additional sense info" },
 137         { 0x00, 0x01, "filemark detected" },
 138         { 0x00, 0x02, "end of partition/medium detected" },
 139         { 0x00, 0x03, "setmark detected" },
 140         { 0x00, 0x04, "begining of partition/medium detected" },
 141         { 0x00, 0x05, "end of data detected" },
 142         { 0x00, 0x06, "i/o process terminated" },
 143         { 0x00, 0x11, "audio play operation in progress" },
 144         { 0x00, 0x12, "audio play operation paused" },
 145         { 0x00, 0x13, "audio play operation successfully completed" },
 146         { 0x00, 0x14, "audio play operation stopped due to error" },
 147         { 0x00, 0x15, "no current audio status to return" },
 148         { 0x00, 0x16, "operation in progress" },
 149         { 0x00, 0x17, "cleaning requested" },
 150         { 0x00, 0x18, "erase operation in progress" },
 151         { 0x00, 0x19, "locate operation in progress" },
 152         { 0x00, 0x1A, "rewind operation in progress" },
 153         { 0x00, 0x1B, "set capacity operation in progress" },
 154         { 0x00, 0x1C, "verify operation in progress" },
 155         { 0x01, 0x00, "no index/sector signal" },
 156         { 0x02, 0x00, "no seek complete" },
 157         { 0x03, 0x00, "peripheral device write fault" },
 158         { 0x03, 0x01, "no write current" },
 159         { 0x03, 0x02, "excessive write errors" },
 160         { 0x04, 0x00, "LUN not ready" },
 161         { 0x04, 0x01, "LUN is becoming ready" },
 162         { 0x04, 0x02, "LUN initializing command required" },
 163         { 0x04, 0x03, "LUN not ready intervention required" },
 164         { 0x04, 0x04, "LUN not ready format in progress" },
 165         { 0x04, 0x05, "LUN not ready, rebuild in progress" },
 166         { 0x04, 0x06, "LUN not ready, recalculation in progress" },
 167         { 0x04, 0x07, "LUN not ready, operation in progress" },
 168         { 0x04, 0x08, "LUN not ready, long write in progress" },
 169         { 0x04, 0x09, "LUN not ready, self-test in progress" },
 170         { 0x04, 0x0A, "LUN not accessible, asymmetric access state "
 171                 "transition" },
 172         { 0x04, 0x0B, "LUN not accessible, target port in standby state" },
 173         { 0x04, 0x0C, "LUN not accessible, target port in unavailable state" },
 174         { 0x04, 0x10, "LUN not ready, auxiliary memory not accessible" },
 175         { 0x05, 0x00, "LUN does not respond to selection" },
 176         { 0x06, 0x00, "reference position found" },
 177         { 0x07, 0x00, "multiple peripheral devices selected" },
 178         { 0x08, 0x00, "LUN communication failure" },
 179         { 0x08, 0x01, "LUN communication time-out" },
 180         { 0x08, 0x02, "LUN communication parity error" },
 181         { 0x08, 0x03, "LUN communication crc error (ultra-DMA/32)" },
 182         { 0x08, 0x04, "unreachable copy target" },
 183         { 0x09, 0x00, "track following error" },
 184         { 0x09, 0x01, "tracking servo failure" },
 185         { 0x09, 0x02, "focus servo failure" },
 186         { 0x09, 0x03, "spindle servo failure" },
 187         { 0x09, 0x04, "head select fault" },
 188         { 0x0a, 0x00, "error log overflow" },
 189         { 0x0b, 0x00, "warning" },
 190         { 0x0b, 0x01, "warning - specified temperature exceeded" },
 191         { 0x0b, 0x02, "warning - enclosure degraded" },
 192         { 0x0c, 0x00, "write error" },
 193         { 0x0c, 0x01, "write error - recovered with auto reallocation" },
 194         { 0x0c, 0x02, "write error - auto reallocation failed" },
 195         { 0x0c, 0x03, "write error - recommend reassignment" },
 196         { 0x0c, 0x04, "compression check miscompare error" },
 197         { 0x0c, 0x05, "data expansion occurred during compression" },
 198         { 0x0c, 0x06, "block not compressible" },
 199         { 0x0c, 0x07, "write error - recovery needed" },
 200         { 0x0c, 0x08, "write error - recovery failed" },
 201         { 0x0c, 0x09, "write error - loss of streaming" },
 202         { 0x0c, 0x0a, "write error - padding blocks added" },
 203         { 0x0c, 0x0b, "auxiliary memory write error" },
 204         { 0x0c, 0x0c, "write error - unexpected unsolicited data" },
 205         { 0x0c, 0x0d, "write error - not enough unsolicited data" },
 206         { 0x0d, 0x00, "error detected by third party temporary initiator" },
 207         { 0x0d, 0x01, "third party device failure" },
 208         { 0x0d, 0x02, "copy target device not reachable" },
 209         { 0x0d, 0x03, "incorrect copy target device type" },
 210         { 0x0d, 0x04, "copy target device data underrun" },
 211         { 0x0d, 0x05, "copy target device data overrun" },
 212         { 0x0e, 0x00, "invalid information unit" },
 213         { 0x0e, 0x01, "information unit too short" },
 214         { 0x0e, 0x02, "information unit too long" },
 215         { 0x10, 0x00, "ID CRC or ECC error" },
 216         { 0x11, 0x00, "unrecovered read error" },
 217         { 0x11, 0x01, "read retries exhausted" },
 218         { 0x11, 0x02, "error too long to correct" },
 219         { 0x11, 0x03, "multiple read errors" },
 220         { 0x11, 0x04, "unrecovered read error - auto reallocate failed" },
 221         { 0x11, 0x05, "L-EC uncorrectable error" },
 222         { 0x11, 0x06, "CIRC unrecovered error" },
 223         { 0x11, 0x07, "data re-synchronization error" },
 224         { 0x11, 0x08, "incomplete block read" },
 225         { 0x11, 0x09, "no gap found" },
 226         { 0x11, 0x0a, "miscorrected error" },
 227         { 0x11, 0x0b, "unrecovered read error - recommend reassignment" },
 228         { 0x11, 0x0c, "unrecovered read error - recommend rewrite the data" },
 229         { 0x11, 0x0d, "de-compression crc error" },
 230         { 0x11, 0x0e, "cannot decompress using declared algorithm" },
 231         { 0x11, 0x0f, "error reading UPC/EAN number" },
 232         { 0x11, 0x10, "error reading ISRC number" },
 233         { 0x11, 0x11, "read error - loss of streaming" },
 234         { 0x11, 0x12, "auxiliary memory read error" },
 235         { 0x11, 0x13, "read error - failed retransmission request" },
 236         { 0x12, 0x00, "address mark not found for ID field" },
 237         { 0x13, 0x00, "address mark not found for data field" },
 238         { 0x14, 0x00, "recorded entity not found" },
 239         { 0x14, 0x01, "record not found" },
 240         { 0x14, 0x02, "filemark or setmark not found" },
 241         { 0x14, 0x03, "end-of-data not found" },
 242         { 0x14, 0x04, "block sequence error" },
 243         { 0x14, 0x05, "record not found - recommend reassignment" },
 244         { 0x14, 0x06, "record not found - data auto-reallocated" },
 245         { 0x14, 0x07, "locate operation failure" },
 246         { 0x15, 0x00, "random positioning error" },
 247         { 0x15, 0x01, "mechanical positioning error" },
 248         { 0x15, 0x02, "positioning error detected by read of medium" },
 249         { 0x16, 0x00, "data sync mark error" },
 250         { 0x16, 0x01, "data sync error - data rewritten" },
 251         { 0x16, 0x02, "data sync error - recommend rewrite" },
 252         { 0x16, 0x03, "data sync error - data auto-reallocated" },
 253         { 0x16, 0x04, "data sync error - recommend reassignment" },
 254         { 0x17, 0x00, "recovered data with no error correction" },
 255         { 0x17, 0x01, "recovered data with retries" },
 256         { 0x17, 0x02, "recovered data with positive head offset" },
 257         { 0x17, 0x03, "recovered data with negative head offset" },
 258         { 0x17, 0x04, "recovered data with retries and/or CIRC applied" },
 259         { 0x17, 0x05, "recovered data using previous sector id" },
 260         { 0x17, 0x06, "recovered data without ECC - data auto-reallocated" },
 261         { 0x17, 0x07, "recovered data without ECC - recommend reassignment" },
 262         { 0x17, 0x08, "recovered data without ECC - recommend rewrite" },
 263         { 0x17, 0x09, "recovered data without ECC - data rewritten" },
 264         { 0x18, 0x00, "recovered data with error correction" },
 265         { 0x18, 0x01, "recovered data with error corr. & retries applied" },
 266         { 0x18, 0x02, "recovered data - data auto-reallocated" },
 267         { 0x18, 0x03, "recovered data with CIRC" },
 268         { 0x18, 0x04, "recovered data with L-EC" },
 269         { 0x18, 0x05, "recovered data - recommend reassignment" },
 270         { 0x18, 0x06, "recovered data - recommend rewrite" },
 271         { 0x18, 0x07, "recovered data with ECC - data rewritten" },
 272         { 0x18, 0x08, "recovered data with linking" },
 273         { 0x19, 0x00, "defect list error" },
 274         { 0x1a, 0x00, "parameter list length error" },
 275         { 0x1b, 0x00, "synchronous data xfer error" },
 276         { 0x1c, 0x00, "defect list not found" },
 277         { 0x1c, 0x01, "primary defect list not found" },
 278         { 0x1c, 0x02, "grown defect list not found" },
 279         { 0x1d, 0x00, "miscompare during verify" },
 280         { 0x1e, 0x00, "recovered ID with ECC" },
 281         { 0x1f, 0x00, "partial defect list transfer" },
 282         { 0x20, 0x00, "invalid command operation code" },
 283         { 0x20, 0x01, "access denied - initiator pending-enrolled" },
 284         { 0x20, 0x02, "access denied - no access rights" },
 285         { 0x20, 0x03, "access denied - invalid mgmt id key" },
 286         { 0x20, 0x04, "illegal command while in write capable state" },
 287         { 0x20, 0x06, "illegal command while in explicit address mode" },
 288         { 0x20, 0x07, "illegal command while in implicit address mode" },
 289         { 0x20, 0x08, "access denied - enrollment conflict" },
 290         { 0x20, 0x09, "access denied - invalid lu identifier" },
 291         { 0x20, 0x0a, "access denied - invalid proxy token" },
 292         { 0x20, 0x0b, "access denied - ACL LUN conflict" },
 293         { 0x21, 0x00, "logical block address out of range" },
 294         { 0x21, 0x01, "invalid element address" },
 295         { 0x21, 0x02, "invalid address for write" },
 296         { 0x22, 0x00, "illegal function" },
 297         { 0x24, 0x00, "invalid field in cdb" },
 298         { 0x24, 0x01, "cdb decryption error" },
 299         { 0x25, 0x00, "LUN not supported" },
 300         { 0x26, 0x00, "invalid field in param list" },
 301         { 0x26, 0x01, "parameter not supported" },
 302         { 0x26, 0x02, "parameter value invalid" },
 303         { 0x26, 0x03, "threshold parameters not supported" },
 304         { 0x26, 0x04, "invalid release of persistent reservation" },
 305         { 0x26, 0x05, "data decryption error" },
 306         { 0x26, 0x06, "too many target descriptors" },
 307         { 0x26, 0x07, "unsupported target descriptor type code" },
 308         { 0x26, 0x08, "too many segment descriptors" },
 309         { 0x26, 0x09, "unsupported segment descriptor type code" },
 310         { 0x26, 0x0a, "unexpected inexact segment" },
 311         { 0x26, 0x0b, "inline data length exceeded" },
 312         { 0x26, 0x0c, "invalid operation for copy source or destination" },
 313         { 0x26, 0x0d, "copy segment granularity violation" },
 314         { 0x27, 0x00, "write protected" },
 315         { 0x27, 0x01, "hardware write protected" },
 316         { 0x27, 0x02, "LUN software write protected" },
 317         { 0x27, 0x03, "associated write protect" },
 318         { 0x27, 0x04, "persistent write protect" },
 319         { 0x27, 0x05, "permanent write protect" },
 320         { 0x27, 0x06, "conditional write protect" },
 321         { 0x28, 0x00, "medium may have changed" },
 322         { 0x28, 0x01, "import or export element accessed" },
 323         { 0x29, 0x00, "power on, reset, or bus reset occurred" },
 324         { 0x29, 0x01, "power on occurred" },
 325         { 0x29, 0x02, "scsi bus reset occurred" },
 326         { 0x29, 0x03, "bus device reset message occurred" },
 327         { 0x29, 0x04, "device internal reset" },
 328         { 0x29, 0x05, "transceiver mode changed to single-ended" },
 329         { 0x29, 0x06, "transceiver mode changed to LVD" },
 330         { 0x29, 0x07, "i_t nexus loss occurred" },
 331         { 0x2a, 0x00, "parameters changed" },
 332         { 0x2a, 0x01, "mode parameters changed" },
 333         { 0x2a, 0x02, "log parameters changed" },
 334         { 0x2a, 0x03, "reservations preempted" },
 335         { 0x2a, 0x04, "reservations released" },
 336         { 0x2a, 0x05, "registrations preempted" },
 337         { 0x2a, 0x06, "asymmetric access state changed" },
 338         { 0x2a, 0x07, "implicit asymmetric access state transition failed" },
 339         { 0x2b, 0x00, "copy cannot execute since host cannot disconnect" },
 340         { 0x2c, 0x00, "command sequence error" },
 341         { 0x2c, 0x03, "current program area is not empty" },
 342         { 0x2c, 0x04, "current program area is empty" },
 343         { 0x2c, 0x06, "persistent prevent conflict" },
 344         { 0x2c, 0x07, "previous busy status" },
 345         { 0x2c, 0x08, "previous task set full status" },
 346         { 0x2c, 0x09, "previous reservation conflict status" },
 347         { 0x2d, 0x00, "overwrite error on update in place" },
 348         { 0x2e, 0x00, "insufficient time for operation" },
 349         { 0x2f, 0x00, "commands cleared by another initiator" },
 350         { 0x30, 0x00, "incompatible medium installed" },
 351         { 0x30, 0x01, "cannot read medium - unknown format" },
 352         { 0x30, 0x02, "cannot read medium - incompatible format" },
 353         { 0x30, 0x03, "cleaning cartridge installed" },
 354         { 0x30, 0x04, "cannot write medium - unknown format" },
 355         { 0x30, 0x05, "cannot write medium - incompatible format" },
 356         { 0x30, 0x06, "cannot format medium - incompatible medium" },
 357         { 0x30, 0x07, "cleaning failure" },
 358         { 0x30, 0x08, "cannot write - application code mismatch" },
 359         { 0x30, 0x09, "current session not fixated for append" },
 360         { 0x30, 0x10, "medium not formatted" },
 361         { 0x31, 0x00, "medium format corrupted" },
 362         { 0x31, 0x01, "format command failed" },
 363         { 0x31, 0x02, "zoned formatting failed due to spare linking" },
 364         { 0x32, 0x00, "no defect spare location available" },
 365         { 0x32, 0x01, "defect list update failure" },
 366         { 0x33, 0x00, "tape length error" },
 367         { 0x34, 0x00, "enclosure failure" },
 368         { 0x35, 0x00, "enclosure services failure" },
 369         { 0x35, 0x01, "unsupported enclosure function" },
 370         { 0x35, 0x02, "enclosure services unavailable" },
 371         { 0x35, 0x03, "enclosure services transfer failure" },
 372         { 0x35, 0x04, "enclosure services transfer refused" },
 373         { 0x36, 0x00, "ribbon, ink, or toner failure" },
 374         { 0x37, 0x00, "rounded parameter" },
 375         { 0x39, 0x00, "saving parameters not supported" },
 376         { 0x3a, 0x00, "medium not present" },
 377         { 0x3a, 0x01, "medium not present - tray closed" },
 378         { 0x3a, 0x02, "medium not present - tray open" },
 379         { 0x3a, 0x03, "medium not present - loadable" },
 380         { 0x3a, 0x04, "medium not present - medium auxiliary memory "
 381                 "accessible" },
 382         { 0x3b, 0x00, "sequential positioning error" },
 383         { 0x3b, 0x01, "tape position error at beginning-of-medium" },
 384         { 0x3b, 0x02, "tape position error at end-of-medium" },
 385         { 0x3b, 0x08, "reposition error" },
 386         { 0x3b, 0x0c, "position past beginning of medium" },
 387         { 0x3b, 0x0d, "medium destination element full" },
 388         { 0x3b, 0x0e, "medium source element empty" },
 389         { 0x3b, 0x0f, "end of medium reached" },
 390         { 0x3b, 0x11, "medium magazine not accessible" },
 391         { 0x3b, 0x12, "medium magazine removed" },
 392         { 0x3b, 0x13, "medium magazine inserted" },
 393         { 0x3b, 0x14, "medium magazine locked" },
 394         { 0x3b, 0x15, "medium magazine unlocked" },
 395         { 0x3b, 0x16, "mechanical positioning or changer error" },
 396         { 0x3d, 0x00, "invalid bits in indentify message" },
 397         { 0x3e, 0x00, "LUN has not self-configured yet" },
 398         { 0x3e, 0x01, "LUN failure" },
 399         { 0x3e, 0x02, "timeout on LUN" },
 400         { 0x3e, 0x03, "LUN failed self-test" },
 401         { 0x3e, 0x04, "LUN unable to update self-test log" },
 402         { 0x3f, 0x00, "target operating conditions have changed" },
 403         { 0x3f, 0x01, "microcode has been changed" },
 404         { 0x3f, 0x02, "changed operating definition" },
 405         { 0x3f, 0x03, "inquiry data has changed" },
 406         { 0x3f, 0x04, "component device attached" },
 407         { 0x3f, 0x05, "device identifier changed" },
 408         { 0x3f, 0x06, "redundancy group created or modified" },
 409         { 0x3f, 0x07, "redundancy group deleted" },
 410         { 0x3f, 0x08, "spare created or modified" },
 411         { 0x3f, 0x09, "spare deleted" },
 412         { 0x3f, 0x0a, "volume set created or modified" },
 413         { 0x3f, 0x0b, "volume set deleted" },
 414         { 0x3f, 0x0c, "volume set deassigned" },
 415         { 0x3f, 0x0d, "volume set reassigned" },
 416         { 0x3f, 0x0e, "reported LUNs data has changed" },
 417         { 0x3f, 0x0f, "echo buffer overwritten" },
 418         { 0x3f, 0x10, "medium loadable" },
 419         { 0x3f, 0x11, "medium auxiliary memory accessible" },
 420         { 0x40, 0x00, "ram failure" },
 421         { 0x41, 0x00, "data path failure" },
 422         { 0x42, 0x00, "power-on or self-test failure" },
 423         { 0x43, 0x00, "message error" },
 424         { 0x44, 0x00, "internal target failure" },
 425         { 0x45, 0x00, "select or reselect failure" },
 426         { 0x46, 0x00, "unsuccessful soft reset" },
 427         { 0x47, 0x00, "scsi parity error" },
 428         { 0x47, 0x01, "data phase crc error detected" },
 429         { 0x47, 0x02, "scsi parity error detected during st data phase" },
 430         { 0x47, 0x03, "information unit iucrc error detected" },
 431         { 0x47, 0x04, "asynchronous information protection error detected" },
 432         { 0x47, 0x05, "protocol service crc error" },
 433         { 0x47, 0x7f, "some commands cleared by iscsi protocol event" },
 434         { 0x48, 0x00, "initiator detected error message received" },
 435         { 0x49, 0x00, "invalid message error" },
 436         { 0x4a, 0x00, "command phase error" },
 437         { 0x4b, 0x00, "data phase error" },
 438         { 0x4b, 0x01, "invalid target port transfer tag received" },
 439         { 0x4b, 0x02, "too much write data" },
 440         { 0x4b, 0x03, "ack/nak timeout" },
 441         { 0x4b, 0x04, "nak received" },
 442         { 0x4b, 0x05, "data offset error" },
 443         { 0x4c, 0x00, "logical unit failed self-configuration" },
 444         { 0x4d, 0x00, "tagged overlapped commands (ASCQ = queue tag)" },
 445         { 0x4e, 0x00, "overlapped commands attempted" },
 446         { 0x50, 0x00, "write append error" },
 447         { 0x51, 0x00, "erase failure" },
 448         { 0x52, 0x00, "cartridge fault" },
 449         { 0x53, 0x00, "media load or eject failed" },
 450         { 0x53, 0x01, "unload tape failure" },
 451         { 0x53, 0x02, "medium removal prevented" },
 452         { 0x54, 0x00, "scsi to host system interface failure" },
 453         { 0x55, 0x00, "system resource failure" },
 454         { 0x55, 0x01, "system buffer full" },
 455         { 0x55, 0x02, "insufficient reservation resources" },
 456         { 0x55, 0x03, "insufficient resources" },
 457         { 0x55, 0x04, "insufficient registration resources" },
 458         { 0x55, 0x05, "insufficient access control resources" },
 459         { 0x55, 0x06, "auxiliary memory out of space" },
 460         { 0x57, 0x00, "unable to recover TOC" },
 461         { 0x58, 0x00, "generation does not exist" },
 462         { 0x59, 0x00, "updated block read" },
 463         { 0x5a, 0x00, "operator request or state change input" },
 464         { 0x5a, 0x01, "operator medium removal request" },
 465         { 0x5a, 0x02, "operator selected write protect" },
 466         { 0x5a, 0x03, "operator selected write permit" },
 467         { 0x5b, 0x00, "log exception" },
 468         { 0x5b, 0x01, "threshold condition met" },
 469         { 0x5b, 0x02, "log counter at maximum" },
 470         { 0x5b, 0x03, "log list codes exhausted" },
 471         { 0x5c, 0x00, "RPL status change" },
 472         { 0x5c, 0x01, "spindles synchronized" },
 473         { 0x5c, 0x02, "spindles not synchronized" },
 474         { 0x5d, 0x00, "drive operation marginal, service immediately"
 475                     " (failure prediction threshold exceeded)" },
 476         { 0x5d, 0x01, "media failure prediction threshold exceeded" },
 477         { 0x5d, 0x02, "LUN failure prediction threshold exceeded" },
 478         { 0x5d, 0x03, "spare area exhaustion prediction threshold exceeded" },
 479         { 0x5d, 0x10, "hardware impending failure general hard drive failure" },
 480         { 0x5d, 0x11, "hardware impending failure drive error rate too high" },
 481         { 0x5d, 0x12, "hardware impending failure data error rate too high" },
 482         { 0x5d, 0x13, "hardware impending failure seek error rate too high" },
 483         { 0x5d, 0x14, "hardware impending failure too many block reassigns" },
 484         { 0x5d, 0x15, "hardware impending failure access times too high" },
 485         { 0x5d, 0x16, "hardware impending failure start unit times too high" },
 486         { 0x5d, 0x17, "hardware impending failure channel parametrics" },
 487         { 0x5d, 0x18, "hardware impending failure controller detected" },
 488         { 0x5d, 0x19, "hardware impending failure throughput performance" },
 489         { 0x5d, 0x1a, "hardware impending failure seek time performance" },
 490         { 0x5d, 0x1b, "hardware impending failure spin-up retry count" },
 491         { 0x5d, 0x1c, "hardware impending failure drive calibration retry "
 492                 "count" },
 493         { 0x5d, 0x20, "controller impending failure general hard drive "
 494                 "failure" },
 495         { 0x5d, 0x21, "controller impending failure drive error rate too "
 496                 "high" },
 497         { 0x5d, 0x22, "controller impending failure data error rate too high" },
 498         { 0x5d, 0x23, "controller impending failure seek error rate too high" },
 499         { 0x5d, 0x24, "controller impending failure too many block reassigns" },
 500         { 0x5d, 0x25, "controller impending failure access times too high" },
 501         { 0x5d, 0x26, "controller impending failure start unit times too "
 502                 "high" },
 503         { 0x5d, 0x27, "controller impending failure channel parametrics" },
 504         { 0x5d, 0x28, "controller impending failure controller detected" },
 505         { 0x5d, 0x29, "controller impending failure throughput performance" },
 506         { 0x5d, 0x2a, "controller impending failure seek time performance" },
 507         { 0x5d, 0x2b, "controller impending failure spin-up retry count" },
 508         { 0x5d, 0x2c, "controller impending failure drive calibration retry "
 509                 "cnt" },
 510         { 0x5d, 0x30, "data channel impending failure general hard drive "
 511                 "failure" },
 512         { 0x5d, 0x31, "data channel impending failure drive error rate too "
 513                 "high" },
 514         { 0x5d, 0x32, "data channel impending failure data error rate too "
 515                 "high" },
 516         { 0x5d, 0x33, "data channel impending failure seek error rate too "
 517                 "high" },
 518         { 0x5d, 0x34, "data channel impending failure too many block "
 519                 "reassigns" },
 520         { 0x5d, 0x35, "data channel impending failure access times too high" },
 521         { 0x5d, 0x36, "data channel impending failure start unit times too "
 522                 "high" },
 523         { 0x5d, 0x37, "data channel impending failure channel parametrics" },
 524         { 0x5d, 0x38, "data channel impending failure controller detected" },
 525         { 0x5d, 0x39, "data channel impending failure throughput performance" },
 526         { 0x5d, 0x3a, "data channel impending failure seek time performance" },
 527         { 0x5d, 0x3b, "data channel impending failure spin-up retry count" },
 528         { 0x5d, 0x3c, "data channel impending failure drive calibrate retry "
 529                 "cnt" },
 530         { 0x5d, 0x40, "servo impending failure general hard drive failure" },
 531         { 0x5d, 0x41, "servo impending failure drive error rate too high" },
 532         { 0x5d, 0x42, "servo impending failure data error rate too high" },
 533         { 0x5d, 0x43, "servo impending failure seek error rate too high" },
 534         { 0x5d, 0x44, "servo impending failure too many block reassigns" },
 535         { 0x5d, 0x45, "servo impending failure access times too high" },
 536         { 0x5d, 0x46, "servo impending failure start unit times too high" },
 537         { 0x5d, 0x47, "servo impending failure channel parametrics" },
 538         { 0x5d, 0x48, "servo impending failure controller detected" },
 539         { 0x5d, 0x49, "servo impending failure throughput performance" },
 540         { 0x5d, 0x4a, "servo impending failure seek time performance" },
 541         { 0x5d, 0x4b, "servo impending failure spin-up retry count" },
 542         { 0x5d, 0x4c, "servo impending failure drive calibration retry count" },
 543         { 0x5d, 0x50, "spindle impending failure general hard drive failure" },
 544         { 0x5d, 0x51, "spindle impending failure drive error rate too high" },
 545         { 0x5d, 0x52, "spindle impending failure data error rate too high" },
 546         { 0x5d, 0x53, "spindle impending failure seek error rate too high" },
 547         { 0x5d, 0x54, "spindle impending failure too many block reassigns" },
 548         { 0x5d, 0x55, "spindle impending failure access times too high" },
 549         { 0x5d, 0x56, "spindle impending failure start unit times too high" },
 550         { 0x5d, 0x57, "spindle impending failure channel parametrics" },
 551         { 0x5d, 0x58, "spindle impending failure controller detected" },
 552         { 0x5d, 0x59, "spindle impending failure throughput performance" },
 553         { 0x5d, 0x5a, "spindle impending failure seek time performance" },
 554         { 0x5d, 0x5b, "spindle impending failure spin-up retry count" },
 555         { 0x5d, 0x5c, "spindle impending failure drive calibration retry "
 556                 "count" },
 557         { 0x5d, 0x60, "firmware impending failure general hard drive failure" },
 558         { 0x5d, 0x61, "firmware impending failure drive error rate too high" },
 559         { 0x5d, 0x62, "firmware impending failure data error rate too high" },
 560         { 0x5d, 0x63, "firmware impending failure seek error rate too high" },
 561         { 0x5d, 0x64, "firmware impending failure too many block reassigns" },
 562         { 0x5d, 0x65, "firmware impending failure access times too high" },
 563         { 0x5d, 0x66, "firmware impending failure start unit times too high" },
 564         { 0x5d, 0x67, "firmware impending failure channel parametrics" },
 565         { 0x5d, 0x68, "firmware impending failure controller detected" },
 566         { 0x5d, 0x69, "firmware impending failure throughput performance" },
 567         { 0x5d, 0x6a, "firmware impending failure seek time performance" },
 568         { 0x5d, 0x6b, "firmware impending failure spin-up retry count" },
 569         { 0x5d, 0x6c, "firmware impending failure drive calibration retry "
 570                 "count" },
 571         { 0x5d, 0xff, "failure prediction threshold exceeded (false)" },
 572         { 0x5e, 0x00, "low power condition active" },
 573         { 0x5e, 0x01, "idle condition activated by timer" },
 574         { 0x5e, 0x02, "standby condition activated by timer" },
 575         { 0x5e, 0x03, "idle condition activated by command" },
 576         { 0x5e, 0x04, "standby condition activated by command" },
 577         { 0x60, 0x00, "lamp failure" },
 578         { 0x61, 0x00, "video aquisition error" },
 579         { 0x62, 0x00, "scan head positioning error" },
 580         { 0x63, 0x00, "end of user area encountered on this track" },
 581         { 0x63, 0x01, "packet does not fit in available space" },
 582         { 0x64, 0x00, "illegal mode for this track" },
 583         { 0x64, 0x01, "invalid packet size" },
 584         { 0x65, 0x00, "voltage fault" },
 585         { 0x66, 0x00, "automatic document feeder cover up" },
 586         { 0x67, 0x00, "configuration failure" },
 587         { 0x67, 0x01, "configuration of incapable LUNs failed" },
 588         { 0x67, 0x02, "add LUN failed" },
 589         { 0x67, 0x03, "modification of LUN failed" },
 590         { 0x67, 0x04, "exchange of LUN failed" },
 591         { 0x67, 0x05, "remove of LUN failed" },
 592         { 0x67, 0x06, "attachment of LUN failed" },
 593         { 0x67, 0x07, "creation of LUN failed" },
 594         { 0x67, 0x08, "assign failure occurred" },
 595         { 0x67, 0x09, "multiply assigned LUN" },
 596         { 0x67, 0x0a, "set target port groups command failed" },
 597         { 0x68, 0x00, "logical unit not configured" },
 598         { 0x69, 0x00, "data loss on logical unit" },
 599         { 0x69, 0x01, "multiple LUN failures" },
 600         { 0x69, 0x02, "parity/data mismatch" },
 601         { 0x6a, 0x00, "informational, refer to log" },
 602         { 0x6b, 0x00, "state change has occured" },
 603         { 0x6b, 0x01, "redundancy level got better" },
 604         { 0x6b, 0x02, "redundancy level got worse" },
 605         { 0x6c, 0x00, "rebuild failure occured" },
 606         { 0x6d, 0x00, "recalculate failure occured" },
 607         { 0x6e, 0x00, "command to logical unit failed" },
 608         { 0x6f, 0x00, "copy protect key exchange failure authentication "
 609                 "failure" },
 610         { 0x6f, 0x01, "copy protect key exchange failure key not present" },
 611         { 0x6f, 0x02, "copy protect key exchange failure key not established" },
 612         { 0x6f, 0x03, "read of scrambled sector without authentication" },
 613         { 0x6f, 0x04, "media region code is mismatched to LUN region" },
 614         { 0x6f, 0x05, "drive region must be permanent/region reset count "
 615                 "error" },
 616         { 0x70, 0xffff, "decompression exception short algorithm id of ASCQ" },
 617         { 0x71, 0x00, "decompression exception long algorithm id" },
 618         { 0x72, 0x00, "session fixation error" },
 619         { 0x72, 0x01, "session fixation error writing lead-in" },
 620         { 0x72, 0x02, "session fixation error writing lead-out" },
 621         { 0x72, 0x03, "session fixation error - incomplete track in session" },
 622         { 0x72, 0x04, "empty or partially written reserved track" },
 623         { 0x72, 0x05, "no more track reservations allowed" },
 624         { 0x73, 0x00, "cd control error" },
 625         { 0x73, 0x01, "power calibration area almost full" },
 626         { 0x73, 0x02, "power calibration area is full" },
 627         { 0x73, 0x03, "power calibration area error" },
 628         { 0x73, 0x04, "program memory area update failure" },
 629         { 0x73, 0x05, "program memory area is full" },
 630         { 0x73, 0x06, "rma/pma is almost full" },
 631         { 0xffff, 0xffff, NULL }
 632 };
 633 
 634 /*
 635  * Given an asc (Additional Sense Code) and ascq (Additional Sense Code
 636  * Qualifier), return a string describing the error information.
 637  */
 638 static char *
 639 scsi_util_asc_ascq_name(uint_t asc, uint_t ascq, char *buf, int buflen)
 640 {
 641         int i = 0;
 642 
 643         while (extended_sense_list[i].asc != 0xffff) {
 644                 if ((asc == extended_sense_list[i].asc) &&
 645                     ((ascq == extended_sense_list[i].ascq) ||
 646                     (extended_sense_list[i].ascq == 0xffff))) {
 647                         return ((char *)extended_sense_list[i].message);
 648                 }
 649                 i++;
 650         }
 651         (void) snprintf(buf, buflen, "<vendor unique code 0x%x>", asc);
 652         return (buf);
 653 }
 654 
 655 /*
 656  * Dumps detailed information about a particular SCSI error condition.
 657  */
 658 static void
 659 scsi_printerr(struct uscsi_cmd *ucmd, struct scsi_extended_sense *rq, int rqlen)
 660 {
 661         diskaddr_t      blkno;
 662         struct scsi_descr_sense_hdr *sdsp = (struct scsi_descr_sense_hdr *)rq;
 663         char msgbuf[MSGBUFLEN];
 664 
 665         if (find_string(sensekey_strings, rq->es_key) == NULL)
 666                 dprintf("unknown error");
 667 
 668         dprintf("during %s:",
 669             find_string(scsi_cmdname_strings, ucmd->uscsi_cdb[0]));
 670 
 671         /*
 672          * Get asc, ascq and info field from sense data.  There are two
 673          * possible formats (fixed sense data and descriptor sense data)
 674          * depending on the value of es_code.
 675          */
 676         switch (rq->es_code) {
 677         case CODE_FMT_DESCR_CURRENT:
 678         case CODE_FMT_DESCR_DEFERRED:
 679                 blkno = (diskaddr_t)scsi_extract_sense_info_descr(sdsp, rqlen);
 680                 if (blkno != (diskaddr_t)-1)
 681                         dprintf(": block %lld (0x%llx)", blkno, blkno);
 682                 dprintf("\n");
 683                 dprintf("ASC: 0x%x   ASCQ: 0x%x    (%s)\n",
 684                     sdsp->ds_add_code, sdsp->ds_qual_code,
 685                     scsi_util_asc_ascq_name(sdsp->ds_add_code,
 686                     sdsp->ds_qual_code, msgbuf, MSGBUFLEN));
 687 
 688                 break;
 689 
 690         case CODE_FMT_FIXED_CURRENT:
 691         case CODE_FMT_FIXED_DEFERRED:
 692         default:
 693                 if (rq->es_valid) {
 694                         blkno = (rq->es_info_1 << 24) |
 695                             (rq->es_info_2 << 16) |
 696                             (rq->es_info_3 << 8) | rq->es_info_4;
 697                         dprintf(": block %lld (0x%llx)", blkno, blkno);
 698                 }
 699                 dprintf("\n");
 700                 if (rq->es_add_len >= 6) {
 701                         dprintf("ASC: 0x%x   ASCQ: 0x%x    (%s)\n",
 702                             rq->es_add_code,
 703                             rq->es_qual_code,
 704                             scsi_util_asc_ascq_name(rq->es_add_code,
 705                             rq->es_qual_code, msgbuf, MSGBUFLEN));
 706                 }
 707                 break;
 708         }
 709 
 710         if (rq->es_key == KEY_ILLEGAL_REQUEST) {
 711                 ddump("cmd:", (caddr_t)ucmd,
 712                     sizeof (struct uscsi_cmd));
 713                 ddump("cdb:", (caddr_t)ucmd->uscsi_cdb,
 714                     ucmd->uscsi_cdblen);
 715         }
 716         ddump("sense:", (caddr_t)rq, rqlen);
 717 
 718         switch (rq->es_code) {
 719         case CODE_FMT_DESCR_CURRENT:
 720         case CODE_FMT_DESCR_DEFERRED:
 721                 scsi_print_descr_sense(sdsp, rqlen);
 722                 break;
 723         case CODE_FMT_FIXED_CURRENT:
 724         case CODE_FMT_FIXED_DEFERRED:
 725         default:
 726                 scsi_print_extended_sense(rq, rqlen);
 727                 break;
 728         }
 729 }
 730 
 731 /*
 732  * Retrieve "information" field from descriptor format sense data.  Iterates
 733  * through each sense descriptor looking for the information descriptor and
 734  * returns the information field from that descriptor.
 735  */
 736 static diskaddr_t
 737 scsi_extract_sense_info_descr(struct scsi_descr_sense_hdr *sdsp, int rqlen)
 738 {
 739         diskaddr_t result;
 740         uint8_t *descr_offset;
 741         int valid_sense_length;
 742         struct scsi_information_sense_descr *isd;
 743 
 744         /*
 745          * Initialize result to -1 indicating there is no information
 746          * descriptor
 747          */
 748         result = (diskaddr_t)-1;
 749 
 750         /*
 751          * The first descriptor will immediately follow the header
 752          */
 753         descr_offset = (uint8_t *)(sdsp+1);
 754 
 755         /*
 756          * Calculate the amount of valid sense data
 757          */
 758         valid_sense_length =
 759             MIN((sizeof (struct scsi_descr_sense_hdr) +
 760             sdsp->ds_addl_sense_length), rqlen);
 761 
 762         /*
 763          * Iterate through the list of descriptors, stopping when we run out of
 764          * sense data
 765          */
 766         while ((descr_offset + sizeof (struct scsi_information_sense_descr)) <=
 767             (uint8_t *)sdsp + valid_sense_length) {
 768                 /*
 769                  * Check if this is an information descriptor.  We can use the
 770                  * scsi_information_sense_descr structure as a template since
 771                  * the first two fields are always the same
 772                  */
 773                 isd = (struct scsi_information_sense_descr *)descr_offset;
 774                 if (isd->isd_descr_type == DESCR_INFORMATION) {
 775                         /*
 776                          * Found an information descriptor.  Copy the
 777                          * information field.  There will only be one
 778                          * information descriptor so we can stop looking.
 779                          */
 780                         result =
 781                             (((diskaddr_t)isd->isd_information[0] << 56) |
 782                             ((diskaddr_t)isd->isd_information[1] << 48) |
 783                             ((diskaddr_t)isd->isd_information[2] << 40) |
 784                             ((diskaddr_t)isd->isd_information[3] << 32) |
 785                             ((diskaddr_t)isd->isd_information[4] << 24) |
 786                             ((diskaddr_t)isd->isd_information[5] << 16) |
 787                             ((diskaddr_t)isd->isd_information[6] << 8)  |
 788                             ((diskaddr_t)isd->isd_information[7]));
 789                         break;
 790                 }
 791 
 792                 /*
 793                  * Get pointer to the next descriptor.  The "additional length"
 794                  * field holds the length of the descriptor except for the
 795                  * "type" and "additional length" fields, so we need to add 2 to
 796                  * get the total length.
 797                  */
 798                 descr_offset += (isd->isd_addl_length + 2);
 799         }
 800 
 801         return (result);
 802 }
 803 
 804 /*
 805  * Display the full scsi_extended_sense as returned by the device
 806  */
 807 static void
 808 scsi_print_extended_sense(struct scsi_extended_sense *rq, int rqlen)
 809 {
 810         static char *scsi_extended_sense_labels[] = {
 811             "Request sense valid:             ",
 812             "Error class and code:            ",
 813             "Segment number:                  ",
 814             "Filemark:                        ",
 815             "End-of-medium:                   ",
 816             "Incorrect length indicator:      ",
 817             "Sense key:                       ",
 818             "Information field:               ",
 819             "Additional sense length:         ",
 820             "Command-specific information:    ",
 821             "Additional sense code:           ",
 822             "Additional sense code qualifier: ",
 823             "Field replaceable unit code:     ",
 824             "Sense-key specific:              ",
 825             "Additional sense bytes:          "
 826         };
 827 
 828         char **p = scsi_extended_sense_labels;
 829 
 830         if (rqlen < (sizeof (*rq) - 2) || !rq->es_valid) {
 831                 /*
 832                  * target should be capable of returning at least 18
 833                  * bytes of data, i.e upto rq->es_skey_specific field.
 834                  * The additional sense bytes (2 or more ...) are optional.
 835                  */
 836                 return;
 837         }
 838 
 839         dprintf("\n%s%s\n", *p++, rq->es_valid ? "yes" : "no");
 840         dprintf("%s0x%02x\n", *p++, (rq->es_class << 4) + rq->es_code);
 841         dprintf("%s%d\n", *p++, rq->es_segnum);
 842         dprintf("%s%s\n", *p++, rq->es_filmk ? "yes" : "no");
 843         dprintf("%s%s\n", *p++, rq->es_eom ? "yes" : "no");
 844         dprintf("%s%s\n", *p++, rq->es_ili ? "yes" : "no");
 845         dprintf("%s%d\n", *p++, rq->es_key);
 846 
 847         dprintf("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++, rq->es_info_1,
 848             rq->es_info_2, rq->es_info_3, rq->es_info_4);
 849         dprintf("%s%d\n", *p++, rq->es_add_len);
 850         dprintf("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++,
 851             rq->es_cmd_info[0], rq->es_cmd_info[1], rq->es_cmd_info[2],
 852             rq->es_cmd_info[3]);
 853         dprintf("%s0x%02x = %d\n", *p++, rq->es_add_code,
 854             rq->es_add_code);
 855         dprintf("%s0x%02x = %d\n", *p++, rq->es_qual_code,
 856             rq->es_qual_code);
 857         dprintf("%s%d\n", *p++, rq->es_fru_code);
 858         dprintf("%s0x%02x 0x%02x 0x%02x\n", *p++,
 859             rq->es_skey_specific[0], rq->es_skey_specific[1],
 860             rq->es_skey_specific[2]);
 861         if (rqlen >= sizeof (*rq)) {
 862                 dprintf("%s0x%02x 0x%02x%s\n", *p, rq->es_add_info[0],
 863                     rq->es_add_info[1], (rqlen > sizeof (*rq)) ? " ..." : "");
 864         }
 865 
 866         dprintf("\n");
 867 }
 868 
 869 /*
 870  * Display the full descriptor sense data as returned by the device
 871  */
 872 static void
 873 scsi_print_descr_sense(struct scsi_descr_sense_hdr *rq, int rqlen)
 874 {
 875         /*
 876          * Labels for the various fields of the scsi_descr_sense_hdr structure
 877          */
 878         static char *scsi_descr_sense_labels[] = {
 879             "Error class and code:            ",
 880             "Sense key:                       ",
 881             "Additional sense length:         ",
 882             "Additional sense code:           ",
 883             "Additional sense code qualifier: ",
 884             "Additional sense bytes:          "
 885         };
 886 
 887         struct scsi_information_sense_descr *isd;
 888         uint8_t *descr_offset;
 889         int valid_sense_length;
 890         char **p = scsi_descr_sense_labels;
 891 
 892         /* Target must return at least 8 bytes of data */
 893         if (rqlen < sizeof (struct scsi_descr_sense_hdr))
 894                 return;
 895 
 896         /* Print descriptor sense header */
 897         dprintf("%s0x%02x\n", *p++, (rq->ds_class << 4) + rq->ds_code);
 898         dprintf("%s%d\n", *p++, rq->ds_key);
 899 
 900         dprintf("%s%d\n", *p++, rq->ds_addl_sense_length);
 901         dprintf("%s0x%02x = %d\n", *p++, rq->ds_add_code,
 902             rq->ds_add_code);
 903         dprintf("%s0x%02x = %d\n", *p++, rq->ds_qual_code,
 904             rq->ds_qual_code);
 905         dprintf("\n");
 906 
 907         /*
 908          * Now print any sense descriptors.   The first descriptor will
 909          * immediately follow the header
 910          */
 911         descr_offset = (uint8_t *)(rq+1); /* Pointer arithmetic */
 912 
 913         /*
 914          * Calculate the amount of valid sense data
 915          */
 916         valid_sense_length =
 917             MIN((sizeof (struct scsi_descr_sense_hdr) +
 918             rq->ds_addl_sense_length), rqlen);
 919 
 920         /*
 921          * Iterate through the list of descriptors, stopping when we
 922          * run out of sense data.  Descriptor format is:
 923          *
 924          * <Descriptor type> <Descriptor length> <Descriptor data> ...
 925          */
 926         while ((descr_offset + *(descr_offset + 1)) <=
 927             (uint8_t *)rq + valid_sense_length) {
 928                 /*
 929                  * Determine descriptor type.  We can use the
 930                  * scsi_information_sense_descr structure as a
 931                  * template since the first two fields are always the
 932                  * same.
 933                  */
 934                 isd = (struct scsi_information_sense_descr *)descr_offset;
 935                 switch (isd->isd_descr_type) {
 936                 case DESCR_INFORMATION: {
 937                         uint64_t information;
 938 
 939                         information =
 940                             (((uint64_t)isd->isd_information[0] << 56) |
 941                             ((uint64_t)isd->isd_information[1] << 48) |
 942                             ((uint64_t)isd->isd_information[2] << 40) |
 943                             ((uint64_t)isd->isd_information[3] << 32) |
 944                             ((uint64_t)isd->isd_information[4] << 24) |
 945                             ((uint64_t)isd->isd_information[5] << 16) |
 946                             ((uint64_t)isd->isd_information[6] << 8)  |
 947                             ((uint64_t)isd->isd_information[7]));
 948                         dprintf("Information field:               "
 949                             "%0" PRIx64 "\n", information);
 950                         break;
 951                 }
 952                 case DESCR_COMMAND_SPECIFIC: {
 953                         struct scsi_cmd_specific_sense_descr *c =
 954                             (struct scsi_cmd_specific_sense_descr *)isd;
 955                         uint64_t cmd_specific;
 956 
 957                         cmd_specific =
 958                             (((uint64_t)c->css_cmd_specific_info[0] << 56) |
 959                             ((uint64_t)c->css_cmd_specific_info[1] << 48) |
 960                             ((uint64_t)c->css_cmd_specific_info[2] << 40) |
 961                             ((uint64_t)c->css_cmd_specific_info[3] << 32) |
 962                             ((uint64_t)c->css_cmd_specific_info[4] << 24) |
 963                             ((uint64_t)c->css_cmd_specific_info[5] << 16) |
 964                             ((uint64_t)c->css_cmd_specific_info[6] << 8)  |
 965                             ((uint64_t)c->css_cmd_specific_info[7]));
 966                         dprintf("Command-specific information:    "
 967                             "%0" PRIx64 "\n", cmd_specific);
 968                         break;
 969                 }
 970                 case DESCR_SENSE_KEY_SPECIFIC: {
 971                         struct scsi_sk_specific_sense_descr *ssd =
 972                             (struct scsi_sk_specific_sense_descr *)isd;
 973                         uint8_t *sk_spec_ptr = (uint8_t *)&ssd->sss_data;
 974                         dprintf("Sense-key specific:              "
 975                             "0x%02x 0x%02x 0x%02x\n", sk_spec_ptr[0],
 976                             sk_spec_ptr[1], sk_spec_ptr[2]);
 977                         break;
 978                 }
 979                 case DESCR_FRU: {
 980                         struct scsi_fru_sense_descr *fsd =
 981                             (struct scsi_fru_sense_descr *)isd;
 982                         dprintf("Field replaceable unit code:     "
 983                             "%d\n", fsd->fs_fru_code);
 984                         break;
 985                 }
 986                 case DESCR_BLOCK_COMMANDS: {
 987                         struct scsi_block_cmd_sense_descr *bsd =
 988                             (struct scsi_block_cmd_sense_descr *)isd;
 989                         dprintf("Incorrect length indicator:      "
 990                             "%s\n", bsd->bcs_ili ? "yes" : "no");
 991                         break;
 992                 }
 993                 default:
 994                         /* Ignore */
 995                         break;
 996                 }
 997 
 998                 /*
 999                  * Get pointer to the next descriptor.  The "additional
1000                  * length" field holds the length of the descriptor except
1001                  * for the "type" and "additional length" fields, so
1002                  * we need to add 2 to get the total length.
1003                  */
1004                 descr_offset += (isd->isd_addl_length + 2);
1005         }
1006 
1007         dprintf("\n");
1008 }
1009 
1010 static int
1011 uscsi_timeout(void)
1012 {
1013         const char *env = getenv("USCSI_TIMEOUT");
1014         static int timeo = -1;
1015         int i;
1016 
1017         if (timeo > 0)
1018                 return (timeo);
1019 
1020         if (env != NULL) {
1021                 i = atoi(env);
1022                 if (i > USCSI_TIMEOUT_MAX)
1023                         i = USCSI_TIMEOUT_MAX;
1024                 else if (i <= 0)
1025                         i = USCSI_DEFAULT_TIMEOUT;
1026         } else
1027                 i = USCSI_DEFAULT_TIMEOUT;
1028 
1029         timeo = i;
1030         return (i);
1031 }
1032 
1033 /*
1034  * Execute a command and determine the result.  Uses the "uscsi" ioctl
1035  * interface, which is fully supported.
1036  *
1037  * If the user wants request sense data to be returned in case of error then ,
1038  * the "uscsi_cmd" structure should have the request sense buffer allocated in
1039  * uscsi_rqbuf.
1040  */
1041 static int
1042 uscsi_cmd(int fd, struct uscsi_cmd *ucmd, void *rqbuf, int *rqlen)
1043 {
1044         struct scsi_extended_sense *rq;
1045         int status;
1046 
1047         /*
1048          * Set function flags for driver.
1049          */
1050         ucmd->uscsi_flags = USCSI_ISOLATE;
1051         if (!ds_debug)
1052                 ucmd->uscsi_flags |= USCSI_SILENT;
1053 
1054         /*
1055          * If this command will perform a read, set the USCSI_READ flag
1056          */
1057         if (ucmd->uscsi_buflen > 0) {
1058                 /*
1059                  * uscsi_cdb is declared as a caddr_t, so any CDB
1060                  * command byte with the MSB set will result in a
1061                  * compiler error unless we cast to an unsigned value.
1062                  */
1063                 switch ((uint8_t)ucmd->uscsi_cdb[0]) {
1064                 case SCMD_MODE_SENSE:
1065                 case SCMD_MODE_SENSE_G1:
1066                 case SCMD_LOG_SENSE_G1:
1067                 case SCMD_REQUEST_SENSE:
1068                         ucmd->uscsi_flags |= USCSI_READ;
1069                         break;
1070 
1071                 case SCMD_MODE_SELECT:
1072                 case SCMD_MODE_SELECT_G1:
1073                         /* LINTED */
1074                         ucmd->uscsi_flags |= USCSI_WRITE;
1075                         break;
1076                 default:
1077                         assert(0);
1078                         break;
1079                 }
1080         }
1081 
1082         /* Set timeout */
1083         ucmd->uscsi_timeout = uscsi_timeout();
1084 
1085         /*
1086          * Set up Request Sense buffer
1087          */
1088 
1089         if (ucmd->uscsi_rqbuf == NULL)  {
1090                 ucmd->uscsi_rqbuf = rqbuf;
1091                 ucmd->uscsi_rqlen = *rqlen;
1092                 ucmd->uscsi_rqresid = *rqlen;
1093         }
1094         if (ucmd->uscsi_rqbuf)
1095                 ucmd->uscsi_flags |= USCSI_RQENABLE;
1096         ucmd->uscsi_rqstatus = IMPOSSIBLE_SCSI_STATUS;
1097 
1098         if (ucmd->uscsi_rqbuf != NULL && ucmd->uscsi_rqlen > 0)
1099                 (void) memset(ucmd->uscsi_rqbuf, 0, ucmd->uscsi_rqlen);
1100 
1101         /*
1102          * Execute the ioctl
1103          */
1104         status = ioctl(fd, USCSICMD, ucmd);
1105         if (status == 0 && ucmd->uscsi_status == 0)
1106                 return (status);
1107 
1108         /*
1109          * If an automatic Request Sense gave us valid info about the error, we
1110          * may be able to use that to print a reasonable error msg.
1111          */
1112         if (ucmd->uscsi_rqstatus == IMPOSSIBLE_SCSI_STATUS) {
1113                 dprintf("No request sense for command %s\n",
1114                     find_string(scsi_cmdname_strings,
1115                     ucmd->uscsi_cdb[0]));
1116                 return (-1);
1117         }
1118         if (ucmd->uscsi_rqstatus != STATUS_GOOD) {
1119                 dprintf("Request sense status for command %s: 0x%x\n",
1120                     find_string(scsi_cmdname_strings,
1121                     ucmd->uscsi_cdb[0]),
1122                     ucmd->uscsi_rqstatus);
1123                 return (-1);
1124         }
1125 
1126         rq = (struct scsi_extended_sense *)ucmd->uscsi_rqbuf;
1127         *rqlen = ucmd->uscsi_rqlen - ucmd->uscsi_rqresid;
1128 
1129         if ((((int)rq->es_add_len) + 8) < MIN_REQUEST_SENSE_LEN ||
1130             rq->es_class != CLASS_EXTENDED_SENSE ||
1131             *rqlen < MIN_REQUEST_SENSE_LEN) {
1132                 dprintf("Request sense for command %s failed\n",
1133                     find_string(scsi_cmdname_strings,
1134                     ucmd->uscsi_cdb[0]));
1135 
1136                 dprintf("Sense data:\n");
1137                 ddump(NULL, (caddr_t)rqbuf, *rqlen);
1138 
1139                 return (-1);
1140         }
1141 
1142         /*
1143          * If the failed command is a Mode Select, and the
1144          * target is indicating that it has rounded one of
1145          * the mode select parameters, as defined in the SCSI-2
1146          * specification, then we should accept the command
1147          * as successful.
1148          */
1149         if (ucmd->uscsi_cdb[0] == SCMD_MODE_SELECT ||
1150             ucmd->uscsi_cdb[0] == SCMD_MODE_SELECT_G1) {
1151                 if (rq->es_key == KEY_RECOVERABLE_ERROR &&
1152                     rq->es_add_code == ROUNDED_PARAMETER &&
1153                     rq->es_qual_code == 0) {
1154                         return (0);
1155                 }
1156         }
1157 
1158         if (ds_debug)
1159                 scsi_printerr(ucmd, rq, *rqlen);
1160         if (rq->es_key != KEY_RECOVERABLE_ERROR)
1161                 return (-1);
1162         return (0);
1163 }
1164 
1165 int
1166 uscsi_request_sense(int fd, caddr_t buf, int buflen, void *rqbuf, int *rqblen)
1167 {
1168         struct uscsi_cmd ucmd;
1169         union scsi_cdb cdb;
1170         int status;
1171 
1172         (void) memset(buf, 0, buflen);
1173         (void) memset(&ucmd, 0, sizeof (ucmd));
1174         (void) memset(&cdb, 0, sizeof (union scsi_cdb));
1175         cdb.scc_cmd = SCMD_REQUEST_SENSE;
1176         FORMG0COUNT(&cdb, (uchar_t)buflen);
1177         ucmd.uscsi_cdb = (caddr_t)&cdb;
1178         ucmd.uscsi_cdblen = CDB_GROUP0;
1179         ucmd.uscsi_bufaddr = buf;
1180         ucmd.uscsi_buflen = buflen;
1181         status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
1182         if (status)
1183                 dprintf("Request sense failed\n");
1184         if (status == 0)
1185                 ddump("Request Sense data:", buf, buflen);
1186 
1187         return (status);
1188 }
1189 
1190 /*
1191  * Execute a uscsi mode sense command.  This can only be used to return one page
1192  * at a time.  Return the mode header/block descriptor and the actual page data
1193  * separately - this allows us to support devices which return either 0 or 1
1194  * block descriptors.  Whatever a device gives us in the mode header/block
1195  * descriptor will be returned to it upon subsequent mode selects.
1196  */
1197 int
1198 uscsi_mode_sense(int fd, int page_code, int page_control, caddr_t page_data,
1199     int page_size, struct scsi_ms_header *header, void *rqbuf, int *rqblen)
1200 {
1201         caddr_t mode_sense_buf;
1202         struct mode_header *hdr;
1203         struct mode_page *pg;
1204         int nbytes;
1205         struct uscsi_cmd ucmd;
1206         union scsi_cdb cdb;
1207         int status;
1208         int maximum;
1209         char *pc;
1210 
1211         assert(page_size >= 0 && page_size < 256);
1212         assert(page_control == PC_CURRENT || page_control == PC_CHANGEABLE ||
1213             page_control == PC_DEFAULT || page_control == PC_SAVED);
1214 
1215         nbytes = sizeof (struct scsi_ms_header) + page_size;
1216         mode_sense_buf = alloca((uint_t)nbytes);
1217 
1218         /*
1219          * Build and execute the uscsi ioctl
1220          */
1221         (void) memset(mode_sense_buf, 0, nbytes);
1222         (void) memset(&ucmd, 0, sizeof (ucmd));
1223         (void) memset(&cdb, 0, sizeof (union scsi_cdb));
1224         cdb.scc_cmd = SCMD_MODE_SENSE;
1225         FORMG0COUNT(&cdb, (uchar_t)nbytes);
1226         cdb.cdb_opaque[2] = page_control | page_code;
1227         ucmd.uscsi_cdb = (caddr_t)&cdb;
1228         ucmd.uscsi_cdblen = CDB_GROUP0;
1229         ucmd.uscsi_bufaddr = mode_sense_buf;
1230         ucmd.uscsi_buflen = nbytes;
1231         status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
1232         if (status) {
1233                 dprintf("Mode sense page 0x%x failed\n", page_code);
1234                 return (-1);
1235         }
1236 
1237         ddump("RAW MODE SENSE BUFFER", mode_sense_buf, nbytes);
1238 
1239         /*
1240          * Verify that the returned data looks reasonable, find the actual page
1241          * data, and copy it into the user's buffer.  Copy the mode_header and
1242          * block_descriptor into the header structure, which can then be used to
1243          * return the same data to the drive when issuing a mode select.
1244          */
1245         hdr = (struct mode_header *)mode_sense_buf;
1246         (void) memset((caddr_t)header, 0, sizeof (struct scsi_ms_header));
1247         if (hdr->bdesc_length != sizeof (struct block_descriptor) &&
1248             hdr->bdesc_length != 0) {
1249                 dprintf("\nMode sense page 0x%x: block descriptor "
1250                     "length %d incorrect\n", page_code, hdr->bdesc_length);
1251                 ddump("Mode sense:", mode_sense_buf, nbytes);
1252                 return (-1);
1253         }
1254         (void) memcpy((caddr_t)header, mode_sense_buf,
1255             (int)(MODE_HEADER_LENGTH + hdr->bdesc_length));
1256         pg = (struct mode_page *)((ulong_t)mode_sense_buf +
1257             MODE_HEADER_LENGTH + hdr->bdesc_length);
1258 
1259         if (page_code == MODEPAGE_ALLPAGES) {
1260                 /* special case */
1261 
1262                 (void) memcpy(page_data, (caddr_t)pg,
1263                     (hdr->length + sizeof (header->ms_header.length)) -
1264                     (MODE_HEADER_LENGTH + hdr->bdesc_length));
1265 
1266                 pc = find_string(page_control_strings, page_control);
1267                 dprintf("\nMode sense page 0x%x (%s):\n", page_code,
1268                     pc != NULL ? pc : "");
1269                 ddump("header:", (caddr_t)header,
1270                     sizeof (struct scsi_ms_header));
1271                 ddump("data:", page_data,
1272                     (hdr->length +
1273                     sizeof (header->ms_header.length)) -
1274                     (MODE_HEADER_LENGTH + hdr->bdesc_length));
1275 
1276                 return (0);
1277         }
1278 
1279         if (pg->code != page_code) {
1280                 dprintf("\nMode sense page 0x%x: incorrect page code 0x%x\n",
1281                     page_code, pg->code);
1282                 ddump("Mode sense:", mode_sense_buf, nbytes);
1283                 return (-1);
1284         }
1285 
1286         /*
1287          * Accept up to "page_size" bytes of mode sense data.  This allows us to
1288          * accept both CCS and SCSI-2 structures, as long as we request the
1289          * greater of the two.
1290          */
1291         maximum = page_size - sizeof (struct mode_page);
1292         if (((int)pg->length) > maximum) {
1293                 dprintf("Mode sense page 0x%x: incorrect page "
1294                     "length %d - expected max %d\n",
1295                     page_code, pg->length, maximum);
1296                 ddump("Mode sense:", mode_sense_buf, nbytes);
1297                 return (-1);
1298         }
1299 
1300         (void) memcpy(page_data, (caddr_t)pg, MODESENSE_PAGE_LEN(pg));
1301 
1302         pc = find_string(page_control_strings, page_control);
1303         dprintf("\nMode sense page 0x%x (%s):\n", page_code,
1304             pc != NULL ? pc : "");
1305         ddump("header:", (caddr_t)header, sizeof (struct scsi_ms_header));
1306         ddump("data:", page_data, MODESENSE_PAGE_LEN(pg));
1307 
1308         return (0);
1309 }
1310 
1311 /*
1312  * Execute a uscsi MODE SENSE(10) command.  This can only be used to return one
1313  * page at a time.  Return the mode header/block descriptor and the actual page
1314  * data separately - this allows us to support devices which return either 0 or
1315  * 1 block descriptors.  Whatever a device gives us in the mode header/block
1316  * descriptor will be returned to it upon subsequent mode selects.
1317  */
1318 int
1319 uscsi_mode_sense_10(int fd, int page_code, int page_control,
1320     caddr_t page_data, int page_size, struct scsi_ms_header_g1 *header,
1321     void *rqbuf, int *rqblen)
1322 {
1323         caddr_t mode_sense_buf;
1324         struct mode_header_g1 *hdr;
1325         struct mode_page *pg;
1326         int nbytes;
1327         struct uscsi_cmd ucmd;
1328         union scsi_cdb cdb;
1329         int status;
1330         int maximum;
1331         ushort_t length, bdesc_length;
1332         char *pc;
1333 
1334         assert(page_size >= 0 && page_size < UINT16_MAX);
1335         assert(page_control == PC_CURRENT || page_control == PC_CHANGEABLE ||
1336             page_control == PC_DEFAULT || page_control == PC_SAVED);
1337 
1338         nbytes = sizeof (struct scsi_ms_header_g1) + page_size;
1339         mode_sense_buf = alloca((uint_t)nbytes);
1340 
1341         (void) memset(mode_sense_buf, 0, nbytes);
1342         (void) memset((char *)&ucmd, 0, sizeof (ucmd));
1343         (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
1344         cdb.scc_cmd = SCMD_MODE_SENSE_G1;
1345         FORMG1COUNT(&cdb, (uint16_t)nbytes);
1346         cdb.cdb_opaque[2] = page_control | page_code;
1347         ucmd.uscsi_cdb = (caddr_t)&cdb;
1348         ucmd.uscsi_cdblen = CDB_GROUP1;
1349         ucmd.uscsi_bufaddr = mode_sense_buf;
1350         ucmd.uscsi_buflen = nbytes;
1351 
1352         status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
1353         if (status) {
1354                 dprintf("Mode sense(10) page 0x%x failed\n",
1355                     page_code);
1356                 return (-1);
1357         }
1358 
1359         ddump("RAW MODE SENSE(10) BUFFER", mode_sense_buf, nbytes);
1360 
1361         /*
1362          * Verify that the returned data looks reasonable, find the actual page
1363          * data, and copy it into the user's buffer.  Copy the mode_header and
1364          * block_descriptor into the header structure, which can then be used to
1365          * return the same data to the drive when issuing a mode select.
1366          */
1367         /* LINTED */
1368         hdr = (struct mode_header_g1 *)mode_sense_buf;
1369 
1370         length = BE_16(hdr->length);
1371         bdesc_length = BE_16(hdr->bdesc_length);
1372 
1373         (void) memset((caddr_t)header, 0, sizeof (struct scsi_ms_header_g1));
1374         if (bdesc_length != sizeof (struct block_descriptor) &&
1375             bdesc_length != 0) {
1376                 dprintf("\nMode sense(10) page 0x%x: block descriptor "
1377                     "length %d incorrect\n", page_code, bdesc_length);
1378                 ddump("Mode sense(10):", mode_sense_buf, nbytes);
1379                 return (-1);
1380         }
1381         (void) memcpy((caddr_t)header, mode_sense_buf,
1382             (int)(MODE_HEADER_LENGTH_G1 + bdesc_length));
1383         pg = (struct mode_page *)((ulong_t)mode_sense_buf +
1384             MODE_HEADER_LENGTH_G1 + bdesc_length);
1385 
1386         if (page_code == MODEPAGE_ALLPAGES) {
1387                 /* special case */
1388 
1389                 (void) memcpy(page_data, (caddr_t)pg,
1390                     (length + sizeof (header->ms_header.length)) -
1391                     (MODE_HEADER_LENGTH_G1 + bdesc_length));
1392 
1393                 pc = find_string(page_control_strings, page_control);
1394                 dprintf("\nMode sense(10) page 0x%x (%s):\n",
1395                     page_code, pc != NULL ? pc : "");
1396                 ddump("header:", (caddr_t)header,
1397                     MODE_HEADER_LENGTH_G1 + bdesc_length);
1398 
1399                 ddump("data:", page_data,
1400                     (length + sizeof (header->ms_header.length)) -
1401                     (MODE_HEADER_LENGTH_G1 + bdesc_length));
1402 
1403                 return (0);
1404         }
1405 
1406         if (pg->code != page_code) {
1407                 dprintf("\nMode sense(10) page 0x%x: incorrect page "
1408                     "code 0x%x\n", page_code, pg->code);
1409                 ddump("Mode sense(10):", mode_sense_buf, nbytes);
1410                 return (-1);
1411         }
1412 
1413         /*
1414          * Accept up to "page_size" bytes of mode sense data.  This allows us to
1415          * accept both CCS and SCSI-2 structures, as long as we request the
1416          * greater of the two.
1417          */
1418         maximum = page_size - sizeof (struct mode_page);
1419         if (((int)pg->length) > maximum) {
1420                 dprintf("Mode sense(10) page 0x%x: incorrect page "
1421                     "length %d - expected max %d\n",
1422                     page_code, pg->length, maximum);
1423                 ddump("Mode sense(10):", mode_sense_buf,
1424                     nbytes);
1425                 return (-1);
1426         }
1427 
1428         (void) memcpy(page_data, (caddr_t)pg, MODESENSE_PAGE_LEN(pg));
1429 
1430         pc = find_string(page_control_strings, page_control);
1431         dprintf("\nMode sense(10) page 0x%x (%s):\n", page_code,
1432             pc != NULL ? pc : "");
1433         ddump("header:", (caddr_t)header,
1434             sizeof (struct scsi_ms_header_g1));
1435         ddump("data:", page_data, MODESENSE_PAGE_LEN(pg));
1436 
1437         return (0);
1438 }
1439 
1440 /*
1441  * Execute a uscsi mode select command.
1442  */
1443 int
1444 uscsi_mode_select(int fd, int page_code, int options, caddr_t page_data,
1445     int page_size, struct scsi_ms_header *header, void *rqbuf, int *rqblen)
1446 {
1447         caddr_t mode_select_buf;
1448         int nbytes;
1449         struct uscsi_cmd ucmd;
1450         union scsi_cdb cdb;
1451         int status;
1452         char *s;
1453 
1454         assert(((struct mode_page *)page_data)->ps == 0);
1455         assert(header->ms_header.length == 0);
1456         assert(header->ms_header.device_specific == 0);
1457         assert((options & ~(MODE_SELECT_SP|MODE_SELECT_PF)) == 0);
1458 
1459         nbytes = sizeof (struct scsi_ms_header) + page_size;
1460         mode_select_buf = alloca((uint_t)nbytes);
1461 
1462         /*
1463          * Build the mode select data out of the header and page data This
1464          * allows us to support devices which return either 0 or 1 block
1465          * descriptors.
1466          */
1467         (void) memset(mode_select_buf, 0, nbytes);
1468         nbytes = MODE_HEADER_LENGTH;
1469         if (header->ms_header.bdesc_length ==
1470             sizeof (struct block_descriptor)) {
1471                 nbytes += sizeof (struct block_descriptor);
1472         }
1473 
1474         s = find_string(mode_select_strings,
1475             options & (MODE_SELECT_SP|MODE_SELECT_PF));
1476         dprintf("\nMode select page 0x%x%s:\n", page_code,
1477             s != NULL ? s : "");
1478         ddump("header:", (caddr_t)header, nbytes);
1479         ddump("data:", (caddr_t)page_data, page_size);
1480 
1481         /*
1482          * Put the header and data together
1483          */
1484         (void) memcpy(mode_select_buf, (caddr_t)header, nbytes);
1485         (void) memcpy(mode_select_buf + nbytes, page_data, page_size);
1486         nbytes += page_size;
1487 
1488         /*
1489          * Build and execute the uscsi ioctl
1490          */
1491         (void) memset((char *)&ucmd, 0, sizeof (ucmd));
1492         (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
1493         cdb.scc_cmd = SCMD_MODE_SELECT;
1494         FORMG0COUNT(&cdb, (uchar_t)nbytes);
1495         cdb.cdb_opaque[1] = (uchar_t)options;
1496         ucmd.uscsi_cdb = (caddr_t)&cdb;
1497         ucmd.uscsi_cdblen = CDB_GROUP0;
1498         ucmd.uscsi_bufaddr = mode_select_buf;
1499         ucmd.uscsi_buflen = nbytes;
1500         status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
1501 
1502         if (status)
1503                 dprintf("Mode select page 0x%x failed\n", page_code);
1504 
1505         return (status);
1506 }
1507 
1508 /*
1509  * Execute a uscsi mode select(10) command.
1510  */
1511 int
1512 uscsi_mode_select_10(int fd, int page_code, int options,
1513     caddr_t page_data, int page_size, struct scsi_ms_header_g1 *header,
1514     void *rqbuf, int *rqblen)
1515 {
1516         caddr_t                         mode_select_buf;
1517         int                             nbytes;
1518         struct uscsi_cmd                ucmd;
1519         union scsi_cdb                  cdb;
1520         int                             status;
1521         char                            *s;
1522 
1523         assert(((struct mode_page *)page_data)->ps == 0);
1524         assert(header->ms_header.length == 0);
1525         assert(header->ms_header.device_specific == 0);
1526         assert((options & ~(MODE_SELECT_SP|MODE_SELECT_PF)) == 0);
1527 
1528         nbytes = sizeof (struct scsi_ms_header_g1) + page_size;
1529         mode_select_buf = alloca((uint_t)nbytes);
1530 
1531         /*
1532          * Build the mode select data out of the header and page data
1533          * This allows us to support devices which return either
1534          * 0 or 1 block descriptors.
1535          */
1536         (void) memset(mode_select_buf, 0, nbytes);
1537         nbytes = sizeof (struct mode_header_g1);
1538         if (BE_16(header->ms_header.bdesc_length) ==
1539             sizeof (struct block_descriptor)) {
1540                 nbytes += sizeof (struct block_descriptor);
1541         }
1542 
1543         /*
1544          * Dump the structures
1545          */
1546         s = find_string(mode_select_strings,
1547             options & (MODE_SELECT_SP|MODE_SELECT_PF));
1548         dprintf("\nMode select(10) page 0x%x%s:\n", page_code,
1549             s != NULL ? s : "");
1550         ddump("header:", (caddr_t)header, nbytes);
1551         ddump("data:", (caddr_t)page_data, page_size);
1552 
1553         /*
1554          * Put the header and data together
1555          */
1556         (void) memcpy(mode_select_buf, (caddr_t)header, nbytes);
1557         (void) memcpy(mode_select_buf + nbytes, page_data, page_size);
1558         nbytes += page_size;
1559 
1560         /*
1561          * Build and execute the uscsi ioctl
1562          */
1563         (void) memset((char *)&ucmd, 0, sizeof (ucmd));
1564         (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
1565         cdb.scc_cmd = SCMD_MODE_SELECT_G1;
1566         FORMG1COUNT(&cdb, (uint16_t)nbytes);
1567         cdb.cdb_opaque[1] = (uchar_t)options;
1568         ucmd.uscsi_cdb = (caddr_t)&cdb;
1569         ucmd.uscsi_cdblen = CDB_GROUP1;
1570         ucmd.uscsi_bufaddr = mode_select_buf;
1571         ucmd.uscsi_buflen = nbytes;
1572         status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
1573 
1574         if (status)
1575                 dprintf("Mode select(10) page 0x%x failed\n", page_code);
1576 
1577         return (status);
1578 }
1579 
1580 int
1581 uscsi_log_sense(int fd, int page_code, int page_control, caddr_t page_data,
1582     int page_size, void *rqbuf, int *rqblen)
1583 {
1584         caddr_t log_sense_buf;
1585         scsi_log_header_t *hdr;
1586         struct uscsi_cmd ucmd;
1587         union scsi_cdb cdb;
1588         int status;
1589         ushort_t len;
1590         char *pc;
1591 
1592         assert(page_size >= 0 && page_size < UINT16_MAX);
1593         assert(page_control == PC_CURRENT || page_control == PC_CHANGEABLE ||
1594             page_control == PC_DEFAULT || page_control == PC_SAVED);
1595 
1596         if (page_size < sizeof (scsi_log_header_t))
1597                 return (-1);
1598 
1599         log_sense_buf = alloca((uint_t)page_size);
1600 
1601         /*
1602          * Build and execute the uscsi ioctl
1603          */
1604         (void) memset(log_sense_buf, 0, page_size);
1605         (void) memset((char *)&ucmd, 0, sizeof (ucmd));
1606         (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
1607         cdb.scc_cmd = SCMD_LOG_SENSE_G1;
1608         FORMG1COUNT(&cdb, (uint16_t)page_size);
1609         cdb.cdb_opaque[2] = page_control | page_code;
1610         ucmd.uscsi_cdb = (caddr_t)&cdb;
1611         ucmd.uscsi_cdblen = CDB_GROUP1;
1612         ucmd.uscsi_bufaddr = log_sense_buf;
1613         ucmd.uscsi_buflen = page_size;
1614         status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
1615         if (status) {
1616                 dprintf("Log sense page 0x%x failed\n", page_code);
1617                 return (-1);
1618         }
1619 
1620         /*
1621          * Verify that the returned data looks reasonable, then copy it into the
1622          * user's buffer.
1623          */
1624         hdr = (scsi_log_header_t *)log_sense_buf;
1625 
1626         /*
1627          * Ensure we have a host-understandable length field
1628          */
1629         len = BE_16(hdr->lh_length);
1630 
1631         if (hdr->lh_code != page_code) {
1632                 dprintf("\nLog sense page 0x%x: incorrect page code 0x%x\n",
1633                     page_code, hdr->lh_code);
1634                 ddump("Log sense:", log_sense_buf, page_size);
1635                 return (-1);
1636         }
1637 
1638         ddump("LOG SENSE RAW OUTPUT", log_sense_buf,
1639             sizeof (scsi_log_header_t) + len);
1640 
1641         /*
1642          * Accept up to "page_size" bytes of mode sense data.  This allows us to
1643          * accept both CCS and SCSI-2 structures, as long as we request the
1644          * greater of the two.
1645          */
1646         (void) memcpy(page_data, (caddr_t)hdr, len +
1647             sizeof (scsi_log_header_t));
1648 
1649         pc = find_string(page_control_strings, page_control);
1650         dprintf("\nLog sense page 0x%x (%s):\n", page_code,
1651             pc != NULL ? pc : "");
1652         ddump("header:", (caddr_t)hdr,
1653             sizeof (scsi_log_header_t));
1654         ddump("data:", (caddr_t)hdr +
1655             sizeof (scsi_log_header_t), len);
1656 
1657         return (0);
1658 }