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