xref: /titanic_51/usr/src/cmd/format/ctlr_scsi.c (revision fa9e4066f08beec538e775443c5be79dd423fcab)
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 		capacity->sc_capacity = BE_64(capacity->sc_capacity);
2559 		capacity->sc_lbasize = BE_32(capacity->sc_lbasize);
2560 	} else {
2561 		capacity->sc_capacity = (uint64_t)BE_32(cap_old.capacity);
2562 		capacity->sc_lbasize = BE_32(cap_old.lbasize);
2563 	}
2564 
2565 	if (status) {
2566 		if (option_msg) {
2567 			/*
2568 			 * Indicate which of the commands failed
2569 			 */
2570 			if (cdb.scc_cmd == SCMD_READ_CAPACITY) {
2571 				err_print("Read capacity failed\n");
2572 			} else {
2573 				err_print("Read capacity 16 failed\n");
2574 			}
2575 		}
2576 	} else if (option_msg && diag_msg) {
2577 		/*
2578 		 * Dump the capacity data if anyone's interested
2579 		 */
2580 		dump("Capacity: ", (caddr_t)capacity,
2581 			sizeof (struct scsi_capacity_16), HEX_ONLY);
2582 	}
2583 	return (status);
2584 }
2585 
2586 
2587 /*
2588  * Reserve the current disk
2589  */
2590 static int
2591 uscsi_reserve_release(int fd, int cmd)
2592 {
2593 	int			status = 0;
2594 #ifdef sparc
2595 	struct uscsi_cmd	ucmd;
2596 	union scsi_cdb		cdb;
2597 
2598 	/*
2599 	 * Build and execute the uscsi ioctl
2600 	 */
2601 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
2602 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2603 	cdb.scc_cmd = (cmd == SCMD_RESERVE) ? SCMD_RESERVE : SCMD_RELEASE;
2604 	ucmd.uscsi_cdb = (caddr_t)&cdb;
2605 	ucmd.uscsi_cdblen = CDB_GROUP0;
2606 	status = uscsi_cmd(fd, &ucmd,
2607 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2608 
2609 	if (status) {
2610 		/*
2611 		 * Reserve/Release(6) failed.
2612 		 * Try Reserve/Release(10) , if it succeeds then
2613 		 * return success.
2614 		 */
2615 		(void) memset((char *)&ucmd, 0, sizeof (ucmd));
2616 		(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2617 		ucmd.uscsi_cdb = (caddr_t)&cdb;
2618 		cdb.scc_cmd = (cmd == SCMD_RESERVE) ?
2619 			SCMD_RESERVE_G1 : SCMD_RELEASE_G1;
2620 		ucmd.uscsi_cdblen = CDB_GROUP1;
2621 		status = uscsi_cmd(fd, &ucmd,
2622 			(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2623 		if (status) {
2624 			if (option_msg) {
2625 			    err_print("%s failed\n", (cmd == SCMD_RESERVE) ?
2626 				"Reserve" : "Release");
2627 			}
2628 		}
2629 	}
2630 #else /* not sparc */
2631 
2632 #ifdef lint
2633 	fd = fd;
2634 	cmd = cmd;
2635 #endif /* lint */
2636 
2637 #endif /* not sparc */
2638 
2639 	return (status);
2640 }
2641 
2642 int
2643 scsi_dump_mode_sense_pages(page_control)
2644 	int			page_control;
2645 {
2646 	struct uscsi_cmd	ucmd;
2647 	union scsi_cdb		cdb;
2648 	char			*msbuf;
2649 	int			nbytes;
2650 	char			*pc_str;
2651 	int			status;
2652 	struct mode_header	*mh;
2653 	char			*p;
2654 	struct mode_page	*mp;
2655 	int			n;
2656 	char			s[16];
2657 	int			result = 0;
2658 
2659 	pc_str = find_string(page_control_strings, page_control);
2660 
2661 	/*
2662 	 * Allocate memory for the mode sense buffer.
2663 	 */
2664 	nbytes = 255;
2665 	msbuf = (char *)zalloc(nbytes);
2666 
2667 	/*
2668 	 * Build and execute the uscsi ioctl
2669 	 */
2670 	(void) memset(msbuf, 0, nbytes);
2671 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
2672 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2673 	cdb.scc_cmd = SCMD_MODE_SENSE;
2674 	FORMG0COUNT(&cdb, (uchar_t)nbytes);
2675 	cdb.cdb_opaque[2] = page_control | 0x3f;
2676 	ucmd.uscsi_cdb = (caddr_t)&cdb;
2677 	ucmd.uscsi_cdblen = CDB_GROUP0;
2678 	ucmd.uscsi_bufaddr = msbuf;
2679 	ucmd.uscsi_buflen = nbytes;
2680 	status = uscsi_cmd(cur_file, &ucmd,
2681 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2682 	if (status) {
2683 		err_print("\nMode sense page 0x3f (%s) failed\n",
2684 			pc_str);
2685 		result = 1;
2686 	} else {
2687 		err_print("\nMode sense pages (%s):\n", pc_str);
2688 		mh = (struct mode_header *)msbuf;
2689 		nbytes = mh->length - sizeof (struct mode_header) -
2690 				mh->bdesc_length + 1;
2691 		p = msbuf + sizeof (struct mode_header) +
2692 			mh->bdesc_length;
2693 		dump("         ", msbuf, sizeof (struct mode_header) +
2694 			(int)mh->bdesc_length, HEX_ONLY);
2695 		while (nbytes > 0) {
2696 			mp = (struct mode_page *)p;
2697 			n = mp->length + sizeof (struct mode_page);
2698 			nbytes -= n;
2699 			if (nbytes < 0)
2700 				break;
2701 			(void) sprintf(s, "   %3x:  ", mp->code);
2702 			dump(s, p, n, HEX_ONLY);
2703 			p += n;
2704 		}
2705 		if (nbytes < 0) {
2706 			err_print("  Sense data formatted incorrectly:\n");
2707 			dump("    ", msbuf, (int)mh->length+1, HEX_ONLY);
2708 			result = 1;
2709 		}
2710 		err_print("\n");
2711 	}
2712 
2713 	free(msbuf);
2714 	return (result);
2715 }
2716 
2717 
2718 static void
2719 scsi_printerr(ucmd, rq, rqlen)
2720 	struct uscsi_cmd		*ucmd;
2721 	struct scsi_extended_sense	*rq;
2722 	int				rqlen;
2723 {
2724 	diskaddr_t	blkno;
2725 	struct scsi_descr_sense_hdr *sdsp =
2726 	    (struct scsi_descr_sense_hdr *)rq;
2727 
2728 	switch (rq->es_key) {
2729 	case KEY_NO_SENSE:
2730 		err_print("No sense error");
2731 		break;
2732 	case KEY_RECOVERABLE_ERROR:
2733 		err_print("Recoverable error");
2734 		break;
2735 	case KEY_NOT_READY:
2736 		err_print("Not ready error");
2737 		break;
2738 	case KEY_MEDIUM_ERROR:
2739 		err_print("Medium error");
2740 		break;
2741 	case KEY_HARDWARE_ERROR:
2742 		err_print("Hardware error");
2743 		break;
2744 	case KEY_ILLEGAL_REQUEST:
2745 		err_print("Illegal request");
2746 		break;
2747 	case KEY_UNIT_ATTENTION:
2748 		err_print("Unit attention error");
2749 		break;
2750 	case KEY_WRITE_PROTECT:
2751 		err_print("Write protect error");
2752 		break;
2753 	case KEY_BLANK_CHECK:
2754 		err_print("Blank check error");
2755 		break;
2756 	case KEY_VENDOR_UNIQUE:
2757 		err_print("Vendor unique error");
2758 		break;
2759 	case KEY_COPY_ABORTED:
2760 		err_print("Copy aborted error");
2761 		break;
2762 	case KEY_ABORTED_COMMAND:
2763 		err_print("Aborted command");
2764 		break;
2765 	case KEY_EQUAL:
2766 		err_print("Equal error");
2767 		break;
2768 	case KEY_VOLUME_OVERFLOW:
2769 		err_print("Volume overflow");
2770 		break;
2771 	case KEY_MISCOMPARE:
2772 		err_print("Miscompare error");
2773 		break;
2774 	case KEY_RESERVED:
2775 		err_print("Reserved error");
2776 		break;
2777 	default:
2778 		err_print("Unknown error");
2779 		break;
2780 	}
2781 
2782 	err_print(" during %s", scsi_find_command_name(ucmd->uscsi_cdb[0]));
2783 
2784 	/*
2785 	 * Get asc, ascq and info field from sense data.  There are two
2786 	 * possible formats (fixed sense data and descriptor sense data)
2787 	 * depending on the value of es_code.
2788 	 */
2789 	switch (rq->es_code) {
2790 	case CODE_FMT_DESCR_CURRENT:
2791 	case CODE_FMT_DESCR_DEFERRED:
2792 		blkno =
2793 		    (diskaddr_t)scsi_extract_sense_info_descr(sdsp, rqlen);
2794 		if (blkno != (diskaddr_t)-1) {
2795 			err_print(": block %lld (0x%llx) (", blkno, blkno);
2796 			pr_dblock(err_print, blkno);
2797 			err_print(")");
2798 		}
2799 
2800 		err_print("\n");
2801 
2802 		err_print("ASC: 0x%x   ASCQ: 0x%x\n",
2803 		    sdsp->ds_add_code, sdsp->ds_qual_code);
2804 		break;
2805 	case CODE_FMT_FIXED_CURRENT:
2806 	case CODE_FMT_FIXED_DEFERRED:
2807 	default:
2808 		if (rq->es_valid) {
2809 			blkno = (rq->es_info_1 << 24) |
2810 			    (rq->es_info_2 << 16) |
2811 			    (rq->es_info_3 << 8) | rq->es_info_4;
2812 			err_print(": block %lld (0x%llx) (", blkno, blkno);
2813 			pr_dblock(err_print, blkno);
2814 			err_print(")");
2815 		}
2816 
2817 		err_print("\n");
2818 
2819 		if (rq->es_add_len >= 6) {
2820 			err_print("ASC: 0x%x   ASCQ: 0x%x\n",
2821 			    rq->es_add_code, rq->es_qual_code);
2822 		}
2823 		break;
2824 	}
2825 
2826 	if (option_msg && diag_msg) {
2827 		if (rq->es_key == KEY_ILLEGAL_REQUEST) {
2828 			dump("cmd:    ", (caddr_t)ucmd,
2829 			    sizeof (struct uscsi_cmd), HEX_ONLY);
2830 			dump("cdb:    ", (caddr_t)ucmd->uscsi_cdb,
2831 			    ucmd->uscsi_cdblen, HEX_ONLY);
2832 		}
2833 		dump("sense:  ", (caddr_t)rq, rqlen, HEX_ONLY);
2834 	}
2835 
2836 	if (option_msg) {
2837 		switch (rq->es_code) {
2838 		case CODE_FMT_DESCR_CURRENT:
2839 		case CODE_FMT_DESCR_DEFERRED:
2840 			scsi_print_descr_sense(sdsp, rqlen);
2841 			break;
2842 		case CODE_FMT_FIXED_CURRENT:
2843 		case CODE_FMT_FIXED_DEFERRED:
2844 		default:
2845 			scsi_print_extended_sense(rq, rqlen);
2846 			break;
2847 		}
2848 	}
2849 }
2850 
2851 /*
2852  * Retrieve "information" field from descriptor format
2853  * sense data.  Iterates through each sense descriptor
2854  * looking for the information descriptor and returns
2855  * the information field from that descriptor.
2856  */
2857 static diskaddr_t
2858 scsi_extract_sense_info_descr(struct scsi_descr_sense_hdr *sdsp, int rqlen)
2859 {
2860 	diskaddr_t result;
2861 	uint8_t *descr_offset;
2862 	int valid_sense_length;
2863 	struct scsi_information_sense_descr *isd;
2864 
2865 	/*
2866 	 * Initialize result to -1 indicating there is no information
2867 	 * descriptor
2868 	 */
2869 	result = (diskaddr_t)-1;
2870 
2871 	/*
2872 	 * The first descriptor will immediately follow the header
2873 	 */
2874 	descr_offset = (uint8_t *)(sdsp+1); /* Pointer arithmetic */
2875 
2876 	/*
2877 	 * Calculate the amount of valid sense data
2878 	 */
2879 	valid_sense_length =
2880 	    min((sizeof (struct scsi_descr_sense_hdr) +
2881 	    sdsp->ds_addl_sense_length),
2882 	    rqlen);
2883 
2884 	/*
2885 	 * Iterate through the list of descriptors, stopping when we
2886 	 * run out of sense data
2887 	 */
2888 	while ((descr_offset + sizeof (struct scsi_information_sense_descr)) <=
2889 	    (uint8_t *)sdsp + valid_sense_length) {
2890 		/*
2891 		 * Check if this is an information descriptor.  We can
2892 		 * use the scsi_information_sense_descr structure as a
2893 		 * template sense the first two fields are always the
2894 		 * same
2895 		 */
2896 		isd = (struct scsi_information_sense_descr *)descr_offset;
2897 		if (isd->isd_descr_type == DESCR_INFORMATION) {
2898 			/*
2899 			 * Found an information descriptor.  Copy the
2900 			 * information field.  There will only be one
2901 			 * information descriptor so we can stop looking.
2902 			 */
2903 			result =
2904 			    (((diskaddr_t)isd->isd_information[0] << 56) |
2905 				((diskaddr_t)isd->isd_information[1] << 48) |
2906 				((diskaddr_t)isd->isd_information[2] << 40) |
2907 				((diskaddr_t)isd->isd_information[3] << 32) |
2908 				((diskaddr_t)isd->isd_information[4] << 24) |
2909 				((diskaddr_t)isd->isd_information[5] << 16) |
2910 				((diskaddr_t)isd->isd_information[6] << 8)  |
2911 				((diskaddr_t)isd->isd_information[7]));
2912 			break;
2913 		}
2914 
2915 		/*
2916 		 * Get pointer to the next descriptor.  The "additional
2917 		 * length" field holds the length of the descriptor except
2918 		 * for the "type" and "additional length" fields, so
2919 		 * we need to add 2 to get the total length.
2920 		 */
2921 		descr_offset += (isd->isd_addl_length + 2);
2922 	}
2923 
2924 	return (result);
2925 }
2926 
2927 /*
2928  * Return a pointer to a string telling us the name of the command.
2929  */
2930 static char *
2931 scsi_find_command_name(uint_t cmd)
2932 {
2933 	struct scsi_command_name *c;
2934 
2935 	for (c = scsi_command_names; c->command != SCMD_UNKNOWN; c++)
2936 		if (c->command == cmd)
2937 			break;
2938 	return (c->name);
2939 }
2940 
2941 
2942 /*
2943  * Return true if we support a particular mode page
2944  */
2945 int
2946 scsi_supported_page(int page) {
2947 	return (page == 1 || page == 2 || page == 3 || page == 4 ||
2948 		page == 8 || page == 0x38);
2949 }
2950 
2951 
2952 int
2953 apply_chg_list(int pageno, int pagsiz, uchar_t *curbits,
2954 		uchar_t *chgbits, struct chg_list *chglist)
2955 {
2956 	uchar_t		c;
2957 	int		i;
2958 	int		m;
2959 	int		delta;
2960 	int		changed = 0;
2961 
2962 	while (chglist != NULL) {
2963 		if (chglist->pageno == pageno &&
2964 				chglist->byteno < pagsiz) {
2965 			i = chglist->byteno;
2966 			c = curbits[i];
2967 			switch (chglist->mode) {
2968 			case CHG_MODE_SET:
2969 				c |= (uchar_t)chglist->value;
2970 				break;
2971 			case CHG_MODE_CLR:
2972 				c &= (uchar_t)chglist->value;
2973 				break;
2974 			case CHG_MODE_ABS:
2975 				c = (uchar_t)chglist->value;
2976 				break;
2977 			}
2978 			/*
2979 			 * Figure out which bits changed, and
2980 			 * are marked as changeable.  If this
2981 			 * result actually differs from the
2982 			 * current value, update the current
2983 			 * value, and note that a mode select
2984 			 * should be done.
2985 			 */
2986 			delta = c ^ curbits[i];
2987 			for (m = 0x01; m < 0x100; m <<= 1) {
2988 				if ((delta & m) && (chgbits[i] & m)) {
2989 					curbits[i] ^= m;
2990 					changed = 1;
2991 				}
2992 			}
2993 		}
2994 		chglist = chglist->next;
2995 	}
2996 
2997 	return (changed);
2998 }
2999 
3000 
3001 /*
3002  * Return whether a given page is affected by an item on
3003  * the change list.
3004  */
3005 static int
3006 chg_list_affects_page(chglist, pageno)
3007 	struct chg_list	*chglist;
3008 	int		pageno;
3009 {
3010 	while (chglist != NULL) {
3011 		if (chglist->pageno == pageno) {
3012 			return (1);
3013 		}
3014 		chglist = chglist->next;
3015 	}
3016 
3017 	return (0);
3018 }
3019 
3020 
3021 /*
3022  * Labels for the various fields of the scsi_extended_sense structure
3023  */
3024 static char *scsi_extended_sense_labels[] = {
3025 	"Request sense valid:             ",
3026 	"Error class and code:            ",
3027 	"Segment number:                  ",
3028 	"Filemark:                        ",
3029 	"End-of-medium:                   ",
3030 	"Incorrect length indicator:      ",
3031 	"Sense key:                       ",
3032 	"Information field:               ",
3033 	"Additional sense length:         ",
3034 	"Command-specific information:    ",
3035 	"Additional sense code:           ",
3036 	"Additional sense code qualifier: ",
3037 	"Field replaceable unit code:     ",
3038 	"Sense-key specific:              ",
3039 	"Additional sense bytes:          "
3040 };
3041 
3042 
3043 /*
3044  * Display the full scsi_extended_sense as returned by the device
3045  */
3046 static void
3047 scsi_print_extended_sense(rq, rqlen)
3048 	struct scsi_extended_sense	*rq;
3049 	int				rqlen;
3050 {
3051 	char			**p;
3052 
3053 	p = scsi_extended_sense_labels;
3054 	if (rqlen < (sizeof (*rq) - 2) || !rq->es_valid) {
3055 		/*
3056 		 * target should be capable of returning at least 18
3057 		 * bytes of data, i.e upto rq->es_skey_specific field.
3058 		 * The additional sense bytes (2 or more ...) are optional.
3059 		 */
3060 		return;
3061 	}
3062 
3063 	fmt_print("\n%s%s\n", *p++, rq->es_valid ? "yes" : "no");
3064 	fmt_print("%s0x%02x\n", *p++, (rq->es_class << 4) + rq->es_code);
3065 	fmt_print("%s%d\n", *p++, rq->es_segnum);
3066 	fmt_print("%s%s\n", *p++, rq->es_filmk ? "yes" : "no");
3067 	fmt_print("%s%s\n", *p++, rq->es_eom ? "yes" : "no");
3068 	fmt_print("%s%s\n", *p++, rq->es_ili ? "yes" : "no");
3069 	fmt_print("%s%d\n", *p++, rq->es_key);
3070 
3071 	fmt_print("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++, rq->es_info_1,
3072 		rq->es_info_2, rq->es_info_3, rq->es_info_4);
3073 	fmt_print("%s%d\n", *p++, rq->es_add_len);
3074 	fmt_print("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++, rq->es_cmd_info[0],
3075 		rq->es_cmd_info[1], rq->es_cmd_info[2], rq->es_cmd_info[3]);
3076 	fmt_print("%s0x%02x = %d\n", *p++, rq->es_add_code, rq->es_add_code);
3077 	fmt_print("%s0x%02x = %d\n", *p++, rq->es_qual_code, rq->es_qual_code);
3078 	fmt_print("%s%d\n", *p++, rq->es_fru_code);
3079 	fmt_print("%s0x%02x 0x%02x 0x%02x\n", *p++, rq->es_skey_specific[0],
3080 		rq->es_skey_specific[1], rq->es_skey_specific[2]);
3081 	if (rqlen >= sizeof (*rq)) {
3082 		fmt_print("%s0x%02x 0x%02x%s\n", *p, rq->es_add_info[0],
3083 		rq->es_add_info[1], (rqlen > sizeof (*rq)) ? " ..." : "");
3084 	}
3085 
3086 	fmt_print("\n");
3087 }
3088 
3089 /*
3090  * Labels for the various fields of the scsi_descr_sense_hdr structure
3091  */
3092 static char *scsi_descr_sense_labels[] = {
3093 	"Error class and code:            ",
3094 	"Sense key:                       ",
3095 	"Additional sense length:         ",
3096 	"Additional sense code:           ",
3097 	"Additional sense code qualifier: ",
3098 	"Additional sense bytes:          "
3099 };
3100 
3101 
3102 /*
3103  * Display the full descriptor sense data as returned by the device
3104  */
3105 
3106 static void
3107 scsi_print_descr_sense(rq, rqlen)
3108 	struct scsi_descr_sense_hdr	*rq;
3109 	int				rqlen;
3110 {
3111 	char			**p;
3112 	uint8_t			*descr_offset;
3113 	int			valid_sense_length;
3114 	struct scsi_information_sense_descr *isd;
3115 
3116 	p = scsi_descr_sense_labels;
3117 	if (rqlen < sizeof (struct scsi_descr_sense_hdr)) {
3118 		/*
3119 		 * target must return at least 8 bytes of data
3120 		 */
3121 		return;
3122 	}
3123 
3124 	/* Print descriptor sense header */
3125 	fmt_print("%s0x%02x\n", *p++, (rq->ds_class << 4) + rq->ds_code);
3126 	fmt_print("%s%d\n", *p++, rq->ds_key);
3127 
3128 	fmt_print("%s%d\n", *p++, rq->ds_addl_sense_length);
3129 	fmt_print("%s0x%02x = %d\n", *p++, rq->ds_add_code, rq->ds_add_code);
3130 	fmt_print("%s0x%02x = %d\n", *p++, rq->ds_qual_code, rq->ds_qual_code);
3131 	fmt_print("\n");
3132 
3133 	/*
3134 	 * Now print any sense descriptors.   The first descriptor will
3135 	 * immediately follow the header
3136 	 */
3137 	descr_offset = (uint8_t *)(rq+1); /* Pointer arithmetic */
3138 
3139 	/*
3140 	 * Calculate the amount of valid sense data
3141 	 */
3142 	valid_sense_length =
3143 	    min((sizeof (struct scsi_descr_sense_hdr) +
3144 		rq->ds_addl_sense_length), rqlen);
3145 
3146 	/*
3147 	 * Iterate through the list of descriptors, stopping when we
3148 	 * run out of sense data.  Descriptor format is:
3149 	 *
3150 	 * <Descriptor type> <Descriptor length> <Descriptor data> ...
3151 	 */
3152 	while ((descr_offset + *(descr_offset + 1)) <=
3153 	    (uint8_t *)rq + valid_sense_length) {
3154 		/*
3155 		 * Determine descriptor type.  We can use the
3156 		 * scsi_information_sense_descr structure as a
3157 		 * template since the first two fields are always the
3158 		 * same.
3159 		 */
3160 		isd = (struct scsi_information_sense_descr *)descr_offset;
3161 		switch (isd->isd_descr_type) {
3162 		case DESCR_INFORMATION: {
3163 			uint64_t information;
3164 
3165 			information =
3166 			    (((uint64_t)isd->isd_information[0] << 56) |
3167 				((uint64_t)isd->isd_information[1] << 48) |
3168 				((uint64_t)isd->isd_information[2] << 40) |
3169 				((uint64_t)isd->isd_information[3] << 32) |
3170 				((uint64_t)isd->isd_information[4] << 24) |
3171 				((uint64_t)isd->isd_information[5] << 16) |
3172 				((uint64_t)isd->isd_information[6] << 8)  |
3173 				((uint64_t)isd->isd_information[7]));
3174 			fmt_print("Information field:               "
3175 			    "%0llx\n", information);
3176 			break;
3177 		}
3178 		case DESCR_COMMAND_SPECIFIC: {
3179 			struct scsi_cmd_specific_sense_descr *c =
3180 			    (struct scsi_cmd_specific_sense_descr *)isd;
3181 			uint64_t cmd_specific;
3182 
3183 			cmd_specific =
3184 			    (((uint64_t)c->css_cmd_specific_info[0] << 56) |
3185 				((uint64_t)c->css_cmd_specific_info[1] << 48) |
3186 				((uint64_t)c->css_cmd_specific_info[2] << 40) |
3187 				((uint64_t)c->css_cmd_specific_info[3] << 32) |
3188 				((uint64_t)c->css_cmd_specific_info[4] << 24) |
3189 				((uint64_t)c->css_cmd_specific_info[5] << 16) |
3190 				((uint64_t)c->css_cmd_specific_info[6] << 8)  |
3191 				((uint64_t)c->css_cmd_specific_info[7]));
3192 			fmt_print("Command-specific information:    "
3193 			    "%0llx\n", cmd_specific);
3194 			break;
3195 		}
3196 		case DESCR_SENSE_KEY_SPECIFIC: {
3197 			struct scsi_sk_specific_sense_descr *ssd =
3198 			    (struct scsi_sk_specific_sense_descr *)isd;
3199 			uint8_t *sk_spec_ptr = (uint8_t *)&ssd->sss_data;
3200 			fmt_print("Sense-key specific:              "
3201 			    "0x%02x 0x%02x 0x%02x\n", sk_spec_ptr[0],
3202 			    sk_spec_ptr[1], sk_spec_ptr[2]);
3203 			break;
3204 		}
3205 		case DESCR_FRU: {
3206 			struct scsi_fru_sense_descr *fsd =
3207 			    (struct scsi_fru_sense_descr *)isd;
3208 			fmt_print("Field replaceable unit code:     "
3209 			    "%d\n", fsd->fs_fru_code);
3210 			break;
3211 		}
3212 		case DESCR_BLOCK_COMMANDS: {
3213 			struct scsi_block_cmd_sense_descr *bsd =
3214 			    (struct scsi_block_cmd_sense_descr *)isd;
3215 			fmt_print("Incorrect length indicator:      "
3216 			    "%s\n", bsd->bcs_ili ? "yes" : "no");
3217 			break;
3218 		}
3219 		default:
3220 			/* Ignore */
3221 			break;
3222 		}
3223 
3224 		/*
3225 		 * Get pointer to the next descriptor.  The "additional
3226 		 * length" field holds the length of the descriptor except
3227 		 * for the "type" and "additional length" fields, so
3228 		 * we need to add 2 to get the total length.
3229 		 */
3230 		descr_offset += (isd->isd_addl_length + 2);
3231 	}
3232 
3233 	fmt_print("\n");
3234 }
3235 
3236 /*
3237  * Function checks if READ DEFECT DATA command is supported
3238  * on the current disk.
3239  */
3240 static int
3241 check_support_for_defects()
3242 {
3243 	struct uscsi_cmd	ucmd;
3244 	union scsi_cdb		cdb;
3245 	struct scsi_defect_list	def_list;
3246 	struct scsi_defect_hdr	*hdr;
3247 	int			status;
3248 	char			rqbuf[255];
3249 	struct scsi_extended_sense	*rq;
3250 
3251 	hdr = (struct scsi_defect_hdr *)&def_list;
3252 
3253 	/*
3254 	 * First get length of list by asking for the header only.
3255 	 */
3256 	(void) memset((char *)&def_list, 0, sizeof (def_list));
3257 
3258 	/*
3259 	 * Build and execute the uscsi ioctl
3260 	 */
3261 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
3262 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
3263 	(void) memset((char *)rqbuf, 0, 255);
3264 	cdb.scc_cmd = SCMD_READ_DEFECT_LIST;
3265 	FORMG1COUNT(&cdb, sizeof (struct scsi_defect_hdr));
3266 	cdb.cdb_opaque[2] = DLD_MAN_DEF_LIST | DLD_BFI_FORMAT;
3267 	ucmd.uscsi_cdb = (caddr_t)&cdb;
3268 	ucmd.uscsi_cdblen = CDB_GROUP1;
3269 	ucmd.uscsi_bufaddr = (caddr_t)hdr;
3270 	ucmd.uscsi_buflen = sizeof (struct scsi_defect_hdr);
3271 	ucmd.uscsi_rqbuf = rqbuf;
3272 	ucmd.uscsi_rqlen = sizeof (rqbuf);
3273 	ucmd.uscsi_rqresid = sizeof (rqbuf);
3274 	rq = (struct scsi_extended_sense *)ucmd.uscsi_rqbuf;
3275 
3276 	status = uscsi_cmd(cur_file, &ucmd,
3277 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
3278 
3279 	if (status != 0) {
3280 		/*
3281 		 * check if read_defect_list_is_supported.
3282 		 */
3283 		if (ucmd.uscsi_rqstatus == STATUS_GOOD &&
3284 			rq->es_key == KEY_ILLEGAL_REQUEST &&
3285 				rq->es_add_code == INVALID_OPCODE)
3286 				return (0);
3287 	}
3288 	return (1);
3289 }
3290 
3291 /*
3292  * Format the disk, the whole disk, and nothing but the disk.
3293  * Function will be called only for disks
3294  * which do not support read defect list command.
3295  */
3296 static int
3297 scsi_format_without_defects()
3298 {
3299 	struct uscsi_cmd	ucmd;
3300 	union scsi_cdb		cdb;
3301 	struct scsi_defect_hdr	defect_hdr;
3302 	int			status;
3303 
3304 	/*
3305 	 * Construct the uscsi format ioctl.
3306 	 * Use fmtdata = 0 , indicating the no source of
3307 	 * defects information is provided .
3308 	 * Function will be called only for disks
3309 	 * which do not support read defect list command.
3310 	 */
3311 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
3312 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
3313 	(void) memset((char *)&defect_hdr, 0, sizeof (defect_hdr));
3314 	cdb.scc_cmd = SCMD_FORMAT;
3315 	ucmd.uscsi_cdb = (caddr_t)&cdb;
3316 	ucmd.uscsi_cdblen = CDB_GROUP0;
3317 	ucmd.uscsi_bufaddr = (caddr_t)&defect_hdr;
3318 	ucmd.uscsi_buflen = sizeof (defect_hdr);
3319 	cdb.cdb_opaque[1] = 0;
3320 	/*
3321 	 * Issue the format ioctl
3322 	 */
3323 	status = uscsi_cmd(cur_file, &ucmd,
3324 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
3325 	return (status);
3326 }
3327 
3328 /*
3329  * Name: test_until_ready
3330  *
3331  * Description: This function is used by scsi_format and
3332  *   scsi_format_raw to poll the device while the FORMAT
3333  *   UNIT cdb in in progress.
3334  *
3335  * Parameters:
3336  *   file descriptor to poll
3337  *
3338  * Returns:
3339  *   0 - good status
3340  *   !0 - bad status
3341  */
3342 static int test_until_ready(int fd) {
3343 	int				status = 1;
3344 	struct uscsi_cmd		ucmd;
3345 	union scsi_cdb			cdb;
3346 	struct scsi_extended_sense	sense;
3347 	time_t				start, check, time_left;
3348 	uint16_t 			progress;
3349 	int				hour, min, sec;
3350 
3351 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
3352 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
3353 
3354 	ucmd.uscsi_cdb		= (caddr_t)&cdb;
3355 	ucmd.uscsi_cdblen	= CDB_GROUP0;
3356 	ucmd.uscsi_rqbuf	= (caddr_t)&sense;
3357 	ucmd.uscsi_rqlen	= SENSE_LEN;
3358 
3359 	start = check = time((time_t *)0);
3360 
3361 	/* Loop sending TEST UNIT READY until format is complete */
3362 	while (status) {
3363 		/* clear last request sense data */
3364 		ucmd.uscsi_rqstatus	= 0;
3365 		ucmd.uscsi_rqresid	= 0;
3366 		(void) memset((char *)&sense, 0, SENSE_LEN);
3367 
3368 		/* issue test unit ready */
3369 		status = uscsi_cmd(fd, &ucmd, F_SILENT
3370 				| F_RQENABLE);
3371 
3372 		check = time((time_t *)0);
3373 
3374 		/* If device returns not ready we get EIO */
3375 		if (status != 0 && errno == EIO) {
3376 			/* Check SKSV if progress indication is avail */
3377 			if (sense.es_skey_specific[0] == 0x80) {
3378 				/* Store progress indication */
3379 				progress = ((uint16_t)sense.
3380 					es_skey_specific[1]) << 8;
3381 				progress |= (uint16_t)sense.
3382 					es_skey_specific[2];
3383 				progress = (uint16_t)(((float)progress /
3384 					(float)PROGRESS_INDICATION_BASE)*100);
3385 
3386 				fmt_print("\015");
3387 
3388 				/*
3389 				 * check to see if we can estimate
3390 				 * time remaining  - wait until the format
3391 				 * is at least 5 percent complete to avoid
3392 				 * wildly-fluctuating time estimates
3393 				 */
3394 				if ((check - start) <= 0 || progress <= 5) {
3395 					/* unable to estimate */
3396 					fmt_print("  %02d%% complete ",
3397 						progress);
3398 				} else {
3399 					/* display with estimated time */
3400 					time_left = (time_t)(((float)(check
3401 						- start) / (float)progress) *
3402 						(float)(100 - progress));
3403 					sec = time_left % 60;
3404 					min = (time_left / 60) % 60;
3405 					hour = time_left / 3600;
3406 
3407 					fmt_print("  %02d%% complete "
3408 						"(%02d:%02d:%02d remaining) ",
3409 						progress, hour, min, sec);
3410 				}
3411 				/* flush or the screen will not update */
3412 				(void) fflush(stdout);
3413 			}
3414 		} else {
3415 			/* format not in progress */
3416 			if (option_msg) {
3417 			fmt_print("\nRequest Sense ASC=0x%x ASCQ=0x%x",
3418 				sense.es_add_code, sense.es_qual_code);
3419 			}
3420 			break;
3421 		}
3422 
3423 		/* delay so we don't waste cpu time */
3424 		(void) sleep(RETRY_DELAY);
3425 	}
3426 	return (status);
3427 }
3428