1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * This file contains functions implementing the scsi menu commands.
29 *
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.
34 */
35 #include "global.h"
36 #include <stdlib.h>
37 #include <ctype.h>
38
39 #include "io.h"
40 #include "menu.h"
41 #include "misc.h"
42 #include "menu_scsi.h"
43 #include "ctlr_scsi.h"
44 #include "startup.h"
45 #include "checkdev.h"
46
47
48 #ifdef __STDC__
49 /*
50 * ANSI prototypes for local static functions
51 */
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);
67 #else
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();
83 #endif /* __STDC__ */
84
85
86 /*
87 * Menu data for the SCSI menu display
88 */
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",
94 "",
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",
104 "",
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"
115 };
116
117 #define N_SCSI_STRINGS (sizeof (scsi_menu_strings) / sizeof (char *))
118
119 /*
120 * Command types
121 */
122 #define CMD_ALL 0
123 #define CMD_FORMAT 1
124 #define CMD_QUIT 2
125 #define CMD_HELP 3
126 #define CMD_LIST 4
127 #define CMD_INQUIRY 5
128 #define CMD_APPLY 6
129 #define CMD_CANCEL 7
130 #define CMD_DISPLAY 8
131
132 /*
133 * SCSI menu commands for minimum recognition
134 */
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 },
146 { NULL }
147 };
148
149 /*
150 * Implied page for mode select change lists
151 */
152 static int current_page;
153 static struct chg_list *change_list;
154
155 /*
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.
160 */
161 int
c_scsi()162 c_scsi()
163 {
164 int i;
165 struct env env;
166 char **menu;
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];
171 char *p;
172 char *p2;
173 int cmd;
174 int pageno;
175 int help = 1;
176
177 /*
178 * Warn casual users that maybe they should not be
179 * using this menu.
180 */
181 fmt_print("\n"
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");
187
188 /*
189 * Initialize change list and current page to empty
190 */
191 current_page = -1;
192 change_list = NULL;
193
194 /*
195 * Build and display the menu.
196 * we only use this for display purposes.
197 */
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 = true;
202 }
203 scsi_menu[i].menu_cmd = NULL;
204 menu = create_menu_list(scsi_menu);
205 /*
206 * Save the environment so a ctrl-C out of a command lands here.
207 */
208 saveenv(env);
209 for (;;) {
210 if (help) {
211 help = 0;
212 fmt_print("\n\nSCSI MENU:\n");
213 display_menu_list(menu);
214 }
215 /*
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.
219 */
220 fmt_print("scsi> ");
221 get_inputline(nclean, sizeof (nclean));
222
223 clean_token(s, nclean);
224
225 /*
226 * Mark the saved environment active so the user can now
227 * do a ctrl-C to get out of the command.
228 */
229 useenv();
230
231 /*
232 * Figure out what the user chose
233 */
234 i = find_value(cmds_list, s, &cmd);
235 if (i == 1) {
236 switch (cmd) {
237 case CMD_ALL:
238 (void) do_mode_sense_all();
239 break;
240 case CMD_FORMAT:
241 (void) do_format();
242 break;
243 case CMD_QUIT:
244 goto exit;
245 /*NOTREACHED*/
246 case CMD_HELP:
247 fmt_print("\n\nSCSI MENU:\n");
248 display_menu_list(menu);
249 break;
250 case CMD_LIST:
251 do_list();
252 break;
253 case CMD_INQUIRY:
254 (void) do_inquiry();
255 break;
256 case CMD_APPLY:
257 do_apply();
258 break;
259 case CMD_CANCEL:
260 do_cancel();
261 break;
262 case CMD_DISPLAY:
263 do_display();
264 break;
265 }
266 } else if (s[0] == 'd') {
267 do_default(s);
268 } else if (s[0] == 'p') {
269 p = s + 1;
270 pageno = (int)strtol(p, &p2, 0);
271 if (p2 == p) {
272 err_print("Syntax error: %s\n", s);
273 goto error;
274 }
275 current_page = pageno;
276 for (p = p2; *p == ' '; p++)
277 ;
278 if (*p == 0) {
279 (void) do_mode_sense(pageno);
280 } else if (*p == 'b') {
281 if (parse_change_spec(s, p, pageno,
282 &change_item)) {
283 (void) do_mode_select(&change_item);
284 }
285 }
286 } else if (s[0] == 'b') {
287 if (current_page == -1) {
288 err_print("\
289 Please display the page on which you'd like to do a mode select\n");
290 goto error;
291 }
292 chg_item = (struct chg_list *)
293 zalloc(sizeof (struct chg_list));
294 if (parse_change_spec(s, s, current_page,
295 chg_item)) {
296 add_new_change_list_item(chg_item);
297 } else {
298 destroy_data((char *)chg_item);
299 }
300 } else if (s[0] == '!') {
301 (void) execute_shell(&s[1], sizeof (s) - 1);
302 help = 1;
303 } else if (s[0] != 0) {
304 err_print("Syntax error: %s\n", s);
305 }
306 error:
307 /*
308 * Mark the saved environment inactive so ctrl-C doesn't
309 * work at the menu itself.
310 */
311 unuseenv();
312 }
313 exit:
314 /*
315 * Clean up the environment stack and free the menu
316 */
317 clearenv();
318 destroy_data((char *)menu);
319
320 /*
321 * Clean up the change list, if anything left over
322 */
323 free_change_list();
324
325 /*
326 * Make sure user is prompted with previous menu
327 */
328 last_menu++;
329
330 return (0);
331 }
332
333
334 /*
335 * Do a mode sense on a particular page, and dump the data.
336 * Get all the various flavors: default, current, saved, changeable.
337 */
338 static int
do_mode_sense(pageno)339 do_mode_sense(pageno)
340 int pageno;
341 {
342 struct scsi_ms_header header;
343 struct mode_page *pg;
344 char msbuf[MAX_MODE_SENSE_SIZE];
345 int result = 0;
346
347 char *default_msg = "default: ";
348 char *saved_msg = "saved: ";
349 char *current_msg = "current: ";
350 char *changeable_msg = "changeable: ";
351
352
353 pg = (struct mode_page *)msbuf;
354
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);
359 result = 1;
360 } else {
361 dump(default_msg, msbuf, MODESENSE_PAGE_LEN(pg),
362 HEX_ONLY);
363 }
364
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);
368 result = 1;
369 } else {
370 dump(current_msg, msbuf, MODESENSE_PAGE_LEN(pg),
371 HEX_ONLY);
372 }
373
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);
377 result = 1;
378 } else {
379 dump(saved_msg, msbuf, MODESENSE_PAGE_LEN(pg),
380 HEX_ONLY);
381 }
382
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);
386 result = 1;
387 } else {
388 dump(changeable_msg, msbuf, MODESENSE_PAGE_LEN(pg),
389 HEX_ONLY);
390 }
391
392 fmt_print("\n");
393 return (result);
394 }
395
396
397 /*
398 * Dump all the pages a device supports
399 */
400 static int
do_mode_sense_all()401 do_mode_sense_all()
402 {
403 int result = 0;
404
405 if (scsi_dump_mode_sense_pages(MODE_SENSE_PC_DEFAULT)) {
406 result = 1;
407 }
408 if (scsi_dump_mode_sense_pages(MODE_SENSE_PC_CURRENT)) {
409 result = 1;
410 }
411 if (scsi_dump_mode_sense_pages(MODE_SENSE_PC_SAVED)) {
412 result = 1;
413 }
414 if (scsi_dump_mode_sense_pages(MODE_SENSE_PC_CHANGEABLE)) {
415 result = 1;
416 }
417 fmt_print("\n");
418 return (result);
419 }
420
421
422 /*
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
426 * are changeable.
427 */
428 static int
do_mode_select(change_item)429 do_mode_select(change_item)
430 struct chg_list *change_item;
431 {
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;
437 int length;
438 int pageno;
439 int flags;
440 int result = 0;
441
442 pageno = change_item->pageno;
443
444 /*
445 * Get changeable mode sense
446 */
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",
450 pageno);
451 return (1);
452 }
453
454 /*
455 * Get saved mode sense. If saved fails, use current values.
456 */
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",
460 pageno);
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",
464 pageno);
465 return (1);
466 } else {
467 err_print("Using current values instead\n");
468 }
469 }
470
471 /*
472 * Use the intersection of the saved and changeable
473 */
474 pg = (struct mode_page *)saved;
475 pg2 = (struct mode_page *)changeable;
476 length = min(MODESENSE_PAGE_LEN(pg), MODESENSE_PAGE_LEN(pg2));
477
478 /*
479 * Try making this change to this page
480 */
481 if (apply_chg_list(pageno, length, (uchar_t *)saved,
482 (uchar_t *)changeable, change_item)) {
483 /*
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.
488 */
489 flags = MODE_SELECT_PF;
490 if (pg->ps) {
491 flags |= MODE_SELECT_SP;
492 }
493 pg->ps = 0;
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)) {
498 /*
499 * Failed - try not saving parameters,
500 * if possible.
501 */
502 if (flags & MODE_SELECT_SP) {
503 flags &= ~MODE_SELECT_SP;
504 if (uscsi_mode_select(cur_file, pageno,
505 flags, saved,
506 length, &header)) {
507 result = 1;
508 }
509 } else {
510 result = 1;
511 }
512 }
513
514 if (result) {
515 fmt_print("\n\
516 Mode select on page %x failed.\n", pageno);
517 } else if ((flags & MODE_SELECT_SP) == 0) {
518 fmt_print("\n\
519 Mode select on page %x ok, but unable to save change permanently.\n", pageno);
520 } else {
521 fmt_print("\n\
522 Mode select on page %x ok.\n", pageno);
523 }
524 } else {
525 err_print("\nDevice cannot support this change\n");
526 }
527
528 fmt_print("\n");
529 return (result);
530 }
531
532
533 /*
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.
536 */
537 static int
do_format()538 do_format()
539 {
540 struct uscsi_cmd ucmd;
541 union scsi_cdb cdb;
542 struct scsi_defect_hdr defect_hdr;
543 int status;
544 u_ioparam_t ioparam;
545 int deflt;
546 int grown_list;
547
548 fmt_print("\n");
549 /*
550 * Are there mounted partitions?
551 */
552 if (checkmount((diskaddr_t)-1, (diskaddr_t)-1)) {
553 err_print("Cannot format disk with mounted partitions\n\n");
554 return (-1);
555 }
556
557 /*
558 * Is any of the partitions being used for swapping.
559 */
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");
563 return (-1);
564 }
565 /*
566 * Are any being used for SVM, VxVM or live upgrade.
567 */
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");
572 return (-1);
573 }
574
575 /*
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.
578 */
579 deflt = 0;
580 ioparam.io_charlist = confirm_list;
581 grown_list = !input(FIO_MSTR, "Format with the Grown Defects list",
582 '?', &ioparam, &deflt, DATA_INPUT);
583
584 /*
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.
593 */
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;
599 if (!grown_list) {
600 /*
601 * No G list. Send empty defect list to replace it.
602 */
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);
607 }
608
609 /*
610 * Issue the format ioctl
611 */
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");
616 return (status);
617 }
618
619
620 /*
621 * List common SCSI-2 mode pages
622 */
623 static void
do_list()624 do_list()
625 {
626 fmt_print("\n\
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");
635 fmt_print("\n");
636 }
637
638
639 /*
640 * Labels for the various fields of the scsi_inquiry structure
641 */
642 static char *scsi_inquiry_labels[] = {
643 "Vendor: ",
644 "Product: ",
645 "Revision: ",
646 "Removable media: ",
647 "Device type: ",
648 "ISO version: ",
649 "ECMA version: ",
650 "ANSI version: ",
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: ",
659 "Linked commands: ",
660 "Command queueing: ",
661 "Soft reset option: "
662 };
663
664
665 /*
666 * Dump the full inquiry as returned by the device
667 */
668 static int
do_inquiry()669 do_inquiry()
670 {
671 char inqbuf[255];
672 struct scsi_inquiry *inq;
673 char **p;
674
675 inq = (struct scsi_inquiry *)inqbuf;
676
677 if (uscsi_inquiry(cur_file, inqbuf, sizeof (inqbuf))) {
678 err_print("\nInquiry failed\n");
679 return (1);
680 }
681
682 fmt_print("\nInquiry:\n");
683 /*
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.
688 */
689 dump(" ", inqbuf, inq->inq_len + 5, HEX_ASCII);
690 fmt_print("\n");
691
692 p = scsi_inquiry_labels;
693
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));
700
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");
717
718 fmt_print("\n");
719 return (0);
720 }
721
722
723 static void
do_apply()724 do_apply()
725 {
726 if (change_list == NULL) {
727 fmt_print("\nlist empty.\n");
728 } else {
729 (void) do_mode_select(change_list);
730 free_change_list();
731 }
732 }
733
734
735 static void
do_cancel()736 do_cancel()
737 {
738 if (change_list == NULL) {
739 fmt_print("\nlist empty.\n");
740 } else {
741 free_change_list();
742 }
743 }
744
745
746 static void
do_display()747 do_display()
748 {
749 struct chg_list *cp;
750
751 if (change_list == NULL) {
752 fmt_print("\nlist empty.\n");
753 } else {
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);
757 switch (cp->mode) {
758 case CHG_MODE_ABS:
759 fmt_print("= 0x%x\n", cp->value);
760 break;
761 case CHG_MODE_SET:
762 fmt_print("|= 0x%x\n", cp->value);
763 break;
764 case CHG_MODE_CLR:
765 fmt_print("&= ~0x%x\n",
766 (~(cp->value)) & 0xff);
767 break;
768 default:
769 impossible("do_display");
770 /*NOTREACHED*/
771 }
772 }
773 fmt_print("\n");
774 }
775 }
776
777
778 static int
parse_change_spec(full_input,input,pageno,chg_item)779 parse_change_spec(full_input, input, pageno, chg_item)
780 char *full_input;
781 char *input;
782 int pageno;
783 struct chg_list *chg_item;
784 {
785 char *p;
786 int tilde;
787
788 assert(*input == 'b');
789
790 chg_item->pageno = pageno;
791 chg_item->next = NULL;
792
793 input++;
794 chg_item->byteno = (int)strtol(input, &p, 0);
795 if (p == input) {
796 err_print("Syntax error: %s\n", full_input);
797 return (0);
798 }
799 if (chg_item->byteno < 2) {
800 err_print(" Unsupported byte offset: %d\n",
801 chg_item->byteno);
802 return (0);
803 }
804 for (input = p; *input == ' '; input++)
805 ;
806 chg_item->mode = CHG_MODE_UNDEFINED;
807 switch (*input++) {
808 case '=':
809 chg_item->mode = CHG_MODE_ABS;
810 break;
811 case '|':
812 if (*input++ == '=') {
813 chg_item->mode = CHG_MODE_SET;
814 }
815 break;
816 case '&':
817 if (*input++ == '=') {
818 chg_item->mode = CHG_MODE_CLR;
819 }
820 break;
821 }
822 if (chg_item->mode == CHG_MODE_UNDEFINED) {
823 err_print("Syntax error: %s\n", full_input);
824 return (0);
825 }
826 for (; *input == ' '; input++)
827 ;
828 if (*input == '~') {
829 tilde = 1;
830 for (input++; *input == ' '; input++)
831 ;
832 } else {
833 tilde = 0;
834 }
835 chg_item->value = (int)strtol(input, &p, 0);
836 if (p == input || *p != 0) {
837 err_print("Syntax error: %s\n", full_input);
838 return (0);
839 }
840 /*
841 * Apply complement if selected.
842 * Constrain to a byte value.
843 */
844 if (tilde) {
845 chg_item->value = ~chg_item->value;
846 }
847 chg_item->value &= 0xff;
848
849 return (1);
850 }
851
852
853 static void
add_new_change_list_item(chg_item)854 add_new_change_list_item(chg_item)
855 struct chg_list *chg_item;
856 {
857 struct chg_list *cp;
858
859 if (change_list == NULL) {
860 change_list = chg_item;
861 } else {
862 for (cp = change_list; cp->next != NULL; cp = cp->next)
863 ;
864 cp->next = chg_item;
865 }
866 chg_item->next = NULL;
867 }
868
869
870 static void
free_change_list()871 free_change_list()
872 {
873 struct chg_list *cp;
874 struct chg_list *cp2;
875
876 cp = change_list;
877 while (cp != NULL) {
878 cp2 = cp->next;
879 destroy_data((char *)cp);
880 cp = cp2;
881 }
882 change_list = NULL;
883 }
884
885
886 static void
do_default(input)887 do_default(input)
888 char *input;
889 {
890 char *s = input;
891 char *p;
892 int n;
893
894 /*
895 * Reset current page indicator
896 */
897 current_page = -1;
898
899 /*
900 * Skip the leading "default" command, which we
901 * must have, or we wouldn't have come here,
902 * and any white space.
903 */
904 while (isspace(*s)) {
905 s++;
906 }
907
908 while (*s && isascii(*s) && isalpha(*s)) {
909 s++;
910 }
911
912 while (isspace(*s)) {
913 s++;
914 }
915
916 /*
917 * Subsequent modifier must be either "p<n>", or "all".
918 */
919 if (*s == 'p') {
920 s++;
921 n = (int)strtol(s, &p, 0);
922 if (p == s || *p != 0) {
923 err_print("Syntax error: %s\n", input);
924 } else {
925 fmt_print("\n");
926 (void) default_page(n);
927 fmt_print("\n");
928 }
929 } else if (*s == 'a') {
930 default_all_pages();
931 } else {
932 err_print("Syntax error: %s\n", input);
933 }
934 }
935
936
937 static void
default_all_pages()938 default_all_pages()
939 {
940 char *p;
941 struct mode_header *mh;
942 struct mode_page *mp;
943 int n;
944 struct uscsi_cmd ucmd;
945 union scsi_cdb cdb;
946 char msbuf[MAX_MODE_SENSE_SIZE];
947 int nbytes = sizeof (msbuf);
948 int status;
949
950 /*
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
955 * format.
956 */
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);
969 if (status) {
970 if (!option_msg) {
971 err_print("\nMode sense page 0x3f failed\n");
972 }
973 return;
974 }
975
976 fmt_print("\n");
977
978 /*
979 * Now parse the page 0x3f
980 */
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;
985
986 while (nbytes > 0) {
987 mp = (struct mode_page *)p;
988 n = mp->length + sizeof (struct mode_page);
989 nbytes -= n;
990 if (nbytes < 0)
991 break;
992 if (default_page(mp->code) == 0) {
993 goto error;
994 }
995 p += n;
996 }
997
998 if (nbytes < 0) {
999 err_print("Mode sense page 0x3f formatted incorrectly:\n");
1000 }
1001 error:
1002 fmt_print("\n");
1003 }
1004
1005
1006 static int
default_page(pageno)1007 default_page(pageno)
1008 int pageno;
1009 {
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;
1017 int length;
1018 int flags;
1019 int i;
1020 int need_mode_select;
1021
1022 /*
1023 * Get default mode sense
1024 */
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",
1028 pageno);
1029 return (0);
1030 }
1031
1032 /*
1033 * Get the current mode sense.
1034 */
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",
1038 pageno);
1039 return (0);
1040 }
1041
1042 /*
1043 * Get saved mode sense. If this fails, assume it is
1044 * the same as the current.
1045 */
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);
1049 }
1050
1051 /*
1052 * Determine if we need a mode select on this page.
1053 * Just deal with the intersection of the three pages.
1054 */
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));
1060
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;
1066 }
1067 }
1068
1069 if (need_mode_select == 0) {
1070 fmt_print("Defaulting page 0x%x: ok\n",
1071 pageno);
1072 return (1);
1073 }
1074
1075 /*
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.
1080 */
1081 length = MODESENSE_PAGE_LEN(cp);
1082 flags = MODE_SELECT_PF;
1083 if (cp->ps) {
1084 flags |= MODE_SELECT_SP;
1085 }
1086 cp->ps = 0;
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)) {
1091 /*
1092 * Failed - try not saving parameters,
1093 * if possible.
1094 */
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",
1100 pageno);
1101 } else {
1102 fmt_print("Defaulting page 0x%x: ",
1103 pageno);
1104 fmt_print("cannot save page permanently\n");
1105 }
1106 } else {
1107 fmt_print("Defaulting page 0x%x: ", pageno);
1108 fmt_print("cannot save page permanently\n");
1109 }
1110 } else {
1111 fmt_print("Defaulting page 0x%x: mode select ok\n", pageno);
1112 }
1113
1114 return (1);
1115 }
1116