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