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