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