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