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