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