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