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]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * This file contains functions implementing the scsi menu commands.
30 * These functions are intended for expert use only, and provide
31 * a raw access to a scsi device's mode pages. The ability to
32 * issue a raw format command is also provided, should a page be
33 * changed that requires a format.
42 #include "menu_scsi.h"
43 #include "ctlr_scsi.h"
50 * ANSI prototypes for local static functions
52 static int do_mode_sense(int);
53 static int do_mode_sense_all(void);
54 static int do_mode_select(struct chg_list
*);
55 static int do_format(void);
56 static void do_list(void);
57 static int do_inquiry(void);
58 static void do_apply(void);
59 static void do_cancel(void);
60 static void do_display(void);
61 static int parse_change_spec(char *, char *, int, struct chg_list
*);
62 static void add_new_change_list_item(struct chg_list
*);
63 static void free_change_list(void);
64 static void do_default(char *);
65 static void default_all_pages(void);
66 static int default_page(int);
68 static int do_mode_sense();
69 static int do_mode_sense_all();
70 static int do_mode_select();
71 static int do_format();
72 static void do_list();
73 static int do_inquiry();
74 static void do_apply();
75 static void do_cancel();
76 static void do_display();
77 static int parse_change_spec();
78 static void add_new_change_list_item();
79 static void free_change_list();
80 static void do_default();
81 static void default_all_pages();
82 static int default_page();
87 * Menu data for the SCSI menu display
89 static char *scsi_menu_strings
[] = {
90 "p<n> - display a mode sense page",
91 "p<n> b<n> <op> [~]<n> - change a byte and issue mode select",
92 "b<n> <op> [~]<n> - add an operation to the mode select list",
93 " for the current page",
95 " where: p<n> specifies the page with page code <n>",
96 " b<n> specifies byte <n> of the page",
97 " <op> can be one of the following operators:",
98 " = (set specified value)",
99 " |= (bitwise OR with current value)",
100 " &= (bitwise AND with current value)",
101 " <n> can be a decimal value in the range 0-255,",
102 " or two hexadecimal digits, in the form 0x<xx>.",
103 " [~] complements the specified value",
105 "apply - apply mode select list",
106 "cancel - cancel mode select list",
107 "display - display mode select list",
108 "all - display all supported mode sense pages",
109 "default p<n> - mode select page <n> to default values",
110 "default all - mode select all pages to default values",
111 "format - format without standard mode selects",
112 "inquiry - display device's inquiry response",
113 "list - list common SCSI-2 mode pages",
114 "!<cmd> - execute <cmd> , then return"
117 #define N_SCSI_STRINGS (sizeof (scsi_menu_strings) / sizeof (char *))
127 #define CMD_INQUIRY 5
130 #define CMD_DISPLAY 8
133 * SCSI menu commands for minimum recognition
135 static struct slist cmds_list
[] = {
136 { "all", NULL
, CMD_ALL
},
137 { "format", NULL
, CMD_FORMAT
},
138 { "quit", NULL
, CMD_QUIT
},
139 { "help", NULL
, CMD_HELP
},
140 { "?", NULL
, CMD_HELP
},
141 { "list", NULL
, CMD_LIST
},
142 { "inquiry", NULL
, CMD_INQUIRY
},
143 { "apply", NULL
, CMD_APPLY
},
144 { "cancel", NULL
, CMD_CANCEL
},
145 { "display", NULL
, CMD_DISPLAY
},
150 * Implied page for mode select change lists
152 static int current_page
;
153 static struct chg_list
*change_list
;
156 * Manage the SCSI menu.
157 * Parse input and dispatch to the appropriate functions.
158 * The commands we accept are not simple one-word commands,
159 * so we cannot use the standard format menu-handling functions.
167 struct menu_item scsi_menu
[N_SCSI_STRINGS
+1];
168 struct chg_list change_item
;
169 struct chg_list
*chg_item
;
170 char s
[MAXPATHLEN
], nclean
[MAXPATHLEN
];
178 * Warn casual users that maybe they should not be
182 "Warning: these functions are intended for expert use only, for\n"
183 "debugging disk devices and for unusual configuration settings.\n"
184 "It is recommended that you do not use this menu for normal disk\n"
185 "configuration and formatting, unless you have explicit instructions,\n"
186 "or know exactly what you are doing.\n");
189 * Initialize change list and current page to empty
195 * Build and display the menu.
196 * we only use this for display purposes.
198 for (i
= 0; i
< N_SCSI_STRINGS
; i
++) {
199 scsi_menu
[i
].menu_cmd
= scsi_menu_strings
[i
];
200 scsi_menu
[i
].menu_func
= NULL
;
201 scsi_menu
[i
].menu_state
= truefxn
;
203 scsi_menu
[i
].menu_cmd
= NULL
;
204 menu
= create_menu_list(scsi_menu
);
206 * Save the environment so a ctrl-C out of a command lands here.
212 fmt_print("\n\nSCSI MENU:\n");
213 display_menu_list(menu
);
216 * Prompt and get next input line. We don't use the
217 * standard input routine, since we need a little
218 * more flexibility in parsing the input.
221 get_inputline(nclean
, sizeof (nclean
));
223 clean_token(s
, nclean
);
226 * Mark the saved environment active so the user can now
227 * do a ctrl-C to get out of the command.
232 * Figure out what the user chose
234 i
= find_value(cmds_list
, s
, &cmd
);
238 (void) do_mode_sense_all();
247 fmt_print("\n\nSCSI MENU:\n");
248 display_menu_list(menu
);
266 } else if (s
[0] == 'd') {
268 } else if (s
[0] == 'p') {
270 pageno
= (int)strtol(p
, &p2
, 0);
272 err_print("Syntax error: %s\n", s
);
275 current_page
= pageno
;
276 for (p
= p2
; *p
== ' '; p
++)
279 (void) do_mode_sense(pageno
);
280 } else if (*p
== 'b') {
281 if (parse_change_spec(s
, p
, pageno
,
283 (void) do_mode_select(&change_item
);
286 } else if (s
[0] == 'b') {
287 if (current_page
== -1) {
289 Please display the page on which you'd like to do a mode select\n");
292 chg_item
= (struct chg_list
*)
293 zalloc(sizeof (struct chg_list
));
294 if (parse_change_spec(s
, s
, current_page
,
296 add_new_change_list_item(chg_item
);
298 destroy_data((char *)chg_item
);
300 } else if (s
[0] == '!') {
301 (void) execute_shell(&s
[1], sizeof (s
) - 1);
303 } else if (s
[0] != 0) {
304 err_print("Syntax error: %s\n", s
);
308 * Mark the saved environment inactive so ctrl-C doesn't
309 * work at the menu itself.
315 * Clean up the environment stack and free the menu
318 destroy_data((char *)menu
);
321 * Clean up the change list, if anything left over
326 * Make sure user is prompted with previous menu
335 * Do a mode sense on a particular page, and dump the data.
336 * Get all the various flavors: default, current, saved, changeable.
339 do_mode_sense(pageno
)
342 struct scsi_ms_header header
;
343 struct mode_page
*pg
;
344 char msbuf
[MAX_MODE_SENSE_SIZE
];
347 char *default_msg
= "default: ";
348 char *saved_msg
= "saved: ";
349 char *current_msg
= "current: ";
350 char *changeable_msg
= "changeable: ";
353 pg
= (struct mode_page
*)msbuf
;
355 fmt_print("\nPage 0x%x:\n", pageno
);
356 if (uscsi_mode_sense(cur_file
, pageno
, MODE_SENSE_PC_DEFAULT
,
357 msbuf
, MAX_MODE_SENSE_SIZE
, &header
)) {
358 err_print("%sfailed\n", default_msg
);
361 dump(default_msg
, msbuf
, MODESENSE_PAGE_LEN(pg
),
365 if (uscsi_mode_sense(cur_file
, pageno
, MODE_SENSE_PC_CURRENT
,
366 msbuf
, MAX_MODE_SENSE_SIZE
, &header
)) {
367 err_print("%sfailed\n", current_msg
);
370 dump(current_msg
, msbuf
, MODESENSE_PAGE_LEN(pg
),
374 if (uscsi_mode_sense(cur_file
, pageno
, MODE_SENSE_PC_SAVED
,
375 msbuf
, MAX_MODE_SENSE_SIZE
, &header
)) {
376 err_print("%sfailed\n", saved_msg
);
379 dump(saved_msg
, msbuf
, MODESENSE_PAGE_LEN(pg
),
383 if (uscsi_mode_sense(cur_file
, pageno
, MODE_SENSE_PC_CHANGEABLE
,
384 msbuf
, MAX_MODE_SENSE_SIZE
, &header
)) {
385 err_print("%sfailed\n", changeable_msg
);
388 dump(changeable_msg
, msbuf
, MODESENSE_PAGE_LEN(pg
),
398 * Dump all the pages a device supports
405 if (scsi_dump_mode_sense_pages(MODE_SENSE_PC_DEFAULT
)) {
408 if (scsi_dump_mode_sense_pages(MODE_SENSE_PC_CURRENT
)) {
411 if (scsi_dump_mode_sense_pages(MODE_SENSE_PC_SAVED
)) {
414 if (scsi_dump_mode_sense_pages(MODE_SENSE_PC_CHANGEABLE
)) {
423 * Get the current mode sense for a particular page, change
424 * a byte, and issue a mode select. Note that we can only
425 * change a value if the device indicates that those bits
429 do_mode_select(change_item
)
430 struct chg_list
*change_item
;
432 struct scsi_ms_header header
;
433 char saved
[MAX_MODE_SENSE_SIZE
];
434 char changeable
[MAX_MODE_SENSE_SIZE
];
435 struct mode_page
*pg
;
436 struct mode_page
*pg2
;
442 pageno
= change_item
->pageno
;
445 * Get changeable mode sense
447 if (uscsi_mode_sense(cur_file
, pageno
, MODE_SENSE_PC_CHANGEABLE
,
448 changeable
, MAX_MODE_SENSE_SIZE
, &header
)) {
449 err_print("Mode sense on page %x (changeable) failed\n",
455 * Get saved mode sense. If saved fails, use current values.
457 if (uscsi_mode_sense(cur_file
, pageno
, MODE_SENSE_PC_SAVED
,
458 saved
, MAX_MODE_SENSE_SIZE
, &header
)) {
459 err_print("Mode sense on page %x (saved) failed\n",
461 if (uscsi_mode_sense(cur_file
, pageno
, MODE_SENSE_PC_CURRENT
,
462 saved
, MAX_MODE_SENSE_SIZE
, &header
)) {
463 err_print("Mode sense on page %x (current) failed\n",
467 err_print("Using current values instead\n");
472 * Use the intersection of the saved and changeable
474 pg
= (struct mode_page
*)saved
;
475 pg2
= (struct mode_page
*)changeable
;
476 length
= min(MODESENSE_PAGE_LEN(pg
), MODESENSE_PAGE_LEN(pg2
));
479 * Try making this change to this page
481 if (apply_chg_list(pageno
, length
, (uchar_t
*)saved
,
482 (uchar_t
*)changeable
, change_item
)) {
484 * A change was made. Do a mode select
485 * We always want to set the Page Format bit.
486 * Set the Save Page bit if the drive indicates
487 * that it can save this page.
489 flags
= MODE_SELECT_PF
;
491 flags
|= MODE_SELECT_SP
;
494 header
.mode_header
.length
= 0;
495 header
.mode_header
.device_specific
= 0;
496 if (uscsi_mode_select(cur_file
, pageno
, flags
,
497 saved
, length
, &header
)) {
499 * Failed - try not saving parameters,
502 if (flags
& MODE_SELECT_SP
) {
503 flags
&= ~MODE_SELECT_SP
;
504 if (uscsi_mode_select(cur_file
, pageno
,
516 Mode select on page %x failed.\n", pageno
);
517 } else if ((flags
& MODE_SELECT_SP
) == 0) {
519 Mode select on page %x ok, but unable to save change permanently.\n", pageno
);
522 Mode select on page %x ok.\n", pageno
);
525 err_print("\nDevice cannot support this change\n");
534 * Format a device, without any of the standard mode selects.
535 * Ask if we should format with the P or the P&G lists.
540 struct uscsi_cmd ucmd
;
542 struct scsi_defect_hdr defect_hdr
;
550 * Are there mounted partitions?
552 if (checkmount((diskaddr_t
)-1, (diskaddr_t
)-1)) {
553 err_print("Cannot format disk with mounted partitions\n\n");
558 * Is any of the partitions being used for swapping.
560 if (checkswap((diskaddr_t
)-1, (diskaddr_t
)-1)) {
561 err_print("Cannot format disk while its partitions are \
562 currently being used for swapping.\n\n");
566 * Are any being used for SVM, VxVM or live upgrade.
568 if (checkdevinuse(cur_disk
->disk_name
, (diskaddr_t
)-1,
569 (diskaddr_t
)-1, 0, 0)) {
570 err_print("Cannot format disk while its partitions are "
571 "currently being used as described.\n");
576 * Let the user choose between formatting with either
577 * the P, or the P&G lists. Note that yes is 0, no is 1.
580 ioparam
.io_charlist
= confirm_list
;
581 grown_list
= !input(FIO_MSTR
, "Format with the Grown Defects list",
582 '?', &ioparam
, &deflt
, DATA_INPUT
);
585 * Construct the uscsi format ioctl.
586 * To format with the P and G list, we set the fmtData
587 * and cmpLst bits to zero. To format with just the
588 * P list, we set the fmtData bit (meaning that we will
589 * send down a defect list in the data phase) and the
590 * cmpLst bit (meaning that the list we send is the
591 * complete G list), and a defect list header with
592 * a defect list length of zero.
594 (void) memset((char *)&ucmd
, 0, sizeof (ucmd
));
595 (void) memset((char *)&cdb
, 0, sizeof (union scsi_cdb
));
596 cdb
.scc_cmd
= SCMD_FORMAT
;
597 ucmd
.uscsi_cdb
= (caddr_t
)&cdb
;
598 ucmd
.uscsi_cdblen
= CDB_GROUP0
;
601 * No G list. Send empty defect list to replace it.
603 cdb
.cdb_opaque
[1] = FPB_DATA
| FPB_CMPLT
| FPB_BFI
;
604 (void) memset((char *)&defect_hdr
, 0, sizeof (defect_hdr
));
605 ucmd
.uscsi_bufaddr
= (caddr_t
)&defect_hdr
;
606 ucmd
.uscsi_buflen
= sizeof (defect_hdr
);
610 * Issue the format ioctl
612 fmt_print("Formatting...\n");
613 (void) fflush(stdout
);
614 status
= uscsi_cmd(cur_file
, &ucmd
, F_NORMAL
);
615 fmt_print(status
? "Format failed\n\n" : "Format ok\n\n");
621 * List common SCSI-2 mode pages
627 Common SCSI-2 pages applicable to direct-access devices:\n\n");
628 fmt_print("Page 0x1 - Read-Write Error Recovery Page\n");
629 fmt_print("Page 0x2 - Disconnect-Reconnect Page\n");
630 fmt_print("Page 0x3 - Format Device Page\n");
631 fmt_print("Page 0x4 - Rigid Disk Geometry Page\n");
632 fmt_print("Page 0x7 - Verify Error Recovery Page\n");
633 fmt_print("Page 0x8 - Caching Page\n");
634 fmt_print("Page 0xA - Control Mode Page\n");
640 * Labels for the various fields of the scsi_inquiry structure
642 static char *scsi_inquiry_labels
[] = {
651 "Async event notification: ",
652 "Terminate i/o process msg: ",
653 "Response data format: ",
654 "Additional length: ",
655 "Relative addressing: ",
656 "32 bit transfers: ",
657 "16 bit transfers: ",
658 "Synchronous transfers: ",
660 "Command queueing: ",
661 "Soft reset option: "
666 * Dump the full inquiry as returned by the device
672 struct scsi_inquiry
*inq
;
675 inq
= (struct scsi_inquiry
*)inqbuf
;
677 if (uscsi_inquiry(cur_file
, inqbuf
, sizeof (inqbuf
))) {
678 err_print("\nInquiry failed\n");
682 fmt_print("\nInquiry:\n");
684 * The SCSI-2 spec defines "Additional length" as (n-4) bytes,
685 * where n is the last byte of the INQUIRY data. Thus
686 * there are n+1 bytes of INQUIRY data. We need to add 5 to
687 * inq_len in order to get all the INQUIRY data.
689 dump(" ", inqbuf
, inq
->inq_len
+ 5, HEX_ASCII
);
692 p
= scsi_inquiry_labels
;
694 fmt_print("%s", *p
++);
695 print_buf(inq
->inq_vid
, sizeof (inq
->inq_vid
));
696 fmt_print("\n%s", *p
++);
697 print_buf(inq
->inq_pid
, sizeof (inq
->inq_pid
));
698 fmt_print("\n%s", *p
++);
699 print_buf(inq
->inq_revision
, sizeof (inq
->inq_revision
));
701 fmt_print("\n%s%s\n", *p
++, inq
->inq_rmb
? "yes" : "no");
702 fmt_print("%s%d\n", *p
++, inq
->inq_qual
);
703 fmt_print("%s%d\n", *p
++, inq
->inq_iso
);
704 fmt_print("%s%d\n", *p
++, inq
->inq_ecma
);
705 fmt_print("%s%d\n", *p
++, inq
->inq_ansi
);
706 fmt_print("%s%s\n", *p
++, inq
->inq_aenc
? "yes" : "no");
707 fmt_print("%s%s\n", *p
++, inq
->inq_trmiop
? "yes" : "no");
708 fmt_print("%s%d\n", *p
++, inq
->inq_rdf
);
709 fmt_print("%s%d\n", *p
++, inq
->inq_len
);
710 fmt_print("%s%s\n", *p
++, inq
->inq_reladdr
? "yes" : "no");
711 fmt_print("%s%s\n", *p
++, inq
->inq_wbus32
? "yes" : "no");
712 fmt_print("%s%s\n", *p
++, inq
->inq_wbus16
? "yes" : "no");
713 fmt_print("%s%s\n", *p
++, inq
->inq_sync
? "yes" : "no");
714 fmt_print("%s%s\n", *p
++, inq
->inq_linked
? "yes" : "no");
715 fmt_print("%s%s\n", *p
++, inq
->inq_cmdque
? "yes" : "no");
716 fmt_print("%s%s\n", *p
++, inq
->inq_sftre
? "yes" : "no");
726 if (change_list
== NULL
) {
727 fmt_print("\nlist empty.\n");
729 (void) do_mode_select(change_list
);
738 if (change_list
== NULL
) {
739 fmt_print("\nlist empty.\n");
751 if (change_list
== NULL
) {
752 fmt_print("\nlist empty.\n");
754 fmt_print("\nPage 0x%x\n", current_page
);
755 for (cp
= change_list
; cp
!= NULL
; cp
= cp
->next
) {
756 fmt_print(" b0x%x ", cp
->byteno
);
759 fmt_print("= 0x%x\n", cp
->value
);
762 fmt_print("|= 0x%x\n", cp
->value
);
765 fmt_print("&= ~0x%x\n",
766 (~(cp
->value
)) & 0xff);
769 impossible("do_display");
779 parse_change_spec(full_input
, input
, pageno
, chg_item
)
783 struct chg_list
*chg_item
;
788 assert(*input
== 'b');
790 chg_item
->pageno
= pageno
;
791 chg_item
->next
= NULL
;
794 chg_item
->byteno
= (int)strtol(input
, &p
, 0);
796 err_print("Syntax error: %s\n", full_input
);
799 if (chg_item
->byteno
< 2) {
800 err_print(" Unsupported byte offset: %d\n",
804 for (input
= p
; *input
== ' '; input
++)
806 chg_item
->mode
= CHG_MODE_UNDEFINED
;
809 chg_item
->mode
= CHG_MODE_ABS
;
812 if (*input
++ == '=') {
813 chg_item
->mode
= CHG_MODE_SET
;
817 if (*input
++ == '=') {
818 chg_item
->mode
= CHG_MODE_CLR
;
822 if (chg_item
->mode
== CHG_MODE_UNDEFINED
) {
823 err_print("Syntax error: %s\n", full_input
);
826 for (; *input
== ' '; input
++)
830 for (input
++; *input
== ' '; input
++)
835 chg_item
->value
= (int)strtol(input
, &p
, 0);
836 if (p
== input
|| *p
!= 0) {
837 err_print("Syntax error: %s\n", full_input
);
841 * Apply complement if selected.
842 * Constrain to a byte value.
845 chg_item
->value
= ~chg_item
->value
;
847 chg_item
->value
&= 0xff;
854 add_new_change_list_item(chg_item
)
855 struct chg_list
*chg_item
;
859 if (change_list
== NULL
) {
860 change_list
= chg_item
;
862 for (cp
= change_list
; cp
->next
!= NULL
; cp
= cp
->next
)
866 chg_item
->next
= NULL
;
874 struct chg_list
*cp2
;
879 destroy_data((char *)cp
);
895 * Reset current page indicator
900 * Skip the leading "default" command, which we
901 * must have, or we wouldn't have come here,
902 * and any white space.
904 while (isspace(*s
)) {
908 while (*s
&& isascii(*s
) && isalpha(*s
)) {
912 while (isspace(*s
)) {
917 * Subsequent modifier must be either "p<n>", or "all".
921 n
= (int)strtol(s
, &p
, 0);
922 if (p
== s
|| *p
!= 0) {
923 err_print("Syntax error: %s\n", input
);
926 (void) default_page(n
);
929 } else if (*s
== 'a') {
932 err_print("Syntax error: %s\n", input
);
941 struct mode_header
*mh
;
942 struct mode_page
*mp
;
944 struct uscsi_cmd ucmd
;
946 char msbuf
[MAX_MODE_SENSE_SIZE
];
947 int nbytes
= sizeof (msbuf
);
951 * Build and execute the uscsi ioctl. Note that
952 * we cannot simply call uscsi_mode_sense() here,
953 * since that function attempts to valididate the
954 * returned data, and the page 0x3f has a unique
957 nbytes
= MAX_MODE_SENSE_SIZE
;
958 (void) memset(msbuf
, 0, nbytes
);
959 (void) memset((char *)&ucmd
, 0, sizeof (ucmd
));
960 (void) memset((char *)&cdb
, 0, sizeof (union scsi_cdb
));
961 cdb
.scc_cmd
= SCMD_MODE_SENSE
;
962 FORMG0COUNT(&cdb
, (uchar_t
)nbytes
);
963 cdb
.cdb_opaque
[2] = MODE_SENSE_PC_DEFAULT
| 0x3f;
964 ucmd
.uscsi_cdb
= (caddr_t
)&cdb
;
965 ucmd
.uscsi_cdblen
= CDB_GROUP0
;
966 ucmd
.uscsi_bufaddr
= msbuf
;
967 ucmd
.uscsi_buflen
= nbytes
;
968 status
= uscsi_cmd(cur_file
, &ucmd
, (option_msg
) ? F_NORMAL
: F_SILENT
);
971 err_print("\nMode sense page 0x3f failed\n");
979 * Now parse the page 0x3f
981 mh
= (struct mode_header
*)msbuf
;
982 nbytes
= mh
->length
- sizeof (struct mode_header
) -
983 mh
->bdesc_length
+ 1;
984 p
= msbuf
+ sizeof (struct mode_header
) + mh
->bdesc_length
;
987 mp
= (struct mode_page
*)p
;
988 n
= mp
->length
+ sizeof (struct mode_page
);
992 if (default_page(mp
->code
) == 0) {
999 err_print("Mode sense page 0x3f formatted incorrectly:\n");
1007 default_page(pageno
)
1010 struct scsi_ms_header header
;
1011 char saved
[MAX_MODE_SENSE_SIZE
];
1012 char current
[MAX_MODE_SENSE_SIZE
];
1013 char dfault
[MAX_MODE_SENSE_SIZE
];
1014 struct mode_page
*sp
;
1015 struct mode_page
*cp
;
1016 struct mode_page
*dp
;
1020 int need_mode_select
;
1023 * Get default mode sense
1025 if (uscsi_mode_sense(cur_file
, pageno
, MODE_SENSE_PC_DEFAULT
,
1026 dfault
, MAX_MODE_SENSE_SIZE
, &header
)) {
1027 err_print("Mode sense on page %x (dfault) failed\n",
1033 * Get the current mode sense.
1035 if (uscsi_mode_sense(cur_file
, pageno
, MODE_SENSE_PC_CURRENT
,
1036 current
, MAX_MODE_SENSE_SIZE
, &header
)) {
1037 err_print("Mode sense on page %x (current) failed\n",
1043 * Get saved mode sense. If this fails, assume it is
1044 * the same as the current.
1046 if (uscsi_mode_sense(cur_file
, pageno
, MODE_SENSE_PC_SAVED
,
1047 saved
, MAX_MODE_SENSE_SIZE
, &header
)) {
1048 (void) memcpy(saved
, current
, MAX_MODE_SENSE_SIZE
);
1052 * Determine if we need a mode select on this page.
1053 * Just deal with the intersection of the three pages.
1055 sp
= (struct mode_page
*)saved
;
1056 cp
= (struct mode_page
*)current
;
1057 dp
= (struct mode_page
*)dfault
;
1058 length
= min(MODESENSE_PAGE_LEN(sp
), MODESENSE_PAGE_LEN(cp
));
1059 length
= min(length
, MODESENSE_PAGE_LEN(dp
));
1061 need_mode_select
= 0;
1062 for (i
= 2; i
< length
; i
++) {
1063 if (current
[i
] != dfault
[i
] || saved
[i
] != dfault
[i
]) {
1064 current
[i
] = dfault
[i
];
1065 need_mode_select
= 1;
1069 if (need_mode_select
== 0) {
1070 fmt_print("Defaulting page 0x%x: ok\n",
1076 * A change was made. Do a mode select
1077 * We always want to set the Page Format bit.
1078 * Set the Save Page bit if the drive indicates
1079 * that it can save this page.
1081 length
= MODESENSE_PAGE_LEN(cp
);
1082 flags
= MODE_SELECT_PF
;
1084 flags
|= MODE_SELECT_SP
;
1087 header
.mode_header
.length
= 0;
1088 header
.mode_header
.device_specific
= 0;
1089 if (uscsi_mode_select(cur_file
, pageno
, flags
,
1090 current
, length
, &header
)) {
1092 * Failed - try not saving parameters,
1095 if (flags
& MODE_SELECT_SP
) {
1096 flags
&= ~MODE_SELECT_SP
;
1097 if (uscsi_mode_select(cur_file
, pageno
, flags
,
1098 saved
, length
, &header
)) {
1099 fmt_print("Defaulting page 0x%x: failed\n",
1102 fmt_print("Defaulting page 0x%x: ",
1104 fmt_print("cannot save page permanently\n");
1107 fmt_print("Defaulting page 0x%x: ", pageno
);
1108 fmt_print("cannot save page permanently\n");
1111 fmt_print("Defaulting page 0x%x: mode select ok\n", pageno
);