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