xref: /illumos-gate/usr/src/cmd/format/ctlr_scsi.c (revision 48bbca81)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright (c) 2016 by Delphix. All rights reserved.
24  */
25 
26 /*
27  * This file contains the routines for embedded scsi disks
28  */
29 #include "global.h"
30 
31 #include <sys/types.h>
32 #include <sys/param.h>
33 #include <sys/ioctl.h>
34 #include <sys/uio.h>
35 #include <sys/fcntl.h>
36 #include <errno.h>
37 #include <memory.h>
38 #include <malloc.h>
39 #include <unistd.h>
40 #include <stdlib.h>
41 #include <values.h>
42 #include <sys/byteorder.h>
43 
44 
45 
46 #include "startup.h"
47 #include "scsi_com.h"
48 #include "misc.h"
49 #include "ctlr_scsi.h"
50 #include "analyze.h"
51 #include "param.h"
52 #include "io.h"
53 
54 
55 #ifndef	DAD_MODE_CACHE_CCS
56 #define	DAD_MODE_CACHE_CCS		0x38
57 #endif /* DAD_MODE_CACHE_CCS */
58 
59 /* format defect header bits */
60 #define	FDH_FOV				0x80
61 #define	FDH_IMMED			0x02
62 
63 #define	SENSE_LEN			20
64 
65 #define	RETRY_DELAY			5
66 
67 #define	PROGRESS_INDICATION_BASE	65536
68 
69 #ifdef	__STDC__
70 /*
71  *	Local prototypes for ANSI C compilers
72  */
73 static int	scsi_format(uint64_t, uint64_t, struct defect_list *);
74 static int	scsi_raw_format(void);
75 static int	scsi_ms_page8(int);
76 static int	scsi_ms_page38(int);
77 static void	scsi_convert_list_to_new(struct defect_list *,
78 			struct scsi_defect_list *, int);
79 static char	*scsi_find_command_name(uint_t);
80 static int	chg_list_affects_page(struct chg_list *, int);
81 static void	scsi_printerr(struct uscsi_cmd *,
82 			struct scsi_extended_sense *, int);
83 static diskaddr_t
84 scsi_extract_sense_info_descr(struct scsi_descr_sense_hdr *sdsp, int rqlen);
85 
86 static void	scsi_print_extended_sense(struct scsi_extended_sense *, int);
87 static void	scsi_print_descr_sense(struct scsi_descr_sense_hdr *, int);
88 
89 static int	test_until_ready(int fd);
90 static int	uscsi_reserve_release(int, int);
91 static int	check_support_for_defects(void);
92 static int	scsi_format_without_defects(void);
93 static int	scsi_ms_page1(int);
94 static int	scsi_ms_page2(int);
95 static int	scsi_ms_page3(int);
96 static int	scsi_ms_page4(int);
97 static int	scsi_repair(uint64_t, int);
98 static int	scsi_read_defect_data(struct defect_list *, int);
99 static int	scsi_ck_format(void);
100 
101 #else	/* __STDC__ */
102 
103 static int	scsi_format();
104 static int	scsi_raw_format();
105 static int	scsi_ms_page8();
106 static int	scsi_ms_page38();
107 static void	scsi_convert_list_to_new();
108 static char	*scsi_find_command_name();
109 static int	chg_list_affects_page();
110 static void	scsi_printerr();
111 static diskaddr_t	scsi_extract_sense_info_descr();
112 static void	scsi_print_extended_sense();
113 static void	scsi_print_descr_sense();
114 
115 static int	test_until_ready();
116 
117 static int	uscsi_reserve_release();
118 static int	check_support_for_defects();
119 static int	scsi_format_without_defects();
120 static int	scsi_ms_page1();
121 static int	scsi_ms_page2();
122 static int	scsi_ms_page3();
123 static int	scsi_ms_page4();
124 static int	scsi_repair();
125 static int	scsi_read_defect_data();
126 static int	scsi_ck_format();
127 
128 #endif	/* __STDC__ */
129 
130 
131 
132 struct	ctlr_ops scsiops = {
133 	scsi_rdwr,
134 	scsi_ck_format,
135 	scsi_format,
136 	scsi_ex_man,
137 	scsi_ex_cur,
138 	scsi_repair,
139 	0,
140 };
141 
142 #define	SCMD_UNKNOWN		0xff
143 
144 /*
145  * Names of commands.  Must have SCMD_UNKNOWN at end of list.
146  */
147 static struct scsi_command_name {
148 	uchar_t command;
149 	char *name;
150 } scsi_command_names[] = {
151 	SCMD_FORMAT,		"format",
152 	SCMD_READ,		"read",
153 	SCMD_WRITE,		"write",
154 	SCMD_READ|SCMD_GROUP1,	"read",
155 	SCMD_WRITE|SCMD_GROUP1,	"write",
156 	SCMD_INQUIRY,		"inquiry",
157 	SCMD_MODE_SELECT,	"mode select",
158 	SCMD_MODE_SENSE,	"mode sense",
159 	SCMD_REASSIGN_BLOCK,	"reassign block",
160 	SCMD_READ_DEFECT_LIST,	"read defect list",
161 	SCMD_UNKNOWN,		"unknown"
162 };
163 
164 
165 /*
166  * Strings for printing mode sense page control values
167  */
168 static slist_t page_control_strings[] = {
169 	{ "current",	"",	MODE_SENSE_PC_CURRENT },
170 	{ "changeable",	"",	MODE_SENSE_PC_CHANGEABLE },
171 	{ "default",	"",	MODE_SENSE_PC_DEFAULT },
172 	{ "saved",	"",	MODE_SENSE_PC_SAVED }
173 };
174 
175 /*
176  * Strings for printing the mode select options
177  */
178 static slist_t mode_select_strings[] = {
179 	{ "",		"",	0 },
180 	{ " (pf)",	"",	MODE_SELECT_PF },
181 	{ " (sp)",	"",	MODE_SELECT_SP },
182 	{ " (pf,sp)",	"",	MODE_SELECT_PF|MODE_SELECT_SP }
183 };
184 
185 static int scsi_format_revolutions = 5;
186 static int scsi_format_timeout = 2*60*60;		/* two hours */
187 
188 /*
189  * READ DEFECT DATA commands is optional as per SCSI-2 spec.
190  * Hence check if the read_defect_data command fails with
191  * Invalid Opcode so that we can give a more meaningful message
192  * to the user.
193  */
194 #define	INVALID_OPCODE	0x20
195 
196 /*
197  * Read or write the disk.
198  */
199 int
200 scsi_rdwr(dir, fd, blkno, secnt, bufaddr, flags, xfercntp)
201 	int	dir;
202 	int	fd;
203 	diskaddr_t	blkno;
204 	int	secnt;
205 	caddr_t bufaddr;
206 	int	flags;
207 	int	*xfercntp;
208 {
209 	struct uscsi_cmd	ucmd;
210 	union scsi_cdb		cdb;
211 	int	max_sectors;
212 	int	rc = 0;
213 
214 	/*
215 	 * If the max xfercnt hasn't been determined start with BUF_SECTS
216 	 * (currently 126 == 63K), otherwise use the xfercnt value
217 	 * my caller saved from the previous invocation.
218 	 */
219 	if (xfercntp == NULL) {
220 		max_sectors = BUF_SECTS;
221 	} else if (*xfercntp == 0) {
222 		max_sectors = BUF_SECTS;
223 		*xfercntp = max_sectors;
224 	} else {
225 		max_sectors = *xfercntp;
226 	}
227 
228 	/*
229 	 * Build and execute the uscsi ioctl.  We build a group0
230 	 * or group1 command as necessary, since some targets
231 	 * do not support group1 commands.
232 	 */
233 	while (secnt)  {
234 		int	nsectors;
235 
236 		nsectors = (max_sectors < secnt) ? max_sectors : secnt;
237 		(void) memset((char *)&ucmd, 0, sizeof (ucmd));
238 		(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
239 		cdb.scc_cmd = (dir == DIR_READ) ? SCMD_READ : SCMD_WRITE;
240 		if (blkno < (2<<20) && nsectors <= 0xff) {
241 			FORMG0ADDR(&cdb, blkno);
242 			FORMG0COUNT(&cdb, nsectors);
243 			ucmd.uscsi_cdblen = CDB_GROUP0;
244 		} else {
245 			if (blkno > 0xffffffff) {
246 				FORMG4LONGADDR(&cdb, blkno);
247 				FORMG4COUNT(&cdb, nsectors);
248 				ucmd.uscsi_cdblen = CDB_GROUP4;
249 				cdb.scc_cmd |= SCMD_GROUP4;
250 			} else {
251 				FORMG1ADDR(&cdb, blkno);
252 				FORMG1COUNT(&cdb, nsectors);
253 				ucmd.uscsi_cdblen = CDB_GROUP1;
254 				cdb.scc_cmd |= SCMD_GROUP1;
255 			}
256 		}
257 		ucmd.uscsi_cdb = (caddr_t)&cdb;
258 		ucmd.uscsi_bufaddr = bufaddr;
259 		ucmd.uscsi_buflen = nsectors * cur_blksz;
260 		rc = uscsi_cmd(fd, &ucmd, flags);
261 
262 		if (rc != 0)
263 			break;
264 
265 		/*
266 		 * check if partial DMA breakup required
267 		 * if so, reduce the request size by half and retry
268 		 * the last request
269 		 */
270 		if (ucmd.uscsi_resid == ucmd.uscsi_buflen) {
271 			max_sectors >>= 1;
272 			if (max_sectors <= 0) {
273 				rc = -1;
274 				break;
275 			}
276 			continue;
277 		}
278 		if (ucmd.uscsi_resid != 0) {
279 			rc = -1;
280 			break;
281 		}
282 
283 		blkno += nsectors;
284 		secnt -= nsectors;
285 		bufaddr += nsectors * cur_blksz;
286 	}
287 
288 	/*
289 	 * If the xfercnt wasn't previously saved or if the
290 	 * new value is smaller than the old value, save the
291 	 * current value in my caller's save area.
292 	 */
293 	if (xfercntp != NULL && max_sectors < *xfercntp) {
294 		if (diag_msg)
295 			err_print("reducing xfercnt %d %d\n",
296 					*xfercntp, max_sectors);
297 		*xfercntp = max_sectors;
298 	}
299 	return (rc);
300 }
301 
302 
303 /*
304  * Check to see if the disk has been formatted.
305  * If we are able to read the first track, we conclude that
306  * the disk has been formatted.
307  */
308 #ifdef i386
309 static int
310 #else /* i386 */
311 static int
312 #endif /* i386 */
313 scsi_ck_format(void)
314 {
315 	int	status;
316 
317 	/*
318 	 * Try to read the first four blocks.
319 	 */
320 	status = scsi_rdwr(DIR_READ, cur_file, (diskaddr_t)0, 4,
321 	    (caddr_t)cur_buf, F_SILENT, NULL);
322 	return (!status);
323 }
324 
325 
326 /*
327  * Format the disk, the whole disk, and nothing but the disk.
328  */
329 /*ARGSUSED*/
330 static int
331 scsi_format(start, end, list)
332 	uint64_t		start;		/* irrelevant for us */
333 	uint64_t		end;
334 	struct defect_list	*list;
335 {
336 	struct uscsi_cmd	ucmd;
337 	union scsi_cdb		cdb;
338 	int			status;
339 	int			flag;
340 	char			rawbuf[MAX_MODE_SENSE_SIZE];
341 	struct scsi_inquiry	*inq;
342 	uint8_t	fmt_prot_info;
343 	uint8_t	prot_field_usage;
344 	uint8_t	param_long_list = 1;
345 	uint8_t	fmt_long_param_header[8];
346 
347 	/*
348 	 * Determine if the target appears to be SCSI-2
349 	 * compliant.  We handle mode sense/mode selects
350 	 * a little differently, depending upon CCS/SCSI-2
351 	 */
352 	if (uscsi_inquiry(cur_file, rawbuf, sizeof (rawbuf))) {
353 		err_print("Inquiry failed\n");
354 		return (-1);
355 	}
356 	inq = (struct scsi_inquiry *)rawbuf;
357 	flag = (inq->inq_rdf == RDF_SCSI2);
358 
359 	/*
360 	 * Reserve the scsi disk before performing mode select and
361 	 * format operations. This will keep other hosts, if any, from
362 	 * touching the disk while we are here.
363 	 */
364 	if (uscsi_reserve_release(cur_file, SCMD_RESERVE)) {
365 		err_print("Reserve failed\n");
366 		return (-1);
367 	}
368 
369 	/*
370 	 * Set up the various SCSI parameters specified before
371 	 * formatting the disk.  Each routine handles the
372 	 * parameters relevant to a particular page.
373 	 * If no parameters are specified for a page, there's
374 	 * no need to do anything.  Otherwise, issue a mode
375 	 * sense for that page.  If a specified parameter
376 	 * differs from the drive's default value, and that
377 	 * parameter is not fixed, then issue a mode select to
378 	 * set the default value for the disk as specified
379 	 * in format.dat.
380 	 */
381 	if (scsi_ms_page1(flag) || scsi_ms_page2(flag) ||
382 		scsi_ms_page4(flag) || scsi_ms_page38(flag) ||
383 			scsi_ms_page8(flag) || scsi_ms_page3(flag)) {
384 		(void) uscsi_reserve_release(cur_file, SCMD_RELEASE);
385 		return (-1);
386 	}
387 
388 	/*
389 	 * If we're debugging the drive, dump every page
390 	 * the device supports, for thorough analysis.
391 	 */
392 	if (option_msg && diag_msg) {
393 		(void) scsi_dump_mode_sense_pages(MODE_SENSE_PC_DEFAULT);
394 		(void) scsi_dump_mode_sense_pages(MODE_SENSE_PC_CURRENT);
395 		(void) scsi_dump_mode_sense_pages(MODE_SENSE_PC_SAVED);
396 		(void) scsi_dump_mode_sense_pages(MODE_SENSE_PC_CHANGEABLE);
397 		err_print("\n");
398 	}
399 
400 	/*
401 	 * Determine the FMTPINFO field in format cdb, and the
402 	 * PROTECTION FIELD USAGE in the long parameter list, via
403 	 * the protection type input by users.
404 	 */
405 	switch (prot_type) {
406 	case PROT_TYPE_0:
407 		fmt_prot_info = 0x00;
408 		prot_field_usage = 0x00;
409 		break;
410 	case PROT_TYPE_1:
411 		fmt_prot_info = 0x02;
412 		prot_field_usage = 0x00;
413 		break;
414 	case PROT_TYPE_2:
415 		fmt_prot_info = 0x03;
416 		prot_field_usage = 0x00;
417 		break;
418 	case PROT_TYPE_3:
419 		fmt_prot_info = 0x03;
420 		prot_field_usage = 0x01;
421 		break;
422 	default:
423 		fmt_print("invalid protection type\n");
424 		return (-1);
425 	}
426 
427 	/*
428 	 * Construct the uscsi format ioctl.  The form depends
429 	 * upon the defect list the user extracted.  If they
430 	 * extracted the "original" list, we format with only
431 	 * the P (manufacturer's defect) list.  Otherwise, we
432 	 * format with both the P and the G (grown) list.
433 	 * To format with the P and G list, we set the fmtData
434 	 * bit, and send an empty list.  To format with the
435 	 * P list only, we also set the cmpLst bit, meaning
436 	 * that the (empty) list we send down is the complete
437 	 * G list, thereby discarding the old G list..
438 	 */
439 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
440 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
441 
442 	cdb.scc_cmd		= SCMD_FORMAT;
443 	ucmd.uscsi_cdb		= (caddr_t)&cdb;
444 	ucmd.uscsi_cdblen	= CDB_GROUP0;
445 	cdb.cdb_opaque[1]	= FPB_DATA;
446 
447 	/*
448 	 * Use the long parameter header in format command,
449 	 * and set the FMTPINFO field., when type 1, 2, 3.
450 	 */
451 	cdb.cdb_opaque[1] |= (param_long_list << 5) | (fmt_prot_info << 6);
452 	(void) memset((char *)fmt_long_param_header, 0,
453 	    sizeof (fmt_long_param_header));
454 
455 	/*
456 	 * Set the PROTECTION FIELD USAGE field in the long
457 	 * parameter list header, which combines with FMTINFO to
458 	 * determine the protection type.
459 	 * The PROTECTION INTERVAL EXPONET field is set default 0.
460 	 * So only one protection information interval is used
461 	 * in type 1, 2, 3.
462 	 */
463 	fmt_long_param_header[0] = prot_field_usage;
464 	fmt_long_param_header[1] = FDH_FOV | FDH_IMMED;
465 	ucmd.uscsi_bufaddr = (caddr_t)fmt_long_param_header;
466 	ucmd.uscsi_buflen = sizeof (fmt_long_param_header);
467 
468 	if ((list->list != NULL) && ((list->flags & LIST_PGLIST) == 0)) {
469 		/*
470 		 * No G list.  The empty list we send down
471 		 * is the complete list.
472 		 */
473 		cdb.cdb_opaque[1] |= FPB_CMPLT;
474 	}
475 
476 	/*
477 	 * Issue the format ioctl
478 	 */
479 	fmt_print("Formatting...\n");
480 	(void) fflush(stdout);
481 	status = uscsi_cmd(cur_file, &ucmd,
482 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
483 
484 	/* check if format with immed was successfully accepted */
485 	if (status == 0) {
486 		/* immed accepted poll to completion */
487 		status = test_until_ready(cur_file);
488 	} else {
489 		/* clear FOV and try again */
490 		(void) memset((char *)fmt_long_param_header, 0,
491 		    sizeof (fmt_long_param_header));
492 		fmt_long_param_header[0] = prot_field_usage;
493 		fmt_long_param_header[1] = FDH_IMMED;
494 		status = uscsi_cmd(cur_file, &ucmd,
495 		    (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
496 		if (status == 0) {
497 			/* immed accepted, poll for progress */
498 			status = test_until_ready(cur_file);
499 		} else {
500 			/*
501 			 * clear defect header and try basecase format
502 			 * command will hang until format complete
503 			 */
504 			(void) memset((char *)fmt_long_param_header, 0,
505 			    sizeof (fmt_long_param_header));
506 			fmt_long_param_header[0] = prot_field_usage;
507 			status = uscsi_cmd(cur_file, &ucmd,
508 			    (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
509 		}
510 	}
511 
512 	/* format failure check					*/
513 	if (status != 0) {
514 		/*
515 		 * formatting failed with fmtdata = 1.
516 		 * Check if defects list command is supported, if it
517 		 * is not supported then use fmtdata = 0.
518 		 * 	From SCSI Spec
519 		 *	    A FmtData bit of zero indicates, the
520 		 *	    source of defect information is not specified.
521 		 * else
522 		 *	proceed to format using with mode selects.
523 		 */
524 		if (!(check_support_for_defects())) {
525 			status = scsi_format_without_defects();
526 		}
527 
528 		if (status != 0) {
529 			fmt_print("Format failed\n");
530 			status = scsi_raw_format();
531 		}
532 	}
533 	(void) uscsi_reserve_release(cur_file, SCMD_RELEASE);
534 	return (status);
535 }
536 
537 /*
538  * Format without any of the standard mode selects ignoring Grown defects list.
539  */
540 static int
541 scsi_raw_format(void)
542 {
543 	struct uscsi_cmd	ucmd;
544 	union scsi_cdb		cdb;
545 	struct scsi_defect_hdr	defect_hdr;
546 	int			status;
547 
548 	fmt_print("\n"
549 	    "Retry of formatting operation without any of the standard\n"
550 	    "mode selects and ignoring disk's Grown Defects list.  The\n"
551 	    "disk may be able to be reformatted this way if an earlier\n"
552 	    "formatting operation was interrupted by a power failure or\n"
553 	    "SCSI bus reset.  The Grown Defects list will be recreated\n"
554 	    "by format verification and surface analysis.\n\n");
555 
556 	if (check("Retry format without mode selects and Grown Defects list")
557 	    != 0) {
558 		return (-1);
559 	}
560 
561 	/*
562 	 * Construct the uscsi format ioctl.
563 	 * To format with the P and G list, we set the fmtData
564 	 * and cmpLst bits to zero.  To format with just the
565 	 * P list, we set the fmtData bit (meaning that we will
566 	 * send down a defect list in the data phase) and the
567 	 * cmpLst bit (meaning that the list we send is the
568 	 * complete G list), and a defect list header with
569 	 * a defect list length of zero.
570 	 */
571 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
572 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
573 	(void) memset((char *)&defect_hdr, 0, sizeof (defect_hdr));
574 
575 	cdb.scc_cmd		= SCMD_FORMAT;
576 	ucmd.uscsi_cdb		= (caddr_t)&cdb;
577 	ucmd.uscsi_cdblen	= CDB_GROUP0;
578 	/* No G list.   Send empty defect list to replace it */
579 	cdb.cdb_opaque[1]	= FPB_DATA | FPB_CMPLT | FPB_BFI;
580 	ucmd.uscsi_bufaddr	= (caddr_t)&defect_hdr;
581 	ucmd.uscsi_buflen	= sizeof (defect_hdr);
582 	defect_hdr.descriptor	= FDH_FOV | FDH_IMMED;
583 
584 	/*
585 	 * Issue the format ioctl
586 	 */
587 	fmt_print("Formatting...\n");
588 	(void) fflush(stdout);
589 	status = uscsi_cmd(cur_file, &ucmd, F_NORMAL);
590 
591 	/* check if format with immed was successfully accepted */
592 	if (status == 0) {
593 		/* immed accepted pool to completion */
594 		status = test_until_ready(cur_file);
595 	} else {
596 		/* clear defect header and try basecase format */
597 		(void) memset((char *)&defect_hdr, 0, sizeof (defect_hdr));
598 		status = uscsi_cmd(cur_file, &ucmd, F_NORMAL);
599 	}
600 
601 	/* fmt_print(status ? "Format failed\n\n" : "Format ok\n\n"); */
602 	return (status);
603 }
604 
605 /*
606  * Estimate the time required for format operation (See 1163770).
607  * format time = (5_revs * p4_heads * p4_cylinders) / p4_rpm
608  * 5 revolutions (correspond to format_time keyword in format.dat file) are:
609  *	1 rev.  for positioning
610  *	2 rev.  for writing the track
611  *	1 rev.  for positioning
612  *	1 rev.  for cerifying the data integrity of the track
613  * The return value is a good estimate on the formatting time in minutes.
614  * Caller should add 50% margin to cover defect management overhead.
615  */
616 int
617 scsi_format_time()
618 {
619 	struct mode_geometry		*page4;
620 	struct scsi_ms_header		header;
621 	int				status;
622 	int				p4_cylinders, p4_heads, p4_rpm;
623 	int				length;
624 	int				format_time;
625 	union {
626 		struct mode_geometry	page4;
627 		char			rawbuf[MAX_MODE_SENSE_SIZE];
628 	} u_page4;
629 
630 
631 	page4 = &u_page4.page4;
632 	(void) memset(&u_page4, 0, sizeof (u_page4));
633 
634 	/*
635 	 * Issue a mode sense to determine the default parameters
636 	 * If it fail, try to use the saved or current instead.
637 	 */
638 	status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
639 	    MODE_SENSE_PC_DEFAULT, (caddr_t)page4,
640 	    MAX_MODE_SENSE_SIZE, &header);
641 
642 	if (status) {
643 		status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
644 		    MODE_SENSE_PC_SAVED, (caddr_t)page4,
645 		    MAX_MODE_SENSE_SIZE, &header);
646 	}
647 	if (status) {
648 		status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
649 		    MODE_SENSE_PC_CURRENT, (caddr_t)page4,
650 		    MAX_MODE_SENSE_SIZE, &header);
651 	}
652 	if (status) {
653 		return (0);
654 	}
655 
656 	/*
657 	 * We only need the common subset between the CCS
658 	 * and SCSI-2 structures, so we can treat both
659 	 * cases identically.
660 	 */
661 	length = MODESENSE_PAGE_LEN(page4);
662 	if (length < MIN_PAGE4_LEN) {
663 		return (0);
664 	}
665 
666 	page4->rpm = BE_16(page4->rpm);
667 	p4_cylinders = (page4->cyl_ub << 16) + (page4->cyl_mb << 8) +
668 	    page4->cyl_lb;
669 	p4_heads = page4->heads;
670 	p4_rpm = page4->rpm;
671 
672 	/*
673 	 * Some drives report 0 for page4->rpm, adjust it to AVG_RPM, 3600.
674 	 */
675 	if (p4_rpm < MIN_RPM || p4_rpm > MAX_RPM) {
676 		err_print("Mode sense page(4) reports rpm value as %d,"
677 		    " adjusting it to %d\n", p4_rpm, AVG_RPM);
678 		p4_rpm = AVG_RPM;
679 	}
680 
681 	if (p4_cylinders <= 0 || p4_heads <= 0)
682 		return (0);
683 
684 	format_time = ((scsi_format_revolutions * p4_heads *
685 	    p4_cylinders) + p4_rpm) / p4_rpm;
686 
687 	if (option_msg && diag_msg) {
688 		err_print("       pcyl:    %d\n", p4_cylinders);
689 		err_print("      heads:    %d\n", p4_heads);
690 		err_print("        rpm:    %d\n", p4_rpm);
691 		err_print("format_time:    %d minutes\n", format_time);
692 	}
693 	return (format_time);
694 }
695 
696 /*
697  * Check disk error recovery parameters via mode sense.
698  * Issue a mode select if we need to change something.
699  */
700 /*ARGSUSED*/
701 static int
702 scsi_ms_page1(scsi2_flag)
703 	int	scsi2_flag;
704 {
705 	struct mode_err_recov		*page1;
706 	struct mode_err_recov		*fixed;
707 	struct scsi_ms_header		header;
708 	struct scsi_ms_header		fixed_hdr;
709 	int				status;
710 	int				tmp1, tmp2;
711 	int				flag;
712 	int				length;
713 	int				sp_flags;
714 	union {
715 		struct mode_err_recov	page1;
716 		char			rawbuf[MAX_MODE_SENSE_SIZE];
717 	} u_page1, u_fixed;
718 
719 
720 	page1 = &u_page1.page1;
721 	fixed = &u_fixed.page1;
722 
723 	/*
724 	 * If debugging, issue mode senses on the default and
725 	 * current values.
726 	 */
727 	if (option_msg && diag_msg) {
728 		(void) uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
729 			MODE_SENSE_PC_DEFAULT, (caddr_t)page1,
730 			MAX_MODE_SENSE_SIZE, &header);
731 		(void) uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
732 			MODE_SENSE_PC_CURRENT, (caddr_t)page1,
733 			MAX_MODE_SENSE_SIZE, &header);
734 	}
735 
736 	/*
737 	 * Issue a mode sense to determine the saved parameters
738 	 * If the saved values fail, use the current instead.
739 	 */
740 	status = uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
741 			MODE_SENSE_PC_SAVED, (caddr_t)page1,
742 			MAX_MODE_SENSE_SIZE, &header);
743 	if (status) {
744 		status = uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
745 			MODE_SENSE_PC_CURRENT, (caddr_t)page1,
746 			MAX_MODE_SENSE_SIZE, &header);
747 		if (status) {
748 			return (0);
749 		}
750 	}
751 
752 	/*
753 	 * We only need the common subset between the CCS
754 	 * and SCSI-2 structures, so we can treat both
755 	 * cases identically.  Whatever the drive gives
756 	 * us, we return to the drive in the mode select,
757 	 * delta'ed by whatever we want to change.
758 	 */
759 	length = MODESENSE_PAGE_LEN(page1);
760 	if (length < MIN_PAGE1_LEN) {
761 		return (0);
762 	}
763 
764 	/*
765 	 * Ask for changeable parameters.
766 	 */
767 	status = uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
768 		MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
769 		MAX_MODE_SENSE_SIZE, &fixed_hdr);
770 	if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE1_LEN) {
771 		return (0);
772 	}
773 
774 	/*
775 	 * We need to issue a mode select only if one or more
776 	 * parameters need to be changed, and those parameters
777 	 * are flagged by the drive as changeable.
778 	 */
779 	flag = 0;
780 	tmp1 = page1->read_retry_count;
781 	tmp2 = page1->write_retry_count;
782 	if (cur_dtype->dtype_options & SUP_READ_RETRIES &&
783 			fixed->read_retry_count != 0) {
784 		flag |= (page1->read_retry_count !=
785 				cur_dtype->dtype_read_retries);
786 		page1->read_retry_count = cur_dtype->dtype_read_retries;
787 	}
788 	if (length > 8) {
789 		if (cur_dtype->dtype_options & SUP_WRITE_RETRIES &&
790 				fixed->write_retry_count != 0) {
791 			flag |= (page1->write_retry_count !=
792 					cur_dtype->dtype_write_retries);
793 			page1->write_retry_count =
794 					cur_dtype->dtype_write_retries;
795 		}
796 	}
797 	/*
798 	 * Report any changes so far...
799 	 */
800 	if (flag && option_msg) {
801 		fmt_print(
802 "PAGE 1: read retries= %d (%d)  write retries= %d (%d)\n",
803 			page1->read_retry_count, tmp1,
804 			page1->write_retry_count, tmp2);
805 	}
806 	/*
807 	 * Apply any changes requested via the change list method
808 	 */
809 	flag |= apply_chg_list(DAD_MODE_ERR_RECOV, length,
810 		(uchar_t *)page1, (uchar_t *)fixed,
811 			cur_dtype->dtype_chglist);
812 	/*
813 	 * If no changes required, do not issue a mode select
814 	 */
815 	if (flag == 0) {
816 		return (0);
817 	}
818 	/*
819 	 * We always want to set the Page Format bit for mode
820 	 * selects.  Set the Save Page bit if the drive indicates
821 	 * that it can save this page via the mode sense.
822 	 */
823 	sp_flags = MODE_SELECT_PF;
824 	if (page1->mode_page.ps) {
825 		sp_flags |= MODE_SELECT_SP;
826 	}
827 	page1->mode_page.ps = 0;
828 	header.mode_header.length = 0;
829 	header.mode_header.device_specific = 0;
830 	status = uscsi_mode_select(cur_file, DAD_MODE_ERR_RECOV,
831 		sp_flags, (caddr_t)page1, length, &header);
832 	if (status && (sp_flags & MODE_SELECT_SP)) {
833 		/* If failed, try not saving mode select params. */
834 		sp_flags &= ~MODE_SELECT_SP;
835 		status = uscsi_mode_select(cur_file, DAD_MODE_ERR_RECOV,
836 			sp_flags, (caddr_t)page1, length, &header);
837 		}
838 	if (status && option_msg) {
839 		err_print("\
840 Warning: Using default error recovery parameters.\n\n");
841 	}
842 
843 	/*
844 	 * If debugging, issue mode senses on the current and
845 	 * saved values, so we can see the result of the mode
846 	 * selects.
847 	 */
848 	if (option_msg && diag_msg) {
849 		(void) uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
850 			MODE_SENSE_PC_CURRENT, (caddr_t)page1,
851 			MAX_MODE_SENSE_SIZE, &header);
852 		(void) uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
853 			MODE_SENSE_PC_SAVED, (caddr_t)page1,
854 			MAX_MODE_SENSE_SIZE, &header);
855 	}
856 
857 	return (0);
858 }
859 
860 /*
861  * Check disk disconnect/reconnect parameters via mode sense.
862  * Issue a mode select if we need to change something.
863  */
864 /*ARGSUSED*/
865 static int
866 scsi_ms_page2(scsi2_flag)
867 	int	scsi2_flag;
868 {
869 	struct mode_disco_reco		*page2;
870 	struct mode_disco_reco		*fixed;
871 	struct scsi_ms_header		header;
872 	struct scsi_ms_header		fixed_hdr;
873 	int				status;
874 	int				flag;
875 	int				length;
876 	int				sp_flags;
877 	union {
878 		struct mode_disco_reco	page2;
879 		char			rawbuf[MAX_MODE_SENSE_SIZE];
880 	} u_page2, u_fixed;
881 
882 	page2 = &u_page2.page2;
883 	fixed = &u_fixed.page2;
884 
885 	/*
886 	 * If debugging, issue mode senses on the default and
887 	 * current values.
888 	 */
889 	if (option_msg && diag_msg) {
890 		(void) uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
891 			MODE_SENSE_PC_DEFAULT, (caddr_t)page2,
892 			MAX_MODE_SENSE_SIZE, &header);
893 		(void) uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
894 			MODE_SENSE_PC_CURRENT, (caddr_t)page2,
895 			MAX_MODE_SENSE_SIZE, &header);
896 	}
897 
898 	/*
899 	 * Issue a mode sense to determine the saved parameters
900 	 * If the saved values fail, use the current instead.
901 	 */
902 	status = uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
903 			MODE_SENSE_PC_SAVED, (caddr_t)page2,
904 			MAX_MODE_SENSE_SIZE, &header);
905 	if (status) {
906 		status = uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
907 			MODE_SENSE_PC_CURRENT, (caddr_t)page2,
908 			MAX_MODE_SENSE_SIZE, &header);
909 		if (status) {
910 			return (0);
911 		}
912 	}
913 
914 	/*
915 	 * We only need the common subset between the CCS
916 	 * and SCSI-2 structures, so we can treat both
917 	 * cases identically.  Whatever the drive gives
918 	 * us, we return to the drive in the mode select,
919 	 * delta'ed by whatever we want to change.
920 	 */
921 	length = MODESENSE_PAGE_LEN(page2);
922 	if (length < MIN_PAGE2_LEN) {
923 		return (0);
924 	}
925 
926 	/*
927 	 * Ask for changeable parameters.
928 	 */
929 	status = uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
930 		MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
931 		MAX_MODE_SENSE_SIZE, &fixed_hdr);
932 	if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE2_LEN) {
933 		return (0);
934 	}
935 
936 	/*
937 	 * We need to issue a mode select only if one or more
938 	 * parameters need to be changed, and those parameters
939 	 * are flagged by the drive as changeable.
940 	 */
941 	flag = 0;
942 	/*
943 	 * Apply any changes requested via the change list method
944 	 */
945 	flag |= apply_chg_list(MODEPAGE_DISCO_RECO, length,
946 		(uchar_t *)page2, (uchar_t *)fixed,
947 			cur_dtype->dtype_chglist);
948 	/*
949 	 * If no changes required, do not issue a mode select
950 	 */
951 	if (flag == 0) {
952 		return (0);
953 	}
954 	/*
955 	 * We always want to set the Page Format bit for mode
956 	 * selects.  Set the Save Page bit if the drive indicates
957 	 * that it can save this page via the mode sense.
958 	 */
959 	sp_flags = MODE_SELECT_PF;
960 	if (page2->mode_page.ps) {
961 		sp_flags |= MODE_SELECT_SP;
962 	}
963 	page2->mode_page.ps = 0;
964 	header.mode_header.length = 0;
965 	header.mode_header.device_specific = 0;
966 	status = uscsi_mode_select(cur_file, MODEPAGE_DISCO_RECO,
967 		MODE_SELECT_SP, (caddr_t)page2, length, &header);
968 	if (status && (sp_flags & MODE_SELECT_SP)) {
969 		/* If failed, try not saving mode select params. */
970 		sp_flags &= ~MODE_SELECT_SP;
971 		status = uscsi_mode_select(cur_file, MODEPAGE_DISCO_RECO,
972 			sp_flags, (caddr_t)page2, length, &header);
973 		}
974 	if (status && option_msg) {
975 		err_print("Warning: Using default .\n\n");
976 	}
977 
978 	/*
979 	 * If debugging, issue mode senses on the current and
980 	 * saved values, so we can see the result of the mode
981 	 * selects.
982 	 */
983 	if (option_msg && diag_msg) {
984 		(void) uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
985 			MODE_SENSE_PC_CURRENT, (caddr_t)page2,
986 			MAX_MODE_SENSE_SIZE, &header);
987 		(void) uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
988 			MODE_SENSE_PC_SAVED, (caddr_t)page2,
989 			MAX_MODE_SENSE_SIZE, &header);
990 	}
991 
992 	return (0);
993 }
994 
995 /*
996  * Check disk format parameters via mode sense.
997  * Issue a mode select if we need to change something.
998  */
999 /*ARGSUSED*/
1000 static int
1001 scsi_ms_page3(scsi2_flag)
1002 	int	scsi2_flag;
1003 {
1004 	struct mode_format		*page3;
1005 	struct mode_format		*fixed;
1006 	struct scsi_ms_header		header;
1007 	struct scsi_ms_header		fixed_hdr;
1008 	int				status;
1009 	int				tmp1, tmp2, tmp3;
1010 	int				tmp4, tmp5, tmp6;
1011 	int				flag;
1012 	int				length;
1013 	int				sp_flags;
1014 	union {
1015 		struct mode_format	page3;
1016 		char			rawbuf[MAX_MODE_SENSE_SIZE];
1017 	} u_page3, u_fixed;
1018 
1019 
1020 	page3 = &u_page3.page3;
1021 	fixed = &u_fixed.page3;
1022 
1023 	/*
1024 	 * If debugging, issue mode senses on the default and
1025 	 * current values.
1026 	 */
1027 	if (option_msg && diag_msg) {
1028 		(void) uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
1029 			MODE_SENSE_PC_DEFAULT, (caddr_t)page3,
1030 			MAX_MODE_SENSE_SIZE, &header);
1031 		(void) uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
1032 			MODE_SENSE_PC_CURRENT, (caddr_t)page3,
1033 			MAX_MODE_SENSE_SIZE, &header);
1034 	}
1035 
1036 	/*
1037 	 * Issue a mode sense to determine the saved parameters
1038 	 * If the saved values fail, use the current instead.
1039 	 */
1040 	status = uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
1041 			MODE_SENSE_PC_SAVED, (caddr_t)page3,
1042 			MAX_MODE_SENSE_SIZE, &header);
1043 	if (status) {
1044 		status = uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
1045 			MODE_SENSE_PC_CURRENT, (caddr_t)page3,
1046 			MAX_MODE_SENSE_SIZE, &header);
1047 		if (status) {
1048 			return (0);
1049 		}
1050 	}
1051 
1052 	/*
1053 	 * We only need the common subset between the CCS
1054 	 * and SCSI-2 structures, so we can treat both
1055 	 * cases identically.  Whatever the drive gives
1056 	 * us, we return to the drive in the mode select,
1057 	 * delta'ed by whatever we want to change.
1058 	 */
1059 	length = MODESENSE_PAGE_LEN(page3);
1060 	if (length < MIN_PAGE3_LEN) {
1061 		return (0);
1062 	}
1063 
1064 	/*
1065 	 * Ask for changeable parameters.
1066 	 */
1067 	status = uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
1068 		MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
1069 		MAX_MODE_SENSE_SIZE, &fixed_hdr);
1070 	if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE3_LEN) {
1071 		return (0);
1072 	}
1073 
1074 	/*
1075 	 * We need to issue a mode select only if one or more
1076 	 * parameters need to be changed, and those parameters
1077 	 * are flagged by the drive as changeable.
1078 	 */
1079 	tmp1 = page3->track_skew;
1080 	tmp2 = page3->cylinder_skew;
1081 	tmp3 = page3->sect_track;
1082 	tmp4 = page3->tracks_per_zone;
1083 	tmp5 = page3->alt_tracks_vol;
1084 	tmp6 = page3->alt_sect_zone;
1085 
1086 	flag = (page3->data_bytes_sect != cur_blksz);
1087 	page3->data_bytes_sect = cur_blksz;
1088 
1089 	flag |= (page3->interleave != 1);
1090 	page3->interleave = 1;
1091 
1092 	if (cur_dtype->dtype_options & SUP_CYLSKEW &&
1093 					fixed->cylinder_skew != 0) {
1094 		flag |= (page3->cylinder_skew != cur_dtype->dtype_cyl_skew);
1095 		page3->cylinder_skew = cur_dtype->dtype_cyl_skew;
1096 	}
1097 	if (cur_dtype->dtype_options & SUP_TRKSKEW &&
1098 					fixed->track_skew != 0) {
1099 		flag |= (page3->track_skew != cur_dtype->dtype_trk_skew);
1100 		page3->track_skew = cur_dtype->dtype_trk_skew;
1101 	}
1102 	if (cur_dtype->dtype_options & SUP_PSECT &&
1103 					fixed->sect_track != 0) {
1104 		flag |= (page3->sect_track != psect);
1105 		page3->sect_track = (ushort_t)psect;
1106 	}
1107 	if (cur_dtype->dtype_options & SUP_TRKS_ZONE &&
1108 					fixed->tracks_per_zone != 0) {
1109 		flag |= (page3->tracks_per_zone != cur_dtype->dtype_trks_zone);
1110 		page3->tracks_per_zone = cur_dtype->dtype_trks_zone;
1111 	}
1112 	if (cur_dtype->dtype_options & SUP_ASECT &&
1113 					fixed->alt_sect_zone != 0) {
1114 		flag |= (page3->alt_sect_zone != cur_dtype->dtype_asect);
1115 		page3->alt_sect_zone = cur_dtype->dtype_asect;
1116 	}
1117 	if (cur_dtype->dtype_options & SUP_ATRKS &&
1118 					fixed->alt_tracks_vol != 0) {
1119 		flag |= (page3->alt_tracks_vol != cur_dtype->dtype_atrks);
1120 		page3->alt_tracks_vol = cur_dtype->dtype_atrks;
1121 	}
1122 	/*
1123 	 * Notify user of any changes so far
1124 	 */
1125 	if (flag && option_msg) {
1126 		fmt_print("PAGE 3: trk skew= %d (%d)   cyl skew= %d (%d)   ",
1127 			page3->track_skew, tmp1, page3->cylinder_skew, tmp2);
1128 		fmt_print("sects/trk= %d (%d)\n", page3->sect_track, tmp3);
1129 		fmt_print("        trks/zone= %d (%d)   alt trks= %d (%d)   ",
1130 			page3->tracks_per_zone, tmp4,
1131 			page3->alt_tracks_vol, tmp5);
1132 		fmt_print("alt sects/zone= %d (%d)\n",
1133 				page3->alt_sect_zone, tmp6);
1134 	}
1135 	/*
1136 	 * Apply any changes requested via the change list method
1137 	 */
1138 	flag |= apply_chg_list(DAD_MODE_FORMAT, length,
1139 		(uchar_t *)page3, (uchar_t *)fixed,
1140 			cur_dtype->dtype_chglist);
1141 	/*
1142 	 * If no changes required, do not issue a mode select
1143 	 */
1144 	if (flag == 0) {
1145 		return (0);
1146 	}
1147 	/*
1148 	 * Issue a mode select
1149 	 */
1150 	/*
1151 	 * We always want to set the Page Format bit for mode
1152 	 * selects.  Set the Save Page bit if the drive indicates
1153 	 * that it can save this page via the mode sense.
1154 	 */
1155 	sp_flags = MODE_SELECT_PF;
1156 	if (page3->mode_page.ps) {
1157 		sp_flags |= MODE_SELECT_SP;
1158 	}
1159 	page3->mode_page.ps = 0;
1160 	header.mode_header.length = 0;
1161 	header.mode_header.device_specific = 0;
1162 	status = uscsi_mode_select(cur_file, DAD_MODE_FORMAT,
1163 		MODE_SELECT_SP, (caddr_t)page3, length, &header);
1164 	if (status && (sp_flags & MODE_SELECT_SP)) {
1165 		/* If failed, try not saving mode select params. */
1166 		sp_flags &= ~MODE_SELECT_SP;
1167 		status = uscsi_mode_select(cur_file, DAD_MODE_FORMAT,
1168 			sp_flags, (caddr_t)page3, length, &header);
1169 		}
1170 	if (status && option_msg) {
1171 		err_print("Warning: Using default drive format parameters.\n");
1172 		err_print("Warning: Drive format may not be correct.\n\n");
1173 	}
1174 
1175 	/*
1176 	 * If debugging, issue mode senses on the current and
1177 	 * saved values, so we can see the result of the mode
1178 	 * selects.
1179 	 */
1180 	if (option_msg && diag_msg) {
1181 		(void) uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
1182 			MODE_SENSE_PC_CURRENT, (caddr_t)page3,
1183 			MAX_MODE_SENSE_SIZE, &header);
1184 		(void) uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
1185 			MODE_SENSE_PC_SAVED, (caddr_t)page3,
1186 			MAX_MODE_SENSE_SIZE, &header);
1187 	}
1188 
1189 	return (0);
1190 }
1191 
1192 /*
1193  * Check disk geometry parameters via mode sense.
1194  * Issue a mode select if we need to change something.
1195  */
1196 /*ARGSUSED*/
1197 static int
1198 scsi_ms_page4(scsi2_flag)
1199 	int	scsi2_flag;
1200 {
1201 	struct mode_geometry		*page4;
1202 	struct mode_geometry		*fixed;
1203 	struct scsi_ms_header		header;
1204 	struct scsi_ms_header		fixed_hdr;
1205 	int				status;
1206 	int				tmp1, tmp2;
1207 	int				flag;
1208 	int				length;
1209 	int				sp_flags;
1210 	union {
1211 		struct mode_geometry	page4;
1212 		char			rawbuf[MAX_MODE_SENSE_SIZE];
1213 	} u_page4, u_fixed;
1214 
1215 	page4 = &u_page4.page4;
1216 	fixed = &u_fixed.page4;
1217 
1218 	/*
1219 	 * If debugging, issue mode senses on the default and
1220 	 * current values.
1221 	 */
1222 	if (option_msg && diag_msg) {
1223 		(void) uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
1224 			MODE_SENSE_PC_DEFAULT, (caddr_t)page4,
1225 			MAX_MODE_SENSE_SIZE, &header);
1226 		(void) uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
1227 			MODE_SENSE_PC_CURRENT, (caddr_t)page4,
1228 			MAX_MODE_SENSE_SIZE, &header);
1229 	}
1230 
1231 	/*
1232 	 * Issue a mode sense to determine the saved parameters
1233 	 * If the saved values fail, use the current instead.
1234 	 */
1235 	status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
1236 			MODE_SENSE_PC_SAVED, (caddr_t)page4,
1237 			MAX_MODE_SENSE_SIZE, &header);
1238 	if (status) {
1239 		status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
1240 			MODE_SENSE_PC_CURRENT, (caddr_t)page4,
1241 			MAX_MODE_SENSE_SIZE, &header);
1242 		if (status) {
1243 			return (0);
1244 		}
1245 	}
1246 
1247 	/*
1248 	 * We only need the common subset between the CCS
1249 	 * and SCSI-2 structures, so we can treat both
1250 	 * cases identically.  Whatever the drive gives
1251 	 * us, we return to the drive in the mode select,
1252 	 * delta'ed by whatever we want to change.
1253 	 */
1254 	length = MODESENSE_PAGE_LEN(page4);
1255 	if (length < MIN_PAGE4_LEN) {
1256 		return (0);
1257 	}
1258 
1259 	/*
1260 	 * Ask for changeable parameters.
1261 	 */
1262 	status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
1263 		MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
1264 		MAX_MODE_SENSE_SIZE, &fixed_hdr);
1265 	if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE4_LEN) {
1266 		return (0);
1267 	}
1268 
1269 	/*
1270 	 * We need to issue a mode select only if one or more
1271 	 * parameters need to be changed, and those parameters
1272 	 * are flagged by the drive as changeable.
1273 	 */
1274 	tmp1 = (page4->cyl_ub << 16) + (page4->cyl_mb << 8) + page4->cyl_lb;
1275 	tmp2 = page4->heads;
1276 
1277 	flag = 0;
1278 	if ((cur_dtype->dtype_options & SUP_PHEAD) && fixed->heads != 0) {
1279 		flag |= (page4->heads != phead);
1280 		page4->heads = phead;
1281 	}
1282 	/*
1283 	 * Notify user of changes so far
1284 	 */
1285 	if (flag && option_msg) {
1286 		fmt_print("PAGE 4:   cylinders= %d    heads= %d (%d)\n",
1287 			tmp1, page4->heads, tmp2);
1288 	}
1289 	/*
1290 	 * Apply any changes requested via the change list method
1291 	 */
1292 	flag |= apply_chg_list(DAD_MODE_GEOMETRY, length,
1293 		(uchar_t *)page4, (uchar_t *)fixed,
1294 			cur_dtype->dtype_chglist);
1295 	/*
1296 	 * If no changes required, do not issue a mode select
1297 	 */
1298 	if (flag == 0) {
1299 		return (0);
1300 	}
1301 	/*
1302 	 * Issue a mode select
1303 	 */
1304 	/*
1305 	 * We always want to set the Page Format bit for mode
1306 	 * selects.  Set the Save Page bit if the drive indicates
1307 	 * that it can save this page via the mode sense.
1308 	 */
1309 	sp_flags = MODE_SELECT_PF;
1310 	if (page4->mode_page.ps) {
1311 		sp_flags |= MODE_SELECT_SP;
1312 	}
1313 	page4->mode_page.ps = 0;
1314 	header.mode_header.length = 0;
1315 	header.mode_header.device_specific = 0;
1316 	status = uscsi_mode_select(cur_file, DAD_MODE_GEOMETRY,
1317 		MODE_SELECT_SP, (caddr_t)page4, length, &header);
1318 	if (status && (sp_flags & MODE_SELECT_SP)) {
1319 		/* If failed, try not saving mode select params. */
1320 		sp_flags &= ~MODE_SELECT_SP;
1321 		status = uscsi_mode_select(cur_file, DAD_MODE_GEOMETRY,
1322 			sp_flags, (caddr_t)page4, length, &header);
1323 		}
1324 	if (status && option_msg) {
1325 		err_print("Warning: Using default drive geometry.\n\n");
1326 	}
1327 
1328 	/*
1329 	 * If debugging, issue mode senses on the current and
1330 	 * saved values, so we can see the result of the mode
1331 	 * selects.
1332 	 */
1333 	if (option_msg && diag_msg) {
1334 		(void) uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
1335 			MODE_SENSE_PC_CURRENT, (caddr_t)page4,
1336 			MAX_MODE_SENSE_SIZE, &header);
1337 		(void) uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
1338 			MODE_SENSE_PC_SAVED, (caddr_t)page4,
1339 			MAX_MODE_SENSE_SIZE, &header);
1340 	}
1341 
1342 	return (0);
1343 }
1344 
1345 /*
1346  * Check SCSI-2 disk cache parameters via mode sense.
1347  * Issue a mode select if we need to change something.
1348  */
1349 /*ARGSUSED*/
1350 static int
1351 scsi_ms_page8(scsi2_flag)
1352 	int	scsi2_flag;
1353 {
1354 	struct mode_cache		*page8;
1355 	struct mode_cache		*fixed;
1356 	struct scsi_ms_header		header;
1357 	struct scsi_ms_header		fixed_hdr;
1358 	int				status;
1359 	int				flag;
1360 	int				length;
1361 	int				sp_flags;
1362 	union {
1363 		struct mode_cache	page8;
1364 		char			rawbuf[MAX_MODE_SENSE_SIZE];
1365 	} u_page8, u_fixed;
1366 
1367 	page8 = &u_page8.page8;
1368 	fixed = &u_fixed.page8;
1369 
1370 	/*
1371 	 * Only SCSI-2 devices support this page
1372 	 */
1373 	if (!scsi2_flag) {
1374 		return (0);
1375 	}
1376 
1377 	/*
1378 	 * If debugging, issue mode senses on the default and
1379 	 * current values.
1380 	 */
1381 	if (option_msg && diag_msg) {
1382 		(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
1383 			MODE_SENSE_PC_DEFAULT, (caddr_t)page8,
1384 			MAX_MODE_SENSE_SIZE, &header);
1385 		(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
1386 			MODE_SENSE_PC_CURRENT, (caddr_t)page8,
1387 			MAX_MODE_SENSE_SIZE, &header);
1388 	}
1389 
1390 	/*
1391 	 * Issue a mode sense to determine the saved parameters
1392 	 * If the saved values fail, use the current instead.
1393 	 */
1394 	status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
1395 			MODE_SENSE_PC_SAVED, (caddr_t)page8,
1396 			MAX_MODE_SENSE_SIZE, &header);
1397 	if (status) {
1398 		status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
1399 			MODE_SENSE_PC_CURRENT, (caddr_t)page8,
1400 			MAX_MODE_SENSE_SIZE, &header);
1401 		if (status) {
1402 			return (0);
1403 		}
1404 	}
1405 
1406 	/*
1407 	 * We only need the common subset between the CCS
1408 	 * and SCSI-2 structures, so we can treat both
1409 	 * cases identically.  Whatever the drive gives
1410 	 * us, we return to the drive in the mode select,
1411 	 * delta'ed by whatever we want to change.
1412 	 */
1413 	length = MODESENSE_PAGE_LEN(page8);
1414 	if (length < MIN_PAGE8_LEN) {
1415 		return (0);
1416 	}
1417 
1418 	/*
1419 	 * Ask for changeable parameters.
1420 	 */
1421 	status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
1422 		MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
1423 		MAX_MODE_SENSE_SIZE, &fixed_hdr);
1424 	if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE8_LEN) {
1425 		return (0);
1426 	}
1427 
1428 	/*
1429 	 * We need to issue a mode select only if one or more
1430 	 * parameters need to be changed, and those parameters
1431 	 * are flagged by the drive as changeable.
1432 	 */
1433 	flag = 0;
1434 	/*
1435 	 * Apply any changes requested via the change list method
1436 	 */
1437 	flag |= apply_chg_list(DAD_MODE_CACHE, length,
1438 		(uchar_t *)page8, (uchar_t *)fixed,
1439 			cur_dtype->dtype_chglist);
1440 	/*
1441 	 * If no changes required, do not issue a mode select
1442 	 */
1443 	if (flag == 0) {
1444 		return (0);
1445 	}
1446 	/*
1447 	 * Issue a mode select
1448 	 */
1449 	/*
1450 	 * We always want to set the Page Format bit for mode
1451 	 * selects.  Set the Save Page bit if the drive indicates
1452 	 * that it can save this page via the mode sense.
1453 	 */
1454 	sp_flags = MODE_SELECT_PF;
1455 	if (page8->mode_page.ps) {
1456 		sp_flags |= MODE_SELECT_SP;
1457 	}
1458 	page8->mode_page.ps = 0;
1459 	header.mode_header.length = 0;
1460 	header.mode_header.device_specific = 0;
1461 	status = uscsi_mode_select(cur_file, DAD_MODE_CACHE,
1462 		sp_flags, (caddr_t)page8, length, &header);
1463 	if (status && (sp_flags & MODE_SELECT_SP)) {
1464 		/* If failed, try not saving mode select params. */
1465 		sp_flags &= ~MODE_SELECT_SP;
1466 		status = uscsi_mode_select(cur_file, DAD_MODE_CACHE,
1467 			sp_flags, (caddr_t)page8, length, &header);
1468 		}
1469 	if (status && option_msg) {
1470 		err_print("\
1471 Warning: Using default SCSI-2 cache parameters.\n\n");
1472 	}
1473 
1474 	/*
1475 	 * If debugging, issue mode senses on the current and
1476 	 * saved values, so we can see the result of the mode
1477 	 * selects.
1478 	 */
1479 	if (option_msg && diag_msg) {
1480 		(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
1481 			MODE_SENSE_PC_CURRENT, (caddr_t)page8,
1482 			MAX_MODE_SENSE_SIZE, &header);
1483 		(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
1484 			MODE_SENSE_PC_SAVED, (caddr_t)page8,
1485 			MAX_MODE_SENSE_SIZE, &header);
1486 	}
1487 
1488 	return (0);
1489 }
1490 
1491 /*
1492  * Check CCS disk cache parameters via mode sense.
1493  * Issue a mode select if we need to change something.
1494  */
1495 /*ARGSUSED*/
1496 static int
1497 scsi_ms_page38(scsi2_flag)
1498 	int	scsi2_flag;
1499 {
1500 	struct mode_cache_ccs		*page38;
1501 	struct mode_cache_ccs		*fixed;
1502 	struct scsi_ms_header		header;
1503 	struct scsi_ms_header		fixed_hdr;
1504 	int				status;
1505 	int				tmp1, tmp2, tmp3, tmp4;
1506 	int				flag;
1507 	int				length;
1508 	int				sp_flags;
1509 	union {
1510 		struct mode_cache_ccs	page38;
1511 		char			rawbuf[MAX_MODE_SENSE_SIZE];
1512 	} u_page38, u_fixed;
1513 
1514 	/*
1515 	 * First, determine if we need to look at page 38 at all.
1516 	 * Not all devices support it.
1517 	 */
1518 	if (((cur_dtype->dtype_options & (SUP_CACHE | SUP_PREFETCH |
1519 		SUP_CACHE_MIN | SUP_CACHE_MAX)) == 0) &&
1520 			(!chg_list_affects_page(cur_dtype->dtype_chglist,
1521 				0x38))) {
1522 		return (0);
1523 	}
1524 
1525 	page38 = &u_page38.page38;
1526 	fixed = &u_fixed.page38;
1527 
1528 	/*
1529 	 * If debugging, issue mode senses on the default and
1530 	 * current values.
1531 	 */
1532 	if (option_msg && diag_msg) {
1533 		(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
1534 			MODE_SENSE_PC_DEFAULT, (caddr_t)page38,
1535 			MAX_MODE_SENSE_SIZE, &header);
1536 		(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
1537 			MODE_SENSE_PC_CURRENT, (caddr_t)page38,
1538 			MAX_MODE_SENSE_SIZE, &header);
1539 	}
1540 
1541 	/*
1542 	 * Issue a mode sense to determine the saved parameters
1543 	 * If the saved values fail, use the current instead.
1544 	 */
1545 	status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
1546 			MODE_SENSE_PC_SAVED, (caddr_t)page38,
1547 			MAX_MODE_SENSE_SIZE, &header);
1548 	if (status) {
1549 		status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
1550 			MODE_SENSE_PC_CURRENT, (caddr_t)page38,
1551 			MAX_MODE_SENSE_SIZE, &header);
1552 		if (status) {
1553 			return (0);
1554 		}
1555 	}
1556 
1557 	/*
1558 	 * We only need the common subset between the CCS
1559 	 * and SCSI-2 structures, so we can treat both
1560 	 * cases identically.  Whatever the drive gives
1561 	 * us, we return to the drive in the mode select,
1562 	 * delta'ed by whatever we want to change.
1563 	 */
1564 	length = MODESENSE_PAGE_LEN(page38);
1565 	if (length < MIN_PAGE38_LEN) {
1566 		return (0);
1567 	}
1568 
1569 	/*
1570 	 * Ask for changeable parameters.
1571 	 */
1572 	status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
1573 		MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
1574 		MAX_MODE_SENSE_SIZE, &fixed_hdr);
1575 	if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE38_LEN) {
1576 		return (0);
1577 	}
1578 
1579 	/*
1580 	 * We need to issue a mode select only if one or more
1581 	 * parameters need to be changed, and those parameters
1582 	 * are flagged by the drive as changeable.
1583 	 */
1584 	tmp1 = page38->mode;
1585 	tmp2 = page38->threshold;
1586 	tmp3 = page38->min_prefetch;
1587 	tmp4 = page38->max_prefetch;
1588 
1589 	flag = 0;
1590 	if ((cur_dtype->dtype_options & SUP_CACHE) &&
1591 			(fixed->mode & cur_dtype->dtype_cache) ==
1592 				cur_dtype->dtype_cache) {
1593 		flag |= (page38->mode != cur_dtype->dtype_cache);
1594 		page38->mode = cur_dtype->dtype_cache;
1595 	}
1596 	if ((cur_dtype->dtype_options & SUP_PREFETCH) &&
1597 		(fixed->threshold & cur_dtype->dtype_threshold) ==
1598 				cur_dtype->dtype_threshold) {
1599 		flag |= (page38->threshold != cur_dtype->dtype_threshold);
1600 		page38->threshold = cur_dtype->dtype_threshold;
1601 	}
1602 	if ((cur_dtype->dtype_options & SUP_CACHE_MIN) &&
1603 		(fixed->min_prefetch & cur_dtype->dtype_prefetch_min) ==
1604 				cur_dtype->dtype_prefetch_min) {
1605 		flag |= (page38->min_prefetch != cur_dtype->dtype_prefetch_min);
1606 		page38->min_prefetch = cur_dtype->dtype_prefetch_min;
1607 	}
1608 	if ((cur_dtype->dtype_options & SUP_CACHE_MAX) &&
1609 		(fixed->max_prefetch & cur_dtype->dtype_prefetch_max) ==
1610 				cur_dtype->dtype_prefetch_max) {
1611 		flag |= (page38->max_prefetch != cur_dtype->dtype_prefetch_max);
1612 		page38->max_prefetch = cur_dtype->dtype_prefetch_max;
1613 	}
1614 	/*
1615 	 * Notify the user of changes up to this point
1616 	 */
1617 	if (flag && option_msg) {
1618 		fmt_print("PAGE 38: cache mode= 0x%x (0x%x)\n",
1619 					page38->mode, tmp1);
1620 		fmt_print("         min. prefetch multiplier= %d   ",
1621 					page38->min_multiplier);
1622 		fmt_print("max. prefetch multiplier= %d\n",
1623 					page38->max_multiplier);
1624 		fmt_print("         threshold= %d (%d)   ",
1625 					page38->threshold, tmp2);
1626 		fmt_print("min. prefetch= %d (%d)   ",
1627 					page38->min_prefetch, tmp3);
1628 		fmt_print("max. prefetch= %d (%d)\n",
1629 					page38->max_prefetch, tmp4);
1630 	}
1631 	/*
1632 	 * Apply any changes requested via the change list method
1633 	 */
1634 	flag |= apply_chg_list(DAD_MODE_CACHE_CCS, length,
1635 		(uchar_t *)page38, (uchar_t *)fixed,
1636 			cur_dtype->dtype_chglist);
1637 	/*
1638 	 * If no changes required, do not issue a mode select
1639 	 */
1640 	if (flag == 0) {
1641 		return (0);
1642 	}
1643 	/*
1644 	 * Issue a mode select
1645 	 *
1646 	 * We always want to set the Page Format bit for mode
1647 	 * selects.  Set the Save Page bit if the drive indicates
1648 	 * that it can save this page via the mode sense.
1649 	 */
1650 	sp_flags = MODE_SELECT_PF;
1651 	if (page38->mode_page.ps) {
1652 		sp_flags |= MODE_SELECT_SP;
1653 	}
1654 	page38->mode_page.ps = 0;
1655 	header.mode_header.length = 0;
1656 	header.mode_header.device_specific = 0;
1657 	status = uscsi_mode_select(cur_file, DAD_MODE_CACHE_CCS,
1658 		sp_flags, (caddr_t)page38, length, &header);
1659 	if (status && (sp_flags & MODE_SELECT_SP)) {
1660 		/* If failed, try not saving mode select params. */
1661 		sp_flags &= ~MODE_SELECT_SP;
1662 		status = uscsi_mode_select(cur_file, DAD_MODE_CACHE_CCS,
1663 			sp_flags, (caddr_t)page38, length, &header);
1664 		}
1665 	if (status && option_msg) {
1666 		err_print("Warning: Using default CCS cache parameters.\n\n");
1667 	}
1668 
1669 	/*
1670 	 * If debugging, issue mode senses on the current and
1671 	 * saved values, so we can see the result of the mode
1672 	 * selects.
1673 	 */
1674 	if (option_msg && diag_msg) {
1675 		(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
1676 			MODE_SENSE_PC_CURRENT, (caddr_t)page38,
1677 			MAX_MODE_SENSE_SIZE, &header);
1678 		(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
1679 			MODE_SENSE_PC_SAVED, (caddr_t)page38,
1680 			MAX_MODE_SENSE_SIZE, &header);
1681 	}
1682 
1683 	return (0);
1684 }
1685 
1686 
1687 /*
1688  * Extract the manufacturer's defect list.
1689  */
1690 int
1691 scsi_ex_man(list)
1692 	struct  defect_list	*list;
1693 {
1694 	int	i;
1695 
1696 	i = scsi_read_defect_data(list, DLD_MAN_DEF_LIST);
1697 	if (i != 0)
1698 		return (i);
1699 	list->flags &= ~LIST_PGLIST;
1700 	return (0);
1701 }
1702 
1703 /*
1704  * Extract the current defect list.
1705  * For embedded scsi drives, this means both the manufacturer's (P)
1706  * and the grown (G) lists.
1707  */
1708 int
1709 scsi_ex_cur(list)
1710 	struct  defect_list *list;
1711 {
1712 	int	i;
1713 
1714 	i = scsi_read_defect_data(list, DLD_GROWN_DEF_LIST|DLD_MAN_DEF_LIST);
1715 	if (i != 0)
1716 		return (i);
1717 	list->flags |= LIST_PGLIST;
1718 	return (0);
1719 }
1720 
1721 
1722 /*
1723  * Extract the grown list only
1724  */
1725 int
1726 scsi_ex_grown(list)
1727 	struct defect_list *list;
1728 {
1729 	int	i;
1730 
1731 	i = scsi_read_defect_data(list, DLD_GROWN_DEF_LIST);
1732 	if (i != 0)
1733 		return (i);
1734 	list->flags |= LIST_PGLIST;
1735 	return (0);
1736 }
1737 
1738 
1739 static int
1740 scsi_read_defect_data(list, pglist_flags)
1741 	struct  defect_list	*list;
1742 	int			pglist_flags;
1743 {
1744 	struct uscsi_cmd	ucmd;
1745 	char			rqbuf[255];
1746 	union scsi_cdb		cdb;
1747 	struct scsi_defect_list	*defects;
1748 	struct scsi_defect_list	def_list;
1749 	struct scsi_defect_hdr	*hdr;
1750 	int			status;
1751 	int			nbytes;
1752 	int			len;	/* returned defect list length */
1753 	struct scsi_extended_sense	*rq;
1754 
1755 	hdr = (struct scsi_defect_hdr *)&def_list;
1756 
1757 	/*
1758 	 * First get length of list by asking for the header only.
1759 	 */
1760 	(void) memset((char *)&def_list, 0, sizeof (def_list));
1761 
1762 	/*
1763 	 * Build and execute the uscsi ioctl
1764 	 */
1765 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
1766 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
1767 	(void) memset((char *)rqbuf, 0, 255);
1768 	cdb.scc_cmd = SCMD_READ_DEFECT_LIST;
1769 	FORMG1COUNT(&cdb, sizeof (struct scsi_defect_hdr));
1770 	cdb.cdb_opaque[2] = pglist_flags | DLD_BFI_FORMAT;
1771 	ucmd.uscsi_cdb = (caddr_t)&cdb;
1772 	ucmd.uscsi_cdblen = CDB_GROUP1;
1773 	ucmd.uscsi_bufaddr = (caddr_t)hdr;
1774 	ucmd.uscsi_buflen = sizeof (struct scsi_defect_hdr);
1775 	ucmd.uscsi_rqbuf = rqbuf;
1776 	ucmd.uscsi_rqlen = sizeof (rqbuf);
1777 	ucmd.uscsi_rqresid = sizeof (rqbuf);
1778 	rq = (struct scsi_extended_sense *)ucmd.uscsi_rqbuf;
1779 
1780 	status = uscsi_cmd(cur_file, &ucmd,
1781 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
1782 
1783 	if (status != 0) {
1784 		/*
1785 		 * check if read_defect_list_is_supported.
1786 		 */
1787 		if (ucmd.uscsi_rqstatus == STATUS_GOOD &&
1788 			rq->es_key == KEY_ILLEGAL_REQUEST &&
1789 					rq->es_add_code == INVALID_OPCODE) {
1790 			err_print("\nWARNING: Current Disk does not support"
1791 				" defect lists. \n");
1792 		} else
1793 		if (option_msg) {
1794 			err_print("No %s defect list.\n",
1795 				pglist_flags & DLD_GROWN_DEF_LIST ?
1796 				"grown" : "manufacturer's");
1797 		}
1798 		return (-1);
1799 	}
1800 
1801 	/*
1802 	 * Read the full list the second time
1803 	 */
1804 	hdr->length = BE_16(hdr->length);
1805 	len = hdr->length;
1806 	nbytes = len + sizeof (struct scsi_defect_hdr);
1807 
1808 	defects = zalloc(nbytes);
1809 	*(struct scsi_defect_hdr *)defects = *(struct scsi_defect_hdr *)hdr;
1810 
1811 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
1812 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
1813 	cdb.scc_cmd = SCMD_READ_DEFECT_LIST;
1814 	FORMG1COUNT(&cdb, nbytes);
1815 	cdb.cdb_opaque[2] = pglist_flags | DLD_BFI_FORMAT;
1816 	ucmd.uscsi_cdb = (caddr_t)&cdb;
1817 	ucmd.uscsi_cdblen = CDB_GROUP1;
1818 	ucmd.uscsi_bufaddr = (caddr_t)defects;
1819 	ucmd.uscsi_buflen = nbytes;
1820 	status = uscsi_cmd(cur_file, &ucmd,
1821 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
1822 
1823 	if (status) {
1824 		err_print("can't read defect list 2nd time");
1825 		destroy_data((char *)defects);
1826 		return (-1);
1827 	}
1828 
1829 	defects->length = BE_16(defects->length);
1830 
1831 	if (len != hdr->length) {
1832 		err_print("not enough defects");
1833 		destroy_data((char *)defects);
1834 		return (-1);
1835 	}
1836 	scsi_convert_list_to_new(list, (struct scsi_defect_list *)defects,
1837 			DLD_BFI_FORMAT);
1838 	destroy_data((char *)defects);
1839 	return (0);
1840 }
1841 
1842 
1843 /*
1844  * Map a block.
1845  */
1846 /*ARGSUSED*/
1847 static int
1848 scsi_repair(bn, flag)
1849 	uint64_t	bn;
1850 	int		flag;
1851 {
1852 	struct uscsi_cmd		ucmd;
1853 	union scsi_cdb			cdb;
1854 	struct scsi_reassign_blk	defect_list;
1855 
1856 	/*
1857 	 * Build and execute the uscsi ioctl
1858 	 */
1859 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
1860 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
1861 	(void) memset((char *)&defect_list, 0,
1862 		sizeof (struct scsi_reassign_blk));
1863 	cdb.scc_cmd = SCMD_REASSIGN_BLOCK;
1864 	ucmd.uscsi_cdb = (caddr_t)&cdb;
1865 	ucmd.uscsi_cdblen = CDB_GROUP0;
1866 	ucmd.uscsi_bufaddr = (caddr_t)&defect_list;
1867 	ucmd.uscsi_buflen = sizeof (struct scsi_reassign_blk);
1868 	defect_list.length = sizeof (defect_list.defect);
1869 	defect_list.length = BE_16(defect_list.length);
1870 	defect_list.defect = bn;
1871 	defect_list.defect = BE_32(defect_list.defect);
1872 	return (uscsi_cmd(cur_file, &ucmd,
1873 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT));
1874 }
1875 
1876 /*
1877  * Convert a SCSI-style defect list to our generic format.
1878  * We can handle different format lists.
1879  */
1880 static void
1881 scsi_convert_list_to_new(list, def_list, list_format)
1882 	struct defect_list		*list;
1883 	struct scsi_defect_list		*def_list;
1884 	int				 list_format;
1885 {
1886 	register struct scsi_bfi_defect	*old_defect, *old_defect1;
1887 	register struct defect_entry	*new_defect;
1888 	register int			len, new_len, obfi, nbfi;
1889 	register int			i;
1890 	int				old_cyl, new_cyl;
1891 	unsigned char			*cp;
1892 
1893 
1894 	switch (list_format) {
1895 
1896 	case DLD_BFI_FORMAT:
1897 		/*
1898 		 * Allocate space for the rest of the list.
1899 		 */
1900 		len = def_list->length / sizeof (struct scsi_bfi_defect);
1901 		old_defect = def_list->list;
1902 		new_defect = (struct defect_entry *)
1903 		    zalloc(deflist_size(cur_blksz, len) *
1904 		    cur_blksz);
1905 
1906 		list->header.magicno = (uint_t)DEFECT_MAGIC;
1907 		list->list = new_defect;
1908 
1909 		for (i = 0, new_len = 0; i < len; new_defect++, new_len++) {
1910 			cp = (unsigned char *)old_defect;
1911 			new_defect->cyl = (cp[0] << 16 | cp[1] << 8) | cp[2];
1912 			new_defect->head = old_defect->head;
1913 			new_defect->bfi = (int)old_defect->bytes_from_index;
1914 			new_defect->bfi = BE_32(new_defect->bfi);
1915 			new_defect->nbits = 0;	/* size of defect */
1916 			old_defect1 = old_defect++;
1917 			i++;
1918 			/*
1919 			 * Since we reached the end of the list, old_defect
1920 			 * now points to an invalid reference, since it got
1921 			 * incremented in the above operation. So we don't
1922 			 * need to proceed further. new_len needs to be
1923 			 * incremented to account for the last element.
1924 			 */
1925 			if (i == len) {
1926 				new_len++;
1927 				break;
1928 			}
1929 			obfi = new_defect->bfi;
1930 			nbfi = (int)old_defect->bytes_from_index;
1931 			nbfi = BE_32(nbfi);
1932 
1933 			old_cyl =  new_defect->cyl;
1934 			cp = (unsigned char *)old_defect;
1935 			new_cyl = (cp[0] << 16 | cp[1] << 8) | cp[2];
1936 
1937 
1938 			/*
1939 			 * Merge adjacent contiguous defect entries into one
1940 			 * and update the length of the defect
1941 			 */
1942 			while ((i < len) &&
1943 				(old_cyl  == new_cyl) &&
1944 				(old_defect->head == old_defect1->head) &&
1945 				(nbfi == (obfi + BITSPERBYTE))) {
1946 				old_defect1 = old_defect++;
1947 				cp = (unsigned char *)old_defect;
1948 				new_cyl = (cp[0] << 16 | cp[1] << 8) | cp[2];
1949 				obfi = (int)old_defect1->bytes_from_index;
1950 				obfi = BE_32(obfi);
1951 				nbfi = (int)old_defect->bytes_from_index;
1952 				nbfi = BE_32(nbfi);
1953 				new_defect->nbits += (8*BITSPERBYTE);
1954 				i++;
1955 			}
1956 		}
1957 
1958 		list->header.count = new_len;
1959 		break;
1960 
1961 	default:
1962 		err_print("scsi_convert_list_to_new: can't deal with it\n");
1963 		exit(0);
1964 		/*NOTREACHED*/
1965 	}
1966 
1967 	(void) checkdefsum(list, CK_MAKESUM);
1968 }
1969 
1970 
1971 
1972 /*
1973  * Execute a command and determine the result.
1974  * Uses the "uscsi" ioctl interface, which is
1975  * fully supported.
1976  *
1977  * If the user wants request sense data to be returned
1978  * in case of error then , the "uscsi_cmd" structure
1979  * should have the request sense buffer allocated in
1980  * uscsi_rqbuf.
1981  *
1982  */
1983 int
1984 uscsi_cmd(fd, ucmd, flags)
1985 	int			fd;
1986 	struct uscsi_cmd	*ucmd;
1987 	int			flags;
1988 {
1989 	struct scsi_extended_sense	*rq;
1990 	char				rqbuf[255];
1991 	int				status;
1992 	int				rqlen;
1993 	int				timeout = 0;
1994 
1995 	/*
1996 	 * Set function flags for driver.
1997 	 */
1998 	ucmd->uscsi_flags = USCSI_ISOLATE;
1999 	if (flags & F_SILENT) {
2000 		ucmd->uscsi_flags |= USCSI_SILENT;
2001 	}
2002 	if (flags & F_RQENABLE) {
2003 		ucmd->uscsi_flags |= USCSI_RQENABLE;
2004 	}
2005 
2006 	/*
2007 	 * If this command will perform a read, set the USCSI_READ flag
2008 	 */
2009 	if (ucmd->uscsi_buflen > 0) {
2010 		/*
2011 		 * uscsi_cdb is declared as a caddr_t, so any CDB
2012 		 * command byte with the MSB set will result in a
2013 		 * compiler error unless we cast to an unsigned value.
2014 		 */
2015 		switch ((uint8_t)ucmd->uscsi_cdb[0]) {
2016 		case SCMD_READ:
2017 		case SCMD_READ|SCMD_GROUP1:
2018 		case SCMD_READ|SCMD_GROUP4:
2019 		case SCMD_MODE_SENSE:
2020 		case SCMD_INQUIRY:
2021 		case SCMD_READ_DEFECT_LIST:
2022 		case SCMD_READ_CAPACITY:
2023 		case SCMD_SVC_ACTION_IN_G4:
2024 			ucmd->uscsi_flags |= USCSI_READ;
2025 			break;
2026 		}
2027 	}
2028 
2029 	/*
2030 	 * Set timeout: 30 seconds for all commands except format
2031 	 */
2032 	switch (ucmd->uscsi_cdb[0]) {
2033 	case SCMD_FORMAT:
2034 		if (ucmd->uscsi_timeout == 0) {
2035 			ucmd->uscsi_timeout = scsi_format_timeout;
2036 			/*
2037 			 * Get the timeout value computed using page4 geometry.
2038 			 * add 50% margin to cover defect management overhead.
2039 			 * add another 50% margin to have a safe timeout.
2040 			 * If it exceeds 2 hours then use this value.
2041 			 */
2042 			if ((timeout = scsi_format_time()) > 0) {
2043 				timeout *= 60;	/* convert to seconds */
2044 				timeout += timeout;
2045 				/*
2046 				 * formatting drives with huge capacity
2047 				 * will cause these heuristics to come
2048 				 * up with times that overflow ~9 hours
2049 				 */
2050 				if (timeout > SHRT_MAX)
2051 					timeout = SHRT_MAX;
2052 				if (timeout > scsi_format_timeout)
2053 					ucmd->uscsi_timeout = timeout;
2054 			}
2055 		}
2056 		if (option_msg && diag_msg) {
2057 			err_print("format_timeout set to %d seconds, %d"
2058 				" required\n", ucmd->uscsi_timeout, timeout);
2059 		}
2060 		break;
2061 
2062 	default:
2063 		ucmd->uscsi_timeout = 30;		/* 30 seconds */
2064 		break;
2065 	}
2066 
2067 	/*
2068 	 * Set up Request Sense buffer
2069 	 */
2070 	ucmd->uscsi_flags |= USCSI_RQENABLE;
2071 
2072 	if (ucmd->uscsi_rqbuf == NULL)  {
2073 		ucmd->uscsi_rqbuf = rqbuf;
2074 		ucmd->uscsi_rqlen = sizeof (rqbuf);
2075 		ucmd->uscsi_rqresid = sizeof (rqbuf);
2076 	}
2077 	ucmd->uscsi_rqstatus = IMPOSSIBLE_SCSI_STATUS;
2078 
2079 	/*
2080 	 * Clear global error state
2081 	 */
2082 	media_error = 0;
2083 
2084 	/*
2085 	 * Execute the ioctl
2086 	 */
2087 	status = ioctl(fd, USCSICMD, ucmd);
2088 	if (status == 0 && ucmd->uscsi_status == 0) {
2089 		return (status);
2090 	}
2091 
2092 	/*
2093 	 * Check the status and return appropriate errors if the disk is
2094 	 * unavailable (could be formatting) or reserved (by other host).
2095 	 * In either case we can not talk to the disk now.
2096 	 */
2097 	if (status == -1 && errno == EAGAIN) {
2098 		disk_error = DISK_STAT_UNAVAILABLE;
2099 		return (DSK_UNAVAILABLE);
2100 	}
2101 	if ((ucmd->uscsi_status & STATUS_MASK) == STATUS_RESERVATION_CONFLICT) {
2102 		disk_error = DISK_STAT_RESERVED;
2103 		return (DSK_RESERVED);
2104 	}
2105 	/*
2106 	 * Check for physically removed or completely unresponsive drive
2107 	 */
2108 	if (status == -1 && !ucmd->uscsi_status && errno == EIO) {
2109 		disk_error = DISK_STAT_UNAVAILABLE;
2110 		return (DSK_UNAVAILABLE);
2111 	}
2112 
2113 	/*
2114 	 * If an automatic Request Sense gave us valid
2115 	 * info about the error, we may be able to use
2116 	 * that to print a reasonable error msg.
2117 	 */
2118 	if (ucmd->uscsi_rqstatus == IMPOSSIBLE_SCSI_STATUS) {
2119 		if (option_msg && diag_msg) {
2120 			err_print("No request sense for command %s\n",
2121 				scsi_find_command_name(ucmd->uscsi_cdb[0]));
2122 		}
2123 		return (-1);
2124 	}
2125 	if (ucmd->uscsi_rqstatus != STATUS_GOOD) {
2126 		if (option_msg && diag_msg) {
2127 			err_print("Request sense status for command %s: 0x%x\n",
2128 				scsi_find_command_name(ucmd->uscsi_cdb[0]),
2129 				ucmd->uscsi_rqstatus);
2130 		}
2131 		return (-1);
2132 	}
2133 	rq = (struct scsi_extended_sense *)ucmd->uscsi_rqbuf;
2134 	rqlen = ucmd->uscsi_rqlen - ucmd->uscsi_rqresid;
2135 	if ((((int)rq->es_add_len) + 8) < MIN_REQUEST_SENSE_LEN ||
2136 			rq->es_class != CLASS_EXTENDED_SENSE ||
2137 				rqlen < MIN_REQUEST_SENSE_LEN) {
2138 		if (option_msg) {
2139 			err_print("Request sense for command %s failed\n",
2140 				scsi_find_command_name(ucmd->uscsi_cdb[0]));
2141 		}
2142 		if (option_msg && diag_msg) {
2143 			err_print("Sense data:\n");
2144 			dump("", (caddr_t)rqbuf, rqlen, HEX_ONLY);
2145 		}
2146 		if (errno == EIO) {
2147 			disk_error = DISK_STAT_UNAVAILABLE;
2148 			return (DSK_UNAVAILABLE);
2149 		} else {
2150 			return (-1);
2151 		}
2152 	}
2153 
2154 	/*
2155 	 * If the failed command is a Mode Select, and the
2156 	 * target is indicating that it has rounded one of
2157 	 * the mode select parameters, as defined in the SCSI-2
2158 	 * specification, then we should accept the command
2159 	 * as successful.
2160 	 */
2161 	if (ucmd->uscsi_cdb[0] == SCMD_MODE_SELECT) {
2162 		if (rq->es_key == KEY_RECOVERABLE_ERROR &&
2163 			rq->es_add_code == ROUNDED_PARAMETER &&
2164 			rq->es_qual_code == 0) {
2165 				return (0);
2166 		}
2167 	}
2168 
2169 	switch (rq->es_key) {
2170 	case KEY_NOT_READY:
2171 		disk_error = DISK_STAT_NOTREADY;
2172 		break;
2173 	case KEY_DATA_PROTECT:
2174 		disk_error = DISK_STAT_DATA_PROTECT;
2175 		break;
2176 	}
2177 
2178 	if (flags & F_ALLERRS) {
2179 		media_error = (rq->es_key == KEY_MEDIUM_ERROR);
2180 	}
2181 	if (!(flags & F_SILENT) || option_msg) {
2182 		scsi_printerr(ucmd, rq, rqlen);
2183 	}
2184 	if ((rq->es_key != KEY_RECOVERABLE_ERROR) || (flags & F_ALLERRS)) {
2185 		return (-1);
2186 	}
2187 
2188 	if (status == -1 && errno == EIO) {
2189 		disk_error = DISK_STAT_UNAVAILABLE;
2190 		return (DSK_UNAVAILABLE);
2191 	}
2192 
2193 	return (0);
2194 }
2195 
2196 
2197 /*
2198  * Execute a uscsi mode sense command.
2199  * This can only be used to return one page at a time.
2200  * Return the mode header/block descriptor and the actual
2201  * page data separately - this allows us to support
2202  * devices which return either 0 or 1 block descriptors.
2203  * Whatever a device gives us in the mode header/block descriptor
2204  * will be returned to it upon subsequent mode selects.
2205  */
2206 int
2207 uscsi_mode_sense(fd, page_code, page_control, page_data, page_size, header)
2208 	int	fd;			/* file descriptor */
2209 	int	page_code;		/* requested page number */
2210 	int	page_control;		/* current, changeable, etc. */
2211 	caddr_t	page_data;		/* place received data here */
2212 	int	page_size;		/* size of page_data */
2213 	struct	scsi_ms_header *header;	/* mode header/block descriptor */
2214 {
2215 	caddr_t			mode_sense_buf;
2216 	struct mode_header	*hdr;
2217 	struct mode_page	*pg;
2218 	int			nbytes;
2219 	struct uscsi_cmd	ucmd;
2220 	union scsi_cdb		cdb;
2221 	int			status;
2222 	int			maximum;
2223 
2224 	assert(page_size >= 0 && page_size < 256);
2225 	assert(page_control == MODE_SENSE_PC_CURRENT ||
2226 		page_control == MODE_SENSE_PC_CHANGEABLE ||
2227 			page_control == MODE_SENSE_PC_DEFAULT ||
2228 				page_control == MODE_SENSE_PC_SAVED);
2229 	/*
2230 	 * Allocate a buffer for the mode sense headers
2231 	 * and mode sense data itself.
2232 	 */
2233 	nbytes = sizeof (struct block_descriptor) +
2234 				sizeof (struct mode_header) + page_size;
2235 	nbytes = page_size;
2236 	if ((mode_sense_buf = malloc((uint_t)nbytes)) == NULL) {
2237 		err_print("cannot malloc %d bytes\n", nbytes);
2238 		return (-1);
2239 	}
2240 
2241 	/*
2242 	 * Build and execute the uscsi ioctl
2243 	 */
2244 	(void) memset(mode_sense_buf, 0, nbytes);
2245 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
2246 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2247 	cdb.scc_cmd = SCMD_MODE_SENSE;
2248 	FORMG0COUNT(&cdb, (uchar_t)nbytes);
2249 	cdb.cdb_opaque[2] = page_control | page_code;
2250 	ucmd.uscsi_cdb = (caddr_t)&cdb;
2251 	ucmd.uscsi_cdblen = CDB_GROUP0;
2252 	ucmd.uscsi_bufaddr = mode_sense_buf;
2253 	ucmd.uscsi_buflen = nbytes;
2254 	status = uscsi_cmd(fd, &ucmd,
2255 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2256 	if (status) {
2257 		if (option_msg) {
2258 			err_print("Mode sense page 0x%x failed\n",
2259 				page_code);
2260 		}
2261 		free(mode_sense_buf);
2262 		return (-1);
2263 	}
2264 
2265 	/*
2266 	 * Verify that the returned data looks reasonabled,
2267 	 * find the actual page data, and copy it into the
2268 	 * user's buffer.  Copy the mode_header and block_descriptor
2269 	 * into the header structure, which can then be used to
2270 	 * return the same data to the drive when issuing a mode select.
2271 	 */
2272 	hdr = (struct mode_header *)mode_sense_buf;
2273 	(void) memset((caddr_t)header, 0, sizeof (struct scsi_ms_header));
2274 	if (hdr->bdesc_length != sizeof (struct block_descriptor) &&
2275 				hdr->bdesc_length != 0) {
2276 		if (option_msg) {
2277 			err_print("\
2278 \nMode sense page 0x%x: block descriptor length %d incorrect\n",
2279 				page_code, hdr->bdesc_length);
2280 			if (diag_msg)
2281 				dump("Mode sense: ", mode_sense_buf,
2282 					nbytes, HEX_ONLY);
2283 		}
2284 		free(mode_sense_buf);
2285 		return (-1);
2286 	}
2287 	(void) memcpy((caddr_t)header, mode_sense_buf,
2288 		(int) (sizeof (struct mode_header) + hdr->bdesc_length));
2289 	pg = (struct mode_page *)((ulong_t)mode_sense_buf +
2290 		sizeof (struct mode_header) + hdr->bdesc_length);
2291 	if (pg->code != page_code) {
2292 		if (option_msg) {
2293 			err_print("\
2294 \nMode sense page 0x%x: incorrect page code 0x%x\n",
2295 				page_code, pg->code);
2296 			if (diag_msg)
2297 				dump("Mode sense: ", mode_sense_buf,
2298 					nbytes, HEX_ONLY);
2299 		}
2300 		free(mode_sense_buf);
2301 		return (-1);
2302 	}
2303 	/*
2304 	 * Accept up to "page_size" bytes of mode sense data.
2305 	 * This allows us to accept both CCS and SCSI-2
2306 	 * structures, as long as we request the greater
2307 	 * of the two.
2308 	 */
2309 	maximum = page_size - sizeof (struct mode_page) - hdr->bdesc_length;
2310 	if (((int)pg->length) > maximum) {
2311 		if (option_msg) {
2312 			err_print("\
2313 Mode sense page 0x%x: incorrect page length %d - expected max %d\n",
2314 				page_code, pg->length, maximum);
2315 			if (diag_msg)
2316 				dump("Mode sense: ", mode_sense_buf,
2317 					nbytes, HEX_ONLY);
2318 		}
2319 		free(mode_sense_buf);
2320 		return (-1);
2321 	}
2322 
2323 	(void) memcpy(page_data, (caddr_t)pg, MODESENSE_PAGE_LEN(pg));
2324 
2325 	if (option_msg && diag_msg) {
2326 		char *pc = find_string(page_control_strings, page_control);
2327 		err_print("\nMode sense page 0x%x (%s):\n", page_code,
2328 			pc != NULL ? pc : "");
2329 		dump("header: ", (caddr_t)header,
2330 			sizeof (struct scsi_ms_header), HEX_ONLY);
2331 		dump("data:   ", page_data,
2332 			MODESENSE_PAGE_LEN(pg), HEX_ONLY);
2333 	}
2334 
2335 	free(mode_sense_buf);
2336 	return (0);
2337 }
2338 
2339 
2340 /*
2341  * Execute a uscsi mode select command.
2342  */
2343 int
2344 uscsi_mode_select(fd, page_code, options, page_data, page_size, header)
2345 	int	fd;			/* file descriptor */
2346 	int	page_code;		/* mode select page */
2347 	int	options;		/* save page/page format */
2348 	caddr_t	page_data;		/* place received data here */
2349 	int	page_size;		/* size of page_data */
2350 	struct	scsi_ms_header *header;	/* mode header/block descriptor */
2351 {
2352 	caddr_t				mode_select_buf;
2353 	int				nbytes;
2354 	struct uscsi_cmd		ucmd;
2355 	union scsi_cdb			cdb;
2356 	int				status;
2357 
2358 	assert(((struct mode_page *)page_data)->ps == 0);
2359 	assert(header->mode_header.length == 0);
2360 	assert(header->mode_header.device_specific == 0);
2361 	assert((options & ~(MODE_SELECT_SP|MODE_SELECT_PF)) == 0);
2362 
2363 	/*
2364 	 * Allocate a buffer for the mode select header and data
2365 	 */
2366 	nbytes = sizeof (struct block_descriptor) +
2367 				sizeof (struct mode_header) + page_size;
2368 	if ((mode_select_buf = malloc((uint_t)nbytes)) == NULL) {
2369 		err_print("cannot malloc %d bytes\n", nbytes);
2370 		return (-1);
2371 	}
2372 
2373 	/*
2374 	 * Build the mode select data out of the header and page data
2375 	 * This allows us to support devices which return either
2376 	 * 0 or 1 block descriptors.
2377 	 */
2378 	(void) memset(mode_select_buf, 0, nbytes);
2379 	nbytes = sizeof (struct mode_header);
2380 	if (header->mode_header.bdesc_length ==
2381 				sizeof (struct block_descriptor)) {
2382 		nbytes += sizeof (struct block_descriptor);
2383 	}
2384 
2385 	/*
2386 	 * Dump the structures if anyone's interested
2387 	 */
2388 	if (option_msg && diag_msg) {
2389 		char *s;
2390 		s = find_string(mode_select_strings,
2391 			options & (MODE_SELECT_SP|MODE_SELECT_PF));
2392 		err_print("\nMode select page 0x%x%s:\n", page_code,
2393 			s != NULL ? s : "");
2394 		dump("header: ", (caddr_t)header,
2395 			nbytes, HEX_ONLY);
2396 		dump("data:   ", (caddr_t)page_data,
2397 			page_size, HEX_ONLY);
2398 	}
2399 
2400 	/*
2401 	 * Fix the code for byte ordering
2402 	 */
2403 
2404 	switch (page_code) {
2405 	case  DAD_MODE_ERR_RECOV:
2406 		{
2407 		struct mode_err_recov *pd;
2408 		pd = (struct mode_err_recov *)(void *)page_data;
2409 		pd->recovery_time_limit = BE_16(pd->recovery_time_limit);
2410 		break;
2411 		}
2412 	case MODEPAGE_DISCO_RECO:
2413 		{
2414 		struct mode_disco_reco *pd;
2415 		pd = (struct mode_disco_reco *)(void *)page_data;
2416 		pd->bus_inactivity_limit = BE_16(pd->bus_inactivity_limit);
2417 		pd->disconect_time_limit = BE_16(pd->disconect_time_limit);
2418 		pd->connect_time_limit = BE_16(pd->connect_time_limit);
2419 		pd->max_burst_size = BE_16(pd->max_burst_size);
2420 		break;
2421 		}
2422 	case DAD_MODE_FORMAT:
2423 		{
2424 		struct mode_format *pd;
2425 		pd = (struct mode_format *)(void *)page_data;
2426 		pd->tracks_per_zone = BE_16(pd->tracks_per_zone);
2427 		pd->alt_sect_zone = BE_16(pd->alt_sect_zone);
2428 		pd->alt_tracks_zone = BE_16(pd->alt_tracks_zone);
2429 		pd->alt_tracks_vol = BE_16(pd->alt_tracks_vol);
2430 		pd->sect_track = BE_16(pd->sect_track);
2431 		pd->data_bytes_sect = BE_16(pd->data_bytes_sect);
2432 		pd->interleave = BE_16(pd->interleave);
2433 		pd->track_skew = BE_16(pd->track_skew);
2434 		pd->cylinder_skew = BE_16(pd->cylinder_skew);
2435 		break;
2436 		}
2437 	case DAD_MODE_GEOMETRY:
2438 		{
2439 		struct mode_geometry *pd;
2440 		pd = (struct mode_geometry *)(void *)page_data;
2441 		pd->step_rate = BE_16(pd->step_rate);
2442 		pd->rpm = BE_16(pd->rpm);
2443 		break;
2444 		}
2445 	case DAD_MODE_CACHE:
2446 		{
2447 		struct mode_cache *pd;
2448 		pd = (struct mode_cache *)(void *)page_data;
2449 		pd->dis_prefetch_len = BE_16(pd->dis_prefetch_len);
2450 		pd->min_prefetch = BE_16(pd->min_prefetch);
2451 		pd->max_prefetch = BE_16(pd->max_prefetch);
2452 		pd->prefetch_ceiling = BE_16(pd->prefetch_ceiling);
2453 		break;
2454 		}
2455 	case MODEPAGE_PDEVICE:
2456 		{
2457 		struct mode_pdevice *pd;
2458 		pd = (struct mode_pdevice *)(void *)page_data;
2459 		pd->if_ident = BE_16(pd->if_ident);
2460 		break;
2461 		}
2462 	case MODEPAGE_CTRL_MODE:
2463 		{
2464 		struct mode_control *pd;
2465 		pd = (struct mode_control *)(void *)page_data;
2466 		pd->ready_aen_holdoff = BE_16(pd->ready_aen_holdoff);
2467 		break;
2468 		}
2469 	}
2470 
2471 	/*
2472 	 * Put the header and data together
2473 	 */
2474 	(void) memcpy(mode_select_buf, (caddr_t)header, nbytes);
2475 	(void) memcpy(mode_select_buf + nbytes, page_data, page_size);
2476 	nbytes += page_size;
2477 
2478 	/*
2479 	 * Build and execute the uscsi ioctl
2480 	 */
2481 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
2482 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2483 	cdb.scc_cmd = SCMD_MODE_SELECT;
2484 	FORMG0COUNT(&cdb, (uchar_t)nbytes);
2485 	cdb.cdb_opaque[1] = (uchar_t)options;
2486 	ucmd.uscsi_cdb = (caddr_t)&cdb;
2487 	ucmd.uscsi_cdblen = CDB_GROUP0;
2488 	ucmd.uscsi_bufaddr = mode_select_buf;
2489 	ucmd.uscsi_buflen = nbytes;
2490 	status = uscsi_cmd(fd, &ucmd,
2491 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2492 
2493 	if (status && option_msg) {
2494 		err_print("Mode select page 0x%x failed\n", page_code);
2495 	}
2496 
2497 	free(mode_select_buf);
2498 	return (status);
2499 }
2500 
2501 
2502 /*
2503  * Execute a uscsi inquiry command and return the
2504  * resulting data.
2505  */
2506 int
2507 uscsi_inquiry(fd, inqbuf, inqbufsiz)
2508 	int		fd;
2509 	caddr_t		inqbuf;
2510 	int		inqbufsiz;
2511 {
2512 	struct uscsi_cmd	ucmd;
2513 	union scsi_cdb		cdb;
2514 	struct scsi_inquiry	*inq;
2515 	int			n;
2516 	int			status;
2517 
2518 	assert(inqbufsiz >= sizeof (struct scsi_inquiry) &&
2519 		inqbufsiz < 256);
2520 
2521 	/*
2522 	 * Build and execute the uscsi ioctl
2523 	 */
2524 	(void) memset((char *)inqbuf, 0, inqbufsiz);
2525 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
2526 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2527 	cdb.scc_cmd = SCMD_INQUIRY;
2528 	FORMG0COUNT(&cdb, (uchar_t)inqbufsiz);
2529 	ucmd.uscsi_cdb = (caddr_t)&cdb;
2530 	ucmd.uscsi_cdblen = CDB_GROUP0;
2531 	ucmd.uscsi_bufaddr = (caddr_t)inqbuf;
2532 	ucmd.uscsi_buflen = inqbufsiz;
2533 	status = uscsi_cmd(fd, &ucmd,
2534 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2535 	if (status) {
2536 		if (option_msg) {
2537 			err_print("Inquiry failed\n");
2538 		}
2539 	} else if (option_msg && diag_msg) {
2540 		/*
2541 		 * Dump the inquiry data if anyone's interested
2542 		 */
2543 		inq = (struct scsi_inquiry *)inqbuf;
2544 		n = (int)inq->inq_len + 4;
2545 		n = min(n, inqbufsiz);
2546 		err_print("Inquiry:\n");
2547 		dump("", (caddr_t)inqbuf, n, HEX_ASCII);
2548 	}
2549 	return (status);
2550 }
2551 
2552 /*
2553  * Execute a uscsi inquiry command with page code 86h
2554  */
2555 int
2556 uscsi_inquiry_page_86h(fd, inqbuf, inqbufsiz)
2557 	int	fd;
2558 	caddr_t	inqbuf;
2559 	int	inqbufsiz;
2560 {
2561 	struct uscsi_cmd	ucmd;
2562 	union scsi_cdb	cdb;
2563 	int	status;
2564 
2565 	assert(inqbuf);
2566 	assert(inqbufsiz >= sizeof (struct scsi_inquiry) &&
2567 	    inqbufsiz < 256);
2568 	/*
2569 	 * Build and execute uscsi ioctl
2570 	 */
2571 	(void) memset((char *)inqbuf, 0, inqbufsiz);
2572 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
2573 	(void) memset((char *)&cdb, 0, sizeof (cdb));
2574 	cdb.scc_cmd = SCMD_INQUIRY;
2575 	FORMG0COUNT(&cdb, (uchar_t)inqbufsiz);
2576 	cdb.cdb_opaque[1] |= 0x01;
2577 	cdb.cdb_opaque[2] = 0x86;
2578 	ucmd.uscsi_cdb = (caddr_t)&cdb;
2579 	ucmd.uscsi_cdblen = CDB_GROUP0;
2580 	ucmd.uscsi_bufaddr = (caddr_t)inqbuf;
2581 	ucmd.uscsi_buflen = inqbufsiz;
2582 
2583 	status = uscsi_cmd(fd, &ucmd,
2584 	    (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2585 
2586 	if (status) {
2587 		if (option_msg) {
2588 			err_print("Inquriy with page_86h failed\n");
2589 		}
2590 	}
2591 	return (status);
2592 }
2593 
2594 /*
2595  * Return the Read Capacity information
2596  */
2597 int
2598 uscsi_read_capacity_16(fd, capacity)
2599 	int	fd;
2600 	struct scsi_capacity_16 *capacity;
2601 {
2602 	struct uscsi_cmd	ucmd;
2603 	union scsi_cdb	cdb;
2604 	int	status;
2605 
2606 	(void) memset((char *)capacity, 0, sizeof (struct scsi_capacity_16));
2607 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
2608 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2609 
2610 	ucmd.uscsi_cdb = (caddr_t)&cdb;
2611 	ucmd.uscsi_cdblen = CDB_GROUP4;
2612 	ucmd.uscsi_bufaddr = (caddr_t)capacity;
2613 	ucmd.uscsi_buflen = sizeof (struct scsi_capacity_16);
2614 
2615 	/*
2616 	 * Read Capacity (16) is a Service Action In command.  One
2617 	 * command byte (0x9E) is overloaded for multiple operations,
2618 	 * with the second CDB byte specifying the desired operation
2619 	 */
2620 	cdb.scc_cmd = SCMD_SVC_ACTION_IN_G4;
2621 	cdb.cdb_opaque[1] = SSVC_ACTION_READ_CAPACITY_G4;
2622 
2623 	/*
2624 	 * Fill in allocation length field
2625 	 */
2626 	cdb.cdb_opaque[10] =
2627 	    (uchar_t)((ucmd.uscsi_buflen & 0xff000000) >> 24);
2628 	cdb.cdb_opaque[11] =
2629 	    (uchar_t)((ucmd.uscsi_buflen & 0x00ff0000) >> 16);
2630 	cdb.cdb_opaque[12] =
2631 	    (uchar_t)((ucmd.uscsi_buflen & 0x0000ff00) >> 8);
2632 	cdb.cdb_opaque[13] =
2633 	    (uchar_t)(ucmd.uscsi_buflen & 0x000000ff);
2634 
2635 	status = uscsi_cmd(fd, &ucmd,
2636 	    (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2637 
2638 	if (status) {
2639 		if (option_msg) {
2640 			err_print("Read capacity 16 failed\n");
2641 		}
2642 	} else if (option_msg && diag_msg) {
2643 		/*
2644 		 * Dump the capacity data if anyone's interested
2645 		 */
2646 		dump("Capacity: ", (caddr_t)capacity,
2647 		    sizeof (struct scsi_capacity_16), HEX_ONLY);
2648 	}
2649 
2650 	capacity->sc_capacity = BE_64(capacity->sc_capacity);
2651 	capacity->sc_lbasize = BE_32(capacity->sc_lbasize);
2652 
2653 	return (status);
2654 }
2655 
2656 int
2657 uscsi_read_capacity(fd, capacity)
2658 	int			fd;
2659 	struct scsi_capacity_16	*capacity;
2660 {
2661 	struct uscsi_cmd	ucmd;
2662 	union scsi_cdb		cdb;
2663 	int			status;
2664 	struct scsi_capacity	cap_old;
2665 
2666 	/*
2667 	 * Build and execute the uscsi ioctl
2668 	 */
2669 	(void) memset((char *)capacity, 0, sizeof (struct scsi_capacity_16));
2670 	(void) memset((char *)&cap_old, 0, sizeof (struct scsi_capacity));
2671 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
2672 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2673 	cdb.scc_cmd = SCMD_READ_CAPACITY;
2674 	ucmd.uscsi_cdb = (caddr_t)&cdb;
2675 	ucmd.uscsi_cdblen = CDB_GROUP1;
2676 	ucmd.uscsi_bufaddr = (caddr_t)&cap_old;
2677 	ucmd.uscsi_buflen = sizeof (struct scsi_capacity);
2678 	status = uscsi_cmd(fd, &ucmd,
2679 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2680 
2681 	if (cap_old.capacity == UINT_MAX32) {
2682 		/*
2683 		 * A capacity of 0xffffffff in response to a
2684 		 * READ CAPACITY 10 indicates that the lun
2685 		 * is too large to report the size in a 32 bit
2686 		 * value, and a READ CAPACITY 16 is required
2687 		 * to get the correct size.
2688 		 */
2689 		(void) memset((char *)&ucmd, 0, sizeof (ucmd));
2690 		(void) memset((char *)&cdb, 0,
2691 			sizeof (union scsi_cdb));
2692 
2693 		ucmd.uscsi_cdb = (caddr_t)&cdb;
2694 		ucmd.uscsi_cdblen = CDB_GROUP4;
2695 		ucmd.uscsi_bufaddr = (caddr_t)capacity;
2696 		ucmd.uscsi_buflen = sizeof (struct scsi_capacity_16);
2697 
2698 		/*
2699 		 * Read Capacity (16) is a Service Action In command.  One
2700 		 * command byte (0x9E) is overloaded for multiple operations,
2701 		 * with the second CDB byte specifying the desired operation
2702 		 */
2703 		cdb.scc_cmd = SCMD_SVC_ACTION_IN_G4;
2704 		cdb.cdb_opaque[1] = SSVC_ACTION_READ_CAPACITY_G4;
2705 
2706 		/*
2707 		 * Fill in allocation length field
2708 		 */
2709 		cdb.cdb_opaque[10] =
2710 		    (uchar_t)((ucmd.uscsi_buflen & 0xff000000) >> 24);
2711 		cdb.cdb_opaque[11] =
2712 		    (uchar_t)((ucmd.uscsi_buflen & 0x00ff0000) >> 16);
2713 		cdb.cdb_opaque[12] =
2714 		    (uchar_t)((ucmd.uscsi_buflen & 0x0000ff00) >> 8);
2715 		cdb.cdb_opaque[13] =
2716 		    (uchar_t)(ucmd.uscsi_buflen & 0x000000ff);
2717 
2718 		status = uscsi_cmd(fd, &ucmd,
2719 			(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2720 	}
2721 
2722 	if (status) {
2723 		if (option_msg) {
2724 			/*
2725 			 * Indicate which of the commands failed
2726 			 */
2727 			if (cdb.scc_cmd == SCMD_READ_CAPACITY) {
2728 				err_print("Read capacity failed\n");
2729 			} else {
2730 				err_print("Read capacity 16 failed\n");
2731 			}
2732 		}
2733 	} else if (option_msg && diag_msg) {
2734 		/*
2735 		 * Dump the capacity data if anyone's interested
2736 		 */
2737 		if (cap_old.capacity == UINT_MAX32) {
2738 			dump("Capacity: ", (caddr_t)capacity,
2739 				sizeof (struct scsi_capacity_16), HEX_ONLY);
2740 		} else {
2741 			dump("Capacity: ", (caddr_t)&cap_old,
2742 				sizeof (struct scsi_capacity), HEX_ONLY);
2743 		}
2744 	}
2745 
2746 	if (cap_old.capacity == UINT_MAX32) {
2747 		capacity->sc_capacity = BE_64(capacity->sc_capacity);
2748 		capacity->sc_lbasize = BE_32(capacity->sc_lbasize);
2749 	} else {
2750 		capacity->sc_capacity = (uint64_t)BE_32(cap_old.capacity);
2751 		capacity->sc_lbasize = BE_32(cap_old.lbasize);
2752 	}
2753 
2754 	return (status);
2755 }
2756 
2757 
2758 /*
2759  * Reserve the current disk
2760  */
2761 static int
2762 uscsi_reserve_release(int fd, int cmd)
2763 {
2764 	int			status = 0;
2765 #ifdef sparc
2766 	struct uscsi_cmd	ucmd;
2767 	union scsi_cdb		cdb;
2768 
2769 	/*
2770 	 * Build and execute the uscsi ioctl
2771 	 */
2772 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
2773 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2774 	cdb.scc_cmd = (cmd == SCMD_RESERVE) ? SCMD_RESERVE : SCMD_RELEASE;
2775 	ucmd.uscsi_cdb = (caddr_t)&cdb;
2776 	ucmd.uscsi_cdblen = CDB_GROUP0;
2777 	status = uscsi_cmd(fd, &ucmd,
2778 	    (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2779 
2780 	if (status) {
2781 		/*
2782 		 * Reserve/Release(6) failed.
2783 		 * Try Reserve/Release(10) , if it succeeds then
2784 		 * return success.
2785 		 */
2786 		(void) memset((char *)&ucmd, 0, sizeof (ucmd));
2787 		(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2788 		ucmd.uscsi_cdb = (caddr_t)&cdb;
2789 		cdb.scc_cmd = (cmd == SCMD_RESERVE) ?
2790 		    SCMD_RESERVE_G1 : SCMD_RELEASE_G1;
2791 		ucmd.uscsi_cdblen = CDB_GROUP1;
2792 		status = uscsi_cmd(fd, &ucmd,
2793 		    (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2794 		if (status) {
2795 			if (option_msg) {
2796 				err_print("%s failed\n", (cmd == SCMD_RESERVE) ?
2797 				    "Reserve" : "Release");
2798 			}
2799 		}
2800 	}
2801 #else /* not sparc */
2802 
2803 #ifdef lint
2804 	fd = fd;
2805 	cmd = cmd;
2806 #endif /* lint */
2807 
2808 #endif /* not sparc */
2809 
2810 	return (status);
2811 }
2812 
2813 int
2814 scsi_dump_mode_sense_pages(page_control)
2815 	int			page_control;
2816 {
2817 	struct uscsi_cmd	ucmd;
2818 	union scsi_cdb		cdb;
2819 	char			*msbuf;
2820 	int			nbytes;
2821 	char			*pc_str;
2822 	int			status;
2823 	struct mode_header	*mh;
2824 	char			*p;
2825 	struct mode_page	*mp;
2826 	int			n;
2827 	char			s[16];
2828 	int			result = 0;
2829 
2830 	pc_str = find_string(page_control_strings, page_control);
2831 
2832 	/*
2833 	 * Allocate memory for the mode sense buffer.
2834 	 */
2835 	nbytes = 255;
2836 	msbuf = (char *)zalloc(nbytes);
2837 
2838 	/*
2839 	 * Build and execute the uscsi ioctl
2840 	 */
2841 	(void) memset(msbuf, 0, nbytes);
2842 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
2843 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2844 	cdb.scc_cmd = SCMD_MODE_SENSE;
2845 	FORMG0COUNT(&cdb, (uchar_t)nbytes);
2846 	cdb.cdb_opaque[2] = page_control | 0x3f;
2847 	ucmd.uscsi_cdb = (caddr_t)&cdb;
2848 	ucmd.uscsi_cdblen = CDB_GROUP0;
2849 	ucmd.uscsi_bufaddr = msbuf;
2850 	ucmd.uscsi_buflen = nbytes;
2851 	status = uscsi_cmd(cur_file, &ucmd,
2852 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2853 	if (status) {
2854 		err_print("\nMode sense page 0x3f (%s) failed\n",
2855 			pc_str);
2856 		result = 1;
2857 	} else {
2858 		err_print("\nMode sense pages (%s):\n", pc_str);
2859 		mh = (struct mode_header *)msbuf;
2860 		nbytes = mh->length - sizeof (struct mode_header) -
2861 				mh->bdesc_length + 1;
2862 		p = msbuf + sizeof (struct mode_header) +
2863 			mh->bdesc_length;
2864 		dump("         ", msbuf, sizeof (struct mode_header) +
2865 			(int)mh->bdesc_length, HEX_ONLY);
2866 		while (nbytes > 0) {
2867 			mp = (struct mode_page *)p;
2868 			n = mp->length + sizeof (struct mode_page);
2869 			nbytes -= n;
2870 			if (nbytes < 0)
2871 				break;
2872 			(void) sprintf(s, "   %3x:  ", mp->code);
2873 			dump(s, p, n, HEX_ONLY);
2874 			p += n;
2875 		}
2876 		if (nbytes < 0) {
2877 			err_print("  Sense data formatted incorrectly:\n");
2878 			dump("    ", msbuf, (int)mh->length+1, HEX_ONLY);
2879 			result = 1;
2880 		}
2881 		err_print("\n");
2882 	}
2883 
2884 	free(msbuf);
2885 	return (result);
2886 }
2887 
2888 
2889 static void
2890 scsi_printerr(ucmd, rq, rqlen)
2891 	struct uscsi_cmd		*ucmd;
2892 	struct scsi_extended_sense	*rq;
2893 	int				rqlen;
2894 {
2895 	diskaddr_t	blkno;
2896 	struct scsi_descr_sense_hdr *sdsp =
2897 	    (struct scsi_descr_sense_hdr *)rq;
2898 
2899 	switch (rq->es_key) {
2900 	case KEY_NO_SENSE:
2901 		err_print("No sense error");
2902 		break;
2903 	case KEY_RECOVERABLE_ERROR:
2904 		err_print("Recoverable error");
2905 		break;
2906 	case KEY_NOT_READY:
2907 		err_print("Not ready error");
2908 		break;
2909 	case KEY_MEDIUM_ERROR:
2910 		err_print("Medium error");
2911 		break;
2912 	case KEY_HARDWARE_ERROR:
2913 		err_print("Hardware error");
2914 		break;
2915 	case KEY_ILLEGAL_REQUEST:
2916 		err_print("Illegal request");
2917 		break;
2918 	case KEY_UNIT_ATTENTION:
2919 		err_print("Unit attention error");
2920 		break;
2921 	case KEY_WRITE_PROTECT:
2922 		err_print("Write protect error");
2923 		break;
2924 	case KEY_BLANK_CHECK:
2925 		err_print("Blank check error");
2926 		break;
2927 	case KEY_VENDOR_UNIQUE:
2928 		err_print("Vendor unique error");
2929 		break;
2930 	case KEY_COPY_ABORTED:
2931 		err_print("Copy aborted error");
2932 		break;
2933 	case KEY_ABORTED_COMMAND:
2934 		err_print("Aborted command");
2935 		break;
2936 	case KEY_EQUAL:
2937 		err_print("Equal error");
2938 		break;
2939 	case KEY_VOLUME_OVERFLOW:
2940 		err_print("Volume overflow");
2941 		break;
2942 	case KEY_MISCOMPARE:
2943 		err_print("Miscompare error");
2944 		break;
2945 	case KEY_RESERVED:
2946 		err_print("Reserved error");
2947 		break;
2948 	default:
2949 		err_print("Unknown error");
2950 		break;
2951 	}
2952 
2953 	err_print(" during %s", scsi_find_command_name(ucmd->uscsi_cdb[0]));
2954 
2955 	/*
2956 	 * Get asc, ascq and info field from sense data.  There are two
2957 	 * possible formats (fixed sense data and descriptor sense data)
2958 	 * depending on the value of es_code.
2959 	 */
2960 	switch (rq->es_code) {
2961 	case CODE_FMT_DESCR_CURRENT:
2962 	case CODE_FMT_DESCR_DEFERRED:
2963 		blkno =
2964 		    (diskaddr_t)scsi_extract_sense_info_descr(sdsp, rqlen);
2965 		if (blkno != (diskaddr_t)-1) {
2966 			err_print(": block %lld (0x%llx) (", blkno, blkno);
2967 			pr_dblock(err_print, blkno);
2968 			err_print(")");
2969 		}
2970 
2971 		err_print("\n");
2972 
2973 		err_print("ASC: 0x%x   ASCQ: 0x%x\n",
2974 		    sdsp->ds_add_code, sdsp->ds_qual_code);
2975 		break;
2976 	case CODE_FMT_FIXED_CURRENT:
2977 	case CODE_FMT_FIXED_DEFERRED:
2978 	default:
2979 		if (rq->es_valid) {
2980 			blkno = (rq->es_info_1 << 24) |
2981 			    (rq->es_info_2 << 16) |
2982 			    (rq->es_info_3 << 8) | rq->es_info_4;
2983 			err_print(": block %lld (0x%llx) (", blkno, blkno);
2984 			pr_dblock(err_print, blkno);
2985 			err_print(")");
2986 		}
2987 
2988 		err_print("\n");
2989 
2990 		if (rq->es_add_len >= 6) {
2991 			err_print("ASC: 0x%x   ASCQ: 0x%x\n",
2992 			    rq->es_add_code, rq->es_qual_code);
2993 		}
2994 		break;
2995 	}
2996 
2997 	if (option_msg && diag_msg) {
2998 		if (rq->es_key == KEY_ILLEGAL_REQUEST) {
2999 			dump("cmd:    ", (caddr_t)ucmd,
3000 			    sizeof (struct uscsi_cmd), HEX_ONLY);
3001 			dump("cdb:    ", (caddr_t)ucmd->uscsi_cdb,
3002 			    ucmd->uscsi_cdblen, HEX_ONLY);
3003 		}
3004 		dump("sense:  ", (caddr_t)rq, rqlen, HEX_ONLY);
3005 	}
3006 
3007 	if (option_msg) {
3008 		switch (rq->es_code) {
3009 		case CODE_FMT_DESCR_CURRENT:
3010 		case CODE_FMT_DESCR_DEFERRED:
3011 			scsi_print_descr_sense(sdsp, rqlen);
3012 			break;
3013 		case CODE_FMT_FIXED_CURRENT:
3014 		case CODE_FMT_FIXED_DEFERRED:
3015 		default:
3016 			scsi_print_extended_sense(rq, rqlen);
3017 			break;
3018 		}
3019 	}
3020 }
3021 
3022 /*
3023  * Retrieve "information" field from descriptor format
3024  * sense data.  Iterates through each sense descriptor
3025  * looking for the information descriptor and returns
3026  * the information field from that descriptor.
3027  */
3028 static diskaddr_t
3029 scsi_extract_sense_info_descr(struct scsi_descr_sense_hdr *sdsp, int rqlen)
3030 {
3031 	diskaddr_t result;
3032 	uint8_t *descr_offset;
3033 	int valid_sense_length;
3034 	struct scsi_information_sense_descr *isd;
3035 
3036 	/*
3037 	 * Initialize result to -1 indicating there is no information
3038 	 * descriptor
3039 	 */
3040 	result = (diskaddr_t)-1;
3041 
3042 	/*
3043 	 * The first descriptor will immediately follow the header
3044 	 */
3045 	descr_offset = (uint8_t *)(sdsp+1); /* Pointer arithmetic */
3046 
3047 	/*
3048 	 * Calculate the amount of valid sense data
3049 	 */
3050 	valid_sense_length =
3051 	    min((sizeof (struct scsi_descr_sense_hdr) +
3052 	    sdsp->ds_addl_sense_length),
3053 	    rqlen);
3054 
3055 	/*
3056 	 * Iterate through the list of descriptors, stopping when we
3057 	 * run out of sense data
3058 	 */
3059 	while ((descr_offset + sizeof (struct scsi_information_sense_descr)) <=
3060 	    (uint8_t *)sdsp + valid_sense_length) {
3061 		/*
3062 		 * Check if this is an information descriptor.  We can
3063 		 * use the scsi_information_sense_descr structure as a
3064 		 * template sense the first two fields are always the
3065 		 * same
3066 		 */
3067 		isd = (struct scsi_information_sense_descr *)descr_offset;
3068 		if (isd->isd_descr_type == DESCR_INFORMATION) {
3069 			/*
3070 			 * Found an information descriptor.  Copy the
3071 			 * information field.  There will only be one
3072 			 * information descriptor so we can stop looking.
3073 			 */
3074 			result =
3075 			    (((diskaddr_t)isd->isd_information[0] << 56) |
3076 			    ((diskaddr_t)isd->isd_information[1] << 48) |
3077 			    ((diskaddr_t)isd->isd_information[2] << 40) |
3078 			    ((diskaddr_t)isd->isd_information[3] << 32) |
3079 			    ((diskaddr_t)isd->isd_information[4] << 24) |
3080 			    ((diskaddr_t)isd->isd_information[5] << 16) |
3081 			    ((diskaddr_t)isd->isd_information[6] << 8)  |
3082 			    ((diskaddr_t)isd->isd_information[7]));
3083 			break;
3084 		}
3085 
3086 		/*
3087 		 * Get pointer to the next descriptor.  The "additional
3088 		 * length" field holds the length of the descriptor except
3089 		 * for the "type" and "additional length" fields, so
3090 		 * we need to add 2 to get the total length.
3091 		 */
3092 		descr_offset += (isd->isd_addl_length + 2);
3093 	}
3094 
3095 	return (result);
3096 }
3097 
3098 /*
3099  * Return a pointer to a string telling us the name of the command.
3100  */
3101 static char *
3102 scsi_find_command_name(uint_t cmd)
3103 {
3104 	struct scsi_command_name *c;
3105 
3106 	for (c = scsi_command_names; c->command != SCMD_UNKNOWN; c++)
3107 		if (c->command == cmd)
3108 			break;
3109 	return (c->name);
3110 }
3111 
3112 
3113 /*
3114  * Return true if we support a particular mode page
3115  */
3116 int
3117 scsi_supported_page(int page) {
3118 	return (page == 1 || page == 2 || page == 3 || page == 4 ||
3119 		page == 8 || page == 0x38);
3120 }
3121 
3122 
3123 int
3124 apply_chg_list(int pageno, int pagsiz, uchar_t *curbits,
3125 		uchar_t *chgbits, struct chg_list *chglist)
3126 {
3127 	uchar_t		c;
3128 	int		i;
3129 	int		m;
3130 	int		delta;
3131 	int		changed = 0;
3132 
3133 	while (chglist != NULL) {
3134 		if (chglist->pageno == pageno &&
3135 		    chglist->byteno < pagsiz) {
3136 			i = chglist->byteno;
3137 			c = curbits[i];
3138 			switch (chglist->mode) {
3139 			case CHG_MODE_SET:
3140 				c |= (uchar_t)chglist->value;
3141 				break;
3142 			case CHG_MODE_CLR:
3143 				c &= (uchar_t)chglist->value;
3144 				break;
3145 			case CHG_MODE_ABS:
3146 				c = (uchar_t)chglist->value;
3147 				break;
3148 			}
3149 			/*
3150 			 * Figure out which bits changed, and
3151 			 * are marked as changeable.  If this
3152 			 * result actually differs from the
3153 			 * current value, update the current
3154 			 * value, and note that a mode select
3155 			 * should be done.
3156 			 */
3157 			delta = c ^ curbits[i];
3158 			for (m = 0x01; m < 0x100; m <<= 1) {
3159 				if ((delta & m) && (chgbits[i] & m)) {
3160 					curbits[i] ^= m;
3161 					changed = 1;
3162 				}
3163 			}
3164 		}
3165 		chglist = chglist->next;
3166 	}
3167 
3168 	return (changed);
3169 }
3170 
3171 
3172 /*
3173  * Return whether a given page is affected by an item on
3174  * the change list.
3175  */
3176 static int
3177 chg_list_affects_page(chglist, pageno)
3178 	struct chg_list	*chglist;
3179 	int		pageno;
3180 {
3181 	while (chglist != NULL) {
3182 		if (chglist->pageno == pageno) {
3183 			return (1);
3184 		}
3185 		chglist = chglist->next;
3186 	}
3187 
3188 	return (0);
3189 }
3190 
3191 
3192 /*
3193  * Labels for the various fields of the scsi_extended_sense structure
3194  */
3195 static char *scsi_extended_sense_labels[] = {
3196 	"Request sense valid:             ",
3197 	"Error class and code:            ",
3198 	"Segment number:                  ",
3199 	"Filemark:                        ",
3200 	"End-of-medium:                   ",
3201 	"Incorrect length indicator:      ",
3202 	"Sense key:                       ",
3203 	"Information field:               ",
3204 	"Additional sense length:         ",
3205 	"Command-specific information:    ",
3206 	"Additional sense code:           ",
3207 	"Additional sense code qualifier: ",
3208 	"Field replaceable unit code:     ",
3209 	"Sense-key specific:              ",
3210 	"Additional sense bytes:          "
3211 };
3212 
3213 
3214 /*
3215  * Display the full scsi_extended_sense as returned by the device
3216  */
3217 static void
3218 scsi_print_extended_sense(rq, rqlen)
3219 	struct scsi_extended_sense	*rq;
3220 	int				rqlen;
3221 {
3222 	char			**p;
3223 
3224 	p = scsi_extended_sense_labels;
3225 	if (rqlen < (sizeof (*rq) - 2) || !rq->es_valid) {
3226 		/*
3227 		 * target should be capable of returning at least 18
3228 		 * bytes of data, i.e upto rq->es_skey_specific field.
3229 		 * The additional sense bytes (2 or more ...) are optional.
3230 		 */
3231 		return;
3232 	}
3233 
3234 	fmt_print("\n%s%s\n", *p++, rq->es_valid ? "yes" : "no");
3235 	fmt_print("%s0x%02x\n", *p++, (rq->es_class << 4) + rq->es_code);
3236 	fmt_print("%s%d\n", *p++, rq->es_segnum);
3237 	fmt_print("%s%s\n", *p++, rq->es_filmk ? "yes" : "no");
3238 	fmt_print("%s%s\n", *p++, rq->es_eom ? "yes" : "no");
3239 	fmt_print("%s%s\n", *p++, rq->es_ili ? "yes" : "no");
3240 	fmt_print("%s%d\n", *p++, rq->es_key);
3241 
3242 	fmt_print("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++, rq->es_info_1,
3243 		rq->es_info_2, rq->es_info_3, rq->es_info_4);
3244 	fmt_print("%s%d\n", *p++, rq->es_add_len);
3245 	fmt_print("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++, rq->es_cmd_info[0],
3246 		rq->es_cmd_info[1], rq->es_cmd_info[2], rq->es_cmd_info[3]);
3247 	fmt_print("%s0x%02x = %d\n", *p++, rq->es_add_code, rq->es_add_code);
3248 	fmt_print("%s0x%02x = %d\n", *p++, rq->es_qual_code, rq->es_qual_code);
3249 	fmt_print("%s%d\n", *p++, rq->es_fru_code);
3250 	fmt_print("%s0x%02x 0x%02x 0x%02x\n", *p++, rq->es_skey_specific[0],
3251 		rq->es_skey_specific[1], rq->es_skey_specific[2]);
3252 	if (rqlen >= sizeof (*rq)) {
3253 		fmt_print("%s0x%02x 0x%02x%s\n", *p, rq->es_add_info[0],
3254 		rq->es_add_info[1], (rqlen > sizeof (*rq)) ? " ..." : "");
3255 	}
3256 
3257 	fmt_print("\n");
3258 }
3259 
3260 /*
3261  * Labels for the various fields of the scsi_descr_sense_hdr structure
3262  */
3263 static char *scsi_descr_sense_labels[] = {
3264 	"Error class and code:            ",
3265 	"Sense key:                       ",
3266 	"Additional sense length:         ",
3267 	"Additional sense code:           ",
3268 	"Additional sense code qualifier: ",
3269 	"Additional sense bytes:          "
3270 };
3271 
3272 
3273 /*
3274  * Display the full descriptor sense data as returned by the device
3275  */
3276 
3277 static void
3278 scsi_print_descr_sense(rq, rqlen)
3279 	struct scsi_descr_sense_hdr	*rq;
3280 	int				rqlen;
3281 {
3282 	char			**p;
3283 	uint8_t			*descr_offset;
3284 	int			valid_sense_length;
3285 	struct scsi_information_sense_descr *isd;
3286 
3287 	p = scsi_descr_sense_labels;
3288 	if (rqlen < sizeof (struct scsi_descr_sense_hdr)) {
3289 		/*
3290 		 * target must return at least 8 bytes of data
3291 		 */
3292 		return;
3293 	}
3294 
3295 	/* Print descriptor sense header */
3296 	fmt_print("%s0x%02x\n", *p++, (rq->ds_class << 4) + rq->ds_code);
3297 	fmt_print("%s%d\n", *p++, rq->ds_key);
3298 
3299 	fmt_print("%s%d\n", *p++, rq->ds_addl_sense_length);
3300 	fmt_print("%s0x%02x = %d\n", *p++, rq->ds_add_code, rq->ds_add_code);
3301 	fmt_print("%s0x%02x = %d\n", *p++, rq->ds_qual_code, rq->ds_qual_code);
3302 	fmt_print("\n");
3303 
3304 	/*
3305 	 * Now print any sense descriptors.   The first descriptor will
3306 	 * immediately follow the header
3307 	 */
3308 	descr_offset = (uint8_t *)(rq+1); /* Pointer arithmetic */
3309 
3310 	/*
3311 	 * Calculate the amount of valid sense data
3312 	 */
3313 	valid_sense_length =
3314 	    min((sizeof (struct scsi_descr_sense_hdr) +
3315 		rq->ds_addl_sense_length), rqlen);
3316 
3317 	/*
3318 	 * Iterate through the list of descriptors, stopping when we
3319 	 * run out of sense data.  Descriptor format is:
3320 	 *
3321 	 * <Descriptor type> <Descriptor length> <Descriptor data> ...
3322 	 */
3323 	while ((descr_offset + *(descr_offset + 1)) <=
3324 	    (uint8_t *)rq + valid_sense_length) {
3325 		/*
3326 		 * Determine descriptor type.  We can use the
3327 		 * scsi_information_sense_descr structure as a
3328 		 * template since the first two fields are always the
3329 		 * same.
3330 		 */
3331 		isd = (struct scsi_information_sense_descr *)descr_offset;
3332 		switch (isd->isd_descr_type) {
3333 		case DESCR_INFORMATION: {
3334 			uint64_t information;
3335 
3336 			information =
3337 			    (((uint64_t)isd->isd_information[0] << 56) |
3338 				((uint64_t)isd->isd_information[1] << 48) |
3339 				((uint64_t)isd->isd_information[2] << 40) |
3340 				((uint64_t)isd->isd_information[3] << 32) |
3341 				((uint64_t)isd->isd_information[4] << 24) |
3342 				((uint64_t)isd->isd_information[5] << 16) |
3343 				((uint64_t)isd->isd_information[6] << 8)  |
3344 				((uint64_t)isd->isd_information[7]));
3345 			fmt_print("Information field:               "
3346 			    "%0llx\n", information);
3347 			break;
3348 		}
3349 		case DESCR_COMMAND_SPECIFIC: {
3350 			struct scsi_cmd_specific_sense_descr *c =
3351 			    (struct scsi_cmd_specific_sense_descr *)isd;
3352 			uint64_t cmd_specific;
3353 
3354 			cmd_specific =
3355 			    (((uint64_t)c->css_cmd_specific_info[0] << 56) |
3356 				((uint64_t)c->css_cmd_specific_info[1] << 48) |
3357 				((uint64_t)c->css_cmd_specific_info[2] << 40) |
3358 				((uint64_t)c->css_cmd_specific_info[3] << 32) |
3359 				((uint64_t)c->css_cmd_specific_info[4] << 24) |
3360 				((uint64_t)c->css_cmd_specific_info[5] << 16) |
3361 				((uint64_t)c->css_cmd_specific_info[6] << 8)  |
3362 				((uint64_t)c->css_cmd_specific_info[7]));
3363 			fmt_print("Command-specific information:    "
3364 			    "%0llx\n", cmd_specific);
3365 			break;
3366 		}
3367 		case DESCR_SENSE_KEY_SPECIFIC: {
3368 			struct scsi_sk_specific_sense_descr *ssd =
3369 			    (struct scsi_sk_specific_sense_descr *)isd;
3370 			uint8_t *sk_spec_ptr = (uint8_t *)&ssd->sss_data;
3371 			fmt_print("Sense-key specific:              "
3372 			    "0x%02x 0x%02x 0x%02x\n", sk_spec_ptr[0],
3373 			    sk_spec_ptr[1], sk_spec_ptr[2]);
3374 			break;
3375 		}
3376 		case DESCR_FRU: {
3377 			struct scsi_fru_sense_descr *fsd =
3378 			    (struct scsi_fru_sense_descr *)isd;
3379 			fmt_print("Field replaceable unit code:     "
3380 			    "%d\n", fsd->fs_fru_code);
3381 			break;
3382 		}
3383 		case DESCR_BLOCK_COMMANDS: {
3384 			struct scsi_block_cmd_sense_descr *bsd =
3385 			    (struct scsi_block_cmd_sense_descr *)isd;
3386 			fmt_print("Incorrect length indicator:      "
3387 			    "%s\n", bsd->bcs_ili ? "yes" : "no");
3388 			break;
3389 		}
3390 		default:
3391 			/* Ignore */
3392 			break;
3393 		}
3394 
3395 		/*
3396 		 * Get pointer to the next descriptor.  The "additional
3397 		 * length" field holds the length of the descriptor except
3398 		 * for the "type" and "additional length" fields, so
3399 		 * we need to add 2 to get the total length.
3400 		 */
3401 		descr_offset += (isd->isd_addl_length + 2);
3402 	}
3403 
3404 	fmt_print("\n");
3405 }
3406 
3407 /*
3408  * Function checks if READ DEFECT DATA command is supported
3409  * on the current disk.
3410  */
3411 static int
3412 check_support_for_defects()
3413 {
3414 	struct uscsi_cmd	ucmd;
3415 	union scsi_cdb		cdb;
3416 	struct scsi_defect_list	def_list;
3417 	struct scsi_defect_hdr	*hdr;
3418 	int			status;
3419 	char			rqbuf[255];
3420 	struct scsi_extended_sense	*rq;
3421 
3422 	hdr = (struct scsi_defect_hdr *)&def_list;
3423 
3424 	/*
3425 	 * First get length of list by asking for the header only.
3426 	 */
3427 	(void) memset((char *)&def_list, 0, sizeof (def_list));
3428 
3429 	/*
3430 	 * Build and execute the uscsi ioctl
3431 	 */
3432 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
3433 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
3434 	(void) memset((char *)rqbuf, 0, 255);
3435 	cdb.scc_cmd = SCMD_READ_DEFECT_LIST;
3436 	FORMG1COUNT(&cdb, sizeof (struct scsi_defect_hdr));
3437 	cdb.cdb_opaque[2] = DLD_MAN_DEF_LIST | DLD_BFI_FORMAT;
3438 	ucmd.uscsi_cdb = (caddr_t)&cdb;
3439 	ucmd.uscsi_cdblen = CDB_GROUP1;
3440 	ucmd.uscsi_bufaddr = (caddr_t)hdr;
3441 	ucmd.uscsi_buflen = sizeof (struct scsi_defect_hdr);
3442 	ucmd.uscsi_rqbuf = rqbuf;
3443 	ucmd.uscsi_rqlen = sizeof (rqbuf);
3444 	ucmd.uscsi_rqresid = sizeof (rqbuf);
3445 	rq = (struct scsi_extended_sense *)ucmd.uscsi_rqbuf;
3446 
3447 	status = uscsi_cmd(cur_file, &ucmd,
3448 	    (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
3449 
3450 	if (status != 0) {
3451 		/*
3452 		 * check if read_defect_list_is_supported.
3453 		 */
3454 		if (ucmd.uscsi_rqstatus == STATUS_GOOD &&
3455 		    rq->es_key == KEY_ILLEGAL_REQUEST &&
3456 		    rq->es_add_code == INVALID_OPCODE)
3457 			return (0);
3458 	}
3459 	return (1);
3460 }
3461 
3462 /*
3463  * Format the disk, the whole disk, and nothing but the disk.
3464  * Function will be called only for disks
3465  * which do not support read defect list command.
3466  */
3467 static int
3468 scsi_format_without_defects()
3469 {
3470 	struct uscsi_cmd	ucmd;
3471 	union scsi_cdb		cdb;
3472 	struct scsi_defect_hdr	defect_hdr;
3473 	int			status;
3474 
3475 	/*
3476 	 * Construct the uscsi format ioctl.
3477 	 * Use fmtdata = 0 , indicating the no source of
3478 	 * defects information is provided .
3479 	 * Function will be called only for disks
3480 	 * which do not support read defect list command.
3481 	 */
3482 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
3483 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
3484 	(void) memset((char *)&defect_hdr, 0, sizeof (defect_hdr));
3485 	cdb.scc_cmd = SCMD_FORMAT;
3486 	ucmd.uscsi_cdb = (caddr_t)&cdb;
3487 	ucmd.uscsi_cdblen = CDB_GROUP0;
3488 	ucmd.uscsi_bufaddr = (caddr_t)&defect_hdr;
3489 	ucmd.uscsi_buflen = sizeof (defect_hdr);
3490 	cdb.cdb_opaque[1] = 0;
3491 	/*
3492 	 * Issue the format ioctl
3493 	 */
3494 	status = uscsi_cmd(cur_file, &ucmd,
3495 	    (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
3496 	return (status);
3497 }
3498 
3499 /*
3500  * Name: test_until_ready
3501  *
3502  * Description: This function is used by scsi_format and
3503  *   scsi_format_raw to poll the device while the FORMAT
3504  *   UNIT cdb in in progress.
3505  *
3506  * Parameters:
3507  *   file descriptor to poll
3508  *
3509  * Returns:
3510  *   0 - good status
3511  *   !0 - bad status
3512  */
3513 static int test_until_ready(int fd) {
3514 	int				status = 1;
3515 	struct uscsi_cmd		ucmd;
3516 	union scsi_cdb			cdb;
3517 	struct scsi_extended_sense	sense;
3518 	time_t				start, check, time_left;
3519 	uint16_t 			progress;
3520 	int				hour, min, sec;
3521 
3522 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
3523 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
3524 
3525 	ucmd.uscsi_cdb		= (caddr_t)&cdb;
3526 	ucmd.uscsi_cdblen	= CDB_GROUP0;
3527 	ucmd.uscsi_rqbuf	= (caddr_t)&sense;
3528 	ucmd.uscsi_rqlen	= SENSE_LEN;
3529 
3530 	start = check = time((time_t *)0);
3531 
3532 	/* Loop sending TEST UNIT READY until format is complete */
3533 	while (status) {
3534 		/* clear last request sense data */
3535 		ucmd.uscsi_rqstatus	= 0;
3536 		ucmd.uscsi_rqresid	= 0;
3537 		(void) memset((char *)&sense, 0, SENSE_LEN);
3538 
3539 		/* issue test unit ready */
3540 		status = uscsi_cmd(fd, &ucmd, F_SILENT
3541 				| F_RQENABLE);
3542 
3543 		check = time((time_t *)0);
3544 
3545 		/* If device returns not ready we get EIO */
3546 		if (status != 0 && errno == EIO) {
3547 			/* Check SKSV if progress indication is avail */
3548 			if (sense.es_skey_specific[0] == 0x80) {
3549 				/* Store progress indication */
3550 				progress = ((uint16_t)sense.
3551 					es_skey_specific[1]) << 8;
3552 				progress |= (uint16_t)sense.
3553 					es_skey_specific[2];
3554 				progress = (uint16_t)(((float)progress /
3555 					(float)PROGRESS_INDICATION_BASE)*100);
3556 
3557 				fmt_print("\015");
3558 
3559 				/*
3560 				 * check to see if we can estimate
3561 				 * time remaining  - wait until the format
3562 				 * is at least 5 percent complete to avoid
3563 				 * wildly-fluctuating time estimates
3564 				 */
3565 				if ((check - start) <= 0 || progress <= 5) {
3566 					/* unable to estimate */
3567 					fmt_print("  %02d%% complete ",
3568 						progress);
3569 				} else {
3570 					/* display with estimated time */
3571 					time_left = (time_t)(((float)(check
3572 						- start) / (float)progress) *
3573 						(float)(100 - progress));
3574 					sec = time_left % 60;
3575 					min = (time_left / 60) % 60;
3576 					hour = time_left / 3600;
3577 
3578 					fmt_print("  %02d%% complete "
3579 						"(%02d:%02d:%02d remaining) ",
3580 						progress, hour, min, sec);
3581 				}
3582 				/* flush or the screen will not update */
3583 				(void) fflush(stdout);
3584 			}
3585 		} else {
3586 			/* format not in progress */
3587 			if (option_msg) {
3588 			fmt_print("\nRequest Sense ASC=0x%x ASCQ=0x%x",
3589 				sense.es_add_code, sense.es_qual_code);
3590 			}
3591 			break;
3592 		}
3593 
3594 		/* delay so we don't waste cpu time */
3595 		(void) sleep(RETRY_DELAY);
3596 	}
3597 	return (status);
3598 }
3599 
3600 /*
3601  * Get the current protection type from the PROT_EN and P_TYPE
3602  */
3603 uint8_t
3604 get_cur_protection_type(struct scsi_capacity_16 *capacity)
3605 {
3606 	uint8_t	cp13;
3607 	uint8_t	prot_en;
3608 	uint8_t	p_type;
3609 
3610 	cp13 = ((capacity->sc_rsvd0 & 0x3f) << 2)
3611 	    | ((capacity->sc_prot_en & 0x01) << 1)
3612 	    | (capacity->sc_rto_en & 0x01);
3613 	prot_en = cp13 & 0x01;
3614 	if (prot_en == 0) {
3615 		p_type = 0;
3616 	} else {
3617 		p_type = (cp13 << 4) >> 5;
3618 		p_type += 1;
3619 	}
3620 	return (p_type);
3621 }
3622