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 } 1659