xref: /titanic_51/usr/src/cmd/format/ctlr_scsi.c (revision fe54a78e1aacf39261ad56e9903bce02e3fb6d21)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
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 * SECSIZE;
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 * SECSIZE;
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 != SECSIZE);
1031 	page3->data_bytes_sect = SECSIZE;
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(LISTSIZE(len) * SECSIZE);
1848 
1849 		list->header.magicno = (uint_t)DEFECT_MAGIC;
1850 		list->list = new_defect;
1851 
1852 		for (i = 0, new_len = 0; i < len; new_defect++, new_len++) {
1853 			cp = (unsigned char *)old_defect;
1854 			new_defect->cyl = (cp[0] << 16 | cp[1] << 8) | cp[2];
1855 			new_defect->head = old_defect->head;
1856 			new_defect->bfi = (int)old_defect->bytes_from_index;
1857 			new_defect->bfi = BE_32(new_defect->bfi);
1858 			new_defect->nbits = 0;	/* size of defect */
1859 			old_defect1 = old_defect++;
1860 			i++;
1861 			/*
1862 			 * Since we reached the end of the list, old_defect
1863 			 * now points to an invalid reference, since it got
1864 			 * incremented in the above operation. So we don't
1865 			 * need to proceed further. new_len needs to be
1866 			 * incremented to account for the last element.
1867 			 */
1868 			if (i == len) {
1869 				new_len++;
1870 				break;
1871 			}
1872 			obfi = new_defect->bfi;
1873 			nbfi = (int)old_defect->bytes_from_index;
1874 			nbfi = BE_32(nbfi);
1875 
1876 			old_cyl =  new_defect->cyl;
1877 			cp = (unsigned char *)old_defect;
1878 			new_cyl = (cp[0] << 16 | cp[1] << 8) | cp[2];
1879 
1880 
1881 			/*
1882 			 * Merge adjacent contiguous defect entries into one
1883 			 * and update the length of the defect
1884 			 */
1885 			while ((i < len) &&
1886 				(old_cyl  == new_cyl) &&
1887 				(old_defect->head == old_defect1->head) &&
1888 				(nbfi == (obfi + BITSPERBYTE))) {
1889 				old_defect1 = old_defect++;
1890 				cp = (unsigned char *)old_defect;
1891 				new_cyl = (cp[0] << 16 | cp[1] << 8) | cp[2];
1892 				obfi = (int)old_defect1->bytes_from_index;
1893 				obfi = BE_32(obfi);
1894 				nbfi = (int)old_defect->bytes_from_index;
1895 				nbfi = BE_32(nbfi);
1896 				new_defect->nbits += (8*BITSPERBYTE);
1897 				i++;
1898 			}
1899 		}
1900 
1901 		list->header.count = new_len;
1902 		break;
1903 
1904 	default:
1905 		err_print("scsi_convert_list_to_new: can't deal with it\n");
1906 		exit(0);
1907 		/*NOTREACHED*/
1908 	}
1909 
1910 	(void) checkdefsum(list, CK_MAKESUM);
1911 }
1912 
1913 
1914 
1915 /*
1916  * Execute a command and determine the result.
1917  * Uses the "uscsi" ioctl interface, which is
1918  * fully supported.
1919  *
1920  * If the user wants request sense data to be returned
1921  * in case of error then , the "uscsi_cmd" structure
1922  * should have the request sense buffer allocated in
1923  * uscsi_rqbuf.
1924  *
1925  */
1926 int
1927 uscsi_cmd(fd, ucmd, flags)
1928 	int			fd;
1929 	struct uscsi_cmd	*ucmd;
1930 	int			flags;
1931 {
1932 	struct scsi_extended_sense	*rq;
1933 	char				rqbuf[255];
1934 	int				status;
1935 	int				rqlen;
1936 	int				timeout = 0;
1937 
1938 	/*
1939 	 * Set function flags for driver.
1940 	 */
1941 	ucmd->uscsi_flags = USCSI_ISOLATE;
1942 	if (flags & F_SILENT) {
1943 		ucmd->uscsi_flags |= USCSI_SILENT;
1944 	}
1945 	if (flags & F_RQENABLE) {
1946 		ucmd->uscsi_flags |= USCSI_RQENABLE;
1947 	}
1948 
1949 	/*
1950 	 * If this command will perform a read, set the USCSI_READ flag
1951 	 */
1952 	if (ucmd->uscsi_buflen > 0) {
1953 		/*
1954 		 * uscsi_cdb is declared as a caddr_t, so any CDB
1955 		 * command byte with the MSB set will result in a
1956 		 * compiler error unless we cast to an unsigned value.
1957 		 */
1958 		switch ((uint8_t)ucmd->uscsi_cdb[0]) {
1959 		case SCMD_READ:
1960 		case SCMD_READ|SCMD_GROUP1:
1961 		case SCMD_READ|SCMD_GROUP4:
1962 		case SCMD_MODE_SENSE:
1963 		case SCMD_INQUIRY:
1964 		case SCMD_READ_DEFECT_LIST:
1965 		case SCMD_READ_CAPACITY:
1966 		case SCMD_SVC_ACTION_IN_G4:
1967 			ucmd->uscsi_flags |= USCSI_READ;
1968 			break;
1969 		}
1970 	}
1971 
1972 	/*
1973 	 * Set timeout: 30 seconds for all commands except format
1974 	 */
1975 	switch (ucmd->uscsi_cdb[0]) {
1976 	case SCMD_FORMAT:
1977 		if (ucmd->uscsi_timeout == 0) {
1978 			ucmd->uscsi_timeout = scsi_format_timeout;
1979 			/*
1980 			 * Get the timeout value computed using page4 geometry.
1981 			 * add 50% margin to cover defect management overhead.
1982 			 * add another 50% margin to have a safe timeout.
1983 			 * If it exceeds 2 hours then use this value.
1984 			 */
1985 			if ((timeout = scsi_format_time()) > 0) {
1986 				timeout *= 60;	/* convert to seconds */
1987 				timeout += timeout;
1988 				/*
1989 				 * formatting drives with huge capacity
1990 				 * will cause these heuristics to come
1991 				 * up with times that overflow ~9 hours
1992 				 */
1993 				if (timeout > SHRT_MAX)
1994 					timeout = SHRT_MAX;
1995 				if (timeout > scsi_format_timeout)
1996 					ucmd->uscsi_timeout = timeout;
1997 			}
1998 		}
1999 		if (option_msg && diag_msg) {
2000 			err_print("format_timeout set to %d seconds, %d"
2001 				" required\n", ucmd->uscsi_timeout, timeout);
2002 		}
2003 		break;
2004 
2005 	default:
2006 		ucmd->uscsi_timeout = 30;		/* 30 seconds */
2007 		break;
2008 	}
2009 
2010 	/*
2011 	 * Set up Request Sense buffer
2012 	 */
2013 	ucmd->uscsi_flags |= USCSI_RQENABLE;
2014 
2015 	if (ucmd->uscsi_rqbuf == NULL)  {
2016 		ucmd->uscsi_rqbuf = rqbuf;
2017 		ucmd->uscsi_rqlen = sizeof (rqbuf);
2018 		ucmd->uscsi_rqresid = sizeof (rqbuf);
2019 	}
2020 	ucmd->uscsi_rqstatus = IMPOSSIBLE_SCSI_STATUS;
2021 
2022 	/*
2023 	 * Clear global error state
2024 	 */
2025 	media_error = 0;
2026 
2027 	/*
2028 	 * Execute the ioctl
2029 	 */
2030 	status = ioctl(fd, USCSICMD, ucmd);
2031 	if (status == 0 && ucmd->uscsi_status == 0) {
2032 		return (status);
2033 	}
2034 
2035 	/*
2036 	 * Check the status and return appropriate errors if the disk is
2037 	 * unavailable (could be formatting) or reserved (by other host).
2038 	 * In either case we can not talk to the disk now.
2039 	 */
2040 	if (status == -1 && errno == EAGAIN) {
2041 		disk_error = DISK_STAT_UNAVAILABLE;
2042 		return (DSK_UNAVAILABLE);
2043 	}
2044 	if ((ucmd->uscsi_status & STATUS_MASK) == STATUS_RESERVATION_CONFLICT) {
2045 		disk_error = DISK_STAT_RESERVED;
2046 		return (DSK_RESERVED);
2047 	}
2048 	/*
2049 	 * Check for physically removed or completely unresponsive drive
2050 	 */
2051 	if (status == -1 && !ucmd->uscsi_status && errno == EIO) {
2052 		disk_error = DISK_STAT_UNAVAILABLE;
2053 		return (DSK_UNAVAILABLE);
2054 	}
2055 
2056 	/*
2057 	 * If an automatic Request Sense gave us valid
2058 	 * info about the error, we may be able to use
2059 	 * that to print a reasonable error msg.
2060 	 */
2061 	if (ucmd->uscsi_rqstatus == IMPOSSIBLE_SCSI_STATUS) {
2062 		if (option_msg && diag_msg) {
2063 			err_print("No request sense for command %s\n",
2064 				scsi_find_command_name(ucmd->uscsi_cdb[0]));
2065 		}
2066 		return (-1);
2067 	}
2068 	if (ucmd->uscsi_rqstatus != STATUS_GOOD) {
2069 		if (option_msg && diag_msg) {
2070 			err_print("Request sense status for command %s: 0x%x\n",
2071 				scsi_find_command_name(ucmd->uscsi_cdb[0]),
2072 				ucmd->uscsi_rqstatus);
2073 		}
2074 		return (-1);
2075 	}
2076 	rq = (struct scsi_extended_sense *)ucmd->uscsi_rqbuf;
2077 	rqlen = ucmd->uscsi_rqlen - ucmd->uscsi_rqresid;
2078 	if ((((int)rq->es_add_len) + 8) < MIN_REQUEST_SENSE_LEN ||
2079 			rq->es_class != CLASS_EXTENDED_SENSE ||
2080 				rqlen < MIN_REQUEST_SENSE_LEN) {
2081 		if (option_msg) {
2082 			err_print("Request sense for command %s failed\n",
2083 				scsi_find_command_name(ucmd->uscsi_cdb[0]));
2084 		}
2085 		if (option_msg && diag_msg) {
2086 			err_print("Sense data:\n");
2087 			dump("", (caddr_t)rqbuf, rqlen, HEX_ONLY);
2088 		}
2089 		if (errno == EIO) {
2090 			disk_error = DISK_STAT_UNAVAILABLE;
2091 			return (DSK_UNAVAILABLE);
2092 		} else {
2093 			return (-1);
2094 		}
2095 	}
2096 
2097 	/*
2098 	 * If the failed command is a Mode Select, and the
2099 	 * target is indicating that it has rounded one of
2100 	 * the mode select parameters, as defined in the SCSI-2
2101 	 * specification, then we should accept the command
2102 	 * as successful.
2103 	 */
2104 	if (ucmd->uscsi_cdb[0] == SCMD_MODE_SELECT) {
2105 		if (rq->es_key == KEY_RECOVERABLE_ERROR &&
2106 			rq->es_add_code == ROUNDED_PARAMETER &&
2107 			rq->es_qual_code == 0) {
2108 				return (0);
2109 		}
2110 	}
2111 
2112 	switch (rq->es_key) {
2113 	case KEY_NOT_READY:
2114 		disk_error = DISK_STAT_NOTREADY;
2115 		break;
2116 	case KEY_DATA_PROTECT:
2117 		disk_error = DISK_STAT_DATA_PROTECT;
2118 		break;
2119 	}
2120 
2121 	if (flags & F_ALLERRS) {
2122 		media_error = (rq->es_key == KEY_MEDIUM_ERROR);
2123 	}
2124 	if (!(flags & F_SILENT) || option_msg) {
2125 		scsi_printerr(ucmd, rq, rqlen);
2126 	}
2127 	if ((rq->es_key != KEY_RECOVERABLE_ERROR) || (flags & F_ALLERRS)) {
2128 		return (-1);
2129 	}
2130 
2131 	if (status == -1 && errno == EIO) {
2132 		disk_error = DISK_STAT_UNAVAILABLE;
2133 		return (DSK_UNAVAILABLE);
2134 	}
2135 
2136 	return (0);
2137 }
2138 
2139 
2140 /*
2141  * Execute a uscsi mode sense command.
2142  * This can only be used to return one page at a time.
2143  * Return the mode header/block descriptor and the actual
2144  * page data separately - this allows us to support
2145  * devices which return either 0 or 1 block descriptors.
2146  * Whatever a device gives us in the mode header/block descriptor
2147  * will be returned to it upon subsequent mode selects.
2148  */
2149 int
2150 uscsi_mode_sense(fd, page_code, page_control, page_data, page_size, header)
2151 	int	fd;			/* file descriptor */
2152 	int	page_code;		/* requested page number */
2153 	int	page_control;		/* current, changeable, etc. */
2154 	caddr_t	page_data;		/* place received data here */
2155 	int	page_size;		/* size of page_data */
2156 	struct	scsi_ms_header *header;	/* mode header/block descriptor */
2157 {
2158 	caddr_t			mode_sense_buf;
2159 	struct mode_header	*hdr;
2160 	struct mode_page	*pg;
2161 	int			nbytes;
2162 	struct uscsi_cmd	ucmd;
2163 	union scsi_cdb		cdb;
2164 	int			status;
2165 	int			maximum;
2166 
2167 	assert(page_size >= 0 && page_size < 256);
2168 	assert(page_control == MODE_SENSE_PC_CURRENT ||
2169 		page_control == MODE_SENSE_PC_CHANGEABLE ||
2170 			page_control == MODE_SENSE_PC_DEFAULT ||
2171 				page_control == MODE_SENSE_PC_SAVED);
2172 	/*
2173 	 * Allocate a buffer for the mode sense headers
2174 	 * and mode sense data itself.
2175 	 */
2176 	nbytes = sizeof (struct block_descriptor) +
2177 				sizeof (struct mode_header) + page_size;
2178 	nbytes = page_size;
2179 	if ((mode_sense_buf = malloc((uint_t)nbytes)) == NULL) {
2180 		err_print("cannot malloc %d bytes\n", nbytes);
2181 		return (-1);
2182 	}
2183 
2184 	/*
2185 	 * Build and execute the uscsi ioctl
2186 	 */
2187 	(void) memset(mode_sense_buf, 0, nbytes);
2188 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
2189 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2190 	cdb.scc_cmd = SCMD_MODE_SENSE;
2191 	FORMG0COUNT(&cdb, (uchar_t)nbytes);
2192 	cdb.cdb_opaque[2] = page_control | page_code;
2193 	ucmd.uscsi_cdb = (caddr_t)&cdb;
2194 	ucmd.uscsi_cdblen = CDB_GROUP0;
2195 	ucmd.uscsi_bufaddr = mode_sense_buf;
2196 	ucmd.uscsi_buflen = nbytes;
2197 	status = uscsi_cmd(fd, &ucmd,
2198 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2199 	if (status) {
2200 		if (option_msg) {
2201 			err_print("Mode sense page 0x%x failed\n",
2202 				page_code);
2203 		}
2204 		free(mode_sense_buf);
2205 		return (-1);
2206 	}
2207 
2208 	/*
2209 	 * Verify that the returned data looks reasonabled,
2210 	 * find the actual page data, and copy it into the
2211 	 * user's buffer.  Copy the mode_header and block_descriptor
2212 	 * into the header structure, which can then be used to
2213 	 * return the same data to the drive when issuing a mode select.
2214 	 */
2215 	hdr = (struct mode_header *)mode_sense_buf;
2216 	(void) memset((caddr_t)header, 0, sizeof (struct scsi_ms_header));
2217 	if (hdr->bdesc_length != sizeof (struct block_descriptor) &&
2218 				hdr->bdesc_length != 0) {
2219 		if (option_msg) {
2220 			err_print("\
2221 \nMode sense page 0x%x: block descriptor length %d incorrect\n",
2222 				page_code, hdr->bdesc_length);
2223 			if (diag_msg)
2224 				dump("Mode sense: ", mode_sense_buf,
2225 					nbytes, HEX_ONLY);
2226 		}
2227 		free(mode_sense_buf);
2228 		return (-1);
2229 	}
2230 	(void) memcpy((caddr_t)header, mode_sense_buf,
2231 		(int) (sizeof (struct mode_header) + hdr->bdesc_length));
2232 	pg = (struct mode_page *)((ulong_t)mode_sense_buf +
2233 		sizeof (struct mode_header) + hdr->bdesc_length);
2234 	if (pg->code != page_code) {
2235 		if (option_msg) {
2236 			err_print("\
2237 \nMode sense page 0x%x: incorrect page code 0x%x\n",
2238 				page_code, pg->code);
2239 			if (diag_msg)
2240 				dump("Mode sense: ", mode_sense_buf,
2241 					nbytes, HEX_ONLY);
2242 		}
2243 		free(mode_sense_buf);
2244 		return (-1);
2245 	}
2246 	/*
2247 	 * Accept up to "page_size" bytes of mode sense data.
2248 	 * This allows us to accept both CCS and SCSI-2
2249 	 * structures, as long as we request the greater
2250 	 * of the two.
2251 	 */
2252 	maximum = page_size - sizeof (struct mode_page) - hdr->bdesc_length;
2253 	if (((int)pg->length) > maximum) {
2254 		if (option_msg) {
2255 			err_print("\
2256 Mode sense page 0x%x: incorrect page length %d - expected max %d\n",
2257 				page_code, pg->length, maximum);
2258 			if (diag_msg)
2259 				dump("Mode sense: ", mode_sense_buf,
2260 					nbytes, HEX_ONLY);
2261 		}
2262 		free(mode_sense_buf);
2263 		return (-1);
2264 	}
2265 
2266 	(void) memcpy(page_data, (caddr_t)pg, MODESENSE_PAGE_LEN(pg));
2267 
2268 	if (option_msg && diag_msg) {
2269 		char *pc = find_string(page_control_strings, page_control);
2270 		err_print("\nMode sense page 0x%x (%s):\n", page_code,
2271 			pc != NULL ? pc : "");
2272 		dump("header: ", (caddr_t)header,
2273 			sizeof (struct scsi_ms_header), HEX_ONLY);
2274 		dump("data:   ", page_data,
2275 			MODESENSE_PAGE_LEN(pg), HEX_ONLY);
2276 	}
2277 
2278 	free(mode_sense_buf);
2279 	return (0);
2280 }
2281 
2282 
2283 /*
2284  * Execute a uscsi mode select command.
2285  */
2286 int
2287 uscsi_mode_select(fd, page_code, options, page_data, page_size, header)
2288 	int	fd;			/* file descriptor */
2289 	int	page_code;		/* mode select page */
2290 	int	options;		/* save page/page format */
2291 	caddr_t	page_data;		/* place received data here */
2292 	int	page_size;		/* size of page_data */
2293 	struct	scsi_ms_header *header;	/* mode header/block descriptor */
2294 {
2295 	caddr_t				mode_select_buf;
2296 	int				nbytes;
2297 	struct uscsi_cmd		ucmd;
2298 	union scsi_cdb			cdb;
2299 	int				status;
2300 
2301 	assert(((struct mode_page *)page_data)->ps == 0);
2302 	assert(header->mode_header.length == 0);
2303 	assert(header->mode_header.device_specific == 0);
2304 	assert((options & ~(MODE_SELECT_SP|MODE_SELECT_PF)) == 0);
2305 
2306 	/*
2307 	 * Allocate a buffer for the mode select header and data
2308 	 */
2309 	nbytes = sizeof (struct block_descriptor) +
2310 				sizeof (struct mode_header) + page_size;
2311 	if ((mode_select_buf = malloc((uint_t)nbytes)) == NULL) {
2312 		err_print("cannot malloc %d bytes\n", nbytes);
2313 		return (-1);
2314 	}
2315 
2316 	/*
2317 	 * Build the mode select data out of the header and page data
2318 	 * This allows us to support devices which return either
2319 	 * 0 or 1 block descriptors.
2320 	 */
2321 	(void) memset(mode_select_buf, 0, nbytes);
2322 	nbytes = sizeof (struct mode_header);
2323 	if (header->mode_header.bdesc_length ==
2324 				sizeof (struct block_descriptor)) {
2325 		nbytes += sizeof (struct block_descriptor);
2326 	}
2327 
2328 	/*
2329 	 * Dump the structures if anyone's interested
2330 	 */
2331 	if (option_msg && diag_msg) {
2332 		char *s;
2333 		s = find_string(mode_select_strings,
2334 			options & (MODE_SELECT_SP|MODE_SELECT_PF));
2335 		err_print("\nMode select page 0x%x%s:\n", page_code,
2336 			s != NULL ? s : "");
2337 		dump("header: ", (caddr_t)header,
2338 			nbytes, HEX_ONLY);
2339 		dump("data:   ", (caddr_t)page_data,
2340 			page_size, HEX_ONLY);
2341 	}
2342 
2343 	/*
2344 	 * Fix the code for byte ordering
2345 	 */
2346 
2347 	switch (page_code) {
2348 	case  DAD_MODE_ERR_RECOV:
2349 		{
2350 		struct mode_err_recov *pd;
2351 		pd = (struct mode_err_recov *)(void *)page_data;
2352 		pd->recovery_time_limit = BE_16(pd->recovery_time_limit);
2353 		break;
2354 		}
2355 	case MODEPAGE_DISCO_RECO:
2356 		{
2357 		struct mode_disco_reco *pd;
2358 		pd = (struct mode_disco_reco *)(void *)page_data;
2359 		pd->bus_inactivity_limit = BE_16(pd->bus_inactivity_limit);
2360 		pd->disconect_time_limit = BE_16(pd->disconect_time_limit);
2361 		pd->connect_time_limit = BE_16(pd->connect_time_limit);
2362 		pd->max_burst_size = BE_16(pd->max_burst_size);
2363 		break;
2364 		}
2365 	case DAD_MODE_FORMAT:
2366 		{
2367 		struct mode_format *pd;
2368 		pd = (struct mode_format *)(void *)page_data;
2369 		pd->tracks_per_zone = BE_16(pd->tracks_per_zone);
2370 		pd->alt_sect_zone = BE_16(pd->alt_sect_zone);
2371 		pd->alt_tracks_zone = BE_16(pd->alt_tracks_zone);
2372 		pd->alt_tracks_vol = BE_16(pd->alt_tracks_vol);
2373 		pd->sect_track = BE_16(pd->sect_track);
2374 		pd->data_bytes_sect = BE_16(pd->data_bytes_sect);
2375 		pd->interleave = BE_16(pd->interleave);
2376 		pd->track_skew = BE_16(pd->track_skew);
2377 		pd->cylinder_skew = BE_16(pd->cylinder_skew);
2378 		break;
2379 		}
2380 	case DAD_MODE_GEOMETRY:
2381 		{
2382 		struct mode_geometry *pd;
2383 		pd = (struct mode_geometry *)(void *)page_data;
2384 		pd->step_rate = BE_16(pd->step_rate);
2385 		pd->rpm = BE_16(pd->rpm);
2386 		break;
2387 		}
2388 	case DAD_MODE_CACHE:
2389 		{
2390 		struct mode_cache *pd;
2391 		pd = (struct mode_cache *)(void *)page_data;
2392 		pd->dis_prefetch_len = BE_16(pd->dis_prefetch_len);
2393 		pd->min_prefetch = BE_16(pd->min_prefetch);
2394 		pd->max_prefetch = BE_16(pd->max_prefetch);
2395 		pd->prefetch_ceiling = BE_16(pd->prefetch_ceiling);
2396 		break;
2397 		}
2398 	case MODEPAGE_PDEVICE:
2399 		{
2400 		struct mode_pdevice *pd;
2401 		pd = (struct mode_pdevice *)(void *)page_data;
2402 		pd->if_ident = BE_16(pd->if_ident);
2403 		break;
2404 		}
2405 	case MODEPAGE_CTRL_MODE:
2406 		{
2407 		struct mode_control *pd;
2408 		pd = (struct mode_control *)(void *)page_data;
2409 		pd->ready_aen_holdoff = BE_16(pd->ready_aen_holdoff);
2410 		break;
2411 		}
2412 	}
2413 
2414 	/*
2415 	 * Put the header and data together
2416 	 */
2417 	(void) memcpy(mode_select_buf, (caddr_t)header, nbytes);
2418 	(void) memcpy(mode_select_buf + nbytes, page_data, page_size);
2419 	nbytes += page_size;
2420 
2421 	/*
2422 	 * Build and execute the uscsi ioctl
2423 	 */
2424 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
2425 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2426 	cdb.scc_cmd = SCMD_MODE_SELECT;
2427 	FORMG0COUNT(&cdb, (uchar_t)nbytes);
2428 	cdb.cdb_opaque[1] = (uchar_t)options;
2429 	ucmd.uscsi_cdb = (caddr_t)&cdb;
2430 	ucmd.uscsi_cdblen = CDB_GROUP0;
2431 	ucmd.uscsi_bufaddr = mode_select_buf;
2432 	ucmd.uscsi_buflen = nbytes;
2433 	status = uscsi_cmd(fd, &ucmd,
2434 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2435 
2436 	if (status && option_msg) {
2437 		err_print("Mode select page 0x%x failed\n", page_code);
2438 	}
2439 
2440 	free(mode_select_buf);
2441 	return (status);
2442 }
2443 
2444 
2445 /*
2446  * Execute a uscsi inquiry command and return the
2447  * resulting data.
2448  */
2449 int
2450 uscsi_inquiry(fd, inqbuf, inqbufsiz)
2451 	int		fd;
2452 	caddr_t		inqbuf;
2453 	int		inqbufsiz;
2454 {
2455 	struct uscsi_cmd	ucmd;
2456 	union scsi_cdb		cdb;
2457 	struct scsi_inquiry	*inq;
2458 	int			n;
2459 	int			status;
2460 
2461 	assert(inqbufsiz >= sizeof (struct scsi_inquiry) &&
2462 		inqbufsiz < 256);
2463 
2464 	/*
2465 	 * Build and execute the uscsi ioctl
2466 	 */
2467 	(void) memset((char *)inqbuf, 0, inqbufsiz);
2468 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
2469 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2470 	cdb.scc_cmd = SCMD_INQUIRY;
2471 	FORMG0COUNT(&cdb, (uchar_t)inqbufsiz);
2472 	ucmd.uscsi_cdb = (caddr_t)&cdb;
2473 	ucmd.uscsi_cdblen = CDB_GROUP0;
2474 	ucmd.uscsi_bufaddr = (caddr_t)inqbuf;
2475 	ucmd.uscsi_buflen = inqbufsiz;
2476 	status = uscsi_cmd(fd, &ucmd,
2477 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2478 	if (status) {
2479 		if (option_msg) {
2480 			err_print("Inquiry failed\n");
2481 		}
2482 	} else if (option_msg && diag_msg) {
2483 		/*
2484 		 * Dump the inquiry data if anyone's interested
2485 		 */
2486 		inq = (struct scsi_inquiry *)inqbuf;
2487 		n = (int)inq->inq_len + 4;
2488 		n = min(n, inqbufsiz);
2489 		err_print("Inquiry:\n");
2490 		dump("", (caddr_t)inqbuf, n, HEX_ASCII);
2491 	}
2492 	return (status);
2493 }
2494 
2495 
2496 /*
2497  * Return the Read Capacity information
2498  */
2499 int
2500 uscsi_read_capacity(fd, capacity)
2501 	int			fd;
2502 	struct scsi_capacity_16	*capacity;
2503 {
2504 	struct uscsi_cmd	ucmd;
2505 	union scsi_cdb		cdb;
2506 	int			status;
2507 	struct scsi_capacity	cap_old;
2508 
2509 	/*
2510 	 * Build and execute the uscsi ioctl
2511 	 */
2512 	(void) memset((char *)capacity, 0, sizeof (struct scsi_capacity_16));
2513 	(void) memset((char *)&cap_old, 0, sizeof (struct scsi_capacity));
2514 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
2515 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2516 	cdb.scc_cmd = SCMD_READ_CAPACITY;
2517 	ucmd.uscsi_cdb = (caddr_t)&cdb;
2518 	ucmd.uscsi_cdblen = CDB_GROUP1;
2519 	ucmd.uscsi_bufaddr = (caddr_t)&cap_old;
2520 	ucmd.uscsi_buflen = sizeof (struct scsi_capacity);
2521 	status = uscsi_cmd(fd, &ucmd,
2522 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2523 
2524 	if (cap_old.capacity == UINT_MAX32) {
2525 		/*
2526 		 * A capacity of 0xffffffff in response to a
2527 		 * READ CAPACITY 10 indicates that the lun
2528 		 * is too large to report the size in a 32 bit
2529 		 * value, and a READ CAPACITY 16 is required
2530 		 * to get the correct size.
2531 		 */
2532 		(void) memset((char *)&ucmd, 0, sizeof (ucmd));
2533 		(void) memset((char *)&cdb, 0,
2534 			sizeof (union scsi_cdb));
2535 
2536 		ucmd.uscsi_cdb = (caddr_t)&cdb;
2537 		ucmd.uscsi_cdblen = CDB_GROUP4;
2538 		ucmd.uscsi_bufaddr = (caddr_t)capacity;
2539 		ucmd.uscsi_buflen = sizeof (struct scsi_capacity_16);
2540 
2541 		/*
2542 		 * Read Capacity (16) is a Service Action In command.  One
2543 		 * command byte (0x9E) is overloaded for multiple operations,
2544 		 * with the second CDB byte specifying the desired operation
2545 		 */
2546 		cdb.scc_cmd = SCMD_SVC_ACTION_IN_G4;
2547 		cdb.cdb_opaque[1] = SSVC_ACTION_READ_CAPACITY_G4;
2548 
2549 		/*
2550 		 * Fill in allocation length field
2551 		 */
2552 		cdb.cdb_opaque[10] =
2553 		    (uchar_t)((ucmd.uscsi_buflen & 0xff000000) >> 24);
2554 		cdb.cdb_opaque[11] =
2555 		    (uchar_t)((ucmd.uscsi_buflen & 0x00ff0000) >> 16);
2556 		cdb.cdb_opaque[12] =
2557 		    (uchar_t)((ucmd.uscsi_buflen & 0x0000ff00) >> 8);
2558 		cdb.cdb_opaque[13] =
2559 		    (uchar_t)(ucmd.uscsi_buflen & 0x000000ff);
2560 
2561 		status = uscsi_cmd(fd, &ucmd,
2562 			(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2563 	}
2564 
2565 	if (status) {
2566 		if (option_msg) {
2567 			/*
2568 			 * Indicate which of the commands failed
2569 			 */
2570 			if (cdb.scc_cmd == SCMD_READ_CAPACITY) {
2571 				err_print("Read capacity failed\n");
2572 			} else {
2573 				err_print("Read capacity 16 failed\n");
2574 			}
2575 		}
2576 	} else if (option_msg && diag_msg) {
2577 		/*
2578 		 * Dump the capacity data if anyone's interested
2579 		 */
2580 		if (cap_old.capacity == UINT_MAX32) {
2581 			dump("Capacity: ", (caddr_t)capacity,
2582 				sizeof (struct scsi_capacity_16), HEX_ONLY);
2583 		} else {
2584 			dump("Capacity: ", (caddr_t)&cap_old,
2585 				sizeof (struct scsi_capacity), HEX_ONLY);
2586 		}
2587 	}
2588 
2589 	if (cap_old.capacity == UINT_MAX32) {
2590 		capacity->sc_capacity = BE_64(capacity->sc_capacity);
2591 		capacity->sc_lbasize = BE_32(capacity->sc_lbasize);
2592 	} else {
2593 		capacity->sc_capacity = (uint64_t)BE_32(cap_old.capacity);
2594 		capacity->sc_lbasize = BE_32(cap_old.lbasize);
2595 	}
2596 
2597 	return (status);
2598 }
2599 
2600 
2601 /*
2602  * Reserve the current disk
2603  */
2604 static int
2605 uscsi_reserve_release(int fd, int cmd)
2606 {
2607 	int			status = 0;
2608 #ifdef sparc
2609 	struct uscsi_cmd	ucmd;
2610 	union scsi_cdb		cdb;
2611 
2612 	/*
2613 	 * Build and execute the uscsi ioctl
2614 	 */
2615 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
2616 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2617 	cdb.scc_cmd = (cmd == SCMD_RESERVE) ? SCMD_RESERVE : SCMD_RELEASE;
2618 	ucmd.uscsi_cdb = (caddr_t)&cdb;
2619 	ucmd.uscsi_cdblen = CDB_GROUP0;
2620 	status = uscsi_cmd(fd, &ucmd,
2621 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2622 
2623 	if (status) {
2624 		/*
2625 		 * Reserve/Release(6) failed.
2626 		 * Try Reserve/Release(10) , if it succeeds then
2627 		 * return success.
2628 		 */
2629 		(void) memset((char *)&ucmd, 0, sizeof (ucmd));
2630 		(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2631 		ucmd.uscsi_cdb = (caddr_t)&cdb;
2632 		cdb.scc_cmd = (cmd == SCMD_RESERVE) ?
2633 			SCMD_RESERVE_G1 : SCMD_RELEASE_G1;
2634 		ucmd.uscsi_cdblen = CDB_GROUP1;
2635 		status = uscsi_cmd(fd, &ucmd,
2636 			(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2637 		if (status) {
2638 			if (option_msg) {
2639 			    err_print("%s failed\n", (cmd == SCMD_RESERVE) ?
2640 				"Reserve" : "Release");
2641 			}
2642 		}
2643 	}
2644 #else /* not sparc */
2645 
2646 #ifdef lint
2647 	fd = fd;
2648 	cmd = cmd;
2649 #endif /* lint */
2650 
2651 #endif /* not sparc */
2652 
2653 	return (status);
2654 }
2655 
2656 int
2657 scsi_dump_mode_sense_pages(page_control)
2658 	int			page_control;
2659 {
2660 	struct uscsi_cmd	ucmd;
2661 	union scsi_cdb		cdb;
2662 	char			*msbuf;
2663 	int			nbytes;
2664 	char			*pc_str;
2665 	int			status;
2666 	struct mode_header	*mh;
2667 	char			*p;
2668 	struct mode_page	*mp;
2669 	int			n;
2670 	char			s[16];
2671 	int			result = 0;
2672 
2673 	pc_str = find_string(page_control_strings, page_control);
2674 
2675 	/*
2676 	 * Allocate memory for the mode sense buffer.
2677 	 */
2678 	nbytes = 255;
2679 	msbuf = (char *)zalloc(nbytes);
2680 
2681 	/*
2682 	 * Build and execute the uscsi ioctl
2683 	 */
2684 	(void) memset(msbuf, 0, nbytes);
2685 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
2686 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2687 	cdb.scc_cmd = SCMD_MODE_SENSE;
2688 	FORMG0COUNT(&cdb, (uchar_t)nbytes);
2689 	cdb.cdb_opaque[2] = page_control | 0x3f;
2690 	ucmd.uscsi_cdb = (caddr_t)&cdb;
2691 	ucmd.uscsi_cdblen = CDB_GROUP0;
2692 	ucmd.uscsi_bufaddr = msbuf;
2693 	ucmd.uscsi_buflen = nbytes;
2694 	status = uscsi_cmd(cur_file, &ucmd,
2695 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2696 	if (status) {
2697 		err_print("\nMode sense page 0x3f (%s) failed\n",
2698 			pc_str);
2699 		result = 1;
2700 	} else {
2701 		err_print("\nMode sense pages (%s):\n", pc_str);
2702 		mh = (struct mode_header *)msbuf;
2703 		nbytes = mh->length - sizeof (struct mode_header) -
2704 				mh->bdesc_length + 1;
2705 		p = msbuf + sizeof (struct mode_header) +
2706 			mh->bdesc_length;
2707 		dump("         ", msbuf, sizeof (struct mode_header) +
2708 			(int)mh->bdesc_length, HEX_ONLY);
2709 		while (nbytes > 0) {
2710 			mp = (struct mode_page *)p;
2711 			n = mp->length + sizeof (struct mode_page);
2712 			nbytes -= n;
2713 			if (nbytes < 0)
2714 				break;
2715 			(void) sprintf(s, "   %3x:  ", mp->code);
2716 			dump(s, p, n, HEX_ONLY);
2717 			p += n;
2718 		}
2719 		if (nbytes < 0) {
2720 			err_print("  Sense data formatted incorrectly:\n");
2721 			dump("    ", msbuf, (int)mh->length+1, HEX_ONLY);
2722 			result = 1;
2723 		}
2724 		err_print("\n");
2725 	}
2726 
2727 	free(msbuf);
2728 	return (result);
2729 }
2730 
2731 
2732 static void
2733 scsi_printerr(ucmd, rq, rqlen)
2734 	struct uscsi_cmd		*ucmd;
2735 	struct scsi_extended_sense	*rq;
2736 	int				rqlen;
2737 {
2738 	diskaddr_t	blkno;
2739 	struct scsi_descr_sense_hdr *sdsp =
2740 	    (struct scsi_descr_sense_hdr *)rq;
2741 
2742 	switch (rq->es_key) {
2743 	case KEY_NO_SENSE:
2744 		err_print("No sense error");
2745 		break;
2746 	case KEY_RECOVERABLE_ERROR:
2747 		err_print("Recoverable error");
2748 		break;
2749 	case KEY_NOT_READY:
2750 		err_print("Not ready error");
2751 		break;
2752 	case KEY_MEDIUM_ERROR:
2753 		err_print("Medium error");
2754 		break;
2755 	case KEY_HARDWARE_ERROR:
2756 		err_print("Hardware error");
2757 		break;
2758 	case KEY_ILLEGAL_REQUEST:
2759 		err_print("Illegal request");
2760 		break;
2761 	case KEY_UNIT_ATTENTION:
2762 		err_print("Unit attention error");
2763 		break;
2764 	case KEY_WRITE_PROTECT:
2765 		err_print("Write protect error");
2766 		break;
2767 	case KEY_BLANK_CHECK:
2768 		err_print("Blank check error");
2769 		break;
2770 	case KEY_VENDOR_UNIQUE:
2771 		err_print("Vendor unique error");
2772 		break;
2773 	case KEY_COPY_ABORTED:
2774 		err_print("Copy aborted error");
2775 		break;
2776 	case KEY_ABORTED_COMMAND:
2777 		err_print("Aborted command");
2778 		break;
2779 	case KEY_EQUAL:
2780 		err_print("Equal error");
2781 		break;
2782 	case KEY_VOLUME_OVERFLOW:
2783 		err_print("Volume overflow");
2784 		break;
2785 	case KEY_MISCOMPARE:
2786 		err_print("Miscompare error");
2787 		break;
2788 	case KEY_RESERVED:
2789 		err_print("Reserved error");
2790 		break;
2791 	default:
2792 		err_print("Unknown error");
2793 		break;
2794 	}
2795 
2796 	err_print(" during %s", scsi_find_command_name(ucmd->uscsi_cdb[0]));
2797 
2798 	/*
2799 	 * Get asc, ascq and info field from sense data.  There are two
2800 	 * possible formats (fixed sense data and descriptor sense data)
2801 	 * depending on the value of es_code.
2802 	 */
2803 	switch (rq->es_code) {
2804 	case CODE_FMT_DESCR_CURRENT:
2805 	case CODE_FMT_DESCR_DEFERRED:
2806 		blkno =
2807 		    (diskaddr_t)scsi_extract_sense_info_descr(sdsp, rqlen);
2808 		if (blkno != (diskaddr_t)-1) {
2809 			err_print(": block %lld (0x%llx) (", blkno, blkno);
2810 			pr_dblock(err_print, blkno);
2811 			err_print(")");
2812 		}
2813 
2814 		err_print("\n");
2815 
2816 		err_print("ASC: 0x%x   ASCQ: 0x%x\n",
2817 		    sdsp->ds_add_code, sdsp->ds_qual_code);
2818 		break;
2819 	case CODE_FMT_FIXED_CURRENT:
2820 	case CODE_FMT_FIXED_DEFERRED:
2821 	default:
2822 		if (rq->es_valid) {
2823 			blkno = (rq->es_info_1 << 24) |
2824 			    (rq->es_info_2 << 16) |
2825 			    (rq->es_info_3 << 8) | rq->es_info_4;
2826 			err_print(": block %lld (0x%llx) (", blkno, blkno);
2827 			pr_dblock(err_print, blkno);
2828 			err_print(")");
2829 		}
2830 
2831 		err_print("\n");
2832 
2833 		if (rq->es_add_len >= 6) {
2834 			err_print("ASC: 0x%x   ASCQ: 0x%x\n",
2835 			    rq->es_add_code, rq->es_qual_code);
2836 		}
2837 		break;
2838 	}
2839 
2840 	if (option_msg && diag_msg) {
2841 		if (rq->es_key == KEY_ILLEGAL_REQUEST) {
2842 			dump("cmd:    ", (caddr_t)ucmd,
2843 			    sizeof (struct uscsi_cmd), HEX_ONLY);
2844 			dump("cdb:    ", (caddr_t)ucmd->uscsi_cdb,
2845 			    ucmd->uscsi_cdblen, HEX_ONLY);
2846 		}
2847 		dump("sense:  ", (caddr_t)rq, rqlen, HEX_ONLY);
2848 	}
2849 
2850 	if (option_msg) {
2851 		switch (rq->es_code) {
2852 		case CODE_FMT_DESCR_CURRENT:
2853 		case CODE_FMT_DESCR_DEFERRED:
2854 			scsi_print_descr_sense(sdsp, rqlen);
2855 			break;
2856 		case CODE_FMT_FIXED_CURRENT:
2857 		case CODE_FMT_FIXED_DEFERRED:
2858 		default:
2859 			scsi_print_extended_sense(rq, rqlen);
2860 			break;
2861 		}
2862 	}
2863 }
2864 
2865 /*
2866  * Retrieve "information" field from descriptor format
2867  * sense data.  Iterates through each sense descriptor
2868  * looking for the information descriptor and returns
2869  * the information field from that descriptor.
2870  */
2871 static diskaddr_t
2872 scsi_extract_sense_info_descr(struct scsi_descr_sense_hdr *sdsp, int rqlen)
2873 {
2874 	diskaddr_t result;
2875 	uint8_t *descr_offset;
2876 	int valid_sense_length;
2877 	struct scsi_information_sense_descr *isd;
2878 
2879 	/*
2880 	 * Initialize result to -1 indicating there is no information
2881 	 * descriptor
2882 	 */
2883 	result = (diskaddr_t)-1;
2884 
2885 	/*
2886 	 * The first descriptor will immediately follow the header
2887 	 */
2888 	descr_offset = (uint8_t *)(sdsp+1); /* Pointer arithmetic */
2889 
2890 	/*
2891 	 * Calculate the amount of valid sense data
2892 	 */
2893 	valid_sense_length =
2894 	    min((sizeof (struct scsi_descr_sense_hdr) +
2895 	    sdsp->ds_addl_sense_length),
2896 	    rqlen);
2897 
2898 	/*
2899 	 * Iterate through the list of descriptors, stopping when we
2900 	 * run out of sense data
2901 	 */
2902 	while ((descr_offset + sizeof (struct scsi_information_sense_descr)) <=
2903 	    (uint8_t *)sdsp + valid_sense_length) {
2904 		/*
2905 		 * Check if this is an information descriptor.  We can
2906 		 * use the scsi_information_sense_descr structure as a
2907 		 * template sense the first two fields are always the
2908 		 * same
2909 		 */
2910 		isd = (struct scsi_information_sense_descr *)descr_offset;
2911 		if (isd->isd_descr_type == DESCR_INFORMATION) {
2912 			/*
2913 			 * Found an information descriptor.  Copy the
2914 			 * information field.  There will only be one
2915 			 * information descriptor so we can stop looking.
2916 			 */
2917 			result =
2918 			    (((diskaddr_t)isd->isd_information[0] << 56) |
2919 				((diskaddr_t)isd->isd_information[1] << 48) |
2920 				((diskaddr_t)isd->isd_information[2] << 40) |
2921 				((diskaddr_t)isd->isd_information[3] << 32) |
2922 				((diskaddr_t)isd->isd_information[4] << 24) |
2923 				((diskaddr_t)isd->isd_information[5] << 16) |
2924 				((diskaddr_t)isd->isd_information[6] << 8)  |
2925 				((diskaddr_t)isd->isd_information[7]));
2926 			break;
2927 		}
2928 
2929 		/*
2930 		 * Get pointer to the next descriptor.  The "additional
2931 		 * length" field holds the length of the descriptor except
2932 		 * for the "type" and "additional length" fields, so
2933 		 * we need to add 2 to get the total length.
2934 		 */
2935 		descr_offset += (isd->isd_addl_length + 2);
2936 	}
2937 
2938 	return (result);
2939 }
2940 
2941 /*
2942  * Return a pointer to a string telling us the name of the command.
2943  */
2944 static char *
2945 scsi_find_command_name(uint_t cmd)
2946 {
2947 	struct scsi_command_name *c;
2948 
2949 	for (c = scsi_command_names; c->command != SCMD_UNKNOWN; c++)
2950 		if (c->command == cmd)
2951 			break;
2952 	return (c->name);
2953 }
2954 
2955 
2956 /*
2957  * Return true if we support a particular mode page
2958  */
2959 int
2960 scsi_supported_page(int page) {
2961 	return (page == 1 || page == 2 || page == 3 || page == 4 ||
2962 		page == 8 || page == 0x38);
2963 }
2964 
2965 
2966 int
2967 apply_chg_list(int pageno, int pagsiz, uchar_t *curbits,
2968 		uchar_t *chgbits, struct chg_list *chglist)
2969 {
2970 	uchar_t		c;
2971 	int		i;
2972 	int		m;
2973 	int		delta;
2974 	int		changed = 0;
2975 
2976 	while (chglist != NULL) {
2977 		if (chglist->pageno == pageno &&
2978 				chglist->byteno < pagsiz) {
2979 			i = chglist->byteno;
2980 			c = curbits[i];
2981 			switch (chglist->mode) {
2982 			case CHG_MODE_SET:
2983 				c |= (uchar_t)chglist->value;
2984 				break;
2985 			case CHG_MODE_CLR:
2986 				c &= (uchar_t)chglist->value;
2987 				break;
2988 			case CHG_MODE_ABS:
2989 				c = (uchar_t)chglist->value;
2990 				break;
2991 			}
2992 			/*
2993 			 * Figure out which bits changed, and
2994 			 * are marked as changeable.  If this
2995 			 * result actually differs from the
2996 			 * current value, update the current
2997 			 * value, and note that a mode select
2998 			 * should be done.
2999 			 */
3000 			delta = c ^ curbits[i];
3001 			for (m = 0x01; m < 0x100; m <<= 1) {
3002 				if ((delta & m) && (chgbits[i] & m)) {
3003 					curbits[i] ^= m;
3004 					changed = 1;
3005 				}
3006 			}
3007 		}
3008 		chglist = chglist->next;
3009 	}
3010 
3011 	return (changed);
3012 }
3013 
3014 
3015 /*
3016  * Return whether a given page is affected by an item on
3017  * the change list.
3018  */
3019 static int
3020 chg_list_affects_page(chglist, pageno)
3021 	struct chg_list	*chglist;
3022 	int		pageno;
3023 {
3024 	while (chglist != NULL) {
3025 		if (chglist->pageno == pageno) {
3026 			return (1);
3027 		}
3028 		chglist = chglist->next;
3029 	}
3030 
3031 	return (0);
3032 }
3033 
3034 
3035 /*
3036  * Labels for the various fields of the scsi_extended_sense structure
3037  */
3038 static char *scsi_extended_sense_labels[] = {
3039 	"Request sense valid:             ",
3040 	"Error class and code:            ",
3041 	"Segment number:                  ",
3042 	"Filemark:                        ",
3043 	"End-of-medium:                   ",
3044 	"Incorrect length indicator:      ",
3045 	"Sense key:                       ",
3046 	"Information field:               ",
3047 	"Additional sense length:         ",
3048 	"Command-specific information:    ",
3049 	"Additional sense code:           ",
3050 	"Additional sense code qualifier: ",
3051 	"Field replaceable unit code:     ",
3052 	"Sense-key specific:              ",
3053 	"Additional sense bytes:          "
3054 };
3055 
3056 
3057 /*
3058  * Display the full scsi_extended_sense as returned by the device
3059  */
3060 static void
3061 scsi_print_extended_sense(rq, rqlen)
3062 	struct scsi_extended_sense	*rq;
3063 	int				rqlen;
3064 {
3065 	char			**p;
3066 
3067 	p = scsi_extended_sense_labels;
3068 	if (rqlen < (sizeof (*rq) - 2) || !rq->es_valid) {
3069 		/*
3070 		 * target should be capable of returning at least 18
3071 		 * bytes of data, i.e upto rq->es_skey_specific field.
3072 		 * The additional sense bytes (2 or more ...) are optional.
3073 		 */
3074 		return;
3075 	}
3076 
3077 	fmt_print("\n%s%s\n", *p++, rq->es_valid ? "yes" : "no");
3078 	fmt_print("%s0x%02x\n", *p++, (rq->es_class << 4) + rq->es_code);
3079 	fmt_print("%s%d\n", *p++, rq->es_segnum);
3080 	fmt_print("%s%s\n", *p++, rq->es_filmk ? "yes" : "no");
3081 	fmt_print("%s%s\n", *p++, rq->es_eom ? "yes" : "no");
3082 	fmt_print("%s%s\n", *p++, rq->es_ili ? "yes" : "no");
3083 	fmt_print("%s%d\n", *p++, rq->es_key);
3084 
3085 	fmt_print("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++, rq->es_info_1,
3086 		rq->es_info_2, rq->es_info_3, rq->es_info_4);
3087 	fmt_print("%s%d\n", *p++, rq->es_add_len);
3088 	fmt_print("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++, rq->es_cmd_info[0],
3089 		rq->es_cmd_info[1], rq->es_cmd_info[2], rq->es_cmd_info[3]);
3090 	fmt_print("%s0x%02x = %d\n", *p++, rq->es_add_code, rq->es_add_code);
3091 	fmt_print("%s0x%02x = %d\n", *p++, rq->es_qual_code, rq->es_qual_code);
3092 	fmt_print("%s%d\n", *p++, rq->es_fru_code);
3093 	fmt_print("%s0x%02x 0x%02x 0x%02x\n", *p++, rq->es_skey_specific[0],
3094 		rq->es_skey_specific[1], rq->es_skey_specific[2]);
3095 	if (rqlen >= sizeof (*rq)) {
3096 		fmt_print("%s0x%02x 0x%02x%s\n", *p, rq->es_add_info[0],
3097 		rq->es_add_info[1], (rqlen > sizeof (*rq)) ? " ..." : "");
3098 	}
3099 
3100 	fmt_print("\n");
3101 }
3102 
3103 /*
3104  * Labels for the various fields of the scsi_descr_sense_hdr structure
3105  */
3106 static char *scsi_descr_sense_labels[] = {
3107 	"Error class and code:            ",
3108 	"Sense key:                       ",
3109 	"Additional sense length:         ",
3110 	"Additional sense code:           ",
3111 	"Additional sense code qualifier: ",
3112 	"Additional sense bytes:          "
3113 };
3114 
3115 
3116 /*
3117  * Display the full descriptor sense data as returned by the device
3118  */
3119 
3120 static void
3121 scsi_print_descr_sense(rq, rqlen)
3122 	struct scsi_descr_sense_hdr	*rq;
3123 	int				rqlen;
3124 {
3125 	char			**p;
3126 	uint8_t			*descr_offset;
3127 	int			valid_sense_length;
3128 	struct scsi_information_sense_descr *isd;
3129 
3130 	p = scsi_descr_sense_labels;
3131 	if (rqlen < sizeof (struct scsi_descr_sense_hdr)) {
3132 		/*
3133 		 * target must return at least 8 bytes of data
3134 		 */
3135 		return;
3136 	}
3137 
3138 	/* Print descriptor sense header */
3139 	fmt_print("%s0x%02x\n", *p++, (rq->ds_class << 4) + rq->ds_code);
3140 	fmt_print("%s%d\n", *p++, rq->ds_key);
3141 
3142 	fmt_print("%s%d\n", *p++, rq->ds_addl_sense_length);
3143 	fmt_print("%s0x%02x = %d\n", *p++, rq->ds_add_code, rq->ds_add_code);
3144 	fmt_print("%s0x%02x = %d\n", *p++, rq->ds_qual_code, rq->ds_qual_code);
3145 	fmt_print("\n");
3146 
3147 	/*
3148 	 * Now print any sense descriptors.   The first descriptor will
3149 	 * immediately follow the header
3150 	 */
3151 	descr_offset = (uint8_t *)(rq+1); /* Pointer arithmetic */
3152 
3153 	/*
3154 	 * Calculate the amount of valid sense data
3155 	 */
3156 	valid_sense_length =
3157 	    min((sizeof (struct scsi_descr_sense_hdr) +
3158 		rq->ds_addl_sense_length), rqlen);
3159 
3160 	/*
3161 	 * Iterate through the list of descriptors, stopping when we
3162 	 * run out of sense data.  Descriptor format is:
3163 	 *
3164 	 * <Descriptor type> <Descriptor length> <Descriptor data> ...
3165 	 */
3166 	while ((descr_offset + *(descr_offset + 1)) <=
3167 	    (uint8_t *)rq + valid_sense_length) {
3168 		/*
3169 		 * Determine descriptor type.  We can use the
3170 		 * scsi_information_sense_descr structure as a
3171 		 * template since the first two fields are always the
3172 		 * same.
3173 		 */
3174 		isd = (struct scsi_information_sense_descr *)descr_offset;
3175 		switch (isd->isd_descr_type) {
3176 		case DESCR_INFORMATION: {
3177 			uint64_t information;
3178 
3179 			information =
3180 			    (((uint64_t)isd->isd_information[0] << 56) |
3181 				((uint64_t)isd->isd_information[1] << 48) |
3182 				((uint64_t)isd->isd_information[2] << 40) |
3183 				((uint64_t)isd->isd_information[3] << 32) |
3184 				((uint64_t)isd->isd_information[4] << 24) |
3185 				((uint64_t)isd->isd_information[5] << 16) |
3186 				((uint64_t)isd->isd_information[6] << 8)  |
3187 				((uint64_t)isd->isd_information[7]));
3188 			fmt_print("Information field:               "
3189 			    "%0llx\n", information);
3190 			break;
3191 		}
3192 		case DESCR_COMMAND_SPECIFIC: {
3193 			struct scsi_cmd_specific_sense_descr *c =
3194 			    (struct scsi_cmd_specific_sense_descr *)isd;
3195 			uint64_t cmd_specific;
3196 
3197 			cmd_specific =
3198 			    (((uint64_t)c->css_cmd_specific_info[0] << 56) |
3199 				((uint64_t)c->css_cmd_specific_info[1] << 48) |
3200 				((uint64_t)c->css_cmd_specific_info[2] << 40) |
3201 				((uint64_t)c->css_cmd_specific_info[3] << 32) |
3202 				((uint64_t)c->css_cmd_specific_info[4] << 24) |
3203 				((uint64_t)c->css_cmd_specific_info[5] << 16) |
3204 				((uint64_t)c->css_cmd_specific_info[6] << 8)  |
3205 				((uint64_t)c->css_cmd_specific_info[7]));
3206 			fmt_print("Command-specific information:    "
3207 			    "%0llx\n", cmd_specific);
3208 			break;
3209 		}
3210 		case DESCR_SENSE_KEY_SPECIFIC: {
3211 			struct scsi_sk_specific_sense_descr *ssd =
3212 			    (struct scsi_sk_specific_sense_descr *)isd;
3213 			uint8_t *sk_spec_ptr = (uint8_t *)&ssd->sss_data;
3214 			fmt_print("Sense-key specific:              "
3215 			    "0x%02x 0x%02x 0x%02x\n", sk_spec_ptr[0],
3216 			    sk_spec_ptr[1], sk_spec_ptr[2]);
3217 			break;
3218 		}
3219 		case DESCR_FRU: {
3220 			struct scsi_fru_sense_descr *fsd =
3221 			    (struct scsi_fru_sense_descr *)isd;
3222 			fmt_print("Field replaceable unit code:     "
3223 			    "%d\n", fsd->fs_fru_code);
3224 			break;
3225 		}
3226 		case DESCR_BLOCK_COMMANDS: {
3227 			struct scsi_block_cmd_sense_descr *bsd =
3228 			    (struct scsi_block_cmd_sense_descr *)isd;
3229 			fmt_print("Incorrect length indicator:      "
3230 			    "%s\n", bsd->bcs_ili ? "yes" : "no");
3231 			break;
3232 		}
3233 		default:
3234 			/* Ignore */
3235 			break;
3236 		}
3237 
3238 		/*
3239 		 * Get pointer to the next descriptor.  The "additional
3240 		 * length" field holds the length of the descriptor except
3241 		 * for the "type" and "additional length" fields, so
3242 		 * we need to add 2 to get the total length.
3243 		 */
3244 		descr_offset += (isd->isd_addl_length + 2);
3245 	}
3246 
3247 	fmt_print("\n");
3248 }
3249 
3250 /*
3251  * Function checks if READ DEFECT DATA command is supported
3252  * on the current disk.
3253  */
3254 static int
3255 check_support_for_defects()
3256 {
3257 	struct uscsi_cmd	ucmd;
3258 	union scsi_cdb		cdb;
3259 	struct scsi_defect_list	def_list;
3260 	struct scsi_defect_hdr	*hdr;
3261 	int			status;
3262 	char			rqbuf[255];
3263 	struct scsi_extended_sense	*rq;
3264 
3265 	hdr = (struct scsi_defect_hdr *)&def_list;
3266 
3267 	/*
3268 	 * First get length of list by asking for the header only.
3269 	 */
3270 	(void) memset((char *)&def_list, 0, sizeof (def_list));
3271 
3272 	/*
3273 	 * Build and execute the uscsi ioctl
3274 	 */
3275 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
3276 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
3277 	(void) memset((char *)rqbuf, 0, 255);
3278 	cdb.scc_cmd = SCMD_READ_DEFECT_LIST;
3279 	FORMG1COUNT(&cdb, sizeof (struct scsi_defect_hdr));
3280 	cdb.cdb_opaque[2] = DLD_MAN_DEF_LIST | DLD_BFI_FORMAT;
3281 	ucmd.uscsi_cdb = (caddr_t)&cdb;
3282 	ucmd.uscsi_cdblen = CDB_GROUP1;
3283 	ucmd.uscsi_bufaddr = (caddr_t)hdr;
3284 	ucmd.uscsi_buflen = sizeof (struct scsi_defect_hdr);
3285 	ucmd.uscsi_rqbuf = rqbuf;
3286 	ucmd.uscsi_rqlen = sizeof (rqbuf);
3287 	ucmd.uscsi_rqresid = sizeof (rqbuf);
3288 	rq = (struct scsi_extended_sense *)ucmd.uscsi_rqbuf;
3289 
3290 	status = uscsi_cmd(cur_file, &ucmd,
3291 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
3292 
3293 	if (status != 0) {
3294 		/*
3295 		 * check if read_defect_list_is_supported.
3296 		 */
3297 		if (ucmd.uscsi_rqstatus == STATUS_GOOD &&
3298 			rq->es_key == KEY_ILLEGAL_REQUEST &&
3299 				rq->es_add_code == INVALID_OPCODE)
3300 				return (0);
3301 	}
3302 	return (1);
3303 }
3304 
3305 /*
3306  * Format the disk, the whole disk, and nothing but the disk.
3307  * Function will be called only for disks
3308  * which do not support read defect list command.
3309  */
3310 static int
3311 scsi_format_without_defects()
3312 {
3313 	struct uscsi_cmd	ucmd;
3314 	union scsi_cdb		cdb;
3315 	struct scsi_defect_hdr	defect_hdr;
3316 	int			status;
3317 
3318 	/*
3319 	 * Construct the uscsi format ioctl.
3320 	 * Use fmtdata = 0 , indicating the no source of
3321 	 * defects information is provided .
3322 	 * Function will be called only for disks
3323 	 * which do not support read defect list command.
3324 	 */
3325 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
3326 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
3327 	(void) memset((char *)&defect_hdr, 0, sizeof (defect_hdr));
3328 	cdb.scc_cmd = SCMD_FORMAT;
3329 	ucmd.uscsi_cdb = (caddr_t)&cdb;
3330 	ucmd.uscsi_cdblen = CDB_GROUP0;
3331 	ucmd.uscsi_bufaddr = (caddr_t)&defect_hdr;
3332 	ucmd.uscsi_buflen = sizeof (defect_hdr);
3333 	cdb.cdb_opaque[1] = 0;
3334 	/*
3335 	 * Issue the format ioctl
3336 	 */
3337 	status = uscsi_cmd(cur_file, &ucmd,
3338 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
3339 	return (status);
3340 }
3341 
3342 /*
3343  * Name: test_until_ready
3344  *
3345  * Description: This function is used by scsi_format and
3346  *   scsi_format_raw to poll the device while the FORMAT
3347  *   UNIT cdb in in progress.
3348  *
3349  * Parameters:
3350  *   file descriptor to poll
3351  *
3352  * Returns:
3353  *   0 - good status
3354  *   !0 - bad status
3355  */
3356 static int test_until_ready(int fd) {
3357 	int				status = 1;
3358 	struct uscsi_cmd		ucmd;
3359 	union scsi_cdb			cdb;
3360 	struct scsi_extended_sense	sense;
3361 	time_t				start, check, time_left;
3362 	uint16_t 			progress;
3363 	int				hour, min, sec;
3364 
3365 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
3366 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
3367 
3368 	ucmd.uscsi_cdb		= (caddr_t)&cdb;
3369 	ucmd.uscsi_cdblen	= CDB_GROUP0;
3370 	ucmd.uscsi_rqbuf	= (caddr_t)&sense;
3371 	ucmd.uscsi_rqlen	= SENSE_LEN;
3372 
3373 	start = check = time((time_t *)0);
3374 
3375 	/* Loop sending TEST UNIT READY until format is complete */
3376 	while (status) {
3377 		/* clear last request sense data */
3378 		ucmd.uscsi_rqstatus	= 0;
3379 		ucmd.uscsi_rqresid	= 0;
3380 		(void) memset((char *)&sense, 0, SENSE_LEN);
3381 
3382 		/* issue test unit ready */
3383 		status = uscsi_cmd(fd, &ucmd, F_SILENT
3384 				| F_RQENABLE);
3385 
3386 		check = time((time_t *)0);
3387 
3388 		/* If device returns not ready we get EIO */
3389 		if (status != 0 && errno == EIO) {
3390 			/* Check SKSV if progress indication is avail */
3391 			if (sense.es_skey_specific[0] == 0x80) {
3392 				/* Store progress indication */
3393 				progress = ((uint16_t)sense.
3394 					es_skey_specific[1]) << 8;
3395 				progress |= (uint16_t)sense.
3396 					es_skey_specific[2];
3397 				progress = (uint16_t)(((float)progress /
3398 					(float)PROGRESS_INDICATION_BASE)*100);
3399 
3400 				fmt_print("\015");
3401 
3402 				/*
3403 				 * check to see if we can estimate
3404 				 * time remaining  - wait until the format
3405 				 * is at least 5 percent complete to avoid
3406 				 * wildly-fluctuating time estimates
3407 				 */
3408 				if ((check - start) <= 0 || progress <= 5) {
3409 					/* unable to estimate */
3410 					fmt_print("  %02d%% complete ",
3411 						progress);
3412 				} else {
3413 					/* display with estimated time */
3414 					time_left = (time_t)(((float)(check
3415 						- start) / (float)progress) *
3416 						(float)(100 - progress));
3417 					sec = time_left % 60;
3418 					min = (time_left / 60) % 60;
3419 					hour = time_left / 3600;
3420 
3421 					fmt_print("  %02d%% complete "
3422 						"(%02d:%02d:%02d remaining) ",
3423 						progress, hour, min, sec);
3424 				}
3425 				/* flush or the screen will not update */
3426 				(void) fflush(stdout);
3427 			}
3428 		} else {
3429 			/* format not in progress */
3430 			if (option_msg) {
3431 			fmt_print("\nRequest Sense ASC=0x%x ASCQ=0x%x",
3432 				sense.es_add_code, sense.es_qual_code);
3433 			}
3434 			break;
3435 		}
3436 
3437 		/* delay so we don't waste cpu time */
3438 		(void) sleep(RETRY_DELAY);
3439 	}
3440 	return (status);
3441 }
3442