1724365f7Ssethg /*
2724365f7Ssethg  * CDDL HEADER START
3724365f7Ssethg  *
4724365f7Ssethg  * The contents of this file are subject to the terms of the
5724365f7Ssethg  * Common Development and Distribution License (the "License").
6724365f7Ssethg  * You may not use this file except in compliance with the License.
7724365f7Ssethg  *
8724365f7Ssethg  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9724365f7Ssethg  * or http://www.opensolaris.org/os/licensing.
10724365f7Ssethg  * See the License for the specific language governing permissions
11724365f7Ssethg  * and limitations under the License.
12724365f7Ssethg  *
13724365f7Ssethg  * When distributing Covered Code, include this CDDL HEADER in each
14724365f7Ssethg  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15724365f7Ssethg  * If applicable, add the following below this CDDL HEADER, with the
16724365f7Ssethg  * fields enclosed by brackets "[]" replaced with your own identifying
17724365f7Ssethg  * information: Portions Copyright [yyyy] [name of copyright owner]
18724365f7Ssethg  *
19724365f7Ssethg  * CDDL HEADER END
20724365f7Ssethg  */
21724365f7Ssethg 
22724365f7Ssethg /*
2324db4641Seschrock  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24724365f7Ssethg  * Use is subject to license terms.
250e35cc03SRobert Mustacchi  * Copyright (c) 2019, Joyent, Inc.
26724365f7Ssethg  */
27724365f7Ssethg 
2824db4641Seschrock /*
2924db4641Seschrock  * This file contains routines for sending and receiving SCSI commands.  The
3024db4641Seschrock  * higher level logic is contained in ds_scsi.c.
3124db4641Seschrock  */
3224db4641Seschrock 
3324db4641Seschrock #include <assert.h>
34724365f7Ssethg #include <sys/types.h>
35724365f7Ssethg #include <sys/param.h>
36724365f7Ssethg #include <inttypes.h>
37724365f7Ssethg #include <stdio.h>
38724365f7Ssethg #include <stdlib.h>
39724365f7Ssethg #include <string.h>
40724365f7Ssethg #include <errno.h>
41724365f7Ssethg #include <stdarg.h>
42724365f7Ssethg #include <limits.h>
43724365f7Ssethg #include <utility.h>
44724365f7Ssethg #include <unistd.h>
45724365f7Ssethg #include <stropts.h>
46724365f7Ssethg #include <alloca.h>
47724365f7Ssethg 
4824db4641Seschrock #include "ds_scsi.h"
4924db4641Seschrock #include "ds_scsi_uscsi.h"
50724365f7Ssethg 
51724365f7Ssethg #define	MSGBUFLEN 64
52724365f7Ssethg #define	USCSI_DEFAULT_TIMEOUT	45
53724365f7Ssethg #define	USCSI_TIMEOUT_MAX	INT_MAX
54724365f7Ssethg 
55724365f7Ssethg static diskaddr_t scsi_extract_sense_info_descr(
56724365f7Ssethg     struct scsi_descr_sense_hdr *sdsp, int rqlen);
57724365f7Ssethg static void scsi_print_extended_sense(struct scsi_extended_sense *rq,
58724365f7Ssethg     int rqlen);
59724365f7Ssethg static void scsi_print_descr_sense(struct scsi_descr_sense_hdr *rq, int rqlen);
60724365f7Ssethg 
6124db4641Seschrock typedef struct slist {
6224db4641Seschrock 	char	*str;
6324db4641Seschrock 	int	value;
6424db4641Seschrock } slist_t;
6524db4641Seschrock 
6624db4641Seschrock static char *
find_string(slist_t * slist,int match_value)6724db4641Seschrock find_string(slist_t *slist, int match_value)
6824db4641Seschrock {
6924db4641Seschrock 	for (; slist->str != NULL; slist++) {
7024db4641Seschrock 		if (slist->value == match_value) {
7124db4641Seschrock 			return (slist->str);
7224db4641Seschrock 		}
7324db4641Seschrock 	}
7424db4641Seschrock 
7524db4641Seschrock 	return ((char *)NULL);
7624db4641Seschrock }
7724db4641Seschrock 
78724365f7Ssethg /*
79724365f7Ssethg  * Strings for printing mode sense page control values
80724365f7Ssethg  */
81724365f7Ssethg static slist_t page_control_strings[] = {
82724365f7Ssethg 	{ "current",	PC_CURRENT },
83724365f7Ssethg 	{ "changeable",	PC_CHANGEABLE },
84724365f7Ssethg 	{ "default",	PC_DEFAULT },
85724365f7Ssethg 	{ "saved",	PC_SAVED },
86724365f7Ssethg 	{ NULL,		0 }
87724365f7Ssethg };
88724365f7Ssethg 
89724365f7Ssethg /*
90724365f7Ssethg  * Strings for printing the mode select options
91724365f7Ssethg  */
92724365f7Ssethg static slist_t mode_select_strings[] = {
93724365f7Ssethg 	{ "",		0 },
94724365f7Ssethg 	{ "(pf)",	MODE_SELECT_PF },
95724365f7Ssethg 	{ "(sp)",	MODE_SELECT_SP },
96724365f7Ssethg 	{ "(pf,sp)",	MODE_SELECT_PF|MODE_SELECT_SP },
97724365f7Ssethg 	{ NULL,		0 }
98724365f7Ssethg };
99724365f7Ssethg 
100724365f7Ssethg static slist_t sensekey_strings[] = {
1010e35cc03SRobert Mustacchi 	{ "No sense error",	KEY_NO_SENSE		},
102724365f7Ssethg 	{ "Recoverable error",	KEY_RECOVERABLE_ERROR	},
103724365f7Ssethg 	{ "Not ready error",	KEY_NOT_READY		},
104724365f7Ssethg 	{ "Medium error",	KEY_MEDIUM_ERROR	},
105724365f7Ssethg 	{ "Hardware error",	KEY_HARDWARE_ERROR	},
106724365f7Ssethg 	{ "Illegal request",	KEY_ILLEGAL_REQUEST	},
107724365f7Ssethg 	{ "Unit attention error", KEY_UNIT_ATTENTION	},
108724365f7Ssethg 	{ "Write protect error", KEY_WRITE_PROTECT	},
109724365f7Ssethg 	{ "Blank check error",	KEY_BLANK_CHECK		},
110724365f7Ssethg 	{ "Vendor unique error", KEY_VENDOR_UNIQUE	},
111724365f7Ssethg 	{ "Copy aborted error",	KEY_COPY_ABORTED	},
112724365f7Ssethg 	{ "Aborted command",	KEY_ABORTED_COMMAND	},
113724365f7Ssethg 	{ "Equal error",	KEY_EQUAL		},
114724365f7Ssethg 	{ "Volume overflow",	KEY_VOLUME_OVERFLOW	},
115724365f7Ssethg 	{ "Miscompare error",	KEY_MISCOMPARE		},
116724365f7Ssethg 	{ "Reserved error",	KEY_RESERVED		},
117724365f7Ssethg 	{ NULL,			0			}
118724365f7Ssethg };
119724365f7Ssethg 
120724365f7Ssethg static slist_t scsi_cmdname_strings[] = {
121724365f7Ssethg 	{ "mode select",	SCMD_MODE_SELECT	},
122724365f7Ssethg 	{ "mode sense",		SCMD_MODE_SENSE		},
123724365f7Ssethg 	{ "mode select(10)",	SCMD_MODE_SELECT_G1	},
124724365f7Ssethg 	{ "mode sense(10)",	SCMD_MODE_SENSE_G1	},
125724365f7Ssethg 	{ "log sense",		SCMD_LOG_SENSE_G1	},
126724365f7Ssethg 	{ "request sense",	SCMD_REQUEST_SENSE	},
127724365f7Ssethg 	{ NULL,			0			}
128724365f7Ssethg };
129724365f7Ssethg 
130724365f7Ssethg static struct _scsi_asq_key_strings {
131724365f7Ssethg 	uint_t asc;
132724365f7Ssethg 	uint_t ascq;
133724365f7Ssethg 	const char *message;
134724365f7Ssethg } extended_sense_list[] = {
135724365f7Ssethg 	{ 0x00, 0x00, "no additional sense info" },
136724365f7Ssethg 	{ 0x00, 0x01, "filemark detected" },
137724365f7Ssethg 	{ 0x00, 0x02, "end of partition/medium detected" },
138724365f7Ssethg 	{ 0x00, 0x03, "setmark detected" },
139724365f7Ssethg 	{ 0x00, 0x04, "begining of partition/medium detected" },
140724365f7Ssethg 	{ 0x00, 0x05, "end of data detected" },
141724365f7Ssethg 	{ 0x00, 0x06, "i/o process terminated" },
142724365f7Ssethg 	{ 0x00, 0x11, "audio play operation in progress" },
143724365f7Ssethg 	{ 0x00, 0x12, "audio play operation paused" },
144724365f7Ssethg 	{ 0x00, 0x13, "audio play operation successfully completed" },
145724365f7Ssethg 	{ 0x00, 0x14, "audio play operation stopped due to error" },
146724365f7Ssethg 	{ 0x00, 0x15, "no current audio status to return" },
147724365f7Ssethg 	{ 0x00, 0x16, "operation in progress" },
148724365f7Ssethg 	{ 0x00, 0x17, "cleaning requested" },
149724365f7Ssethg 	{ 0x00, 0x18, "erase operation in progress" },
150724365f7Ssethg 	{ 0x00, 0x19, "locate operation in progress" },
151724365f7Ssethg 	{ 0x00, 0x1A, "rewind operation in progress" },
152724365f7Ssethg 	{ 0x00, 0x1B, "set capacity operation in progress" },
153724365f7Ssethg 	{ 0x00, 0x1C, "verify operation in progress" },
154724365f7Ssethg 	{ 0x01, 0x00, "no index/sector signal" },
155724365f7Ssethg 	{ 0x02, 0x00, "no seek complete" },
156724365f7Ssethg 	{ 0x03, 0x00, "peripheral device write fault" },
157724365f7Ssethg 	{ 0x03, 0x01, "no write current" },
158724365f7Ssethg 	{ 0x03, 0x02, "excessive write errors" },
159724365f7Ssethg 	{ 0x04, 0x00, "LUN not ready" },
160724365f7Ssethg 	{ 0x04, 0x01, "LUN is becoming ready" },
161724365f7Ssethg 	{ 0x04, 0x02, "LUN initializing command required" },
162724365f7Ssethg 	{ 0x04, 0x03, "LUN not ready intervention required" },
163724365f7Ssethg 	{ 0x04, 0x04, "LUN not ready format in progress" },
164724365f7Ssethg 	{ 0x04, 0x05, "LUN not ready, rebuild in progress" },
165724365f7Ssethg 	{ 0x04, 0x06, "LUN not ready, recalculation in progress" },
166724365f7Ssethg 	{ 0x04, 0x07, "LUN not ready, operation in progress" },
167724365f7Ssethg 	{ 0x04, 0x08, "LUN not ready, long write in progress" },
168724365f7Ssethg 	{ 0x04, 0x09, "LUN not ready, self-test in progress" },
169724365f7Ssethg 	{ 0x04, 0x0A, "LUN not accessible, asymmetric access state "
170724365f7Ssethg 		"transition" },
171724365f7Ssethg 	{ 0x04, 0x0B, "LUN not accessible, target port in standby state" },
172724365f7Ssethg 	{ 0x04, 0x0C, "LUN not accessible, target port in unavailable state" },
173724365f7Ssethg 	{ 0x04, 0x10, "LUN not ready, auxiliary memory not accessible" },
174724365f7Ssethg 	{ 0x05, 0x00, "LUN does not respond to selection" },
175724365f7Ssethg 	{ 0x06, 0x00, "reference position found" },
176724365f7Ssethg 	{ 0x07, 0x00, "multiple peripheral devices selected" },
177724365f7Ssethg 	{ 0x08, 0x00, "LUN communication failure" },
178724365f7Ssethg 	{ 0x08, 0x01, "LUN communication time-out" },
179724365f7Ssethg 	{ 0x08, 0x02, "LUN communication parity error" },
180724365f7Ssethg 	{ 0x08, 0x03, "LUN communication crc error (ultra-DMA/32)" },
181724365f7Ssethg 	{ 0x08, 0x04, "unreachable copy target" },
182724365f7Ssethg 	{ 0x09, 0x00, "track following error" },
183724365f7Ssethg 	{ 0x09, 0x01, "tracking servo failure" },
184724365f7Ssethg 	{ 0x09, 0x02, "focus servo failure" },
185724365f7Ssethg 	{ 0x09, 0x03, "spindle servo failure" },
186724365f7Ssethg 	{ 0x09, 0x04, "head select fault" },
187724365f7Ssethg 	{ 0x0a, 0x00, "error log overflow" },
188724365f7Ssethg 	{ 0x0b, 0x00, "warning" },
189724365f7Ssethg 	{ 0x0b, 0x01, "warning - specified temperature exceeded" },
190724365f7Ssethg 	{ 0x0b, 0x02, "warning - enclosure degraded" },
191724365f7Ssethg 	{ 0x0c, 0x00, "write error" },
192724365f7Ssethg 	{ 0x0c, 0x01, "write error - recovered with auto reallocation" },
193724365f7Ssethg 	{ 0x0c, 0x02, "write error - auto reallocation failed" },
194724365f7Ssethg 	{ 0x0c, 0x03, "write error - recommend reassignment" },
195724365f7Ssethg 	{ 0x0c, 0x04, "compression check miscompare error" },
196724365f7Ssethg 	{ 0x0c, 0x05, "data expansion occurred during compression" },
197724365f7Ssethg 	{ 0x0c, 0x06, "block not compressible" },
198724365f7Ssethg 	{ 0x0c, 0x07, "write error - recovery needed" },
199724365f7Ssethg 	{ 0x0c, 0x08, "write error - recovery failed" },
200724365f7Ssethg 	{ 0x0c, 0x09, "write error - loss of streaming" },
201724365f7Ssethg 	{ 0x0c, 0x0a, "write error - padding blocks added" },
202724365f7Ssethg 	{ 0x0c, 0x0b, "auxiliary memory write error" },
203724365f7Ssethg 	{ 0x0c, 0x0c, "write error - unexpected unsolicited data" },
204724365f7Ssethg 	{ 0x0c, 0x0d, "write error - not enough unsolicited data" },
205724365f7Ssethg 	{ 0x0d, 0x00, "error detected by third party temporary initiator" },
206724365f7Ssethg 	{ 0x0d, 0x01, "third party device failure" },
207724365f7Ssethg 	{ 0x0d, 0x02, "copy target device not reachable" },
208724365f7Ssethg 	{ 0x0d, 0x03, "incorrect copy target device type" },
209724365f7Ssethg 	{ 0x0d, 0x04, "copy target device data underrun" },
210724365f7Ssethg 	{ 0x0d, 0x05, "copy target device data overrun" },
211724365f7Ssethg 	{ 0x0e, 0x00, "invalid information unit" },
212724365f7Ssethg 	{ 0x0e, 0x01, "information unit too short" },
213724365f7Ssethg 	{ 0x0e, 0x02, "information unit too long" },
214724365f7Ssethg 	{ 0x10, 0x00, "ID CRC or ECC error" },
215724365f7Ssethg 	{ 0x11, 0x00, "unrecovered read error" },
216724365f7Ssethg 	{ 0x11, 0x01, "read retries exhausted" },
217724365f7Ssethg 	{ 0x11, 0x02, "error too long to correct" },
218724365f7Ssethg 	{ 0x11, 0x03, "multiple read errors" },
219724365f7Ssethg 	{ 0x11, 0x04, "unrecovered read error - auto reallocate failed" },
220724365f7Ssethg 	{ 0x11, 0x05, "L-EC uncorrectable error" },
221724365f7Ssethg 	{ 0x11, 0x06, "CIRC unrecovered error" },
222724365f7Ssethg 	{ 0x11, 0x07, "data re-synchronization error" },
223724365f7Ssethg 	{ 0x11, 0x08, "incomplete block read" },
224724365f7Ssethg 	{ 0x11, 0x09, "no gap found" },
225724365f7Ssethg 	{ 0x11, 0x0a, "miscorrected error" },
226724365f7Ssethg 	{ 0x11, 0x0b, "unrecovered read error - recommend reassignment" },
227724365f7Ssethg 	{ 0x11, 0x0c, "unrecovered read error - recommend rewrite the data" },
228724365f7Ssethg 	{ 0x11, 0x0d, "de-compression crc error" },
229724365f7Ssethg 	{ 0x11, 0x0e, "cannot decompress using declared algorithm" },
230724365f7Ssethg 	{ 0x11, 0x0f, "error reading UPC/EAN number" },
231724365f7Ssethg 	{ 0x11, 0x10, "error reading ISRC number" },
232724365f7Ssethg 	{ 0x11, 0x11, "read error - loss of streaming" },
233724365f7Ssethg 	{ 0x11, 0x12, "auxiliary memory read error" },
234724365f7Ssethg 	{ 0x11, 0x13, "read error - failed retransmission request" },
235724365f7Ssethg 	{ 0x12, 0x00, "address mark not found for ID field" },
236724365f7Ssethg 	{ 0x13, 0x00, "address mark not found for data field" },
237724365f7Ssethg 	{ 0x14, 0x00, "recorded entity not found" },
238724365f7Ssethg 	{ 0x14, 0x01, "record not found" },
239724365f7Ssethg 	{ 0x14, 0x02, "filemark or setmark not found" },
240724365f7Ssethg 	{ 0x14, 0x03, "end-of-data not found" },
241724365f7Ssethg 	{ 0x14, 0x04, "block sequence error" },
242724365f7Ssethg 	{ 0x14, 0x05, "record not found - recommend reassignment" },
243724365f7Ssethg 	{ 0x14, 0x06, "record not found - data auto-reallocated" },
244724365f7Ssethg 	{ 0x14, 0x07, "locate operation failure" },
245724365f7Ssethg 	{ 0x15, 0x00, "random positioning error" },
246724365f7Ssethg 	{ 0x15, 0x01, "mechanical positioning error" },
247724365f7Ssethg 	{ 0x15, 0x02, "positioning error detected by read of medium" },
248724365f7Ssethg 	{ 0x16, 0x00, "data sync mark error" },
249724365f7Ssethg 	{ 0x16, 0x01, "data sync error - data rewritten" },
250724365f7Ssethg 	{ 0x16, 0x02, "data sync error - recommend rewrite" },
251724365f7Ssethg 	{ 0x16, 0x03, "data sync error - data auto-reallocated" },
252724365f7Ssethg 	{ 0x16, 0x04, "data sync error - recommend reassignment" },
253724365f7Ssethg 	{ 0x17, 0x00, "recovered data with no error correction" },
254724365f7Ssethg 	{ 0x17, 0x01, "recovered data with retries" },
255724365f7Ssethg 	{ 0x17, 0x02, "recovered data with positive head offset" },
256724365f7Ssethg 	{ 0x17, 0x03, "recovered data with negative head offset" },
257724365f7Ssethg 	{ 0x17, 0x04, "recovered data with retries and/or CIRC applied" },
258724365f7Ssethg 	{ 0x17, 0x05, "recovered data using previous sector id" },
259724365f7Ssethg 	{ 0x17, 0x06, "recovered data without ECC - data auto-reallocated" },
260724365f7Ssethg 	{ 0x17, 0x07, "recovered data without ECC - recommend reassignment" },
261724365f7Ssethg 	{ 0x17, 0x08, "recovered data without ECC - recommend rewrite" },
262724365f7Ssethg 	{ 0x17, 0x09, "recovered data without ECC - data rewritten" },
263724365f7Ssethg 	{ 0x18, 0x00, "recovered data with error correction" },
264724365f7Ssethg 	{ 0x18, 0x01, "recovered data with error corr. & retries applied" },
265724365f7Ssethg 	{ 0x18, 0x02, "recovered data - data auto-reallocated" },
266724365f7Ssethg 	{ 0x18, 0x03, "recovered data with CIRC" },
267724365f7Ssethg 	{ 0x18, 0x04, "recovered data with L-EC" },
268724365f7Ssethg 	{ 0x18, 0x05, "recovered data - recommend reassignment" },
269724365f7Ssethg 	{ 0x18, 0x06, "recovered data - recommend rewrite" },
270724365f7Ssethg 	{ 0x18, 0x07, "recovered data with ECC - data rewritten" },
271724365f7Ssethg 	{ 0x18, 0x08, "recovered data with linking" },
272724365f7Ssethg 	{ 0x19, 0x00, "defect list error" },
273724365f7Ssethg 	{ 0x1a, 0x00, "parameter list length error" },
274724365f7Ssethg 	{ 0x1b, 0x00, "synchronous data xfer error" },
275724365f7Ssethg 	{ 0x1c, 0x00, "defect list not found" },
276724365f7Ssethg 	{ 0x1c, 0x01, "primary defect list not found" },
277724365f7Ssethg 	{ 0x1c, 0x02, "grown defect list not found" },
278724365f7Ssethg 	{ 0x1d, 0x00, "miscompare during verify" },
279724365f7Ssethg 	{ 0x1e, 0x00, "recovered ID with ECC" },
280724365f7Ssethg 	{ 0x1f, 0x00, "partial defect list transfer" },
281724365f7Ssethg 	{ 0x20, 0x00, "invalid command operation code" },
282724365f7Ssethg 	{ 0x20, 0x01, "access denied - initiator pending-enrolled" },
283724365f7Ssethg 	{ 0x20, 0x02, "access denied - no access rights" },
284724365f7Ssethg 	{ 0x20, 0x03, "access denied - invalid mgmt id key" },
285724365f7Ssethg 	{ 0x20, 0x04, "illegal command while in write capable state" },
286724365f7Ssethg 	{ 0x20, 0x06, "illegal command while in explicit address mode" },
287724365f7Ssethg 	{ 0x20, 0x07, "illegal command while in implicit address mode" },
288724365f7Ssethg 	{ 0x20, 0x08, "access denied - enrollment conflict" },
289724365f7Ssethg 	{ 0x20, 0x09, "access denied - invalid lu identifier" },
290724365f7Ssethg 	{ 0x20, 0x0a, "access denied - invalid proxy token" },
291724365f7Ssethg 	{ 0x20, 0x0b, "access denied - ACL LUN conflict" },
292724365f7Ssethg 	{ 0x21, 0x00, "logical block address out of range" },
293724365f7Ssethg 	{ 0x21, 0x01, "invalid element address" },
294724365f7Ssethg 	{ 0x21, 0x02, "invalid address for write" },
295724365f7Ssethg 	{ 0x22, 0x00, "illegal function" },
296724365f7Ssethg 	{ 0x24, 0x00, "invalid field in cdb" },
297724365f7Ssethg 	{ 0x24, 0x01, "cdb decryption error" },
298724365f7Ssethg 	{ 0x25, 0x00, "LUN not supported" },
299724365f7Ssethg 	{ 0x26, 0x00, "invalid field in param list" },
300724365f7Ssethg 	{ 0x26, 0x01, "parameter not supported" },
301724365f7Ssethg 	{ 0x26, 0x02, "parameter value invalid" },
302724365f7Ssethg 	{ 0x26, 0x03, "threshold parameters not supported" },
303724365f7Ssethg 	{ 0x26, 0x04, "invalid release of persistent reservation" },
304724365f7Ssethg 	{ 0x26, 0x05, "data decryption error" },
305724365f7Ssethg 	{ 0x26, 0x06, "too many target descriptors" },
306724365f7Ssethg 	{ 0x26, 0x07, "unsupported target descriptor type code" },
307724365f7Ssethg 	{ 0x26, 0x08, "too many segment descriptors" },
308724365f7Ssethg 	{ 0x26, 0x09, "unsupported segment descriptor type code" },
309724365f7Ssethg 	{ 0x26, 0x0a, "unexpected inexact segment" },
310724365f7Ssethg 	{ 0x26, 0x0b, "inline data length exceeded" },
311724365f7Ssethg 	{ 0x26, 0x0c, "invalid operation for copy source or destination" },
312724365f7Ssethg 	{ 0x26, 0x0d, "copy segment granularity violation" },
313724365f7Ssethg 	{ 0x27, 0x00, "write protected" },
314724365f7Ssethg 	{ 0x27, 0x01, "hardware write protected" },
315724365f7Ssethg 	{ 0x27, 0x02, "LUN software write protected" },
316724365f7Ssethg 	{ 0x27, 0x03, "associated write protect" },
317724365f7Ssethg 	{ 0x27, 0x04, "persistent write protect" },
318724365f7Ssethg 	{ 0x27, 0x05, "permanent write protect" },
319724365f7Ssethg 	{ 0x27, 0x06, "conditional write protect" },
320724365f7Ssethg 	{ 0x28, 0x00, "medium may have changed" },
321724365f7Ssethg 	{ 0x28, 0x01, "import or export element accessed" },
322724365f7Ssethg 	{ 0x29, 0x00, "power on, reset, or bus reset occurred" },
323724365f7Ssethg 	{ 0x29, 0x01, "power on occurred" },
324724365f7Ssethg 	{ 0x29, 0x02, "scsi bus reset occurred" },
325724365f7Ssethg 	{ 0x29, 0x03, "bus device reset message occurred" },
326724365f7Ssethg 	{ 0x29, 0x04, "device internal reset" },
327724365f7Ssethg 	{ 0x29, 0x05, "transceiver mode changed to single-ended" },
328724365f7Ssethg 	{ 0x29, 0x06, "transceiver mode changed to LVD" },
329724365f7Ssethg 	{ 0x29, 0x07, "i_t nexus loss occurred" },
330724365f7Ssethg 	{ 0x2a, 0x00, "parameters changed" },
331724365f7Ssethg 	{ 0x2a, 0x01, "mode parameters changed" },
332724365f7Ssethg 	{ 0x2a, 0x02, "log parameters changed" },
333724365f7Ssethg 	{ 0x2a, 0x03, "reservations preempted" },
334724365f7Ssethg 	{ 0x2a, 0x04, "reservations released" },
335724365f7Ssethg 	{ 0x2a, 0x05, "registrations preempted" },
336724365f7Ssethg 	{ 0x2a, 0x06, "asymmetric access state changed" },
337724365f7Ssethg 	{ 0x2a, 0x07, "implicit asymmetric access state transition failed" },
338724365f7Ssethg 	{ 0x2b, 0x00, "copy cannot execute since host cannot disconnect" },
339724365f7Ssethg 	{ 0x2c, 0x00, "command sequence error" },
340724365f7Ssethg 	{ 0x2c, 0x03, "current program area is not empty" },
341724365f7Ssethg 	{ 0x2c, 0x04, "current program area is empty" },
342724365f7Ssethg 	{ 0x2c, 0x06, "persistent prevent conflict" },
343724365f7Ssethg 	{ 0x2c, 0x07, "previous busy status" },
344724365f7Ssethg 	{ 0x2c, 0x08, "previous task set full status" },
345724365f7Ssethg 	{ 0x2c, 0x09, "previous reservation conflict status" },
346724365f7Ssethg 	{ 0x2d, 0x00, "overwrite error on update in place" },
347724365f7Ssethg 	{ 0x2e, 0x00, "insufficient time for operation" },
348724365f7Ssethg 	{ 0x2f, 0x00, "commands cleared by another initiator" },
349724365f7Ssethg 	{ 0x30, 0x00, "incompatible medium installed" },
350724365f7Ssethg 	{ 0x30, 0x01, "cannot read medium - unknown format" },
351724365f7Ssethg 	{ 0x30, 0x02, "cannot read medium - incompatible format" },
352724365f7Ssethg 	{ 0x30, 0x03, "cleaning cartridge installed" },
353724365f7Ssethg 	{ 0x30, 0x04, "cannot write medium - unknown format" },
354724365f7Ssethg 	{ 0x30, 0x05, "cannot write medium - incompatible format" },
355724365f7Ssethg 	{ 0x30, 0x06, "cannot format medium - incompatible medium" },
356724365f7Ssethg 	{ 0x30, 0x07, "cleaning failure" },
357724365f7Ssethg 	{ 0x30, 0x08, "cannot write - application code mismatch" },
358724365f7Ssethg 	{ 0x30, 0x09, "current session not fixated for append" },
359724365f7Ssethg 	{ 0x30, 0x10, "medium not formatted" },
360724365f7Ssethg 	{ 0x31, 0x00, "medium format corrupted" },
361724365f7Ssethg 	{ 0x31, 0x01, "format command failed" },
362724365f7Ssethg 	{ 0x31, 0x02, "zoned formatting failed due to spare linking" },
363724365f7Ssethg 	{ 0x32, 0x00, "no defect spare location available" },
364724365f7Ssethg 	{ 0x32, 0x01, "defect list update failure" },
365724365f7Ssethg 	{ 0x33, 0x00, "tape length error" },
366724365f7Ssethg 	{ 0x34, 0x00, "enclosure failure" },
367724365f7Ssethg 	{ 0x35, 0x00, "enclosure services failure" },
368724365f7Ssethg 	{ 0x35, 0x01, "unsupported enclosure function" },
369724365f7Ssethg 	{ 0x35, 0x02, "enclosure services unavailable" },
370724365f7Ssethg 	{ 0x35, 0x03, "enclosure services transfer failure" },
371724365f7Ssethg 	{ 0x35, 0x04, "enclosure services transfer refused" },
372724365f7Ssethg 	{ 0x36, 0x00, "ribbon, ink, or toner failure" },
373724365f7Ssethg 	{ 0x37, 0x00, "rounded parameter" },
374724365f7Ssethg 	{ 0x39, 0x00, "saving parameters not supported" },
375724365f7Ssethg 	{ 0x3a, 0x00, "medium not present" },
376724365f7Ssethg 	{ 0x3a, 0x01, "medium not present - tray closed" },
377724365f7Ssethg 	{ 0x3a, 0x02, "medium not present - tray open" },
378724365f7Ssethg 	{ 0x3a, 0x03, "medium not present - loadable" },
379724365f7Ssethg 	{ 0x3a, 0x04, "medium not present - medium auxiliary memory "
380724365f7Ssethg 		"accessible" },
381724365f7Ssethg 	{ 0x3b, 0x00, "sequential positioning error" },
382724365f7Ssethg 	{ 0x3b, 0x01, "tape position error at beginning-of-medium" },
383724365f7Ssethg 	{ 0x3b, 0x02, "tape position error at end-of-medium" },
384724365f7Ssethg 	{ 0x3b, 0x08, "reposition error" },
385724365f7Ssethg 	{ 0x3b, 0x0c, "position past beginning of medium" },
386724365f7Ssethg 	{ 0x3b, 0x0d, "medium destination element full" },
387724365f7Ssethg 	{ 0x3b, 0x0e, "medium source element empty" },
388724365f7Ssethg 	{ 0x3b, 0x0f, "end of medium reached" },
389724365f7Ssethg 	{ 0x3b, 0x11, "medium magazine not accessible" },
390724365f7Ssethg 	{ 0x3b, 0x12, "medium magazine removed" },
391724365f7Ssethg 	{ 0x3b, 0x13, "medium magazine inserted" },
392724365f7Ssethg 	{ 0x3b, 0x14, "medium magazine locked" },
393724365f7Ssethg 	{ 0x3b, 0x15, "medium magazine unlocked" },
394724365f7Ssethg 	{ 0x3b, 0x16, "mechanical positioning or changer error" },
395724365f7Ssethg 	{ 0x3d, 0x00, "invalid bits in indentify message" },
396724365f7Ssethg 	{ 0x3e, 0x00, "LUN has not self-configured yet" },
397724365f7Ssethg 	{ 0x3e, 0x01, "LUN failure" },
398724365f7Ssethg 	{ 0x3e, 0x02, "timeout on LUN" },
399724365f7Ssethg 	{ 0x3e, 0x03, "LUN failed self-test" },
400724365f7Ssethg 	{ 0x3e, 0x04, "LUN unable to update self-test log" },
401724365f7Ssethg 	{ 0x3f, 0x00, "target operating conditions have changed" },
402724365f7Ssethg 	{ 0x3f, 0x01, "microcode has been changed" },
403724365f7Ssethg 	{ 0x3f, 0x02, "changed operating definition" },
404724365f7Ssethg 	{ 0x3f, 0x03, "inquiry data has changed" },
405724365f7Ssethg 	{ 0x3f, 0x04, "component device attached" },
406724365f7Ssethg 	{ 0x3f, 0x05, "device identifier changed" },
407724365f7Ssethg 	{ 0x3f, 0x06, "redundancy group created or modified" },
408724365f7Ssethg 	{ 0x3f, 0x07, "redundancy group deleted" },
409724365f7Ssethg 	{ 0x3f, 0x08, "spare created or modified" },
410724365f7Ssethg 	{ 0x3f, 0x09, "spare deleted" },
411724365f7Ssethg 	{ 0x3f, 0x0a, "volume set created or modified" },
412724365f7Ssethg 	{ 0x3f, 0x0b, "volume set deleted" },
413724365f7Ssethg 	{ 0x3f, 0x0c, "volume set deassigned" },
414724365f7Ssethg 	{ 0x3f, 0x0d, "volume set reassigned" },
415724365f7Ssethg 	{ 0x3f, 0x0e, "reported LUNs data has changed" },
416724365f7Ssethg 	{ 0x3f, 0x0f, "echo buffer overwritten" },
417724365f7Ssethg 	{ 0x3f, 0x10, "medium loadable" },
418724365f7Ssethg 	{ 0x3f, 0x11, "medium auxiliary memory accessible" },
419724365f7Ssethg 	{ 0x40, 0x00, "ram failure" },
420724365f7Ssethg 	{ 0x41, 0x00, "data path failure" },
421724365f7Ssethg 	{ 0x42, 0x00, "power-on or self-test failure" },
422724365f7Ssethg 	{ 0x43, 0x00, "message error" },
423724365f7Ssethg 	{ 0x44, 0x00, "internal target failure" },
424724365f7Ssethg 	{ 0x45, 0x00, "select or reselect failure" },
425724365f7Ssethg 	{ 0x46, 0x00, "unsuccessful soft reset" },
426724365f7Ssethg 	{ 0x47, 0x00, "scsi parity error" },
427724365f7Ssethg 	{ 0x47, 0x01, "data phase crc error detected" },
428724365f7Ssethg 	{ 0x47, 0x02, "scsi parity error detected during st data phase" },
429724365f7Ssethg 	{ 0x47, 0x03, "information unit iucrc error detected" },
430724365f7Ssethg 	{ 0x47, 0x04, "asynchronous information protection error detected" },
431724365f7Ssethg 	{ 0x47, 0x05, "protocol service crc error" },
432724365f7Ssethg 	{ 0x47, 0x7f, "some commands cleared by iscsi protocol event" },
433724365f7Ssethg 	{ 0x48, 0x00, "initiator detected error message received" },
434724365f7Ssethg 	{ 0x49, 0x00, "invalid message error" },
435724365f7Ssethg 	{ 0x4a, 0x00, "command phase error" },
436724365f7Ssethg 	{ 0x4b, 0x00, "data phase error" },
437724365f7Ssethg 	{ 0x4b, 0x01, "invalid target port transfer tag received" },
438724365f7Ssethg 	{ 0x4b, 0x02, "too much write data" },
439724365f7Ssethg 	{ 0x4b, 0x03, "ack/nak timeout" },
440724365f7Ssethg 	{ 0x4b, 0x04, "nak received" },
441724365f7Ssethg 	{ 0x4b, 0x05, "data offset error" },
442724365f7Ssethg 	{ 0x4c, 0x00, "logical unit failed self-configuration" },
443724365f7Ssethg 	{ 0x4d, 0x00, "tagged overlapped commands (ASCQ = queue tag)" },
444724365f7Ssethg 	{ 0x4e, 0x00, "overlapped commands attempted" },
445724365f7Ssethg 	{ 0x50, 0x00, "write append error" },
446724365f7Ssethg 	{ 0x51, 0x00, "erase failure" },
447724365f7Ssethg 	{ 0x52, 0x00, "cartridge fault" },
448724365f7Ssethg 	{ 0x53, 0x00, "media load or eject failed" },
449724365f7Ssethg 	{ 0x53, 0x01, "unload tape failure" },
450724365f7Ssethg 	{ 0x53, 0x02, "medium removal prevented" },
451724365f7Ssethg 	{ 0x54, 0x00, "scsi to host system interface failure" },
452724365f7Ssethg 	{ 0x55, 0x00, "system resource failure" },
453724365f7Ssethg 	{ 0x55, 0x01, "system buffer full" },
454724365f7Ssethg 	{ 0x55, 0x02, "insufficient reservation resources" },
455724365f7Ssethg 	{ 0x55, 0x03, "insufficient resources" },
456724365f7Ssethg 	{ 0x55, 0x04, "insufficient registration resources" },
457724365f7Ssethg 	{ 0x55, 0x05, "insufficient access control resources" },
458724365f7Ssethg 	{ 0x55, 0x06, "auxiliary memory out of space" },
459724365f7Ssethg 	{ 0x57, 0x00, "unable to recover TOC" },
460724365f7Ssethg 	{ 0x58, 0x00, "generation does not exist" },
461724365f7Ssethg 	{ 0x59, 0x00, "updated block read" },
462724365f7Ssethg 	{ 0x5a, 0x00, "operator request or state change input" },
463724365f7Ssethg 	{ 0x5a, 0x01, "operator medium removal request" },
464724365f7Ssethg 	{ 0x5a, 0x02, "operator selected write protect" },
465724365f7Ssethg 	{ 0x5a, 0x03, "operator selected write permit" },
466724365f7Ssethg 	{ 0x5b, 0x00, "log exception" },
467724365f7Ssethg 	{ 0x5b, 0x01, "threshold condition met" },
468724365f7Ssethg 	{ 0x5b, 0x02, "log counter at maximum" },
469724365f7Ssethg 	{ 0x5b, 0x03, "log list codes exhausted" },
470724365f7Ssethg 	{ 0x5c, 0x00, "RPL status change" },
471724365f7Ssethg 	{ 0x5c, 0x01, "spindles synchronized" },
472724365f7Ssethg 	{ 0x5c, 0x02, "spindles not synchronized" },
473724365f7Ssethg 	{ 0x5d, 0x00, "drive operation marginal, service immediately"
474724365f7Ssethg 		    " (failure prediction threshold exceeded)" },
475724365f7Ssethg 	{ 0x5d, 0x01, "media failure prediction threshold exceeded" },
476724365f7Ssethg 	{ 0x5d, 0x02, "LUN failure prediction threshold exceeded" },
477724365f7Ssethg 	{ 0x5d, 0x03, "spare area exhaustion prediction threshold exceeded" },
478724365f7Ssethg 	{ 0x5d, 0x10, "hardware impending failure general hard drive failure" },
479724365f7Ssethg 	{ 0x5d, 0x11, "hardware impending failure drive error rate too high" },
480724365f7Ssethg 	{ 0x5d, 0x12, "hardware impending failure data error rate too high" },
481724365f7Ssethg 	{ 0x5d, 0x13, "hardware impending failure seek error rate too high" },
482724365f7Ssethg 	{ 0x5d, 0x14, "hardware impending failure too many block reassigns" },
483724365f7Ssethg 	{ 0x5d, 0x15, "hardware impending failure access times too high" },
484724365f7Ssethg 	{ 0x5d, 0x16, "hardware impending failure start unit times too high" },
485724365f7Ssethg 	{ 0x5d, 0x17, "hardware impending failure channel parametrics" },
486724365f7Ssethg 	{ 0x5d, 0x18, "hardware impending failure controller detected" },
487724365f7Ssethg 	{ 0x5d, 0x19, "hardware impending failure throughput performance" },
488724365f7Ssethg 	{ 0x5d, 0x1a, "hardware impending failure seek time performance" },
489724365f7Ssethg 	{ 0x5d, 0x1b, "hardware impending failure spin-up retry count" },
490724365f7Ssethg 	{ 0x5d, 0x1c, "hardware impending failure drive calibration retry "
491724365f7Ssethg 		"count" },
492724365f7Ssethg 	{ 0x5d, 0x20, "controller impending failure general hard drive "
493724365f7Ssethg 		"failure" },
494724365f7Ssethg 	{ 0x5d, 0x21, "controller impending failure drive error rate too "
495724365f7Ssethg 		"high" },
496724365f7Ssethg 	{ 0x5d, 0x22, "controller impending failure data error rate too high" },
497724365f7Ssethg 	{ 0x5d, 0x23, "controller impending failure seek error rate too high" },
498724365f7Ssethg 	{ 0x5d, 0x24, "controller impending failure too many block reassigns" },
499724365f7Ssethg 	{ 0x5d, 0x25, "controller impending failure access times too high" },
500724365f7Ssethg 	{ 0x5d, 0x26, "controller impending failure start unit times too "
501724365f7Ssethg 		"high" },
502724365f7Ssethg 	{ 0x5d, 0x27, "controller impending failure channel parametrics" },
503724365f7Ssethg 	{ 0x5d, 0x28, "controller impending failure controller detected" },
504724365f7Ssethg 	{ 0x5d, 0x29, "controller impending failure throughput performance" },
505724365f7Ssethg 	{ 0x5d, 0x2a, "controller impending failure seek time performance" },
506724365f7Ssethg 	{ 0x5d, 0x2b, "controller impending failure spin-up retry count" },
507724365f7Ssethg 	{ 0x5d, 0x2c, "controller impending failure drive calibration retry "
508724365f7Ssethg 		"cnt" },
509724365f7Ssethg 	{ 0x5d, 0x30, "data channel impending failure general hard drive "
510724365f7Ssethg 		"failure" },
511724365f7Ssethg 	{ 0x5d, 0x31, "data channel impending failure drive error rate too "
512724365f7Ssethg 		"high" },
513724365f7Ssethg 	{ 0x5d, 0x32, "data channel impending failure data error rate too "
514724365f7Ssethg 		"high" },
515724365f7Ssethg 	{ 0x5d, 0x33, "data channel impending failure seek error rate too "
516724365f7Ssethg 		"high" },
517724365f7Ssethg 	{ 0x5d, 0x34, "data channel impending failure too many block "
518724365f7Ssethg 		"reassigns" },
519724365f7Ssethg 	{ 0x5d, 0x35, "data channel impending failure access times too high" },
520724365f7Ssethg 	{ 0x5d, 0x36, "data channel impending failure start unit times too "
521724365f7Ssethg 		"high" },
522724365f7Ssethg 	{ 0x5d, 0x37, "data channel impending failure channel parametrics" },
523724365f7Ssethg 	{ 0x5d, 0x38, "data channel impending failure controller detected" },
524724365f7Ssethg 	{ 0x5d, 0x39, "data channel impending failure throughput performance" },
525724365f7Ssethg 	{ 0x5d, 0x3a, "data channel impending failure seek time performance" },
526724365f7Ssethg 	{ 0x5d, 0x3b, "data channel impending failure spin-up retry count" },
527724365f7Ssethg 	{ 0x5d, 0x3c, "data channel impending failure drive calibrate retry "
528724365f7Ssethg 		"cnt" },
529724365f7Ssethg 	{ 0x5d, 0x40, "servo impending failure general hard drive failure" },
530724365f7Ssethg 	{ 0x5d, 0x41, "servo impending failure drive error rate too high" },
531724365f7Ssethg 	{ 0x5d, 0x42, "servo impending failure data error rate too high" },
532724365f7Ssethg 	{ 0x5d, 0x43, "servo impending failure seek error rate too high" },
533724365f7Ssethg 	{ 0x5d, 0x44, "servo impending failure too many block reassigns" },
534724365f7Ssethg 	{ 0x5d, 0x45, "servo impending failure access times too high" },
535724365f7Ssethg 	{ 0x5d, 0x46, "servo impending failure start unit times too high" },
536724365f7Ssethg 	{ 0x5d, 0x47, "servo impending failure channel parametrics" },
537724365f7Ssethg 	{ 0x5d, 0x48, "servo impending failure controller detected" },
538724365f7Ssethg 	{ 0x5d, 0x49, "servo impending failure throughput performance" },
539724365f7Ssethg 	{ 0x5d, 0x4a, "servo impending failure seek time performance" },
540724365f7Ssethg 	{ 0x5d, 0x4b, "servo impending failure spin-up retry count" },
541724365f7Ssethg 	{ 0x5d, 0x4c, "servo impending failure drive calibration retry count" },
542724365f7Ssethg 	{ 0x5d, 0x50, "spindle impending failure general hard drive failure" },
543724365f7Ssethg 	{ 0x5d, 0x51, "spindle impending failure drive error rate too high" },
544724365f7Ssethg 	{ 0x5d, 0x52, "spindle impending failure data error rate too high" },
545724365f7Ssethg 	{ 0x5d, 0x53, "spindle impending failure seek error rate too high" },
546724365f7Ssethg 	{ 0x5d, 0x54, "spindle impending failure too many block reassigns" },
547724365f7Ssethg 	{ 0x5d, 0x55, "spindle impending failure access times too high" },
548724365f7Ssethg 	{ 0x5d, 0x56, "spindle impending failure start unit times too high" },
549724365f7Ssethg 	{ 0x5d, 0x57, "spindle impending failure channel parametrics" },
550724365f7Ssethg 	{ 0x5d, 0x58, "spindle impending failure controller detected" },
551724365f7Ssethg 	{ 0x5d, 0x59, "spindle impending failure throughput performance" },
552724365f7Ssethg 	{ 0x5d, 0x5a, "spindle impending failure seek time performance" },
553724365f7Ssethg 	{ 0x5d, 0x5b, "spindle impending failure spin-up retry count" },
554724365f7Ssethg 	{ 0x5d, 0x5c, "spindle impending failure drive calibration retry "
555724365f7Ssethg 		"count" },
556724365f7Ssethg 	{ 0x5d, 0x60, "firmware impending failure general hard drive failure" },
557724365f7Ssethg 	{ 0x5d, 0x61, "firmware impending failure drive error rate too high" },
558724365f7Ssethg 	{ 0x5d, 0x62, "firmware impending failure data error rate too high" },
559724365f7Ssethg 	{ 0x5d, 0x63, "firmware impending failure seek error rate too high" },
560724365f7Ssethg 	{ 0x5d, 0x64, "firmware impending failure too many block reassigns" },
561724365f7Ssethg 	{ 0x5d, 0x65, "firmware impending failure access times too high" },
562724365f7Ssethg 	{ 0x5d, 0x66, "firmware impending failure start unit times too high" },
563724365f7Ssethg 	{ 0x5d, 0x67, "firmware impending failure channel parametrics" },
564724365f7Ssethg 	{ 0x5d, 0x68, "firmware impending failure controller detected" },
565724365f7Ssethg 	{ 0x5d, 0x69, "firmware impending failure throughput performance" },
566724365f7Ssethg 	{ 0x5d, 0x6a, "firmware impending failure seek time performance" },
567724365f7Ssethg 	{ 0x5d, 0x6b, "firmware impending failure spin-up retry count" },
568724365f7Ssethg 	{ 0x5d, 0x6c, "firmware impending failure drive calibration retry "
569724365f7Ssethg 		"count" },
570724365f7Ssethg 	{ 0x5d, 0xff, "failure prediction threshold exceeded (false)" },
571724365f7Ssethg 	{ 0x5e, 0x00, "low power condition active" },
572724365f7Ssethg 	{ 0x5e, 0x01, "idle condition activated by timer" },
573724365f7Ssethg 	{ 0x5e, 0x02, "standby condition activated by timer" },
574724365f7Ssethg 	{ 0x5e, 0x03, "idle condition activated by command" },
575724365f7Ssethg 	{ 0x5e, 0x04, "standby condition activated by command" },
576724365f7Ssethg 	{ 0x60, 0x00, "lamp failure" },
577724365f7Ssethg 	{ 0x61, 0x00, "video aquisition error" },
578724365f7Ssethg 	{ 0x62, 0x00, "scan head positioning error" },
579724365f7Ssethg 	{ 0x63, 0x00, "end of user area encountered on this track" },
580724365f7Ssethg 	{ 0x63, 0x01, "packet does not fit in available space" },
581724365f7Ssethg 	{ 0x64, 0x00, "illegal mode for this track" },
582724365f7Ssethg 	{ 0x64, 0x01, "invalid packet size" },
583724365f7Ssethg 	{ 0x65, 0x00, "voltage fault" },
584724365f7Ssethg 	{ 0x66, 0x00, "automatic document feeder cover up" },
585724365f7Ssethg 	{ 0x67, 0x00, "configuration failure" },
586724365f7Ssethg 	{ 0x67, 0x01, "configuration of incapable LUNs failed" },
587724365f7Ssethg 	{ 0x67, 0x02, "add LUN failed" },
588724365f7Ssethg 	{ 0x67, 0x03, "modification of LUN failed" },
589724365f7Ssethg 	{ 0x67, 0x04, "exchange of LUN failed" },
590724365f7Ssethg 	{ 0x67, 0x05, "remove of LUN failed" },
591724365f7Ssethg 	{ 0x67, 0x06, "attachment of LUN failed" },
592724365f7Ssethg 	{ 0x67, 0x07, "creation of LUN failed" },
593724365f7Ssethg 	{ 0x67, 0x08, "assign failure occurred" },
594724365f7Ssethg 	{ 0x67, 0x09, "multiply assigned LUN" },
595724365f7Ssethg 	{ 0x67, 0x0a, "set target port groups command failed" },
596724365f7Ssethg 	{ 0x68, 0x00, "logical unit not configured" },
597724365f7Ssethg 	{ 0x69, 0x00, "data loss on logical unit" },
598724365f7Ssethg 	{ 0x69, 0x01, "multiple LUN failures" },
599724365f7Ssethg 	{ 0x69, 0x02, "parity/data mismatch" },
600724365f7Ssethg 	{ 0x6a, 0x00, "informational, refer to log" },
601724365f7Ssethg 	{ 0x6b, 0x00, "state change has occured" },
602724365f7Ssethg 	{ 0x6b, 0x01, "redundancy level got better" },
603724365f7Ssethg 	{ 0x6b, 0x02, "redundancy level got worse" },
604724365f7Ssethg 	{ 0x6c, 0x00, "rebuild failure occured" },
605724365f7Ssethg 	{ 0x6d, 0x00, "recalculate failure occured" },
606724365f7Ssethg 	{ 0x6e, 0x00, "command to logical unit failed" },
607724365f7Ssethg 	{ 0x6f, 0x00, "copy protect key exchange failure authentication "
608724365f7Ssethg 		"failure" },
609724365f7Ssethg 	{ 0x6f, 0x01, "copy protect key exchange failure key not present" },
610724365f7Ssethg 	{ 0x6f, 0x02, "copy protect key exchange failure key not established" },
611724365f7Ssethg 	{ 0x6f, 0x03, "read of scrambled sector without authentication" },
612724365f7Ssethg 	{ 0x6f, 0x04, "media region code is mismatched to LUN region" },
613724365f7Ssethg 	{ 0x6f, 0x05, "drive region must be permanent/region reset count "
614724365f7Ssethg 		"error" },
615724365f7Ssethg 	{ 0x70, 0xffff, "decompression exception short algorithm id of ASCQ" },
616724365f7Ssethg 	{ 0x71, 0x00, "decompression exception long algorithm id" },
617724365f7Ssethg 	{ 0x72, 0x00, "session fixation error" },
618724365f7Ssethg 	{ 0x72, 0x01, "session fixation error writing lead-in" },
619724365f7Ssethg 	{ 0x72, 0x02, "session fixation error writing lead-out" },
620724365f7Ssethg 	{ 0x72, 0x03, "session fixation error - incomplete track in session" },
621724365f7Ssethg 	{ 0x72, 0x04, "empty or partially written reserved track" },
622724365f7Ssethg 	{ 0x72, 0x05, "no more track reservations allowed" },
623724365f7Ssethg 	{ 0x73, 0x00, "cd control error" },
624724365f7Ssethg 	{ 0x73, 0x01, "power calibration area almost full" },
625724365f7Ssethg 	{ 0x73, 0x02, "power calibration area is full" },
626724365f7Ssethg 	{ 0x73, 0x03, "power calibration area error" },
627724365f7Ssethg 	{ 0x73, 0x04, "program memory area update failure" },
628724365f7Ssethg 	{ 0x73, 0x05, "program memory area is full" },
629724365f7Ssethg 	{ 0x73, 0x06, "rma/pma is almost full" },
630724365f7Ssethg 	{ 0xffff, 0xffff, NULL }
631724365f7Ssethg };
632724365f7Ssethg 
63324db4641Seschrock /*
63424db4641Seschrock  * Given an asc (Additional Sense Code) and ascq (Additional Sense Code
63524db4641Seschrock  * Qualifier), return a string describing the error information.
63624db4641Seschrock  */
637724365f7Ssethg static char *
scsi_util_asc_ascq_name(uint_t asc,uint_t ascq,char * buf,int buflen)638724365f7Ssethg scsi_util_asc_ascq_name(uint_t asc, uint_t ascq, char *buf, int buflen)
639724365f7Ssethg {
640724365f7Ssethg 	int i = 0;
641724365f7Ssethg 
642724365f7Ssethg 	while (extended_sense_list[i].asc != 0xffff) {
643724365f7Ssethg 		if ((asc == extended_sense_list[i].asc) &&
644724365f7Ssethg 		    ((ascq == extended_sense_list[i].ascq) ||
645724365f7Ssethg 		    (extended_sense_list[i].ascq == 0xffff))) {
646724365f7Ssethg 			return ((char *)extended_sense_list[i].message);
647724365f7Ssethg 		}
648724365f7Ssethg 		i++;
649724365f7Ssethg 	}
650724365f7Ssethg 	(void) snprintf(buf, buflen, "<vendor unique code 0x%x>", asc);
651724365f7Ssethg 	return (buf);
652724365f7Ssethg }
653724365f7Ssethg 
65424db4641Seschrock /*
65524db4641Seschrock  * Dumps detailed information about a particular SCSI error condition.
65624db4641Seschrock  */
657724365f7Ssethg static void
scsi_printerr(struct uscsi_cmd * ucmd,struct scsi_extended_sense * rq,int rqlen)65824db4641Seschrock scsi_printerr(struct uscsi_cmd *ucmd, struct scsi_extended_sense *rq, int rqlen)
659724365f7Ssethg {
660724365f7Ssethg 	diskaddr_t	blkno;
66124db4641Seschrock 	struct scsi_descr_sense_hdr *sdsp = (struct scsi_descr_sense_hdr *)rq;
662724365f7Ssethg 	char msgbuf[MSGBUFLEN];
663724365f7Ssethg 
66424db4641Seschrock 	if (find_string(sensekey_strings, rq->es_key) == NULL)
66524db4641Seschrock 		dprintf("unknown error");
666724365f7Ssethg 
66724db4641Seschrock 	dprintf("during %s:",
668724365f7Ssethg 	    find_string(scsi_cmdname_strings, ucmd->uscsi_cdb[0]));
669724365f7Ssethg 
670724365f7Ssethg 	/*
671724365f7Ssethg 	 * Get asc, ascq and info field from sense data.  There are two
672724365f7Ssethg 	 * possible formats (fixed sense data and descriptor sense data)
673724365f7Ssethg 	 * depending on the value of es_code.
674724365f7Ssethg 	 */
675724365f7Ssethg 	switch (rq->es_code) {
676724365f7Ssethg 	case CODE_FMT_DESCR_CURRENT:
677724365f7Ssethg 	case CODE_FMT_DESCR_DEFERRED:
67824db4641Seschrock 		blkno = (diskaddr_t)scsi_extract_sense_info_descr(sdsp, rqlen);
679724365f7Ssethg 		if (blkno != (diskaddr_t)-1)
68024db4641Seschrock 			dprintf(": block %lld (0x%llx)", blkno, blkno);
68124db4641Seschrock 		dprintf("\n");
68224db4641Seschrock 		dprintf("ASC: 0x%x   ASCQ: 0x%x    (%s)\n",
68324db4641Seschrock 		    sdsp->ds_add_code, sdsp->ds_qual_code,
684724365f7Ssethg 		    scsi_util_asc_ascq_name(sdsp->ds_add_code,
68524db4641Seschrock 		    sdsp->ds_qual_code, msgbuf, MSGBUFLEN));
686724365f7Ssethg 
687724365f7Ssethg 		break;
68824db4641Seschrock 
689724365f7Ssethg 	case CODE_FMT_FIXED_CURRENT:
690724365f7Ssethg 	case CODE_FMT_FIXED_DEFERRED:
691724365f7Ssethg 	default:
692724365f7Ssethg 		if (rq->es_valid) {
693724365f7Ssethg 			blkno = (rq->es_info_1 << 24) |
694724365f7Ssethg 			    (rq->es_info_2 << 16) |
695724365f7Ssethg 			    (rq->es_info_3 << 8) | rq->es_info_4;
69624db4641Seschrock 			dprintf(": block %lld (0x%llx)", blkno, blkno);
697724365f7Ssethg 		}
69824db4641Seschrock 		dprintf("\n");
699724365f7Ssethg 		if (rq->es_add_len >= 6) {
70024db4641Seschrock 			dprintf("ASC: 0x%x   ASCQ: 0x%x    (%s)\n",
701724365f7Ssethg 			    rq->es_add_code,
702724365f7Ssethg 			    rq->es_qual_code,
703724365f7Ssethg 			    scsi_util_asc_ascq_name(rq->es_add_code,
70424db4641Seschrock 			    rq->es_qual_code, msgbuf, MSGBUFLEN));
705724365f7Ssethg 		}
706724365f7Ssethg 		break;
707724365f7Ssethg 	}
708724365f7Ssethg 
709724365f7Ssethg 	if (rq->es_key == KEY_ILLEGAL_REQUEST) {
71024db4641Seschrock 		ddump("cmd:", (caddr_t)ucmd,
711724365f7Ssethg 		    sizeof (struct uscsi_cmd));
71224db4641Seschrock 		ddump("cdb:", (caddr_t)ucmd->uscsi_cdb,
713724365f7Ssethg 		    ucmd->uscsi_cdblen);
714724365f7Ssethg 	}
71524db4641Seschrock 	ddump("sense:", (caddr_t)rq, rqlen);
716724365f7Ssethg 
717724365f7Ssethg 	switch (rq->es_code) {
718724365f7Ssethg 	case CODE_FMT_DESCR_CURRENT:
719724365f7Ssethg 	case CODE_FMT_DESCR_DEFERRED:
720724365f7Ssethg 		scsi_print_descr_sense(sdsp, rqlen);
721724365f7Ssethg 		break;
722724365f7Ssethg 	case CODE_FMT_FIXED_CURRENT:
723724365f7Ssethg 	case CODE_FMT_FIXED_DEFERRED:
724724365f7Ssethg 	default:
725724365f7Ssethg 		scsi_print_extended_sense(rq, rqlen);
726724365f7Ssethg 		break;
727724365f7Ssethg 	}
728724365f7Ssethg }
729724365f7Ssethg 
730724365f7Ssethg /*
73124db4641Seschrock  * Retrieve "information" field from descriptor format sense data.  Iterates
73224db4641Seschrock  * through each sense descriptor looking for the information descriptor and
73324db4641Seschrock  * returns the information field from that descriptor.
734724365f7Ssethg  */
735724365f7Ssethg static diskaddr_t
scsi_extract_sense_info_descr(struct scsi_descr_sense_hdr * sdsp,int rqlen)736724365f7Ssethg scsi_extract_sense_info_descr(struct scsi_descr_sense_hdr *sdsp, int rqlen)
737724365f7Ssethg {
738724365f7Ssethg 	diskaddr_t result;
739724365f7Ssethg 	uint8_t *descr_offset;
740724365f7Ssethg 	int valid_sense_length;
741724365f7Ssethg 	struct scsi_information_sense_descr *isd;
742724365f7Ssethg 
743724365f7Ssethg 	/*
744724365f7Ssethg 	 * Initialize result to -1 indicating there is no information
745724365f7Ssethg 	 * descriptor
746724365f7Ssethg 	 */
747724365f7Ssethg 	result = (diskaddr_t)-1;
748724365f7Ssethg 
749724365f7Ssethg 	/*
750724365f7Ssethg 	 * The first descriptor will immediately follow the header
751724365f7Ssethg 	 */
75224db4641Seschrock 	descr_offset = (uint8_t *)(sdsp+1);
753724365f7Ssethg 
754724365f7Ssethg 	/*
755724365f7Ssethg 	 * Calculate the amount of valid sense data
756724365f7Ssethg 	 */
757724365f7Ssethg 	valid_sense_length =
758724365f7Ssethg 	    MIN((sizeof (struct scsi_descr_sense_hdr) +
75924db4641Seschrock 	    sdsp->ds_addl_sense_length), rqlen);
760724365f7Ssethg 
761724365f7Ssethg 	/*
76224db4641Seschrock 	 * Iterate through the list of descriptors, stopping when we run out of
76324db4641Seschrock 	 * sense data
764724365f7Ssethg 	 */
765724365f7Ssethg 	while ((descr_offset + sizeof (struct scsi_information_sense_descr)) <=
766724365f7Ssethg 	    (uint8_t *)sdsp + valid_sense_length) {
767724365f7Ssethg 		/*
76824db4641Seschrock 		 * Check if this is an information descriptor.  We can use the
76924db4641Seschrock 		 * scsi_information_sense_descr structure as a template since
77024db4641Seschrock 		 * the first two fields are always the same
771724365f7Ssethg 		 */
772724365f7Ssethg 		isd = (struct scsi_information_sense_descr *)descr_offset;
773724365f7Ssethg 		if (isd->isd_descr_type == DESCR_INFORMATION) {
774724365f7Ssethg 			/*
775724365f7Ssethg 			 * Found an information descriptor.  Copy the
776724365f7Ssethg 			 * information field.  There will only be one
777724365f7Ssethg 			 * information descriptor so we can stop looking.
778724365f7Ssethg 			 */
779724365f7Ssethg 			result =
780724365f7Ssethg 			    (((diskaddr_t)isd->isd_information[0] << 56) |
78124db4641Seschrock 			    ((diskaddr_t)isd->isd_information[1] << 48) |
78224db4641Seschrock 			    ((diskaddr_t)isd->isd_information[2] << 40) |
78324db4641Seschrock 			    ((diskaddr_t)isd->isd_information[3] << 32) |
78424db4641Seschrock 			    ((diskaddr_t)isd->isd_information[4] << 24) |
78524db4641Seschrock 			    ((diskaddr_t)isd->isd_information[5] << 16) |
78624db4641Seschrock 			    ((diskaddr_t)isd->isd_information[6] << 8)  |
78724db4641Seschrock 			    ((diskaddr_t)isd->isd_information[7]));
788724365f7Ssethg 			break;
789724365f7Ssethg 		}
790724365f7Ssethg 
791724365f7Ssethg 		/*
79224db4641Seschrock 		 * Get pointer to the next descriptor.  The "additional length"
79324db4641Seschrock 		 * field holds the length of the descriptor except for the
79424db4641Seschrock 		 * "type" and "additional length" fields, so we need to add 2 to
79524db4641Seschrock 		 * get the total length.
796724365f7Ssethg 		 */
797724365f7Ssethg 		descr_offset += (isd->isd_addl_length + 2);
798724365f7Ssethg 	}
799724365f7Ssethg 
800724365f7Ssethg 	return (result);
801724365f7Ssethg }
802724365f7Ssethg 
803724365f7Ssethg /*
804724365f7Ssethg  * Display the full scsi_extended_sense as returned by the device
805724365f7Ssethg  */
806724365f7Ssethg static void
scsi_print_extended_sense(struct scsi_extended_sense * rq,int rqlen)807724365f7Ssethg scsi_print_extended_sense(struct scsi_extended_sense *rq, int rqlen)
808724365f7Ssethg {
809724365f7Ssethg 	static char *scsi_extended_sense_labels[] = {
81024db4641Seschrock 	    "Request sense valid:             ",
81124db4641Seschrock 	    "Error class and code:            ",
81224db4641Seschrock 	    "Segment number:                  ",
81324db4641Seschrock 	    "Filemark:                        ",
81424db4641Seschrock 	    "End-of-medium:                   ",
81524db4641Seschrock 	    "Incorrect length indicator:      ",
81624db4641Seschrock 	    "Sense key:                       ",
81724db4641Seschrock 	    "Information field:               ",
81824db4641Seschrock 	    "Additional sense length:         ",
81924db4641Seschrock 	    "Command-specific information:    ",
82024db4641Seschrock 	    "Additional sense code:           ",
82124db4641Seschrock 	    "Additional sense code qualifier: ",
82224db4641Seschrock 	    "Field replaceable unit code:     ",
82324db4641Seschrock 	    "Sense-key specific:              ",
82424db4641Seschrock 	    "Additional sense bytes:          "
825724365f7Ssethg 	};
826724365f7Ssethg 
827724365f7Ssethg 	char **p = scsi_extended_sense_labels;
828724365f7Ssethg 
829724365f7Ssethg 	if (rqlen < (sizeof (*rq) - 2) || !rq->es_valid) {
830724365f7Ssethg 		/*
831724365f7Ssethg 		 * target should be capable of returning at least 18
832724365f7Ssethg 		 * bytes of data, i.e upto rq->es_skey_specific field.
833724365f7Ssethg 		 * The additional sense bytes (2 or more ...) are optional.
834724365f7Ssethg 		 */
835724365f7Ssethg 		return;
836724365f7Ssethg 	}
837724365f7Ssethg 
83824db4641Seschrock 	dprintf("\n%s%s\n", *p++, rq->es_valid ? "yes" : "no");
83924db4641Seschrock 	dprintf("%s0x%02x\n", *p++, (rq->es_class << 4) + rq->es_code);
84024db4641Seschrock 	dprintf("%s%d\n", *p++, rq->es_segnum);
84124db4641Seschrock 	dprintf("%s%s\n", *p++, rq->es_filmk ? "yes" : "no");
84224db4641Seschrock 	dprintf("%s%s\n", *p++, rq->es_eom ? "yes" : "no");
84324db4641Seschrock 	dprintf("%s%s\n", *p++, rq->es_ili ? "yes" : "no");
84424db4641Seschrock 	dprintf("%s%d\n", *p++, rq->es_key);
84524db4641Seschrock 
84624db4641Seschrock 	dprintf("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++, rq->es_info_1,
84724db4641Seschrock 	    rq->es_info_2, rq->es_info_3, rq->es_info_4);
84824db4641Seschrock 	dprintf("%s%d\n", *p++, rq->es_add_len);
84924db4641Seschrock 	dprintf("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++,
85024db4641Seschrock 	    rq->es_cmd_info[0], rq->es_cmd_info[1], rq->es_cmd_info[2],
85124db4641Seschrock 	    rq->es_cmd_info[3]);
85224db4641Seschrock 	dprintf("%s0x%02x = %d\n", *p++, rq->es_add_code,
85324db4641Seschrock 	    rq->es_add_code);
85424db4641Seschrock 	dprintf("%s0x%02x = %d\n", *p++, rq->es_qual_code,
85524db4641Seschrock 	    rq->es_qual_code);
85624db4641Seschrock 	dprintf("%s%d\n", *p++, rq->es_fru_code);
85724db4641Seschrock 	dprintf("%s0x%02x 0x%02x 0x%02x\n", *p++,
85824db4641Seschrock 	    rq->es_skey_specific[0], rq->es_skey_specific[1],
85924db4641Seschrock 	    rq->es_skey_specific[2]);
860724365f7Ssethg 	if (rqlen >= sizeof (*rq)) {
86124db4641Seschrock 		dprintf("%s0x%02x 0x%02x%s\n", *p, rq->es_add_info[0],
86224db4641Seschrock 		    rq->es_add_info[1], (rqlen > sizeof (*rq)) ? " ..." : "");
863724365f7Ssethg 	}
864724365f7Ssethg 
86524db4641Seschrock 	dprintf("\n");
866724365f7Ssethg }
867724365f7Ssethg 
868724365f7Ssethg /*
869724365f7Ssethg  * Display the full descriptor sense data as returned by the device
870724365f7Ssethg  */
871724365f7Ssethg static void
scsi_print_descr_sense(struct scsi_descr_sense_hdr * rq,int rqlen)872724365f7Ssethg scsi_print_descr_sense(struct scsi_descr_sense_hdr *rq, int rqlen)
873724365f7Ssethg {
87424db4641Seschrock 	/*
87524db4641Seschrock 	 * Labels for the various fields of the scsi_descr_sense_hdr structure
87624db4641Seschrock 	 */
877724365f7Ssethg 	static char *scsi_descr_sense_labels[] = {
87824db4641Seschrock 	    "Error class and code:            ",
87924db4641Seschrock 	    "Sense key:                       ",
88024db4641Seschrock 	    "Additional sense length:         ",
88124db4641Seschrock 	    "Additional sense code:           ",
88224db4641Seschrock 	    "Additional sense code qualifier: ",
88324db4641Seschrock 	    "Additional sense bytes:          "
884724365f7Ssethg 	};
885724365f7Ssethg 
886724365f7Ssethg 	struct scsi_information_sense_descr *isd;
887724365f7Ssethg 	uint8_t	*descr_offset;
88824db4641Seschrock 	int valid_sense_length;
88924db4641Seschrock 	char **p = scsi_descr_sense_labels;
890724365f7Ssethg 
89124db4641Seschrock 	/* Target must return at least 8 bytes of data */
89224db4641Seschrock 	if (rqlen < sizeof (struct scsi_descr_sense_hdr))
893724365f7Ssethg 		return;
894724365f7Ssethg 
895724365f7Ssethg 	/* Print descriptor sense header */
89624db4641Seschrock 	dprintf("%s0x%02x\n", *p++, (rq->ds_class << 4) + rq->ds_code);
89724db4641Seschrock 	dprintf("%s%d\n", *p++, rq->ds_key);
898724365f7Ssethg 
89924db4641Seschrock 	dprintf("%s%d\n", *p++, rq->ds_addl_sense_length);
90024db4641Seschrock 	dprintf("%s0x%02x = %d\n", *p++, rq->ds_add_code,
901724365f7Ssethg 	    rq->ds_add_code);
90224db4641Seschrock 	dprintf("%s0x%02x = %d\n", *p++, rq->ds_qual_code,
903724365f7Ssethg 	    rq->ds_qual_code);
90424db4641Seschrock 	dprintf("\n");
905724365f7Ssethg 
906724365f7Ssethg 	/*
907724365f7Ssethg 	 * Now print any sense descriptors.   The first descriptor will
908724365f7Ssethg 	 * immediately follow the header
909724365f7Ssethg 	 */
910724365f7Ssethg 	descr_offset = (uint8_t *)(rq+1); /* Pointer arithmetic */
911724365f7Ssethg 
912724365f7Ssethg 	/*
913724365f7Ssethg 	 * Calculate the amount of valid sense data
914724365f7Ssethg 	 */
915724365f7Ssethg 	valid_sense_length =
916724365f7Ssethg 	    MIN((sizeof (struct scsi_descr_sense_hdr) +
91724db4641Seschrock 	    rq->ds_addl_sense_length), rqlen);
918724365f7Ssethg 
919724365f7Ssethg 	/*
920724365f7Ssethg 	 * Iterate through the list of descriptors, stopping when we
921724365f7Ssethg 	 * run out of sense data.  Descriptor format is:
922724365f7Ssethg 	 *
923724365f7Ssethg 	 * <Descriptor type> <Descriptor length> <Descriptor data> ...
924724365f7Ssethg 	 */
925724365f7Ssethg 	while ((descr_offset + *(descr_offset + 1)) <=
926724365f7Ssethg 	    (uint8_t *)rq + valid_sense_length) {
927724365f7Ssethg 		/*
928724365f7Ssethg 		 * Determine descriptor type.  We can use the
929724365f7Ssethg 		 * scsi_information_sense_descr structure as a
930724365f7Ssethg 		 * template since the first two fields are always the
931724365f7Ssethg 		 * same.
932724365f7Ssethg 		 */
933724365f7Ssethg 		isd = (struct scsi_information_sense_descr *)descr_offset;
934724365f7Ssethg 		switch (isd->isd_descr_type) {
935724365f7Ssethg 		case DESCR_INFORMATION: {
936724365f7Ssethg 			uint64_t information;
937724365f7Ssethg 
938724365f7Ssethg 			information =
939724365f7Ssethg 			    (((uint64_t)isd->isd_information[0] << 56) |
94024db4641Seschrock 			    ((uint64_t)isd->isd_information[1] << 48) |
94124db4641Seschrock 			    ((uint64_t)isd->isd_information[2] << 40) |
94224db4641Seschrock 			    ((uint64_t)isd->isd_information[3] << 32) |
94324db4641Seschrock 			    ((uint64_t)isd->isd_information[4] << 24) |
94424db4641Seschrock 			    ((uint64_t)isd->isd_information[5] << 16) |
94524db4641Seschrock 			    ((uint64_t)isd->isd_information[6] << 8)  |
94624db4641Seschrock 			    ((uint64_t)isd->isd_information[7]));
94724db4641Seschrock 			dprintf("Information field:               "
948724365f7Ssethg 			    "%0" PRIx64 "\n", information);
949724365f7Ssethg 			break;
950724365f7Ssethg 		}
951724365f7Ssethg 		case DESCR_COMMAND_SPECIFIC: {
952724365f7Ssethg 			struct scsi_cmd_specific_sense_descr *c =
953724365f7Ssethg 			    (struct scsi_cmd_specific_sense_descr *)isd;
954724365f7Ssethg 			uint64_t cmd_specific;
955724365f7Ssethg 
956724365f7Ssethg 			cmd_specific =
957724365f7Ssethg 			    (((uint64_t)c->css_cmd_specific_info[0] << 56) |
95824db4641Seschrock 			    ((uint64_t)c->css_cmd_specific_info[1] << 48) |
95924db4641Seschrock 			    ((uint64_t)c->css_cmd_specific_info[2] << 40) |
96024db4641Seschrock 			    ((uint64_t)c->css_cmd_specific_info[3] << 32) |
96124db4641Seschrock 			    ((uint64_t)c->css_cmd_specific_info[4] << 24) |
96224db4641Seschrock 			    ((uint64_t)c->css_cmd_specific_info[5] << 16) |
96324db4641Seschrock 			    ((uint64_t)c->css_cmd_specific_info[6] << 8)  |
96424db4641Seschrock 			    ((uint64_t)c->css_cmd_specific_info[7]));
96524db4641Seschrock 			dprintf("Command-specific information:    "
966724365f7Ssethg 			    "%0" PRIx64 "\n", cmd_specific);
967724365f7Ssethg 			break;
968724365f7Ssethg 		}
969724365f7Ssethg 		case DESCR_SENSE_KEY_SPECIFIC: {
970724365f7Ssethg 			struct scsi_sk_specific_sense_descr *ssd =
971724365f7Ssethg 			    (struct scsi_sk_specific_sense_descr *)isd;
972724365f7Ssethg 			uint8_t *sk_spec_ptr = (uint8_t *)&ssd->sss_data;
97324db4641Seschrock 			dprintf("Sense-key specific:              "
974724365f7Ssethg 			    "0x%02x 0x%02x 0x%02x\n", sk_spec_ptr[0],
975724365f7Ssethg 			    sk_spec_ptr[1], sk_spec_ptr[2]);
976724365f7Ssethg 			break;
977724365f7Ssethg 		}
978724365f7Ssethg 		case DESCR_FRU: {
979724365f7Ssethg 			struct scsi_fru_sense_descr *fsd =
980724365f7Ssethg 			    (struct scsi_fru_sense_descr *)isd;
98124db4641Seschrock 			dprintf("Field replaceable unit code:     "
982724365f7Ssethg 			    "%d\n", fsd->fs_fru_code);
983724365f7Ssethg 			break;
984724365f7Ssethg 		}
985724365f7Ssethg 		case DESCR_BLOCK_COMMANDS: {
986724365f7Ssethg 			struct scsi_block_cmd_sense_descr *bsd =
987724365f7Ssethg 			    (struct scsi_block_cmd_sense_descr *)isd;
98824db4641Seschrock 			dprintf("Incorrect length indicator:      "
989724365f7Ssethg 			    "%s\n", bsd->bcs_ili ? "yes" : "no");
990724365f7Ssethg 			break;
991724365f7Ssethg 		}
992724365f7Ssethg 		default:
993724365f7Ssethg 			/* Ignore */
994724365f7Ssethg 			break;
995724365f7Ssethg 		}
996724365f7Ssethg 
997724365f7Ssethg 		/*
998724365f7Ssethg 		 * Get pointer to the next descriptor.  The "additional
999724365f7Ssethg 		 * length" field holds the length of the descriptor except
1000724365f7Ssethg 		 * for the "type" and "additional length" fields, so
1001724365f7Ssethg 		 * we need to add 2 to get the total length.
1002724365f7Ssethg 		 */
1003724365f7Ssethg 		descr_offset += (isd->isd_addl_length + 2);
1004724365f7Ssethg 	}
1005724365f7Ssethg 
100624db4641Seschrock 	dprintf("\n");
1007724365f7Ssethg }
1008724365f7Ssethg 
1009724365f7Ssethg static int
uscsi_timeout(void)1010724365f7Ssethg uscsi_timeout(void)
1011724365f7Ssethg {
1012724365f7Ssethg 	const char *env = getenv("USCSI_TIMEOUT");
1013724365f7Ssethg 	static int timeo = -1;
1014724365f7Ssethg 	int i;
1015724365f7Ssethg 
1016724365f7Ssethg 	if (timeo > 0)
1017724365f7Ssethg 		return (timeo);
1018724365f7Ssethg 
1019724365f7Ssethg 	if (env != NULL) {
1020724365f7Ssethg 		i = atoi(env);
1021724365f7Ssethg 		if (i > USCSI_TIMEOUT_MAX)
1022724365f7Ssethg 			i = USCSI_TIMEOUT_MAX;
1023724365f7Ssethg 		else if (i < 0)
1024724365f7Ssethg 			i = USCSI_DEFAULT_TIMEOUT;
1025724365f7Ssethg 	} else
1026724365f7Ssethg 		i = USCSI_DEFAULT_TIMEOUT;
1027724365f7Ssethg 
1028724365f7Ssethg 	timeo = i;
1029724365f7Ssethg 	return (i);
1030724365f7Ssethg }
1031724365f7Ssethg 
1032724365f7Ssethg /*
103324db4641Seschrock  * Execute a command and determine the result.  Uses the "uscsi" ioctl
103424db4641Seschrock  * interface, which is fully supported.
1035724365f7Ssethg  *
103624db4641Seschrock  * If the user wants request sense data to be returned in case of error then ,
103724db4641Seschrock  * the "uscsi_cmd" structure should have the request sense buffer allocated in
1038724365f7Ssethg  * uscsi_rqbuf.
1039724365f7Ssethg  */
1040724365f7Ssethg static int
uscsi_cmd(int fd,struct uscsi_cmd * ucmd,void * rqbuf,int * rqlen)104124db4641Seschrock uscsi_cmd(int fd, struct uscsi_cmd *ucmd, void *rqbuf, int *rqlen)
1042724365f7Ssethg {
104324db4641Seschrock 	struct scsi_extended_sense *rq;
104424db4641Seschrock 	int status;
1045724365f7Ssethg 
1046724365f7Ssethg 	/*
1047724365f7Ssethg 	 * Set function flags for driver.
1048724365f7Ssethg 	 */
1049724365f7Ssethg 	ucmd->uscsi_flags = USCSI_ISOLATE;
105024db4641Seschrock 	if (!ds_debug)
1051724365f7Ssethg 		ucmd->uscsi_flags |= USCSI_SILENT;
1052724365f7Ssethg 
1053724365f7Ssethg 	/*
1054724365f7Ssethg 	 * If this command will perform a read, set the USCSI_READ flag
1055724365f7Ssethg 	 */
1056724365f7Ssethg 	if (ucmd->uscsi_buflen > 0) {
1057724365f7Ssethg 		/*
1058724365f7Ssethg 		 * uscsi_cdb is declared as a caddr_t, so any CDB
1059724365f7Ssethg 		 * command byte with the MSB set will result in a
1060724365f7Ssethg 		 * compiler error unless we cast to an unsigned value.
1061724365f7Ssethg 		 */
1062724365f7Ssethg 		switch ((uint8_t)ucmd->uscsi_cdb[0]) {
1063724365f7Ssethg 		case SCMD_MODE_SENSE:
1064724365f7Ssethg 		case SCMD_MODE_SENSE_G1:
1065724365f7Ssethg 		case SCMD_LOG_SENSE_G1:
1066724365f7Ssethg 		case SCMD_REQUEST_SENSE:
1067724365f7Ssethg 			ucmd->uscsi_flags |= USCSI_READ;
1068724365f7Ssethg 			break;
1069724365f7Ssethg 
1070724365f7Ssethg 		case SCMD_MODE_SELECT:
1071724365f7Ssethg 		case SCMD_MODE_SELECT_G1:
107224db4641Seschrock 			/* LINTED */
1073724365f7Ssethg 			ucmd->uscsi_flags |= USCSI_WRITE;
1074724365f7Ssethg 			break;
107524db4641Seschrock 		default:
107624db4641Seschrock 			assert(0);
107724db4641Seschrock 			break;
1078724365f7Ssethg 		}
1079724365f7Ssethg 	}
1080724365f7Ssethg 
1081724365f7Ssethg 	/* Set timeout */
1082724365f7Ssethg 	ucmd->uscsi_timeout = uscsi_timeout();
1083724365f7Ssethg 
1084724365f7Ssethg 	/*
1085724365f7Ssethg 	 * Set up Request Sense buffer
1086724365f7Ssethg 	 */
1087724365f7Ssethg 
1088724365f7Ssethg 	if (ucmd->uscsi_rqbuf == NULL)  {
1089724365f7Ssethg 		ucmd->uscsi_rqbuf = rqbuf;
1090724365f7Ssethg 		ucmd->uscsi_rqlen = *rqlen;
1091724365f7Ssethg 		ucmd->uscsi_rqresid = *rqlen;
1092724365f7Ssethg 	}
109324db4641Seschrock 	if (ucmd->uscsi_rqbuf)
109424db4641Seschrock 		ucmd->uscsi_flags |= USCSI_RQENABLE;
1095724365f7Ssethg 	ucmd->uscsi_rqstatus = IMPOSSIBLE_SCSI_STATUS;
1096724365f7Ssethg 
1097724365f7Ssethg 	if (ucmd->uscsi_rqbuf != NULL && ucmd->uscsi_rqlen > 0)
1098724365f7Ssethg 		(void) memset(ucmd->uscsi_rqbuf, 0, ucmd->uscsi_rqlen);
1099724365f7Ssethg 
1100724365f7Ssethg 	/*
1101724365f7Ssethg 	 * Execute the ioctl
1102724365f7Ssethg 	 */
1103724365f7Ssethg 	status = ioctl(fd, USCSICMD, ucmd);
110424db4641Seschrock 	if (status == 0 && ucmd->uscsi_status == 0)
1105724365f7Ssethg 		return (status);
1106724365f7Ssethg 
1107724365f7Ssethg 	/*
110824db4641Seschrock 	 * If an automatic Request Sense gave us valid info about the error, we
110924db4641Seschrock 	 * may be able to use that to print a reasonable error msg.
1110724365f7Ssethg 	 */
1111724365f7Ssethg 	if (ucmd->uscsi_rqstatus == IMPOSSIBLE_SCSI_STATUS) {
111224db4641Seschrock 		dprintf("No request sense for command %s\n",
111324db4641Seschrock 		    find_string(scsi_cmdname_strings,
111424db4641Seschrock 		    ucmd->uscsi_cdb[0]));
1115724365f7Ssethg 		return (-1);
1116724365f7Ssethg 	}
1117724365f7Ssethg 	if (ucmd->uscsi_rqstatus != STATUS_GOOD) {
111824db4641Seschrock 		dprintf("Request sense status for command %s: 0x%x\n",
1119724365f7Ssethg 		    find_string(scsi_cmdname_strings,
1120724365f7Ssethg 		    ucmd->uscsi_cdb[0]),
1121724365f7Ssethg 		    ucmd->uscsi_rqstatus);
1122724365f7Ssethg 		return (-1);
1123724365f7Ssethg 	}
112424db4641Seschrock 
1125724365f7Ssethg 	rq = (struct scsi_extended_sense *)ucmd->uscsi_rqbuf;
1126724365f7Ssethg 	*rqlen = ucmd->uscsi_rqlen - ucmd->uscsi_rqresid;
1127724365f7Ssethg 
112824db4641Seschrock 	if ((((int)rq->es_add_len) + 8) < MIN_REQUEST_SENSE_LEN ||
112924db4641Seschrock 	    rq->es_class != CLASS_EXTENDED_SENSE ||
113024db4641Seschrock 	    *rqlen < MIN_REQUEST_SENSE_LEN) {
113124db4641Seschrock 		dprintf("Request sense for command %s failed\n",
113224db4641Seschrock 		    find_string(scsi_cmdname_strings,
113324db4641Seschrock 		    ucmd->uscsi_cdb[0]));
1134724365f7Ssethg 
113524db4641Seschrock 		dprintf("Sense data:\n");
113624db4641Seschrock 		ddump(NULL, (caddr_t)rqbuf, *rqlen);
1137724365f7Ssethg 
1138724365f7Ssethg 		return (-1);
1139724365f7Ssethg 	}
1140724365f7Ssethg 
1141724365f7Ssethg 	/*
1142724365f7Ssethg 	 * If the failed command is a Mode Select, and the
1143724365f7Ssethg 	 * target is indicating that it has rounded one of
1144724365f7Ssethg 	 * the mode select parameters, as defined in the SCSI-2
1145724365f7Ssethg 	 * specification, then we should accept the command
1146724365f7Ssethg 	 * as successful.
1147724365f7Ssethg 	 */
1148724365f7Ssethg 	if (ucmd->uscsi_cdb[0] == SCMD_MODE_SELECT ||
1149724365f7Ssethg 	    ucmd->uscsi_cdb[0] == SCMD_MODE_SELECT_G1) {
1150724365f7Ssethg 		if (rq->es_key == KEY_RECOVERABLE_ERROR &&
115124db4641Seschrock 		    rq->es_add_code == ROUNDED_PARAMETER &&
115224db4641Seschrock 		    rq->es_qual_code == 0) {
115324db4641Seschrock 			return (0);
1154724365f7Ssethg 		}
1155724365f7Ssethg 	}
1156724365f7Ssethg 
115724db4641Seschrock 	if (ds_debug)
1158724365f7Ssethg 		scsi_printerr(ucmd, rq, *rqlen);
115924db4641Seschrock 	if (rq->es_key != KEY_RECOVERABLE_ERROR)
1160724365f7Ssethg 		return (-1);
1161724365f7Ssethg 	return (0);
1162724365f7Ssethg }
1163724365f7Ssethg 
1164724365f7Ssethg int
uscsi_request_sense(int fd,caddr_t buf,int buflen,void * rqbuf,int * rqblen)1165724365f7Ssethg uscsi_request_sense(int fd, caddr_t buf, int buflen, void *rqbuf, int *rqblen)
1166724365f7Ssethg {
116724db4641Seschrock 	struct uscsi_cmd ucmd;
116824db4641Seschrock 	union scsi_cdb cdb;
116924db4641Seschrock 	int status;
1170724365f7Ssethg 
1171724365f7Ssethg 	(void) memset(buf, 0, buflen);
117224db4641Seschrock 	(void) memset(&ucmd, 0, sizeof (ucmd));
117324db4641Seschrock 	(void) memset(&cdb, 0, sizeof (union scsi_cdb));
1174724365f7Ssethg 	cdb.scc_cmd = SCMD_REQUEST_SENSE;
1175724365f7Ssethg 	FORMG0COUNT(&cdb, (uchar_t)buflen);
1176724365f7Ssethg 	ucmd.uscsi_cdb = (caddr_t)&cdb;
1177724365f7Ssethg 	ucmd.uscsi_cdblen = CDB_GROUP0;
1178724365f7Ssethg 	ucmd.uscsi_bufaddr = buf;
1179724365f7Ssethg 	ucmd.uscsi_buflen = buflen;
118024db4641Seschrock 	status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
118124db4641Seschrock 	if (status)
118224db4641Seschrock 		dprintf("Request sense failed\n");
1183724365f7Ssethg 	if (status == 0)
118424db4641Seschrock 		ddump("Request Sense data:", buf, buflen);
1185724365f7Ssethg 
1186724365f7Ssethg 	return (status);
1187724365f7Ssethg }
1188724365f7Ssethg 
1189724365f7Ssethg /*
119024db4641Seschrock  * Execute a uscsi mode sense command.  This can only be used to return one page
119124db4641Seschrock  * at a time.  Return the mode header/block descriptor and the actual page data
119224db4641Seschrock  * separately - this allows us to support devices which return either 0 or 1
119324db4641Seschrock  * block descriptors.  Whatever a device gives us in the mode header/block
119424db4641Seschrock  * descriptor will be returned to it upon subsequent mode selects.
1195724365f7Ssethg  */
1196724365f7Ssethg int
uscsi_mode_sense(int fd,int page_code,int page_control,caddr_t page_data,int page_size,struct scsi_ms_header * header,void * rqbuf,int * rqblen)1197724365f7Ssethg uscsi_mode_sense(int fd, int page_code, int page_control, caddr_t page_data,
1198724365f7Ssethg     int page_size, struct scsi_ms_header *header, void *rqbuf, int *rqblen)
1199724365f7Ssethg {
120024db4641Seschrock 	caddr_t mode_sense_buf;
120124db4641Seschrock 	struct mode_header *hdr;
120224db4641Seschrock 	struct mode_page *pg;
120324db4641Seschrock 	int nbytes;
120424db4641Seschrock 	struct uscsi_cmd ucmd;
120524db4641Seschrock 	union scsi_cdb cdb;
120624db4641Seschrock 	int status;
120724db4641Seschrock 	int maximum;
120824db4641Seschrock 	char *pc;
120924db4641Seschrock 
121024db4641Seschrock 	assert(page_size >= 0 && page_size < 256);
121124db4641Seschrock 	assert(page_control == PC_CURRENT || page_control == PC_CHANGEABLE ||
121224db4641Seschrock 	    page_control == PC_DEFAULT || page_control == PC_SAVED);
1213724365f7Ssethg 
121424db4641Seschrock 	nbytes = sizeof (struct scsi_ms_header) + page_size;
121524db4641Seschrock 	mode_sense_buf = alloca((uint_t)nbytes);
1216724365f7Ssethg 
1217724365f7Ssethg 	/*
1218724365f7Ssethg 	 * Build and execute the uscsi ioctl
1219724365f7Ssethg 	 */
1220724365f7Ssethg 	(void) memset(mode_sense_buf, 0, nbytes);
122124db4641Seschrock 	(void) memset(&ucmd, 0, sizeof (ucmd));
122224db4641Seschrock 	(void) memset(&cdb, 0, sizeof (union scsi_cdb));
1223724365f7Ssethg 	cdb.scc_cmd = SCMD_MODE_SENSE;
1224724365f7Ssethg 	FORMG0COUNT(&cdb, (uchar_t)nbytes);
1225724365f7Ssethg 	cdb.cdb_opaque[2] = page_control | page_code;
1226724365f7Ssethg 	ucmd.uscsi_cdb = (caddr_t)&cdb;
1227724365f7Ssethg 	ucmd.uscsi_cdblen = CDB_GROUP0;
1228724365f7Ssethg 	ucmd.uscsi_bufaddr = mode_sense_buf;
1229724365f7Ssethg 	ucmd.uscsi_buflen = nbytes;
123024db4641Seschrock 	status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
1231724365f7Ssethg 	if (status) {
123224db4641Seschrock 		dprintf("Mode sense page 0x%x failed\n", page_code);
1233724365f7Ssethg 		return (-1);
1234724365f7Ssethg 	}
1235724365f7Ssethg 
123624db4641Seschrock 	ddump("RAW MODE SENSE BUFFER", mode_sense_buf, nbytes);
1237724365f7Ssethg 
1238724365f7Ssethg 	/*
123924db4641Seschrock 	 * Verify that the returned data looks reasonable, find the actual page
124024db4641Seschrock 	 * data, and copy it into the user's buffer.  Copy the mode_header and
124124db4641Seschrock 	 * block_descriptor into the header structure, which can then be used to
1242724365f7Ssethg 	 * return the same data to the drive when issuing a mode select.
1243724365f7Ssethg 	 */
1244724365f7Ssethg 	hdr = (struct mode_header *)mode_sense_buf;
1245724365f7Ssethg 	(void) memset((caddr_t)header, 0, sizeof (struct scsi_ms_header));
12460e35cc03SRobert Mustacchi 
12470e35cc03SRobert Mustacchi 	/*
12480e35cc03SRobert Mustacchi 	 * Check to see if we have a valid header length. We've occasionally
12490e35cc03SRobert Mustacchi 	 * seen hardware return zero here, even though they filled in the media
12500e35cc03SRobert Mustacchi 	 * type.
12510e35cc03SRobert Mustacchi 	 */
12520e35cc03SRobert Mustacchi 	if (hdr->length == 0) {
12530e35cc03SRobert Mustacchi 		dprintf("\nMode sense page 0x%x: has header length for zero\n",
12540e35cc03SRobert Mustacchi 		    page_code);
12550e35cc03SRobert Mustacchi 		ddump("Mode sense:", mode_sense_buf, nbytes);
12560e35cc03SRobert Mustacchi 		return (-1);
12570e35cc03SRobert Mustacchi 	}
12580e35cc03SRobert Mustacchi 
1259724365f7Ssethg 	if (hdr->bdesc_length != sizeof (struct block_descriptor) &&
126024db4641Seschrock 	    hdr->bdesc_length != 0) {
126124db4641Seschrock 		dprintf("\nMode sense page 0x%x: block descriptor "
126224db4641Seschrock 		    "length %d incorrect\n", page_code, hdr->bdesc_length);
126324db4641Seschrock 		ddump("Mode sense:", mode_sense_buf, nbytes);
1264724365f7Ssethg 		return (-1);
1265724365f7Ssethg 	}
1266724365f7Ssethg 	(void) memcpy((caddr_t)header, mode_sense_buf,
126724db4641Seschrock 	    (int)(MODE_HEADER_LENGTH + hdr->bdesc_length));
1268724365f7Ssethg 	pg = (struct mode_page *)((ulong_t)mode_sense_buf +
126924db4641Seschrock 	    MODE_HEADER_LENGTH + hdr->bdesc_length);
1270724365f7Ssethg 
127124db4641Seschrock 	if (page_code == MODEPAGE_ALLPAGES) {
127224db4641Seschrock 		/* special case */
1273724365f7Ssethg 
12740e35cc03SRobert Mustacchi 		if ((hdr->length + sizeof (header->ms_header.length)) <
12750e35cc03SRobert Mustacchi 		    (MODE_HEADER_LENGTH + hdr->bdesc_length)) {
12760e35cc03SRobert Mustacchi 			dprintf("\nHeader length would spiral into a "
12770e35cc03SRobert Mustacchi 			    "negative bcopy\n");
12780e35cc03SRobert Mustacchi 			return (-1);
12790e35cc03SRobert Mustacchi 		}
12800e35cc03SRobert Mustacchi 
1281724365f7Ssethg 		(void) memcpy(page_data, (caddr_t)pg,
128224db4641Seschrock 		    (hdr->length + sizeof (header->ms_header.length)) -
1283724365f7Ssethg 		    (MODE_HEADER_LENGTH + hdr->bdesc_length));
1284724365f7Ssethg 
1285724365f7Ssethg 		pc = find_string(page_control_strings, page_control);
128624db4641Seschrock 		dprintf("\nMode sense page 0x%x (%s):\n", page_code,
128724db4641Seschrock 		    pc != NULL ? pc : "");
128824db4641Seschrock 		ddump("header:", (caddr_t)header,
128924db4641Seschrock 		    sizeof (struct scsi_ms_header));
129024db4641Seschrock 		ddump("data:", page_data,
1291724365f7Ssethg 		    (hdr->length +
129224db4641Seschrock 		    sizeof (header->ms_header.length)) -
1293724365f7Ssethg 		    (MODE_HEADER_LENGTH + hdr->bdesc_length));
1294724365f7Ssethg 
1295724365f7Ssethg 		return (0);
1296724365f7Ssethg 	}
1297724365f7Ssethg 
1298724365f7Ssethg 	if (pg->code != page_code) {
129924db4641Seschrock 		dprintf("\nMode sense page 0x%x: incorrect page code 0x%x\n",
130024db4641Seschrock 		    page_code, pg->code);
130124db4641Seschrock 		ddump("Mode sense:", mode_sense_buf, nbytes);
1302724365f7Ssethg 		return (-1);
1303724365f7Ssethg 	}
130424db4641Seschrock 
1305724365f7Ssethg 	/*
130624db4641Seschrock 	 * Accept up to "page_size" bytes of mode sense data.  This allows us to
130724db4641Seschrock 	 * accept both CCS and SCSI-2 structures, as long as we request the
130824db4641Seschrock 	 * greater of the two.
1309724365f7Ssethg 	 */
1310724365f7Ssethg 	maximum = page_size - sizeof (struct mode_page);
1311724365f7Ssethg 	if (((int)pg->length) > maximum) {
131224db4641Seschrock 		dprintf("Mode sense page 0x%x: incorrect page "
131324db4641Seschrock 		    "length %d - expected max %d\n",
1314724365f7Ssethg 		    page_code, pg->length, maximum);
131524db4641Seschrock 		ddump("Mode sense:", mode_sense_buf, nbytes);
1316724365f7Ssethg 		return (-1);
1317724365f7Ssethg 	}
1318724365f7Ssethg 
1319724365f7Ssethg 	(void) memcpy(page_data, (caddr_t)pg, MODESENSE_PAGE_LEN(pg));
1320724365f7Ssethg 
1321724365f7Ssethg 	pc = find_string(page_control_strings, page_control);
132224db4641Seschrock 	dprintf("\nMode sense page 0x%x (%s):\n", page_code,
132324db4641Seschrock 	    pc != NULL ? pc : "");
132424db4641Seschrock 	ddump("header:", (caddr_t)header, sizeof (struct scsi_ms_header));
132524db4641Seschrock 	ddump("data:", page_data, MODESENSE_PAGE_LEN(pg));
1326724365f7Ssethg 
1327724365f7Ssethg 	return (0);
1328724365f7Ssethg }
1329724365f7Ssethg 
1330724365f7Ssethg /*
133124db4641Seschrock  * Execute a uscsi MODE SENSE(10) command.  This can only be used to return one
133224db4641Seschrock  * page at a time.  Return the mode header/block descriptor and the actual page
133324db4641Seschrock  * data separately - this allows us to support devices which return either 0 or
133424db4641Seschrock  * 1 block descriptors.  Whatever a device gives us in the mode header/block
133524db4641Seschrock  * descriptor will be returned to it upon subsequent mode selects.
1336724365f7Ssethg  */
1337724365f7Ssethg int
uscsi_mode_sense_10(int fd,int page_code,int page_control,caddr_t page_data,int page_size,struct scsi_ms_header_g1 * header,void * rqbuf,int * rqblen)1338724365f7Ssethg uscsi_mode_sense_10(int fd, int page_code, int page_control,
1339724365f7Ssethg     caddr_t page_data, int page_size, struct scsi_ms_header_g1 *header,
1340724365f7Ssethg     void *rqbuf, int *rqblen)
1341724365f7Ssethg {
134224db4641Seschrock 	caddr_t mode_sense_buf;
134324db4641Seschrock 	struct mode_header_g1 *hdr;
134424db4641Seschrock 	struct mode_page *pg;
134524db4641Seschrock 	int nbytes;
134624db4641Seschrock 	struct uscsi_cmd ucmd;
134724db4641Seschrock 	union scsi_cdb cdb;
134824db4641Seschrock 	int status;
134924db4641Seschrock 	int maximum;
135024db4641Seschrock 	ushort_t length, bdesc_length;
135124db4641Seschrock 	char *pc;
135224db4641Seschrock 
135324db4641Seschrock 	assert(page_size >= 0 && page_size < UINT16_MAX);
135424db4641Seschrock 	assert(page_control == PC_CURRENT || page_control == PC_CHANGEABLE ||
135524db4641Seschrock 	    page_control == PC_DEFAULT || page_control == PC_SAVED);
1356724365f7Ssethg 
135724db4641Seschrock 	nbytes = sizeof (struct scsi_ms_header_g1) + page_size;
135824db4641Seschrock 	mode_sense_buf = alloca((uint_t)nbytes);
1359724365f7Ssethg 
1360724365f7Ssethg 	(void) memset(mode_sense_buf, 0, nbytes);
1361724365f7Ssethg 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
1362724365f7Ssethg 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
1363724365f7Ssethg 	cdb.scc_cmd = SCMD_MODE_SENSE_G1;
1364724365f7Ssethg 	FORMG1COUNT(&cdb, (uint16_t)nbytes);
1365724365f7Ssethg 	cdb.cdb_opaque[2] = page_control | page_code;
1366724365f7Ssethg 	ucmd.uscsi_cdb = (caddr_t)&cdb;
1367724365f7Ssethg 	ucmd.uscsi_cdblen = CDB_GROUP1;
1368724365f7Ssethg 	ucmd.uscsi_bufaddr = mode_sense_buf;
1369724365f7Ssethg 	ucmd.uscsi_buflen = nbytes;
1370724365f7Ssethg 
137124db4641Seschrock 	status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
1372724365f7Ssethg 	if (status) {
137324db4641Seschrock 		dprintf("Mode sense(10) page 0x%x failed\n",
1374724365f7Ssethg 		    page_code);
1375724365f7Ssethg 		return (-1);
1376724365f7Ssethg 	}
1377724365f7Ssethg 
137824db4641Seschrock 	ddump("RAW MODE SENSE(10) BUFFER", mode_sense_buf, nbytes);
1379724365f7Ssethg 
1380724365f7Ssethg 	/*
138124db4641Seschrock 	 * Verify that the returned data looks reasonable, find the actual page
138224db4641Seschrock 	 * data, and copy it into the user's buffer.  Copy the mode_header and
138324db4641Seschrock 	 * block_descriptor into the header structure, which can then be used to
1384724365f7Ssethg 	 * return the same data to the drive when issuing a mode select.
1385724365f7Ssethg 	 */
138624db4641Seschrock 	/* LINTED */
1387724365f7Ssethg 	hdr = (struct mode_header_g1 *)mode_sense_buf;
1388724365f7Ssethg 
1389724365f7Ssethg 	length = BE_16(hdr->length);
1390724365f7Ssethg 	bdesc_length = BE_16(hdr->bdesc_length);
1391724365f7Ssethg 
1392724365f7Ssethg 	(void) memset((caddr_t)header, 0, sizeof (struct scsi_ms_header_g1));
1393724365f7Ssethg 	if (bdesc_length != sizeof (struct block_descriptor) &&
139424db4641Seschrock 	    bdesc_length != 0) {
139524db4641Seschrock 		dprintf("\nMode sense(10) page 0x%x: block descriptor "
139624db4641Seschrock 		    "length %d incorrect\n", page_code, bdesc_length);
139724db4641Seschrock 		ddump("Mode sense(10):", mode_sense_buf, nbytes);
1398724365f7Ssethg 		return (-1);
1399724365f7Ssethg 	}
1400724365f7Ssethg 	(void) memcpy((caddr_t)header, mode_sense_buf,
140124db4641Seschrock 	    (int)(MODE_HEADER_LENGTH_G1 + bdesc_length));
1402724365f7Ssethg 	pg = (struct mode_page *)((ulong_t)mode_sense_buf +
140324db4641Seschrock 	    MODE_HEADER_LENGTH_G1 + bdesc_length);
1404724365f7Ssethg 
140524db4641Seschrock 	if (page_code == MODEPAGE_ALLPAGES) {
140624db4641Seschrock 		/* special case */
1407724365f7Ssethg 
1408724365f7Ssethg 		(void) memcpy(page_data, (caddr_t)pg,
140924db4641Seschrock 		    (length + sizeof (header->ms_header.length)) -
1410724365f7Ssethg 		    (MODE_HEADER_LENGTH_G1 + bdesc_length));
1411724365f7Ssethg 
141224db4641Seschrock 		pc = find_string(page_control_strings, page_control);
141324db4641Seschrock 		dprintf("\nMode sense(10) page 0x%x (%s):\n",
1414724365f7Ssethg 		    page_code, pc != NULL ? pc : "");
141524db4641Seschrock 		ddump("header:", (caddr_t)header,
1416724365f7Ssethg 		    MODE_HEADER_LENGTH_G1 + bdesc_length);
1417724365f7Ssethg 
141824db4641Seschrock 		ddump("data:", page_data,
141924db4641Seschrock 		    (length + sizeof (header->ms_header.length)) -
1420724365f7Ssethg 		    (MODE_HEADER_LENGTH_G1 + bdesc_length));
1421724365f7Ssethg 
1422724365f7Ssethg 		return (0);
1423724365f7Ssethg 	}
1424724365f7Ssethg 
1425724365f7Ssethg 	if (pg->code != page_code) {
142624db4641Seschrock 		dprintf("\nMode sense(10) page 0x%x: incorrect page "
142724db4641Seschrock 		    "code 0x%x\n", page_code, pg->code);
142824db4641Seschrock 		ddump("Mode sense(10):", mode_sense_buf, nbytes);
1429724365f7Ssethg 		return (-1);
1430724365f7Ssethg 	}
143124db4641Seschrock 
1432724365f7Ssethg 	/*
143324db4641Seschrock 	 * Accept up to "page_size" bytes of mode sense data.  This allows us to
143424db4641Seschrock 	 * accept both CCS and SCSI-2 structures, as long as we request the
143524db4641Seschrock 	 * greater of the two.
1436724365f7Ssethg 	 */
1437724365f7Ssethg 	maximum = page_size - sizeof (struct mode_page);
1438724365f7Ssethg 	if (((int)pg->length) > maximum) {
143924db4641Seschrock 		dprintf("Mode sense(10) page 0x%x: incorrect page "
144024db4641Seschrock 		    "length %d - expected max %d\n",
144124db4641Seschrock 		    page_code, pg->length, maximum);
144224db4641Seschrock 		ddump("Mode sense(10):", mode_sense_buf,
1443724365f7Ssethg 		    nbytes);
1444724365f7Ssethg 		return (-1);
1445724365f7Ssethg 	}
1446724365f7Ssethg 
1447724365f7Ssethg 	(void) memcpy(page_data, (caddr_t)pg, MODESENSE_PAGE_LEN(pg));
1448724365f7Ssethg 
1449724365f7Ssethg 	pc = find_string(page_control_strings, page_control);
145024db4641Seschrock 	dprintf("\nMode sense(10) page 0x%x (%s):\n", page_code,
1451724365f7Ssethg 	    pc != NULL ? pc : "");
145224db4641Seschrock 	ddump("header:", (caddr_t)header,
1453724365f7Ssethg 	    sizeof (struct scsi_ms_header_g1));
145424db4641Seschrock 	ddump("data:", page_data, MODESENSE_PAGE_LEN(pg));
1455724365f7Ssethg 
1456724365f7Ssethg 	return (0);
1457724365f7Ssethg }
1458724365f7Ssethg 
1459724365f7Ssethg /*
1460724365f7Ssethg  * Execute a uscsi mode select command.
1461724365f7Ssethg  */
1462724365f7Ssethg int
uscsi_mode_select(int fd,int page_code,int options,caddr_t page_data,int page_size,struct scsi_ms_header * header,void * rqbuf,int * rqblen)1463724365f7Ssethg uscsi_mode_select(int fd, int page_code, int options, caddr_t page_data,
1464724365f7Ssethg     int page_size, struct scsi_ms_header *header, void *rqbuf, int *rqblen)
1465724365f7Ssethg {
146624db4641Seschrock 	caddr_t mode_select_buf;
146724db4641Seschrock 	int nbytes;
146824db4641Seschrock 	struct uscsi_cmd ucmd;
146924db4641Seschrock 	union scsi_cdb cdb;
147024db4641Seschrock 	int status;
147124db4641Seschrock 	char *s;
147224db4641Seschrock 
147324db4641Seschrock 	assert(((struct mode_page *)page_data)->ps == 0);
147424db4641Seschrock 	assert(header->ms_header.length == 0);
147524db4641Seschrock 	assert(header->ms_header.device_specific == 0);
147624db4641Seschrock 	assert((options & ~(MODE_SELECT_SP|MODE_SELECT_PF)) == 0);
1477724365f7Ssethg 
1478724365f7Ssethg 	nbytes = sizeof (struct scsi_ms_header) + page_size;
147924db4641Seschrock 	mode_select_buf = alloca((uint_t)nbytes);
1480724365f7Ssethg 
1481724365f7Ssethg 	/*
148224db4641Seschrock 	 * Build the mode select data out of the header and page data This
148324db4641Seschrock 	 * allows us to support devices which return either 0 or 1 block
148424db4641Seschrock 	 * descriptors.
1485724365f7Ssethg 	 */
1486724365f7Ssethg 	(void) memset(mode_select_buf, 0, nbytes);
1487724365f7Ssethg 	nbytes = MODE_HEADER_LENGTH;
148824db4641Seschrock 	if (header->ms_header.bdesc_length ==
148924db4641Seschrock 	    sizeof (struct block_descriptor)) {
1490724365f7Ssethg 		nbytes += sizeof (struct block_descriptor);
1491724365f7Ssethg 	}
1492724365f7Ssethg 
1493724365f7Ssethg 	s = find_string(mode_select_strings,
149424db4641Seschrock 	    options & (MODE_SELECT_SP|MODE_SELECT_PF));
149524db4641Seschrock 	dprintf("\nMode select page 0x%x%s:\n", page_code,
149624db4641Seschrock 	    s != NULL ? s : "");
149724db4641Seschrock 	ddump("header:", (caddr_t)header, nbytes);
149824db4641Seschrock 	ddump("data:", (caddr_t)page_data, page_size);
1499724365f7Ssethg 
1500724365f7Ssethg 	/*
1501724365f7Ssethg 	 * Put the header and data together
1502724365f7Ssethg 	 */
1503724365f7Ssethg 	(void) memcpy(mode_select_buf, (caddr_t)header, nbytes);
1504724365f7Ssethg 	(void) memcpy(mode_select_buf + nbytes, page_data, page_size);
1505724365f7Ssethg 	nbytes += page_size;
1506724365f7Ssethg 
1507724365f7Ssethg 	/*
1508724365f7Ssethg 	 * Build and execute the uscsi ioctl
1509724365f7Ssethg 	 */
1510724365f7Ssethg 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
1511724365f7Ssethg 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
1512724365f7Ssethg 	cdb.scc_cmd = SCMD_MODE_SELECT;
1513724365f7Ssethg 	FORMG0COUNT(&cdb, (uchar_t)nbytes);
1514724365f7Ssethg 	cdb.cdb_opaque[1] = (uchar_t)options;
1515724365f7Ssethg 	ucmd.uscsi_cdb = (caddr_t)&cdb;
1516724365f7Ssethg 	ucmd.uscsi_cdblen = CDB_GROUP0;
1517724365f7Ssethg 	ucmd.uscsi_bufaddr = mode_select_buf;
1518724365f7Ssethg 	ucmd.uscsi_buflen = nbytes;
151924db4641Seschrock 	status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
1520724365f7Ssethg 
152124db4641Seschrock 	if (status)
152224db4641Seschrock 		dprintf("Mode select page 0x%x failed\n", page_code);
1523724365f7Ssethg 
1524724365f7Ssethg 	return (status);
1525724365f7Ssethg }
1526724365f7Ssethg 
1527724365f7Ssethg /*
1528724365f7Ssethg  * Execute a uscsi mode select(10) command.
1529724365f7Ssethg  */
1530724365f7Ssethg int
uscsi_mode_select_10(int fd,int page_code,int options,caddr_t page_data,int page_size,struct scsi_ms_header_g1 * header,void * rqbuf,int * rqblen)1531724365f7Ssethg uscsi_mode_select_10(int fd, int page_code, int options,
1532724365f7Ssethg     caddr_t page_data, int page_size, struct scsi_ms_header_g1 *header,
1533724365f7Ssethg     void *rqbuf, int *rqblen)
1534724365f7Ssethg {
1535724365f7Ssethg 	caddr_t				mode_select_buf;
1536724365f7Ssethg 	int				nbytes;
1537724365f7Ssethg 	struct uscsi_cmd		ucmd;
1538724365f7Ssethg 	union scsi_cdb			cdb;
1539724365f7Ssethg 	int				status;
1540724365f7Ssethg 	char				*s;
1541724365f7Ssethg 
154224db4641Seschrock 	assert(((struct mode_page *)page_data)->ps == 0);
154324db4641Seschrock 	assert(header->ms_header.length == 0);
154424db4641Seschrock 	assert(header->ms_header.device_specific == 0);
154524db4641Seschrock 	assert((options & ~(MODE_SELECT_SP|MODE_SELECT_PF)) == 0);
1546724365f7Ssethg 
1547724365f7Ssethg 	nbytes = sizeof (struct scsi_ms_header_g1) + page_size;
154824db4641Seschrock 	mode_select_buf = alloca((uint_t)nbytes);
1549724365f7Ssethg 
1550724365f7Ssethg 	/*
1551724365f7Ssethg 	 * Build the mode select data out of the header and page data
1552724365f7Ssethg 	 * This allows us to support devices which return either
1553724365f7Ssethg 	 * 0 or 1 block descriptors.
1554724365f7Ssethg 	 */
1555724365f7Ssethg 	(void) memset(mode_select_buf, 0, nbytes);
1556724365f7Ssethg 	nbytes = sizeof (struct mode_header_g1);
155724db4641Seschrock 	if (BE_16(header->ms_header.bdesc_length) ==
155824db4641Seschrock 	    sizeof (struct block_descriptor)) {
1559724365f7Ssethg 		nbytes += sizeof (struct block_descriptor);
1560724365f7Ssethg 	}
1561724365f7Ssethg 
1562724365f7Ssethg 	/*
156324db4641Seschrock 	 * Dump the structures
1564724365f7Ssethg 	 */
1565724365f7Ssethg 	s = find_string(mode_select_strings,
156624db4641Seschrock 	    options & (MODE_SELECT_SP|MODE_SELECT_PF));
156724db4641Seschrock 	dprintf("\nMode select(10) page 0x%x%s:\n", page_code,
156824db4641Seschrock 	    s != NULL ? s : "");
156924db4641Seschrock 	ddump("header:", (caddr_t)header, nbytes);
157024db4641Seschrock 	ddump("data:", (caddr_t)page_data, page_size);
1571724365f7Ssethg 
1572724365f7Ssethg 	/*
1573724365f7Ssethg 	 * Put the header and data together
1574724365f7Ssethg 	 */
1575724365f7Ssethg 	(void) memcpy(mode_select_buf, (caddr_t)header, nbytes);
1576724365f7Ssethg 	(void) memcpy(mode_select_buf + nbytes, page_data, page_size);
1577724365f7Ssethg 	nbytes += page_size;
1578724365f7Ssethg 
1579724365f7Ssethg 	/*
1580724365f7Ssethg 	 * Build and execute the uscsi ioctl
1581724365f7Ssethg 	 */
1582724365f7Ssethg 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
1583724365f7Ssethg 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
1584724365f7Ssethg 	cdb.scc_cmd = SCMD_MODE_SELECT_G1;
1585724365f7Ssethg 	FORMG1COUNT(&cdb, (uint16_t)nbytes);
1586724365f7Ssethg 	cdb.cdb_opaque[1] = (uchar_t)options;
1587724365f7Ssethg 	ucmd.uscsi_cdb = (caddr_t)&cdb;
1588724365f7Ssethg 	ucmd.uscsi_cdblen = CDB_GROUP1;
1589724365f7Ssethg 	ucmd.uscsi_bufaddr = mode_select_buf;
1590724365f7Ssethg 	ucmd.uscsi_buflen = nbytes;
159124db4641Seschrock 	status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
1592724365f7Ssethg 
159324db4641Seschrock 	if (status)
159424db4641Seschrock 		dprintf("Mode select(10) page 0x%x failed\n", page_code);
1595724365f7Ssethg 
1596724365f7Ssethg 	return (status);
1597724365f7Ssethg }
1598724365f7Ssethg 
1599724365f7Ssethg int
uscsi_log_sense(int fd,int page_code,int page_control,caddr_t page_data,int page_size,void * rqbuf,int * rqblen)1600724365f7Ssethg uscsi_log_sense(int fd, int page_code, int page_control, caddr_t page_data,
1601724365f7Ssethg     int page_size, void *rqbuf, int *rqblen)
1602724365f7Ssethg {
160324db4641Seschrock 	caddr_t log_sense_buf;
160424db4641Seschrock 	scsi_log_header_t *hdr;
160524db4641Seschrock 	struct uscsi_cmd ucmd;
160624db4641Seschrock 	union scsi_cdb cdb;
160724db4641Seschrock 	int status;
160824db4641Seschrock 	ushort_t len;
160924db4641Seschrock 	char *pc;
161024db4641Seschrock 
161124db4641Seschrock 	assert(page_size >= 0 && page_size < UINT16_MAX);
161224db4641Seschrock 	assert(page_control == PC_CURRENT || page_control == PC_CHANGEABLE ||
161324db4641Seschrock 	    page_control == PC_DEFAULT || page_control == PC_SAVED);
161424db4641Seschrock 
161524db4641Seschrock 	if (page_size < sizeof (scsi_log_header_t))
1616724365f7Ssethg 		return (-1);
1617724365f7Ssethg 
1618*77ac0eaaSToomas Soome 	log_sense_buf = calloc(1, page_size);
1619*77ac0eaaSToomas Soome 	if (log_sense_buf == NULL)
1620*77ac0eaaSToomas Soome 		return (-1);
1621724365f7Ssethg 
1622724365f7Ssethg 	/*
1623724365f7Ssethg 	 * Build and execute the uscsi ioctl
1624724365f7Ssethg 	 */
1625724365f7Ssethg 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
1626724365f7Ssethg 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
1627724365f7Ssethg 	cdb.scc_cmd = SCMD_LOG_SENSE_G1;
1628724365f7Ssethg 	FORMG1COUNT(&cdb, (uint16_t)page_size);
1629724365f7Ssethg 	cdb.cdb_opaque[2] = page_control | page_code;
1630724365f7Ssethg 	ucmd.uscsi_cdb = (caddr_t)&cdb;
1631724365f7Ssethg 	ucmd.uscsi_cdblen = CDB_GROUP1;
1632724365f7Ssethg 	ucmd.uscsi_bufaddr = log_sense_buf;
1633724365f7Ssethg 	ucmd.uscsi_buflen = page_size;
163424db4641Seschrock 	status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
1635724365f7Ssethg 	if (status) {
163624db4641Seschrock 		dprintf("Log sense page 0x%x failed\n", page_code);
1637*77ac0eaaSToomas Soome 		free(log_sense_buf);
1638724365f7Ssethg 		return (-1);
1639724365f7Ssethg 	}
1640724365f7Ssethg 
1641724365f7Ssethg 	/*
164224db4641Seschrock 	 * Verify that the returned data looks reasonable, then copy it into the
164324db4641Seschrock 	 * user's buffer.
1644724365f7Ssethg 	 */
164524db4641Seschrock 	hdr = (scsi_log_header_t *)log_sense_buf;
1646724365f7Ssethg 
1647724365f7Ssethg 	/*
1648724365f7Ssethg 	 * Ensure we have a host-understandable length field
1649724365f7Ssethg 	 */
165024db4641Seschrock 	len = BE_16(hdr->lh_length);
1651724365f7Ssethg 
165224db4641Seschrock 	if (hdr->lh_code != page_code) {
165324db4641Seschrock 		dprintf("\nLog sense page 0x%x: incorrect page code 0x%x\n",
165424db4641Seschrock 		    page_code, hdr->lh_code);
165524db4641Seschrock 		ddump("Log sense:", log_sense_buf, page_size);
1656*77ac0eaaSToomas Soome 		free(log_sense_buf);
1657724365f7Ssethg 		return (-1);
1658724365f7Ssethg 	}
1659724365f7Ssethg 
166024db4641Seschrock 	ddump("LOG SENSE RAW OUTPUT", log_sense_buf,
166124db4641Seschrock 	    sizeof (scsi_log_header_t) + len);
1662724365f7Ssethg 
1663724365f7Ssethg 	/*
166424db4641Seschrock 	 * Accept up to "page_size" bytes of mode sense data.  This allows us to
166524db4641Seschrock 	 * accept both CCS and SCSI-2 structures, as long as we request the
166624db4641Seschrock 	 * greater of the two.
1667724365f7Ssethg 	 */
1668724365f7Ssethg 	(void) memcpy(page_data, (caddr_t)hdr, len +
166924db4641Seschrock 	    sizeof (scsi_log_header_t));
1670724365f7Ssethg 
1671724365f7Ssethg 	pc = find_string(page_control_strings, page_control);
167224db4641Seschrock 	dprintf("\nLog sense page 0x%x (%s):\n", page_code,
167324db4641Seschrock 	    pc != NULL ? pc : "");
167424db4641Seschrock 	ddump("header:", (caddr_t)hdr,
167524db4641Seschrock 	    sizeof (scsi_log_header_t));
167624db4641Seschrock 	ddump("data:", (caddr_t)hdr +
167724db4641Seschrock 	    sizeof (scsi_log_header_t), len);
1678*77ac0eaaSToomas Soome 	free(log_sense_buf);
1679724365f7Ssethg 
1680724365f7Ssethg 	return (0);
1681724365f7Ssethg }
1682