Move /var/svc/log to /var/log/svc
[unleashed/lotheac.git] / usr / src / cmd / format / ctlr_scsi.c
blob49d4afb77eb31a39eb28cf33d843edd09a5fe856
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright (c) 2016 by Delphix. All rights reserved.
27 * This file contains the routines for embedded scsi disks
29 #include "global.h"
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>
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"
55 #ifndef DAD_MODE_CACHE_CCS
56 #define DAD_MODE_CACHE_CCS 0x38
57 #endif /* DAD_MODE_CACHE_CCS */
59 /* format defect header bits */
60 #define FDH_FOV 0x80
61 #define FDH_IMMED 0x02
63 #define SENSE_LEN 20
65 #define RETRY_DELAY 5
67 #define PROGRESS_INDICATION_BASE 65536
69 #ifdef __STDC__
71 * Local prototypes for ANSI C compilers
73 static int scsi_format(uint64_t, uint64_t, struct defect_list *);
74 static int scsi_raw_format(void);
75 static int scsi_ms_page8(int);
76 static int scsi_ms_page38(int);
77 static void scsi_convert_list_to_new(struct defect_list *,
78 struct scsi_defect_list *, int);
79 static char *scsi_find_command_name(uint_t);
80 static int chg_list_affects_page(struct chg_list *, int);
81 static void scsi_printerr(struct uscsi_cmd *,
82 struct scsi_extended_sense *, int);
83 static diskaddr_t
84 scsi_extract_sense_info_descr(struct scsi_descr_sense_hdr *sdsp, int rqlen);
86 static void scsi_print_extended_sense(struct scsi_extended_sense *, int);
87 static void scsi_print_descr_sense(struct scsi_descr_sense_hdr *, int);
89 static int test_until_ready(int fd);
90 static int uscsi_reserve_release(int, int);
91 static int check_support_for_defects(void);
92 static int scsi_format_without_defects(void);
93 static int scsi_ms_page1(int);
94 static int scsi_ms_page2(int);
95 static int scsi_ms_page3(int);
96 static int scsi_ms_page4(int);
97 static int scsi_repair(uint64_t, int);
98 static int scsi_read_defect_data(struct defect_list *, int);
99 static int scsi_ck_format(void);
101 #else /* __STDC__ */
103 static int scsi_format();
104 static int scsi_raw_format();
105 static int scsi_ms_page8();
106 static int scsi_ms_page38();
107 static void scsi_convert_list_to_new();
108 static char *scsi_find_command_name();
109 static int chg_list_affects_page();
110 static void scsi_printerr();
111 static diskaddr_t scsi_extract_sense_info_descr();
112 static void scsi_print_extended_sense();
113 static void scsi_print_descr_sense();
115 static int test_until_ready();
117 static int uscsi_reserve_release();
118 static int check_support_for_defects();
119 static int scsi_format_without_defects();
120 static int scsi_ms_page1();
121 static int scsi_ms_page2();
122 static int scsi_ms_page3();
123 static int scsi_ms_page4();
124 static int scsi_repair();
125 static int scsi_read_defect_data();
126 static int scsi_ck_format();
128 #endif /* __STDC__ */
132 struct ctlr_ops scsiops = {
133 scsi_rdwr,
134 scsi_ck_format,
135 scsi_format,
136 scsi_ex_man,
137 scsi_ex_cur,
138 scsi_repair,
142 #define SCMD_UNKNOWN 0xff
145 * Names of commands. Must have SCMD_UNKNOWN at end of list.
147 static struct scsi_command_name {
148 uchar_t command;
149 char *name;
150 } scsi_command_names[] = {
151 SCMD_FORMAT, "format",
152 SCMD_READ, "read",
153 SCMD_WRITE, "write",
154 SCMD_READ|SCMD_GROUP1, "read",
155 SCMD_WRITE|SCMD_GROUP1, "write",
156 SCMD_INQUIRY, "inquiry",
157 SCMD_MODE_SELECT, "mode select",
158 SCMD_MODE_SENSE, "mode sense",
159 SCMD_REASSIGN_BLOCK, "reassign block",
160 SCMD_READ_DEFECT_LIST, "read defect list",
161 SCMD_UNKNOWN, "unknown"
166 * Strings for printing mode sense page control values
168 static slist_t page_control_strings[] = {
169 { "current", "", MODE_SENSE_PC_CURRENT },
170 { "changeable", "", MODE_SENSE_PC_CHANGEABLE },
171 { "default", "", MODE_SENSE_PC_DEFAULT },
172 { "saved", "", MODE_SENSE_PC_SAVED }
176 * Strings for printing the mode select options
178 static slist_t mode_select_strings[] = {
179 { "", "", 0 },
180 { " (pf)", "", MODE_SELECT_PF },
181 { " (sp)", "", MODE_SELECT_SP },
182 { " (pf,sp)", "", MODE_SELECT_PF|MODE_SELECT_SP }
185 static int scsi_format_revolutions = 5;
186 static int scsi_format_timeout = 2*60*60; /* two hours */
189 * READ DEFECT DATA commands is optional as per SCSI-2 spec.
190 * Hence check if the read_defect_data command fails with
191 * Invalid Opcode so that we can give a more meaningful message
192 * to the user.
194 #define INVALID_OPCODE 0x20
197 * Read or write the disk.
200 scsi_rdwr(dir, fd, blkno, secnt, bufaddr, flags, xfercntp)
201 int dir;
202 int fd;
203 diskaddr_t blkno;
204 int secnt;
205 caddr_t bufaddr;
206 int flags;
207 int *xfercntp;
209 struct uscsi_cmd ucmd;
210 union scsi_cdb cdb;
211 int max_sectors;
212 int rc = 0;
215 * If the max xfercnt hasn't been determined start with BUF_SECTS
216 * (currently 126 == 63K), otherwise use the xfercnt value
217 * my caller saved from the previous invocation.
219 if (xfercntp == NULL) {
220 max_sectors = BUF_SECTS;
221 } else if (*xfercntp == 0) {
222 max_sectors = BUF_SECTS;
223 *xfercntp = max_sectors;
224 } else {
225 max_sectors = *xfercntp;
229 * Build and execute the uscsi ioctl. We build a group0
230 * or group1 command as necessary, since some targets
231 * do not support group1 commands.
233 while (secnt) {
234 int nsectors;
236 nsectors = (max_sectors < secnt) ? max_sectors : secnt;
237 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
238 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
239 cdb.scc_cmd = (dir == DIR_READ) ? SCMD_READ : SCMD_WRITE;
240 if (blkno < (2<<20) && nsectors <= 0xff) {
241 FORMG0ADDR(&cdb, blkno);
242 FORMG0COUNT(&cdb, nsectors);
243 ucmd.uscsi_cdblen = CDB_GROUP0;
244 } else {
245 if (blkno > 0xffffffff) {
246 FORMG4LONGADDR(&cdb, blkno);
247 FORMG4COUNT(&cdb, nsectors);
248 ucmd.uscsi_cdblen = CDB_GROUP4;
249 cdb.scc_cmd |= SCMD_GROUP4;
250 } else {
251 FORMG1ADDR(&cdb, blkno);
252 FORMG1COUNT(&cdb, nsectors);
253 ucmd.uscsi_cdblen = CDB_GROUP1;
254 cdb.scc_cmd |= SCMD_GROUP1;
257 ucmd.uscsi_cdb = (caddr_t)&cdb;
258 ucmd.uscsi_bufaddr = bufaddr;
259 ucmd.uscsi_buflen = nsectors * cur_blksz;
260 rc = uscsi_cmd(fd, &ucmd, flags);
262 if (rc != 0)
263 break;
266 * check if partial DMA breakup required
267 * if so, reduce the request size by half and retry
268 * the last request
270 if (ucmd.uscsi_resid == ucmd.uscsi_buflen) {
271 max_sectors >>= 1;
272 if (max_sectors <= 0) {
273 rc = -1;
274 break;
276 continue;
278 if (ucmd.uscsi_resid != 0) {
279 rc = -1;
280 break;
283 blkno += nsectors;
284 secnt -= nsectors;
285 bufaddr += nsectors * cur_blksz;
289 * If the xfercnt wasn't previously saved or if the
290 * new value is smaller than the old value, save the
291 * current value in my caller's save area.
293 if (xfercntp != NULL && max_sectors < *xfercntp) {
294 if (diag_msg)
295 err_print("reducing xfercnt %d %d\n",
296 *xfercntp, max_sectors);
297 *xfercntp = max_sectors;
299 return (rc);
304 * Check to see if the disk has been formatted.
305 * If we are able to read the first track, we conclude that
306 * the disk has been formatted.
308 #ifdef i386
309 static int
310 #else /* i386 */
311 static int
312 #endif /* i386 */
313 scsi_ck_format(void)
315 int status;
318 * Try to read the first four blocks.
320 status = scsi_rdwr(DIR_READ, cur_file, (diskaddr_t)0, 4,
321 (caddr_t)cur_buf, F_SILENT, NULL);
322 return (!status);
327 * Format the disk, the whole disk, and nothing but the disk.
329 /*ARGSUSED*/
330 static int
331 scsi_format(start, end, list)
332 uint64_t start; /* irrelevant for us */
333 uint64_t end;
334 struct defect_list *list;
336 struct uscsi_cmd ucmd;
337 union scsi_cdb cdb;
338 int status;
339 int flag;
340 char rawbuf[MAX_MODE_SENSE_SIZE];
341 struct scsi_inquiry *inq;
342 uint8_t fmt_prot_info;
343 uint8_t prot_field_usage;
344 uint8_t param_long_list = 1;
345 uint8_t fmt_long_param_header[8];
348 * Determine if the target appears to be SCSI-2
349 * compliant. We handle mode sense/mode selects
350 * a little differently, depending upon CCS/SCSI-2
352 if (uscsi_inquiry(cur_file, rawbuf, sizeof (rawbuf))) {
353 err_print("Inquiry failed\n");
354 return (-1);
356 inq = (struct scsi_inquiry *)rawbuf;
357 flag = (inq->inq_rdf == RDF_SCSI2);
360 * Reserve the scsi disk before performing mode select and
361 * format operations. This will keep other hosts, if any, from
362 * touching the disk while we are here.
364 if (uscsi_reserve_release(cur_file, SCMD_RESERVE)) {
365 err_print("Reserve failed\n");
366 return (-1);
370 * Set up the various SCSI parameters specified before
371 * formatting the disk. Each routine handles the
372 * parameters relevant to a particular page.
373 * If no parameters are specified for a page, there's
374 * no need to do anything. Otherwise, issue a mode
375 * sense for that page. If a specified parameter
376 * differs from the drive's default value, and that
377 * parameter is not fixed, then issue a mode select to
378 * set the default value for the disk as specified
379 * in format.dat.
381 if (scsi_ms_page1(flag) || scsi_ms_page2(flag) ||
382 scsi_ms_page4(flag) || scsi_ms_page38(flag) ||
383 scsi_ms_page8(flag) || scsi_ms_page3(flag)) {
384 (void) uscsi_reserve_release(cur_file, SCMD_RELEASE);
385 return (-1);
389 * If we're debugging the drive, dump every page
390 * the device supports, for thorough analysis.
392 if (option_msg && diag_msg) {
393 (void) scsi_dump_mode_sense_pages(MODE_SENSE_PC_DEFAULT);
394 (void) scsi_dump_mode_sense_pages(MODE_SENSE_PC_CURRENT);
395 (void) scsi_dump_mode_sense_pages(MODE_SENSE_PC_SAVED);
396 (void) scsi_dump_mode_sense_pages(MODE_SENSE_PC_CHANGEABLE);
397 err_print("\n");
401 * Determine the FMTPINFO field in format cdb, and the
402 * PROTECTION FIELD USAGE in the long parameter list, via
403 * the protection type input by users.
405 switch (prot_type) {
406 case PROT_TYPE_0:
407 fmt_prot_info = 0x00;
408 prot_field_usage = 0x00;
409 break;
410 case PROT_TYPE_1:
411 fmt_prot_info = 0x02;
412 prot_field_usage = 0x00;
413 break;
414 case PROT_TYPE_2:
415 fmt_prot_info = 0x03;
416 prot_field_usage = 0x00;
417 break;
418 case PROT_TYPE_3:
419 fmt_prot_info = 0x03;
420 prot_field_usage = 0x01;
421 break;
422 default:
423 fmt_print("invalid protection type\n");
424 return (-1);
428 * Construct the uscsi format ioctl. The form depends
429 * upon the defect list the user extracted. If they
430 * extracted the "original" list, we format with only
431 * the P (manufacturer's defect) list. Otherwise, we
432 * format with both the P and the G (grown) list.
433 * To format with the P and G list, we set the fmtData
434 * bit, and send an empty list. To format with the
435 * P list only, we also set the cmpLst bit, meaning
436 * that the (empty) list we send down is the complete
437 * G list, thereby discarding the old G list..
439 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
440 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
442 cdb.scc_cmd = SCMD_FORMAT;
443 ucmd.uscsi_cdb = (caddr_t)&cdb;
444 ucmd.uscsi_cdblen = CDB_GROUP0;
445 cdb.cdb_opaque[1] = FPB_DATA;
448 * Use the long parameter header in format command,
449 * and set the FMTPINFO field., when type 1, 2, 3.
451 cdb.cdb_opaque[1] |= (param_long_list << 5) | (fmt_prot_info << 6);
452 (void) memset((char *)fmt_long_param_header, 0,
453 sizeof (fmt_long_param_header));
456 * Set the PROTECTION FIELD USAGE field in the long
457 * parameter list header, which combines with FMTINFO to
458 * determine the protection type.
459 * The PROTECTION INTERVAL EXPONET field is set default 0.
460 * So only one protection information interval is used
461 * in type 1, 2, 3.
463 fmt_long_param_header[0] = prot_field_usage;
464 fmt_long_param_header[1] = FDH_FOV | FDH_IMMED;
465 ucmd.uscsi_bufaddr = (caddr_t)fmt_long_param_header;
466 ucmd.uscsi_buflen = sizeof (fmt_long_param_header);
468 if ((list->list != NULL) && ((list->flags & LIST_PGLIST) == 0)) {
470 * No G list. The empty list we send down
471 * is the complete list.
473 cdb.cdb_opaque[1] |= FPB_CMPLT;
477 * Issue the format ioctl
479 fmt_print("Formatting...\n");
480 (void) fflush(stdout);
481 status = uscsi_cmd(cur_file, &ucmd,
482 (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
484 /* check if format with immed was successfully accepted */
485 if (status == 0) {
486 /* immed accepted poll to completion */
487 status = test_until_ready(cur_file);
488 } else {
489 /* clear FOV and try again */
490 (void) memset((char *)fmt_long_param_header, 0,
491 sizeof (fmt_long_param_header));
492 fmt_long_param_header[0] = prot_field_usage;
493 fmt_long_param_header[1] = FDH_IMMED;
494 status = uscsi_cmd(cur_file, &ucmd,
495 (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
496 if (status == 0) {
497 /* immed accepted, poll for progress */
498 status = test_until_ready(cur_file);
499 } else {
501 * clear defect header and try basecase format
502 * command will hang until format complete
504 (void) memset((char *)fmt_long_param_header, 0,
505 sizeof (fmt_long_param_header));
506 fmt_long_param_header[0] = prot_field_usage;
507 status = uscsi_cmd(cur_file, &ucmd,
508 (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
512 /* format failure check */
513 if (status != 0) {
515 * formatting failed with fmtdata = 1.
516 * Check if defects list command is supported, if it
517 * is not supported then use fmtdata = 0.
518 * From SCSI Spec
519 * A FmtData bit of zero indicates, the
520 * source of defect information is not specified.
521 * else
522 * proceed to format using with mode selects.
524 if (!(check_support_for_defects())) {
525 status = scsi_format_without_defects();
528 if (status != 0) {
529 fmt_print("Format failed\n");
530 status = scsi_raw_format();
533 (void) uscsi_reserve_release(cur_file, SCMD_RELEASE);
534 return (status);
538 * Format without any of the standard mode selects ignoring Grown defects list.
540 static int
541 scsi_raw_format(void)
543 struct uscsi_cmd ucmd;
544 union scsi_cdb cdb;
545 struct scsi_defect_hdr defect_hdr;
546 int status;
548 fmt_print("\n"
549 "Retry of formatting operation without any of the standard\n"
550 "mode selects and ignoring disk's Grown Defects list. The\n"
551 "disk may be able to be reformatted this way if an earlier\n"
552 "formatting operation was interrupted by a power failure or\n"
553 "SCSI bus reset. The Grown Defects list will be recreated\n"
554 "by format verification and surface analysis.\n\n");
556 if (check("Retry format without mode selects and Grown Defects list")
557 != 0) {
558 return (-1);
562 * Construct the uscsi format ioctl.
563 * To format with the P and G list, we set the fmtData
564 * and cmpLst bits to zero. To format with just the
565 * P list, we set the fmtData bit (meaning that we will
566 * send down a defect list in the data phase) and the
567 * cmpLst bit (meaning that the list we send is the
568 * complete G list), and a defect list header with
569 * a defect list length of zero.
571 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
572 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
573 (void) memset((char *)&defect_hdr, 0, sizeof (defect_hdr));
575 cdb.scc_cmd = SCMD_FORMAT;
576 ucmd.uscsi_cdb = (caddr_t)&cdb;
577 ucmd.uscsi_cdblen = CDB_GROUP0;
578 /* No G list. Send empty defect list to replace it */
579 cdb.cdb_opaque[1] = FPB_DATA | FPB_CMPLT | FPB_BFI;
580 ucmd.uscsi_bufaddr = (caddr_t)&defect_hdr;
581 ucmd.uscsi_buflen = sizeof (defect_hdr);
582 defect_hdr.descriptor = FDH_FOV | FDH_IMMED;
585 * Issue the format ioctl
587 fmt_print("Formatting...\n");
588 (void) fflush(stdout);
589 status = uscsi_cmd(cur_file, &ucmd, F_NORMAL);
591 /* check if format with immed was successfully accepted */
592 if (status == 0) {
593 /* immed accepted pool to completion */
594 status = test_until_ready(cur_file);
595 } else {
596 /* clear defect header and try basecase format */
597 (void) memset((char *)&defect_hdr, 0, sizeof (defect_hdr));
598 status = uscsi_cmd(cur_file, &ucmd, F_NORMAL);
601 /* fmt_print(status ? "Format failed\n\n" : "Format ok\n\n"); */
602 return (status);
606 * Estimate the time required for format operation (See 1163770).
607 * format time = (5_revs * p4_heads * p4_cylinders) / p4_rpm
608 * 5 revolutions (correspond to format_time keyword in format.dat file) are:
609 * 1 rev. for positioning
610 * 2 rev. for writing the track
611 * 1 rev. for positioning
612 * 1 rev. for cerifying the data integrity of the track
613 * The return value is a good estimate on the formatting time in minutes.
614 * Caller should add 50% margin to cover defect management overhead.
617 scsi_format_time()
619 struct mode_geometry *page4;
620 struct scsi_ms_header header;
621 int status;
622 int p4_cylinders, p4_heads, p4_rpm;
623 int length;
624 int format_time;
625 union {
626 struct mode_geometry page4;
627 char rawbuf[MAX_MODE_SENSE_SIZE];
628 } u_page4;
631 page4 = &u_page4.page4;
632 (void) memset(&u_page4, 0, sizeof (u_page4));
635 * Issue a mode sense to determine the default parameters
636 * If it fail, try to use the saved or current instead.
638 status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
639 MODE_SENSE_PC_DEFAULT, (caddr_t)page4,
640 MAX_MODE_SENSE_SIZE, &header);
642 if (status) {
643 status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
644 MODE_SENSE_PC_SAVED, (caddr_t)page4,
645 MAX_MODE_SENSE_SIZE, &header);
647 if (status) {
648 status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
649 MODE_SENSE_PC_CURRENT, (caddr_t)page4,
650 MAX_MODE_SENSE_SIZE, &header);
652 if (status) {
653 return (0);
657 * We only need the common subset between the CCS
658 * and SCSI-2 structures, so we can treat both
659 * cases identically.
661 length = MODESENSE_PAGE_LEN(page4);
662 if (length < MIN_PAGE4_LEN) {
663 return (0);
666 page4->rpm = BE_16(page4->rpm);
667 p4_cylinders = (page4->cyl_ub << 16) + (page4->cyl_mb << 8) +
668 page4->cyl_lb;
669 p4_heads = page4->heads;
670 p4_rpm = page4->rpm;
673 * Some drives report 0 for page4->rpm, adjust it to AVG_RPM, 3600.
675 if (p4_rpm < MIN_RPM || p4_rpm > MAX_RPM) {
676 err_print("Mode sense page(4) reports rpm value as %d,"
677 " adjusting it to %d\n", p4_rpm, AVG_RPM);
678 p4_rpm = AVG_RPM;
681 if (p4_cylinders <= 0 || p4_heads <= 0)
682 return (0);
684 format_time = ((scsi_format_revolutions * p4_heads *
685 p4_cylinders) + p4_rpm) / p4_rpm;
687 if (option_msg && diag_msg) {
688 err_print(" pcyl: %d\n", p4_cylinders);
689 err_print(" heads: %d\n", p4_heads);
690 err_print(" rpm: %d\n", p4_rpm);
691 err_print("format_time: %d minutes\n", format_time);
693 return (format_time);
697 * Check disk error recovery parameters via mode sense.
698 * Issue a mode select if we need to change something.
700 /*ARGSUSED*/
701 static int
702 scsi_ms_page1(scsi2_flag)
703 int scsi2_flag;
705 struct mode_err_recov *page1;
706 struct mode_err_recov *fixed;
707 struct scsi_ms_header header;
708 struct scsi_ms_header fixed_hdr;
709 int status;
710 int tmp1, tmp2;
711 int flag;
712 int length;
713 int sp_flags;
714 union {
715 struct mode_err_recov page1;
716 char rawbuf[MAX_MODE_SENSE_SIZE];
717 } u_page1, u_fixed;
720 page1 = &u_page1.page1;
721 fixed = &u_fixed.page1;
724 * If debugging, issue mode senses on the default and
725 * current values.
727 if (option_msg && diag_msg) {
728 (void) uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
729 MODE_SENSE_PC_DEFAULT, (caddr_t)page1,
730 MAX_MODE_SENSE_SIZE, &header);
731 (void) uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
732 MODE_SENSE_PC_CURRENT, (caddr_t)page1,
733 MAX_MODE_SENSE_SIZE, &header);
737 * Issue a mode sense to determine the saved parameters
738 * If the saved values fail, use the current instead.
740 status = uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
741 MODE_SENSE_PC_SAVED, (caddr_t)page1,
742 MAX_MODE_SENSE_SIZE, &header);
743 if (status) {
744 status = uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
745 MODE_SENSE_PC_CURRENT, (caddr_t)page1,
746 MAX_MODE_SENSE_SIZE, &header);
747 if (status) {
748 return (0);
753 * We only need the common subset between the CCS
754 * and SCSI-2 structures, so we can treat both
755 * cases identically. Whatever the drive gives
756 * us, we return to the drive in the mode select,
757 * delta'ed by whatever we want to change.
759 length = MODESENSE_PAGE_LEN(page1);
760 if (length < MIN_PAGE1_LEN) {
761 return (0);
765 * Ask for changeable parameters.
767 status = uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
768 MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
769 MAX_MODE_SENSE_SIZE, &fixed_hdr);
770 if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE1_LEN) {
771 return (0);
775 * We need to issue a mode select only if one or more
776 * parameters need to be changed, and those parameters
777 * are flagged by the drive as changeable.
779 flag = 0;
780 tmp1 = page1->read_retry_count;
781 tmp2 = page1->write_retry_count;
782 if (cur_dtype->dtype_options & SUP_READ_RETRIES &&
783 fixed->read_retry_count != 0) {
784 flag |= (page1->read_retry_count !=
785 cur_dtype->dtype_read_retries);
786 page1->read_retry_count = cur_dtype->dtype_read_retries;
788 if (length > 8) {
789 if (cur_dtype->dtype_options & SUP_WRITE_RETRIES &&
790 fixed->write_retry_count != 0) {
791 flag |= (page1->write_retry_count !=
792 cur_dtype->dtype_write_retries);
793 page1->write_retry_count =
794 cur_dtype->dtype_write_retries;
798 * Report any changes so far...
800 if (flag && option_msg) {
801 fmt_print(
802 "PAGE 1: read retries= %d (%d) write retries= %d (%d)\n",
803 page1->read_retry_count, tmp1,
804 page1->write_retry_count, tmp2);
807 * Apply any changes requested via the change list method
809 flag |= apply_chg_list(DAD_MODE_ERR_RECOV, length,
810 (uchar_t *)page1, (uchar_t *)fixed,
811 cur_dtype->dtype_chglist);
813 * If no changes required, do not issue a mode select
815 if (flag == 0) {
816 return (0);
819 * We always want to set the Page Format bit for mode
820 * selects. Set the Save Page bit if the drive indicates
821 * that it can save this page via the mode sense.
823 sp_flags = MODE_SELECT_PF;
824 if (page1->mode_page.ps) {
825 sp_flags |= MODE_SELECT_SP;
827 page1->mode_page.ps = 0;
828 header.mode_header.length = 0;
829 header.mode_header.device_specific = 0;
830 status = uscsi_mode_select(cur_file, DAD_MODE_ERR_RECOV,
831 sp_flags, (caddr_t)page1, length, &header);
832 if (status && (sp_flags & MODE_SELECT_SP)) {
833 /* If failed, try not saving mode select params. */
834 sp_flags &= ~MODE_SELECT_SP;
835 status = uscsi_mode_select(cur_file, DAD_MODE_ERR_RECOV,
836 sp_flags, (caddr_t)page1, length, &header);
838 if (status && option_msg) {
839 err_print("\
840 Warning: Using default error recovery parameters.\n\n");
844 * If debugging, issue mode senses on the current and
845 * saved values, so we can see the result of the mode
846 * selects.
848 if (option_msg && diag_msg) {
849 (void) uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
850 MODE_SENSE_PC_CURRENT, (caddr_t)page1,
851 MAX_MODE_SENSE_SIZE, &header);
852 (void) uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
853 MODE_SENSE_PC_SAVED, (caddr_t)page1,
854 MAX_MODE_SENSE_SIZE, &header);
857 return (0);
861 * Check disk disconnect/reconnect parameters via mode sense.
862 * Issue a mode select if we need to change something.
864 /*ARGSUSED*/
865 static int
866 scsi_ms_page2(scsi2_flag)
867 int scsi2_flag;
869 struct mode_disco_reco *page2;
870 struct mode_disco_reco *fixed;
871 struct scsi_ms_header header;
872 struct scsi_ms_header fixed_hdr;
873 int status;
874 int flag;
875 int length;
876 int sp_flags;
877 union {
878 struct mode_disco_reco page2;
879 char rawbuf[MAX_MODE_SENSE_SIZE];
880 } u_page2, u_fixed;
882 page2 = &u_page2.page2;
883 fixed = &u_fixed.page2;
886 * If debugging, issue mode senses on the default and
887 * current values.
889 if (option_msg && diag_msg) {
890 (void) uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
891 MODE_SENSE_PC_DEFAULT, (caddr_t)page2,
892 MAX_MODE_SENSE_SIZE, &header);
893 (void) uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
894 MODE_SENSE_PC_CURRENT, (caddr_t)page2,
895 MAX_MODE_SENSE_SIZE, &header);
899 * Issue a mode sense to determine the saved parameters
900 * If the saved values fail, use the current instead.
902 status = uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
903 MODE_SENSE_PC_SAVED, (caddr_t)page2,
904 MAX_MODE_SENSE_SIZE, &header);
905 if (status) {
906 status = uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
907 MODE_SENSE_PC_CURRENT, (caddr_t)page2,
908 MAX_MODE_SENSE_SIZE, &header);
909 if (status) {
910 return (0);
915 * We only need the common subset between the CCS
916 * and SCSI-2 structures, so we can treat both
917 * cases identically. Whatever the drive gives
918 * us, we return to the drive in the mode select,
919 * delta'ed by whatever we want to change.
921 length = MODESENSE_PAGE_LEN(page2);
922 if (length < MIN_PAGE2_LEN) {
923 return (0);
927 * Ask for changeable parameters.
929 status = uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
930 MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
931 MAX_MODE_SENSE_SIZE, &fixed_hdr);
932 if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE2_LEN) {
933 return (0);
937 * We need to issue a mode select only if one or more
938 * parameters need to be changed, and those parameters
939 * are flagged by the drive as changeable.
941 flag = 0;
943 * Apply any changes requested via the change list method
945 flag |= apply_chg_list(MODEPAGE_DISCO_RECO, length,
946 (uchar_t *)page2, (uchar_t *)fixed,
947 cur_dtype->dtype_chglist);
949 * If no changes required, do not issue a mode select
951 if (flag == 0) {
952 return (0);
955 * We always want to set the Page Format bit for mode
956 * selects. Set the Save Page bit if the drive indicates
957 * that it can save this page via the mode sense.
959 sp_flags = MODE_SELECT_PF;
960 if (page2->mode_page.ps) {
961 sp_flags |= MODE_SELECT_SP;
963 page2->mode_page.ps = 0;
964 header.mode_header.length = 0;
965 header.mode_header.device_specific = 0;
966 status = uscsi_mode_select(cur_file, MODEPAGE_DISCO_RECO,
967 MODE_SELECT_SP, (caddr_t)page2, length, &header);
968 if (status && (sp_flags & MODE_SELECT_SP)) {
969 /* If failed, try not saving mode select params. */
970 sp_flags &= ~MODE_SELECT_SP;
971 status = uscsi_mode_select(cur_file, MODEPAGE_DISCO_RECO,
972 sp_flags, (caddr_t)page2, length, &header);
974 if (status && option_msg) {
975 err_print("Warning: Using default .\n\n");
979 * If debugging, issue mode senses on the current and
980 * saved values, so we can see the result of the mode
981 * selects.
983 if (option_msg && diag_msg) {
984 (void) uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
985 MODE_SENSE_PC_CURRENT, (caddr_t)page2,
986 MAX_MODE_SENSE_SIZE, &header);
987 (void) uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
988 MODE_SENSE_PC_SAVED, (caddr_t)page2,
989 MAX_MODE_SENSE_SIZE, &header);
992 return (0);
996 * Check disk format parameters via mode sense.
997 * Issue a mode select if we need to change something.
999 /*ARGSUSED*/
1000 static int
1001 scsi_ms_page3(scsi2_flag)
1002 int scsi2_flag;
1004 struct mode_format *page3;
1005 struct mode_format *fixed;
1006 struct scsi_ms_header header;
1007 struct scsi_ms_header fixed_hdr;
1008 int status;
1009 int tmp1, tmp2, tmp3;
1010 int tmp4, tmp5, tmp6;
1011 int flag;
1012 int length;
1013 int sp_flags;
1014 union {
1015 struct mode_format page3;
1016 char rawbuf[MAX_MODE_SENSE_SIZE];
1017 } u_page3, u_fixed;
1020 page3 = &u_page3.page3;
1021 fixed = &u_fixed.page3;
1024 * If debugging, issue mode senses on the default and
1025 * current values.
1027 if (option_msg && diag_msg) {
1028 (void) uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
1029 MODE_SENSE_PC_DEFAULT, (caddr_t)page3,
1030 MAX_MODE_SENSE_SIZE, &header);
1031 (void) uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
1032 MODE_SENSE_PC_CURRENT, (caddr_t)page3,
1033 MAX_MODE_SENSE_SIZE, &header);
1037 * Issue a mode sense to determine the saved parameters
1038 * If the saved values fail, use the current instead.
1040 status = uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
1041 MODE_SENSE_PC_SAVED, (caddr_t)page3,
1042 MAX_MODE_SENSE_SIZE, &header);
1043 if (status) {
1044 status = uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
1045 MODE_SENSE_PC_CURRENT, (caddr_t)page3,
1046 MAX_MODE_SENSE_SIZE, &header);
1047 if (status) {
1048 return (0);
1053 * We only need the common subset between the CCS
1054 * and SCSI-2 structures, so we can treat both
1055 * cases identically. Whatever the drive gives
1056 * us, we return to the drive in the mode select,
1057 * delta'ed by whatever we want to change.
1059 length = MODESENSE_PAGE_LEN(page3);
1060 if (length < MIN_PAGE3_LEN) {
1061 return (0);
1065 * Ask for changeable parameters.
1067 status = uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
1068 MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
1069 MAX_MODE_SENSE_SIZE, &fixed_hdr);
1070 if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE3_LEN) {
1071 return (0);
1075 * We need to issue a mode select only if one or more
1076 * parameters need to be changed, and those parameters
1077 * are flagged by the drive as changeable.
1079 tmp1 = page3->track_skew;
1080 tmp2 = page3->cylinder_skew;
1081 tmp3 = page3->sect_track;
1082 tmp4 = page3->tracks_per_zone;
1083 tmp5 = page3->alt_tracks_vol;
1084 tmp6 = page3->alt_sect_zone;
1086 flag = (page3->data_bytes_sect != cur_blksz);
1087 page3->data_bytes_sect = cur_blksz;
1089 flag |= (page3->interleave != 1);
1090 page3->interleave = 1;
1092 if (cur_dtype->dtype_options & SUP_CYLSKEW &&
1093 fixed->cylinder_skew != 0) {
1094 flag |= (page3->cylinder_skew != cur_dtype->dtype_cyl_skew);
1095 page3->cylinder_skew = cur_dtype->dtype_cyl_skew;
1097 if (cur_dtype->dtype_options & SUP_TRKSKEW &&
1098 fixed->track_skew != 0) {
1099 flag |= (page3->track_skew != cur_dtype->dtype_trk_skew);
1100 page3->track_skew = cur_dtype->dtype_trk_skew;
1102 if (cur_dtype->dtype_options & SUP_PSECT &&
1103 fixed->sect_track != 0) {
1104 flag |= (page3->sect_track != psect);
1105 page3->sect_track = (ushort_t)psect;
1107 if (cur_dtype->dtype_options & SUP_TRKS_ZONE &&
1108 fixed->tracks_per_zone != 0) {
1109 flag |= (page3->tracks_per_zone != cur_dtype->dtype_trks_zone);
1110 page3->tracks_per_zone = cur_dtype->dtype_trks_zone;
1112 if (cur_dtype->dtype_options & SUP_ASECT &&
1113 fixed->alt_sect_zone != 0) {
1114 flag |= (page3->alt_sect_zone != cur_dtype->dtype_asect);
1115 page3->alt_sect_zone = cur_dtype->dtype_asect;
1117 if (cur_dtype->dtype_options & SUP_ATRKS &&
1118 fixed->alt_tracks_vol != 0) {
1119 flag |= (page3->alt_tracks_vol != cur_dtype->dtype_atrks);
1120 page3->alt_tracks_vol = cur_dtype->dtype_atrks;
1123 * Notify user of any changes so far
1125 if (flag && option_msg) {
1126 fmt_print("PAGE 3: trk skew= %d (%d) cyl skew= %d (%d) ",
1127 page3->track_skew, tmp1, page3->cylinder_skew, tmp2);
1128 fmt_print("sects/trk= %d (%d)\n", page3->sect_track, tmp3);
1129 fmt_print(" trks/zone= %d (%d) alt trks= %d (%d) ",
1130 page3->tracks_per_zone, tmp4,
1131 page3->alt_tracks_vol, tmp5);
1132 fmt_print("alt sects/zone= %d (%d)\n",
1133 page3->alt_sect_zone, tmp6);
1136 * Apply any changes requested via the change list method
1138 flag |= apply_chg_list(DAD_MODE_FORMAT, length,
1139 (uchar_t *)page3, (uchar_t *)fixed,
1140 cur_dtype->dtype_chglist);
1142 * If no changes required, do not issue a mode select
1144 if (flag == 0) {
1145 return (0);
1148 * Issue a mode select
1151 * We always want to set the Page Format bit for mode
1152 * selects. Set the Save Page bit if the drive indicates
1153 * that it can save this page via the mode sense.
1155 sp_flags = MODE_SELECT_PF;
1156 if (page3->mode_page.ps) {
1157 sp_flags |= MODE_SELECT_SP;
1159 page3->mode_page.ps = 0;
1160 header.mode_header.length = 0;
1161 header.mode_header.device_specific = 0;
1162 status = uscsi_mode_select(cur_file, DAD_MODE_FORMAT,
1163 MODE_SELECT_SP, (caddr_t)page3, length, &header);
1164 if (status && (sp_flags & MODE_SELECT_SP)) {
1165 /* If failed, try not saving mode select params. */
1166 sp_flags &= ~MODE_SELECT_SP;
1167 status = uscsi_mode_select(cur_file, DAD_MODE_FORMAT,
1168 sp_flags, (caddr_t)page3, length, &header);
1170 if (status && option_msg) {
1171 err_print("Warning: Using default drive format parameters.\n");
1172 err_print("Warning: Drive format may not be correct.\n\n");
1176 * If debugging, issue mode senses on the current and
1177 * saved values, so we can see the result of the mode
1178 * selects.
1180 if (option_msg && diag_msg) {
1181 (void) uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
1182 MODE_SENSE_PC_CURRENT, (caddr_t)page3,
1183 MAX_MODE_SENSE_SIZE, &header);
1184 (void) uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
1185 MODE_SENSE_PC_SAVED, (caddr_t)page3,
1186 MAX_MODE_SENSE_SIZE, &header);
1189 return (0);
1193 * Check disk geometry parameters via mode sense.
1194 * Issue a mode select if we need to change something.
1196 /*ARGSUSED*/
1197 static int
1198 scsi_ms_page4(scsi2_flag)
1199 int scsi2_flag;
1201 struct mode_geometry *page4;
1202 struct mode_geometry *fixed;
1203 struct scsi_ms_header header;
1204 struct scsi_ms_header fixed_hdr;
1205 int status;
1206 int tmp1, tmp2;
1207 int flag;
1208 int length;
1209 int sp_flags;
1210 union {
1211 struct mode_geometry page4;
1212 char rawbuf[MAX_MODE_SENSE_SIZE];
1213 } u_page4, u_fixed;
1215 page4 = &u_page4.page4;
1216 fixed = &u_fixed.page4;
1219 * If debugging, issue mode senses on the default and
1220 * current values.
1222 if (option_msg && diag_msg) {
1223 (void) uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
1224 MODE_SENSE_PC_DEFAULT, (caddr_t)page4,
1225 MAX_MODE_SENSE_SIZE, &header);
1226 (void) uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
1227 MODE_SENSE_PC_CURRENT, (caddr_t)page4,
1228 MAX_MODE_SENSE_SIZE, &header);
1232 * Issue a mode sense to determine the saved parameters
1233 * If the saved values fail, use the current instead.
1235 status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
1236 MODE_SENSE_PC_SAVED, (caddr_t)page4,
1237 MAX_MODE_SENSE_SIZE, &header);
1238 if (status) {
1239 status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
1240 MODE_SENSE_PC_CURRENT, (caddr_t)page4,
1241 MAX_MODE_SENSE_SIZE, &header);
1242 if (status) {
1243 return (0);
1248 * We only need the common subset between the CCS
1249 * and SCSI-2 structures, so we can treat both
1250 * cases identically. Whatever the drive gives
1251 * us, we return to the drive in the mode select,
1252 * delta'ed by whatever we want to change.
1254 length = MODESENSE_PAGE_LEN(page4);
1255 if (length < MIN_PAGE4_LEN) {
1256 return (0);
1260 * Ask for changeable parameters.
1262 status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
1263 MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
1264 MAX_MODE_SENSE_SIZE, &fixed_hdr);
1265 if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE4_LEN) {
1266 return (0);
1270 * We need to issue a mode select only if one or more
1271 * parameters need to be changed, and those parameters
1272 * are flagged by the drive as changeable.
1274 tmp1 = (page4->cyl_ub << 16) + (page4->cyl_mb << 8) + page4->cyl_lb;
1275 tmp2 = page4->heads;
1277 flag = 0;
1278 if ((cur_dtype->dtype_options & SUP_PHEAD) && fixed->heads != 0) {
1279 flag |= (page4->heads != phead);
1280 page4->heads = phead;
1283 * Notify user of changes so far
1285 if (flag && option_msg) {
1286 fmt_print("PAGE 4: cylinders= %d heads= %d (%d)\n",
1287 tmp1, page4->heads, tmp2);
1290 * Apply any changes requested via the change list method
1292 flag |= apply_chg_list(DAD_MODE_GEOMETRY, length,
1293 (uchar_t *)page4, (uchar_t *)fixed,
1294 cur_dtype->dtype_chglist);
1296 * If no changes required, do not issue a mode select
1298 if (flag == 0) {
1299 return (0);
1302 * Issue a mode select
1305 * We always want to set the Page Format bit for mode
1306 * selects. Set the Save Page bit if the drive indicates
1307 * that it can save this page via the mode sense.
1309 sp_flags = MODE_SELECT_PF;
1310 if (page4->mode_page.ps) {
1311 sp_flags |= MODE_SELECT_SP;
1313 page4->mode_page.ps = 0;
1314 header.mode_header.length = 0;
1315 header.mode_header.device_specific = 0;
1316 status = uscsi_mode_select(cur_file, DAD_MODE_GEOMETRY,
1317 MODE_SELECT_SP, (caddr_t)page4, length, &header);
1318 if (status && (sp_flags & MODE_SELECT_SP)) {
1319 /* If failed, try not saving mode select params. */
1320 sp_flags &= ~MODE_SELECT_SP;
1321 status = uscsi_mode_select(cur_file, DAD_MODE_GEOMETRY,
1322 sp_flags, (caddr_t)page4, length, &header);
1324 if (status && option_msg) {
1325 err_print("Warning: Using default drive geometry.\n\n");
1329 * If debugging, issue mode senses on the current and
1330 * saved values, so we can see the result of the mode
1331 * selects.
1333 if (option_msg && diag_msg) {
1334 (void) uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
1335 MODE_SENSE_PC_CURRENT, (caddr_t)page4,
1336 MAX_MODE_SENSE_SIZE, &header);
1337 (void) uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
1338 MODE_SENSE_PC_SAVED, (caddr_t)page4,
1339 MAX_MODE_SENSE_SIZE, &header);
1342 return (0);
1346 * Check SCSI-2 disk cache parameters via mode sense.
1347 * Issue a mode select if we need to change something.
1349 /*ARGSUSED*/
1350 static int
1351 scsi_ms_page8(scsi2_flag)
1352 int scsi2_flag;
1354 struct mode_cache *page8;
1355 struct mode_cache *fixed;
1356 struct scsi_ms_header header;
1357 struct scsi_ms_header fixed_hdr;
1358 int status;
1359 int flag;
1360 int length;
1361 int sp_flags;
1362 union {
1363 struct mode_cache page8;
1364 char rawbuf[MAX_MODE_SENSE_SIZE];
1365 } u_page8, u_fixed;
1367 page8 = &u_page8.page8;
1368 fixed = &u_fixed.page8;
1371 * Only SCSI-2 devices support this page
1373 if (!scsi2_flag) {
1374 return (0);
1378 * If debugging, issue mode senses on the default and
1379 * current values.
1381 if (option_msg && diag_msg) {
1382 (void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
1383 MODE_SENSE_PC_DEFAULT, (caddr_t)page8,
1384 MAX_MODE_SENSE_SIZE, &header);
1385 (void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
1386 MODE_SENSE_PC_CURRENT, (caddr_t)page8,
1387 MAX_MODE_SENSE_SIZE, &header);
1391 * Issue a mode sense to determine the saved parameters
1392 * If the saved values fail, use the current instead.
1394 status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
1395 MODE_SENSE_PC_SAVED, (caddr_t)page8,
1396 MAX_MODE_SENSE_SIZE, &header);
1397 if (status) {
1398 status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
1399 MODE_SENSE_PC_CURRENT, (caddr_t)page8,
1400 MAX_MODE_SENSE_SIZE, &header);
1401 if (status) {
1402 return (0);
1407 * We only need the common subset between the CCS
1408 * and SCSI-2 structures, so we can treat both
1409 * cases identically. Whatever the drive gives
1410 * us, we return to the drive in the mode select,
1411 * delta'ed by whatever we want to change.
1413 length = MODESENSE_PAGE_LEN(page8);
1414 if (length < MIN_PAGE8_LEN) {
1415 return (0);
1419 * Ask for changeable parameters.
1421 status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
1422 MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
1423 MAX_MODE_SENSE_SIZE, &fixed_hdr);
1424 if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE8_LEN) {
1425 return (0);
1429 * We need to issue a mode select only if one or more
1430 * parameters need to be changed, and those parameters
1431 * are flagged by the drive as changeable.
1433 flag = 0;
1435 * Apply any changes requested via the change list method
1437 flag |= apply_chg_list(DAD_MODE_CACHE, length,
1438 (uchar_t *)page8, (uchar_t *)fixed,
1439 cur_dtype->dtype_chglist);
1441 * If no changes required, do not issue a mode select
1443 if (flag == 0) {
1444 return (0);
1447 * Issue a mode select
1450 * We always want to set the Page Format bit for mode
1451 * selects. Set the Save Page bit if the drive indicates
1452 * that it can save this page via the mode sense.
1454 sp_flags = MODE_SELECT_PF;
1455 if (page8->mode_page.ps) {
1456 sp_flags |= MODE_SELECT_SP;
1458 page8->mode_page.ps = 0;
1459 header.mode_header.length = 0;
1460 header.mode_header.device_specific = 0;
1461 status = uscsi_mode_select(cur_file, DAD_MODE_CACHE,
1462 sp_flags, (caddr_t)page8, length, &header);
1463 if (status && (sp_flags & MODE_SELECT_SP)) {
1464 /* If failed, try not saving mode select params. */
1465 sp_flags &= ~MODE_SELECT_SP;
1466 status = uscsi_mode_select(cur_file, DAD_MODE_CACHE,
1467 sp_flags, (caddr_t)page8, length, &header);
1469 if (status && option_msg) {
1470 err_print("\
1471 Warning: Using default SCSI-2 cache parameters.\n\n");
1475 * If debugging, issue mode senses on the current and
1476 * saved values, so we can see the result of the mode
1477 * selects.
1479 if (option_msg && diag_msg) {
1480 (void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
1481 MODE_SENSE_PC_CURRENT, (caddr_t)page8,
1482 MAX_MODE_SENSE_SIZE, &header);
1483 (void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
1484 MODE_SENSE_PC_SAVED, (caddr_t)page8,
1485 MAX_MODE_SENSE_SIZE, &header);
1488 return (0);
1492 * Check CCS disk cache parameters via mode sense.
1493 * Issue a mode select if we need to change something.
1495 /*ARGSUSED*/
1496 static int
1497 scsi_ms_page38(scsi2_flag)
1498 int scsi2_flag;
1500 struct mode_cache_ccs *page38;
1501 struct mode_cache_ccs *fixed;
1502 struct scsi_ms_header header;
1503 struct scsi_ms_header fixed_hdr;
1504 int status;
1505 int tmp1, tmp2, tmp3, tmp4;
1506 int flag;
1507 int length;
1508 int sp_flags;
1509 union {
1510 struct mode_cache_ccs page38;
1511 char rawbuf[MAX_MODE_SENSE_SIZE];
1512 } u_page38, u_fixed;
1515 * First, determine if we need to look at page 38 at all.
1516 * Not all devices support it.
1518 if (((cur_dtype->dtype_options & (SUP_CACHE | SUP_PREFETCH |
1519 SUP_CACHE_MIN | SUP_CACHE_MAX)) == 0) &&
1520 (!chg_list_affects_page(cur_dtype->dtype_chglist,
1521 0x38))) {
1522 return (0);
1525 page38 = &u_page38.page38;
1526 fixed = &u_fixed.page38;
1529 * If debugging, issue mode senses on the default and
1530 * current values.
1532 if (option_msg && diag_msg) {
1533 (void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
1534 MODE_SENSE_PC_DEFAULT, (caddr_t)page38,
1535 MAX_MODE_SENSE_SIZE, &header);
1536 (void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
1537 MODE_SENSE_PC_CURRENT, (caddr_t)page38,
1538 MAX_MODE_SENSE_SIZE, &header);
1542 * Issue a mode sense to determine the saved parameters
1543 * If the saved values fail, use the current instead.
1545 status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
1546 MODE_SENSE_PC_SAVED, (caddr_t)page38,
1547 MAX_MODE_SENSE_SIZE, &header);
1548 if (status) {
1549 status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
1550 MODE_SENSE_PC_CURRENT, (caddr_t)page38,
1551 MAX_MODE_SENSE_SIZE, &header);
1552 if (status) {
1553 return (0);
1558 * We only need the common subset between the CCS
1559 * and SCSI-2 structures, so we can treat both
1560 * cases identically. Whatever the drive gives
1561 * us, we return to the drive in the mode select,
1562 * delta'ed by whatever we want to change.
1564 length = MODESENSE_PAGE_LEN(page38);
1565 if (length < MIN_PAGE38_LEN) {
1566 return (0);
1570 * Ask for changeable parameters.
1572 status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
1573 MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
1574 MAX_MODE_SENSE_SIZE, &fixed_hdr);
1575 if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE38_LEN) {
1576 return (0);
1580 * We need to issue a mode select only if one or more
1581 * parameters need to be changed, and those parameters
1582 * are flagged by the drive as changeable.
1584 tmp1 = page38->mode;
1585 tmp2 = page38->threshold;
1586 tmp3 = page38->min_prefetch;
1587 tmp4 = page38->max_prefetch;
1589 flag = 0;
1590 if ((cur_dtype->dtype_options & SUP_CACHE) &&
1591 (fixed->mode & cur_dtype->dtype_cache) ==
1592 cur_dtype->dtype_cache) {
1593 flag |= (page38->mode != cur_dtype->dtype_cache);
1594 page38->mode = cur_dtype->dtype_cache;
1596 if ((cur_dtype->dtype_options & SUP_PREFETCH) &&
1597 (fixed->threshold & cur_dtype->dtype_threshold) ==
1598 cur_dtype->dtype_threshold) {
1599 flag |= (page38->threshold != cur_dtype->dtype_threshold);
1600 page38->threshold = cur_dtype->dtype_threshold;
1602 if ((cur_dtype->dtype_options & SUP_CACHE_MIN) &&
1603 (fixed->min_prefetch & cur_dtype->dtype_prefetch_min) ==
1604 cur_dtype->dtype_prefetch_min) {
1605 flag |= (page38->min_prefetch != cur_dtype->dtype_prefetch_min);
1606 page38->min_prefetch = cur_dtype->dtype_prefetch_min;
1608 if ((cur_dtype->dtype_options & SUP_CACHE_MAX) &&
1609 (fixed->max_prefetch & cur_dtype->dtype_prefetch_max) ==
1610 cur_dtype->dtype_prefetch_max) {
1611 flag |= (page38->max_prefetch != cur_dtype->dtype_prefetch_max);
1612 page38->max_prefetch = cur_dtype->dtype_prefetch_max;
1615 * Notify the user of changes up to this point
1617 if (flag && option_msg) {
1618 fmt_print("PAGE 38: cache mode= 0x%x (0x%x)\n",
1619 page38->mode, tmp1);
1620 fmt_print(" min. prefetch multiplier= %d ",
1621 page38->min_multiplier);
1622 fmt_print("max. prefetch multiplier= %d\n",
1623 page38->max_multiplier);
1624 fmt_print(" threshold= %d (%d) ",
1625 page38->threshold, tmp2);
1626 fmt_print("min. prefetch= %d (%d) ",
1627 page38->min_prefetch, tmp3);
1628 fmt_print("max. prefetch= %d (%d)\n",
1629 page38->max_prefetch, tmp4);
1632 * Apply any changes requested via the change list method
1634 flag |= apply_chg_list(DAD_MODE_CACHE_CCS, length,
1635 (uchar_t *)page38, (uchar_t *)fixed,
1636 cur_dtype->dtype_chglist);
1638 * If no changes required, do not issue a mode select
1640 if (flag == 0) {
1641 return (0);
1644 * Issue a mode select
1646 * We always want to set the Page Format bit for mode
1647 * selects. Set the Save Page bit if the drive indicates
1648 * that it can save this page via the mode sense.
1650 sp_flags = MODE_SELECT_PF;
1651 if (page38->mode_page.ps) {
1652 sp_flags |= MODE_SELECT_SP;
1654 page38->mode_page.ps = 0;
1655 header.mode_header.length = 0;
1656 header.mode_header.device_specific = 0;
1657 status = uscsi_mode_select(cur_file, DAD_MODE_CACHE_CCS,
1658 sp_flags, (caddr_t)page38, length, &header);
1659 if (status && (sp_flags & MODE_SELECT_SP)) {
1660 /* If failed, try not saving mode select params. */
1661 sp_flags &= ~MODE_SELECT_SP;
1662 status = uscsi_mode_select(cur_file, DAD_MODE_CACHE_CCS,
1663 sp_flags, (caddr_t)page38, length, &header);
1665 if (status && option_msg) {
1666 err_print("Warning: Using default CCS cache parameters.\n\n");
1670 * If debugging, issue mode senses on the current and
1671 * saved values, so we can see the result of the mode
1672 * selects.
1674 if (option_msg && diag_msg) {
1675 (void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
1676 MODE_SENSE_PC_CURRENT, (caddr_t)page38,
1677 MAX_MODE_SENSE_SIZE, &header);
1678 (void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
1679 MODE_SENSE_PC_SAVED, (caddr_t)page38,
1680 MAX_MODE_SENSE_SIZE, &header);
1683 return (0);
1688 * Extract the manufacturer's defect list.
1691 scsi_ex_man(list)
1692 struct defect_list *list;
1694 int i;
1696 i = scsi_read_defect_data(list, DLD_MAN_DEF_LIST);
1697 if (i != 0)
1698 return (i);
1699 list->flags &= ~LIST_PGLIST;
1700 return (0);
1704 * Extract the current defect list.
1705 * For embedded scsi drives, this means both the manufacturer's (P)
1706 * and the grown (G) lists.
1709 scsi_ex_cur(list)
1710 struct defect_list *list;
1712 int i;
1714 i = scsi_read_defect_data(list, DLD_GROWN_DEF_LIST|DLD_MAN_DEF_LIST);
1715 if (i != 0)
1716 return (i);
1717 list->flags |= LIST_PGLIST;
1718 return (0);
1723 * Extract the grown list only
1726 scsi_ex_grown(list)
1727 struct defect_list *list;
1729 int i;
1731 i = scsi_read_defect_data(list, DLD_GROWN_DEF_LIST);
1732 if (i != 0)
1733 return (i);
1734 list->flags |= LIST_PGLIST;
1735 return (0);
1739 static int
1740 scsi_read_defect_data(list, pglist_flags)
1741 struct defect_list *list;
1742 int pglist_flags;
1744 struct uscsi_cmd ucmd;
1745 char rqbuf[255];
1746 union scsi_cdb cdb;
1747 struct scsi_defect_list *defects;
1748 struct scsi_defect_list def_list;
1749 struct scsi_defect_hdr *hdr;
1750 int status;
1751 int nbytes;
1752 int len; /* returned defect list length */
1753 struct scsi_extended_sense *rq;
1755 hdr = (struct scsi_defect_hdr *)&def_list;
1758 * First get length of list by asking for the header only.
1760 (void) memset((char *)&def_list, 0, sizeof (def_list));
1763 * Build and execute the uscsi ioctl
1765 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
1766 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
1767 (void) memset((char *)rqbuf, 0, 255);
1768 cdb.scc_cmd = SCMD_READ_DEFECT_LIST;
1769 FORMG1COUNT(&cdb, sizeof (struct scsi_defect_hdr));
1770 cdb.cdb_opaque[2] = pglist_flags | DLD_BFI_FORMAT;
1771 ucmd.uscsi_cdb = (caddr_t)&cdb;
1772 ucmd.uscsi_cdblen = CDB_GROUP1;
1773 ucmd.uscsi_bufaddr = (caddr_t)hdr;
1774 ucmd.uscsi_buflen = sizeof (struct scsi_defect_hdr);
1775 ucmd.uscsi_rqbuf = rqbuf;
1776 ucmd.uscsi_rqlen = sizeof (rqbuf);
1777 ucmd.uscsi_rqresid = sizeof (rqbuf);
1778 rq = (struct scsi_extended_sense *)ucmd.uscsi_rqbuf;
1780 status = uscsi_cmd(cur_file, &ucmd,
1781 (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
1783 if (status != 0) {
1785 * check if read_defect_list_is_supported.
1787 if (ucmd.uscsi_rqstatus == STATUS_GOOD &&
1788 rq->es_key == KEY_ILLEGAL_REQUEST &&
1789 rq->es_add_code == INVALID_OPCODE) {
1790 err_print("\nWARNING: Current Disk does not support"
1791 " defect lists. \n");
1792 } else
1793 if (option_msg) {
1794 err_print("No %s defect list.\n",
1795 pglist_flags & DLD_GROWN_DEF_LIST ?
1796 "grown" : "manufacturer's");
1798 return (-1);
1802 * Read the full list the second time
1804 hdr->length = BE_16(hdr->length);
1805 len = hdr->length;
1806 nbytes = len + sizeof (struct scsi_defect_hdr);
1808 defects = zalloc(nbytes);
1809 *(struct scsi_defect_hdr *)defects = *(struct scsi_defect_hdr *)hdr;
1811 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
1812 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
1813 cdb.scc_cmd = SCMD_READ_DEFECT_LIST;
1814 FORMG1COUNT(&cdb, nbytes);
1815 cdb.cdb_opaque[2] = pglist_flags | DLD_BFI_FORMAT;
1816 ucmd.uscsi_cdb = (caddr_t)&cdb;
1817 ucmd.uscsi_cdblen = CDB_GROUP1;
1818 ucmd.uscsi_bufaddr = (caddr_t)defects;
1819 ucmd.uscsi_buflen = nbytes;
1820 status = uscsi_cmd(cur_file, &ucmd,
1821 (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
1823 if (status) {
1824 err_print("can't read defect list 2nd time");
1825 destroy_data((char *)defects);
1826 return (-1);
1829 defects->length = BE_16(defects->length);
1831 if (len != hdr->length) {
1832 err_print("not enough defects");
1833 destroy_data((char *)defects);
1834 return (-1);
1836 scsi_convert_list_to_new(list, (struct scsi_defect_list *)defects,
1837 DLD_BFI_FORMAT);
1838 destroy_data((char *)defects);
1839 return (0);
1844 * Map a block.
1846 /*ARGSUSED*/
1847 static int
1848 scsi_repair(bn, flag)
1849 uint64_t bn;
1850 int flag;
1852 struct uscsi_cmd ucmd;
1853 union scsi_cdb cdb;
1854 struct scsi_reassign_blk defect_list;
1857 * Build and execute the uscsi ioctl
1859 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
1860 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
1861 (void) memset((char *)&defect_list, 0,
1862 sizeof (struct scsi_reassign_blk));
1863 cdb.scc_cmd = SCMD_REASSIGN_BLOCK;
1864 ucmd.uscsi_cdb = (caddr_t)&cdb;
1865 ucmd.uscsi_cdblen = CDB_GROUP0;
1866 ucmd.uscsi_bufaddr = (caddr_t)&defect_list;
1867 ucmd.uscsi_buflen = sizeof (struct scsi_reassign_blk);
1868 defect_list.length = sizeof (defect_list.defect);
1869 defect_list.length = BE_16(defect_list.length);
1870 defect_list.defect = bn;
1871 defect_list.defect = BE_32(defect_list.defect);
1872 return (uscsi_cmd(cur_file, &ucmd,
1873 (option_msg && diag_msg) ? F_NORMAL : F_SILENT));
1877 * Convert a SCSI-style defect list to our generic format.
1878 * We can handle different format lists.
1880 static void
1881 scsi_convert_list_to_new(list, def_list, list_format)
1882 struct defect_list *list;
1883 struct scsi_defect_list *def_list;
1884 int list_format;
1886 register struct scsi_bfi_defect *old_defect, *old_defect1;
1887 register struct defect_entry *new_defect;
1888 register int len, new_len, obfi, nbfi;
1889 register int i;
1890 int old_cyl, new_cyl;
1891 unsigned char *cp;
1894 switch (list_format) {
1896 case DLD_BFI_FORMAT:
1898 * Allocate space for the rest of the list.
1900 len = def_list->length / sizeof (struct scsi_bfi_defect);
1901 old_defect = def_list->list;
1902 new_defect = (struct defect_entry *)
1903 zalloc(deflist_size(cur_blksz, len) *
1904 cur_blksz);
1906 list->header.magicno = (uint_t)DEFECT_MAGIC;
1907 list->list = new_defect;
1909 for (i = 0, new_len = 0; i < len; new_defect++, new_len++) {
1910 cp = (unsigned char *)old_defect;
1911 new_defect->cyl = (cp[0] << 16 | cp[1] << 8) | cp[2];
1912 new_defect->head = old_defect->head;
1913 new_defect->bfi = (int)old_defect->bytes_from_index;
1914 new_defect->bfi = BE_32(new_defect->bfi);
1915 new_defect->nbits = 0; /* size of defect */
1916 old_defect1 = old_defect++;
1917 i++;
1919 * Since we reached the end of the list, old_defect
1920 * now points to an invalid reference, since it got
1921 * incremented in the above operation. So we don't
1922 * need to proceed further. new_len needs to be
1923 * incremented to account for the last element.
1925 if (i == len) {
1926 new_len++;
1927 break;
1929 obfi = new_defect->bfi;
1930 nbfi = (int)old_defect->bytes_from_index;
1931 nbfi = BE_32(nbfi);
1933 old_cyl = new_defect->cyl;
1934 cp = (unsigned char *)old_defect;
1935 new_cyl = (cp[0] << 16 | cp[1] << 8) | cp[2];
1939 * Merge adjacent contiguous defect entries into one
1940 * and update the length of the defect
1942 while ((i < len) &&
1943 (old_cyl == new_cyl) &&
1944 (old_defect->head == old_defect1->head) &&
1945 (nbfi == (obfi + BITSPERBYTE))) {
1946 old_defect1 = old_defect++;
1947 cp = (unsigned char *)old_defect;
1948 new_cyl = (cp[0] << 16 | cp[1] << 8) | cp[2];
1949 obfi = (int)old_defect1->bytes_from_index;
1950 obfi = BE_32(obfi);
1951 nbfi = (int)old_defect->bytes_from_index;
1952 nbfi = BE_32(nbfi);
1953 new_defect->nbits += (8*BITSPERBYTE);
1954 i++;
1958 list->header.count = new_len;
1959 break;
1961 default:
1962 err_print("scsi_convert_list_to_new: can't deal with it\n");
1963 exit(0);
1964 /*NOTREACHED*/
1967 (void) checkdefsum(list, CK_MAKESUM);
1973 * Execute a command and determine the result.
1974 * Uses the "uscsi" ioctl interface, which is
1975 * fully supported.
1977 * If the user wants request sense data to be returned
1978 * in case of error then , the "uscsi_cmd" structure
1979 * should have the request sense buffer allocated in
1980 * uscsi_rqbuf.
1984 uscsi_cmd(fd, ucmd, flags)
1985 int fd;
1986 struct uscsi_cmd *ucmd;
1987 int flags;
1989 struct scsi_extended_sense *rq;
1990 char rqbuf[255];
1991 int status;
1992 int rqlen;
1993 int timeout = 0;
1996 * Set function flags for driver.
1998 ucmd->uscsi_flags = USCSI_ISOLATE;
1999 if (flags & F_SILENT) {
2000 ucmd->uscsi_flags |= USCSI_SILENT;
2002 if (flags & F_RQENABLE) {
2003 ucmd->uscsi_flags |= USCSI_RQENABLE;
2007 * If this command will perform a read, set the USCSI_READ flag
2009 if (ucmd->uscsi_buflen > 0) {
2011 * uscsi_cdb is declared as a caddr_t, so any CDB
2012 * command byte with the MSB set will result in a
2013 * compiler error unless we cast to an unsigned value.
2015 switch ((uint8_t)ucmd->uscsi_cdb[0]) {
2016 case SCMD_READ:
2017 case SCMD_READ|SCMD_GROUP1:
2018 case SCMD_READ|SCMD_GROUP4:
2019 case SCMD_MODE_SENSE:
2020 case SCMD_INQUIRY:
2021 case SCMD_READ_DEFECT_LIST:
2022 case SCMD_READ_CAPACITY:
2023 case SCMD_SVC_ACTION_IN_G4:
2024 ucmd->uscsi_flags |= USCSI_READ;
2025 break;
2030 * Set timeout: 30 seconds for all commands except format
2032 switch (ucmd->uscsi_cdb[0]) {
2033 case SCMD_FORMAT:
2034 if (ucmd->uscsi_timeout == 0) {
2035 ucmd->uscsi_timeout = scsi_format_timeout;
2037 * Get the timeout value computed using page4 geometry.
2038 * add 50% margin to cover defect management overhead.
2039 * add another 50% margin to have a safe timeout.
2040 * If it exceeds 2 hours then use this value.
2042 if ((timeout = scsi_format_time()) > 0) {
2043 timeout *= 60; /* convert to seconds */
2044 timeout += timeout;
2046 * formatting drives with huge capacity
2047 * will cause these heuristics to come
2048 * up with times that overflow ~9 hours
2050 if (timeout > SHRT_MAX)
2051 timeout = SHRT_MAX;
2052 if (timeout > scsi_format_timeout)
2053 ucmd->uscsi_timeout = timeout;
2056 if (option_msg && diag_msg) {
2057 err_print("format_timeout set to %d seconds, %d"
2058 " required\n", ucmd->uscsi_timeout, timeout);
2060 break;
2062 default:
2063 ucmd->uscsi_timeout = 30; /* 30 seconds */
2064 break;
2068 * Set up Request Sense buffer
2070 ucmd->uscsi_flags |= USCSI_RQENABLE;
2072 if (ucmd->uscsi_rqbuf == NULL) {
2073 ucmd->uscsi_rqbuf = rqbuf;
2074 ucmd->uscsi_rqlen = sizeof (rqbuf);
2075 ucmd->uscsi_rqresid = sizeof (rqbuf);
2077 ucmd->uscsi_rqstatus = IMPOSSIBLE_SCSI_STATUS;
2080 * Clear global error state
2082 media_error = 0;
2085 * Execute the ioctl
2087 status = ioctl(fd, USCSICMD, ucmd);
2088 if (status == 0 && ucmd->uscsi_status == 0) {
2089 return (status);
2093 * Check the status and return appropriate errors if the disk is
2094 * unavailable (could be formatting) or reserved (by other host).
2095 * In either case we can not talk to the disk now.
2097 if (status == -1 && errno == EAGAIN) {
2098 disk_error = DISK_STAT_UNAVAILABLE;
2099 return (DSK_UNAVAILABLE);
2101 if ((ucmd->uscsi_status & STATUS_MASK) == STATUS_RESERVATION_CONFLICT) {
2102 disk_error = DISK_STAT_RESERVED;
2103 return (DSK_RESERVED);
2106 * Check for physically removed or completely unresponsive drive
2108 if (status == -1 && !ucmd->uscsi_status && errno == EIO) {
2109 disk_error = DISK_STAT_UNAVAILABLE;
2110 return (DSK_UNAVAILABLE);
2114 * If an automatic Request Sense gave us valid
2115 * info about the error, we may be able to use
2116 * that to print a reasonable error msg.
2118 if (ucmd->uscsi_rqstatus == IMPOSSIBLE_SCSI_STATUS) {
2119 if (option_msg && diag_msg) {
2120 err_print("No request sense for command %s\n",
2121 scsi_find_command_name(ucmd->uscsi_cdb[0]));
2123 return (-1);
2125 if (ucmd->uscsi_rqstatus != STATUS_GOOD) {
2126 if (option_msg && diag_msg) {
2127 err_print("Request sense status for command %s: 0x%x\n",
2128 scsi_find_command_name(ucmd->uscsi_cdb[0]),
2129 ucmd->uscsi_rqstatus);
2131 return (-1);
2133 rq = (struct scsi_extended_sense *)ucmd->uscsi_rqbuf;
2134 rqlen = ucmd->uscsi_rqlen - ucmd->uscsi_rqresid;
2135 if ((((int)rq->es_add_len) + 8) < MIN_REQUEST_SENSE_LEN ||
2136 rq->es_class != CLASS_EXTENDED_SENSE ||
2137 rqlen < MIN_REQUEST_SENSE_LEN) {
2138 if (option_msg) {
2139 err_print("Request sense for command %s failed\n",
2140 scsi_find_command_name(ucmd->uscsi_cdb[0]));
2142 if (option_msg && diag_msg) {
2143 err_print("Sense data:\n");
2144 dump("", (caddr_t)rqbuf, rqlen, HEX_ONLY);
2146 if (errno == EIO) {
2147 disk_error = DISK_STAT_UNAVAILABLE;
2148 return (DSK_UNAVAILABLE);
2149 } else {
2150 return (-1);
2155 * If the failed command is a Mode Select, and the
2156 * target is indicating that it has rounded one of
2157 * the mode select parameters, as defined in the SCSI-2
2158 * specification, then we should accept the command
2159 * as successful.
2161 if (ucmd->uscsi_cdb[0] == SCMD_MODE_SELECT) {
2162 if (rq->es_key == KEY_RECOVERABLE_ERROR &&
2163 rq->es_add_code == ROUNDED_PARAMETER &&
2164 rq->es_qual_code == 0) {
2165 return (0);
2169 switch (rq->es_key) {
2170 case KEY_NOT_READY:
2171 disk_error = DISK_STAT_NOTREADY;
2172 break;
2173 case KEY_DATA_PROTECT:
2174 disk_error = DISK_STAT_DATA_PROTECT;
2175 break;
2178 if (flags & F_ALLERRS) {
2179 media_error = (rq->es_key == KEY_MEDIUM_ERROR);
2181 if (!(flags & F_SILENT) || option_msg) {
2182 scsi_printerr(ucmd, rq, rqlen);
2184 if ((rq->es_key != KEY_RECOVERABLE_ERROR) || (flags & F_ALLERRS)) {
2185 return (-1);
2188 if (status == -1 && errno == EIO) {
2189 disk_error = DISK_STAT_UNAVAILABLE;
2190 return (DSK_UNAVAILABLE);
2193 return (0);
2198 * Execute a uscsi mode sense command.
2199 * This can only be used to return one page at a time.
2200 * Return the mode header/block descriptor and the actual
2201 * page data separately - this allows us to support
2202 * devices which return either 0 or 1 block descriptors.
2203 * Whatever a device gives us in the mode header/block descriptor
2204 * will be returned to it upon subsequent mode selects.
2207 uscsi_mode_sense(fd, page_code, page_control, page_data, page_size, header)
2208 int fd; /* file descriptor */
2209 int page_code; /* requested page number */
2210 int page_control; /* current, changeable, etc. */
2211 caddr_t page_data; /* place received data here */
2212 int page_size; /* size of page_data */
2213 struct scsi_ms_header *header; /* mode header/block descriptor */
2215 caddr_t mode_sense_buf;
2216 struct mode_header *hdr;
2217 struct mode_page *pg;
2218 int nbytes;
2219 struct uscsi_cmd ucmd;
2220 union scsi_cdb cdb;
2221 int status;
2222 int maximum;
2224 assert(page_size >= 0 && page_size < 256);
2225 assert(page_control == MODE_SENSE_PC_CURRENT ||
2226 page_control == MODE_SENSE_PC_CHANGEABLE ||
2227 page_control == MODE_SENSE_PC_DEFAULT ||
2228 page_control == MODE_SENSE_PC_SAVED);
2230 * Allocate a buffer for the mode sense headers
2231 * and mode sense data itself.
2233 nbytes = sizeof (struct block_descriptor) +
2234 sizeof (struct mode_header) + page_size;
2235 nbytes = page_size;
2236 if ((mode_sense_buf = malloc((uint_t)nbytes)) == NULL) {
2237 err_print("cannot malloc %d bytes\n", nbytes);
2238 return (-1);
2242 * Build and execute the uscsi ioctl
2244 (void) memset(mode_sense_buf, 0, nbytes);
2245 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
2246 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2247 cdb.scc_cmd = SCMD_MODE_SENSE;
2248 FORMG0COUNT(&cdb, (uchar_t)nbytes);
2249 cdb.cdb_opaque[2] = page_control | page_code;
2250 ucmd.uscsi_cdb = (caddr_t)&cdb;
2251 ucmd.uscsi_cdblen = CDB_GROUP0;
2252 ucmd.uscsi_bufaddr = mode_sense_buf;
2253 ucmd.uscsi_buflen = nbytes;
2254 status = uscsi_cmd(fd, &ucmd,
2255 (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2256 if (status) {
2257 if (option_msg) {
2258 err_print("Mode sense page 0x%x failed\n",
2259 page_code);
2261 free(mode_sense_buf);
2262 return (-1);
2266 * Verify that the returned data looks reasonabled,
2267 * find the actual page data, and copy it into the
2268 * user's buffer. Copy the mode_header and block_descriptor
2269 * into the header structure, which can then be used to
2270 * return the same data to the drive when issuing a mode select.
2272 hdr = (struct mode_header *)mode_sense_buf;
2273 (void) memset((caddr_t)header, 0, sizeof (struct scsi_ms_header));
2274 if (hdr->bdesc_length != sizeof (struct block_descriptor) &&
2275 hdr->bdesc_length != 0) {
2276 if (option_msg) {
2277 err_print("\
2278 \nMode sense page 0x%x: block descriptor length %d incorrect\n",
2279 page_code, hdr->bdesc_length);
2280 if (diag_msg)
2281 dump("Mode sense: ", mode_sense_buf,
2282 nbytes, HEX_ONLY);
2284 free(mode_sense_buf);
2285 return (-1);
2287 (void) memcpy((caddr_t)header, mode_sense_buf,
2288 (int) (sizeof (struct mode_header) + hdr->bdesc_length));
2289 pg = (struct mode_page *)((ulong_t)mode_sense_buf +
2290 sizeof (struct mode_header) + hdr->bdesc_length);
2291 if (pg->code != page_code) {
2292 if (option_msg) {
2293 err_print("\
2294 \nMode sense page 0x%x: incorrect page code 0x%x\n",
2295 page_code, pg->code);
2296 if (diag_msg)
2297 dump("Mode sense: ", mode_sense_buf,
2298 nbytes, HEX_ONLY);
2300 free(mode_sense_buf);
2301 return (-1);
2304 * Accept up to "page_size" bytes of mode sense data.
2305 * This allows us to accept both CCS and SCSI-2
2306 * structures, as long as we request the greater
2307 * of the two.
2309 maximum = page_size - sizeof (struct mode_page) - hdr->bdesc_length;
2310 if (((int)pg->length) > maximum) {
2311 if (option_msg) {
2312 err_print("\
2313 Mode sense page 0x%x: incorrect page length %d - expected max %d\n",
2314 page_code, pg->length, maximum);
2315 if (diag_msg)
2316 dump("Mode sense: ", mode_sense_buf,
2317 nbytes, HEX_ONLY);
2319 free(mode_sense_buf);
2320 return (-1);
2323 (void) memcpy(page_data, (caddr_t)pg, MODESENSE_PAGE_LEN(pg));
2325 if (option_msg && diag_msg) {
2326 char *pc = find_string(page_control_strings, page_control);
2327 err_print("\nMode sense page 0x%x (%s):\n", page_code,
2328 pc != NULL ? pc : "");
2329 dump("header: ", (caddr_t)header,
2330 sizeof (struct scsi_ms_header), HEX_ONLY);
2331 dump("data: ", page_data,
2332 MODESENSE_PAGE_LEN(pg), HEX_ONLY);
2335 free(mode_sense_buf);
2336 return (0);
2341 * Execute a uscsi mode select command.
2344 uscsi_mode_select(fd, page_code, options, page_data, page_size, header)
2345 int fd; /* file descriptor */
2346 int page_code; /* mode select page */
2347 int options; /* save page/page format */
2348 caddr_t page_data; /* place received data here */
2349 int page_size; /* size of page_data */
2350 struct scsi_ms_header *header; /* mode header/block descriptor */
2352 caddr_t mode_select_buf;
2353 int nbytes;
2354 struct uscsi_cmd ucmd;
2355 union scsi_cdb cdb;
2356 int status;
2358 assert(((struct mode_page *)page_data)->ps == 0);
2359 assert(header->mode_header.length == 0);
2360 assert(header->mode_header.device_specific == 0);
2361 assert((options & ~(MODE_SELECT_SP|MODE_SELECT_PF)) == 0);
2364 * Allocate a buffer for the mode select header and data
2366 nbytes = sizeof (struct block_descriptor) +
2367 sizeof (struct mode_header) + page_size;
2368 if ((mode_select_buf = malloc((uint_t)nbytes)) == NULL) {
2369 err_print("cannot malloc %d bytes\n", nbytes);
2370 return (-1);
2374 * Build the mode select data out of the header and page data
2375 * This allows us to support devices which return either
2376 * 0 or 1 block descriptors.
2378 (void) memset(mode_select_buf, 0, nbytes);
2379 nbytes = sizeof (struct mode_header);
2380 if (header->mode_header.bdesc_length ==
2381 sizeof (struct block_descriptor)) {
2382 nbytes += sizeof (struct block_descriptor);
2386 * Dump the structures if anyone's interested
2388 if (option_msg && diag_msg) {
2389 char *s;
2390 s = find_string(mode_select_strings,
2391 options & (MODE_SELECT_SP|MODE_SELECT_PF));
2392 err_print("\nMode select page 0x%x%s:\n", page_code,
2393 s != NULL ? s : "");
2394 dump("header: ", (caddr_t)header,
2395 nbytes, HEX_ONLY);
2396 dump("data: ", (caddr_t)page_data,
2397 page_size, HEX_ONLY);
2401 * Fix the code for byte ordering
2404 switch (page_code) {
2405 case DAD_MODE_ERR_RECOV:
2407 struct mode_err_recov *pd;
2408 pd = (struct mode_err_recov *)(void *)page_data;
2409 pd->recovery_time_limit = BE_16(pd->recovery_time_limit);
2410 break;
2412 case MODEPAGE_DISCO_RECO:
2414 struct mode_disco_reco *pd;
2415 pd = (struct mode_disco_reco *)(void *)page_data;
2416 pd->bus_inactivity_limit = BE_16(pd->bus_inactivity_limit);
2417 pd->disconect_time_limit = BE_16(pd->disconect_time_limit);
2418 pd->connect_time_limit = BE_16(pd->connect_time_limit);
2419 pd->max_burst_size = BE_16(pd->max_burst_size);
2420 break;
2422 case DAD_MODE_FORMAT:
2424 struct mode_format *pd;
2425 pd = (struct mode_format *)(void *)page_data;
2426 pd->tracks_per_zone = BE_16(pd->tracks_per_zone);
2427 pd->alt_sect_zone = BE_16(pd->alt_sect_zone);
2428 pd->alt_tracks_zone = BE_16(pd->alt_tracks_zone);
2429 pd->alt_tracks_vol = BE_16(pd->alt_tracks_vol);
2430 pd->sect_track = BE_16(pd->sect_track);
2431 pd->data_bytes_sect = BE_16(pd->data_bytes_sect);
2432 pd->interleave = BE_16(pd->interleave);
2433 pd->track_skew = BE_16(pd->track_skew);
2434 pd->cylinder_skew = BE_16(pd->cylinder_skew);
2435 break;
2437 case DAD_MODE_GEOMETRY:
2439 struct mode_geometry *pd;
2440 pd = (struct mode_geometry *)(void *)page_data;
2441 pd->step_rate = BE_16(pd->step_rate);
2442 pd->rpm = BE_16(pd->rpm);
2443 break;
2445 case DAD_MODE_CACHE:
2447 struct mode_cache *pd;
2448 pd = (struct mode_cache *)(void *)page_data;
2449 pd->dis_prefetch_len = BE_16(pd->dis_prefetch_len);
2450 pd->min_prefetch = BE_16(pd->min_prefetch);
2451 pd->max_prefetch = BE_16(pd->max_prefetch);
2452 pd->prefetch_ceiling = BE_16(pd->prefetch_ceiling);
2453 break;
2455 case MODEPAGE_PDEVICE:
2457 struct mode_pdevice *pd;
2458 pd = (struct mode_pdevice *)(void *)page_data;
2459 pd->if_ident = BE_16(pd->if_ident);
2460 break;
2462 case MODEPAGE_CTRL_MODE:
2464 struct mode_control *pd;
2465 pd = (struct mode_control *)(void *)page_data;
2466 pd->ready_aen_holdoff = BE_16(pd->ready_aen_holdoff);
2467 break;
2472 * Put the header and data together
2474 (void) memcpy(mode_select_buf, (caddr_t)header, nbytes);
2475 (void) memcpy(mode_select_buf + nbytes, page_data, page_size);
2476 nbytes += page_size;
2479 * Build and execute the uscsi ioctl
2481 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
2482 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2483 cdb.scc_cmd = SCMD_MODE_SELECT;
2484 FORMG0COUNT(&cdb, (uchar_t)nbytes);
2485 cdb.cdb_opaque[1] = (uchar_t)options;
2486 ucmd.uscsi_cdb = (caddr_t)&cdb;
2487 ucmd.uscsi_cdblen = CDB_GROUP0;
2488 ucmd.uscsi_bufaddr = mode_select_buf;
2489 ucmd.uscsi_buflen = nbytes;
2490 status = uscsi_cmd(fd, &ucmd,
2491 (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2493 if (status && option_msg) {
2494 err_print("Mode select page 0x%x failed\n", page_code);
2497 free(mode_select_buf);
2498 return (status);
2503 * Execute a uscsi inquiry command and return the
2504 * resulting data.
2507 uscsi_inquiry(fd, inqbuf, inqbufsiz)
2508 int fd;
2509 caddr_t inqbuf;
2510 int inqbufsiz;
2512 struct uscsi_cmd ucmd;
2513 union scsi_cdb cdb;
2514 struct scsi_inquiry *inq;
2515 int n;
2516 int status;
2518 assert(inqbufsiz >= sizeof (struct scsi_inquiry) &&
2519 inqbufsiz < 256);
2522 * Build and execute the uscsi ioctl
2524 (void) memset((char *)inqbuf, 0, inqbufsiz);
2525 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
2526 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2527 cdb.scc_cmd = SCMD_INQUIRY;
2528 FORMG0COUNT(&cdb, (uchar_t)inqbufsiz);
2529 ucmd.uscsi_cdb = (caddr_t)&cdb;
2530 ucmd.uscsi_cdblen = CDB_GROUP0;
2531 ucmd.uscsi_bufaddr = (caddr_t)inqbuf;
2532 ucmd.uscsi_buflen = inqbufsiz;
2533 status = uscsi_cmd(fd, &ucmd,
2534 (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2535 if (status) {
2536 if (option_msg) {
2537 err_print("Inquiry failed\n");
2539 } else if (option_msg && diag_msg) {
2541 * Dump the inquiry data if anyone's interested
2543 inq = (struct scsi_inquiry *)inqbuf;
2544 n = (int)inq->inq_len + 4;
2545 n = min(n, inqbufsiz);
2546 err_print("Inquiry:\n");
2547 dump("", (caddr_t)inqbuf, n, HEX_ASCII);
2549 return (status);
2553 * Execute a uscsi inquiry command with page code 86h
2556 uscsi_inquiry_page_86h(fd, inqbuf, inqbufsiz)
2557 int fd;
2558 caddr_t inqbuf;
2559 int inqbufsiz;
2561 struct uscsi_cmd ucmd;
2562 union scsi_cdb cdb;
2563 int status;
2565 assert(inqbuf);
2566 assert(inqbufsiz >= sizeof (struct scsi_inquiry) &&
2567 inqbufsiz < 256);
2569 * Build and execute uscsi ioctl
2571 (void) memset((char *)inqbuf, 0, inqbufsiz);
2572 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
2573 (void) memset((char *)&cdb, 0, sizeof (cdb));
2574 cdb.scc_cmd = SCMD_INQUIRY;
2575 FORMG0COUNT(&cdb, (uchar_t)inqbufsiz);
2576 cdb.cdb_opaque[1] |= 0x01;
2577 cdb.cdb_opaque[2] = 0x86;
2578 ucmd.uscsi_cdb = (caddr_t)&cdb;
2579 ucmd.uscsi_cdblen = CDB_GROUP0;
2580 ucmd.uscsi_bufaddr = (caddr_t)inqbuf;
2581 ucmd.uscsi_buflen = inqbufsiz;
2583 status = uscsi_cmd(fd, &ucmd,
2584 (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2586 if (status) {
2587 if (option_msg) {
2588 err_print("Inquriy with page_86h failed\n");
2591 return (status);
2595 * Return the Read Capacity information
2598 uscsi_read_capacity_16(fd, capacity)
2599 int fd;
2600 struct scsi_capacity_16 *capacity;
2602 struct uscsi_cmd ucmd;
2603 union scsi_cdb cdb;
2604 int status;
2606 (void) memset((char *)capacity, 0, sizeof (struct scsi_capacity_16));
2607 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
2608 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2610 ucmd.uscsi_cdb = (caddr_t)&cdb;
2611 ucmd.uscsi_cdblen = CDB_GROUP4;
2612 ucmd.uscsi_bufaddr = (caddr_t)capacity;
2613 ucmd.uscsi_buflen = sizeof (struct scsi_capacity_16);
2616 * Read Capacity (16) is a Service Action In command. One
2617 * command byte (0x9E) is overloaded for multiple operations,
2618 * with the second CDB byte specifying the desired operation
2620 cdb.scc_cmd = SCMD_SVC_ACTION_IN_G4;
2621 cdb.cdb_opaque[1] = SSVC_ACTION_READ_CAPACITY_G4;
2624 * Fill in allocation length field
2626 cdb.cdb_opaque[10] =
2627 (uchar_t)((ucmd.uscsi_buflen & 0xff000000) >> 24);
2628 cdb.cdb_opaque[11] =
2629 (uchar_t)((ucmd.uscsi_buflen & 0x00ff0000) >> 16);
2630 cdb.cdb_opaque[12] =
2631 (uchar_t)((ucmd.uscsi_buflen & 0x0000ff00) >> 8);
2632 cdb.cdb_opaque[13] =
2633 (uchar_t)(ucmd.uscsi_buflen & 0x000000ff);
2635 status = uscsi_cmd(fd, &ucmd,
2636 (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2638 if (status) {
2639 if (option_msg) {
2640 err_print("Read capacity 16 failed\n");
2642 } else if (option_msg && diag_msg) {
2644 * Dump the capacity data if anyone's interested
2646 dump("Capacity: ", (caddr_t)capacity,
2647 sizeof (struct scsi_capacity_16), HEX_ONLY);
2650 capacity->sc_capacity = BE_64(capacity->sc_capacity);
2651 capacity->sc_lbasize = BE_32(capacity->sc_lbasize);
2653 return (status);
2657 uscsi_read_capacity(fd, capacity)
2658 int fd;
2659 struct scsi_capacity_16 *capacity;
2661 struct uscsi_cmd ucmd;
2662 union scsi_cdb cdb;
2663 int status;
2664 struct scsi_capacity cap_old;
2667 * Build and execute the uscsi ioctl
2669 (void) memset((char *)capacity, 0, sizeof (struct scsi_capacity_16));
2670 (void) memset((char *)&cap_old, 0, sizeof (struct scsi_capacity));
2671 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
2672 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2673 cdb.scc_cmd = SCMD_READ_CAPACITY;
2674 ucmd.uscsi_cdb = (caddr_t)&cdb;
2675 ucmd.uscsi_cdblen = CDB_GROUP1;
2676 ucmd.uscsi_bufaddr = (caddr_t)&cap_old;
2677 ucmd.uscsi_buflen = sizeof (struct scsi_capacity);
2678 status = uscsi_cmd(fd, &ucmd,
2679 (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2681 if (cap_old.capacity == UINT_MAX32) {
2683 * A capacity of 0xffffffff in response to a
2684 * READ CAPACITY 10 indicates that the lun
2685 * is too large to report the size in a 32 bit
2686 * value, and a READ CAPACITY 16 is required
2687 * to get the correct size.
2689 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
2690 (void) memset((char *)&cdb, 0,
2691 sizeof (union scsi_cdb));
2693 ucmd.uscsi_cdb = (caddr_t)&cdb;
2694 ucmd.uscsi_cdblen = CDB_GROUP4;
2695 ucmd.uscsi_bufaddr = (caddr_t)capacity;
2696 ucmd.uscsi_buflen = sizeof (struct scsi_capacity_16);
2699 * Read Capacity (16) is a Service Action In command. One
2700 * command byte (0x9E) is overloaded for multiple operations,
2701 * with the second CDB byte specifying the desired operation
2703 cdb.scc_cmd = SCMD_SVC_ACTION_IN_G4;
2704 cdb.cdb_opaque[1] = SSVC_ACTION_READ_CAPACITY_G4;
2707 * Fill in allocation length field
2709 cdb.cdb_opaque[10] =
2710 (uchar_t)((ucmd.uscsi_buflen & 0xff000000) >> 24);
2711 cdb.cdb_opaque[11] =
2712 (uchar_t)((ucmd.uscsi_buflen & 0x00ff0000) >> 16);
2713 cdb.cdb_opaque[12] =
2714 (uchar_t)((ucmd.uscsi_buflen & 0x0000ff00) >> 8);
2715 cdb.cdb_opaque[13] =
2716 (uchar_t)(ucmd.uscsi_buflen & 0x000000ff);
2718 status = uscsi_cmd(fd, &ucmd,
2719 (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2722 if (status) {
2723 if (option_msg) {
2725 * Indicate which of the commands failed
2727 if (cdb.scc_cmd == SCMD_READ_CAPACITY) {
2728 err_print("Read capacity failed\n");
2729 } else {
2730 err_print("Read capacity 16 failed\n");
2733 } else if (option_msg && diag_msg) {
2735 * Dump the capacity data if anyone's interested
2737 if (cap_old.capacity == UINT_MAX32) {
2738 dump("Capacity: ", (caddr_t)capacity,
2739 sizeof (struct scsi_capacity_16), HEX_ONLY);
2740 } else {
2741 dump("Capacity: ", (caddr_t)&cap_old,
2742 sizeof (struct scsi_capacity), HEX_ONLY);
2746 if (cap_old.capacity == UINT_MAX32) {
2747 capacity->sc_capacity = BE_64(capacity->sc_capacity);
2748 capacity->sc_lbasize = BE_32(capacity->sc_lbasize);
2749 } else {
2750 capacity->sc_capacity = (uint64_t)BE_32(cap_old.capacity);
2751 capacity->sc_lbasize = BE_32(cap_old.lbasize);
2754 return (status);
2759 * Reserve the current disk
2761 static int
2762 uscsi_reserve_release(int fd, int cmd)
2764 int status = 0;
2765 #ifdef sparc
2766 struct uscsi_cmd ucmd;
2767 union scsi_cdb cdb;
2770 * Build and execute the uscsi ioctl
2772 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
2773 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2774 cdb.scc_cmd = (cmd == SCMD_RESERVE) ? SCMD_RESERVE : SCMD_RELEASE;
2775 ucmd.uscsi_cdb = (caddr_t)&cdb;
2776 ucmd.uscsi_cdblen = CDB_GROUP0;
2777 status = uscsi_cmd(fd, &ucmd,
2778 (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2780 if (status) {
2782 * Reserve/Release(6) failed.
2783 * Try Reserve/Release(10) , if it succeeds then
2784 * return success.
2786 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
2787 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2788 ucmd.uscsi_cdb = (caddr_t)&cdb;
2789 cdb.scc_cmd = (cmd == SCMD_RESERVE) ?
2790 SCMD_RESERVE_G1 : SCMD_RELEASE_G1;
2791 ucmd.uscsi_cdblen = CDB_GROUP1;
2792 status = uscsi_cmd(fd, &ucmd,
2793 (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2794 if (status) {
2795 if (option_msg) {
2796 err_print("%s failed\n", (cmd == SCMD_RESERVE) ?
2797 "Reserve" : "Release");
2801 #else /* not sparc */
2804 #endif /* not sparc */
2806 return (status);
2810 scsi_dump_mode_sense_pages(page_control)
2811 int page_control;
2813 struct uscsi_cmd ucmd;
2814 union scsi_cdb cdb;
2815 char *msbuf;
2816 int nbytes;
2817 char *pc_str;
2818 int status;
2819 struct mode_header *mh;
2820 char *p;
2821 struct mode_page *mp;
2822 int n;
2823 char s[16];
2824 int result = 0;
2826 pc_str = find_string(page_control_strings, page_control);
2829 * Allocate memory for the mode sense buffer.
2831 nbytes = 255;
2832 msbuf = (char *)zalloc(nbytes);
2835 * Build and execute the uscsi ioctl
2837 (void) memset(msbuf, 0, nbytes);
2838 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
2839 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
2840 cdb.scc_cmd = SCMD_MODE_SENSE;
2841 FORMG0COUNT(&cdb, (uchar_t)nbytes);
2842 cdb.cdb_opaque[2] = page_control | 0x3f;
2843 ucmd.uscsi_cdb = (caddr_t)&cdb;
2844 ucmd.uscsi_cdblen = CDB_GROUP0;
2845 ucmd.uscsi_bufaddr = msbuf;
2846 ucmd.uscsi_buflen = nbytes;
2847 status = uscsi_cmd(cur_file, &ucmd,
2848 (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
2849 if (status) {
2850 err_print("\nMode sense page 0x3f (%s) failed\n",
2851 pc_str);
2852 result = 1;
2853 } else {
2854 err_print("\nMode sense pages (%s):\n", pc_str);
2855 mh = (struct mode_header *)msbuf;
2856 nbytes = mh->length - sizeof (struct mode_header) -
2857 mh->bdesc_length + 1;
2858 p = msbuf + sizeof (struct mode_header) +
2859 mh->bdesc_length;
2860 dump(" ", msbuf, sizeof (struct mode_header) +
2861 (int)mh->bdesc_length, HEX_ONLY);
2862 while (nbytes > 0) {
2863 mp = (struct mode_page *)p;
2864 n = mp->length + sizeof (struct mode_page);
2865 nbytes -= n;
2866 if (nbytes < 0)
2867 break;
2868 (void) sprintf(s, " %3x: ", mp->code);
2869 dump(s, p, n, HEX_ONLY);
2870 p += n;
2872 if (nbytes < 0) {
2873 err_print(" Sense data formatted incorrectly:\n");
2874 dump(" ", msbuf, (int)mh->length+1, HEX_ONLY);
2875 result = 1;
2877 err_print("\n");
2880 free(msbuf);
2881 return (result);
2885 static void
2886 scsi_printerr(ucmd, rq, rqlen)
2887 struct uscsi_cmd *ucmd;
2888 struct scsi_extended_sense *rq;
2889 int rqlen;
2891 diskaddr_t blkno;
2892 struct scsi_descr_sense_hdr *sdsp =
2893 (struct scsi_descr_sense_hdr *)rq;
2895 switch (rq->es_key) {
2896 case KEY_NO_SENSE:
2897 err_print("No sense error");
2898 break;
2899 case KEY_RECOVERABLE_ERROR:
2900 err_print("Recoverable error");
2901 break;
2902 case KEY_NOT_READY:
2903 err_print("Not ready error");
2904 break;
2905 case KEY_MEDIUM_ERROR:
2906 err_print("Medium error");
2907 break;
2908 case KEY_HARDWARE_ERROR:
2909 err_print("Hardware error");
2910 break;
2911 case KEY_ILLEGAL_REQUEST:
2912 err_print("Illegal request");
2913 break;
2914 case KEY_UNIT_ATTENTION:
2915 err_print("Unit attention error");
2916 break;
2917 case KEY_WRITE_PROTECT:
2918 err_print("Write protect error");
2919 break;
2920 case KEY_BLANK_CHECK:
2921 err_print("Blank check error");
2922 break;
2923 case KEY_VENDOR_UNIQUE:
2924 err_print("Vendor unique error");
2925 break;
2926 case KEY_COPY_ABORTED:
2927 err_print("Copy aborted error");
2928 break;
2929 case KEY_ABORTED_COMMAND:
2930 err_print("Aborted command");
2931 break;
2932 case KEY_EQUAL:
2933 err_print("Equal error");
2934 break;
2935 case KEY_VOLUME_OVERFLOW:
2936 err_print("Volume overflow");
2937 break;
2938 case KEY_MISCOMPARE:
2939 err_print("Miscompare error");
2940 break;
2941 case KEY_RESERVED:
2942 err_print("Reserved error");
2943 break;
2944 default:
2945 err_print("Unknown error");
2946 break;
2949 err_print(" during %s", scsi_find_command_name(ucmd->uscsi_cdb[0]));
2952 * Get asc, ascq and info field from sense data. There are two
2953 * possible formats (fixed sense data and descriptor sense data)
2954 * depending on the value of es_code.
2956 switch (rq->es_code) {
2957 case CODE_FMT_DESCR_CURRENT:
2958 case CODE_FMT_DESCR_DEFERRED:
2959 blkno =
2960 (diskaddr_t)scsi_extract_sense_info_descr(sdsp, rqlen);
2961 if (blkno != (diskaddr_t)-1) {
2962 err_print(": block %lld (0x%llx) (", blkno, blkno);
2963 pr_dblock(err_print, blkno);
2964 err_print(")");
2967 err_print("\n");
2969 err_print("ASC: 0x%x ASCQ: 0x%x\n",
2970 sdsp->ds_add_code, sdsp->ds_qual_code);
2971 break;
2972 case CODE_FMT_FIXED_CURRENT:
2973 case CODE_FMT_FIXED_DEFERRED:
2974 default:
2975 if (rq->es_valid) {
2976 blkno = (rq->es_info_1 << 24) |
2977 (rq->es_info_2 << 16) |
2978 (rq->es_info_3 << 8) | rq->es_info_4;
2979 err_print(": block %lld (0x%llx) (", blkno, blkno);
2980 pr_dblock(err_print, blkno);
2981 err_print(")");
2984 err_print("\n");
2986 if (rq->es_add_len >= 6) {
2987 err_print("ASC: 0x%x ASCQ: 0x%x\n",
2988 rq->es_add_code, rq->es_qual_code);
2990 break;
2993 if (option_msg && diag_msg) {
2994 if (rq->es_key == KEY_ILLEGAL_REQUEST) {
2995 dump("cmd: ", (caddr_t)ucmd,
2996 sizeof (struct uscsi_cmd), HEX_ONLY);
2997 dump("cdb: ", (caddr_t)ucmd->uscsi_cdb,
2998 ucmd->uscsi_cdblen, HEX_ONLY);
3000 dump("sense: ", (caddr_t)rq, rqlen, HEX_ONLY);
3003 if (option_msg) {
3004 switch (rq->es_code) {
3005 case CODE_FMT_DESCR_CURRENT:
3006 case CODE_FMT_DESCR_DEFERRED:
3007 scsi_print_descr_sense(sdsp, rqlen);
3008 break;
3009 case CODE_FMT_FIXED_CURRENT:
3010 case CODE_FMT_FIXED_DEFERRED:
3011 default:
3012 scsi_print_extended_sense(rq, rqlen);
3013 break;
3019 * Retrieve "information" field from descriptor format
3020 * sense data. Iterates through each sense descriptor
3021 * looking for the information descriptor and returns
3022 * the information field from that descriptor.
3024 static diskaddr_t
3025 scsi_extract_sense_info_descr(struct scsi_descr_sense_hdr *sdsp, int rqlen)
3027 diskaddr_t result;
3028 uint8_t *descr_offset;
3029 int valid_sense_length;
3030 struct scsi_information_sense_descr *isd;
3033 * Initialize result to -1 indicating there is no information
3034 * descriptor
3036 result = (diskaddr_t)-1;
3039 * The first descriptor will immediately follow the header
3041 descr_offset = (uint8_t *)(sdsp+1); /* Pointer arithmetic */
3044 * Calculate the amount of valid sense data
3046 valid_sense_length =
3047 min((sizeof (struct scsi_descr_sense_hdr) +
3048 sdsp->ds_addl_sense_length),
3049 rqlen);
3052 * Iterate through the list of descriptors, stopping when we
3053 * run out of sense data
3055 while ((descr_offset + sizeof (struct scsi_information_sense_descr)) <=
3056 (uint8_t *)sdsp + valid_sense_length) {
3058 * Check if this is an information descriptor. We can
3059 * use the scsi_information_sense_descr structure as a
3060 * template sense the first two fields are always the
3061 * same
3063 isd = (struct scsi_information_sense_descr *)descr_offset;
3064 if (isd->isd_descr_type == DESCR_INFORMATION) {
3066 * Found an information descriptor. Copy the
3067 * information field. There will only be one
3068 * information descriptor so we can stop looking.
3070 result =
3071 (((diskaddr_t)isd->isd_information[0] << 56) |
3072 ((diskaddr_t)isd->isd_information[1] << 48) |
3073 ((diskaddr_t)isd->isd_information[2] << 40) |
3074 ((diskaddr_t)isd->isd_information[3] << 32) |
3075 ((diskaddr_t)isd->isd_information[4] << 24) |
3076 ((diskaddr_t)isd->isd_information[5] << 16) |
3077 ((diskaddr_t)isd->isd_information[6] << 8) |
3078 ((diskaddr_t)isd->isd_information[7]));
3079 break;
3083 * Get pointer to the next descriptor. The "additional
3084 * length" field holds the length of the descriptor except
3085 * for the "type" and "additional length" fields, so
3086 * we need to add 2 to get the total length.
3088 descr_offset += (isd->isd_addl_length + 2);
3091 return (result);
3095 * Return a pointer to a string telling us the name of the command.
3097 static char *
3098 scsi_find_command_name(uint_t cmd)
3100 struct scsi_command_name *c;
3102 for (c = scsi_command_names; c->command != SCMD_UNKNOWN; c++)
3103 if (c->command == cmd)
3104 break;
3105 return (c->name);
3110 * Return true if we support a particular mode page
3113 scsi_supported_page(int page) {
3114 return (page == 1 || page == 2 || page == 3 || page == 4 ||
3115 page == 8 || page == 0x38);
3120 apply_chg_list(int pageno, int pagsiz, uchar_t *curbits,
3121 uchar_t *chgbits, struct chg_list *chglist)
3123 uchar_t c;
3124 int i;
3125 int m;
3126 int delta;
3127 int changed = 0;
3129 while (chglist != NULL) {
3130 if (chglist->pageno == pageno &&
3131 chglist->byteno < pagsiz) {
3132 i = chglist->byteno;
3133 c = curbits[i];
3134 switch (chglist->mode) {
3135 case CHG_MODE_SET:
3136 c |= (uchar_t)chglist->value;
3137 break;
3138 case CHG_MODE_CLR:
3139 c &= (uchar_t)chglist->value;
3140 break;
3141 case CHG_MODE_ABS:
3142 c = (uchar_t)chglist->value;
3143 break;
3146 * Figure out which bits changed, and
3147 * are marked as changeable. If this
3148 * result actually differs from the
3149 * current value, update the current
3150 * value, and note that a mode select
3151 * should be done.
3153 delta = c ^ curbits[i];
3154 for (m = 0x01; m < 0x100; m <<= 1) {
3155 if ((delta & m) && (chgbits[i] & m)) {
3156 curbits[i] ^= m;
3157 changed = 1;
3161 chglist = chglist->next;
3164 return (changed);
3169 * Return whether a given page is affected by an item on
3170 * the change list.
3172 static int
3173 chg_list_affects_page(chglist, pageno)
3174 struct chg_list *chglist;
3175 int pageno;
3177 while (chglist != NULL) {
3178 if (chglist->pageno == pageno) {
3179 return (1);
3181 chglist = chglist->next;
3184 return (0);
3189 * Labels for the various fields of the scsi_extended_sense structure
3191 static char *scsi_extended_sense_labels[] = {
3192 "Request sense valid: ",
3193 "Error class and code: ",
3194 "Segment number: ",
3195 "Filemark: ",
3196 "End-of-medium: ",
3197 "Incorrect length indicator: ",
3198 "Sense key: ",
3199 "Information field: ",
3200 "Additional sense length: ",
3201 "Command-specific information: ",
3202 "Additional sense code: ",
3203 "Additional sense code qualifier: ",
3204 "Field replaceable unit code: ",
3205 "Sense-key specific: ",
3206 "Additional sense bytes: "
3211 * Display the full scsi_extended_sense as returned by the device
3213 static void
3214 scsi_print_extended_sense(rq, rqlen)
3215 struct scsi_extended_sense *rq;
3216 int rqlen;
3218 char **p;
3220 p = scsi_extended_sense_labels;
3221 if (rqlen < (sizeof (*rq) - 2) || !rq->es_valid) {
3223 * target should be capable of returning at least 18
3224 * bytes of data, i.e upto rq->es_skey_specific field.
3225 * The additional sense bytes (2 or more ...) are optional.
3227 return;
3230 fmt_print("\n%s%s\n", *p++, rq->es_valid ? "yes" : "no");
3231 fmt_print("%s0x%02x\n", *p++, (rq->es_class << 4) + rq->es_code);
3232 fmt_print("%s%d\n", *p++, rq->es_segnum);
3233 fmt_print("%s%s\n", *p++, rq->es_filmk ? "yes" : "no");
3234 fmt_print("%s%s\n", *p++, rq->es_eom ? "yes" : "no");
3235 fmt_print("%s%s\n", *p++, rq->es_ili ? "yes" : "no");
3236 fmt_print("%s%d\n", *p++, rq->es_key);
3238 fmt_print("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++, rq->es_info_1,
3239 rq->es_info_2, rq->es_info_3, rq->es_info_4);
3240 fmt_print("%s%d\n", *p++, rq->es_add_len);
3241 fmt_print("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++, rq->es_cmd_info[0],
3242 rq->es_cmd_info[1], rq->es_cmd_info[2], rq->es_cmd_info[3]);
3243 fmt_print("%s0x%02x = %d\n", *p++, rq->es_add_code, rq->es_add_code);
3244 fmt_print("%s0x%02x = %d\n", *p++, rq->es_qual_code, rq->es_qual_code);
3245 fmt_print("%s%d\n", *p++, rq->es_fru_code);
3246 fmt_print("%s0x%02x 0x%02x 0x%02x\n", *p++, rq->es_skey_specific[0],
3247 rq->es_skey_specific[1], rq->es_skey_specific[2]);
3248 if (rqlen >= sizeof (*rq)) {
3249 fmt_print("%s0x%02x 0x%02x%s\n", *p, rq->es_add_info[0],
3250 rq->es_add_info[1], (rqlen > sizeof (*rq)) ? " ..." : "");
3253 fmt_print("\n");
3257 * Labels for the various fields of the scsi_descr_sense_hdr structure
3259 static char *scsi_descr_sense_labels[] = {
3260 "Error class and code: ",
3261 "Sense key: ",
3262 "Additional sense length: ",
3263 "Additional sense code: ",
3264 "Additional sense code qualifier: ",
3265 "Additional sense bytes: "
3270 * Display the full descriptor sense data as returned by the device
3273 static void
3274 scsi_print_descr_sense(rq, rqlen)
3275 struct scsi_descr_sense_hdr *rq;
3276 int rqlen;
3278 char **p;
3279 uint8_t *descr_offset;
3280 int valid_sense_length;
3281 struct scsi_information_sense_descr *isd;
3283 p = scsi_descr_sense_labels;
3284 if (rqlen < sizeof (struct scsi_descr_sense_hdr)) {
3286 * target must return at least 8 bytes of data
3288 return;
3291 /* Print descriptor sense header */
3292 fmt_print("%s0x%02x\n", *p++, (rq->ds_class << 4) + rq->ds_code);
3293 fmt_print("%s%d\n", *p++, rq->ds_key);
3295 fmt_print("%s%d\n", *p++, rq->ds_addl_sense_length);
3296 fmt_print("%s0x%02x = %d\n", *p++, rq->ds_add_code, rq->ds_add_code);
3297 fmt_print("%s0x%02x = %d\n", *p++, rq->ds_qual_code, rq->ds_qual_code);
3298 fmt_print("\n");
3301 * Now print any sense descriptors. The first descriptor will
3302 * immediately follow the header
3304 descr_offset = (uint8_t *)(rq+1); /* Pointer arithmetic */
3307 * Calculate the amount of valid sense data
3309 valid_sense_length =
3310 min((sizeof (struct scsi_descr_sense_hdr) +
3311 rq->ds_addl_sense_length), rqlen);
3314 * Iterate through the list of descriptors, stopping when we
3315 * run out of sense data. Descriptor format is:
3317 * <Descriptor type> <Descriptor length> <Descriptor data> ...
3319 while ((descr_offset + *(descr_offset + 1)) <=
3320 (uint8_t *)rq + valid_sense_length) {
3322 * Determine descriptor type. We can use the
3323 * scsi_information_sense_descr structure as a
3324 * template since the first two fields are always the
3325 * same.
3327 isd = (struct scsi_information_sense_descr *)descr_offset;
3328 switch (isd->isd_descr_type) {
3329 case DESCR_INFORMATION: {
3330 uint64_t information;
3332 information =
3333 (((uint64_t)isd->isd_information[0] << 56) |
3334 ((uint64_t)isd->isd_information[1] << 48) |
3335 ((uint64_t)isd->isd_information[2] << 40) |
3336 ((uint64_t)isd->isd_information[3] << 32) |
3337 ((uint64_t)isd->isd_information[4] << 24) |
3338 ((uint64_t)isd->isd_information[5] << 16) |
3339 ((uint64_t)isd->isd_information[6] << 8) |
3340 ((uint64_t)isd->isd_information[7]));
3341 fmt_print("Information field: "
3342 "%0llx\n", information);
3343 break;
3345 case DESCR_COMMAND_SPECIFIC: {
3346 struct scsi_cmd_specific_sense_descr *c =
3347 (struct scsi_cmd_specific_sense_descr *)isd;
3348 uint64_t cmd_specific;
3350 cmd_specific =
3351 (((uint64_t)c->css_cmd_specific_info[0] << 56) |
3352 ((uint64_t)c->css_cmd_specific_info[1] << 48) |
3353 ((uint64_t)c->css_cmd_specific_info[2] << 40) |
3354 ((uint64_t)c->css_cmd_specific_info[3] << 32) |
3355 ((uint64_t)c->css_cmd_specific_info[4] << 24) |
3356 ((uint64_t)c->css_cmd_specific_info[5] << 16) |
3357 ((uint64_t)c->css_cmd_specific_info[6] << 8) |
3358 ((uint64_t)c->css_cmd_specific_info[7]));
3359 fmt_print("Command-specific information: "
3360 "%0llx\n", cmd_specific);
3361 break;
3363 case DESCR_SENSE_KEY_SPECIFIC: {
3364 struct scsi_sk_specific_sense_descr *ssd =
3365 (struct scsi_sk_specific_sense_descr *)isd;
3366 uint8_t *sk_spec_ptr = (uint8_t *)&ssd->sss_data;
3367 fmt_print("Sense-key specific: "
3368 "0x%02x 0x%02x 0x%02x\n", sk_spec_ptr[0],
3369 sk_spec_ptr[1], sk_spec_ptr[2]);
3370 break;
3372 case DESCR_FRU: {
3373 struct scsi_fru_sense_descr *fsd =
3374 (struct scsi_fru_sense_descr *)isd;
3375 fmt_print("Field replaceable unit code: "
3376 "%d\n", fsd->fs_fru_code);
3377 break;
3379 case DESCR_BLOCK_COMMANDS: {
3380 struct scsi_block_cmd_sense_descr *bsd =
3381 (struct scsi_block_cmd_sense_descr *)isd;
3382 fmt_print("Incorrect length indicator: "
3383 "%s\n", bsd->bcs_ili ? "yes" : "no");
3384 break;
3386 default:
3387 /* Ignore */
3388 break;
3392 * Get pointer to the next descriptor. The "additional
3393 * length" field holds the length of the descriptor except
3394 * for the "type" and "additional length" fields, so
3395 * we need to add 2 to get the total length.
3397 descr_offset += (isd->isd_addl_length + 2);
3400 fmt_print("\n");
3404 * Function checks if READ DEFECT DATA command is supported
3405 * on the current disk.
3407 static int
3408 check_support_for_defects()
3410 struct uscsi_cmd ucmd;
3411 union scsi_cdb cdb;
3412 struct scsi_defect_list def_list;
3413 struct scsi_defect_hdr *hdr;
3414 int status;
3415 char rqbuf[255];
3416 struct scsi_extended_sense *rq;
3418 hdr = (struct scsi_defect_hdr *)&def_list;
3421 * First get length of list by asking for the header only.
3423 (void) memset((char *)&def_list, 0, sizeof (def_list));
3426 * Build and execute the uscsi ioctl
3428 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
3429 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
3430 (void) memset((char *)rqbuf, 0, 255);
3431 cdb.scc_cmd = SCMD_READ_DEFECT_LIST;
3432 FORMG1COUNT(&cdb, sizeof (struct scsi_defect_hdr));
3433 cdb.cdb_opaque[2] = DLD_MAN_DEF_LIST | DLD_BFI_FORMAT;
3434 ucmd.uscsi_cdb = (caddr_t)&cdb;
3435 ucmd.uscsi_cdblen = CDB_GROUP1;
3436 ucmd.uscsi_bufaddr = (caddr_t)hdr;
3437 ucmd.uscsi_buflen = sizeof (struct scsi_defect_hdr);
3438 ucmd.uscsi_rqbuf = rqbuf;
3439 ucmd.uscsi_rqlen = sizeof (rqbuf);
3440 ucmd.uscsi_rqresid = sizeof (rqbuf);
3441 rq = (struct scsi_extended_sense *)ucmd.uscsi_rqbuf;
3443 status = uscsi_cmd(cur_file, &ucmd,
3444 (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
3446 if (status != 0) {
3448 * check if read_defect_list_is_supported.
3450 if (ucmd.uscsi_rqstatus == STATUS_GOOD &&
3451 rq->es_key == KEY_ILLEGAL_REQUEST &&
3452 rq->es_add_code == INVALID_OPCODE)
3453 return (0);
3455 return (1);
3459 * Format the disk, the whole disk, and nothing but the disk.
3460 * Function will be called only for disks
3461 * which do not support read defect list command.
3463 static int
3464 scsi_format_without_defects()
3466 struct uscsi_cmd ucmd;
3467 union scsi_cdb cdb;
3468 struct scsi_defect_hdr defect_hdr;
3469 int status;
3472 * Construct the uscsi format ioctl.
3473 * Use fmtdata = 0 , indicating the no source of
3474 * defects information is provided .
3475 * Function will be called only for disks
3476 * which do not support read defect list command.
3478 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
3479 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
3480 (void) memset((char *)&defect_hdr, 0, sizeof (defect_hdr));
3481 cdb.scc_cmd = SCMD_FORMAT;
3482 ucmd.uscsi_cdb = (caddr_t)&cdb;
3483 ucmd.uscsi_cdblen = CDB_GROUP0;
3484 ucmd.uscsi_bufaddr = (caddr_t)&defect_hdr;
3485 ucmd.uscsi_buflen = sizeof (defect_hdr);
3486 cdb.cdb_opaque[1] = 0;
3488 * Issue the format ioctl
3490 status = uscsi_cmd(cur_file, &ucmd,
3491 (option_msg && diag_msg) ? F_NORMAL : F_SILENT);
3492 return (status);
3496 * Name: test_until_ready
3498 * Description: This function is used by scsi_format and
3499 * scsi_format_raw to poll the device while the FORMAT
3500 * UNIT cdb in in progress.
3502 * Parameters:
3503 * file descriptor to poll
3505 * Returns:
3506 * 0 - good status
3507 * !0 - bad status
3509 static int test_until_ready(int fd) {
3510 int status = 1;
3511 struct uscsi_cmd ucmd;
3512 union scsi_cdb cdb;
3513 struct scsi_extended_sense sense;
3514 time_t start, check, time_left;
3515 uint16_t progress;
3516 int hour, min, sec;
3518 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
3519 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
3521 ucmd.uscsi_cdb = (caddr_t)&cdb;
3522 ucmd.uscsi_cdblen = CDB_GROUP0;
3523 ucmd.uscsi_rqbuf = (caddr_t)&sense;
3524 ucmd.uscsi_rqlen = SENSE_LEN;
3526 start = check = time((time_t *)0);
3528 /* Loop sending TEST UNIT READY until format is complete */
3529 while (status) {
3530 /* clear last request sense data */
3531 ucmd.uscsi_rqstatus = 0;
3532 ucmd.uscsi_rqresid = 0;
3533 (void) memset((char *)&sense, 0, SENSE_LEN);
3535 /* issue test unit ready */
3536 status = uscsi_cmd(fd, &ucmd, F_SILENT
3537 | F_RQENABLE);
3539 check = time((time_t *)0);
3541 /* If device returns not ready we get EIO */
3542 if (status != 0 && errno == EIO) {
3543 /* Check SKSV if progress indication is avail */
3544 if (sense.es_skey_specific[0] == 0x80) {
3545 /* Store progress indication */
3546 progress = ((uint16_t)sense.
3547 es_skey_specific[1]) << 8;
3548 progress |= (uint16_t)sense.
3549 es_skey_specific[2];
3550 progress = (uint16_t)(((float)progress /
3551 (float)PROGRESS_INDICATION_BASE)*100);
3553 fmt_print("\015");
3556 * check to see if we can estimate
3557 * time remaining - wait until the format
3558 * is at least 5 percent complete to avoid
3559 * wildly-fluctuating time estimates
3561 if ((check - start) <= 0 || progress <= 5) {
3562 /* unable to estimate */
3563 fmt_print(" %02d%% complete ",
3564 progress);
3565 } else {
3566 /* display with estimated time */
3567 time_left = (time_t)(((float)(check
3568 - start) / (float)progress) *
3569 (float)(100 - progress));
3570 sec = time_left % 60;
3571 min = (time_left / 60) % 60;
3572 hour = time_left / 3600;
3574 fmt_print(" %02d%% complete "
3575 "(%02d:%02d:%02d remaining) ",
3576 progress, hour, min, sec);
3578 /* flush or the screen will not update */
3579 (void) fflush(stdout);
3581 } else {
3582 /* format not in progress */
3583 if (option_msg) {
3584 fmt_print("\nRequest Sense ASC=0x%x ASCQ=0x%x",
3585 sense.es_add_code, sense.es_qual_code);
3587 break;
3590 /* delay so we don't waste cpu time */
3591 (void) sleep(RETRY_DELAY);
3593 return (status);
3597 * Get the current protection type from the PROT_EN and P_TYPE
3599 uint8_t
3600 get_cur_protection_type(struct scsi_capacity_16 *capacity)
3602 uint8_t cp13;
3603 uint8_t prot_en;
3604 uint8_t p_type;
3606 cp13 = ((capacity->sc_rsvd0 & 0x3f) << 2)
3607 | ((capacity->sc_prot_en & 0x01) << 1)
3608 | (capacity->sc_rto_en & 0x01);
3609 prot_en = cp13 & 0x01;
3610 if (prot_en == 0) {
3611 p_type = 0;
3612 } else {
3613 p_type = (cp13 << 4) >> 5;
3614 p_type += 1;
3616 return (p_type);