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