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