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