xref: /onnv-gate/usr/src/cmd/format/ctlr_scsi.c (revision 12930:e50abc8ca9fa)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
56765Sbz211116  * Common Development and Distribution License (the "License").
66765Sbz211116  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*12930SRalph.Turner@Sun.COM  * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
230Sstevel@tonic-gate  */
240Sstevel@tonic-gate 
250Sstevel@tonic-gate /*
260Sstevel@tonic-gate  * This file contains the routines for embedded scsi disks
270Sstevel@tonic-gate  */
280Sstevel@tonic-gate #include "global.h"
290Sstevel@tonic-gate 
300Sstevel@tonic-gate #include <sys/types.h>
310Sstevel@tonic-gate #include <sys/param.h>
320Sstevel@tonic-gate #include <sys/ioctl.h>
330Sstevel@tonic-gate #include <sys/uio.h>
340Sstevel@tonic-gate #include <sys/fcntl.h>
350Sstevel@tonic-gate #include <errno.h>
360Sstevel@tonic-gate #include <memory.h>
370Sstevel@tonic-gate #include <malloc.h>
380Sstevel@tonic-gate #include <unistd.h>
390Sstevel@tonic-gate #include <stdlib.h>
400Sstevel@tonic-gate #include <values.h>
410Sstevel@tonic-gate #include <sys/byteorder.h>
420Sstevel@tonic-gate 
430Sstevel@tonic-gate 
440Sstevel@tonic-gate 
450Sstevel@tonic-gate #include "startup.h"
460Sstevel@tonic-gate #include "scsi_com.h"
470Sstevel@tonic-gate #include "misc.h"
480Sstevel@tonic-gate #include "ctlr_scsi.h"
490Sstevel@tonic-gate #include "analyze.h"
500Sstevel@tonic-gate #include "param.h"
510Sstevel@tonic-gate #include "io.h"
520Sstevel@tonic-gate 
530Sstevel@tonic-gate 
540Sstevel@tonic-gate #ifndef	DAD_MODE_CACHE_CCS
550Sstevel@tonic-gate #define	DAD_MODE_CACHE_CCS		0x38
560Sstevel@tonic-gate #endif /* DAD_MODE_CACHE_CCS */
570Sstevel@tonic-gate 
580Sstevel@tonic-gate /* format defect header bits */
590Sstevel@tonic-gate #define	FDH_FOV				0x80
600Sstevel@tonic-gate #define	FDH_IMMED			0x02
610Sstevel@tonic-gate 
620Sstevel@tonic-gate #define	SENSE_LEN			20
630Sstevel@tonic-gate 
640Sstevel@tonic-gate #define	RETRY_DELAY			5
650Sstevel@tonic-gate 
660Sstevel@tonic-gate #define	PROGRESS_INDICATION_BASE	65536
670Sstevel@tonic-gate 
680Sstevel@tonic-gate #ifdef	__STDC__
690Sstevel@tonic-gate /*
700Sstevel@tonic-gate  *	Local prototypes for ANSI C compilers
710Sstevel@tonic-gate  */
720Sstevel@tonic-gate static int	scsi_format(uint64_t, uint64_t, struct defect_list *);
730Sstevel@tonic-gate static int	scsi_raw_format(void);
740Sstevel@tonic-gate static int	scsi_ms_page8(int);
750Sstevel@tonic-gate static int	scsi_ms_page38(int);
760Sstevel@tonic-gate static void	scsi_convert_list_to_new(struct defect_list *,
770Sstevel@tonic-gate 			struct scsi_defect_list *, int);
780Sstevel@tonic-gate static char	*scsi_find_command_name(uint_t);
790Sstevel@tonic-gate static int	chg_list_affects_page(struct chg_list *, int);
800Sstevel@tonic-gate static void	scsi_printerr(struct uscsi_cmd *,
810Sstevel@tonic-gate 			struct scsi_extended_sense *, int);
820Sstevel@tonic-gate static diskaddr_t
830Sstevel@tonic-gate scsi_extract_sense_info_descr(struct scsi_descr_sense_hdr *sdsp, int rqlen);
840Sstevel@tonic-gate 
850Sstevel@tonic-gate static void	scsi_print_extended_sense(struct scsi_extended_sense *, int);
860Sstevel@tonic-gate static void	scsi_print_descr_sense(struct scsi_descr_sense_hdr *, int);
870Sstevel@tonic-gate 
880Sstevel@tonic-gate static int	test_until_ready(int fd);
890Sstevel@tonic-gate static int	uscsi_reserve_release(int, int);
900Sstevel@tonic-gate static int	check_support_for_defects(void);
910Sstevel@tonic-gate static int	scsi_format_without_defects(void);
920Sstevel@tonic-gate static int	scsi_ms_page1(int);
930Sstevel@tonic-gate static int	scsi_ms_page2(int);
940Sstevel@tonic-gate static int	scsi_ms_page3(int);
950Sstevel@tonic-gate static int	scsi_ms_page4(int);
960Sstevel@tonic-gate static int	scsi_repair(uint64_t, int);
970Sstevel@tonic-gate static int	scsi_read_defect_data(struct defect_list *, int);
980Sstevel@tonic-gate static int	scsi_ck_format(void);
990Sstevel@tonic-gate 
1000Sstevel@tonic-gate #else	/* __STDC__ */
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate static int	scsi_format();
1030Sstevel@tonic-gate static int	scsi_raw_format();
1040Sstevel@tonic-gate static int	scsi_ms_page8();
1050Sstevel@tonic-gate static int	scsi_ms_page38();
1060Sstevel@tonic-gate static void	scsi_convert_list_to_new();
1070Sstevel@tonic-gate static char	*scsi_find_command_name();
1080Sstevel@tonic-gate static int	chg_list_affects_page();
1090Sstevel@tonic-gate static void	scsi_printerr();
1100Sstevel@tonic-gate static diskaddr_t	scsi_extract_sense_info_descr();
1110Sstevel@tonic-gate static void	scsi_print_extended_sense();
1120Sstevel@tonic-gate static void	scsi_print_descr_sense();
1130Sstevel@tonic-gate 
1140Sstevel@tonic-gate static int	test_until_ready();
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate static int	uscsi_reserve_release();
1170Sstevel@tonic-gate static int	check_support_for_defects();
1180Sstevel@tonic-gate static int	scsi_format_without_defects();
1190Sstevel@tonic-gate static int	scsi_ms_page1();
1200Sstevel@tonic-gate static int	scsi_ms_page2();
1210Sstevel@tonic-gate static int	scsi_ms_page3();
1220Sstevel@tonic-gate static int	scsi_ms_page4();
1230Sstevel@tonic-gate static int	scsi_repair();
1240Sstevel@tonic-gate static int	scsi_read_defect_data();
1250Sstevel@tonic-gate static int	scsi_ck_format();
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate #endif	/* __STDC__ */
1280Sstevel@tonic-gate 
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate 
1310Sstevel@tonic-gate struct	ctlr_ops scsiops = {
1320Sstevel@tonic-gate 	scsi_rdwr,
1330Sstevel@tonic-gate 	scsi_ck_format,
1340Sstevel@tonic-gate 	scsi_format,
1350Sstevel@tonic-gate 	scsi_ex_man,
1360Sstevel@tonic-gate 	scsi_ex_cur,
1370Sstevel@tonic-gate 	scsi_repair,
1380Sstevel@tonic-gate 	0,
1390Sstevel@tonic-gate };
1400Sstevel@tonic-gate 
1410Sstevel@tonic-gate #define	SCMD_UNKNOWN		0xff
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate /*
1440Sstevel@tonic-gate  * Names of commands.  Must have SCMD_UNKNOWN at end of list.
1450Sstevel@tonic-gate  */
1460Sstevel@tonic-gate static struct scsi_command_name {
1470Sstevel@tonic-gate 	uchar_t command;
1480Sstevel@tonic-gate 	char *name;
1490Sstevel@tonic-gate } scsi_command_names[] = {
1500Sstevel@tonic-gate 	SCMD_FORMAT,		"format",
1510Sstevel@tonic-gate 	SCMD_READ,		"read",
1520Sstevel@tonic-gate 	SCMD_WRITE,		"write",
1530Sstevel@tonic-gate 	SCMD_READ|SCMD_GROUP1,	"read",
1540Sstevel@tonic-gate 	SCMD_WRITE|SCMD_GROUP1,	"write",
1550Sstevel@tonic-gate 	SCMD_INQUIRY,		"inquiry",
1560Sstevel@tonic-gate 	SCMD_MODE_SELECT,	"mode select",
1570Sstevel@tonic-gate 	SCMD_MODE_SENSE,	"mode sense",
1580Sstevel@tonic-gate 	SCMD_REASSIGN_BLOCK,	"reassign block",
1590Sstevel@tonic-gate 	SCMD_READ_DEFECT_LIST,	"read defect list",
1600Sstevel@tonic-gate 	SCMD_UNKNOWN,		"unknown"
1610Sstevel@tonic-gate };
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate /*
1650Sstevel@tonic-gate  * Strings for printing mode sense page control values
1660Sstevel@tonic-gate  */
1670Sstevel@tonic-gate static slist_t page_control_strings[] = {
1680Sstevel@tonic-gate 	{ "current",	"",	MODE_SENSE_PC_CURRENT },
1690Sstevel@tonic-gate 	{ "changeable",	"",	MODE_SENSE_PC_CHANGEABLE },
1700Sstevel@tonic-gate 	{ "default",	"",	MODE_SENSE_PC_DEFAULT },
1710Sstevel@tonic-gate 	{ "saved",	"",	MODE_SENSE_PC_SAVED }
1720Sstevel@tonic-gate };
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate /*
1750Sstevel@tonic-gate  * Strings for printing the mode select options
1760Sstevel@tonic-gate  */
1770Sstevel@tonic-gate static slist_t mode_select_strings[] = {
1780Sstevel@tonic-gate 	{ "",		"",	0 },
1790Sstevel@tonic-gate 	{ " (pf)",	"",	MODE_SELECT_PF },
1800Sstevel@tonic-gate 	{ " (sp)",	"",	MODE_SELECT_SP },
1810Sstevel@tonic-gate 	{ " (pf,sp)",	"",	MODE_SELECT_PF|MODE_SELECT_SP }
1820Sstevel@tonic-gate };
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate static int scsi_format_revolutions = 5;
1850Sstevel@tonic-gate static int scsi_format_timeout = 2*60*60;		/* two hours */
1860Sstevel@tonic-gate 
1870Sstevel@tonic-gate /*
1880Sstevel@tonic-gate  * READ DEFECT DATA commands is optional as per SCSI-2 spec.
1890Sstevel@tonic-gate  * Hence check if the read_defect_data command fails with
1900Sstevel@tonic-gate  * Invalid Opcode so that we can give a more meaningful message
1910Sstevel@tonic-gate  * to the user.
1920Sstevel@tonic-gate  */
1930Sstevel@tonic-gate #define	INVALID_OPCODE	0x20
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate /*
1960Sstevel@tonic-gate  * Read or write the disk.
1970Sstevel@tonic-gate  */
1980Sstevel@tonic-gate int
scsi_rdwr(dir,fd,blkno,secnt,bufaddr,flags,xfercntp)1990Sstevel@tonic-gate scsi_rdwr(dir, fd, blkno, secnt, bufaddr, flags, xfercntp)
2000Sstevel@tonic-gate 	int	dir;
2010Sstevel@tonic-gate 	int	fd;
2020Sstevel@tonic-gate 	diskaddr_t	blkno;
2030Sstevel@tonic-gate 	int	secnt;
2040Sstevel@tonic-gate 	caddr_t bufaddr;
2050Sstevel@tonic-gate 	int	flags;
2060Sstevel@tonic-gate 	int	*xfercntp;
2070Sstevel@tonic-gate {
2080Sstevel@tonic-gate 	struct uscsi_cmd	ucmd;
2090Sstevel@tonic-gate 	union scsi_cdb		cdb;
2100Sstevel@tonic-gate 	int	max_sectors;
2110Sstevel@tonic-gate 	int	rc = 0;
2120Sstevel@tonic-gate 
2130Sstevel@tonic-gate 	/*
2140Sstevel@tonic-gate 	 * If the max xfercnt hasn't been determined start with BUF_SECTS
2150Sstevel@tonic-gate 	 * (currently 126 == 63K), otherwise use the xfercnt value
2160Sstevel@tonic-gate 	 * my caller saved from the previous invocation.
2170Sstevel@tonic-gate 	 */
2180Sstevel@tonic-gate 	if (xfercntp == NULL) {
2190Sstevel@tonic-gate 		max_sectors = BUF_SECTS;
2200Sstevel@tonic-gate 	} else if (*xfercntp == 0) {
2210Sstevel@tonic-gate 		max_sectors = BUF_SECTS;
2220Sstevel@tonic-gate 		*xfercntp = max_sectors;
2230Sstevel@tonic-gate 	} else {
2240Sstevel@tonic-gate 		max_sectors = *xfercntp;
2250Sstevel@tonic-gate 	}
2260Sstevel@tonic-gate 
2270Sstevel@tonic-gate 	/*
2280Sstevel@tonic-gate 	 * Build and execute the uscsi ioctl.  We build a group0
2290Sstevel@tonic-gate 	 * or group1 command as necessary, since some targets
2300Sstevel@tonic-gate 	 * do not support group1 commands.
2310Sstevel@tonic-gate 	 */
2320Sstevel@tonic-gate 	while (secnt)  {
2330Sstevel@tonic-gate 		int	nsectors;
2340Sstevel@tonic-gate 
2350Sstevel@tonic-gate 		nsectors = (max_sectors < secnt) ? max_sectors : secnt;
2360Sstevel@tonic-gate 		(void) memset((char *)&ucmd, 0, sizeof (ucmd));
2370Sstevel@tonic-gate 		(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2380Sstevel@tonic-gate 		cdb.scc_cmd = (dir == DIR_READ) ? SCMD_READ : SCMD_WRITE;
2390Sstevel@tonic-gate 		if (blkno < (2<<20) && nsectors <= 0xff) {
2400Sstevel@tonic-gate 			FORMG0ADDR(&cdb, blkno);
2410Sstevel@tonic-gate 			FORMG0COUNT(&cdb, nsectors);
2420Sstevel@tonic-gate 			ucmd.uscsi_cdblen = CDB_GROUP0;
2430Sstevel@tonic-gate 		} else {
2440Sstevel@tonic-gate 			if (blkno > 0xffffffff) {
2450Sstevel@tonic-gate 				FORMG4LONGADDR(&cdb, blkno);
2460Sstevel@tonic-gate 				FORMG4COUNT(&cdb, nsectors);
2470Sstevel@tonic-gate 				ucmd.uscsi_cdblen = CDB_GROUP4;
2480Sstevel@tonic-gate 				cdb.scc_cmd |= SCMD_GROUP4;
2490Sstevel@tonic-gate 			} else {
2500Sstevel@tonic-gate 				FORMG1ADDR(&cdb, blkno);
2510Sstevel@tonic-gate 				FORMG1COUNT(&cdb, nsectors);
2520Sstevel@tonic-gate 				ucmd.uscsi_cdblen = CDB_GROUP1;
2530Sstevel@tonic-gate 				cdb.scc_cmd |= SCMD_GROUP1;
2540Sstevel@tonic-gate 			}
2550Sstevel@tonic-gate 		}
2560Sstevel@tonic-gate 		ucmd.uscsi_cdb = (caddr_t)&cdb;
2570Sstevel@tonic-gate 		ucmd.uscsi_bufaddr = bufaddr;
2589889SLarry.Liu@Sun.COM 		ucmd.uscsi_buflen = nsectors * cur_blksz;
2590Sstevel@tonic-gate 		rc = uscsi_cmd(fd, &ucmd, flags);
2600Sstevel@tonic-gate 
2610Sstevel@tonic-gate 		if (rc != 0)
2620Sstevel@tonic-gate 			break;
2630Sstevel@tonic-gate 
2640Sstevel@tonic-gate 		/*
2650Sstevel@tonic-gate 		 * check if partial DMA breakup required
2660Sstevel@tonic-gate 		 * if so, reduce the request size by half and retry
2670Sstevel@tonic-gate 		 * the last request
2680Sstevel@tonic-gate 		 */
2690Sstevel@tonic-gate 		if (ucmd.uscsi_resid == ucmd.uscsi_buflen) {
2700Sstevel@tonic-gate 			max_sectors >>= 1;
2710Sstevel@tonic-gate 			if (max_sectors <= 0) {
2720Sstevel@tonic-gate 				rc = -1;
2730Sstevel@tonic-gate 				break;
2740Sstevel@tonic-gate 			}
2750Sstevel@tonic-gate 			continue;
2760Sstevel@tonic-gate 		}
2770Sstevel@tonic-gate 		if (ucmd.uscsi_resid != 0) {
2780Sstevel@tonic-gate 			rc = -1;
2790Sstevel@tonic-gate 			break;
2800Sstevel@tonic-gate 		}
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate 		blkno += nsectors;
2830Sstevel@tonic-gate 		secnt -= nsectors;
2849889SLarry.Liu@Sun.COM 		bufaddr += nsectors * cur_blksz;
2850Sstevel@tonic-gate 	}
2860Sstevel@tonic-gate 
2870Sstevel@tonic-gate 	/*
2880Sstevel@tonic-gate 	 * If the xfercnt wasn't previously saved or if the
2890Sstevel@tonic-gate 	 * new value is smaller than the old value, save the
2900Sstevel@tonic-gate 	 * current value in my caller's save area.
2910Sstevel@tonic-gate 	 */
2920Sstevel@tonic-gate 	if (xfercntp != NULL && max_sectors < *xfercntp) {
2930Sstevel@tonic-gate 		if (diag_msg)
2940Sstevel@tonic-gate 			err_print("reducing xfercnt %d %d\n",
2950Sstevel@tonic-gate 					*xfercntp, max_sectors);
2960Sstevel@tonic-gate 		*xfercntp = max_sectors;
2970Sstevel@tonic-gate 	}
2980Sstevel@tonic-gate 	return (rc);
2990Sstevel@tonic-gate }
3000Sstevel@tonic-gate 
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate /*
3030Sstevel@tonic-gate  * Check to see if the disk has been formatted.
3040Sstevel@tonic-gate  * If we are able to read the first track, we conclude that
3050Sstevel@tonic-gate  * the disk has been formatted.
3060Sstevel@tonic-gate  */
3070Sstevel@tonic-gate #ifdef i386
3080Sstevel@tonic-gate static int
3090Sstevel@tonic-gate #else /* i386 */
3100Sstevel@tonic-gate static int
3110Sstevel@tonic-gate #endif /* i386 */
scsi_ck_format(void)3120Sstevel@tonic-gate scsi_ck_format(void)
3130Sstevel@tonic-gate {
3140Sstevel@tonic-gate 	int	status;
3150Sstevel@tonic-gate 
3160Sstevel@tonic-gate 	/*
3170Sstevel@tonic-gate 	 * Try to read the first four blocks.
3180Sstevel@tonic-gate 	 */
3197563SPrasad.Singamsetty@Sun.COM 	status = scsi_rdwr(DIR_READ, cur_file, (diskaddr_t)0, 4,
3209889SLarry.Liu@Sun.COM 	    (caddr_t)cur_buf, F_SILENT, NULL);
3210Sstevel@tonic-gate 	return (!status);
3220Sstevel@tonic-gate }
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate /*
3260Sstevel@tonic-gate  * Format the disk, the whole disk, and nothing but the disk.
3270Sstevel@tonic-gate  */
3280Sstevel@tonic-gate /*ARGSUSED*/
3290Sstevel@tonic-gate static int
scsi_format(start,end,list)3300Sstevel@tonic-gate scsi_format(start, end, list)
3310Sstevel@tonic-gate 	uint64_t		start;		/* irrelevant for us */
3320Sstevel@tonic-gate 	uint64_t		end;
3330Sstevel@tonic-gate 	struct defect_list	*list;
3340Sstevel@tonic-gate {
3350Sstevel@tonic-gate 	struct uscsi_cmd	ucmd;
3360Sstevel@tonic-gate 	union scsi_cdb		cdb;
3370Sstevel@tonic-gate 	int			status;
3380Sstevel@tonic-gate 	int			flag;
33912594SShengliang.Zhang@Sun.COM 	char			rawbuf[MAX_MODE_SENSE_SIZE];
3400Sstevel@tonic-gate 	struct scsi_inquiry	*inq;
34112594SShengliang.Zhang@Sun.COM 	uint8_t	fmt_prot_info;
34212594SShengliang.Zhang@Sun.COM 	uint8_t	prot_field_usage;
34312594SShengliang.Zhang@Sun.COM 	uint8_t	param_long_list = 1;
34412594SShengliang.Zhang@Sun.COM 	uint8_t	fmt_long_param_header[8];
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate 	/*
3470Sstevel@tonic-gate 	 * Determine if the target appears to be SCSI-2
3480Sstevel@tonic-gate 	 * compliant.  We handle mode sense/mode selects
3490Sstevel@tonic-gate 	 * a little differently, depending upon CCS/SCSI-2
3500Sstevel@tonic-gate 	 */
3510Sstevel@tonic-gate 	if (uscsi_inquiry(cur_file, rawbuf, sizeof (rawbuf))) {
3520Sstevel@tonic-gate 		err_print("Inquiry failed\n");
3530Sstevel@tonic-gate 		return (-1);
3540Sstevel@tonic-gate 	}
3550Sstevel@tonic-gate 	inq = (struct scsi_inquiry *)rawbuf;
3560Sstevel@tonic-gate 	flag = (inq->inq_rdf == RDF_SCSI2);
3570Sstevel@tonic-gate 
3580Sstevel@tonic-gate 	/*
3590Sstevel@tonic-gate 	 * Reserve the scsi disk before performing mode select and
3600Sstevel@tonic-gate 	 * format operations. This will keep other hosts, if any, from
3610Sstevel@tonic-gate 	 * touching the disk while we are here.
3620Sstevel@tonic-gate 	 */
3630Sstevel@tonic-gate 	if (uscsi_reserve_release(cur_file, SCMD_RESERVE)) {
3640Sstevel@tonic-gate 		err_print("Reserve failed\n");
3650Sstevel@tonic-gate 		return (-1);
3660Sstevel@tonic-gate 	}
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate 	/*
3690Sstevel@tonic-gate 	 * Set up the various SCSI parameters specified before
3700Sstevel@tonic-gate 	 * formatting the disk.  Each routine handles the
3713517Smp204432 	 * parameters relevant to a particular page.
3720Sstevel@tonic-gate 	 * If no parameters are specified for a page, there's
3730Sstevel@tonic-gate 	 * no need to do anything.  Otherwise, issue a mode
3740Sstevel@tonic-gate 	 * sense for that page.  If a specified parameter
3750Sstevel@tonic-gate 	 * differs from the drive's default value, and that
3760Sstevel@tonic-gate 	 * parameter is not fixed, then issue a mode select to
3770Sstevel@tonic-gate 	 * set the default value for the disk as specified
3780Sstevel@tonic-gate 	 * in format.dat.
3790Sstevel@tonic-gate 	 */
3800Sstevel@tonic-gate 	if (scsi_ms_page1(flag) || scsi_ms_page2(flag) ||
3810Sstevel@tonic-gate 		scsi_ms_page4(flag) || scsi_ms_page38(flag) ||
3820Sstevel@tonic-gate 			scsi_ms_page8(flag) || scsi_ms_page3(flag)) {
3830Sstevel@tonic-gate 		(void) uscsi_reserve_release(cur_file, SCMD_RELEASE);
3840Sstevel@tonic-gate 		return (-1);
3850Sstevel@tonic-gate 	}
3860Sstevel@tonic-gate 
3870Sstevel@tonic-gate 	/*
3880Sstevel@tonic-gate 	 * If we're debugging the drive, dump every page
3890Sstevel@tonic-gate 	 * the device supports, for thorough analysis.
3900Sstevel@tonic-gate 	 */
3910Sstevel@tonic-gate 	if (option_msg && diag_msg) {
3920Sstevel@tonic-gate 		(void) scsi_dump_mode_sense_pages(MODE_SENSE_PC_DEFAULT);
3930Sstevel@tonic-gate 		(void) scsi_dump_mode_sense_pages(MODE_SENSE_PC_CURRENT);
3940Sstevel@tonic-gate 		(void) scsi_dump_mode_sense_pages(MODE_SENSE_PC_SAVED);
3950Sstevel@tonic-gate 		(void) scsi_dump_mode_sense_pages(MODE_SENSE_PC_CHANGEABLE);
3960Sstevel@tonic-gate 		err_print("\n");
3970Sstevel@tonic-gate 	}
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate 	/*
40012594SShengliang.Zhang@Sun.COM 	 * Determine the FMTPINFO field in format cdb, and the
40112594SShengliang.Zhang@Sun.COM 	 * PROTECTION FIELD USAGE in the long parameter list, via
40212594SShengliang.Zhang@Sun.COM 	 * the protection type input by users.
40312594SShengliang.Zhang@Sun.COM 	 */
40412594SShengliang.Zhang@Sun.COM 	switch (prot_type) {
40512594SShengliang.Zhang@Sun.COM 	case PROT_TYPE_0:
40612594SShengliang.Zhang@Sun.COM 		fmt_prot_info = 0x00;
40712594SShengliang.Zhang@Sun.COM 		prot_field_usage = 0x00;
40812594SShengliang.Zhang@Sun.COM 		break;
40912594SShengliang.Zhang@Sun.COM 	case PROT_TYPE_1:
41012594SShengliang.Zhang@Sun.COM 		fmt_prot_info = 0x02;
41112594SShengliang.Zhang@Sun.COM 		prot_field_usage = 0x00;
41212594SShengliang.Zhang@Sun.COM 		break;
41312594SShengliang.Zhang@Sun.COM 	case PROT_TYPE_2:
41412594SShengliang.Zhang@Sun.COM 		fmt_prot_info = 0x03;
41512594SShengliang.Zhang@Sun.COM 		prot_field_usage = 0x00;
41612594SShengliang.Zhang@Sun.COM 		break;
41712594SShengliang.Zhang@Sun.COM 	case PROT_TYPE_3:
41812594SShengliang.Zhang@Sun.COM 		fmt_prot_info = 0x03;
41912594SShengliang.Zhang@Sun.COM 		prot_field_usage = 0x01;
42012594SShengliang.Zhang@Sun.COM 		break;
42112594SShengliang.Zhang@Sun.COM 	default:
42212594SShengliang.Zhang@Sun.COM 		fmt_print("invalid protection type\n");
42312594SShengliang.Zhang@Sun.COM 		return (-1);
42412594SShengliang.Zhang@Sun.COM 	}
42512594SShengliang.Zhang@Sun.COM 
42612594SShengliang.Zhang@Sun.COM 	/*
4270Sstevel@tonic-gate 	 * Construct the uscsi format ioctl.  The form depends
4280Sstevel@tonic-gate 	 * upon the defect list the user extracted.  If s/he
4290Sstevel@tonic-gate 	 * extracted the "original" list, we format with only
4300Sstevel@tonic-gate 	 * the P (manufacturer's defect) list.  Otherwise, we
4310Sstevel@tonic-gate 	 * format with both the P and the G (grown) list.
4320Sstevel@tonic-gate 	 * To format with the P and G list, we set the fmtData
4330Sstevel@tonic-gate 	 * bit, and send an empty list.  To format with the
4340Sstevel@tonic-gate 	 * P list only, we also set the cmpLst bit, meaning
4350Sstevel@tonic-gate 	 * that the (empty) list we send down is the complete
4360Sstevel@tonic-gate 	 * G list, thereby discarding the old G list..
4370Sstevel@tonic-gate 	 */
4380Sstevel@tonic-gate 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
4390Sstevel@tonic-gate 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
4400Sstevel@tonic-gate 
4410Sstevel@tonic-gate 	cdb.scc_cmd		= SCMD_FORMAT;
4420Sstevel@tonic-gate 	ucmd.uscsi_cdb		= (caddr_t)&cdb;
4430Sstevel@tonic-gate 	ucmd.uscsi_cdblen	= CDB_GROUP0;
4440Sstevel@tonic-gate 	cdb.cdb_opaque[1]	= FPB_DATA;
44512594SShengliang.Zhang@Sun.COM 
44612594SShengliang.Zhang@Sun.COM 	/*
44712594SShengliang.Zhang@Sun.COM 	 * Use the long parameter header in format command,
44812594SShengliang.Zhang@Sun.COM 	 * and set the FMTPINFO field., when type 1, 2, 3.
44912594SShengliang.Zhang@Sun.COM 	 */
45012594SShengliang.Zhang@Sun.COM 	cdb.cdb_opaque[1] |= (param_long_list << 5) | (fmt_prot_info << 6);
45112594SShengliang.Zhang@Sun.COM 	(void) memset((char *)fmt_long_param_header, 0,
45212594SShengliang.Zhang@Sun.COM 	    sizeof (fmt_long_param_header));
45312594SShengliang.Zhang@Sun.COM 
45412594SShengliang.Zhang@Sun.COM 	/*
45512594SShengliang.Zhang@Sun.COM 	 * Set the PROTECTION FIELD USAGE field in the long
45612594SShengliang.Zhang@Sun.COM 	 * parameter list header, which combines with FMTINFO to
45712594SShengliang.Zhang@Sun.COM 	 * determine the protection type.
45812594SShengliang.Zhang@Sun.COM 	 * The PROTECTION INTERVAL EXPONET field is set default 0.
45912594SShengliang.Zhang@Sun.COM 	 * So only one protection information interval is used
46012594SShengliang.Zhang@Sun.COM 	 * in type 1, 2, 3.
46112594SShengliang.Zhang@Sun.COM 	 */
46212594SShengliang.Zhang@Sun.COM 	fmt_long_param_header[0] = prot_field_usage;
46312594SShengliang.Zhang@Sun.COM 	fmt_long_param_header[1] = FDH_FOV | FDH_IMMED;
46412594SShengliang.Zhang@Sun.COM 	ucmd.uscsi_bufaddr = (caddr_t)fmt_long_param_header;
46512594SShengliang.Zhang@Sun.COM 	ucmd.uscsi_buflen = sizeof (fmt_long_param_header);
4660Sstevel@tonic-gate 
4670Sstevel@tonic-gate 	if ((list->list != NULL) && ((list->flags & LIST_PGLIST) == 0)) {
4680Sstevel@tonic-gate 		/*
4690Sstevel@tonic-gate 		 * No G list.  The empty list we send down
4700Sstevel@tonic-gate 		 * is the complete list.
4710Sstevel@tonic-gate 		 */
4720Sstevel@tonic-gate 		cdb.cdb_opaque[1] |= FPB_CMPLT;
4730Sstevel@tonic-gate 	}
4740Sstevel@tonic-gate 
4750Sstevel@tonic-gate 	/*
4760Sstevel@tonic-gate 	 * Issue the format ioctl
4770Sstevel@tonic-gate 	 */
4780Sstevel@tonic-gate 	fmt_print("Formatting...\n");
4790Sstevel@tonic-gate 	(void) fflush(stdout);
4800Sstevel@tonic-gate 	status = uscsi_cmd(cur_file, &ucmd,
4810Sstevel@tonic-gate 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate 	/* check if format with immed was successfully accepted */
4840Sstevel@tonic-gate 	if (status == 0) {
485*12930SRalph.Turner@Sun.COM 		/* immed accepted poll to completion */
4860Sstevel@tonic-gate 		status = test_until_ready(cur_file);
4870Sstevel@tonic-gate 	} else {
488*12930SRalph.Turner@Sun.COM 		/* clear FOV and try again */
48912594SShengliang.Zhang@Sun.COM 		(void) memset((char *)fmt_long_param_header, 0,
49012594SShengliang.Zhang@Sun.COM 		    sizeof (fmt_long_param_header));
49112594SShengliang.Zhang@Sun.COM 		fmt_long_param_header[0] = prot_field_usage;
492*12930SRalph.Turner@Sun.COM 		fmt_long_param_header[1] = FDH_IMMED;
4930Sstevel@tonic-gate 		status = uscsi_cmd(cur_file, &ucmd,
494*12930SRalph.Turner@Sun.COM 		    (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
495*12930SRalph.Turner@Sun.COM 		if (status == 0) {
496*12930SRalph.Turner@Sun.COM 			/* immed accepted, poll for progress */
497*12930SRalph.Turner@Sun.COM 			status = test_until_ready(cur_file);
498*12930SRalph.Turner@Sun.COM 		} else {
499*12930SRalph.Turner@Sun.COM 			/*
500*12930SRalph.Turner@Sun.COM 			 * clear defect header and try basecase format
501*12930SRalph.Turner@Sun.COM 			 * command will hang until format complete
502*12930SRalph.Turner@Sun.COM 			 */
503*12930SRalph.Turner@Sun.COM 			(void) memset((char *)fmt_long_param_header, 0,
504*12930SRalph.Turner@Sun.COM 			    sizeof (fmt_long_param_header));
505*12930SRalph.Turner@Sun.COM 			fmt_long_param_header[0] = prot_field_usage;
506*12930SRalph.Turner@Sun.COM 			status = uscsi_cmd(cur_file, &ucmd,
507*12930SRalph.Turner@Sun.COM 			    (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
508*12930SRalph.Turner@Sun.COM 		}
5090Sstevel@tonic-gate 	}
5100Sstevel@tonic-gate 
5110Sstevel@tonic-gate 	/* format failure check					*/
5120Sstevel@tonic-gate 	if (status != 0) {
5130Sstevel@tonic-gate 		/*
5140Sstevel@tonic-gate 		 * formatting failed with fmtdata = 1.
5150Sstevel@tonic-gate 		 * Check if defects list command is supported, if it
5160Sstevel@tonic-gate 		 * is not supported then use fmtdata = 0.
5170Sstevel@tonic-gate 		 * 	From SCSI Spec
5180Sstevel@tonic-gate 		 *	    A FmtData bit of zero indicates, the
5190Sstevel@tonic-gate 		 *	    source of defect information is not specified.
5200Sstevel@tonic-gate 		 * else
5210Sstevel@tonic-gate 		 *	proceed to format using with mode selects.
5220Sstevel@tonic-gate 		 */
5230Sstevel@tonic-gate 		if (!(check_support_for_defects())) {
5240Sstevel@tonic-gate 			status = scsi_format_without_defects();
5250Sstevel@tonic-gate 		}
5260Sstevel@tonic-gate 
5270Sstevel@tonic-gate 		if (status != 0) {
5280Sstevel@tonic-gate 			fmt_print("Format failed\n");
5290Sstevel@tonic-gate 			status = scsi_raw_format();
5300Sstevel@tonic-gate 		}
5310Sstevel@tonic-gate 	}
5320Sstevel@tonic-gate 	(void) uscsi_reserve_release(cur_file, SCMD_RELEASE);
5330Sstevel@tonic-gate 	return (status);
5340Sstevel@tonic-gate }
5350Sstevel@tonic-gate 
5360Sstevel@tonic-gate /*
5370Sstevel@tonic-gate  * Format without any of the standard mode selects ignoring Grown defects list.
5380Sstevel@tonic-gate  */
5390Sstevel@tonic-gate static int
scsi_raw_format(void)5400Sstevel@tonic-gate scsi_raw_format(void)
5410Sstevel@tonic-gate {
5420Sstevel@tonic-gate 	struct uscsi_cmd	ucmd;
5430Sstevel@tonic-gate 	union scsi_cdb		cdb;
5440Sstevel@tonic-gate 	struct scsi_defect_hdr	defect_hdr;
5450Sstevel@tonic-gate 	int			status;
5460Sstevel@tonic-gate 
5470Sstevel@tonic-gate 	fmt_print("\n"
5480Sstevel@tonic-gate 	    "Retry of formatting operation without any of the standard\n"
5490Sstevel@tonic-gate 	    "mode selects and ignoring disk's Grown Defects list.  The\n"
5500Sstevel@tonic-gate 	    "disk may be able to be reformatted this way if an earlier\n"
5510Sstevel@tonic-gate 	    "formatting operation was interrupted by a power failure or\n"
5520Sstevel@tonic-gate 	    "SCSI bus reset.  The Grown Defects list will be recreated\n"
5530Sstevel@tonic-gate 	    "by format verification and surface analysis.\n\n");
5540Sstevel@tonic-gate 
5550Sstevel@tonic-gate 	if (check("Retry format without mode selects and Grown Defects list")
5569889SLarry.Liu@Sun.COM 	    != 0) {
5570Sstevel@tonic-gate 		return (-1);
5580Sstevel@tonic-gate 	}
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 	/*
5610Sstevel@tonic-gate 	 * Construct the uscsi format ioctl.
5620Sstevel@tonic-gate 	 * To format with the P and G list, we set the fmtData
5630Sstevel@tonic-gate 	 * and cmpLst bits to zero.  To format with just the
5640Sstevel@tonic-gate 	 * P list, we set the fmtData bit (meaning that we will
5650Sstevel@tonic-gate 	 * send down a defect list in the data phase) and the
5660Sstevel@tonic-gate 	 * cmpLst bit (meaning that the list we send is the
5670Sstevel@tonic-gate 	 * complete G list), and a defect list header with
5680Sstevel@tonic-gate 	 * a defect list length of zero.
5690Sstevel@tonic-gate 	 */
5700Sstevel@tonic-gate 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
5710Sstevel@tonic-gate 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
5720Sstevel@tonic-gate 	(void) memset((char *)&defect_hdr, 0, sizeof (defect_hdr));
5730Sstevel@tonic-gate 
5740Sstevel@tonic-gate 	cdb.scc_cmd		= SCMD_FORMAT;
5750Sstevel@tonic-gate 	ucmd.uscsi_cdb		= (caddr_t)&cdb;
5760Sstevel@tonic-gate 	ucmd.uscsi_cdblen	= CDB_GROUP0;
5770Sstevel@tonic-gate 	/* No G list.   Send empty defect list to replace it */
5780Sstevel@tonic-gate 	cdb.cdb_opaque[1]	= FPB_DATA | FPB_CMPLT | FPB_BFI;
5790Sstevel@tonic-gate 	ucmd.uscsi_bufaddr	= (caddr_t)&defect_hdr;
5800Sstevel@tonic-gate 	ucmd.uscsi_buflen	= sizeof (defect_hdr);
5810Sstevel@tonic-gate 	defect_hdr.descriptor	= FDH_FOV | FDH_IMMED;
5820Sstevel@tonic-gate 
5830Sstevel@tonic-gate 	/*
5840Sstevel@tonic-gate 	 * Issue the format ioctl
5850Sstevel@tonic-gate 	 */
5860Sstevel@tonic-gate 	fmt_print("Formatting...\n");
5870Sstevel@tonic-gate 	(void) fflush(stdout);
5880Sstevel@tonic-gate 	status = uscsi_cmd(cur_file, &ucmd, F_NORMAL);
5890Sstevel@tonic-gate 
5900Sstevel@tonic-gate 	/* check if format with immed was successfully accepted */
5910Sstevel@tonic-gate 	if (status == 0) {
5920Sstevel@tonic-gate 		/* immed accepted pool to completion */
5930Sstevel@tonic-gate 		status = test_until_ready(cur_file);
5940Sstevel@tonic-gate 	} else {
5950Sstevel@tonic-gate 		/* clear defect header and try basecase format */
5960Sstevel@tonic-gate 		(void) memset((char *)&defect_hdr, 0, sizeof (defect_hdr));
5970Sstevel@tonic-gate 		status = uscsi_cmd(cur_file, &ucmd, F_NORMAL);
5980Sstevel@tonic-gate 	}
5990Sstevel@tonic-gate 
6000Sstevel@tonic-gate 	/* fmt_print(status ? "Format failed\n\n" : "Format ok\n\n"); */
6010Sstevel@tonic-gate 	return (status);
6020Sstevel@tonic-gate }
6030Sstevel@tonic-gate 
6040Sstevel@tonic-gate /*
6050Sstevel@tonic-gate  * Estimate the time required for format operation (See 1163770).
6060Sstevel@tonic-gate  * format time = (5_revs * p4_heads * p4_cylinders) / p4_rpm
6070Sstevel@tonic-gate  * 5 revolutions (correspond to format_time keyword in format.dat file) are:
6080Sstevel@tonic-gate  *	1 rev.  for positioning
6090Sstevel@tonic-gate  *	2 rev.  for writing the track
6100Sstevel@tonic-gate  *	1 rev.  for positioning
6110Sstevel@tonic-gate  *	1 rev.  for cerifying the data integrity of the track
6120Sstevel@tonic-gate  * The return value is a good estimate on the formatting time in minutes.
6130Sstevel@tonic-gate  * Caller should add 50% margin to cover defect management overhead.
6140Sstevel@tonic-gate  */
6150Sstevel@tonic-gate int
scsi_format_time()6160Sstevel@tonic-gate scsi_format_time()
6170Sstevel@tonic-gate {
6180Sstevel@tonic-gate 	struct mode_geometry		*page4;
6190Sstevel@tonic-gate 	struct scsi_ms_header		header;
6200Sstevel@tonic-gate 	int				status;
6210Sstevel@tonic-gate 	int				p4_cylinders, p4_heads, p4_rpm;
6220Sstevel@tonic-gate 	int				length;
6230Sstevel@tonic-gate 	int				format_time;
6240Sstevel@tonic-gate 	union {
6250Sstevel@tonic-gate 		struct mode_geometry	page4;
6260Sstevel@tonic-gate 		char			rawbuf[MAX_MODE_SENSE_SIZE];
6270Sstevel@tonic-gate 	} u_page4;
6280Sstevel@tonic-gate 
6290Sstevel@tonic-gate 
6300Sstevel@tonic-gate 	page4 = &u_page4.page4;
6310Sstevel@tonic-gate 	(void) memset(&u_page4, 0, sizeof (u_page4));
6320Sstevel@tonic-gate 
6330Sstevel@tonic-gate 	/*
6340Sstevel@tonic-gate 	 * Issue a mode sense to determine the default parameters
6350Sstevel@tonic-gate 	 * If it fail, try to use the saved or current instead.
6360Sstevel@tonic-gate 	 */
6370Sstevel@tonic-gate 	status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
6389889SLarry.Liu@Sun.COM 	    MODE_SENSE_PC_DEFAULT, (caddr_t)page4,
6399889SLarry.Liu@Sun.COM 	    MAX_MODE_SENSE_SIZE, &header);
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate 	if (status) {
6420Sstevel@tonic-gate 		status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
6439889SLarry.Liu@Sun.COM 		    MODE_SENSE_PC_SAVED, (caddr_t)page4,
6449889SLarry.Liu@Sun.COM 		    MAX_MODE_SENSE_SIZE, &header);
6450Sstevel@tonic-gate 	}
6460Sstevel@tonic-gate 	if (status) {
6470Sstevel@tonic-gate 		status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
6489889SLarry.Liu@Sun.COM 		    MODE_SENSE_PC_CURRENT, (caddr_t)page4,
6499889SLarry.Liu@Sun.COM 		    MAX_MODE_SENSE_SIZE, &header);
6500Sstevel@tonic-gate 	}
6510Sstevel@tonic-gate 	if (status) {
6520Sstevel@tonic-gate 		return (0);
6530Sstevel@tonic-gate 	}
6540Sstevel@tonic-gate 
6550Sstevel@tonic-gate 	/*
6560Sstevel@tonic-gate 	 * We only need the common subset between the CCS
6570Sstevel@tonic-gate 	 * and SCSI-2 structures, so we can treat both
6580Sstevel@tonic-gate 	 * cases identically.
6590Sstevel@tonic-gate 	 */
6600Sstevel@tonic-gate 	length = MODESENSE_PAGE_LEN(page4);
6610Sstevel@tonic-gate 	if (length < MIN_PAGE4_LEN) {
6620Sstevel@tonic-gate 		return (0);
6630Sstevel@tonic-gate 	}
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate 	page4->rpm = BE_16(page4->rpm);
6660Sstevel@tonic-gate 	p4_cylinders = (page4->cyl_ub << 16) + (page4->cyl_mb << 8) +
6679889SLarry.Liu@Sun.COM 	    page4->cyl_lb;
6680Sstevel@tonic-gate 	p4_heads = page4->heads;
6690Sstevel@tonic-gate 	p4_rpm = page4->rpm;
6700Sstevel@tonic-gate 
6710Sstevel@tonic-gate 	/*
6720Sstevel@tonic-gate 	 * Some drives report 0 for page4->rpm, adjust it to AVG_RPM, 3600.
6730Sstevel@tonic-gate 	 */
6740Sstevel@tonic-gate 	if (p4_rpm < MIN_RPM || p4_rpm > MAX_RPM) {
6750Sstevel@tonic-gate 		err_print("Mode sense page(4) reports rpm value as %d,"
6769889SLarry.Liu@Sun.COM 		    " adjusting it to %d\n", p4_rpm, AVG_RPM);
6770Sstevel@tonic-gate 		p4_rpm = AVG_RPM;
6780Sstevel@tonic-gate 	}
6790Sstevel@tonic-gate 
6800Sstevel@tonic-gate 	if (p4_cylinders <= 0 || p4_heads <= 0)
6810Sstevel@tonic-gate 		return (0);
6820Sstevel@tonic-gate 
6830Sstevel@tonic-gate 	format_time = ((scsi_format_revolutions * p4_heads *
6849889SLarry.Liu@Sun.COM 	    p4_cylinders) + p4_rpm) / p4_rpm;
6850Sstevel@tonic-gate 
6860Sstevel@tonic-gate 	if (option_msg && diag_msg) {
6870Sstevel@tonic-gate 		err_print("       pcyl:    %d\n", p4_cylinders);
6880Sstevel@tonic-gate 		err_print("      heads:    %d\n", p4_heads);
6890Sstevel@tonic-gate 		err_print("        rpm:    %d\n", p4_rpm);
6900Sstevel@tonic-gate 		err_print("format_time:    %d minutes\n", format_time);
6910Sstevel@tonic-gate 	}
6920Sstevel@tonic-gate 	return (format_time);
6930Sstevel@tonic-gate }
6940Sstevel@tonic-gate 
6950Sstevel@tonic-gate /*
6960Sstevel@tonic-gate  * Check disk error recovery parameters via mode sense.
6970Sstevel@tonic-gate  * Issue a mode select if we need to change something.
6980Sstevel@tonic-gate  */
6990Sstevel@tonic-gate /*ARGSUSED*/
7000Sstevel@tonic-gate static int
scsi_ms_page1(scsi2_flag)7010Sstevel@tonic-gate scsi_ms_page1(scsi2_flag)
7020Sstevel@tonic-gate 	int	scsi2_flag;
7030Sstevel@tonic-gate {
7040Sstevel@tonic-gate 	struct mode_err_recov		*page1;
7050Sstevel@tonic-gate 	struct mode_err_recov		*fixed;
7060Sstevel@tonic-gate 	struct scsi_ms_header		header;
7070Sstevel@tonic-gate 	struct scsi_ms_header		fixed_hdr;
7080Sstevel@tonic-gate 	int				status;
7090Sstevel@tonic-gate 	int				tmp1, tmp2;
7100Sstevel@tonic-gate 	int				flag;
7110Sstevel@tonic-gate 	int				length;
7120Sstevel@tonic-gate 	int				sp_flags;
7130Sstevel@tonic-gate 	union {
7140Sstevel@tonic-gate 		struct mode_err_recov	page1;
7150Sstevel@tonic-gate 		char			rawbuf[MAX_MODE_SENSE_SIZE];
7160Sstevel@tonic-gate 	} u_page1, u_fixed;
7170Sstevel@tonic-gate 
7180Sstevel@tonic-gate 
7190Sstevel@tonic-gate 	page1 = &u_page1.page1;
7200Sstevel@tonic-gate 	fixed = &u_fixed.page1;
7210Sstevel@tonic-gate 
7220Sstevel@tonic-gate 	/*
7230Sstevel@tonic-gate 	 * If debugging, issue mode senses on the default and
7240Sstevel@tonic-gate 	 * current values.
7250Sstevel@tonic-gate 	 */
7260Sstevel@tonic-gate 	if (option_msg && diag_msg) {
7270Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
7280Sstevel@tonic-gate 			MODE_SENSE_PC_DEFAULT, (caddr_t)page1,
7290Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
7300Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
7310Sstevel@tonic-gate 			MODE_SENSE_PC_CURRENT, (caddr_t)page1,
7320Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
7330Sstevel@tonic-gate 	}
7340Sstevel@tonic-gate 
7350Sstevel@tonic-gate 	/*
7360Sstevel@tonic-gate 	 * Issue a mode sense to determine the saved parameters
7370Sstevel@tonic-gate 	 * If the saved values fail, use the current instead.
7380Sstevel@tonic-gate 	 */
7390Sstevel@tonic-gate 	status = uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
7400Sstevel@tonic-gate 			MODE_SENSE_PC_SAVED, (caddr_t)page1,
7410Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
7420Sstevel@tonic-gate 	if (status) {
7430Sstevel@tonic-gate 		status = uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
7440Sstevel@tonic-gate 			MODE_SENSE_PC_CURRENT, (caddr_t)page1,
7450Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
7460Sstevel@tonic-gate 		if (status) {
7470Sstevel@tonic-gate 			return (0);
7480Sstevel@tonic-gate 		}
7490Sstevel@tonic-gate 	}
7500Sstevel@tonic-gate 
7510Sstevel@tonic-gate 	/*
7520Sstevel@tonic-gate 	 * We only need the common subset between the CCS
7530Sstevel@tonic-gate 	 * and SCSI-2 structures, so we can treat both
7540Sstevel@tonic-gate 	 * cases identically.  Whatever the drive gives
7550Sstevel@tonic-gate 	 * us, we return to the drive in the mode select,
7560Sstevel@tonic-gate 	 * delta'ed by whatever we want to change.
7570Sstevel@tonic-gate 	 */
7580Sstevel@tonic-gate 	length = MODESENSE_PAGE_LEN(page1);
7590Sstevel@tonic-gate 	if (length < MIN_PAGE1_LEN) {
7600Sstevel@tonic-gate 		return (0);
7610Sstevel@tonic-gate 	}
7620Sstevel@tonic-gate 
7630Sstevel@tonic-gate 	/*
7640Sstevel@tonic-gate 	 * Ask for changeable parameters.
7650Sstevel@tonic-gate 	 */
7660Sstevel@tonic-gate 	status = uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
7670Sstevel@tonic-gate 		MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
7680Sstevel@tonic-gate 		MAX_MODE_SENSE_SIZE, &fixed_hdr);
7690Sstevel@tonic-gate 	if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE1_LEN) {
7700Sstevel@tonic-gate 		return (0);
7710Sstevel@tonic-gate 	}
7720Sstevel@tonic-gate 
7730Sstevel@tonic-gate 	/*
7740Sstevel@tonic-gate 	 * We need to issue a mode select only if one or more
7750Sstevel@tonic-gate 	 * parameters need to be changed, and those parameters
7760Sstevel@tonic-gate 	 * are flagged by the drive as changeable.
7770Sstevel@tonic-gate 	 */
7780Sstevel@tonic-gate 	flag = 0;
7790Sstevel@tonic-gate 	tmp1 = page1->read_retry_count;
7800Sstevel@tonic-gate 	tmp2 = page1->write_retry_count;
7810Sstevel@tonic-gate 	if (cur_dtype->dtype_options & SUP_READ_RETRIES &&
7820Sstevel@tonic-gate 			fixed->read_retry_count != 0) {
7830Sstevel@tonic-gate 		flag |= (page1->read_retry_count !=
7840Sstevel@tonic-gate 				cur_dtype->dtype_read_retries);
7850Sstevel@tonic-gate 		page1->read_retry_count = cur_dtype->dtype_read_retries;
7860Sstevel@tonic-gate 	}
7870Sstevel@tonic-gate 	if (length > 8) {
7880Sstevel@tonic-gate 		if (cur_dtype->dtype_options & SUP_WRITE_RETRIES &&
7890Sstevel@tonic-gate 				fixed->write_retry_count != 0) {
7900Sstevel@tonic-gate 			flag |= (page1->write_retry_count !=
7910Sstevel@tonic-gate 					cur_dtype->dtype_write_retries);
7920Sstevel@tonic-gate 			page1->write_retry_count =
7930Sstevel@tonic-gate 					cur_dtype->dtype_write_retries;
7940Sstevel@tonic-gate 		}
7950Sstevel@tonic-gate 	}
7960Sstevel@tonic-gate 	/*
7970Sstevel@tonic-gate 	 * Report any changes so far...
7980Sstevel@tonic-gate 	 */
7990Sstevel@tonic-gate 	if (flag && option_msg) {
8000Sstevel@tonic-gate 		fmt_print(
8010Sstevel@tonic-gate "PAGE 1: read retries= %d (%d)  write retries= %d (%d)\n",
8020Sstevel@tonic-gate 			page1->read_retry_count, tmp1,
8030Sstevel@tonic-gate 			page1->write_retry_count, tmp2);
8040Sstevel@tonic-gate 	}
8050Sstevel@tonic-gate 	/*
8060Sstevel@tonic-gate 	 * Apply any changes requested via the change list method
8070Sstevel@tonic-gate 	 */
8080Sstevel@tonic-gate 	flag |= apply_chg_list(DAD_MODE_ERR_RECOV, length,
8090Sstevel@tonic-gate 		(uchar_t *)page1, (uchar_t *)fixed,
8100Sstevel@tonic-gate 			cur_dtype->dtype_chglist);
8110Sstevel@tonic-gate 	/*
8120Sstevel@tonic-gate 	 * If no changes required, do not issue a mode select
8130Sstevel@tonic-gate 	 */
8140Sstevel@tonic-gate 	if (flag == 0) {
8150Sstevel@tonic-gate 		return (0);
8160Sstevel@tonic-gate 	}
8170Sstevel@tonic-gate 	/*
8180Sstevel@tonic-gate 	 * We always want to set the Page Format bit for mode
8190Sstevel@tonic-gate 	 * selects.  Set the Save Page bit if the drive indicates
8200Sstevel@tonic-gate 	 * that it can save this page via the mode sense.
8210Sstevel@tonic-gate 	 */
8220Sstevel@tonic-gate 	sp_flags = MODE_SELECT_PF;
8230Sstevel@tonic-gate 	if (page1->mode_page.ps) {
8240Sstevel@tonic-gate 		sp_flags |= MODE_SELECT_SP;
8250Sstevel@tonic-gate 	}
8260Sstevel@tonic-gate 	page1->mode_page.ps = 0;
8270Sstevel@tonic-gate 	header.mode_header.length = 0;
8280Sstevel@tonic-gate 	header.mode_header.device_specific = 0;
8290Sstevel@tonic-gate 	status = uscsi_mode_select(cur_file, DAD_MODE_ERR_RECOV,
8300Sstevel@tonic-gate 		sp_flags, (caddr_t)page1, length, &header);
8310Sstevel@tonic-gate 	if (status && (sp_flags & MODE_SELECT_SP)) {
8320Sstevel@tonic-gate 		/* If failed, try not saving mode select params. */
8330Sstevel@tonic-gate 		sp_flags &= ~MODE_SELECT_SP;
8340Sstevel@tonic-gate 		status = uscsi_mode_select(cur_file, DAD_MODE_ERR_RECOV,
8350Sstevel@tonic-gate 			sp_flags, (caddr_t)page1, length, &header);
8360Sstevel@tonic-gate 		}
8370Sstevel@tonic-gate 	if (status && option_msg) {
8380Sstevel@tonic-gate 		err_print("\
8390Sstevel@tonic-gate Warning: Using default error recovery parameters.\n\n");
8400Sstevel@tonic-gate 	}
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate 	/*
8430Sstevel@tonic-gate 	 * If debugging, issue mode senses on the current and
8440Sstevel@tonic-gate 	 * saved values, so we can see the result of the mode
8450Sstevel@tonic-gate 	 * selects.
8460Sstevel@tonic-gate 	 */
8470Sstevel@tonic-gate 	if (option_msg && diag_msg) {
8480Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
8490Sstevel@tonic-gate 			MODE_SENSE_PC_CURRENT, (caddr_t)page1,
8500Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
8510Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
8520Sstevel@tonic-gate 			MODE_SENSE_PC_SAVED, (caddr_t)page1,
8530Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
8540Sstevel@tonic-gate 	}
8550Sstevel@tonic-gate 
8560Sstevel@tonic-gate 	return (0);
8570Sstevel@tonic-gate }
8580Sstevel@tonic-gate 
8590Sstevel@tonic-gate /*
8600Sstevel@tonic-gate  * Check disk disconnect/reconnect parameters via mode sense.
8610Sstevel@tonic-gate  * Issue a mode select if we need to change something.
8620Sstevel@tonic-gate  */
8630Sstevel@tonic-gate /*ARGSUSED*/
8640Sstevel@tonic-gate static int
scsi_ms_page2(scsi2_flag)8650Sstevel@tonic-gate scsi_ms_page2(scsi2_flag)
8660Sstevel@tonic-gate 	int	scsi2_flag;
8670Sstevel@tonic-gate {
8680Sstevel@tonic-gate 	struct mode_disco_reco		*page2;
8690Sstevel@tonic-gate 	struct mode_disco_reco		*fixed;
8700Sstevel@tonic-gate 	struct scsi_ms_header		header;
8710Sstevel@tonic-gate 	struct scsi_ms_header		fixed_hdr;
8720Sstevel@tonic-gate 	int				status;
8730Sstevel@tonic-gate 	int				flag;
8740Sstevel@tonic-gate 	int				length;
8750Sstevel@tonic-gate 	int				sp_flags;
8760Sstevel@tonic-gate 	union {
8770Sstevel@tonic-gate 		struct mode_disco_reco	page2;
8780Sstevel@tonic-gate 		char			rawbuf[MAX_MODE_SENSE_SIZE];
8790Sstevel@tonic-gate 	} u_page2, u_fixed;
8800Sstevel@tonic-gate 
8810Sstevel@tonic-gate 	page2 = &u_page2.page2;
8820Sstevel@tonic-gate 	fixed = &u_fixed.page2;
8830Sstevel@tonic-gate 
8840Sstevel@tonic-gate 	/*
8850Sstevel@tonic-gate 	 * If debugging, issue mode senses on the default and
8860Sstevel@tonic-gate 	 * current values.
8870Sstevel@tonic-gate 	 */
8880Sstevel@tonic-gate 	if (option_msg && diag_msg) {
8890Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
8900Sstevel@tonic-gate 			MODE_SENSE_PC_DEFAULT, (caddr_t)page2,
8910Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
8920Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
8930Sstevel@tonic-gate 			MODE_SENSE_PC_CURRENT, (caddr_t)page2,
8940Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
8950Sstevel@tonic-gate 	}
8960Sstevel@tonic-gate 
8970Sstevel@tonic-gate 	/*
8980Sstevel@tonic-gate 	 * Issue a mode sense to determine the saved parameters
8990Sstevel@tonic-gate 	 * If the saved values fail, use the current instead.
9000Sstevel@tonic-gate 	 */
9010Sstevel@tonic-gate 	status = uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
9020Sstevel@tonic-gate 			MODE_SENSE_PC_SAVED, (caddr_t)page2,
9030Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
9040Sstevel@tonic-gate 	if (status) {
9050Sstevel@tonic-gate 		status = uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
9060Sstevel@tonic-gate 			MODE_SENSE_PC_CURRENT, (caddr_t)page2,
9070Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
9080Sstevel@tonic-gate 		if (status) {
9090Sstevel@tonic-gate 			return (0);
9100Sstevel@tonic-gate 		}
9110Sstevel@tonic-gate 	}
9120Sstevel@tonic-gate 
9130Sstevel@tonic-gate 	/*
9140Sstevel@tonic-gate 	 * We only need the common subset between the CCS
9150Sstevel@tonic-gate 	 * and SCSI-2 structures, so we can treat both
9160Sstevel@tonic-gate 	 * cases identically.  Whatever the drive gives
9170Sstevel@tonic-gate 	 * us, we return to the drive in the mode select,
9180Sstevel@tonic-gate 	 * delta'ed by whatever we want to change.
9190Sstevel@tonic-gate 	 */
9200Sstevel@tonic-gate 	length = MODESENSE_PAGE_LEN(page2);
9210Sstevel@tonic-gate 	if (length < MIN_PAGE2_LEN) {
9220Sstevel@tonic-gate 		return (0);
9230Sstevel@tonic-gate 	}
9240Sstevel@tonic-gate 
9250Sstevel@tonic-gate 	/*
9260Sstevel@tonic-gate 	 * Ask for changeable parameters.
9270Sstevel@tonic-gate 	 */
9280Sstevel@tonic-gate 	status = uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
9290Sstevel@tonic-gate 		MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
9300Sstevel@tonic-gate 		MAX_MODE_SENSE_SIZE, &fixed_hdr);
9310Sstevel@tonic-gate 	if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE2_LEN) {
9320Sstevel@tonic-gate 		return (0);
9330Sstevel@tonic-gate 	}
9340Sstevel@tonic-gate 
9350Sstevel@tonic-gate 	/*
9360Sstevel@tonic-gate 	 * We need to issue a mode select only if one or more
9370Sstevel@tonic-gate 	 * parameters need to be changed, and those parameters
9380Sstevel@tonic-gate 	 * are flagged by the drive as changeable.
9390Sstevel@tonic-gate 	 */
9400Sstevel@tonic-gate 	flag = 0;
9410Sstevel@tonic-gate 	/*
9420Sstevel@tonic-gate 	 * Apply any changes requested via the change list method
9430Sstevel@tonic-gate 	 */
9440Sstevel@tonic-gate 	flag |= apply_chg_list(MODEPAGE_DISCO_RECO, length,
9450Sstevel@tonic-gate 		(uchar_t *)page2, (uchar_t *)fixed,
9460Sstevel@tonic-gate 			cur_dtype->dtype_chglist);
9470Sstevel@tonic-gate 	/*
9480Sstevel@tonic-gate 	 * If no changes required, do not issue a mode select
9490Sstevel@tonic-gate 	 */
9500Sstevel@tonic-gate 	if (flag == 0) {
9510Sstevel@tonic-gate 		return (0);
9520Sstevel@tonic-gate 	}
9530Sstevel@tonic-gate 	/*
9540Sstevel@tonic-gate 	 * We always want to set the Page Format bit for mode
9550Sstevel@tonic-gate 	 * selects.  Set the Save Page bit if the drive indicates
9560Sstevel@tonic-gate 	 * that it can save this page via the mode sense.
9570Sstevel@tonic-gate 	 */
9580Sstevel@tonic-gate 	sp_flags = MODE_SELECT_PF;
9590Sstevel@tonic-gate 	if (page2->mode_page.ps) {
9600Sstevel@tonic-gate 		sp_flags |= MODE_SELECT_SP;
9610Sstevel@tonic-gate 	}
9620Sstevel@tonic-gate 	page2->mode_page.ps = 0;
9630Sstevel@tonic-gate 	header.mode_header.length = 0;
9640Sstevel@tonic-gate 	header.mode_header.device_specific = 0;
9650Sstevel@tonic-gate 	status = uscsi_mode_select(cur_file, MODEPAGE_DISCO_RECO,
9660Sstevel@tonic-gate 		MODE_SELECT_SP, (caddr_t)page2, length, &header);
9670Sstevel@tonic-gate 	if (status && (sp_flags & MODE_SELECT_SP)) {
9680Sstevel@tonic-gate 		/* If failed, try not saving mode select params. */
9690Sstevel@tonic-gate 		sp_flags &= ~MODE_SELECT_SP;
9700Sstevel@tonic-gate 		status = uscsi_mode_select(cur_file, MODEPAGE_DISCO_RECO,
9710Sstevel@tonic-gate 			sp_flags, (caddr_t)page2, length, &header);
9720Sstevel@tonic-gate 		}
9730Sstevel@tonic-gate 	if (status && option_msg) {
9740Sstevel@tonic-gate 		err_print("Warning: Using default .\n\n");
9750Sstevel@tonic-gate 	}
9760Sstevel@tonic-gate 
9770Sstevel@tonic-gate 	/*
9780Sstevel@tonic-gate 	 * If debugging, issue mode senses on the current and
9790Sstevel@tonic-gate 	 * saved values, so we can see the result of the mode
9800Sstevel@tonic-gate 	 * selects.
9810Sstevel@tonic-gate 	 */
9820Sstevel@tonic-gate 	if (option_msg && diag_msg) {
9830Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
9840Sstevel@tonic-gate 			MODE_SENSE_PC_CURRENT, (caddr_t)page2,
9850Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
9860Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
9870Sstevel@tonic-gate 			MODE_SENSE_PC_SAVED, (caddr_t)page2,
9880Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
9890Sstevel@tonic-gate 	}
9900Sstevel@tonic-gate 
9910Sstevel@tonic-gate 	return (0);
9920Sstevel@tonic-gate }
9930Sstevel@tonic-gate 
9940Sstevel@tonic-gate /*
9950Sstevel@tonic-gate  * Check disk format parameters via mode sense.
9960Sstevel@tonic-gate  * Issue a mode select if we need to change something.
9970Sstevel@tonic-gate  */
9980Sstevel@tonic-gate /*ARGSUSED*/
9990Sstevel@tonic-gate static int
scsi_ms_page3(scsi2_flag)10000Sstevel@tonic-gate scsi_ms_page3(scsi2_flag)
10010Sstevel@tonic-gate 	int	scsi2_flag;
10020Sstevel@tonic-gate {
10030Sstevel@tonic-gate 	struct mode_format		*page3;
10040Sstevel@tonic-gate 	struct mode_format		*fixed;
10050Sstevel@tonic-gate 	struct scsi_ms_header		header;
10060Sstevel@tonic-gate 	struct scsi_ms_header		fixed_hdr;
10070Sstevel@tonic-gate 	int				status;
10080Sstevel@tonic-gate 	int				tmp1, tmp2, tmp3;
10090Sstevel@tonic-gate 	int				tmp4, tmp5, tmp6;
10100Sstevel@tonic-gate 	int				flag;
10110Sstevel@tonic-gate 	int				length;
10120Sstevel@tonic-gate 	int				sp_flags;
10130Sstevel@tonic-gate 	union {
10140Sstevel@tonic-gate 		struct mode_format	page3;
10150Sstevel@tonic-gate 		char			rawbuf[MAX_MODE_SENSE_SIZE];
10160Sstevel@tonic-gate 	} u_page3, u_fixed;
10170Sstevel@tonic-gate 
10180Sstevel@tonic-gate 
10190Sstevel@tonic-gate 	page3 = &u_page3.page3;
10200Sstevel@tonic-gate 	fixed = &u_fixed.page3;
10210Sstevel@tonic-gate 
10220Sstevel@tonic-gate 	/*
10230Sstevel@tonic-gate 	 * If debugging, issue mode senses on the default and
10240Sstevel@tonic-gate 	 * current values.
10250Sstevel@tonic-gate 	 */
10260Sstevel@tonic-gate 	if (option_msg && diag_msg) {
10270Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
10280Sstevel@tonic-gate 			MODE_SENSE_PC_DEFAULT, (caddr_t)page3,
10290Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
10300Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
10310Sstevel@tonic-gate 			MODE_SENSE_PC_CURRENT, (caddr_t)page3,
10320Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
10330Sstevel@tonic-gate 	}
10340Sstevel@tonic-gate 
10350Sstevel@tonic-gate 	/*
10360Sstevel@tonic-gate 	 * Issue a mode sense to determine the saved parameters
10370Sstevel@tonic-gate 	 * If the saved values fail, use the current instead.
10380Sstevel@tonic-gate 	 */
10390Sstevel@tonic-gate 	status = uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
10400Sstevel@tonic-gate 			MODE_SENSE_PC_SAVED, (caddr_t)page3,
10410Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
10420Sstevel@tonic-gate 	if (status) {
10430Sstevel@tonic-gate 		status = uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
10440Sstevel@tonic-gate 			MODE_SENSE_PC_CURRENT, (caddr_t)page3,
10450Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
10460Sstevel@tonic-gate 		if (status) {
10470Sstevel@tonic-gate 			return (0);
10480Sstevel@tonic-gate 		}
10490Sstevel@tonic-gate 	}
10500Sstevel@tonic-gate 
10510Sstevel@tonic-gate 	/*
10520Sstevel@tonic-gate 	 * We only need the common subset between the CCS
10530Sstevel@tonic-gate 	 * and SCSI-2 structures, so we can treat both
10540Sstevel@tonic-gate 	 * cases identically.  Whatever the drive gives
10550Sstevel@tonic-gate 	 * us, we return to the drive in the mode select,
10560Sstevel@tonic-gate 	 * delta'ed by whatever we want to change.
10570Sstevel@tonic-gate 	 */
10580Sstevel@tonic-gate 	length = MODESENSE_PAGE_LEN(page3);
10590Sstevel@tonic-gate 	if (length < MIN_PAGE3_LEN) {
10600Sstevel@tonic-gate 		return (0);
10610Sstevel@tonic-gate 	}
10620Sstevel@tonic-gate 
10630Sstevel@tonic-gate 	/*
10640Sstevel@tonic-gate 	 * Ask for changeable parameters.
10650Sstevel@tonic-gate 	 */
10660Sstevel@tonic-gate 	status = uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
10670Sstevel@tonic-gate 		MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
10680Sstevel@tonic-gate 		MAX_MODE_SENSE_SIZE, &fixed_hdr);
10690Sstevel@tonic-gate 	if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE3_LEN) {
10700Sstevel@tonic-gate 		return (0);
10710Sstevel@tonic-gate 	}
10720Sstevel@tonic-gate 
10730Sstevel@tonic-gate 	/*
10740Sstevel@tonic-gate 	 * We need to issue a mode select only if one or more
10750Sstevel@tonic-gate 	 * parameters need to be changed, and those parameters
10760Sstevel@tonic-gate 	 * are flagged by the drive as changeable.
10770Sstevel@tonic-gate 	 */
10780Sstevel@tonic-gate 	tmp1 = page3->track_skew;
10790Sstevel@tonic-gate 	tmp2 = page3->cylinder_skew;
10800Sstevel@tonic-gate 	tmp3 = page3->sect_track;
10810Sstevel@tonic-gate 	tmp4 = page3->tracks_per_zone;
10820Sstevel@tonic-gate 	tmp5 = page3->alt_tracks_vol;
10830Sstevel@tonic-gate 	tmp6 = page3->alt_sect_zone;
10840Sstevel@tonic-gate 
10859889SLarry.Liu@Sun.COM 	flag = (page3->data_bytes_sect != cur_blksz);
10869889SLarry.Liu@Sun.COM 	page3->data_bytes_sect = cur_blksz;
10870Sstevel@tonic-gate 
10880Sstevel@tonic-gate 	flag |= (page3->interleave != 1);
10890Sstevel@tonic-gate 	page3->interleave = 1;
10900Sstevel@tonic-gate 
10910Sstevel@tonic-gate 	if (cur_dtype->dtype_options & SUP_CYLSKEW &&
10920Sstevel@tonic-gate 					fixed->cylinder_skew != 0) {
10930Sstevel@tonic-gate 		flag |= (page3->cylinder_skew != cur_dtype->dtype_cyl_skew);
10940Sstevel@tonic-gate 		page3->cylinder_skew = cur_dtype->dtype_cyl_skew;
10950Sstevel@tonic-gate 	}
10960Sstevel@tonic-gate 	if (cur_dtype->dtype_options & SUP_TRKSKEW &&
10970Sstevel@tonic-gate 					fixed->track_skew != 0) {
10980Sstevel@tonic-gate 		flag |= (page3->track_skew != cur_dtype->dtype_trk_skew);
10990Sstevel@tonic-gate 		page3->track_skew = cur_dtype->dtype_trk_skew;
11000Sstevel@tonic-gate 	}
11010Sstevel@tonic-gate 	if (cur_dtype->dtype_options & SUP_PSECT &&
11020Sstevel@tonic-gate 					fixed->sect_track != 0) {
11030Sstevel@tonic-gate 		flag |= (page3->sect_track != psect);
11040Sstevel@tonic-gate 		page3->sect_track = (ushort_t)psect;
11050Sstevel@tonic-gate 	}
11060Sstevel@tonic-gate 	if (cur_dtype->dtype_options & SUP_TRKS_ZONE &&
11070Sstevel@tonic-gate 					fixed->tracks_per_zone != 0) {
11080Sstevel@tonic-gate 		flag |= (page3->tracks_per_zone != cur_dtype->dtype_trks_zone);
11090Sstevel@tonic-gate 		page3->tracks_per_zone = cur_dtype->dtype_trks_zone;
11100Sstevel@tonic-gate 	}
11110Sstevel@tonic-gate 	if (cur_dtype->dtype_options & SUP_ASECT &&
11120Sstevel@tonic-gate 					fixed->alt_sect_zone != 0) {
11130Sstevel@tonic-gate 		flag |= (page3->alt_sect_zone != cur_dtype->dtype_asect);
11140Sstevel@tonic-gate 		page3->alt_sect_zone = cur_dtype->dtype_asect;
11150Sstevel@tonic-gate 	}
11160Sstevel@tonic-gate 	if (cur_dtype->dtype_options & SUP_ATRKS &&
11170Sstevel@tonic-gate 					fixed->alt_tracks_vol != 0) {
11180Sstevel@tonic-gate 		flag |= (page3->alt_tracks_vol != cur_dtype->dtype_atrks);
11190Sstevel@tonic-gate 		page3->alt_tracks_vol = cur_dtype->dtype_atrks;
11200Sstevel@tonic-gate 	}
11210Sstevel@tonic-gate 	/*
11220Sstevel@tonic-gate 	 * Notify user of any changes so far
11230Sstevel@tonic-gate 	 */
11240Sstevel@tonic-gate 	if (flag && option_msg) {
11250Sstevel@tonic-gate 		fmt_print("PAGE 3: trk skew= %d (%d)   cyl skew= %d (%d)   ",
11260Sstevel@tonic-gate 			page3->track_skew, tmp1, page3->cylinder_skew, tmp2);
11270Sstevel@tonic-gate 		fmt_print("sects/trk= %d (%d)\n", page3->sect_track, tmp3);
11280Sstevel@tonic-gate 		fmt_print("        trks/zone= %d (%d)   alt trks= %d (%d)   ",
11290Sstevel@tonic-gate 			page3->tracks_per_zone, tmp4,
11300Sstevel@tonic-gate 			page3->alt_tracks_vol, tmp5);
11310Sstevel@tonic-gate 		fmt_print("alt sects/zone= %d (%d)\n",
11320Sstevel@tonic-gate 				page3->alt_sect_zone, tmp6);
11330Sstevel@tonic-gate 	}
11340Sstevel@tonic-gate 	/*
11350Sstevel@tonic-gate 	 * Apply any changes requested via the change list method
11360Sstevel@tonic-gate 	 */
11370Sstevel@tonic-gate 	flag |= apply_chg_list(DAD_MODE_FORMAT, length,
11380Sstevel@tonic-gate 		(uchar_t *)page3, (uchar_t *)fixed,
11390Sstevel@tonic-gate 			cur_dtype->dtype_chglist);
11400Sstevel@tonic-gate 	/*
11410Sstevel@tonic-gate 	 * If no changes required, do not issue a mode select
11420Sstevel@tonic-gate 	 */
11430Sstevel@tonic-gate 	if (flag == 0) {
11440Sstevel@tonic-gate 		return (0);
11450Sstevel@tonic-gate 	}
11460Sstevel@tonic-gate 	/*
11470Sstevel@tonic-gate 	 * Issue a mode select
11480Sstevel@tonic-gate 	 */
11490Sstevel@tonic-gate 	/*
11500Sstevel@tonic-gate 	 * We always want to set the Page Format bit for mode
11510Sstevel@tonic-gate 	 * selects.  Set the Save Page bit if the drive indicates
11520Sstevel@tonic-gate 	 * that it can save this page via the mode sense.
11530Sstevel@tonic-gate 	 */
11540Sstevel@tonic-gate 	sp_flags = MODE_SELECT_PF;
11550Sstevel@tonic-gate 	if (page3->mode_page.ps) {
11560Sstevel@tonic-gate 		sp_flags |= MODE_SELECT_SP;
11570Sstevel@tonic-gate 	}
11580Sstevel@tonic-gate 	page3->mode_page.ps = 0;
11590Sstevel@tonic-gate 	header.mode_header.length = 0;
11600Sstevel@tonic-gate 	header.mode_header.device_specific = 0;
11610Sstevel@tonic-gate 	status = uscsi_mode_select(cur_file, DAD_MODE_FORMAT,
11620Sstevel@tonic-gate 		MODE_SELECT_SP, (caddr_t)page3, length, &header);
11630Sstevel@tonic-gate 	if (status && (sp_flags & MODE_SELECT_SP)) {
11640Sstevel@tonic-gate 		/* If failed, try not saving mode select params. */
11650Sstevel@tonic-gate 		sp_flags &= ~MODE_SELECT_SP;
11660Sstevel@tonic-gate 		status = uscsi_mode_select(cur_file, DAD_MODE_FORMAT,
11670Sstevel@tonic-gate 			sp_flags, (caddr_t)page3, length, &header);
11680Sstevel@tonic-gate 		}
11690Sstevel@tonic-gate 	if (status && option_msg) {
11700Sstevel@tonic-gate 		err_print("Warning: Using default drive format parameters.\n");
11710Sstevel@tonic-gate 		err_print("Warning: Drive format may not be correct.\n\n");
11720Sstevel@tonic-gate 	}
11730Sstevel@tonic-gate 
11740Sstevel@tonic-gate 	/*
11750Sstevel@tonic-gate 	 * If debugging, issue mode senses on the current and
11760Sstevel@tonic-gate 	 * saved values, so we can see the result of the mode
11770Sstevel@tonic-gate 	 * selects.
11780Sstevel@tonic-gate 	 */
11790Sstevel@tonic-gate 	if (option_msg && diag_msg) {
11800Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
11810Sstevel@tonic-gate 			MODE_SENSE_PC_CURRENT, (caddr_t)page3,
11820Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
11830Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
11840Sstevel@tonic-gate 			MODE_SENSE_PC_SAVED, (caddr_t)page3,
11850Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
11860Sstevel@tonic-gate 	}
11870Sstevel@tonic-gate 
11880Sstevel@tonic-gate 	return (0);
11890Sstevel@tonic-gate }
11900Sstevel@tonic-gate 
11910Sstevel@tonic-gate /*
11920Sstevel@tonic-gate  * Check disk geometry parameters via mode sense.
11930Sstevel@tonic-gate  * Issue a mode select if we need to change something.
11940Sstevel@tonic-gate  */
11950Sstevel@tonic-gate /*ARGSUSED*/
11960Sstevel@tonic-gate static int
scsi_ms_page4(scsi2_flag)11970Sstevel@tonic-gate scsi_ms_page4(scsi2_flag)
11980Sstevel@tonic-gate 	int	scsi2_flag;
11990Sstevel@tonic-gate {
12000Sstevel@tonic-gate 	struct mode_geometry		*page4;
12010Sstevel@tonic-gate 	struct mode_geometry		*fixed;
12020Sstevel@tonic-gate 	struct scsi_ms_header		header;
12030Sstevel@tonic-gate 	struct scsi_ms_header		fixed_hdr;
12040Sstevel@tonic-gate 	int				status;
12050Sstevel@tonic-gate 	int				tmp1, tmp2;
12060Sstevel@tonic-gate 	int				flag;
12070Sstevel@tonic-gate 	int				length;
12080Sstevel@tonic-gate 	int				sp_flags;
12090Sstevel@tonic-gate 	union {
12100Sstevel@tonic-gate 		struct mode_geometry	page4;
12110Sstevel@tonic-gate 		char			rawbuf[MAX_MODE_SENSE_SIZE];
12120Sstevel@tonic-gate 	} u_page4, u_fixed;
12130Sstevel@tonic-gate 
12140Sstevel@tonic-gate 	page4 = &u_page4.page4;
12150Sstevel@tonic-gate 	fixed = &u_fixed.page4;
12160Sstevel@tonic-gate 
12170Sstevel@tonic-gate 	/*
12180Sstevel@tonic-gate 	 * If debugging, issue mode senses on the default and
12190Sstevel@tonic-gate 	 * current values.
12200Sstevel@tonic-gate 	 */
12210Sstevel@tonic-gate 	if (option_msg && diag_msg) {
12220Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
12230Sstevel@tonic-gate 			MODE_SENSE_PC_DEFAULT, (caddr_t)page4,
12240Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
12250Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
12260Sstevel@tonic-gate 			MODE_SENSE_PC_CURRENT, (caddr_t)page4,
12270Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
12280Sstevel@tonic-gate 	}
12290Sstevel@tonic-gate 
12300Sstevel@tonic-gate 	/*
12310Sstevel@tonic-gate 	 * Issue a mode sense to determine the saved parameters
12320Sstevel@tonic-gate 	 * If the saved values fail, use the current instead.
12330Sstevel@tonic-gate 	 */
12340Sstevel@tonic-gate 	status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
12350Sstevel@tonic-gate 			MODE_SENSE_PC_SAVED, (caddr_t)page4,
12360Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
12370Sstevel@tonic-gate 	if (status) {
12380Sstevel@tonic-gate 		status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
12390Sstevel@tonic-gate 			MODE_SENSE_PC_CURRENT, (caddr_t)page4,
12400Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
12410Sstevel@tonic-gate 		if (status) {
12420Sstevel@tonic-gate 			return (0);
12430Sstevel@tonic-gate 		}
12440Sstevel@tonic-gate 	}
12450Sstevel@tonic-gate 
12460Sstevel@tonic-gate 	/*
12470Sstevel@tonic-gate 	 * We only need the common subset between the CCS
12480Sstevel@tonic-gate 	 * and SCSI-2 structures, so we can treat both
12490Sstevel@tonic-gate 	 * cases identically.  Whatever the drive gives
12500Sstevel@tonic-gate 	 * us, we return to the drive in the mode select,
12510Sstevel@tonic-gate 	 * delta'ed by whatever we want to change.
12520Sstevel@tonic-gate 	 */
12530Sstevel@tonic-gate 	length = MODESENSE_PAGE_LEN(page4);
12540Sstevel@tonic-gate 	if (length < MIN_PAGE4_LEN) {
12550Sstevel@tonic-gate 		return (0);
12560Sstevel@tonic-gate 	}
12570Sstevel@tonic-gate 
12580Sstevel@tonic-gate 	/*
12590Sstevel@tonic-gate 	 * Ask for changeable parameters.
12600Sstevel@tonic-gate 	 */
12610Sstevel@tonic-gate 	status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
12620Sstevel@tonic-gate 		MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
12630Sstevel@tonic-gate 		MAX_MODE_SENSE_SIZE, &fixed_hdr);
12640Sstevel@tonic-gate 	if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE4_LEN) {
12650Sstevel@tonic-gate 		return (0);
12660Sstevel@tonic-gate 	}
12670Sstevel@tonic-gate 
12680Sstevel@tonic-gate 	/*
12690Sstevel@tonic-gate 	 * We need to issue a mode select only if one or more
12700Sstevel@tonic-gate 	 * parameters need to be changed, and those parameters
12710Sstevel@tonic-gate 	 * are flagged by the drive as changeable.
12720Sstevel@tonic-gate 	 */
12730Sstevel@tonic-gate 	tmp1 = (page4->cyl_ub << 16) + (page4->cyl_mb << 8) + page4->cyl_lb;
12740Sstevel@tonic-gate 	tmp2 = page4->heads;
12750Sstevel@tonic-gate 
12760Sstevel@tonic-gate 	flag = 0;
12770Sstevel@tonic-gate 	if ((cur_dtype->dtype_options & SUP_PHEAD) && fixed->heads != 0) {
12780Sstevel@tonic-gate 		flag |= (page4->heads != phead);
12790Sstevel@tonic-gate 		page4->heads = phead;
12800Sstevel@tonic-gate 	}
12810Sstevel@tonic-gate 	/*
12820Sstevel@tonic-gate 	 * Notify user of changes so far
12830Sstevel@tonic-gate 	 */
12840Sstevel@tonic-gate 	if (flag && option_msg) {
12850Sstevel@tonic-gate 		fmt_print("PAGE 4:   cylinders= %d    heads= %d (%d)\n",
12860Sstevel@tonic-gate 			tmp1, page4->heads, tmp2);
12870Sstevel@tonic-gate 	}
12880Sstevel@tonic-gate 	/*
12890Sstevel@tonic-gate 	 * Apply any changes requested via the change list method
12900Sstevel@tonic-gate 	 */
12910Sstevel@tonic-gate 	flag |= apply_chg_list(DAD_MODE_GEOMETRY, length,
12920Sstevel@tonic-gate 		(uchar_t *)page4, (uchar_t *)fixed,
12930Sstevel@tonic-gate 			cur_dtype->dtype_chglist);
12940Sstevel@tonic-gate 	/*
12950Sstevel@tonic-gate 	 * If no changes required, do not issue a mode select
12960Sstevel@tonic-gate 	 */
12970Sstevel@tonic-gate 	if (flag == 0) {
12980Sstevel@tonic-gate 		return (0);
12990Sstevel@tonic-gate 	}
13000Sstevel@tonic-gate 	/*
13010Sstevel@tonic-gate 	 * Issue a mode select
13020Sstevel@tonic-gate 	 */
13030Sstevel@tonic-gate 	/*
13040Sstevel@tonic-gate 	 * We always want to set the Page Format bit for mode
13050Sstevel@tonic-gate 	 * selects.  Set the Save Page bit if the drive indicates
13060Sstevel@tonic-gate 	 * that it can save this page via the mode sense.
13070Sstevel@tonic-gate 	 */
13080Sstevel@tonic-gate 	sp_flags = MODE_SELECT_PF;
13090Sstevel@tonic-gate 	if (page4->mode_page.ps) {
13100Sstevel@tonic-gate 		sp_flags |= MODE_SELECT_SP;
13110Sstevel@tonic-gate 	}
13120Sstevel@tonic-gate 	page4->mode_page.ps = 0;
13130Sstevel@tonic-gate 	header.mode_header.length = 0;
13140Sstevel@tonic-gate 	header.mode_header.device_specific = 0;
13150Sstevel@tonic-gate 	status = uscsi_mode_select(cur_file, DAD_MODE_GEOMETRY,
13160Sstevel@tonic-gate 		MODE_SELECT_SP, (caddr_t)page4, length, &header);
13170Sstevel@tonic-gate 	if (status && (sp_flags & MODE_SELECT_SP)) {
13180Sstevel@tonic-gate 		/* If failed, try not saving mode select params. */
13190Sstevel@tonic-gate 		sp_flags &= ~MODE_SELECT_SP;
13200Sstevel@tonic-gate 		status = uscsi_mode_select(cur_file, DAD_MODE_GEOMETRY,
13210Sstevel@tonic-gate 			sp_flags, (caddr_t)page4, length, &header);
13220Sstevel@tonic-gate 		}
13230Sstevel@tonic-gate 	if (status && option_msg) {
13240Sstevel@tonic-gate 		err_print("Warning: Using default drive geometry.\n\n");
13250Sstevel@tonic-gate 	}
13260Sstevel@tonic-gate 
13270Sstevel@tonic-gate 	/*
13280Sstevel@tonic-gate 	 * If debugging, issue mode senses on the current and
13290Sstevel@tonic-gate 	 * saved values, so we can see the result of the mode
13300Sstevel@tonic-gate 	 * selects.
13310Sstevel@tonic-gate 	 */
13320Sstevel@tonic-gate 	if (option_msg && diag_msg) {
13330Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
13340Sstevel@tonic-gate 			MODE_SENSE_PC_CURRENT, (caddr_t)page4,
13350Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
13360Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
13370Sstevel@tonic-gate 			MODE_SENSE_PC_SAVED, (caddr_t)page4,
13380Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
13390Sstevel@tonic-gate 	}
13400Sstevel@tonic-gate 
13410Sstevel@tonic-gate 	return (0);
13420Sstevel@tonic-gate }
13430Sstevel@tonic-gate 
13440Sstevel@tonic-gate /*
13450Sstevel@tonic-gate  * Check SCSI-2 disk cache parameters via mode sense.
13460Sstevel@tonic-gate  * Issue a mode select if we need to change something.
13470Sstevel@tonic-gate  */
13480Sstevel@tonic-gate /*ARGSUSED*/
13490Sstevel@tonic-gate static int
scsi_ms_page8(scsi2_flag)13500Sstevel@tonic-gate scsi_ms_page8(scsi2_flag)
13510Sstevel@tonic-gate 	int	scsi2_flag;
13520Sstevel@tonic-gate {
13530Sstevel@tonic-gate 	struct mode_cache		*page8;
13540Sstevel@tonic-gate 	struct mode_cache		*fixed;
13550Sstevel@tonic-gate 	struct scsi_ms_header		header;
13560Sstevel@tonic-gate 	struct scsi_ms_header		fixed_hdr;
13570Sstevel@tonic-gate 	int				status;
13580Sstevel@tonic-gate 	int				flag;
13590Sstevel@tonic-gate 	int				length;
13600Sstevel@tonic-gate 	int				sp_flags;
13610Sstevel@tonic-gate 	union {
13620Sstevel@tonic-gate 		struct mode_cache	page8;
13630Sstevel@tonic-gate 		char			rawbuf[MAX_MODE_SENSE_SIZE];
13640Sstevel@tonic-gate 	} u_page8, u_fixed;
13650Sstevel@tonic-gate 
13660Sstevel@tonic-gate 	page8 = &u_page8.page8;
13670Sstevel@tonic-gate 	fixed = &u_fixed.page8;
13680Sstevel@tonic-gate 
13690Sstevel@tonic-gate 	/*
13700Sstevel@tonic-gate 	 * Only SCSI-2 devices support this page
13710Sstevel@tonic-gate 	 */
13720Sstevel@tonic-gate 	if (!scsi2_flag) {
13730Sstevel@tonic-gate 		return (0);
13740Sstevel@tonic-gate 	}
13750Sstevel@tonic-gate 
13760Sstevel@tonic-gate 	/*
13770Sstevel@tonic-gate 	 * If debugging, issue mode senses on the default and
13780Sstevel@tonic-gate 	 * current values.
13790Sstevel@tonic-gate 	 */
13800Sstevel@tonic-gate 	if (option_msg && diag_msg) {
13810Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
13820Sstevel@tonic-gate 			MODE_SENSE_PC_DEFAULT, (caddr_t)page8,
13830Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
13840Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
13850Sstevel@tonic-gate 			MODE_SENSE_PC_CURRENT, (caddr_t)page8,
13860Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
13870Sstevel@tonic-gate 	}
13880Sstevel@tonic-gate 
13890Sstevel@tonic-gate 	/*
13900Sstevel@tonic-gate 	 * Issue a mode sense to determine the saved parameters
13910Sstevel@tonic-gate 	 * If the saved values fail, use the current instead.
13920Sstevel@tonic-gate 	 */
13930Sstevel@tonic-gate 	status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
13940Sstevel@tonic-gate 			MODE_SENSE_PC_SAVED, (caddr_t)page8,
13950Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
13960Sstevel@tonic-gate 	if (status) {
13970Sstevel@tonic-gate 		status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
13980Sstevel@tonic-gate 			MODE_SENSE_PC_CURRENT, (caddr_t)page8,
13990Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
14000Sstevel@tonic-gate 		if (status) {
14010Sstevel@tonic-gate 			return (0);
14020Sstevel@tonic-gate 		}
14030Sstevel@tonic-gate 	}
14040Sstevel@tonic-gate 
14050Sstevel@tonic-gate 	/*
14060Sstevel@tonic-gate 	 * We only need the common subset between the CCS
14070Sstevel@tonic-gate 	 * and SCSI-2 structures, so we can treat both
14080Sstevel@tonic-gate 	 * cases identically.  Whatever the drive gives
14090Sstevel@tonic-gate 	 * us, we return to the drive in the mode select,
14100Sstevel@tonic-gate 	 * delta'ed by whatever we want to change.
14110Sstevel@tonic-gate 	 */
14120Sstevel@tonic-gate 	length = MODESENSE_PAGE_LEN(page8);
14130Sstevel@tonic-gate 	if (length < MIN_PAGE8_LEN) {
14140Sstevel@tonic-gate 		return (0);
14150Sstevel@tonic-gate 	}
14160Sstevel@tonic-gate 
14170Sstevel@tonic-gate 	/*
14180Sstevel@tonic-gate 	 * Ask for changeable parameters.
14190Sstevel@tonic-gate 	 */
14200Sstevel@tonic-gate 	status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
14210Sstevel@tonic-gate 		MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
14220Sstevel@tonic-gate 		MAX_MODE_SENSE_SIZE, &fixed_hdr);
14230Sstevel@tonic-gate 	if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE8_LEN) {
14240Sstevel@tonic-gate 		return (0);
14250Sstevel@tonic-gate 	}
14260Sstevel@tonic-gate 
14270Sstevel@tonic-gate 	/*
14280Sstevel@tonic-gate 	 * We need to issue a mode select only if one or more
14290Sstevel@tonic-gate 	 * parameters need to be changed, and those parameters
14300Sstevel@tonic-gate 	 * are flagged by the drive as changeable.
14310Sstevel@tonic-gate 	 */
14320Sstevel@tonic-gate 	flag = 0;
14330Sstevel@tonic-gate 	/*
14340Sstevel@tonic-gate 	 * Apply any changes requested via the change list method
14350Sstevel@tonic-gate 	 */
14360Sstevel@tonic-gate 	flag |= apply_chg_list(DAD_MODE_CACHE, length,
14370Sstevel@tonic-gate 		(uchar_t *)page8, (uchar_t *)fixed,
14380Sstevel@tonic-gate 			cur_dtype->dtype_chglist);
14390Sstevel@tonic-gate 	/*
14400Sstevel@tonic-gate 	 * If no changes required, do not issue a mode select
14410Sstevel@tonic-gate 	 */
14420Sstevel@tonic-gate 	if (flag == 0) {
14430Sstevel@tonic-gate 		return (0);
14440Sstevel@tonic-gate 	}
14450Sstevel@tonic-gate 	/*
14460Sstevel@tonic-gate 	 * Issue a mode select
14470Sstevel@tonic-gate 	 */
14480Sstevel@tonic-gate 	/*
14490Sstevel@tonic-gate 	 * We always want to set the Page Format bit for mode
14500Sstevel@tonic-gate 	 * selects.  Set the Save Page bit if the drive indicates
14510Sstevel@tonic-gate 	 * that it can save this page via the mode sense.
14520Sstevel@tonic-gate 	 */
14530Sstevel@tonic-gate 	sp_flags = MODE_SELECT_PF;
14540Sstevel@tonic-gate 	if (page8->mode_page.ps) {
14550Sstevel@tonic-gate 		sp_flags |= MODE_SELECT_SP;
14560Sstevel@tonic-gate 	}
14570Sstevel@tonic-gate 	page8->mode_page.ps = 0;
14580Sstevel@tonic-gate 	header.mode_header.length = 0;
14590Sstevel@tonic-gate 	header.mode_header.device_specific = 0;
14600Sstevel@tonic-gate 	status = uscsi_mode_select(cur_file, DAD_MODE_CACHE,
14610Sstevel@tonic-gate 		sp_flags, (caddr_t)page8, length, &header);
14620Sstevel@tonic-gate 	if (status && (sp_flags & MODE_SELECT_SP)) {
14630Sstevel@tonic-gate 		/* If failed, try not saving mode select params. */
14640Sstevel@tonic-gate 		sp_flags &= ~MODE_SELECT_SP;
14650Sstevel@tonic-gate 		status = uscsi_mode_select(cur_file, DAD_MODE_CACHE,
14660Sstevel@tonic-gate 			sp_flags, (caddr_t)page8, length, &header);
14670Sstevel@tonic-gate 		}
14680Sstevel@tonic-gate 	if (status && option_msg) {
14690Sstevel@tonic-gate 		err_print("\
14700Sstevel@tonic-gate Warning: Using default SCSI-2 cache parameters.\n\n");
14710Sstevel@tonic-gate 	}
14720Sstevel@tonic-gate 
14730Sstevel@tonic-gate 	/*
14740Sstevel@tonic-gate 	 * If debugging, issue mode senses on the current and
14750Sstevel@tonic-gate 	 * saved values, so we can see the result of the mode
14760Sstevel@tonic-gate 	 * selects.
14770Sstevel@tonic-gate 	 */
14780Sstevel@tonic-gate 	if (option_msg && diag_msg) {
14790Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
14800Sstevel@tonic-gate 			MODE_SENSE_PC_CURRENT, (caddr_t)page8,
14810Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
14820Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
14830Sstevel@tonic-gate 			MODE_SENSE_PC_SAVED, (caddr_t)page8,
14840Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
14850Sstevel@tonic-gate 	}
14860Sstevel@tonic-gate 
14870Sstevel@tonic-gate 	return (0);
14880Sstevel@tonic-gate }
14890Sstevel@tonic-gate 
14900Sstevel@tonic-gate /*
14910Sstevel@tonic-gate  * Check CCS disk cache parameters via mode sense.
14920Sstevel@tonic-gate  * Issue a mode select if we need to change something.
14930Sstevel@tonic-gate  */
14940Sstevel@tonic-gate /*ARGSUSED*/
14950Sstevel@tonic-gate static int
scsi_ms_page38(scsi2_flag)14960Sstevel@tonic-gate scsi_ms_page38(scsi2_flag)
14970Sstevel@tonic-gate 	int	scsi2_flag;
14980Sstevel@tonic-gate {
14990Sstevel@tonic-gate 	struct mode_cache_ccs		*page38;
15000Sstevel@tonic-gate 	struct mode_cache_ccs		*fixed;
15010Sstevel@tonic-gate 	struct scsi_ms_header		header;
15020Sstevel@tonic-gate 	struct scsi_ms_header		fixed_hdr;
15030Sstevel@tonic-gate 	int				status;
15040Sstevel@tonic-gate 	int				tmp1, tmp2, tmp3, tmp4;
15050Sstevel@tonic-gate 	int				flag;
15060Sstevel@tonic-gate 	int				length;
15070Sstevel@tonic-gate 	int				sp_flags;
15080Sstevel@tonic-gate 	union {
15090Sstevel@tonic-gate 		struct mode_cache_ccs	page38;
15100Sstevel@tonic-gate 		char			rawbuf[MAX_MODE_SENSE_SIZE];
15110Sstevel@tonic-gate 	} u_page38, u_fixed;
15120Sstevel@tonic-gate 
15130Sstevel@tonic-gate 	/*
15140Sstevel@tonic-gate 	 * First, determine if we need to look at page 38 at all.
15150Sstevel@tonic-gate 	 * Not all devices support it.
15160Sstevel@tonic-gate 	 */
15170Sstevel@tonic-gate 	if (((cur_dtype->dtype_options & (SUP_CACHE | SUP_PREFETCH |
15180Sstevel@tonic-gate 		SUP_CACHE_MIN | SUP_CACHE_MAX)) == 0) &&
15190Sstevel@tonic-gate 			(!chg_list_affects_page(cur_dtype->dtype_chglist,
15200Sstevel@tonic-gate 				0x38))) {
15210Sstevel@tonic-gate 		return (0);
15220Sstevel@tonic-gate 	}
15230Sstevel@tonic-gate 
15240Sstevel@tonic-gate 	page38 = &u_page38.page38;
15250Sstevel@tonic-gate 	fixed = &u_fixed.page38;
15260Sstevel@tonic-gate 
15270Sstevel@tonic-gate 	/*
15280Sstevel@tonic-gate 	 * If debugging, issue mode senses on the default and
15290Sstevel@tonic-gate 	 * current values.
15300Sstevel@tonic-gate 	 */
15310Sstevel@tonic-gate 	if (option_msg && diag_msg) {
15320Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
15330Sstevel@tonic-gate 			MODE_SENSE_PC_DEFAULT, (caddr_t)page38,
15340Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
15350Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
15360Sstevel@tonic-gate 			MODE_SENSE_PC_CURRENT, (caddr_t)page38,
15370Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
15380Sstevel@tonic-gate 	}
15390Sstevel@tonic-gate 
15400Sstevel@tonic-gate 	/*
15410Sstevel@tonic-gate 	 * Issue a mode sense to determine the saved parameters
15420Sstevel@tonic-gate 	 * If the saved values fail, use the current instead.
15430Sstevel@tonic-gate 	 */
15440Sstevel@tonic-gate 	status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
15450Sstevel@tonic-gate 			MODE_SENSE_PC_SAVED, (caddr_t)page38,
15460Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
15470Sstevel@tonic-gate 	if (status) {
15480Sstevel@tonic-gate 		status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
15490Sstevel@tonic-gate 			MODE_SENSE_PC_CURRENT, (caddr_t)page38,
15500Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
15510Sstevel@tonic-gate 		if (status) {
15520Sstevel@tonic-gate 			return (0);
15530Sstevel@tonic-gate 		}
15540Sstevel@tonic-gate 	}
15550Sstevel@tonic-gate 
15560Sstevel@tonic-gate 	/*
15570Sstevel@tonic-gate 	 * We only need the common subset between the CCS
15580Sstevel@tonic-gate 	 * and SCSI-2 structures, so we can treat both
15590Sstevel@tonic-gate 	 * cases identically.  Whatever the drive gives
15600Sstevel@tonic-gate 	 * us, we return to the drive in the mode select,
15610Sstevel@tonic-gate 	 * delta'ed by whatever we want to change.
15620Sstevel@tonic-gate 	 */
15630Sstevel@tonic-gate 	length = MODESENSE_PAGE_LEN(page38);
15640Sstevel@tonic-gate 	if (length < MIN_PAGE38_LEN) {
15650Sstevel@tonic-gate 		return (0);
15660Sstevel@tonic-gate 	}
15670Sstevel@tonic-gate 
15680Sstevel@tonic-gate 	/*
15690Sstevel@tonic-gate 	 * Ask for changeable parameters.
15700Sstevel@tonic-gate 	 */
15710Sstevel@tonic-gate 	status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
15720Sstevel@tonic-gate 		MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
15730Sstevel@tonic-gate 		MAX_MODE_SENSE_SIZE, &fixed_hdr);
15740Sstevel@tonic-gate 	if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE38_LEN) {
15750Sstevel@tonic-gate 		return (0);
15760Sstevel@tonic-gate 	}
15770Sstevel@tonic-gate 
15780Sstevel@tonic-gate 	/*
15790Sstevel@tonic-gate 	 * We need to issue a mode select only if one or more
15800Sstevel@tonic-gate 	 * parameters need to be changed, and those parameters
15810Sstevel@tonic-gate 	 * are flagged by the drive as changeable.
15820Sstevel@tonic-gate 	 */
15830Sstevel@tonic-gate 	tmp1 = page38->mode;
15840Sstevel@tonic-gate 	tmp2 = page38->threshold;
15850Sstevel@tonic-gate 	tmp3 = page38->min_prefetch;
15860Sstevel@tonic-gate 	tmp4 = page38->max_prefetch;
15870Sstevel@tonic-gate 
15880Sstevel@tonic-gate 	flag = 0;
15890Sstevel@tonic-gate 	if ((cur_dtype->dtype_options & SUP_CACHE) &&
15900Sstevel@tonic-gate 			(fixed->mode & cur_dtype->dtype_cache) ==
15910Sstevel@tonic-gate 				cur_dtype->dtype_cache) {
15920Sstevel@tonic-gate 		flag |= (page38->mode != cur_dtype->dtype_cache);
15930Sstevel@tonic-gate 		page38->mode = cur_dtype->dtype_cache;
15940Sstevel@tonic-gate 	}
15950Sstevel@tonic-gate 	if ((cur_dtype->dtype_options & SUP_PREFETCH) &&
15960Sstevel@tonic-gate 		(fixed->threshold & cur_dtype->dtype_threshold) ==
15970Sstevel@tonic-gate 				cur_dtype->dtype_threshold) {
15980Sstevel@tonic-gate 		flag |= (page38->threshold != cur_dtype->dtype_threshold);
15990Sstevel@tonic-gate 		page38->threshold = cur_dtype->dtype_threshold;
16000Sstevel@tonic-gate 	}
16010Sstevel@tonic-gate 	if ((cur_dtype->dtype_options & SUP_CACHE_MIN) &&
16020Sstevel@tonic-gate 		(fixed->min_prefetch & cur_dtype->dtype_prefetch_min) ==
16030Sstevel@tonic-gate 				cur_dtype->dtype_prefetch_min) {
16040Sstevel@tonic-gate 		flag |= (page38->min_prefetch != cur_dtype->dtype_prefetch_min);
16050Sstevel@tonic-gate 		page38->min_prefetch = cur_dtype->dtype_prefetch_min;
16060Sstevel@tonic-gate 	}
16070Sstevel@tonic-gate 	if ((cur_dtype->dtype_options & SUP_CACHE_MAX) &&
16080Sstevel@tonic-gate 		(fixed->max_prefetch & cur_dtype->dtype_prefetch_max) ==
16090Sstevel@tonic-gate 				cur_dtype->dtype_prefetch_max) {
16100Sstevel@tonic-gate 		flag |= (page38->max_prefetch != cur_dtype->dtype_prefetch_max);
16110Sstevel@tonic-gate 		page38->max_prefetch = cur_dtype->dtype_prefetch_max;
16120Sstevel@tonic-gate 	}
16130Sstevel@tonic-gate 	/*
16140Sstevel@tonic-gate 	 * Notify the user of changes up to this point
16150Sstevel@tonic-gate 	 */
16160Sstevel@tonic-gate 	if (flag && option_msg) {
16170Sstevel@tonic-gate 		fmt_print("PAGE 38: cache mode= 0x%x (0x%x)\n",
16180Sstevel@tonic-gate 					page38->mode, tmp1);
16190Sstevel@tonic-gate 		fmt_print("         min. prefetch multiplier= %d   ",
16200Sstevel@tonic-gate 					page38->min_multiplier);
16210Sstevel@tonic-gate 		fmt_print("max. prefetch multiplier= %d\n",
16220Sstevel@tonic-gate 					page38->max_multiplier);
16230Sstevel@tonic-gate 		fmt_print("         threshold= %d (%d)   ",
16240Sstevel@tonic-gate 					page38->threshold, tmp2);
16250Sstevel@tonic-gate 		fmt_print("min. prefetch= %d (%d)   ",
16260Sstevel@tonic-gate 					page38->min_prefetch, tmp3);
16270Sstevel@tonic-gate 		fmt_print("max. prefetch= %d (%d)\n",
16280Sstevel@tonic-gate 					page38->max_prefetch, tmp4);
16290Sstevel@tonic-gate 	}
16300Sstevel@tonic-gate 	/*
16310Sstevel@tonic-gate 	 * Apply any changes requested via the change list method
16320Sstevel@tonic-gate 	 */
16330Sstevel@tonic-gate 	flag |= apply_chg_list(DAD_MODE_CACHE_CCS, length,
16340Sstevel@tonic-gate 		(uchar_t *)page38, (uchar_t *)fixed,
16350Sstevel@tonic-gate 			cur_dtype->dtype_chglist);
16360Sstevel@tonic-gate 	/*
16370Sstevel@tonic-gate 	 * If no changes required, do not issue a mode select
16380Sstevel@tonic-gate 	 */
16390Sstevel@tonic-gate 	if (flag == 0) {
16400Sstevel@tonic-gate 		return (0);
16410Sstevel@tonic-gate 	}
16420Sstevel@tonic-gate 	/*
16430Sstevel@tonic-gate 	 * Issue a mode select
16440Sstevel@tonic-gate 	 *
16450Sstevel@tonic-gate 	 * We always want to set the Page Format bit for mode
16460Sstevel@tonic-gate 	 * selects.  Set the Save Page bit if the drive indicates
16470Sstevel@tonic-gate 	 * that it can save this page via the mode sense.
16480Sstevel@tonic-gate 	 */
16490Sstevel@tonic-gate 	sp_flags = MODE_SELECT_PF;
16500Sstevel@tonic-gate 	if (page38->mode_page.ps) {
16510Sstevel@tonic-gate 		sp_flags |= MODE_SELECT_SP;
16520Sstevel@tonic-gate 	}
16530Sstevel@tonic-gate 	page38->mode_page.ps = 0;
16540Sstevel@tonic-gate 	header.mode_header.length = 0;
16550Sstevel@tonic-gate 	header.mode_header.device_specific = 0;
16560Sstevel@tonic-gate 	status = uscsi_mode_select(cur_file, DAD_MODE_CACHE_CCS,
16570Sstevel@tonic-gate 		sp_flags, (caddr_t)page38, length, &header);
16580Sstevel@tonic-gate 	if (status && (sp_flags & MODE_SELECT_SP)) {
16590Sstevel@tonic-gate 		/* If failed, try not saving mode select params. */
16600Sstevel@tonic-gate 		sp_flags &= ~MODE_SELECT_SP;
16610Sstevel@tonic-gate 		status = uscsi_mode_select(cur_file, DAD_MODE_CACHE_CCS,
16620Sstevel@tonic-gate 			sp_flags, (caddr_t)page38, length, &header);
16630Sstevel@tonic-gate 		}
16640Sstevel@tonic-gate 	if (status && option_msg) {
16650Sstevel@tonic-gate 		err_print("Warning: Using default CCS cache parameters.\n\n");
16660Sstevel@tonic-gate 	}
16670Sstevel@tonic-gate 
16680Sstevel@tonic-gate 	/*
16690Sstevel@tonic-gate 	 * If debugging, issue mode senses on the current and
16700Sstevel@tonic-gate 	 * saved values, so we can see the result of the mode
16710Sstevel@tonic-gate 	 * selects.
16720Sstevel@tonic-gate 	 */
16730Sstevel@tonic-gate 	if (option_msg && diag_msg) {
16740Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
16750Sstevel@tonic-gate 			MODE_SENSE_PC_CURRENT, (caddr_t)page38,
16760Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
16770Sstevel@tonic-gate 		(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
16780Sstevel@tonic-gate 			MODE_SENSE_PC_SAVED, (caddr_t)page38,
16790Sstevel@tonic-gate 			MAX_MODE_SENSE_SIZE, &header);
16800Sstevel@tonic-gate 	}
16810Sstevel@tonic-gate 
16820Sstevel@tonic-gate 	return (0);
16830Sstevel@tonic-gate }
16840Sstevel@tonic-gate 
16850Sstevel@tonic-gate 
16860Sstevel@tonic-gate /*
16870Sstevel@tonic-gate  * Extract the manufacturer's defect list.
16880Sstevel@tonic-gate  */
16890Sstevel@tonic-gate int
scsi_ex_man(list)16900Sstevel@tonic-gate scsi_ex_man(list)
16910Sstevel@tonic-gate 	struct  defect_list	*list;
16920Sstevel@tonic-gate {
16930Sstevel@tonic-gate 	int	i;
16940Sstevel@tonic-gate 
16950Sstevel@tonic-gate 	i = scsi_read_defect_data(list, DLD_MAN_DEF_LIST);
16960Sstevel@tonic-gate 	if (i != 0)
16970Sstevel@tonic-gate 		return (i);
16980Sstevel@tonic-gate 	list->flags &= ~LIST_PGLIST;
16990Sstevel@tonic-gate 	return (0);
17000Sstevel@tonic-gate }
17010Sstevel@tonic-gate 
17020Sstevel@tonic-gate /*
17030Sstevel@tonic-gate  * Extract the current defect list.
17040Sstevel@tonic-gate  * For embedded scsi drives, this means both the manufacturer's (P)
17050Sstevel@tonic-gate  * and the grown (G) lists.
17060Sstevel@tonic-gate  */
17070Sstevel@tonic-gate int
scsi_ex_cur(list)17080Sstevel@tonic-gate scsi_ex_cur(list)
17090Sstevel@tonic-gate 	struct  defect_list *list;
17100Sstevel@tonic-gate {
17110Sstevel@tonic-gate 	int	i;
17120Sstevel@tonic-gate 
17130Sstevel@tonic-gate 	i = scsi_read_defect_data(list, DLD_GROWN_DEF_LIST|DLD_MAN_DEF_LIST);
17140Sstevel@tonic-gate 	if (i != 0)
17150Sstevel@tonic-gate 		return (i);
17160Sstevel@tonic-gate 	list->flags |= LIST_PGLIST;
17170Sstevel@tonic-gate 	return (0);
17180Sstevel@tonic-gate }
17190Sstevel@tonic-gate 
17200Sstevel@tonic-gate 
17210Sstevel@tonic-gate /*
17220Sstevel@tonic-gate  * Extract the grown list only
17230Sstevel@tonic-gate  */
17240Sstevel@tonic-gate int
scsi_ex_grown(list)17250Sstevel@tonic-gate scsi_ex_grown(list)
17260Sstevel@tonic-gate 	struct defect_list *list;
17270Sstevel@tonic-gate {
17280Sstevel@tonic-gate 	int	i;
17290Sstevel@tonic-gate 
17300Sstevel@tonic-gate 	i = scsi_read_defect_data(list, DLD_GROWN_DEF_LIST);
17310Sstevel@tonic-gate 	if (i != 0)
17320Sstevel@tonic-gate 		return (i);
17330Sstevel@tonic-gate 	list->flags |= LIST_PGLIST;
17340Sstevel@tonic-gate 	return (0);
17350Sstevel@tonic-gate }
17360Sstevel@tonic-gate 
17370Sstevel@tonic-gate 
17380Sstevel@tonic-gate static int
scsi_read_defect_data(list,pglist_flags)17390Sstevel@tonic-gate scsi_read_defect_data(list, pglist_flags)
17400Sstevel@tonic-gate 	struct  defect_list	*list;
17410Sstevel@tonic-gate 	int			pglist_flags;
17420Sstevel@tonic-gate {
17430Sstevel@tonic-gate 	struct uscsi_cmd	ucmd;
17440Sstevel@tonic-gate 	char			rqbuf[255];
17450Sstevel@tonic-gate 	union scsi_cdb		cdb;
17460Sstevel@tonic-gate 	struct scsi_defect_list	*defects;
17470Sstevel@tonic-gate 	struct scsi_defect_list	def_list;
17480Sstevel@tonic-gate 	struct scsi_defect_hdr	*hdr;
17490Sstevel@tonic-gate 	int			status;
17500Sstevel@tonic-gate 	int			nbytes;
17510Sstevel@tonic-gate 	int			len;	/* returned defect list length */
17520Sstevel@tonic-gate 	struct scsi_extended_sense	*rq;
17530Sstevel@tonic-gate 
17540Sstevel@tonic-gate 	hdr = (struct scsi_defect_hdr *)&def_list;
17550Sstevel@tonic-gate 
17560Sstevel@tonic-gate 	/*
17570Sstevel@tonic-gate 	 * First get length of list by asking for the header only.
17580Sstevel@tonic-gate 	 */
17590Sstevel@tonic-gate 	(void) memset((char *)&def_list, 0, sizeof (def_list));
17600Sstevel@tonic-gate 
17610Sstevel@tonic-gate 	/*
17620Sstevel@tonic-gate 	 * Build and execute the uscsi ioctl
17630Sstevel@tonic-gate 	 */
17640Sstevel@tonic-gate 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
17650Sstevel@tonic-gate 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
17660Sstevel@tonic-gate 	(void) memset((char *)rqbuf, 0, 255);
17670Sstevel@tonic-gate 	cdb.scc_cmd = SCMD_READ_DEFECT_LIST;
17680Sstevel@tonic-gate 	FORMG1COUNT(&cdb, sizeof (struct scsi_defect_hdr));
17690Sstevel@tonic-gate 	cdb.cdb_opaque[2] = pglist_flags | DLD_BFI_FORMAT;
17700Sstevel@tonic-gate 	ucmd.uscsi_cdb = (caddr_t)&cdb;
17710Sstevel@tonic-gate 	ucmd.uscsi_cdblen = CDB_GROUP1;
17720Sstevel@tonic-gate 	ucmd.uscsi_bufaddr = (caddr_t)hdr;
17730Sstevel@tonic-gate 	ucmd.uscsi_buflen = sizeof (struct scsi_defect_hdr);
17740Sstevel@tonic-gate 	ucmd.uscsi_rqbuf = rqbuf;
17750Sstevel@tonic-gate 	ucmd.uscsi_rqlen = sizeof (rqbuf);
17760Sstevel@tonic-gate 	ucmd.uscsi_rqresid = sizeof (rqbuf);
17770Sstevel@tonic-gate 	rq = (struct scsi_extended_sense *)ucmd.uscsi_rqbuf;
17780Sstevel@tonic-gate 
17790Sstevel@tonic-gate 	status = uscsi_cmd(cur_file, &ucmd,
17800Sstevel@tonic-gate 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
17810Sstevel@tonic-gate 
17820Sstevel@tonic-gate 	if (status != 0) {
17830Sstevel@tonic-gate 		/*
17840Sstevel@tonic-gate 		 * check if read_defect_list_is_supported.
17850Sstevel@tonic-gate 		 */
17860Sstevel@tonic-gate 		if (ucmd.uscsi_rqstatus == STATUS_GOOD &&
17870Sstevel@tonic-gate 			rq->es_key == KEY_ILLEGAL_REQUEST &&
17880Sstevel@tonic-gate 					rq->es_add_code == INVALID_OPCODE) {
17890Sstevel@tonic-gate 			err_print("\nWARNING: Current Disk does not support"
17900Sstevel@tonic-gate 				" defect lists. \n");
17910Sstevel@tonic-gate 		} else
17920Sstevel@tonic-gate 		if (option_msg) {
17930Sstevel@tonic-gate 			err_print("No %s defect list.\n",
17940Sstevel@tonic-gate 				pglist_flags & DLD_GROWN_DEF_LIST ?
17950Sstevel@tonic-gate 				"grown" : "manufacturer's");
17960Sstevel@tonic-gate 		}
17970Sstevel@tonic-gate 		return (-1);
17980Sstevel@tonic-gate 	}
17990Sstevel@tonic-gate 
18000Sstevel@tonic-gate 	/*
18010Sstevel@tonic-gate 	 * Read the full list the second time
18020Sstevel@tonic-gate 	 */
18030Sstevel@tonic-gate 	hdr->length = BE_16(hdr->length);
18040Sstevel@tonic-gate 	len = hdr->length;
18050Sstevel@tonic-gate 	nbytes = len + sizeof (struct scsi_defect_hdr);
18060Sstevel@tonic-gate 
18070Sstevel@tonic-gate 	defects = zalloc(nbytes);
18080Sstevel@tonic-gate 	*(struct scsi_defect_hdr *)defects = *(struct scsi_defect_hdr *)hdr;
18090Sstevel@tonic-gate 
18100Sstevel@tonic-gate 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
18110Sstevel@tonic-gate 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
18120Sstevel@tonic-gate 	cdb.scc_cmd = SCMD_READ_DEFECT_LIST;
18130Sstevel@tonic-gate 	FORMG1COUNT(&cdb, nbytes);
18140Sstevel@tonic-gate 	cdb.cdb_opaque[2] = pglist_flags | DLD_BFI_FORMAT;
18150Sstevel@tonic-gate 	ucmd.uscsi_cdb = (caddr_t)&cdb;
18160Sstevel@tonic-gate 	ucmd.uscsi_cdblen = CDB_GROUP1;
18170Sstevel@tonic-gate 	ucmd.uscsi_bufaddr = (caddr_t)defects;
18180Sstevel@tonic-gate 	ucmd.uscsi_buflen = nbytes;
18190Sstevel@tonic-gate 	status = uscsi_cmd(cur_file, &ucmd,
18200Sstevel@tonic-gate 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
18210Sstevel@tonic-gate 
18220Sstevel@tonic-gate 	if (status) {
18230Sstevel@tonic-gate 		err_print("can't read defect list 2nd time");
18240Sstevel@tonic-gate 		destroy_data((char *)defects);
18250Sstevel@tonic-gate 		return (-1);
18260Sstevel@tonic-gate 	}
18270Sstevel@tonic-gate 
18280Sstevel@tonic-gate 	defects->length = BE_16(defects->length);
18290Sstevel@tonic-gate 
18300Sstevel@tonic-gate 	if (len != hdr->length) {
18310Sstevel@tonic-gate 		err_print("not enough defects");
18320Sstevel@tonic-gate 		destroy_data((char *)defects);
18330Sstevel@tonic-gate 		return (-1);
18340Sstevel@tonic-gate 	}
18350Sstevel@tonic-gate 	scsi_convert_list_to_new(list, (struct scsi_defect_list *)defects,
18360Sstevel@tonic-gate 			DLD_BFI_FORMAT);
18370Sstevel@tonic-gate 	destroy_data((char *)defects);
18380Sstevel@tonic-gate 	return (0);
18390Sstevel@tonic-gate }
18400Sstevel@tonic-gate 
18410Sstevel@tonic-gate 
18420Sstevel@tonic-gate /*
18430Sstevel@tonic-gate  * Map a block.
18440Sstevel@tonic-gate  */
18450Sstevel@tonic-gate /*ARGSUSED*/
18460Sstevel@tonic-gate static int
scsi_repair(bn,flag)18470Sstevel@tonic-gate scsi_repair(bn, flag)
18480Sstevel@tonic-gate 	uint64_t	bn;
18490Sstevel@tonic-gate 	int		flag;
18500Sstevel@tonic-gate {
18510Sstevel@tonic-gate 	struct uscsi_cmd		ucmd;
18520Sstevel@tonic-gate 	union scsi_cdb			cdb;
18530Sstevel@tonic-gate 	struct scsi_reassign_blk	defect_list;
18540Sstevel@tonic-gate 
18550Sstevel@tonic-gate 	/*
18560Sstevel@tonic-gate 	 * Build and execute the uscsi ioctl
18570Sstevel@tonic-gate 	 */
18580Sstevel@tonic-gate 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
18590Sstevel@tonic-gate 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
18600Sstevel@tonic-gate 	(void) memset((char *)&defect_list, 0,
18610Sstevel@tonic-gate 		sizeof (struct scsi_reassign_blk));
18620Sstevel@tonic-gate 	cdb.scc_cmd = SCMD_REASSIGN_BLOCK;
18630Sstevel@tonic-gate 	ucmd.uscsi_cdb = (caddr_t)&cdb;
18640Sstevel@tonic-gate 	ucmd.uscsi_cdblen = CDB_GROUP0;
18650Sstevel@tonic-gate 	ucmd.uscsi_bufaddr = (caddr_t)&defect_list;
18660Sstevel@tonic-gate 	ucmd.uscsi_buflen = sizeof (struct scsi_reassign_blk);
18670Sstevel@tonic-gate 	defect_list.length = sizeof (defect_list.defect);
18680Sstevel@tonic-gate 	defect_list.length = BE_16(defect_list.length);
18690Sstevel@tonic-gate 	defect_list.defect = bn;
18700Sstevel@tonic-gate 	defect_list.defect = BE_32(defect_list.defect);
18710Sstevel@tonic-gate 	return (uscsi_cmd(cur_file, &ucmd,
18720Sstevel@tonic-gate 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT));
18730Sstevel@tonic-gate }
18740Sstevel@tonic-gate 
18750Sstevel@tonic-gate /*
18760Sstevel@tonic-gate  * Convert a SCSI-style defect list to our generic format.
18770Sstevel@tonic-gate  * We can handle different format lists.
18780Sstevel@tonic-gate  */
18790Sstevel@tonic-gate static void
scsi_convert_list_to_new(list,def_list,list_format)18800Sstevel@tonic-gate scsi_convert_list_to_new(list, def_list, list_format)
18810Sstevel@tonic-gate 	struct defect_list		*list;
18820Sstevel@tonic-gate 	struct scsi_defect_list		*def_list;
18830Sstevel@tonic-gate 	int				 list_format;
18840Sstevel@tonic-gate {
18850Sstevel@tonic-gate 	register struct scsi_bfi_defect	*old_defect, *old_defect1;
18860Sstevel@tonic-gate 	register struct defect_entry	*new_defect;
18870Sstevel@tonic-gate 	register int			len, new_len, obfi, nbfi;
18880Sstevel@tonic-gate 	register int			i;
18890Sstevel@tonic-gate 	int				old_cyl, new_cyl;
18900Sstevel@tonic-gate 	unsigned char			*cp;
18910Sstevel@tonic-gate 
18920Sstevel@tonic-gate 
18930Sstevel@tonic-gate 	switch (list_format) {
18940Sstevel@tonic-gate 
18950Sstevel@tonic-gate 	case DLD_BFI_FORMAT:
18960Sstevel@tonic-gate 		/*
18970Sstevel@tonic-gate 		 * Allocate space for the rest of the list.
18980Sstevel@tonic-gate 		 */
18990Sstevel@tonic-gate 		len = def_list->length / sizeof (struct scsi_bfi_defect);
19000Sstevel@tonic-gate 		old_defect = def_list->list;
19010Sstevel@tonic-gate 		new_defect = (struct defect_entry *)
19029889SLarry.Liu@Sun.COM 		    zalloc(deflist_size(cur_blksz, len) *
19039889SLarry.Liu@Sun.COM 		    cur_blksz);
19040Sstevel@tonic-gate 
19050Sstevel@tonic-gate 		list->header.magicno = (uint_t)DEFECT_MAGIC;
19060Sstevel@tonic-gate 		list->list = new_defect;
19070Sstevel@tonic-gate 
19080Sstevel@tonic-gate 		for (i = 0, new_len = 0; i < len; new_defect++, new_len++) {
19090Sstevel@tonic-gate 			cp = (unsigned char *)old_defect;
19100Sstevel@tonic-gate 			new_defect->cyl = (cp[0] << 16 | cp[1] << 8) | cp[2];
19110Sstevel@tonic-gate 			new_defect->head = old_defect->head;
19120Sstevel@tonic-gate 			new_defect->bfi = (int)old_defect->bytes_from_index;
19130Sstevel@tonic-gate 			new_defect->bfi = BE_32(new_defect->bfi);
19140Sstevel@tonic-gate 			new_defect->nbits = 0;	/* size of defect */
19150Sstevel@tonic-gate 			old_defect1 = old_defect++;
19160Sstevel@tonic-gate 			i++;
19170Sstevel@tonic-gate 			/*
19180Sstevel@tonic-gate 			 * Since we reached the end of the list, old_defect
19190Sstevel@tonic-gate 			 * now points to an invalid reference, since it got
19200Sstevel@tonic-gate 			 * incremented in the above operation. So we don't
19210Sstevel@tonic-gate 			 * need to proceed further. new_len needs to be
19220Sstevel@tonic-gate 			 * incremented to account for the last element.
19230Sstevel@tonic-gate 			 */
19240Sstevel@tonic-gate 			if (i == len) {
19250Sstevel@tonic-gate 				new_len++;
19260Sstevel@tonic-gate 				break;
19270Sstevel@tonic-gate 			}
19280Sstevel@tonic-gate 			obfi = new_defect->bfi;
19290Sstevel@tonic-gate 			nbfi = (int)old_defect->bytes_from_index;
19300Sstevel@tonic-gate 			nbfi = BE_32(nbfi);
19310Sstevel@tonic-gate 
19320Sstevel@tonic-gate 			old_cyl =  new_defect->cyl;
19330Sstevel@tonic-gate 			cp = (unsigned char *)old_defect;
19340Sstevel@tonic-gate 			new_cyl = (cp[0] << 16 | cp[1] << 8) | cp[2];
19350Sstevel@tonic-gate 
19360Sstevel@tonic-gate 
19370Sstevel@tonic-gate 			/*
19380Sstevel@tonic-gate 			 * Merge adjacent contiguous defect entries into one
19390Sstevel@tonic-gate 			 * and update the length of the defect
19400Sstevel@tonic-gate 			 */
19410Sstevel@tonic-gate 			while ((i < len) &&
19420Sstevel@tonic-gate 				(old_cyl  == new_cyl) &&
19430Sstevel@tonic-gate 				(old_defect->head == old_defect1->head) &&
19440Sstevel@tonic-gate 				(nbfi == (obfi + BITSPERBYTE))) {
19450Sstevel@tonic-gate 				old_defect1 = old_defect++;
19460Sstevel@tonic-gate 				cp = (unsigned char *)old_defect;
19470Sstevel@tonic-gate 				new_cyl = (cp[0] << 16 | cp[1] << 8) | cp[2];
19480Sstevel@tonic-gate 				obfi = (int)old_defect1->bytes_from_index;
19490Sstevel@tonic-gate 				obfi = BE_32(obfi);
19500Sstevel@tonic-gate 				nbfi = (int)old_defect->bytes_from_index;
19510Sstevel@tonic-gate 				nbfi = BE_32(nbfi);
19520Sstevel@tonic-gate 				new_defect->nbits += (8*BITSPERBYTE);
19530Sstevel@tonic-gate 				i++;
19540Sstevel@tonic-gate 			}
19550Sstevel@tonic-gate 		}
19560Sstevel@tonic-gate 
19570Sstevel@tonic-gate 		list->header.count = new_len;
19580Sstevel@tonic-gate 		break;
19590Sstevel@tonic-gate 
19600Sstevel@tonic-gate 	default:
19610Sstevel@tonic-gate 		err_print("scsi_convert_list_to_new: can't deal with it\n");
19620Sstevel@tonic-gate 		exit(0);
19630Sstevel@tonic-gate 		/*NOTREACHED*/
19640Sstevel@tonic-gate 	}
19650Sstevel@tonic-gate 
19660Sstevel@tonic-gate 	(void) checkdefsum(list, CK_MAKESUM);
19670Sstevel@tonic-gate }
19680Sstevel@tonic-gate 
19690Sstevel@tonic-gate 
19700Sstevel@tonic-gate 
19710Sstevel@tonic-gate /*
19720Sstevel@tonic-gate  * Execute a command and determine the result.
19730Sstevel@tonic-gate  * Uses the "uscsi" ioctl interface, which is
19740Sstevel@tonic-gate  * fully supported.
19750Sstevel@tonic-gate  *
19760Sstevel@tonic-gate  * If the user wants request sense data to be returned
19770Sstevel@tonic-gate  * in case of error then , the "uscsi_cmd" structure
19780Sstevel@tonic-gate  * should have the request sense buffer allocated in
19790Sstevel@tonic-gate  * uscsi_rqbuf.
19800Sstevel@tonic-gate  *
19810Sstevel@tonic-gate  */
19820Sstevel@tonic-gate int
uscsi_cmd(fd,ucmd,flags)19830Sstevel@tonic-gate uscsi_cmd(fd, ucmd, flags)
19840Sstevel@tonic-gate 	int			fd;
19850Sstevel@tonic-gate 	struct uscsi_cmd	*ucmd;
19860Sstevel@tonic-gate 	int			flags;
19870Sstevel@tonic-gate {
19880Sstevel@tonic-gate 	struct scsi_extended_sense	*rq;
19890Sstevel@tonic-gate 	char				rqbuf[255];
19900Sstevel@tonic-gate 	int				status;
19910Sstevel@tonic-gate 	int				rqlen;
19920Sstevel@tonic-gate 	int				timeout = 0;
19930Sstevel@tonic-gate 
19940Sstevel@tonic-gate 	/*
19950Sstevel@tonic-gate 	 * Set function flags for driver.
19960Sstevel@tonic-gate 	 */
19970Sstevel@tonic-gate 	ucmd->uscsi_flags = USCSI_ISOLATE;
19980Sstevel@tonic-gate 	if (flags & F_SILENT) {
19990Sstevel@tonic-gate 		ucmd->uscsi_flags |= USCSI_SILENT;
20000Sstevel@tonic-gate 	}
20010Sstevel@tonic-gate 	if (flags & F_RQENABLE) {
20020Sstevel@tonic-gate 		ucmd->uscsi_flags |= USCSI_RQENABLE;
20030Sstevel@tonic-gate 	}
20040Sstevel@tonic-gate 
20050Sstevel@tonic-gate 	/*
20060Sstevel@tonic-gate 	 * If this command will perform a read, set the USCSI_READ flag
20070Sstevel@tonic-gate 	 */
20080Sstevel@tonic-gate 	if (ucmd->uscsi_buflen > 0) {
20090Sstevel@tonic-gate 		/*
20100Sstevel@tonic-gate 		 * uscsi_cdb is declared as a caddr_t, so any CDB
20110Sstevel@tonic-gate 		 * command byte with the MSB set will result in a
20120Sstevel@tonic-gate 		 * compiler error unless we cast to an unsigned value.
20130Sstevel@tonic-gate 		 */
20140Sstevel@tonic-gate 		switch ((uint8_t)ucmd->uscsi_cdb[0]) {
20150Sstevel@tonic-gate 		case SCMD_READ:
20160Sstevel@tonic-gate 		case SCMD_READ|SCMD_GROUP1:
20170Sstevel@tonic-gate 		case SCMD_READ|SCMD_GROUP4:
20180Sstevel@tonic-gate 		case SCMD_MODE_SENSE:
20190Sstevel@tonic-gate 		case SCMD_INQUIRY:
20200Sstevel@tonic-gate 		case SCMD_READ_DEFECT_LIST:
20210Sstevel@tonic-gate 		case SCMD_READ_CAPACITY:
20220Sstevel@tonic-gate 		case SCMD_SVC_ACTION_IN_G4:
20230Sstevel@tonic-gate 			ucmd->uscsi_flags |= USCSI_READ;
20240Sstevel@tonic-gate 			break;
20250Sstevel@tonic-gate 		}
20260Sstevel@tonic-gate 	}
20270Sstevel@tonic-gate 
20280Sstevel@tonic-gate 	/*
20290Sstevel@tonic-gate 	 * Set timeout: 30 seconds for all commands except format
20300Sstevel@tonic-gate 	 */
20310Sstevel@tonic-gate 	switch (ucmd->uscsi_cdb[0]) {
20320Sstevel@tonic-gate 	case SCMD_FORMAT:
20330Sstevel@tonic-gate 		if (ucmd->uscsi_timeout == 0) {
20340Sstevel@tonic-gate 			ucmd->uscsi_timeout = scsi_format_timeout;
20350Sstevel@tonic-gate 			/*
20360Sstevel@tonic-gate 			 * Get the timeout value computed using page4 geometry.
20370Sstevel@tonic-gate 			 * add 50% margin to cover defect management overhead.
20380Sstevel@tonic-gate 			 * add another 50% margin to have a safe timeout.
20390Sstevel@tonic-gate 			 * If it exceeds 2 hours then use this value.
20400Sstevel@tonic-gate 			 */
20410Sstevel@tonic-gate 			if ((timeout = scsi_format_time()) > 0) {
20420Sstevel@tonic-gate 				timeout *= 60;	/* convert to seconds */
20430Sstevel@tonic-gate 				timeout += timeout;
20440Sstevel@tonic-gate 				/*
20450Sstevel@tonic-gate 				 * formatting drives with huge capacity
20460Sstevel@tonic-gate 				 * will cause these heuristics to come
20470Sstevel@tonic-gate 				 * up with times that overflow ~9 hours
20480Sstevel@tonic-gate 				 */
20490Sstevel@tonic-gate 				if (timeout > SHRT_MAX)
20500Sstevel@tonic-gate 					timeout = SHRT_MAX;
20510Sstevel@tonic-gate 				if (timeout > scsi_format_timeout)
20520Sstevel@tonic-gate 					ucmd->uscsi_timeout = timeout;
20530Sstevel@tonic-gate 			}
20540Sstevel@tonic-gate 		}
20550Sstevel@tonic-gate 		if (option_msg && diag_msg) {
20560Sstevel@tonic-gate 			err_print("format_timeout set to %d seconds, %d"
20570Sstevel@tonic-gate 				" required\n", ucmd->uscsi_timeout, timeout);
20580Sstevel@tonic-gate 		}
20590Sstevel@tonic-gate 		break;
20600Sstevel@tonic-gate 
20610Sstevel@tonic-gate 	default:
20620Sstevel@tonic-gate 		ucmd->uscsi_timeout = 30;		/* 30 seconds */
20630Sstevel@tonic-gate 		break;
20640Sstevel@tonic-gate 	}
20650Sstevel@tonic-gate 
20660Sstevel@tonic-gate 	/*
20670Sstevel@tonic-gate 	 * Set up Request Sense buffer
20680Sstevel@tonic-gate 	 */
20690Sstevel@tonic-gate 	ucmd->uscsi_flags |= USCSI_RQENABLE;
20700Sstevel@tonic-gate 
20710Sstevel@tonic-gate 	if (ucmd->uscsi_rqbuf == NULL)  {
20720Sstevel@tonic-gate 		ucmd->uscsi_rqbuf = rqbuf;
20730Sstevel@tonic-gate 		ucmd->uscsi_rqlen = sizeof (rqbuf);
20740Sstevel@tonic-gate 		ucmd->uscsi_rqresid = sizeof (rqbuf);
20750Sstevel@tonic-gate 	}
20760Sstevel@tonic-gate 	ucmd->uscsi_rqstatus = IMPOSSIBLE_SCSI_STATUS;
20770Sstevel@tonic-gate 
20780Sstevel@tonic-gate 	/*
20790Sstevel@tonic-gate 	 * Clear global error state
20800Sstevel@tonic-gate 	 */
20810Sstevel@tonic-gate 	media_error = 0;
20820Sstevel@tonic-gate 
20830Sstevel@tonic-gate 	/*
20840Sstevel@tonic-gate 	 * Execute the ioctl
20850Sstevel@tonic-gate 	 */
20860Sstevel@tonic-gate 	status = ioctl(fd, USCSICMD, ucmd);
20870Sstevel@tonic-gate 	if (status == 0 && ucmd->uscsi_status == 0) {
20880Sstevel@tonic-gate 		return (status);
20890Sstevel@tonic-gate 	}
20900Sstevel@tonic-gate 
20910Sstevel@tonic-gate 	/*
20920Sstevel@tonic-gate 	 * Check the status and return appropriate errors if the disk is
20930Sstevel@tonic-gate 	 * unavailable (could be formatting) or reserved (by other host).
20940Sstevel@tonic-gate 	 * In either case we can not talk to the disk now.
20950Sstevel@tonic-gate 	 */
20960Sstevel@tonic-gate 	if (status == -1 && errno == EAGAIN) {
20970Sstevel@tonic-gate 		disk_error = DISK_STAT_UNAVAILABLE;
20980Sstevel@tonic-gate 		return (DSK_UNAVAILABLE);
20990Sstevel@tonic-gate 	}
21000Sstevel@tonic-gate 	if ((ucmd->uscsi_status & STATUS_MASK) == STATUS_RESERVATION_CONFLICT) {
21010Sstevel@tonic-gate 		disk_error = DISK_STAT_RESERVED;
21020Sstevel@tonic-gate 		return (DSK_RESERVED);
21030Sstevel@tonic-gate 	}
21040Sstevel@tonic-gate 	/*
21050Sstevel@tonic-gate 	 * Check for physically removed or completely unresponsive drive
21060Sstevel@tonic-gate 	 */
21070Sstevel@tonic-gate 	if (status == -1 && !ucmd->uscsi_status && errno == EIO) {
21080Sstevel@tonic-gate 		disk_error = DISK_STAT_UNAVAILABLE;
21090Sstevel@tonic-gate 		return (DSK_UNAVAILABLE);
21100Sstevel@tonic-gate 	}
21110Sstevel@tonic-gate 
21120Sstevel@tonic-gate 	/*
21130Sstevel@tonic-gate 	 * If an automatic Request Sense gave us valid
21140Sstevel@tonic-gate 	 * info about the error, we may be able to use
21150Sstevel@tonic-gate 	 * that to print a reasonable error msg.
21160Sstevel@tonic-gate 	 */
21170Sstevel@tonic-gate 	if (ucmd->uscsi_rqstatus == IMPOSSIBLE_SCSI_STATUS) {
21180Sstevel@tonic-gate 		if (option_msg && diag_msg) {
21190Sstevel@tonic-gate 			err_print("No request sense for command %s\n",
21200Sstevel@tonic-gate 				scsi_find_command_name(ucmd->uscsi_cdb[0]));
21210Sstevel@tonic-gate 		}
21220Sstevel@tonic-gate 		return (-1);
21230Sstevel@tonic-gate 	}
21240Sstevel@tonic-gate 	if (ucmd->uscsi_rqstatus != STATUS_GOOD) {
21250Sstevel@tonic-gate 		if (option_msg && diag_msg) {
21260Sstevel@tonic-gate 			err_print("Request sense status for command %s: 0x%x\n",
21270Sstevel@tonic-gate 				scsi_find_command_name(ucmd->uscsi_cdb[0]),
21280Sstevel@tonic-gate 				ucmd->uscsi_rqstatus);
21290Sstevel@tonic-gate 		}
21300Sstevel@tonic-gate 		return (-1);
21310Sstevel@tonic-gate 	}
21320Sstevel@tonic-gate 	rq = (struct scsi_extended_sense *)ucmd->uscsi_rqbuf;
21330Sstevel@tonic-gate 	rqlen = ucmd->uscsi_rqlen - ucmd->uscsi_rqresid;
21340Sstevel@tonic-gate 	if ((((int)rq->es_add_len) + 8) < MIN_REQUEST_SENSE_LEN ||
21350Sstevel@tonic-gate 			rq->es_class != CLASS_EXTENDED_SENSE ||
21360Sstevel@tonic-gate 				rqlen < MIN_REQUEST_SENSE_LEN) {
21370Sstevel@tonic-gate 		if (option_msg) {
21380Sstevel@tonic-gate 			err_print("Request sense for command %s failed\n",
21390Sstevel@tonic-gate 				scsi_find_command_name(ucmd->uscsi_cdb[0]));
21400Sstevel@tonic-gate 		}
21410Sstevel@tonic-gate 		if (option_msg && diag_msg) {
21420Sstevel@tonic-gate 			err_print("Sense data:\n");
21430Sstevel@tonic-gate 			dump("", (caddr_t)rqbuf, rqlen, HEX_ONLY);
21440Sstevel@tonic-gate 		}
21456765Sbz211116 		if (errno == EIO) {
21466765Sbz211116 			disk_error = DISK_STAT_UNAVAILABLE;
21476765Sbz211116 			return (DSK_UNAVAILABLE);
21486765Sbz211116 		} else {
21496765Sbz211116 			return (-1);
21506765Sbz211116 		}
21510Sstevel@tonic-gate 	}
21520Sstevel@tonic-gate 
21530Sstevel@tonic-gate 	/*
21540Sstevel@tonic-gate 	 * If the failed command is a Mode Select, and the
21550Sstevel@tonic-gate 	 * target is indicating that it has rounded one of
21560Sstevel@tonic-gate 	 * the mode select parameters, as defined in the SCSI-2
21570Sstevel@tonic-gate 	 * specification, then we should accept the command
21580Sstevel@tonic-gate 	 * as successful.
21590Sstevel@tonic-gate 	 */
21600Sstevel@tonic-gate 	if (ucmd->uscsi_cdb[0] == SCMD_MODE_SELECT) {
21610Sstevel@tonic-gate 		if (rq->es_key == KEY_RECOVERABLE_ERROR &&
21620Sstevel@tonic-gate 			rq->es_add_code == ROUNDED_PARAMETER &&
21630Sstevel@tonic-gate 			rq->es_qual_code == 0) {
21640Sstevel@tonic-gate 				return (0);
21650Sstevel@tonic-gate 		}
21660Sstevel@tonic-gate 	}
21670Sstevel@tonic-gate 
21680Sstevel@tonic-gate 	switch (rq->es_key) {
21690Sstevel@tonic-gate 	case KEY_NOT_READY:
21700Sstevel@tonic-gate 		disk_error = DISK_STAT_NOTREADY;
21710Sstevel@tonic-gate 		break;
21720Sstevel@tonic-gate 	case KEY_DATA_PROTECT:
21730Sstevel@tonic-gate 		disk_error = DISK_STAT_DATA_PROTECT;
21740Sstevel@tonic-gate 		break;
21750Sstevel@tonic-gate 	}
21760Sstevel@tonic-gate 
21770Sstevel@tonic-gate 	if (flags & F_ALLERRS) {
21780Sstevel@tonic-gate 		media_error = (rq->es_key == KEY_MEDIUM_ERROR);
21790Sstevel@tonic-gate 	}
21800Sstevel@tonic-gate 	if (!(flags & F_SILENT) || option_msg) {
21810Sstevel@tonic-gate 		scsi_printerr(ucmd, rq, rqlen);
21820Sstevel@tonic-gate 	}
21830Sstevel@tonic-gate 	if ((rq->es_key != KEY_RECOVERABLE_ERROR) || (flags & F_ALLERRS)) {
21840Sstevel@tonic-gate 		return (-1);
21850Sstevel@tonic-gate 	}
21866765Sbz211116 
21876765Sbz211116 	if (status == -1 && errno == EIO) {
21886765Sbz211116 		disk_error = DISK_STAT_UNAVAILABLE;
21896765Sbz211116 		return (DSK_UNAVAILABLE);
21906765Sbz211116 	}
21916765Sbz211116 
21920Sstevel@tonic-gate 	return (0);
21930Sstevel@tonic-gate }
21940Sstevel@tonic-gate 
21950Sstevel@tonic-gate 
21960Sstevel@tonic-gate /*
21970Sstevel@tonic-gate  * Execute a uscsi mode sense command.
21980Sstevel@tonic-gate  * This can only be used to return one page at a time.
21990Sstevel@tonic-gate  * Return the mode header/block descriptor and the actual
22000Sstevel@tonic-gate  * page data separately - this allows us to support
22010Sstevel@tonic-gate  * devices which return either 0 or 1 block descriptors.
22020Sstevel@tonic-gate  * Whatever a device gives us in the mode header/block descriptor
22030Sstevel@tonic-gate  * will be returned to it upon subsequent mode selects.
22040Sstevel@tonic-gate  */
22050Sstevel@tonic-gate int
uscsi_mode_sense(fd,page_code,page_control,page_data,page_size,header)22060Sstevel@tonic-gate uscsi_mode_sense(fd, page_code, page_control, page_data, page_size, header)
22070Sstevel@tonic-gate 	int	fd;			/* file descriptor */
22080Sstevel@tonic-gate 	int	page_code;		/* requested page number */
22090Sstevel@tonic-gate 	int	page_control;		/* current, changeable, etc. */
22100Sstevel@tonic-gate 	caddr_t	page_data;		/* place received data here */
22110Sstevel@tonic-gate 	int	page_size;		/* size of page_data */
22120Sstevel@tonic-gate 	struct	scsi_ms_header *header;	/* mode header/block descriptor */
22130Sstevel@tonic-gate {
22140Sstevel@tonic-gate 	caddr_t			mode_sense_buf;
22150Sstevel@tonic-gate 	struct mode_header	*hdr;
22160Sstevel@tonic-gate 	struct mode_page	*pg;
22170Sstevel@tonic-gate 	int			nbytes;
22180Sstevel@tonic-gate 	struct uscsi_cmd	ucmd;
22190Sstevel@tonic-gate 	union scsi_cdb		cdb;
22200Sstevel@tonic-gate 	int			status;
22210Sstevel@tonic-gate 	int			maximum;
22220Sstevel@tonic-gate 
22230Sstevel@tonic-gate 	assert(page_size >= 0 && page_size < 256);
22240Sstevel@tonic-gate 	assert(page_control == MODE_SENSE_PC_CURRENT ||
22250Sstevel@tonic-gate 		page_control == MODE_SENSE_PC_CHANGEABLE ||
22260Sstevel@tonic-gate 			page_control == MODE_SENSE_PC_DEFAULT ||
22270Sstevel@tonic-gate 				page_control == MODE_SENSE_PC_SAVED);
22280Sstevel@tonic-gate 	/*
22290Sstevel@tonic-gate 	 * Allocate a buffer for the mode sense headers
22300Sstevel@tonic-gate 	 * and mode sense data itself.
22310Sstevel@tonic-gate 	 */
22320Sstevel@tonic-gate 	nbytes = sizeof (struct block_descriptor) +
22330Sstevel@tonic-gate 				sizeof (struct mode_header) + page_size;
22340Sstevel@tonic-gate 	nbytes = page_size;
22350Sstevel@tonic-gate 	if ((mode_sense_buf = malloc((uint_t)nbytes)) == NULL) {
22360Sstevel@tonic-gate 		err_print("cannot malloc %d bytes\n", nbytes);
22370Sstevel@tonic-gate 		return (-1);
22380Sstevel@tonic-gate 	}
22390Sstevel@tonic-gate 
22400Sstevel@tonic-gate 	/*
22410Sstevel@tonic-gate 	 * Build and execute the uscsi ioctl
22420Sstevel@tonic-gate 	 */
22430Sstevel@tonic-gate 	(void) memset(mode_sense_buf, 0, nbytes);
22440Sstevel@tonic-gate 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
22450Sstevel@tonic-gate 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
22460Sstevel@tonic-gate 	cdb.scc_cmd = SCMD_MODE_SENSE;
22470Sstevel@tonic-gate 	FORMG0COUNT(&cdb, (uchar_t)nbytes);
22480Sstevel@tonic-gate 	cdb.cdb_opaque[2] = page_control | page_code;
22490Sstevel@tonic-gate 	ucmd.uscsi_cdb = (caddr_t)&cdb;
22500Sstevel@tonic-gate 	ucmd.uscsi_cdblen = CDB_GROUP0;
22510Sstevel@tonic-gate 	ucmd.uscsi_bufaddr = mode_sense_buf;
22520Sstevel@tonic-gate 	ucmd.uscsi_buflen = nbytes;
22530Sstevel@tonic-gate 	status = uscsi_cmd(fd, &ucmd,
22540Sstevel@tonic-gate 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
22550Sstevel@tonic-gate 	if (status) {
22560Sstevel@tonic-gate 		if (option_msg) {
22570Sstevel@tonic-gate 			err_print("Mode sense page 0x%x failed\n",
22580Sstevel@tonic-gate 				page_code);
22590Sstevel@tonic-gate 		}
22600Sstevel@tonic-gate 		free(mode_sense_buf);
22610Sstevel@tonic-gate 		return (-1);
22620Sstevel@tonic-gate 	}
22630Sstevel@tonic-gate 
22640Sstevel@tonic-gate 	/*
22650Sstevel@tonic-gate 	 * Verify that the returned data looks reasonabled,
22660Sstevel@tonic-gate 	 * find the actual page data, and copy it into the
22670Sstevel@tonic-gate 	 * user's buffer.  Copy the mode_header and block_descriptor
22680Sstevel@tonic-gate 	 * into the header structure, which can then be used to
22690Sstevel@tonic-gate 	 * return the same data to the drive when issuing a mode select.
22700Sstevel@tonic-gate 	 */
22710Sstevel@tonic-gate 	hdr = (struct mode_header *)mode_sense_buf;
22720Sstevel@tonic-gate 	(void) memset((caddr_t)header, 0, sizeof (struct scsi_ms_header));
22730Sstevel@tonic-gate 	if (hdr->bdesc_length != sizeof (struct block_descriptor) &&
22740Sstevel@tonic-gate 				hdr->bdesc_length != 0) {
22750Sstevel@tonic-gate 		if (option_msg) {
22760Sstevel@tonic-gate 			err_print("\
22770Sstevel@tonic-gate \nMode sense page 0x%x: block descriptor length %d incorrect\n",
22780Sstevel@tonic-gate 				page_code, hdr->bdesc_length);
22790Sstevel@tonic-gate 			if (diag_msg)
22800Sstevel@tonic-gate 				dump("Mode sense: ", mode_sense_buf,
22810Sstevel@tonic-gate 					nbytes, HEX_ONLY);
22820Sstevel@tonic-gate 		}
22830Sstevel@tonic-gate 		free(mode_sense_buf);
22840Sstevel@tonic-gate 		return (-1);
22850Sstevel@tonic-gate 	}
22860Sstevel@tonic-gate 	(void) memcpy((caddr_t)header, mode_sense_buf,
22870Sstevel@tonic-gate 		(int) (sizeof (struct mode_header) + hdr->bdesc_length));
22880Sstevel@tonic-gate 	pg = (struct mode_page *)((ulong_t)mode_sense_buf +
22890Sstevel@tonic-gate 		sizeof (struct mode_header) + hdr->bdesc_length);
22900Sstevel@tonic-gate 	if (pg->code != page_code) {
22910Sstevel@tonic-gate 		if (option_msg) {
22920Sstevel@tonic-gate 			err_print("\
22930Sstevel@tonic-gate \nMode sense page 0x%x: incorrect page code 0x%x\n",
22940Sstevel@tonic-gate 				page_code, pg->code);
22950Sstevel@tonic-gate 			if (diag_msg)
22960Sstevel@tonic-gate 				dump("Mode sense: ", mode_sense_buf,
22970Sstevel@tonic-gate 					nbytes, HEX_ONLY);
22980Sstevel@tonic-gate 		}
22990Sstevel@tonic-gate 		free(mode_sense_buf);
23000Sstevel@tonic-gate 		return (-1);
23010Sstevel@tonic-gate 	}
23020Sstevel@tonic-gate 	/*
23030Sstevel@tonic-gate 	 * Accept up to "page_size" bytes of mode sense data.
23040Sstevel@tonic-gate 	 * This allows us to accept both CCS and SCSI-2
23050Sstevel@tonic-gate 	 * structures, as long as we request the greater
23060Sstevel@tonic-gate 	 * of the two.
23070Sstevel@tonic-gate 	 */
23080Sstevel@tonic-gate 	maximum = page_size - sizeof (struct mode_page) - hdr->bdesc_length;
23090Sstevel@tonic-gate 	if (((int)pg->length) > maximum) {
23100Sstevel@tonic-gate 		if (option_msg) {
23110Sstevel@tonic-gate 			err_print("\
23120Sstevel@tonic-gate Mode sense page 0x%x: incorrect page length %d - expected max %d\n",
23130Sstevel@tonic-gate 				page_code, pg->length, maximum);
23140Sstevel@tonic-gate 			if (diag_msg)
23150Sstevel@tonic-gate 				dump("Mode sense: ", mode_sense_buf,
23160Sstevel@tonic-gate 					nbytes, HEX_ONLY);
23170Sstevel@tonic-gate 		}
23180Sstevel@tonic-gate 		free(mode_sense_buf);
23190Sstevel@tonic-gate 		return (-1);
23200Sstevel@tonic-gate 	}
23210Sstevel@tonic-gate 
23220Sstevel@tonic-gate 	(void) memcpy(page_data, (caddr_t)pg, MODESENSE_PAGE_LEN(pg));
23230Sstevel@tonic-gate 
23240Sstevel@tonic-gate 	if (option_msg && diag_msg) {
23250Sstevel@tonic-gate 		char *pc = find_string(page_control_strings, page_control);
23260Sstevel@tonic-gate 		err_print("\nMode sense page 0x%x (%s):\n", page_code,
23270Sstevel@tonic-gate 			pc != NULL ? pc : "");
23280Sstevel@tonic-gate 		dump("header: ", (caddr_t)header,
23290Sstevel@tonic-gate 			sizeof (struct scsi_ms_header), HEX_ONLY);
23300Sstevel@tonic-gate 		dump("data:   ", page_data,
23310Sstevel@tonic-gate 			MODESENSE_PAGE_LEN(pg), HEX_ONLY);
23320Sstevel@tonic-gate 	}
23330Sstevel@tonic-gate 
23340Sstevel@tonic-gate 	free(mode_sense_buf);
23350Sstevel@tonic-gate 	return (0);
23360Sstevel@tonic-gate }
23370Sstevel@tonic-gate 
23380Sstevel@tonic-gate 
23390Sstevel@tonic-gate /*
23400Sstevel@tonic-gate  * Execute a uscsi mode select command.
23410Sstevel@tonic-gate  */
23420Sstevel@tonic-gate int
uscsi_mode_select(fd,page_code,options,page_data,page_size,header)23430Sstevel@tonic-gate uscsi_mode_select(fd, page_code, options, page_data, page_size, header)
23440Sstevel@tonic-gate 	int	fd;			/* file descriptor */
23450Sstevel@tonic-gate 	int	page_code;		/* mode select page */
23460Sstevel@tonic-gate 	int	options;		/* save page/page format */
23470Sstevel@tonic-gate 	caddr_t	page_data;		/* place received data here */
23480Sstevel@tonic-gate 	int	page_size;		/* size of page_data */
23490Sstevel@tonic-gate 	struct	scsi_ms_header *header;	/* mode header/block descriptor */
23500Sstevel@tonic-gate {
23510Sstevel@tonic-gate 	caddr_t				mode_select_buf;
23520Sstevel@tonic-gate 	int				nbytes;
23530Sstevel@tonic-gate 	struct uscsi_cmd		ucmd;
23540Sstevel@tonic-gate 	union scsi_cdb			cdb;
23550Sstevel@tonic-gate 	int				status;
23560Sstevel@tonic-gate 
23570Sstevel@tonic-gate 	assert(((struct mode_page *)page_data)->ps == 0);
23580Sstevel@tonic-gate 	assert(header->mode_header.length == 0);
23590Sstevel@tonic-gate 	assert(header->mode_header.device_specific == 0);
23600Sstevel@tonic-gate 	assert((options & ~(MODE_SELECT_SP|MODE_SELECT_PF)) == 0);
23610Sstevel@tonic-gate 
23620Sstevel@tonic-gate 	/*
23630Sstevel@tonic-gate 	 * Allocate a buffer for the mode select header and data
23640Sstevel@tonic-gate 	 */
23650Sstevel@tonic-gate 	nbytes = sizeof (struct block_descriptor) +
23660Sstevel@tonic-gate 				sizeof (struct mode_header) + page_size;
23670Sstevel@tonic-gate 	if ((mode_select_buf = malloc((uint_t)nbytes)) == NULL) {
23680Sstevel@tonic-gate 		err_print("cannot malloc %d bytes\n", nbytes);
23690Sstevel@tonic-gate 		return (-1);
23700Sstevel@tonic-gate 	}
23710Sstevel@tonic-gate 
23720Sstevel@tonic-gate 	/*
23730Sstevel@tonic-gate 	 * Build the mode select data out of the header and page data
23740Sstevel@tonic-gate 	 * This allows us to support devices which return either
23750Sstevel@tonic-gate 	 * 0 or 1 block descriptors.
23760Sstevel@tonic-gate 	 */
23770Sstevel@tonic-gate 	(void) memset(mode_select_buf, 0, nbytes);
23780Sstevel@tonic-gate 	nbytes = sizeof (struct mode_header);
23790Sstevel@tonic-gate 	if (header->mode_header.bdesc_length ==
23800Sstevel@tonic-gate 				sizeof (struct block_descriptor)) {
23810Sstevel@tonic-gate 		nbytes += sizeof (struct block_descriptor);
23820Sstevel@tonic-gate 	}
23830Sstevel@tonic-gate 
23840Sstevel@tonic-gate 	/*
23850Sstevel@tonic-gate 	 * Dump the structures if anyone's interested
23860Sstevel@tonic-gate 	 */
23870Sstevel@tonic-gate 	if (option_msg && diag_msg) {
23880Sstevel@tonic-gate 		char *s;
23890Sstevel@tonic-gate 		s = find_string(mode_select_strings,
23900Sstevel@tonic-gate 			options & (MODE_SELECT_SP|MODE_SELECT_PF));
23910Sstevel@tonic-gate 		err_print("\nMode select page 0x%x%s:\n", page_code,
23920Sstevel@tonic-gate 			s != NULL ? s : "");
23930Sstevel@tonic-gate 		dump("header: ", (caddr_t)header,
23940Sstevel@tonic-gate 			nbytes, HEX_ONLY);
23950Sstevel@tonic-gate 		dump("data:   ", (caddr_t)page_data,
23960Sstevel@tonic-gate 			page_size, HEX_ONLY);
23970Sstevel@tonic-gate 	}
23980Sstevel@tonic-gate 
23990Sstevel@tonic-gate 	/*
24000Sstevel@tonic-gate 	 * Fix the code for byte ordering
24010Sstevel@tonic-gate 	 */
24020Sstevel@tonic-gate 
24030Sstevel@tonic-gate 	switch (page_code) {
24040Sstevel@tonic-gate 	case  DAD_MODE_ERR_RECOV:
24050Sstevel@tonic-gate 		{
24060Sstevel@tonic-gate 		struct mode_err_recov *pd;
24070Sstevel@tonic-gate 		pd = (struct mode_err_recov *)(void *)page_data;
24080Sstevel@tonic-gate 		pd->recovery_time_limit = BE_16(pd->recovery_time_limit);
24090Sstevel@tonic-gate 		break;
24100Sstevel@tonic-gate 		}
24110Sstevel@tonic-gate 	case MODEPAGE_DISCO_RECO:
24120Sstevel@tonic-gate 		{
24130Sstevel@tonic-gate 		struct mode_disco_reco *pd;
24140Sstevel@tonic-gate 		pd = (struct mode_disco_reco *)(void *)page_data;
24150Sstevel@tonic-gate 		pd->bus_inactivity_limit = BE_16(pd->bus_inactivity_limit);
24160Sstevel@tonic-gate 		pd->disconect_time_limit = BE_16(pd->disconect_time_limit);
24170Sstevel@tonic-gate 		pd->connect_time_limit = BE_16(pd->connect_time_limit);
24180Sstevel@tonic-gate 		pd->max_burst_size = BE_16(pd->max_burst_size);
24190Sstevel@tonic-gate 		break;
24200Sstevel@tonic-gate 		}
24210Sstevel@tonic-gate 	case DAD_MODE_FORMAT:
24220Sstevel@tonic-gate 		{
24230Sstevel@tonic-gate 		struct mode_format *pd;
24240Sstevel@tonic-gate 		pd = (struct mode_format *)(void *)page_data;
24250Sstevel@tonic-gate 		pd->tracks_per_zone = BE_16(pd->tracks_per_zone);
24260Sstevel@tonic-gate 		pd->alt_sect_zone = BE_16(pd->alt_sect_zone);
24270Sstevel@tonic-gate 		pd->alt_tracks_zone = BE_16(pd->alt_tracks_zone);
24280Sstevel@tonic-gate 		pd->alt_tracks_vol = BE_16(pd->alt_tracks_vol);
24290Sstevel@tonic-gate 		pd->sect_track = BE_16(pd->sect_track);
24300Sstevel@tonic-gate 		pd->data_bytes_sect = BE_16(pd->data_bytes_sect);
24310Sstevel@tonic-gate 		pd->interleave = BE_16(pd->interleave);
24320Sstevel@tonic-gate 		pd->track_skew = BE_16(pd->track_skew);
24330Sstevel@tonic-gate 		pd->cylinder_skew = BE_16(pd->cylinder_skew);
24340Sstevel@tonic-gate 		break;
24350Sstevel@tonic-gate 		}
24360Sstevel@tonic-gate 	case DAD_MODE_GEOMETRY:
24370Sstevel@tonic-gate 		{
24380Sstevel@tonic-gate 		struct mode_geometry *pd;
24390Sstevel@tonic-gate 		pd = (struct mode_geometry *)(void *)page_data;
24400Sstevel@tonic-gate 		pd->step_rate = BE_16(pd->step_rate);
24410Sstevel@tonic-gate 		pd->rpm = BE_16(pd->rpm);
24420Sstevel@tonic-gate 		break;
24430Sstevel@tonic-gate 		}
24440Sstevel@tonic-gate 	case DAD_MODE_CACHE:
24450Sstevel@tonic-gate 		{
24460Sstevel@tonic-gate 		struct mode_cache *pd;
24470Sstevel@tonic-gate 		pd = (struct mode_cache *)(void *)page_data;
24480Sstevel@tonic-gate 		pd->dis_prefetch_len = BE_16(pd->dis_prefetch_len);
24490Sstevel@tonic-gate 		pd->min_prefetch = BE_16(pd->min_prefetch);
24500Sstevel@tonic-gate 		pd->max_prefetch = BE_16(pd->max_prefetch);
24510Sstevel@tonic-gate 		pd->prefetch_ceiling = BE_16(pd->prefetch_ceiling);
24520Sstevel@tonic-gate 		break;
24530Sstevel@tonic-gate 		}
24540Sstevel@tonic-gate 	case MODEPAGE_PDEVICE:
24550Sstevel@tonic-gate 		{
24560Sstevel@tonic-gate 		struct mode_pdevice *pd;
24570Sstevel@tonic-gate 		pd = (struct mode_pdevice *)(void *)page_data;
24580Sstevel@tonic-gate 		pd->if_ident = BE_16(pd->if_ident);
24590Sstevel@tonic-gate 		break;
24600Sstevel@tonic-gate 		}
24610Sstevel@tonic-gate 	case MODEPAGE_CTRL_MODE:
24620Sstevel@tonic-gate 		{
24630Sstevel@tonic-gate 		struct mode_control *pd;
24640Sstevel@tonic-gate 		pd = (struct mode_control *)(void *)page_data;
24650Sstevel@tonic-gate 		pd->ready_aen_holdoff = BE_16(pd->ready_aen_holdoff);
24660Sstevel@tonic-gate 		break;
24670Sstevel@tonic-gate 		}
24680Sstevel@tonic-gate 	}
24690Sstevel@tonic-gate 
24700Sstevel@tonic-gate 	/*
24710Sstevel@tonic-gate 	 * Put the header and data together
24720Sstevel@tonic-gate 	 */
24730Sstevel@tonic-gate 	(void) memcpy(mode_select_buf, (caddr_t)header, nbytes);
24740Sstevel@tonic-gate 	(void) memcpy(mode_select_buf + nbytes, page_data, page_size);
24750Sstevel@tonic-gate 	nbytes += page_size;
24760Sstevel@tonic-gate 
24770Sstevel@tonic-gate 	/*
24780Sstevel@tonic-gate 	 * Build and execute the uscsi ioctl
24790Sstevel@tonic-gate 	 */
24800Sstevel@tonic-gate 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
24810Sstevel@tonic-gate 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
24820Sstevel@tonic-gate 	cdb.scc_cmd = SCMD_MODE_SELECT;
24830Sstevel@tonic-gate 	FORMG0COUNT(&cdb, (uchar_t)nbytes);
24840Sstevel@tonic-gate 	cdb.cdb_opaque[1] = (uchar_t)options;
24850Sstevel@tonic-gate 	ucmd.uscsi_cdb = (caddr_t)&cdb;
24860Sstevel@tonic-gate 	ucmd.uscsi_cdblen = CDB_GROUP0;
24870Sstevel@tonic-gate 	ucmd.uscsi_bufaddr = mode_select_buf;
24880Sstevel@tonic-gate 	ucmd.uscsi_buflen = nbytes;
24890Sstevel@tonic-gate 	status = uscsi_cmd(fd, &ucmd,
24900Sstevel@tonic-gate 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
24910Sstevel@tonic-gate 
24920Sstevel@tonic-gate 	if (status && option_msg) {
24930Sstevel@tonic-gate 		err_print("Mode select page 0x%x failed\n", page_code);
24940Sstevel@tonic-gate 	}
24950Sstevel@tonic-gate 
24960Sstevel@tonic-gate 	free(mode_select_buf);
24970Sstevel@tonic-gate 	return (status);
24980Sstevel@tonic-gate }
24990Sstevel@tonic-gate 
25000Sstevel@tonic-gate 
25010Sstevel@tonic-gate /*
25020Sstevel@tonic-gate  * Execute a uscsi inquiry command and return the
25030Sstevel@tonic-gate  * resulting data.
25040Sstevel@tonic-gate  */
25050Sstevel@tonic-gate int
uscsi_inquiry(fd,inqbuf,inqbufsiz)25060Sstevel@tonic-gate uscsi_inquiry(fd, inqbuf, inqbufsiz)
25070Sstevel@tonic-gate 	int		fd;
25080Sstevel@tonic-gate 	caddr_t		inqbuf;
25090Sstevel@tonic-gate 	int		inqbufsiz;
25100Sstevel@tonic-gate {
25110Sstevel@tonic-gate 	struct uscsi_cmd	ucmd;
25120Sstevel@tonic-gate 	union scsi_cdb		cdb;
25130Sstevel@tonic-gate 	struct scsi_inquiry	*inq;
25140Sstevel@tonic-gate 	int			n;
25150Sstevel@tonic-gate 	int			status;
25160Sstevel@tonic-gate 
25170Sstevel@tonic-gate 	assert(inqbufsiz >= sizeof (struct scsi_inquiry) &&
25180Sstevel@tonic-gate 		inqbufsiz < 256);
25190Sstevel@tonic-gate 
25200Sstevel@tonic-gate 	/*
25210Sstevel@tonic-gate 	 * Build and execute the uscsi ioctl
25220Sstevel@tonic-gate 	 */
25230Sstevel@tonic-gate 	(void) memset((char *)inqbuf, 0, inqbufsiz);
25240Sstevel@tonic-gate 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
25250Sstevel@tonic-gate 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
25260Sstevel@tonic-gate 	cdb.scc_cmd = SCMD_INQUIRY;
25270Sstevel@tonic-gate 	FORMG0COUNT(&cdb, (uchar_t)inqbufsiz);
25280Sstevel@tonic-gate 	ucmd.uscsi_cdb = (caddr_t)&cdb;
25290Sstevel@tonic-gate 	ucmd.uscsi_cdblen = CDB_GROUP0;
25300Sstevel@tonic-gate 	ucmd.uscsi_bufaddr = (caddr_t)inqbuf;
25310Sstevel@tonic-gate 	ucmd.uscsi_buflen = inqbufsiz;
25320Sstevel@tonic-gate 	status = uscsi_cmd(fd, &ucmd,
25330Sstevel@tonic-gate 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
25340Sstevel@tonic-gate 	if (status) {
25350Sstevel@tonic-gate 		if (option_msg) {
25360Sstevel@tonic-gate 			err_print("Inquiry failed\n");
25370Sstevel@tonic-gate 		}
25380Sstevel@tonic-gate 	} else if (option_msg && diag_msg) {
25390Sstevel@tonic-gate 		/*
25400Sstevel@tonic-gate 		 * Dump the inquiry data if anyone's interested
25410Sstevel@tonic-gate 		 */
25420Sstevel@tonic-gate 		inq = (struct scsi_inquiry *)inqbuf;
25430Sstevel@tonic-gate 		n = (int)inq->inq_len + 4;
25440Sstevel@tonic-gate 		n = min(n, inqbufsiz);
25450Sstevel@tonic-gate 		err_print("Inquiry:\n");
25460Sstevel@tonic-gate 		dump("", (caddr_t)inqbuf, n, HEX_ASCII);
25470Sstevel@tonic-gate 	}
25480Sstevel@tonic-gate 	return (status);
25490Sstevel@tonic-gate }
25500Sstevel@tonic-gate 
255112594SShengliang.Zhang@Sun.COM /*
255212594SShengliang.Zhang@Sun.COM  * Execute a uscsi inquiry command with page code 86h
255312594SShengliang.Zhang@Sun.COM  */
255412594SShengliang.Zhang@Sun.COM int
uscsi_inquiry_page_86h(fd,inqbuf,inqbufsiz)255512594SShengliang.Zhang@Sun.COM uscsi_inquiry_page_86h(fd, inqbuf, inqbufsiz)
255612594SShengliang.Zhang@Sun.COM 	int	fd;
255712594SShengliang.Zhang@Sun.COM 	caddr_t	inqbuf;
255812594SShengliang.Zhang@Sun.COM 	int	inqbufsiz;
255912594SShengliang.Zhang@Sun.COM {
256012594SShengliang.Zhang@Sun.COM 	struct uscsi_cmd	ucmd;
256112594SShengliang.Zhang@Sun.COM 	union scsi_cdb	cdb;
256212594SShengliang.Zhang@Sun.COM 	int	status;
256312594SShengliang.Zhang@Sun.COM 
256412594SShengliang.Zhang@Sun.COM 	assert(inqbuf);
256512594SShengliang.Zhang@Sun.COM 	assert(inqbufsiz >= sizeof (struct scsi_inquiry) &&
256612594SShengliang.Zhang@Sun.COM 	    inqbufsiz < 256);
256712594SShengliang.Zhang@Sun.COM 	/*
256812594SShengliang.Zhang@Sun.COM 	 * Build and execute uscsi ioctl
256912594SShengliang.Zhang@Sun.COM 	 */
257012594SShengliang.Zhang@Sun.COM 	(void) memset((char *)inqbuf, 0, inqbufsiz);
257112594SShengliang.Zhang@Sun.COM 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
257212594SShengliang.Zhang@Sun.COM 	(void) memset((char *)&cdb, 0, sizeof (cdb));
257312594SShengliang.Zhang@Sun.COM 	cdb.scc_cmd = SCMD_INQUIRY;
257412594SShengliang.Zhang@Sun.COM 	FORMG0COUNT(&cdb, (uchar_t)inqbufsiz);
257512594SShengliang.Zhang@Sun.COM 	cdb.cdb_opaque[1] |= 0x01;
257612594SShengliang.Zhang@Sun.COM 	cdb.cdb_opaque[2] = 0x86;
257712594SShengliang.Zhang@Sun.COM 	ucmd.uscsi_cdb = (caddr_t)&cdb;
257812594SShengliang.Zhang@Sun.COM 	ucmd.uscsi_cdblen = CDB_GROUP0;
257912594SShengliang.Zhang@Sun.COM 	ucmd.uscsi_bufaddr = (caddr_t)inqbuf;
258012594SShengliang.Zhang@Sun.COM 	ucmd.uscsi_buflen = inqbufsiz;
258112594SShengliang.Zhang@Sun.COM 
258212594SShengliang.Zhang@Sun.COM 	status = uscsi_cmd(fd, &ucmd,
258312594SShengliang.Zhang@Sun.COM 	    (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
258412594SShengliang.Zhang@Sun.COM 
258512594SShengliang.Zhang@Sun.COM 	if (status) {
258612594SShengliang.Zhang@Sun.COM 		if (option_msg) {
258712594SShengliang.Zhang@Sun.COM 			err_print("Inquriy with page_86h failed\n");
258812594SShengliang.Zhang@Sun.COM 		}
258912594SShengliang.Zhang@Sun.COM 	}
259012594SShengliang.Zhang@Sun.COM 	return (status);
259112594SShengliang.Zhang@Sun.COM }
25920Sstevel@tonic-gate 
25930Sstevel@tonic-gate /*
25940Sstevel@tonic-gate  * Return the Read Capacity information
25950Sstevel@tonic-gate  */
25960Sstevel@tonic-gate int
uscsi_read_capacity_16(fd,capacity)259712594SShengliang.Zhang@Sun.COM uscsi_read_capacity_16(fd, capacity)
259812594SShengliang.Zhang@Sun.COM 	int	fd;
259912594SShengliang.Zhang@Sun.COM 	struct scsi_capacity_16 *capacity;
260012594SShengliang.Zhang@Sun.COM {
260112594SShengliang.Zhang@Sun.COM 	struct uscsi_cmd	ucmd;
260212594SShengliang.Zhang@Sun.COM 	union scsi_cdb	cdb;
260312594SShengliang.Zhang@Sun.COM 	int	status;
260412594SShengliang.Zhang@Sun.COM 
260512594SShengliang.Zhang@Sun.COM 	(void) memset((char *)capacity, 0, sizeof (struct scsi_capacity_16));
260612594SShengliang.Zhang@Sun.COM 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
260712594SShengliang.Zhang@Sun.COM 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
260812594SShengliang.Zhang@Sun.COM 
260912594SShengliang.Zhang@Sun.COM 	ucmd.uscsi_cdb = (caddr_t)&cdb;
261012594SShengliang.Zhang@Sun.COM 	ucmd.uscsi_cdblen = CDB_GROUP4;
261112594SShengliang.Zhang@Sun.COM 	ucmd.uscsi_bufaddr = (caddr_t)capacity;
261212594SShengliang.Zhang@Sun.COM 	ucmd.uscsi_buflen = sizeof (struct scsi_capacity_16);
261312594SShengliang.Zhang@Sun.COM 
261412594SShengliang.Zhang@Sun.COM 	/*
261512594SShengliang.Zhang@Sun.COM 	 * Read Capacity (16) is a Service Action In command.  One
261612594SShengliang.Zhang@Sun.COM 	 * command byte (0x9E) is overloaded for multiple operations,
261712594SShengliang.Zhang@Sun.COM 	 * with the second CDB byte specifying the desired operation
261812594SShengliang.Zhang@Sun.COM 	 */
261912594SShengliang.Zhang@Sun.COM 	cdb.scc_cmd = SCMD_SVC_ACTION_IN_G4;
262012594SShengliang.Zhang@Sun.COM 	cdb.cdb_opaque[1] = SSVC_ACTION_READ_CAPACITY_G4;
262112594SShengliang.Zhang@Sun.COM 
262212594SShengliang.Zhang@Sun.COM 	/*
262312594SShengliang.Zhang@Sun.COM 	 * Fill in allocation length field
262412594SShengliang.Zhang@Sun.COM 	 */
262512594SShengliang.Zhang@Sun.COM 	cdb.cdb_opaque[10] =
262612594SShengliang.Zhang@Sun.COM 	    (uchar_t)((ucmd.uscsi_buflen & 0xff000000) >> 24);
262712594SShengliang.Zhang@Sun.COM 	cdb.cdb_opaque[11] =
262812594SShengliang.Zhang@Sun.COM 	    (uchar_t)((ucmd.uscsi_buflen & 0x00ff0000) >> 16);
262912594SShengliang.Zhang@Sun.COM 	cdb.cdb_opaque[12] =
263012594SShengliang.Zhang@Sun.COM 	    (uchar_t)((ucmd.uscsi_buflen & 0x0000ff00) >> 8);
263112594SShengliang.Zhang@Sun.COM 	cdb.cdb_opaque[13] =
263212594SShengliang.Zhang@Sun.COM 	    (uchar_t)(ucmd.uscsi_buflen & 0x000000ff);
263312594SShengliang.Zhang@Sun.COM 
263412594SShengliang.Zhang@Sun.COM 	status = uscsi_cmd(fd, &ucmd,
263512594SShengliang.Zhang@Sun.COM 	    (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
263612594SShengliang.Zhang@Sun.COM 
263712594SShengliang.Zhang@Sun.COM 	if (status) {
263812594SShengliang.Zhang@Sun.COM 		if (option_msg) {
263912594SShengliang.Zhang@Sun.COM 			err_print("Read capacity 16 failed\n");
264012594SShengliang.Zhang@Sun.COM 		}
264112594SShengliang.Zhang@Sun.COM 	} else if (option_msg && diag_msg) {
264212594SShengliang.Zhang@Sun.COM 		/*
264312594SShengliang.Zhang@Sun.COM 		 * Dump the capacity data if anyone's interested
264412594SShengliang.Zhang@Sun.COM 		 */
264512594SShengliang.Zhang@Sun.COM 		dump("Capacity: ", (caddr_t)capacity,
264612594SShengliang.Zhang@Sun.COM 		    sizeof (struct scsi_capacity_16), HEX_ONLY);
264712594SShengliang.Zhang@Sun.COM 	}
264812594SShengliang.Zhang@Sun.COM 
264912594SShengliang.Zhang@Sun.COM 	capacity->sc_capacity = BE_64(capacity->sc_capacity);
265012594SShengliang.Zhang@Sun.COM 	capacity->sc_lbasize = BE_32(capacity->sc_lbasize);
265112594SShengliang.Zhang@Sun.COM 
265212594SShengliang.Zhang@Sun.COM 	return (status);
265312594SShengliang.Zhang@Sun.COM }
265412594SShengliang.Zhang@Sun.COM 
265512594SShengliang.Zhang@Sun.COM int
uscsi_read_capacity(fd,capacity)26560Sstevel@tonic-gate uscsi_read_capacity(fd, capacity)
26570Sstevel@tonic-gate 	int			fd;
26580Sstevel@tonic-gate 	struct scsi_capacity_16	*capacity;
26590Sstevel@tonic-gate {
26600Sstevel@tonic-gate 	struct uscsi_cmd	ucmd;
26610Sstevel@tonic-gate 	union scsi_cdb		cdb;
26620Sstevel@tonic-gate 	int			status;
26630Sstevel@tonic-gate 	struct scsi_capacity	cap_old;
26640Sstevel@tonic-gate 
26650Sstevel@tonic-gate 	/*
26660Sstevel@tonic-gate 	 * Build and execute the uscsi ioctl
26670Sstevel@tonic-gate 	 */
26680Sstevel@tonic-gate 	(void) memset((char *)capacity, 0, sizeof (struct scsi_capacity_16));
26690Sstevel@tonic-gate 	(void) memset((char *)&cap_old, 0, sizeof (struct scsi_capacity));
26700Sstevel@tonic-gate 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
26710Sstevel@tonic-gate 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
26720Sstevel@tonic-gate 	cdb.scc_cmd = SCMD_READ_CAPACITY;
26730Sstevel@tonic-gate 	ucmd.uscsi_cdb = (caddr_t)&cdb;
26740Sstevel@tonic-gate 	ucmd.uscsi_cdblen = CDB_GROUP1;
26750Sstevel@tonic-gate 	ucmd.uscsi_bufaddr = (caddr_t)&cap_old;
26760Sstevel@tonic-gate 	ucmd.uscsi_buflen = sizeof (struct scsi_capacity);
26770Sstevel@tonic-gate 	status = uscsi_cmd(fd, &ucmd,
26780Sstevel@tonic-gate 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
26790Sstevel@tonic-gate 
26800Sstevel@tonic-gate 	if (cap_old.capacity == UINT_MAX32) {
26810Sstevel@tonic-gate 		/*
26820Sstevel@tonic-gate 		 * A capacity of 0xffffffff in response to a
26830Sstevel@tonic-gate 		 * READ CAPACITY 10 indicates that the lun
26840Sstevel@tonic-gate 		 * is too large to report the size in a 32 bit
26850Sstevel@tonic-gate 		 * value, and a READ CAPACITY 16 is required
26860Sstevel@tonic-gate 		 * to get the correct size.
26870Sstevel@tonic-gate 		 */
26880Sstevel@tonic-gate 		(void) memset((char *)&ucmd, 0, sizeof (ucmd));
26890Sstevel@tonic-gate 		(void) memset((char *)&cdb, 0,
26900Sstevel@tonic-gate 			sizeof (union scsi_cdb));
26910Sstevel@tonic-gate 
26920Sstevel@tonic-gate 		ucmd.uscsi_cdb = (caddr_t)&cdb;
26930Sstevel@tonic-gate 		ucmd.uscsi_cdblen = CDB_GROUP4;
26940Sstevel@tonic-gate 		ucmd.uscsi_bufaddr = (caddr_t)capacity;
26950Sstevel@tonic-gate 		ucmd.uscsi_buflen = sizeof (struct scsi_capacity_16);
26960Sstevel@tonic-gate 
26970Sstevel@tonic-gate 		/*
26980Sstevel@tonic-gate 		 * Read Capacity (16) is a Service Action In command.  One
26990Sstevel@tonic-gate 		 * command byte (0x9E) is overloaded for multiple operations,
27000Sstevel@tonic-gate 		 * with the second CDB byte specifying the desired operation
27010Sstevel@tonic-gate 		 */
27020Sstevel@tonic-gate 		cdb.scc_cmd = SCMD_SVC_ACTION_IN_G4;
27030Sstevel@tonic-gate 		cdb.cdb_opaque[1] = SSVC_ACTION_READ_CAPACITY_G4;
27040Sstevel@tonic-gate 
27050Sstevel@tonic-gate 		/*
27060Sstevel@tonic-gate 		 * Fill in allocation length field
27070Sstevel@tonic-gate 		 */
27080Sstevel@tonic-gate 		cdb.cdb_opaque[10] =
27090Sstevel@tonic-gate 		    (uchar_t)((ucmd.uscsi_buflen & 0xff000000) >> 24);
27100Sstevel@tonic-gate 		cdb.cdb_opaque[11] =
27110Sstevel@tonic-gate 		    (uchar_t)((ucmd.uscsi_buflen & 0x00ff0000) >> 16);
27120Sstevel@tonic-gate 		cdb.cdb_opaque[12] =
27130Sstevel@tonic-gate 		    (uchar_t)((ucmd.uscsi_buflen & 0x0000ff00) >> 8);
27140Sstevel@tonic-gate 		cdb.cdb_opaque[13] =
27150Sstevel@tonic-gate 		    (uchar_t)(ucmd.uscsi_buflen & 0x000000ff);
27160Sstevel@tonic-gate 
27170Sstevel@tonic-gate 		status = uscsi_cmd(fd, &ucmd,
27180Sstevel@tonic-gate 			(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
27190Sstevel@tonic-gate 	}
27200Sstevel@tonic-gate 
27210Sstevel@tonic-gate 	if (status) {
27220Sstevel@tonic-gate 		if (option_msg) {
27230Sstevel@tonic-gate 			/*
27240Sstevel@tonic-gate 			 * Indicate which of the commands failed
27250Sstevel@tonic-gate 			 */
27260Sstevel@tonic-gate 			if (cdb.scc_cmd == SCMD_READ_CAPACITY) {
27270Sstevel@tonic-gate 				err_print("Read capacity failed\n");
27280Sstevel@tonic-gate 			} else {
27290Sstevel@tonic-gate 				err_print("Read capacity 16 failed\n");
27300Sstevel@tonic-gate 			}
27310Sstevel@tonic-gate 		}
27320Sstevel@tonic-gate 	} else if (option_msg && diag_msg) {
27330Sstevel@tonic-gate 		/*
27340Sstevel@tonic-gate 		 * Dump the capacity data if anyone's interested
27350Sstevel@tonic-gate 		 */
27361180Slh195018 		if (cap_old.capacity == UINT_MAX32) {
27371180Slh195018 			dump("Capacity: ", (caddr_t)capacity,
27381180Slh195018 				sizeof (struct scsi_capacity_16), HEX_ONLY);
27391180Slh195018 		} else {
27401180Slh195018 			dump("Capacity: ", (caddr_t)&cap_old,
27411180Slh195018 				sizeof (struct scsi_capacity), HEX_ONLY);
27421180Slh195018 		}
27430Sstevel@tonic-gate 	}
27441180Slh195018 
27451180Slh195018 	if (cap_old.capacity == UINT_MAX32) {
27461180Slh195018 		capacity->sc_capacity = BE_64(capacity->sc_capacity);
27471180Slh195018 		capacity->sc_lbasize = BE_32(capacity->sc_lbasize);
27481180Slh195018 	} else {
27491180Slh195018 		capacity->sc_capacity = (uint64_t)BE_32(cap_old.capacity);
27501180Slh195018 		capacity->sc_lbasize = BE_32(cap_old.lbasize);
27511180Slh195018 	}
27521180Slh195018 
27530Sstevel@tonic-gate 	return (status);
27540Sstevel@tonic-gate }
27550Sstevel@tonic-gate 
27560Sstevel@tonic-gate 
27570Sstevel@tonic-gate /*
27580Sstevel@tonic-gate  * Reserve the current disk
27590Sstevel@tonic-gate  */
27600Sstevel@tonic-gate static int
uscsi_reserve_release(int fd,int cmd)27610Sstevel@tonic-gate uscsi_reserve_release(int fd, int cmd)
27620Sstevel@tonic-gate {
27630Sstevel@tonic-gate 	int			status = 0;
27640Sstevel@tonic-gate #ifdef sparc
27650Sstevel@tonic-gate 	struct uscsi_cmd	ucmd;
27660Sstevel@tonic-gate 	union scsi_cdb		cdb;
27670Sstevel@tonic-gate 
27680Sstevel@tonic-gate 	/*
27690Sstevel@tonic-gate 	 * Build and execute the uscsi ioctl
27700Sstevel@tonic-gate 	 */
27710Sstevel@tonic-gate 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
27720Sstevel@tonic-gate 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
27730Sstevel@tonic-gate 	cdb.scc_cmd = (cmd == SCMD_RESERVE) ? SCMD_RESERVE : SCMD_RELEASE;
27740Sstevel@tonic-gate 	ucmd.uscsi_cdb = (caddr_t)&cdb;
27750Sstevel@tonic-gate 	ucmd.uscsi_cdblen = CDB_GROUP0;
27760Sstevel@tonic-gate 	status = uscsi_cmd(fd, &ucmd,
27779889SLarry.Liu@Sun.COM 	    (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
27780Sstevel@tonic-gate 
27790Sstevel@tonic-gate 	if (status) {
27800Sstevel@tonic-gate 		/*
27810Sstevel@tonic-gate 		 * Reserve/Release(6) failed.
27820Sstevel@tonic-gate 		 * Try Reserve/Release(10) , if it succeeds then
27830Sstevel@tonic-gate 		 * return success.
27840Sstevel@tonic-gate 		 */
27850Sstevel@tonic-gate 		(void) memset((char *)&ucmd, 0, sizeof (ucmd));
27860Sstevel@tonic-gate 		(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
27870Sstevel@tonic-gate 		ucmd.uscsi_cdb = (caddr_t)&cdb;
27880Sstevel@tonic-gate 		cdb.scc_cmd = (cmd == SCMD_RESERVE) ?
27899889SLarry.Liu@Sun.COM 		    SCMD_RESERVE_G1 : SCMD_RELEASE_G1;
27900Sstevel@tonic-gate 		ucmd.uscsi_cdblen = CDB_GROUP1;
27910Sstevel@tonic-gate 		status = uscsi_cmd(fd, &ucmd,
27929889SLarry.Liu@Sun.COM 		    (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
27930Sstevel@tonic-gate 		if (status) {
27940Sstevel@tonic-gate 			if (option_msg) {
27959889SLarry.Liu@Sun.COM 				err_print("%s failed\n", (cmd == SCMD_RESERVE) ?
27969889SLarry.Liu@Sun.COM 				    "Reserve" : "Release");
27970Sstevel@tonic-gate 			}
27980Sstevel@tonic-gate 		}
27990Sstevel@tonic-gate 	}
28000Sstevel@tonic-gate #else /* not sparc */
28010Sstevel@tonic-gate 
28020Sstevel@tonic-gate #ifdef lint
28030Sstevel@tonic-gate 	fd = fd;
28040Sstevel@tonic-gate 	cmd = cmd;
28050Sstevel@tonic-gate #endif /* lint */
28060Sstevel@tonic-gate 
28070Sstevel@tonic-gate #endif /* not sparc */
28080Sstevel@tonic-gate 
28090Sstevel@tonic-gate 	return (status);
28100Sstevel@tonic-gate }
28110Sstevel@tonic-gate 
28120Sstevel@tonic-gate int
scsi_dump_mode_sense_pages(page_control)28130Sstevel@tonic-gate scsi_dump_mode_sense_pages(page_control)
28140Sstevel@tonic-gate 	int			page_control;
28150Sstevel@tonic-gate {
28160Sstevel@tonic-gate 	struct uscsi_cmd	ucmd;
28170Sstevel@tonic-gate 	union scsi_cdb		cdb;
28180Sstevel@tonic-gate 	char			*msbuf;
28190Sstevel@tonic-gate 	int			nbytes;
28200Sstevel@tonic-gate 	char			*pc_str;
28210Sstevel@tonic-gate 	int			status;
28220Sstevel@tonic-gate 	struct mode_header	*mh;
28230Sstevel@tonic-gate 	char			*p;
28240Sstevel@tonic-gate 	struct mode_page	*mp;
28250Sstevel@tonic-gate 	int			n;
28260Sstevel@tonic-gate 	char			s[16];
28270Sstevel@tonic-gate 	int			result = 0;
28280Sstevel@tonic-gate 
28290Sstevel@tonic-gate 	pc_str = find_string(page_control_strings, page_control);
28300Sstevel@tonic-gate 
28310Sstevel@tonic-gate 	/*
28320Sstevel@tonic-gate 	 * Allocate memory for the mode sense buffer.
28330Sstevel@tonic-gate 	 */
28340Sstevel@tonic-gate 	nbytes = 255;
28350Sstevel@tonic-gate 	msbuf = (char *)zalloc(nbytes);
28360Sstevel@tonic-gate 
28370Sstevel@tonic-gate 	/*
28380Sstevel@tonic-gate 	 * Build and execute the uscsi ioctl
28390Sstevel@tonic-gate 	 */
28400Sstevel@tonic-gate 	(void) memset(msbuf, 0, nbytes);
28410Sstevel@tonic-gate 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
28420Sstevel@tonic-gate 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
28430Sstevel@tonic-gate 	cdb.scc_cmd = SCMD_MODE_SENSE;
28440Sstevel@tonic-gate 	FORMG0COUNT(&cdb, (uchar_t)nbytes);
28450Sstevel@tonic-gate 	cdb.cdb_opaque[2] = page_control | 0x3f;
28460Sstevel@tonic-gate 	ucmd.uscsi_cdb = (caddr_t)&cdb;
28470Sstevel@tonic-gate 	ucmd.uscsi_cdblen = CDB_GROUP0;
28480Sstevel@tonic-gate 	ucmd.uscsi_bufaddr = msbuf;
28490Sstevel@tonic-gate 	ucmd.uscsi_buflen = nbytes;
28500Sstevel@tonic-gate 	status = uscsi_cmd(cur_file, &ucmd,
28510Sstevel@tonic-gate 		(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
28520Sstevel@tonic-gate 	if (status) {
28530Sstevel@tonic-gate 		err_print("\nMode sense page 0x3f (%s) failed\n",
28540Sstevel@tonic-gate 			pc_str);
28550Sstevel@tonic-gate 		result = 1;
28560Sstevel@tonic-gate 	} else {
28570Sstevel@tonic-gate 		err_print("\nMode sense pages (%s):\n", pc_str);
28580Sstevel@tonic-gate 		mh = (struct mode_header *)msbuf;
28590Sstevel@tonic-gate 		nbytes = mh->length - sizeof (struct mode_header) -
28600Sstevel@tonic-gate 				mh->bdesc_length + 1;
28610Sstevel@tonic-gate 		p = msbuf + sizeof (struct mode_header) +
28620Sstevel@tonic-gate 			mh->bdesc_length;
28630Sstevel@tonic-gate 		dump("         ", msbuf, sizeof (struct mode_header) +
28640Sstevel@tonic-gate 			(int)mh->bdesc_length, HEX_ONLY);
28650Sstevel@tonic-gate 		while (nbytes > 0) {
28660Sstevel@tonic-gate 			mp = (struct mode_page *)p;
28670Sstevel@tonic-gate 			n = mp->length + sizeof (struct mode_page);
28680Sstevel@tonic-gate 			nbytes -= n;
28690Sstevel@tonic-gate 			if (nbytes < 0)
28700Sstevel@tonic-gate 				break;
28710Sstevel@tonic-gate 			(void) sprintf(s, "   %3x:  ", mp->code);
28720Sstevel@tonic-gate 			dump(s, p, n, HEX_ONLY);
28730Sstevel@tonic-gate 			p += n;
28740Sstevel@tonic-gate 		}
28750Sstevel@tonic-gate 		if (nbytes < 0) {
28760Sstevel@tonic-gate 			err_print("  Sense data formatted incorrectly:\n");
28770Sstevel@tonic-gate 			dump("    ", msbuf, (int)mh->length+1, HEX_ONLY);
28780Sstevel@tonic-gate 			result = 1;
28790Sstevel@tonic-gate 		}
28800Sstevel@tonic-gate 		err_print("\n");
28810Sstevel@tonic-gate 	}
28820Sstevel@tonic-gate 
28830Sstevel@tonic-gate 	free(msbuf);
28840Sstevel@tonic-gate 	return (result);
28850Sstevel@tonic-gate }
28860Sstevel@tonic-gate 
28870Sstevel@tonic-gate 
28880Sstevel@tonic-gate static void
scsi_printerr(ucmd,rq,rqlen)28890Sstevel@tonic-gate scsi_printerr(ucmd, rq, rqlen)
28900Sstevel@tonic-gate 	struct uscsi_cmd		*ucmd;
28910Sstevel@tonic-gate 	struct scsi_extended_sense	*rq;
28920Sstevel@tonic-gate 	int				rqlen;
28930Sstevel@tonic-gate {
28940Sstevel@tonic-gate 	diskaddr_t	blkno;
28950Sstevel@tonic-gate 	struct scsi_descr_sense_hdr *sdsp =
28960Sstevel@tonic-gate 	    (struct scsi_descr_sense_hdr *)rq;
28970Sstevel@tonic-gate 
28980Sstevel@tonic-gate 	switch (rq->es_key) {
28990Sstevel@tonic-gate 	case KEY_NO_SENSE:
29000Sstevel@tonic-gate 		err_print("No sense error");
29010Sstevel@tonic-gate 		break;
29020Sstevel@tonic-gate 	case KEY_RECOVERABLE_ERROR:
29030Sstevel@tonic-gate 		err_print("Recoverable error");
29040Sstevel@tonic-gate 		break;
29050Sstevel@tonic-gate 	case KEY_NOT_READY:
29060Sstevel@tonic-gate 		err_print("Not ready error");
29070Sstevel@tonic-gate 		break;
29080Sstevel@tonic-gate 	case KEY_MEDIUM_ERROR:
29090Sstevel@tonic-gate 		err_print("Medium error");
29100Sstevel@tonic-gate 		break;
29110Sstevel@tonic-gate 	case KEY_HARDWARE_ERROR:
29120Sstevel@tonic-gate 		err_print("Hardware error");
29130Sstevel@tonic-gate 		break;
29140Sstevel@tonic-gate 	case KEY_ILLEGAL_REQUEST:
29150Sstevel@tonic-gate 		err_print("Illegal request");
29160Sstevel@tonic-gate 		break;
29170Sstevel@tonic-gate 	case KEY_UNIT_ATTENTION:
29180Sstevel@tonic-gate 		err_print("Unit attention error");
29190Sstevel@tonic-gate 		break;
29200Sstevel@tonic-gate 	case KEY_WRITE_PROTECT:
29210Sstevel@tonic-gate 		err_print("Write protect error");
29220Sstevel@tonic-gate 		break;
29230Sstevel@tonic-gate 	case KEY_BLANK_CHECK:
29240Sstevel@tonic-gate 		err_print("Blank check error");
29250Sstevel@tonic-gate 		break;
29260Sstevel@tonic-gate 	case KEY_VENDOR_UNIQUE:
29270Sstevel@tonic-gate 		err_print("Vendor unique error");
29280Sstevel@tonic-gate 		break;
29290Sstevel@tonic-gate 	case KEY_COPY_ABORTED:
29300Sstevel@tonic-gate 		err_print("Copy aborted error");
29310Sstevel@tonic-gate 		break;
29320Sstevel@tonic-gate 	case KEY_ABORTED_COMMAND:
29330Sstevel@tonic-gate 		err_print("Aborted command");
29340Sstevel@tonic-gate 		break;
29350Sstevel@tonic-gate 	case KEY_EQUAL:
29360Sstevel@tonic-gate 		err_print("Equal error");
29370Sstevel@tonic-gate 		break;
29380Sstevel@tonic-gate 	case KEY_VOLUME_OVERFLOW:
29390Sstevel@tonic-gate 		err_print("Volume overflow");
29400Sstevel@tonic-gate 		break;
29410Sstevel@tonic-gate 	case KEY_MISCOMPARE:
29420Sstevel@tonic-gate 		err_print("Miscompare error");
29430Sstevel@tonic-gate 		break;
29440Sstevel@tonic-gate 	case KEY_RESERVED:
29450Sstevel@tonic-gate 		err_print("Reserved error");
29460Sstevel@tonic-gate 		break;
29470Sstevel@tonic-gate 	default:
29480Sstevel@tonic-gate 		err_print("Unknown error");
29490Sstevel@tonic-gate 		break;
29500Sstevel@tonic-gate 	}
29510Sstevel@tonic-gate 
29520Sstevel@tonic-gate 	err_print(" during %s", scsi_find_command_name(ucmd->uscsi_cdb[0]));
29530Sstevel@tonic-gate 
29540Sstevel@tonic-gate 	/*
29550Sstevel@tonic-gate 	 * Get asc, ascq and info field from sense data.  There are two
29560Sstevel@tonic-gate 	 * possible formats (fixed sense data and descriptor sense data)
29570Sstevel@tonic-gate 	 * depending on the value of es_code.
29580Sstevel@tonic-gate 	 */
29590Sstevel@tonic-gate 	switch (rq->es_code) {
29600Sstevel@tonic-gate 	case CODE_FMT_DESCR_CURRENT:
29610Sstevel@tonic-gate 	case CODE_FMT_DESCR_DEFERRED:
29620Sstevel@tonic-gate 		blkno =
29630Sstevel@tonic-gate 		    (diskaddr_t)scsi_extract_sense_info_descr(sdsp, rqlen);
29640Sstevel@tonic-gate 		if (blkno != (diskaddr_t)-1) {
29650Sstevel@tonic-gate 			err_print(": block %lld (0x%llx) (", blkno, blkno);
29660Sstevel@tonic-gate 			pr_dblock(err_print, blkno);
29670Sstevel@tonic-gate 			err_print(")");
29680Sstevel@tonic-gate 		}
29690Sstevel@tonic-gate 
29700Sstevel@tonic-gate 		err_print("\n");
29710Sstevel@tonic-gate 
29720Sstevel@tonic-gate 		err_print("ASC: 0x%x   ASCQ: 0x%x\n",
29730Sstevel@tonic-gate 		    sdsp->ds_add_code, sdsp->ds_qual_code);
29740Sstevel@tonic-gate 		break;
29750Sstevel@tonic-gate 	case CODE_FMT_FIXED_CURRENT:
29760Sstevel@tonic-gate 	case CODE_FMT_FIXED_DEFERRED:
29770Sstevel@tonic-gate 	default:
29780Sstevel@tonic-gate 		if (rq->es_valid) {
29790Sstevel@tonic-gate 			blkno = (rq->es_info_1 << 24) |
29800Sstevel@tonic-gate 			    (rq->es_info_2 << 16) |
29810Sstevel@tonic-gate 			    (rq->es_info_3 << 8) | rq->es_info_4;
29820Sstevel@tonic-gate 			err_print(": block %lld (0x%llx) (", blkno, blkno);
29830Sstevel@tonic-gate 			pr_dblock(err_print, blkno);
29840Sstevel@tonic-gate 			err_print(")");
29850Sstevel@tonic-gate 		}
29860Sstevel@tonic-gate 
29870Sstevel@tonic-gate 		err_print("\n");
29880Sstevel@tonic-gate 
29890Sstevel@tonic-gate 		if (rq->es_add_len >= 6) {
29900Sstevel@tonic-gate 			err_print("ASC: 0x%x   ASCQ: 0x%x\n",
29910Sstevel@tonic-gate 			    rq->es_add_code, rq->es_qual_code);
29920Sstevel@tonic-gate 		}
29930Sstevel@tonic-gate 		break;
29940Sstevel@tonic-gate 	}
29950Sstevel@tonic-gate 
29960Sstevel@tonic-gate 	if (option_msg && diag_msg) {
29970Sstevel@tonic-gate 		if (rq->es_key == KEY_ILLEGAL_REQUEST) {
29980Sstevel@tonic-gate 			dump("cmd:    ", (caddr_t)ucmd,
29990Sstevel@tonic-gate 			    sizeof (struct uscsi_cmd), HEX_ONLY);
30000Sstevel@tonic-gate 			dump("cdb:    ", (caddr_t)ucmd->uscsi_cdb,
30010Sstevel@tonic-gate 			    ucmd->uscsi_cdblen, HEX_ONLY);
30020Sstevel@tonic-gate 		}
30030Sstevel@tonic-gate 		dump("sense:  ", (caddr_t)rq, rqlen, HEX_ONLY);
30040Sstevel@tonic-gate 	}
30050Sstevel@tonic-gate 
30060Sstevel@tonic-gate 	if (option_msg) {
30070Sstevel@tonic-gate 		switch (rq->es_code) {
30080Sstevel@tonic-gate 		case CODE_FMT_DESCR_CURRENT:
30090Sstevel@tonic-gate 		case CODE_FMT_DESCR_DEFERRED:
30100Sstevel@tonic-gate 			scsi_print_descr_sense(sdsp, rqlen);
30110Sstevel@tonic-gate 			break;
30120Sstevel@tonic-gate 		case CODE_FMT_FIXED_CURRENT:
30130Sstevel@tonic-gate 		case CODE_FMT_FIXED_DEFERRED:
30140Sstevel@tonic-gate 		default:
30150Sstevel@tonic-gate 			scsi_print_extended_sense(rq, rqlen);
30160Sstevel@tonic-gate 			break;
30170Sstevel@tonic-gate 		}
30180Sstevel@tonic-gate 	}
30190Sstevel@tonic-gate }
30200Sstevel@tonic-gate 
30210Sstevel@tonic-gate /*
30220Sstevel@tonic-gate  * Retrieve "information" field from descriptor format
30230Sstevel@tonic-gate  * sense data.  Iterates through each sense descriptor
30240Sstevel@tonic-gate  * looking for the information descriptor and returns
30250Sstevel@tonic-gate  * the information field from that descriptor.
30260Sstevel@tonic-gate  */
30270Sstevel@tonic-gate static diskaddr_t
scsi_extract_sense_info_descr(struct scsi_descr_sense_hdr * sdsp,int rqlen)30280Sstevel@tonic-gate scsi_extract_sense_info_descr(struct scsi_descr_sense_hdr *sdsp, int rqlen)
30290Sstevel@tonic-gate {
30300Sstevel@tonic-gate 	diskaddr_t result;
30310Sstevel@tonic-gate 	uint8_t *descr_offset;
30320Sstevel@tonic-gate 	int valid_sense_length;
30330Sstevel@tonic-gate 	struct scsi_information_sense_descr *isd;
30340Sstevel@tonic-gate 
30350Sstevel@tonic-gate 	/*
30360Sstevel@tonic-gate 	 * Initialize result to -1 indicating there is no information
30370Sstevel@tonic-gate 	 * descriptor
30380Sstevel@tonic-gate 	 */
30390Sstevel@tonic-gate 	result = (diskaddr_t)-1;
30400Sstevel@tonic-gate 
30410Sstevel@tonic-gate 	/*
30420Sstevel@tonic-gate 	 * The first descriptor will immediately follow the header
30430Sstevel@tonic-gate 	 */
30440Sstevel@tonic-gate 	descr_offset = (uint8_t *)(sdsp+1); /* Pointer arithmetic */
30450Sstevel@tonic-gate 
30460Sstevel@tonic-gate 	/*
30470Sstevel@tonic-gate 	 * Calculate the amount of valid sense data
30480Sstevel@tonic-gate 	 */
30490Sstevel@tonic-gate 	valid_sense_length =
30500Sstevel@tonic-gate 	    min((sizeof (struct scsi_descr_sense_hdr) +
30510Sstevel@tonic-gate 	    sdsp->ds_addl_sense_length),
30520Sstevel@tonic-gate 	    rqlen);
30530Sstevel@tonic-gate 
30540Sstevel@tonic-gate 	/*
30550Sstevel@tonic-gate 	 * Iterate through the list of descriptors, stopping when we
30560Sstevel@tonic-gate 	 * run out of sense data
30570Sstevel@tonic-gate 	 */
30580Sstevel@tonic-gate 	while ((descr_offset + sizeof (struct scsi_information_sense_descr)) <=
30590Sstevel@tonic-gate 	    (uint8_t *)sdsp + valid_sense_length) {
30600Sstevel@tonic-gate 		/*
30610Sstevel@tonic-gate 		 * Check if this is an information descriptor.  We can
30620Sstevel@tonic-gate 		 * use the scsi_information_sense_descr structure as a
30630Sstevel@tonic-gate 		 * template sense the first two fields are always the
30640Sstevel@tonic-gate 		 * same
30650Sstevel@tonic-gate 		 */
30660Sstevel@tonic-gate 		isd = (struct scsi_information_sense_descr *)descr_offset;
30670Sstevel@tonic-gate 		if (isd->isd_descr_type == DESCR_INFORMATION) {
30680Sstevel@tonic-gate 			/*
30690Sstevel@tonic-gate 			 * Found an information descriptor.  Copy the
30700Sstevel@tonic-gate 			 * information field.  There will only be one
30710Sstevel@tonic-gate 			 * information descriptor so we can stop looking.
30720Sstevel@tonic-gate 			 */
30730Sstevel@tonic-gate 			result =
30740Sstevel@tonic-gate 			    (((diskaddr_t)isd->isd_information[0] << 56) |
30759889SLarry.Liu@Sun.COM 			    ((diskaddr_t)isd->isd_information[1] << 48) |
30769889SLarry.Liu@Sun.COM 			    ((diskaddr_t)isd->isd_information[2] << 40) |
30779889SLarry.Liu@Sun.COM 			    ((diskaddr_t)isd->isd_information[3] << 32) |
30789889SLarry.Liu@Sun.COM 			    ((diskaddr_t)isd->isd_information[4] << 24) |
30799889SLarry.Liu@Sun.COM 			    ((diskaddr_t)isd->isd_information[5] << 16) |
30809889SLarry.Liu@Sun.COM 			    ((diskaddr_t)isd->isd_information[6] << 8)  |
30819889SLarry.Liu@Sun.COM 			    ((diskaddr_t)isd->isd_information[7]));
30820Sstevel@tonic-gate 			break;
30830Sstevel@tonic-gate 		}
30840Sstevel@tonic-gate 
30850Sstevel@tonic-gate 		/*
30860Sstevel@tonic-gate 		 * Get pointer to the next descriptor.  The "additional
30870Sstevel@tonic-gate 		 * length" field holds the length of the descriptor except
30880Sstevel@tonic-gate 		 * for the "type" and "additional length" fields, so
30890Sstevel@tonic-gate 		 * we need to add 2 to get the total length.
30900Sstevel@tonic-gate 		 */
30910Sstevel@tonic-gate 		descr_offset += (isd->isd_addl_length + 2);
30920Sstevel@tonic-gate 	}
30930Sstevel@tonic-gate 
30940Sstevel@tonic-gate 	return (result);
30950Sstevel@tonic-gate }
30960Sstevel@tonic-gate 
30970Sstevel@tonic-gate /*
30980Sstevel@tonic-gate  * Return a pointer to a string telling us the name of the command.
30990Sstevel@tonic-gate  */
31000Sstevel@tonic-gate static char *
scsi_find_command_name(uint_t cmd)31010Sstevel@tonic-gate scsi_find_command_name(uint_t cmd)
31020Sstevel@tonic-gate {
31030Sstevel@tonic-gate 	struct scsi_command_name *c;
31040Sstevel@tonic-gate 
31050Sstevel@tonic-gate 	for (c = scsi_command_names; c->command != SCMD_UNKNOWN; c++)
31060Sstevel@tonic-gate 		if (c->command == cmd)
31070Sstevel@tonic-gate 			break;
31080Sstevel@tonic-gate 	return (c->name);
31090Sstevel@tonic-gate }
31100Sstevel@tonic-gate 
31110Sstevel@tonic-gate 
31120Sstevel@tonic-gate /*
31130Sstevel@tonic-gate  * Return true if we support a particular mode page
31140Sstevel@tonic-gate  */
31150Sstevel@tonic-gate int
scsi_supported_page(int page)31160Sstevel@tonic-gate scsi_supported_page(int page) {
31170Sstevel@tonic-gate 	return (page == 1 || page == 2 || page == 3 || page == 4 ||
31180Sstevel@tonic-gate 		page == 8 || page == 0x38);
31190Sstevel@tonic-gate }
31200Sstevel@tonic-gate 
31210Sstevel@tonic-gate 
31220Sstevel@tonic-gate int
apply_chg_list(int pageno,int pagsiz,uchar_t * curbits,uchar_t * chgbits,struct chg_list * chglist)31230Sstevel@tonic-gate apply_chg_list(int pageno, int pagsiz, uchar_t *curbits,
31240Sstevel@tonic-gate 		uchar_t *chgbits, struct chg_list *chglist)
31250Sstevel@tonic-gate {
31260Sstevel@tonic-gate 	uchar_t		c;
31270Sstevel@tonic-gate 	int		i;
31280Sstevel@tonic-gate 	int		m;
31290Sstevel@tonic-gate 	int		delta;
31300Sstevel@tonic-gate 	int		changed = 0;
31310Sstevel@tonic-gate 
31320Sstevel@tonic-gate 	while (chglist != NULL) {
31330Sstevel@tonic-gate 		if (chglist->pageno == pageno &&
31349889SLarry.Liu@Sun.COM 		    chglist->byteno < pagsiz) {
31350Sstevel@tonic-gate 			i = chglist->byteno;
31360Sstevel@tonic-gate 			c = curbits[i];
31370Sstevel@tonic-gate 			switch (chglist->mode) {
31380Sstevel@tonic-gate 			case CHG_MODE_SET:
31390Sstevel@tonic-gate 				c |= (uchar_t)chglist->value;
31400Sstevel@tonic-gate 				break;
31410Sstevel@tonic-gate 			case CHG_MODE_CLR:
31420Sstevel@tonic-gate 				c &= (uchar_t)chglist->value;
31430Sstevel@tonic-gate 				break;
31440Sstevel@tonic-gate 			case CHG_MODE_ABS:
31450Sstevel@tonic-gate 				c = (uchar_t)chglist->value;
31460Sstevel@tonic-gate 				break;
31470Sstevel@tonic-gate 			}
31480Sstevel@tonic-gate 			/*
31490Sstevel@tonic-gate 			 * Figure out which bits changed, and
31500Sstevel@tonic-gate 			 * are marked as changeable.  If this
31510Sstevel@tonic-gate 			 * result actually differs from the
31520Sstevel@tonic-gate 			 * current value, update the current
31530Sstevel@tonic-gate 			 * value, and note that a mode select
31540Sstevel@tonic-gate 			 * should be done.
31550Sstevel@tonic-gate 			 */
31560Sstevel@tonic-gate 			delta = c ^ curbits[i];
31570Sstevel@tonic-gate 			for (m = 0x01; m < 0x100; m <<= 1) {
31580Sstevel@tonic-gate 				if ((delta & m) && (chgbits[i] & m)) {
31590Sstevel@tonic-gate 					curbits[i] ^= m;
31600Sstevel@tonic-gate 					changed = 1;
31610Sstevel@tonic-gate 				}
31620Sstevel@tonic-gate 			}
31630Sstevel@tonic-gate 		}
31640Sstevel@tonic-gate 		chglist = chglist->next;
31650Sstevel@tonic-gate 	}
31660Sstevel@tonic-gate 
31670Sstevel@tonic-gate 	return (changed);
31680Sstevel@tonic-gate }
31690Sstevel@tonic-gate 
31700Sstevel@tonic-gate 
31710Sstevel@tonic-gate /*
31720Sstevel@tonic-gate  * Return whether a given page is affected by an item on
31730Sstevel@tonic-gate  * the change list.
31740Sstevel@tonic-gate  */
31750Sstevel@tonic-gate static int
chg_list_affects_page(chglist,pageno)31760Sstevel@tonic-gate chg_list_affects_page(chglist, pageno)
31770Sstevel@tonic-gate 	struct chg_list	*chglist;
31780Sstevel@tonic-gate 	int		pageno;
31790Sstevel@tonic-gate {
31800Sstevel@tonic-gate 	while (chglist != NULL) {
31810Sstevel@tonic-gate 		if (chglist->pageno == pageno) {
31820Sstevel@tonic-gate 			return (1);
31830Sstevel@tonic-gate 		}
31840Sstevel@tonic-gate 		chglist = chglist->next;
31850Sstevel@tonic-gate 	}
31860Sstevel@tonic-gate 
31870Sstevel@tonic-gate 	return (0);
31880Sstevel@tonic-gate }
31890Sstevel@tonic-gate 
31900Sstevel@tonic-gate 
31910Sstevel@tonic-gate /*
31920Sstevel@tonic-gate  * Labels for the various fields of the scsi_extended_sense structure
31930Sstevel@tonic-gate  */
31940Sstevel@tonic-gate static char *scsi_extended_sense_labels[] = {
31950Sstevel@tonic-gate 	"Request sense valid:             ",
31960Sstevel@tonic-gate 	"Error class and code:            ",
31970Sstevel@tonic-gate 	"Segment number:                  ",
31980Sstevel@tonic-gate 	"Filemark:                        ",
31990Sstevel@tonic-gate 	"End-of-medium:                   ",
32000Sstevel@tonic-gate 	"Incorrect length indicator:      ",
32010Sstevel@tonic-gate 	"Sense key:                       ",
32020Sstevel@tonic-gate 	"Information field:               ",
32030Sstevel@tonic-gate 	"Additional sense length:         ",
32040Sstevel@tonic-gate 	"Command-specific information:    ",
32050Sstevel@tonic-gate 	"Additional sense code:           ",
32060Sstevel@tonic-gate 	"Additional sense code qualifier: ",
32070Sstevel@tonic-gate 	"Field replaceable unit code:     ",
32080Sstevel@tonic-gate 	"Sense-key specific:              ",
32090Sstevel@tonic-gate 	"Additional sense bytes:          "
32100Sstevel@tonic-gate };
32110Sstevel@tonic-gate 
32120Sstevel@tonic-gate 
32130Sstevel@tonic-gate /*
32140Sstevel@tonic-gate  * Display the full scsi_extended_sense as returned by the device
32150Sstevel@tonic-gate  */
32160Sstevel@tonic-gate static void
scsi_print_extended_sense(rq,rqlen)32170Sstevel@tonic-gate scsi_print_extended_sense(rq, rqlen)
32180Sstevel@tonic-gate 	struct scsi_extended_sense	*rq;
32190Sstevel@tonic-gate 	int				rqlen;
32200Sstevel@tonic-gate {
32210Sstevel@tonic-gate 	char			**p;
32220Sstevel@tonic-gate 
32230Sstevel@tonic-gate 	p = scsi_extended_sense_labels;
32240Sstevel@tonic-gate 	if (rqlen < (sizeof (*rq) - 2) || !rq->es_valid) {
32250Sstevel@tonic-gate 		/*
32260Sstevel@tonic-gate 		 * target should be capable of returning at least 18
32270Sstevel@tonic-gate 		 * bytes of data, i.e upto rq->es_skey_specific field.
32280Sstevel@tonic-gate 		 * The additional sense bytes (2 or more ...) are optional.
32290Sstevel@tonic-gate 		 */
32300Sstevel@tonic-gate 		return;
32310Sstevel@tonic-gate 	}
32320Sstevel@tonic-gate 
32330Sstevel@tonic-gate 	fmt_print("\n%s%s\n", *p++, rq->es_valid ? "yes" : "no");
32340Sstevel@tonic-gate 	fmt_print("%s0x%02x\n", *p++, (rq->es_class << 4) + rq->es_code);
32350Sstevel@tonic-gate 	fmt_print("%s%d\n", *p++, rq->es_segnum);
32360Sstevel@tonic-gate 	fmt_print("%s%s\n", *p++, rq->es_filmk ? "yes" : "no");
32370Sstevel@tonic-gate 	fmt_print("%s%s\n", *p++, rq->es_eom ? "yes" : "no");
32380Sstevel@tonic-gate 	fmt_print("%s%s\n", *p++, rq->es_ili ? "yes" : "no");
32390Sstevel@tonic-gate 	fmt_print("%s%d\n", *p++, rq->es_key);
32400Sstevel@tonic-gate 
32410Sstevel@tonic-gate 	fmt_print("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++, rq->es_info_1,
32420Sstevel@tonic-gate 		rq->es_info_2, rq->es_info_3, rq->es_info_4);
32430Sstevel@tonic-gate 	fmt_print("%s%d\n", *p++, rq->es_add_len);
32440Sstevel@tonic-gate 	fmt_print("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++, rq->es_cmd_info[0],
32450Sstevel@tonic-gate 		rq->es_cmd_info[1], rq->es_cmd_info[2], rq->es_cmd_info[3]);
32460Sstevel@tonic-gate 	fmt_print("%s0x%02x = %d\n", *p++, rq->es_add_code, rq->es_add_code);
32470Sstevel@tonic-gate 	fmt_print("%s0x%02x = %d\n", *p++, rq->es_qual_code, rq->es_qual_code);
32480Sstevel@tonic-gate 	fmt_print("%s%d\n", *p++, rq->es_fru_code);
32490Sstevel@tonic-gate 	fmt_print("%s0x%02x 0x%02x 0x%02x\n", *p++, rq->es_skey_specific[0],
32500Sstevel@tonic-gate 		rq->es_skey_specific[1], rq->es_skey_specific[2]);
32510Sstevel@tonic-gate 	if (rqlen >= sizeof (*rq)) {
32520Sstevel@tonic-gate 		fmt_print("%s0x%02x 0x%02x%s\n", *p, rq->es_add_info[0],
32530Sstevel@tonic-gate 		rq->es_add_info[1], (rqlen > sizeof (*rq)) ? " ..." : "");
32540Sstevel@tonic-gate 	}
32550Sstevel@tonic-gate 
32560Sstevel@tonic-gate 	fmt_print("\n");
32570Sstevel@tonic-gate }
32580Sstevel@tonic-gate 
32590Sstevel@tonic-gate /*
32600Sstevel@tonic-gate  * Labels for the various fields of the scsi_descr_sense_hdr structure
32610Sstevel@tonic-gate  */
32620Sstevel@tonic-gate static char *scsi_descr_sense_labels[] = {
32630Sstevel@tonic-gate 	"Error class and code:            ",
32640Sstevel@tonic-gate 	"Sense key:                       ",
32650Sstevel@tonic-gate 	"Additional sense length:         ",
32660Sstevel@tonic-gate 	"Additional sense code:           ",
32670Sstevel@tonic-gate 	"Additional sense code qualifier: ",
32680Sstevel@tonic-gate 	"Additional sense bytes:          "
32690Sstevel@tonic-gate };
32700Sstevel@tonic-gate 
32710Sstevel@tonic-gate 
32720Sstevel@tonic-gate /*
32730Sstevel@tonic-gate  * Display the full descriptor sense data as returned by the device
32740Sstevel@tonic-gate  */
32750Sstevel@tonic-gate 
32760Sstevel@tonic-gate static void
scsi_print_descr_sense(rq,rqlen)32770Sstevel@tonic-gate scsi_print_descr_sense(rq, rqlen)
32780Sstevel@tonic-gate 	struct scsi_descr_sense_hdr	*rq;
32790Sstevel@tonic-gate 	int				rqlen;
32800Sstevel@tonic-gate {
32810Sstevel@tonic-gate 	char			**p;
32820Sstevel@tonic-gate 	uint8_t			*descr_offset;
32830Sstevel@tonic-gate 	int			valid_sense_length;
32840Sstevel@tonic-gate 	struct scsi_information_sense_descr *isd;
32850Sstevel@tonic-gate 
32860Sstevel@tonic-gate 	p = scsi_descr_sense_labels;
32870Sstevel@tonic-gate 	if (rqlen < sizeof (struct scsi_descr_sense_hdr)) {
32880Sstevel@tonic-gate 		/*
32890Sstevel@tonic-gate 		 * target must return at least 8 bytes of data
32900Sstevel@tonic-gate 		 */
32910Sstevel@tonic-gate 		return;
32920Sstevel@tonic-gate 	}
32930Sstevel@tonic-gate 
32940Sstevel@tonic-gate 	/* Print descriptor sense header */
32950Sstevel@tonic-gate 	fmt_print("%s0x%02x\n", *p++, (rq->ds_class << 4) + rq->ds_code);
32960Sstevel@tonic-gate 	fmt_print("%s%d\n", *p++, rq->ds_key);
32970Sstevel@tonic-gate 
32980Sstevel@tonic-gate 	fmt_print("%s%d\n", *p++, rq->ds_addl_sense_length);
32990Sstevel@tonic-gate 	fmt_print("%s0x%02x = %d\n", *p++, rq->ds_add_code, rq->ds_add_code);
33000Sstevel@tonic-gate 	fmt_print("%s0x%02x = %d\n", *p++, rq->ds_qual_code, rq->ds_qual_code);
33010Sstevel@tonic-gate 	fmt_print("\n");
33020Sstevel@tonic-gate 
33030Sstevel@tonic-gate 	/*
33040Sstevel@tonic-gate 	 * Now print any sense descriptors.   The first descriptor will
33050Sstevel@tonic-gate 	 * immediately follow the header
33060Sstevel@tonic-gate 	 */
33070Sstevel@tonic-gate 	descr_offset = (uint8_t *)(rq+1); /* Pointer arithmetic */
33080Sstevel@tonic-gate 
33090Sstevel@tonic-gate 	/*
33100Sstevel@tonic-gate 	 * Calculate the amount of valid sense data
33110Sstevel@tonic-gate 	 */
33120Sstevel@tonic-gate 	valid_sense_length =
33130Sstevel@tonic-gate 	    min((sizeof (struct scsi_descr_sense_hdr) +
33140Sstevel@tonic-gate 		rq->ds_addl_sense_length), rqlen);
33150Sstevel@tonic-gate 
33160Sstevel@tonic-gate 	/*
33170Sstevel@tonic-gate 	 * Iterate through the list of descriptors, stopping when we
33180Sstevel@tonic-gate 	 * run out of sense data.  Descriptor format is:
33190Sstevel@tonic-gate 	 *
33200Sstevel@tonic-gate 	 * <Descriptor type> <Descriptor length> <Descriptor data> ...
33210Sstevel@tonic-gate 	 */
33220Sstevel@tonic-gate 	while ((descr_offset + *(descr_offset + 1)) <=
33230Sstevel@tonic-gate 	    (uint8_t *)rq + valid_sense_length) {
33240Sstevel@tonic-gate 		/*
33250Sstevel@tonic-gate 		 * Determine descriptor type.  We can use the
33260Sstevel@tonic-gate 		 * scsi_information_sense_descr structure as a
33270Sstevel@tonic-gate 		 * template since the first two fields are always the
33280Sstevel@tonic-gate 		 * same.
33290Sstevel@tonic-gate 		 */
33300Sstevel@tonic-gate 		isd = (struct scsi_information_sense_descr *)descr_offset;
33310Sstevel@tonic-gate 		switch (isd->isd_descr_type) {
33320Sstevel@tonic-gate 		case DESCR_INFORMATION: {
33330Sstevel@tonic-gate 			uint64_t information;
33340Sstevel@tonic-gate 
33350Sstevel@tonic-gate 			information =
33360Sstevel@tonic-gate 			    (((uint64_t)isd->isd_information[0] << 56) |
33370Sstevel@tonic-gate 				((uint64_t)isd->isd_information[1] << 48) |
33380Sstevel@tonic-gate 				((uint64_t)isd->isd_information[2] << 40) |
33390Sstevel@tonic-gate 				((uint64_t)isd->isd_information[3] << 32) |
33400Sstevel@tonic-gate 				((uint64_t)isd->isd_information[4] << 24) |
33410Sstevel@tonic-gate 				((uint64_t)isd->isd_information[5] << 16) |
33420Sstevel@tonic-gate 				((uint64_t)isd->isd_information[6] << 8)  |
33430Sstevel@tonic-gate 				((uint64_t)isd->isd_information[7]));
33440Sstevel@tonic-gate 			fmt_print("Information field:               "
33450Sstevel@tonic-gate 			    "%0llx\n", information);
33460Sstevel@tonic-gate 			break;
33470Sstevel@tonic-gate 		}
33480Sstevel@tonic-gate 		case DESCR_COMMAND_SPECIFIC: {
33490Sstevel@tonic-gate 			struct scsi_cmd_specific_sense_descr *c =
33500Sstevel@tonic-gate 			    (struct scsi_cmd_specific_sense_descr *)isd;
33510Sstevel@tonic-gate 			uint64_t cmd_specific;
33520Sstevel@tonic-gate 
33530Sstevel@tonic-gate 			cmd_specific =
33540Sstevel@tonic-gate 			    (((uint64_t)c->css_cmd_specific_info[0] << 56) |
33550Sstevel@tonic-gate 				((uint64_t)c->css_cmd_specific_info[1] << 48) |
33560Sstevel@tonic-gate 				((uint64_t)c->css_cmd_specific_info[2] << 40) |
33570Sstevel@tonic-gate 				((uint64_t)c->css_cmd_specific_info[3] << 32) |
33580Sstevel@tonic-gate 				((uint64_t)c->css_cmd_specific_info[4] << 24) |
33590Sstevel@tonic-gate 				((uint64_t)c->css_cmd_specific_info[5] << 16) |
33600Sstevel@tonic-gate 				((uint64_t)c->css_cmd_specific_info[6] << 8)  |
33610Sstevel@tonic-gate 				((uint64_t)c->css_cmd_specific_info[7]));
33620Sstevel@tonic-gate 			fmt_print("Command-specific information:    "
33630Sstevel@tonic-gate 			    "%0llx\n", cmd_specific);
33640Sstevel@tonic-gate 			break;
33650Sstevel@tonic-gate 		}
33660Sstevel@tonic-gate 		case DESCR_SENSE_KEY_SPECIFIC: {
33670Sstevel@tonic-gate 			struct scsi_sk_specific_sense_descr *ssd =
33680Sstevel@tonic-gate 			    (struct scsi_sk_specific_sense_descr *)isd;
33690Sstevel@tonic-gate 			uint8_t *sk_spec_ptr = (uint8_t *)&ssd->sss_data;
33700Sstevel@tonic-gate 			fmt_print("Sense-key specific:              "
33710Sstevel@tonic-gate 			    "0x%02x 0x%02x 0x%02x\n", sk_spec_ptr[0],
33720Sstevel@tonic-gate 			    sk_spec_ptr[1], sk_spec_ptr[2]);
33730Sstevel@tonic-gate 			break;
33740Sstevel@tonic-gate 		}
33750Sstevel@tonic-gate 		case DESCR_FRU: {
33760Sstevel@tonic-gate 			struct scsi_fru_sense_descr *fsd =
33770Sstevel@tonic-gate 			    (struct scsi_fru_sense_descr *)isd;
33780Sstevel@tonic-gate 			fmt_print("Field replaceable unit code:     "
33790Sstevel@tonic-gate 			    "%d\n", fsd->fs_fru_code);
33800Sstevel@tonic-gate 			break;
33810Sstevel@tonic-gate 		}
33820Sstevel@tonic-gate 		case DESCR_BLOCK_COMMANDS: {
33830Sstevel@tonic-gate 			struct scsi_block_cmd_sense_descr *bsd =
33840Sstevel@tonic-gate 			    (struct scsi_block_cmd_sense_descr *)isd;
33850Sstevel@tonic-gate 			fmt_print("Incorrect length indicator:      "
33860Sstevel@tonic-gate 			    "%s\n", bsd->bcs_ili ? "yes" : "no");
33870Sstevel@tonic-gate 			break;
33880Sstevel@tonic-gate 		}
33890Sstevel@tonic-gate 		default:
33900Sstevel@tonic-gate 			/* Ignore */
33910Sstevel@tonic-gate 			break;
33920Sstevel@tonic-gate 		}
33930Sstevel@tonic-gate 
33940Sstevel@tonic-gate 		/*
33950Sstevel@tonic-gate 		 * Get pointer to the next descriptor.  The "additional
33960Sstevel@tonic-gate 		 * length" field holds the length of the descriptor except
33970Sstevel@tonic-gate 		 * for the "type" and "additional length" fields, so
33980Sstevel@tonic-gate 		 * we need to add 2 to get the total length.
33990Sstevel@tonic-gate 		 */
34000Sstevel@tonic-gate 		descr_offset += (isd->isd_addl_length + 2);
34010Sstevel@tonic-gate 	}
34020Sstevel@tonic-gate 
34030Sstevel@tonic-gate 	fmt_print("\n");
34040Sstevel@tonic-gate }
34050Sstevel@tonic-gate 
34060Sstevel@tonic-gate /*
34070Sstevel@tonic-gate  * Function checks if READ DEFECT DATA command is supported
34080Sstevel@tonic-gate  * on the current disk.
34090Sstevel@tonic-gate  */
34100Sstevel@tonic-gate static int
check_support_for_defects()34110Sstevel@tonic-gate check_support_for_defects()
34120Sstevel@tonic-gate {
34130Sstevel@tonic-gate 	struct uscsi_cmd	ucmd;
34140Sstevel@tonic-gate 	union scsi_cdb		cdb;
34150Sstevel@tonic-gate 	struct scsi_defect_list	def_list;
34160Sstevel@tonic-gate 	struct scsi_defect_hdr	*hdr;
34170Sstevel@tonic-gate 	int			status;
34180Sstevel@tonic-gate 	char			rqbuf[255];
34190Sstevel@tonic-gate 	struct scsi_extended_sense	*rq;
34200Sstevel@tonic-gate 
34210Sstevel@tonic-gate 	hdr = (struct scsi_defect_hdr *)&def_list;
34220Sstevel@tonic-gate 
34230Sstevel@tonic-gate 	/*
34240Sstevel@tonic-gate 	 * First get length of list by asking for the header only.
34250Sstevel@tonic-gate 	 */
34260Sstevel@tonic-gate 	(void) memset((char *)&def_list, 0, sizeof (def_list));
34270Sstevel@tonic-gate 
34280Sstevel@tonic-gate 	/*
34290Sstevel@tonic-gate 	 * Build and execute the uscsi ioctl
34300Sstevel@tonic-gate 	 */
34310Sstevel@tonic-gate 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
34320Sstevel@tonic-gate 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
34330Sstevel@tonic-gate 	(void) memset((char *)rqbuf, 0, 255);
34340Sstevel@tonic-gate 	cdb.scc_cmd = SCMD_READ_DEFECT_LIST;
34350Sstevel@tonic-gate 	FORMG1COUNT(&cdb, sizeof (struct scsi_defect_hdr));
34360Sstevel@tonic-gate 	cdb.cdb_opaque[2] = DLD_MAN_DEF_LIST | DLD_BFI_FORMAT;
34370Sstevel@tonic-gate 	ucmd.uscsi_cdb = (caddr_t)&cdb;
34380Sstevel@tonic-gate 	ucmd.uscsi_cdblen = CDB_GROUP1;
34390Sstevel@tonic-gate 	ucmd.uscsi_bufaddr = (caddr_t)hdr;
34400Sstevel@tonic-gate 	ucmd.uscsi_buflen = sizeof (struct scsi_defect_hdr);
34410Sstevel@tonic-gate 	ucmd.uscsi_rqbuf = rqbuf;
34420Sstevel@tonic-gate 	ucmd.uscsi_rqlen = sizeof (rqbuf);
34430Sstevel@tonic-gate 	ucmd.uscsi_rqresid = sizeof (rqbuf);
34440Sstevel@tonic-gate 	rq = (struct scsi_extended_sense *)ucmd.uscsi_rqbuf;
34450Sstevel@tonic-gate 
34460Sstevel@tonic-gate 	status = uscsi_cmd(cur_file, &ucmd,
34479889SLarry.Liu@Sun.COM 	    (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
34480Sstevel@tonic-gate 
34490Sstevel@tonic-gate 	if (status != 0) {
34500Sstevel@tonic-gate 		/*
34510Sstevel@tonic-gate 		 * check if read_defect_list_is_supported.
34520Sstevel@tonic-gate 		 */
34530Sstevel@tonic-gate 		if (ucmd.uscsi_rqstatus == STATUS_GOOD &&
34549889SLarry.Liu@Sun.COM 		    rq->es_key == KEY_ILLEGAL_REQUEST &&
34559889SLarry.Liu@Sun.COM 		    rq->es_add_code == INVALID_OPCODE)
34569889SLarry.Liu@Sun.COM 			return (0);
34570Sstevel@tonic-gate 	}
34580Sstevel@tonic-gate 	return (1);
34590Sstevel@tonic-gate }
34600Sstevel@tonic-gate 
34610Sstevel@tonic-gate /*
34620Sstevel@tonic-gate  * Format the disk, the whole disk, and nothing but the disk.
34630Sstevel@tonic-gate  * Function will be called only for disks
34640Sstevel@tonic-gate  * which do not support read defect list command.
34650Sstevel@tonic-gate  */
34660Sstevel@tonic-gate static int
scsi_format_without_defects()34670Sstevel@tonic-gate scsi_format_without_defects()
34680Sstevel@tonic-gate {
34690Sstevel@tonic-gate 	struct uscsi_cmd	ucmd;
34700Sstevel@tonic-gate 	union scsi_cdb		cdb;
34710Sstevel@tonic-gate 	struct scsi_defect_hdr	defect_hdr;
34720Sstevel@tonic-gate 	int			status;
34730Sstevel@tonic-gate 
34740Sstevel@tonic-gate 	/*
34750Sstevel@tonic-gate 	 * Construct the uscsi format ioctl.
34760Sstevel@tonic-gate 	 * Use fmtdata = 0 , indicating the no source of
34770Sstevel@tonic-gate 	 * defects information is provided .
34780Sstevel@tonic-gate 	 * Function will be called only for disks
34790Sstevel@tonic-gate 	 * which do not support read defect list command.
34800Sstevel@tonic-gate 	 */
34810Sstevel@tonic-gate 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
34820Sstevel@tonic-gate 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
34830Sstevel@tonic-gate 	(void) memset((char *)&defect_hdr, 0, sizeof (defect_hdr));
34840Sstevel@tonic-gate 	cdb.scc_cmd = SCMD_FORMAT;
34850Sstevel@tonic-gate 	ucmd.uscsi_cdb = (caddr_t)&cdb;
34860Sstevel@tonic-gate 	ucmd.uscsi_cdblen = CDB_GROUP0;
34870Sstevel@tonic-gate 	ucmd.uscsi_bufaddr = (caddr_t)&defect_hdr;
34880Sstevel@tonic-gate 	ucmd.uscsi_buflen = sizeof (defect_hdr);
34890Sstevel@tonic-gate 	cdb.cdb_opaque[1] = 0;
34900Sstevel@tonic-gate 	/*
34910Sstevel@tonic-gate 	 * Issue the format ioctl
34920Sstevel@tonic-gate 	 */
34930Sstevel@tonic-gate 	status = uscsi_cmd(cur_file, &ucmd,
34949889SLarry.Liu@Sun.COM 	    (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
34950Sstevel@tonic-gate 	return (status);
34960Sstevel@tonic-gate }
34970Sstevel@tonic-gate 
34980Sstevel@tonic-gate /*
34990Sstevel@tonic-gate  * Name: test_until_ready
35000Sstevel@tonic-gate  *
35010Sstevel@tonic-gate  * Description: This function is used by scsi_format and
35020Sstevel@tonic-gate  *   scsi_format_raw to poll the device while the FORMAT
35030Sstevel@tonic-gate  *   UNIT cdb in in progress.
35040Sstevel@tonic-gate  *
35050Sstevel@tonic-gate  * Parameters:
35060Sstevel@tonic-gate  *   file descriptor to poll
35070Sstevel@tonic-gate  *
35080Sstevel@tonic-gate  * Returns:
35090Sstevel@tonic-gate  *   0 - good status
35100Sstevel@tonic-gate  *   !0 - bad status
35110Sstevel@tonic-gate  */
test_until_ready(int fd)35120Sstevel@tonic-gate static int test_until_ready(int fd) {
35130Sstevel@tonic-gate 	int				status = 1;
35140Sstevel@tonic-gate 	struct uscsi_cmd		ucmd;
35150Sstevel@tonic-gate 	union scsi_cdb			cdb;
35160Sstevel@tonic-gate 	struct scsi_extended_sense	sense;
35170Sstevel@tonic-gate 	time_t				start, check, time_left;
35180Sstevel@tonic-gate 	uint16_t 			progress;
35190Sstevel@tonic-gate 	int				hour, min, sec;
35200Sstevel@tonic-gate 
35210Sstevel@tonic-gate 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
35220Sstevel@tonic-gate 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
35230Sstevel@tonic-gate 
35240Sstevel@tonic-gate 	ucmd.uscsi_cdb		= (caddr_t)&cdb;
35250Sstevel@tonic-gate 	ucmd.uscsi_cdblen	= CDB_GROUP0;
35260Sstevel@tonic-gate 	ucmd.uscsi_rqbuf	= (caddr_t)&sense;
35270Sstevel@tonic-gate 	ucmd.uscsi_rqlen	= SENSE_LEN;
35280Sstevel@tonic-gate 
35290Sstevel@tonic-gate 	start = check = time((time_t *)0);
35300Sstevel@tonic-gate 
35310Sstevel@tonic-gate 	/* Loop sending TEST UNIT READY until format is complete */
35320Sstevel@tonic-gate 	while (status) {
35330Sstevel@tonic-gate 		/* clear last request sense data */
35340Sstevel@tonic-gate 		ucmd.uscsi_rqstatus	= 0;
35350Sstevel@tonic-gate 		ucmd.uscsi_rqresid	= 0;
35360Sstevel@tonic-gate 		(void) memset((char *)&sense, 0, SENSE_LEN);
35370Sstevel@tonic-gate 
35380Sstevel@tonic-gate 		/* issue test unit ready */
35390Sstevel@tonic-gate 		status = uscsi_cmd(fd, &ucmd, F_SILENT
35400Sstevel@tonic-gate 				| F_RQENABLE);
35410Sstevel@tonic-gate 
35420Sstevel@tonic-gate 		check = time((time_t *)0);
35430Sstevel@tonic-gate 
35440Sstevel@tonic-gate 		/* If device returns not ready we get EIO */
35450Sstevel@tonic-gate 		if (status != 0 && errno == EIO) {
35460Sstevel@tonic-gate 			/* Check SKSV if progress indication is avail */
35470Sstevel@tonic-gate 			if (sense.es_skey_specific[0] == 0x80) {
35480Sstevel@tonic-gate 				/* Store progress indication */
35490Sstevel@tonic-gate 				progress = ((uint16_t)sense.
35500Sstevel@tonic-gate 					es_skey_specific[1]) << 8;
35510Sstevel@tonic-gate 				progress |= (uint16_t)sense.
35520Sstevel@tonic-gate 					es_skey_specific[2];
35530Sstevel@tonic-gate 				progress = (uint16_t)(((float)progress /
35540Sstevel@tonic-gate 					(float)PROGRESS_INDICATION_BASE)*100);
35550Sstevel@tonic-gate 
35560Sstevel@tonic-gate 				fmt_print("\015");
35570Sstevel@tonic-gate 
35580Sstevel@tonic-gate 				/*
35590Sstevel@tonic-gate 				 * check to see if we can estimate
35600Sstevel@tonic-gate 				 * time remaining  - wait until the format
35610Sstevel@tonic-gate 				 * is at least 5 percent complete to avoid
35620Sstevel@tonic-gate 				 * wildly-fluctuating time estimates
35630Sstevel@tonic-gate 				 */
35640Sstevel@tonic-gate 				if ((check - start) <= 0 || progress <= 5) {
35650Sstevel@tonic-gate 					/* unable to estimate */
35660Sstevel@tonic-gate 					fmt_print("  %02d%% complete ",
35670Sstevel@tonic-gate 						progress);
35680Sstevel@tonic-gate 				} else {
35690Sstevel@tonic-gate 					/* display with estimated time */
35700Sstevel@tonic-gate 					time_left = (time_t)(((float)(check
35710Sstevel@tonic-gate 						- start) / (float)progress) *
35720Sstevel@tonic-gate 						(float)(100 - progress));
35730Sstevel@tonic-gate 					sec = time_left % 60;
35740Sstevel@tonic-gate 					min = (time_left / 60) % 60;
35750Sstevel@tonic-gate 					hour = time_left / 3600;
35760Sstevel@tonic-gate 
35770Sstevel@tonic-gate 					fmt_print("  %02d%% complete "
35780Sstevel@tonic-gate 						"(%02d:%02d:%02d remaining) ",
35790Sstevel@tonic-gate 						progress, hour, min, sec);
35800Sstevel@tonic-gate 				}
35810Sstevel@tonic-gate 				/* flush or the screen will not update */
35820Sstevel@tonic-gate 				(void) fflush(stdout);
35830Sstevel@tonic-gate 			}
35840Sstevel@tonic-gate 		} else {
35850Sstevel@tonic-gate 			/* format not in progress */
35860Sstevel@tonic-gate 			if (option_msg) {
35870Sstevel@tonic-gate 			fmt_print("\nRequest Sense ASC=0x%x ASCQ=0x%x",
35880Sstevel@tonic-gate 				sense.es_add_code, sense.es_qual_code);
35890Sstevel@tonic-gate 			}
35900Sstevel@tonic-gate 			break;
35910Sstevel@tonic-gate 		}
35920Sstevel@tonic-gate 
35930Sstevel@tonic-gate 		/* delay so we don't waste cpu time */
35940Sstevel@tonic-gate 		(void) sleep(RETRY_DELAY);
35950Sstevel@tonic-gate 	}
35960Sstevel@tonic-gate 	return (status);
35970Sstevel@tonic-gate }
359812594SShengliang.Zhang@Sun.COM 
359912594SShengliang.Zhang@Sun.COM /*
360012594SShengliang.Zhang@Sun.COM  * Get the current protection type from the PROT_EN and P_TYPE
360112594SShengliang.Zhang@Sun.COM  */
360212594SShengliang.Zhang@Sun.COM uint8_t
get_cur_protection_type(struct scsi_capacity_16 * capacity)360312594SShengliang.Zhang@Sun.COM get_cur_protection_type(struct scsi_capacity_16 *capacity)
360412594SShengliang.Zhang@Sun.COM {
360512594SShengliang.Zhang@Sun.COM 	uint8_t	cp13;
360612594SShengliang.Zhang@Sun.COM 	uint8_t	prot_en;
360712594SShengliang.Zhang@Sun.COM 	uint8_t	p_type;
360812594SShengliang.Zhang@Sun.COM 
360912594SShengliang.Zhang@Sun.COM 	cp13 = ((capacity->sc_rsvd0 & 0x3f) << 2)
361012594SShengliang.Zhang@Sun.COM 	    | ((capacity->sc_prot_en & 0x01) << 1)
361112594SShengliang.Zhang@Sun.COM 	    | (capacity->sc_rto_en & 0x01);
361212594SShengliang.Zhang@Sun.COM 	prot_en = cp13 & 0x01;
361312594SShengliang.Zhang@Sun.COM 	if (prot_en == 0) {
361412594SShengliang.Zhang@Sun.COM 		p_type = 0;
361512594SShengliang.Zhang@Sun.COM 	} else {
361612594SShengliang.Zhang@Sun.COM 		p_type = (cp13 << 4) >> 5;
361712594SShengliang.Zhang@Sun.COM 		p_type += 1;
361812594SShengliang.Zhang@Sun.COM 	}
361912594SShengliang.Zhang@Sun.COM 	return (p_type);
362012594SShengliang.Zhang@Sun.COM }
3621