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 static int do_mode_sense(int);
48 static int do_mode_sense_all(void);
49 static int do_mode_select(struct chg_list *);
50 static int do_format(void);
51 static void do_list(void);
52 static int do_inquiry(void);
53 static void do_apply(void);
54 static void do_cancel(void);
55 static void do_display(void);
56 static int parse_change_spec(char *, char *, int, struct chg_list *);
57 static void add_new_change_list_item(struct chg_list *);
58 static void free_change_list(void);
59 static void do_default(char *);
60 static void default_all_pages(void);
61 static int default_page(int);
62
63 /*
64 * Menu data for the SCSI menu display
65 */
66 static char *scsi_menu_strings[] = {
67 "p<n> - display a mode sense page",
68 "p<n> b<n> <op> [~]<n> - change a byte and issue mode select",
69 "b<n> <op> [~]<n> - add an operation to the mode select list",
70 " for the current page",
71 "",
72 " where: p<n> specifies the page with page code <n>",
73 " b<n> specifies byte <n> of the page",
74 " <op> can be one of the following operators:",
75 " = (set specified value)",
76 " |= (bitwise OR with current value)",
77 " &= (bitwise AND with current value)",
78 " <n> can be a decimal value in the range 0-255,",
79 " or two hexadecimal digits, in the form 0x<xx>.",
80 " [~] complements the specified value",
81 "",
82 "apply - apply mode select list",
83 "cancel - cancel mode select list",
84 "display - display mode select list",
85 "all - display all supported mode sense pages",
86 "default p<n> - mode select page <n> to default values",
87 "default all - mode select all pages to default values",
88 "format - format without standard mode selects",
89 "inquiry - display device's inquiry response",
90 "list - list common SCSI-2 mode pages",
91 "!<cmd> - execute <cmd> , then return"
92 };
93
94 #define N_SCSI_STRINGS (sizeof (scsi_menu_strings) / sizeof (char *))
95
96 /*
97 * Command types
98 */
99 #define CMD_ALL 0
100 #define CMD_FORMAT 1
101 #define CMD_QUIT 2
102 #define CMD_HELP 3
103 #define CMD_LIST 4
104 #define CMD_INQUIRY 5
105 #define CMD_APPLY 6
106 #define CMD_CANCEL 7
107 #define CMD_DISPLAY 8
108
109 /*
110 * SCSI menu commands for minimum recognition
111 */
112 static struct slist cmds_list[] = {
113 { "all", NULL, CMD_ALL },
114 { "format", NULL, CMD_FORMAT },
115 { "quit", NULL, CMD_QUIT },
116 { "help", NULL, CMD_HELP },
117 { "?", NULL, CMD_HELP },
118 { "list", NULL, CMD_LIST },
119 { "inquiry", NULL, CMD_INQUIRY },
120 { "apply", NULL, CMD_APPLY },
121 { "cancel", NULL, CMD_CANCEL },
122 { "display", NULL, CMD_DISPLAY },
123 { NULL }
124 };
125
126 /*
127 * Implied page for mode select change lists
128 */
129 static int current_page;
130 static struct chg_list *change_list;
131
132 /*
133 * Manage the SCSI menu.
134 * Parse input and dispatch to the appropriate functions.
135 * The commands we accept are not simple one-word commands,
136 * so we cannot use the standard format menu-handling functions.
137 */
138 int
c_scsi(void)139 c_scsi(void)
140 {
141 int i;
142 struct env env;
143 char **menu;
144 struct menu_item scsi_menu[N_SCSI_STRINGS+1];
145 struct chg_list change_item;
146 struct chg_list *chg_item;
147 char s[MAXPATHLEN], nclean[MAXPATHLEN];
148 char *p;
149 char *p2;
150 int cmd;
151 int pageno;
152 int help = 1;
153
154 /*
155 * Warn casual users that maybe they should not be
156 * using this menu.
157 */
158 fmt_print("\n"
159 "Warning: these functions are intended for expert use only, for\n"
160 "debugging disk devices and for unusual configuration settings.\n"
161 "It is recommended that you do not use this menu for normal disk\n"
162 "configuration and formatting, unless you have explicit instructions,\n"
163 "or know exactly what you are doing.\n");
164
165 /*
166 * Initialize change list and current page to empty
167 */
168 current_page = -1;
169 change_list = NULL;
170
171 /*
172 * Build and display the menu.
173 * we only use this for display purposes.
174 */
175 for (i = 0; i < N_SCSI_STRINGS; i++) {
176 scsi_menu[i].menu_cmd = scsi_menu_strings[i];
177 scsi_menu[i].menu_func = NULL;
178 scsi_menu[i].menu_state = true;
179 }
180 scsi_menu[i].menu_cmd = NULL;
181 menu = create_menu_list(scsi_menu);
182 /*
183 * Save the environment so a ctrl-C out of a command lands here.
184 */
185 saveenv(env);
186 for (;;) {
187 if (help) {
188 help = 0;
189 fmt_print("\n\nSCSI MENU:\n");
190 display_menu_list(menu);
191 }
192 /*
193 * Prompt and get next input line. We don't use the
194 * standard input routine, since we need a little
195 * more flexibility in parsing the input.
196 */
197 fmt_print("scsi> ");
198 get_inputline(nclean, sizeof (nclean));
199
200 clean_token(s, nclean);
201
202 /*
203 * Mark the saved environment active so the user can now
204 * do a ctrl-C to get out of the command.
205 */
206 useenv();
207
208 /*
209 * Figure out what the user chose
210 */
211 i = find_value(cmds_list, s, &cmd);
212 if (i == 1) {
213 switch (cmd) {
214 case CMD_ALL:
215 (void) do_mode_sense_all();
216 break;
217 case CMD_FORMAT:
218 (void) do_format();
219 break;
220 case CMD_QUIT:
221 goto exit;
222 /*NOTREACHED*/
223 case CMD_HELP:
224 fmt_print("\n\nSCSI MENU:\n");
225 display_menu_list(menu);
226 break;
227 case CMD_LIST:
228 do_list();
229 break;
230 case CMD_INQUIRY:
231 (void) do_inquiry();
232 break;
233 case CMD_APPLY:
234 do_apply();
235 break;
236 case CMD_CANCEL:
237 do_cancel();
238 break;
239 case CMD_DISPLAY:
240 do_display();
241 break;
242 }
243 } else if (s[0] == 'd') {
244 do_default(s);
245 } else if (s[0] == 'p') {
246 p = s + 1;
247 pageno = (int)strtol(p, &p2, 0);
248 if (p2 == p) {
249 err_print("Syntax error: %s\n", s);
250 goto error;
251 }
252 current_page = pageno;
253 for (p = p2; *p == ' '; p++)
254 ;
255 if (*p == 0) {
256 (void) do_mode_sense(pageno);
257 } else if (*p == 'b') {
258 if (parse_change_spec(s, p, pageno,
259 &change_item)) {
260 (void) do_mode_select(&change_item);
261 }
262 }
263 } else if (s[0] == 'b') {
264 if (current_page == -1) {
265 err_print("\
266 Please display the page on which you'd like to do a mode select\n");
267 goto error;
268 }
269 chg_item = (struct chg_list *)
270 zalloc(sizeof (struct chg_list));
271 if (parse_change_spec(s, s, current_page,
272 chg_item)) {
273 add_new_change_list_item(chg_item);
274 } else {
275 destroy_data((char *)chg_item);
276 }
277 } else if (s[0] == '!') {
278 (void) execute_shell(&s[1], sizeof (s) - 1);
279 help = 1;
280 } else if (s[0] != 0) {
281 err_print("Syntax error: %s\n", s);
282 }
283 error:
284 /*
285 * Mark the saved environment inactive so ctrl-C doesn't
286 * work at the menu itself.
287 */
288 unuseenv();
289 }
290 exit:
291 /*
292 * Clean up the environment stack and free the menu
293 */
294 clearenv();
295 destroy_data((char *)menu);
296
297 /*
298 * Clean up the change list, if anything left over
299 */
300 free_change_list();
301
302 /*
303 * Make sure user is prompted with previous menu
304 */
305 last_menu++;
306
307 return (0);
308 }
309
310
311 /*
312 * Do a mode sense on a particular page, and dump the data.
313 * Get all the various flavors: default, current, saved, changeable.
314 */
315 static int
do_mode_sense(int pageno)316 do_mode_sense(int pageno)
317 {
318 struct scsi_ms_header header;
319 struct mode_page *pg;
320 char msbuf[MAX_MODE_SENSE_SIZE];
321 int result = 0;
322
323 char *default_msg = "default: ";
324 char *saved_msg = "saved: ";
325 char *current_msg = "current: ";
326 char *changeable_msg = "changeable: ";
327
328
329 pg = (struct mode_page *)msbuf;
330
331 fmt_print("\nPage 0x%x:\n", pageno);
332 if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_DEFAULT,
333 msbuf, MAX_MODE_SENSE_SIZE, &header)) {
334 err_print("%sfailed\n", default_msg);
335 result = 1;
336 } else {
337 dump(default_msg, msbuf, MODESENSE_PAGE_LEN(pg), HEX_ONLY);
338 }
339
340 if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_CURRENT,
341 msbuf, MAX_MODE_SENSE_SIZE, &header)) {
342 err_print("%sfailed\n", current_msg);
343 result = 1;
344 } else {
345 dump(current_msg, msbuf, MODESENSE_PAGE_LEN(pg), HEX_ONLY);
346 }
347
348 if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_SAVED,
349 msbuf, MAX_MODE_SENSE_SIZE, &header)) {
350 err_print("%sfailed\n", saved_msg);
351 result = 1;
352 } else {
353 dump(saved_msg, msbuf, MODESENSE_PAGE_LEN(pg), HEX_ONLY);
354 }
355
356 if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_CHANGEABLE,
357 msbuf, MAX_MODE_SENSE_SIZE, &header)) {
358 err_print("%sfailed\n", changeable_msg);
359 result = 1;
360 } else {
361 dump(changeable_msg, msbuf, MODESENSE_PAGE_LEN(pg), HEX_ONLY);
362 }
363
364 fmt_print("\n");
365 return (result);
366 }
367
368
369 /*
370 * Dump all the pages a device supports
371 */
372 static int
do_mode_sense_all(void)373 do_mode_sense_all(void)
374 {
375 int result = 0;
376
377 if (scsi_dump_mode_sense_pages(MODE_SENSE_PC_DEFAULT)) {
378 result = 1;
379 }
380 if (scsi_dump_mode_sense_pages(MODE_SENSE_PC_CURRENT)) {
381 result = 1;
382 }
383 if (scsi_dump_mode_sense_pages(MODE_SENSE_PC_SAVED)) {
384 result = 1;
385 }
386 if (scsi_dump_mode_sense_pages(MODE_SENSE_PC_CHANGEABLE)) {
387 result = 1;
388 }
389 fmt_print("\n");
390 return (result);
391 }
392
393
394 /*
395 * Get the current mode sense for a particular page, change
396 * a byte, and issue a mode select. Note that we can only
397 * change a value if the device indicates that those bits
398 * are changeable.
399 */
400 static int
do_mode_select(struct chg_list * change_item)401 do_mode_select(struct chg_list *change_item)
402 {
403 struct scsi_ms_header header;
404 char saved[MAX_MODE_SENSE_SIZE];
405 char changeable[MAX_MODE_SENSE_SIZE];
406 struct mode_page *pg;
407 struct mode_page *pg2;
408 int length;
409 int pageno;
410 int flags;
411 int result = 0;
412
413 pageno = change_item->pageno;
414
415 /*
416 * Get changeable mode sense
417 */
418 if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_CHANGEABLE,
419 changeable, MAX_MODE_SENSE_SIZE, &header)) {
420 err_print("Mode sense on page %x (changeable) failed\n",
421 pageno);
422 return (1);
423 }
424
425 /*
426 * Get saved mode sense. If saved fails, use current values.
427 */
428 if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_SAVED,
429 saved, MAX_MODE_SENSE_SIZE, &header)) {
430 err_print("Mode sense on page %x (saved) failed\n", pageno);
431 if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_CURRENT,
432 saved, MAX_MODE_SENSE_SIZE, &header)) {
433 err_print("Mode sense on page %x (current) failed\n",
434 pageno);
435 return (1);
436 } else {
437 err_print("Using current values instead\n");
438 }
439 }
440
441 /*
442 * Use the intersection of the saved and changeable
443 */
444 pg = (struct mode_page *)saved;
445 pg2 = (struct mode_page *)changeable;
446 length = min(MODESENSE_PAGE_LEN(pg), MODESENSE_PAGE_LEN(pg2));
447
448 /*
449 * Try making this change to this page
450 */
451 if (apply_chg_list(pageno, length, (uchar_t *)saved,
452 (uchar_t *)changeable, change_item)) {
453 /*
454 * A change was made. Do a mode select
455 * We always want to set the Page Format bit.
456 * Set the Save Page bit if the drive indicates
457 * that it can save this page.
458 */
459 flags = MODE_SELECT_PF;
460 if (pg->ps) {
461 flags |= MODE_SELECT_SP;
462 }
463 pg->ps = 0;
464 header.mode_header.length = 0;
465 header.mode_header.device_specific = 0;
466 if (uscsi_mode_select(cur_file, pageno, flags,
467 saved, length, &header)) {
468 /*
469 * Failed - try not saving parameters,
470 * if possible.
471 */
472 if (flags & MODE_SELECT_SP) {
473 flags &= ~MODE_SELECT_SP;
474 if (uscsi_mode_select(cur_file, pageno,
475 flags, saved, length, &header)) {
476 result = 1;
477 }
478 } else {
479 result = 1;
480 }
481 }
482
483 if (result) {
484 fmt_print("\n\
485 Mode select on page %x failed.\n", pageno);
486 } else if ((flags & MODE_SELECT_SP) == 0) {
487 fmt_print("\n\
488 Mode select on page %x ok, but unable to save change permanently.\n", pageno);
489 } else {
490 fmt_print("\n\
491 Mode select on page %x ok.\n", pageno);
492 }
493 } else {
494 err_print("\nDevice cannot support this change\n");
495 }
496
497 fmt_print("\n");
498 return (result);
499 }
500
501
502 /*
503 * Format a device, without any of the standard mode selects.
504 * Ask if we should format with the P or the P&G lists.
505 */
506 static int
do_format(void)507 do_format(void)
508 {
509 struct uscsi_cmd ucmd;
510 union scsi_cdb cdb;
511 struct scsi_defect_hdr defect_hdr;
512 int status;
513 u_ioparam_t ioparam;
514 int deflt;
515 int grown_list;
516
517 fmt_print("\n");
518 /*
519 * Are there mounted partitions?
520 */
521 if (checkmount((diskaddr_t)-1, (diskaddr_t)-1)) {
522 err_print("Cannot format disk with mounted partitions\n\n");
523 return (-1);
524 }
525
526 /*
527 * Is any of the partitions being used for swapping.
528 */
529 if (checkswap((diskaddr_t)-1, (diskaddr_t)-1)) {
530 err_print("Cannot format disk while its partitions are \
531 currently being used for swapping.\n\n");
532 return (-1);
533 }
534 /*
535 * Are any being used for SVM, VxVM or live upgrade.
536 */
537 if (checkdevinuse(cur_disk->disk_name, (diskaddr_t)-1,
538 (diskaddr_t)-1, 0, 0)) {
539 err_print("Cannot format disk while its partitions are "
540 "currently being used as described.\n");
541 return (-1);
542 }
543
544 /*
545 * Let the user choose between formatting with either
546 * the P, or the P&G lists. Note that yes is 0, no is 1.
547 */
548 deflt = 0;
549 ioparam.io_charlist = confirm_list;
550 grown_list = !input(FIO_MSTR, "Format with the Grown Defects list",
551 '?', &ioparam, &deflt, DATA_INPUT);
552
553 /*
554 * Construct the uscsi format ioctl.
555 * To format with the P and G list, we set the fmtData
556 * and cmpLst bits to zero. To format with just the
557 * P list, we set the fmtData bit (meaning that we will
558 * send down a defect list in the data phase) and the
559 * cmpLst bit (meaning that the list we send is the
560 * complete G list), and a defect list header with
561 * a defect list length of zero.
562 */
563 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
564 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
565 cdb.scc_cmd = SCMD_FORMAT;
566 ucmd.uscsi_cdb = (caddr_t)&cdb;
567 ucmd.uscsi_cdblen = CDB_GROUP0;
568 if (!grown_list) {
569 /*
570 * No G list. Send empty defect list to replace it.
571 */
572 cdb.cdb_opaque[1] = FPB_DATA | FPB_CMPLT | FPB_BFI;
573 (void) memset((char *)&defect_hdr, 0, sizeof (defect_hdr));
574 ucmd.uscsi_bufaddr = (caddr_t)&defect_hdr;
575 ucmd.uscsi_buflen = sizeof (defect_hdr);
576 }
577
578 /*
579 * Issue the format ioctl
580 */
581 fmt_print("Formatting...\n");
582 (void) fflush(stdout);
583 status = uscsi_cmd(cur_file, &ucmd, F_NORMAL);
584 fmt_print(status ? "Format failed\n\n" : "Format ok\n\n");
585 return (status);
586 }
587
588
589 /*
590 * List common SCSI-2 mode pages
591 */
592 static void
do_list(void)593 do_list(void)
594 {
595 fmt_print("\n\
596 Common SCSI-2 pages applicable to direct-access devices:\n\n");
597 fmt_print("Page 0x1 - Read-Write Error Recovery Page\n");
598 fmt_print("Page 0x2 - Disconnect-Reconnect Page\n");
599 fmt_print("Page 0x3 - Format Device Page\n");
600 fmt_print("Page 0x4 - Rigid Disk Geometry Page\n");
601 fmt_print("Page 0x7 - Verify Error Recovery Page\n");
602 fmt_print("Page 0x8 - Caching Page\n");
603 fmt_print("Page 0xA - Control Mode Page\n");
604 fmt_print("\n");
605 }
606
607
608 /*
609 * Labels for the various fields of the scsi_inquiry structure
610 */
611 static char *scsi_inquiry_labels[] = {
612 "Vendor: ",
613 "Product: ",
614 "Revision: ",
615 "Removable media: ",
616 "Device type: ",
617 "ISO version: ",
618 "ECMA version: ",
619 "ANSI version: ",
620 "Async event notification: ",
621 "Terminate i/o process msg: ",
622 "Response data format: ",
623 "Additional length: ",
624 "Relative addressing: ",
625 "32 bit transfers: ",
626 "16 bit transfers: ",
627 "Synchronous transfers: ",
628 "Linked commands: ",
629 "Command queueing: ",
630 "Soft reset option: "
631 };
632
633
634 /*
635 * Dump the full inquiry as returned by the device
636 */
637 static int
do_inquiry(void)638 do_inquiry(void)
639 {
640 char inqbuf[255];
641 struct scsi_inquiry *inq;
642 char **p;
643
644 inq = (struct scsi_inquiry *)inqbuf;
645
646 if (uscsi_inquiry(cur_file, inqbuf, sizeof (inqbuf))) {
647 err_print("\nInquiry failed\n");
648 return (1);
649 }
650
651 fmt_print("\nInquiry:\n");
652 /*
653 * The SCSI-2 spec defines "Additional length" as (n-4) bytes,
654 * where n is the last byte of the INQUIRY data. Thus
655 * there are n+1 bytes of INQUIRY data. We need to add 5 to
656 * inq_len in order to get all the INQUIRY data.
657 */
658 dump(" ", inqbuf, inq->inq_len + 5, HEX_ASCII);
659 fmt_print("\n");
660
661 p = scsi_inquiry_labels;
662
663 fmt_print("%s", *p++);
664 print_buf(inq->inq_vid, sizeof (inq->inq_vid));
665 fmt_print("\n%s", *p++);
666 print_buf(inq->inq_pid, sizeof (inq->inq_pid));
667 fmt_print("\n%s", *p++);
668 print_buf(inq->inq_revision, sizeof (inq->inq_revision));
669
670 fmt_print("\n%s%s\n", *p++, inq->inq_rmb ? "yes" : "no");
671 fmt_print("%s%d\n", *p++, inq->inq_qual);
672 fmt_print("%s%d\n", *p++, inq->inq_iso);
673 fmt_print("%s%d\n", *p++, inq->inq_ecma);
674 fmt_print("%s%d\n", *p++, inq->inq_ansi);
675 fmt_print("%s%s\n", *p++, inq->inq_aenc ? "yes" : "no");
676 fmt_print("%s%s\n", *p++, inq->inq_trmiop ? "yes" : "no");
677 fmt_print("%s%d\n", *p++, inq->inq_rdf);
678 fmt_print("%s%d\n", *p++, inq->inq_len);
679 fmt_print("%s%s\n", *p++, inq->inq_reladdr ? "yes" : "no");
680 fmt_print("%s%s\n", *p++, inq->inq_wbus32 ? "yes" : "no");
681 fmt_print("%s%s\n", *p++, inq->inq_wbus16 ? "yes" : "no");
682 fmt_print("%s%s\n", *p++, inq->inq_sync ? "yes" : "no");
683 fmt_print("%s%s\n", *p++, inq->inq_linked ? "yes" : "no");
684 fmt_print("%s%s\n", *p++, inq->inq_cmdque ? "yes" : "no");
685 fmt_print("%s%s\n", *p++, inq->inq_sftre ? "yes" : "no");
686
687 fmt_print("\n");
688 return (0);
689 }
690
691
692 static void
do_apply(void)693 do_apply(void)
694 {
695 if (change_list == NULL) {
696 fmt_print("\nlist empty.\n");
697 } else {
698 (void) do_mode_select(change_list);
699 free_change_list();
700 }
701 }
702
703
704 static void
do_cancel(void)705 do_cancel(void)
706 {
707 if (change_list == NULL) {
708 fmt_print("\nlist empty.\n");
709 } else {
710 free_change_list();
711 }
712 }
713
714
715 static void
do_display(void)716 do_display(void)
717 {
718 struct chg_list *cp;
719
720 if (change_list == NULL) {
721 fmt_print("\nlist empty.\n");
722 } else {
723 fmt_print("\nPage 0x%x\n", current_page);
724 for (cp = change_list; cp != NULL; cp = cp->next) {
725 fmt_print(" b0x%x ", cp->byteno);
726 switch (cp->mode) {
727 case CHG_MODE_ABS:
728 fmt_print("= 0x%x\n", cp->value);
729 break;
730 case CHG_MODE_SET:
731 fmt_print("|= 0x%x\n", cp->value);
732 break;
733 case CHG_MODE_CLR:
734 fmt_print("&= ~0x%x\n",
735 (~(cp->value)) & 0xff);
736 break;
737 default:
738 impossible("do_display");
739 /*NOTREACHED*/
740 }
741 }
742 fmt_print("\n");
743 }
744 }
745
746
747 static int
parse_change_spec(char * full_input,char * input,int pageno,struct chg_list * chg_item)748 parse_change_spec(char *full_input, char *input, int pageno,
749 struct chg_list *chg_item)
750 {
751 char *p;
752 int tilde;
753
754 assert(*input == 'b');
755
756 chg_item->pageno = pageno;
757 chg_item->next = NULL;
758
759 input++;
760 chg_item->byteno = (int)strtol(input, &p, 0);
761 if (p == input) {
762 err_print("Syntax error: %s\n", full_input);
763 return (0);
764 }
765 if (chg_item->byteno < 2) {
766 err_print(" Unsupported byte offset: %d\n", chg_item->byteno);
767 return (0);
768 }
769 for (input = p; *input == ' '; input++)
770 ;
771 chg_item->mode = CHG_MODE_UNDEFINED;
772 switch (*input++) {
773 case '=':
774 chg_item->mode = CHG_MODE_ABS;
775 break;
776 case '|':
777 if (*input++ == '=') {
778 chg_item->mode = CHG_MODE_SET;
779 }
780 break;
781 case '&':
782 if (*input++ == '=') {
783 chg_item->mode = CHG_MODE_CLR;
784 }
785 break;
786 }
787 if (chg_item->mode == CHG_MODE_UNDEFINED) {
788 err_print("Syntax error: %s\n", full_input);
789 return (0);
790 }
791 for (; *input == ' '; input++)
792 ;
793 if (*input == '~') {
794 tilde = 1;
795 for (input++; *input == ' '; input++)
796 ;
797 } else {
798 tilde = 0;
799 }
800 chg_item->value = (int)strtol(input, &p, 0);
801 if (p == input || *p != 0) {
802 err_print("Syntax error: %s\n", full_input);
803 return (0);
804 }
805 /*
806 * Apply complement if selected.
807 * Constrain to a byte value.
808 */
809 if (tilde) {
810 chg_item->value = ~chg_item->value;
811 }
812 chg_item->value &= 0xff;
813
814 return (1);
815 }
816
817
818 static void
add_new_change_list_item(struct chg_list * chg_item)819 add_new_change_list_item(struct chg_list *chg_item)
820 {
821 struct chg_list *cp;
822
823 if (change_list == NULL) {
824 change_list = chg_item;
825 } else {
826 for (cp = change_list; cp->next != NULL; cp = cp->next)
827 ;
828 cp->next = chg_item;
829 }
830 chg_item->next = NULL;
831 }
832
833
834 static void
free_change_list(void)835 free_change_list(void)
836 {
837 struct chg_list *cp;
838 struct chg_list *cp2;
839
840 cp = change_list;
841 while (cp != NULL) {
842 cp2 = cp->next;
843 destroy_data((char *)cp);
844 cp = cp2;
845 }
846 change_list = NULL;
847 }
848
849
850 static void
do_default(char * input)851 do_default(char *input)
852 {
853 char *s = input;
854 char *p;
855 int n;
856
857 /*
858 * Reset current page indicator
859 */
860 current_page = -1;
861
862 /*
863 * Skip the leading "default" command, which we
864 * must have, or we wouldn't have come here,
865 * and any white space.
866 */
867 while (isspace(*s)) {
868 s++;
869 }
870
871 while (*s && isascii(*s) && isalpha(*s)) {
872 s++;
873 }
874
875 while (isspace(*s)) {
876 s++;
877 }
878
879 /*
880 * Subsequent modifier must be either "p<n>", or "all".
881 */
882 if (*s == 'p') {
883 s++;
884 n = (int)strtol(s, &p, 0);
885 if (p == s || *p != 0) {
886 err_print("Syntax error: %s\n", input);
887 } else {
888 fmt_print("\n");
889 (void) default_page(n);
890 fmt_print("\n");
891 }
892 } else if (*s == 'a') {
893 default_all_pages();
894 } else {
895 err_print("Syntax error: %s\n", input);
896 }
897 }
898
899
900 static void
default_all_pages(void)901 default_all_pages(void)
902 {
903 char *p;
904 struct mode_header *mh;
905 struct mode_page *mp;
906 int n;
907 struct uscsi_cmd ucmd;
908 union scsi_cdb cdb;
909 char msbuf[MAX_MODE_SENSE_SIZE];
910 int nbytes = sizeof (msbuf);
911 int status;
912
913 /*
914 * Build and execute the uscsi ioctl. Note that
915 * we cannot simply call uscsi_mode_sense() here,
916 * since that function attempts to valididate the
917 * returned data, and the page 0x3f has a unique
918 * format.
919 */
920 nbytes = MAX_MODE_SENSE_SIZE;
921 (void) memset(msbuf, 0, nbytes);
922 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
923 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
924 cdb.scc_cmd = SCMD_MODE_SENSE;
925 FORMG0COUNT(&cdb, (uchar_t)nbytes);
926 cdb.cdb_opaque[2] = MODE_SENSE_PC_DEFAULT | 0x3f;
927 ucmd.uscsi_cdb = (caddr_t)&cdb;
928 ucmd.uscsi_cdblen = CDB_GROUP0;
929 ucmd.uscsi_bufaddr = msbuf;
930 ucmd.uscsi_buflen = nbytes;
931 status = uscsi_cmd(cur_file, &ucmd, (option_msg) ? F_NORMAL : F_SILENT);
932 if (status) {
933 if (!option_msg) {
934 err_print("\nMode sense page 0x3f failed\n");
935 }
936 return;
937 }
938
939 fmt_print("\n");
940
941 /*
942 * Now parse the page 0x3f
943 */
944 mh = (struct mode_header *)msbuf;
945 nbytes = mh->length - sizeof (struct mode_header) -
946 mh->bdesc_length + 1;
947 p = msbuf + sizeof (struct mode_header) + mh->bdesc_length;
948
949 while (nbytes > 0) {
950 mp = (struct mode_page *)p;
951 n = mp->length + sizeof (struct mode_page);
952 nbytes -= n;
953 if (nbytes < 0)
954 break;
955 if (default_page(mp->code) == 0) {
956 goto error;
957 }
958 p += n;
959 }
960
961 if (nbytes < 0) {
962 err_print("Mode sense page 0x3f formatted incorrectly:\n");
963 }
964 error:
965 fmt_print("\n");
966 }
967
968
969 static int
default_page(int pageno)970 default_page(int pageno)
971 {
972 struct scsi_ms_header header;
973 char saved[MAX_MODE_SENSE_SIZE];
974 char current[MAX_MODE_SENSE_SIZE];
975 char dfault[MAX_MODE_SENSE_SIZE];
976 struct mode_page *sp;
977 struct mode_page *cp;
978 struct mode_page *dp;
979 int length;
980 int flags;
981 int i;
982 int need_mode_select;
983
984 /*
985 * Get default mode sense
986 */
987 if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_DEFAULT,
988 dfault, MAX_MODE_SENSE_SIZE, &header)) {
989 err_print("Mode sense on page %x (dfault) failed\n", pageno);
990 return (0);
991 }
992
993 /*
994 * Get the current mode sense.
995 */
996 if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_CURRENT,
997 current, MAX_MODE_SENSE_SIZE, &header)) {
998 err_print("Mode sense on page %x (current) failed\n", pageno);
999 return (0);
1000 }
1001
1002 /*
1003 * Get saved mode sense. If this fails, assume it is
1004 * the same as the current.
1005 */
1006 if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_SAVED,
1007 saved, MAX_MODE_SENSE_SIZE, &header)) {
1008 (void) memcpy(saved, current, MAX_MODE_SENSE_SIZE);
1009 }
1010
1011 /*
1012 * Determine if we need a mode select on this page.
1013 * Just deal with the intersection of the three pages.
1014 */
1015 sp = (struct mode_page *)saved;
1016 cp = (struct mode_page *)current;
1017 dp = (struct mode_page *)dfault;
1018 length = min(MODESENSE_PAGE_LEN(sp), MODESENSE_PAGE_LEN(cp));
1019 length = min(length, MODESENSE_PAGE_LEN(dp));
1020
1021 need_mode_select = 0;
1022 for (i = 2; i < length; i++) {
1023 if (current[i] != dfault[i] || saved[i] != dfault[i]) {
1024 current[i] = dfault[i];
1025 need_mode_select = 1;
1026 }
1027 }
1028
1029 if (need_mode_select == 0) {
1030 fmt_print("Defaulting page 0x%x: ok\n", pageno);
1031 return (1);
1032 }
1033
1034 /*
1035 * A change was made. Do a mode select
1036 * We always want to set the Page Format bit.
1037 * Set the Save Page bit if the drive indicates
1038 * that it can save this page.
1039 */
1040 length = MODESENSE_PAGE_LEN(cp);
1041 flags = MODE_SELECT_PF;
1042 if (cp->ps) {
1043 flags |= MODE_SELECT_SP;
1044 }
1045 cp->ps = 0;
1046 header.mode_header.length = 0;
1047 header.mode_header.device_specific = 0;
1048 if (uscsi_mode_select(cur_file, pageno, flags,
1049 current, length, &header)) {
1050 /*
1051 * Failed - try not saving parameters,
1052 * if possible.
1053 */
1054 if (flags & MODE_SELECT_SP) {
1055 flags &= ~MODE_SELECT_SP;
1056 if (uscsi_mode_select(cur_file, pageno, flags,
1057 saved, length, &header)) {
1058 fmt_print("Defaulting page 0x%x: failed\n",
1059 pageno);
1060 } else {
1061 fmt_print("Defaulting page 0x%x: ", pageno);
1062 fmt_print("cannot save page permanently\n");
1063 }
1064 } else {
1065 fmt_print("Defaulting page 0x%x: ", pageno);
1066 fmt_print("cannot save page permanently\n");
1067 }
1068 } else {
1069 fmt_print("Defaulting page 0x%x: mode select ok\n", pageno);
1070 }
1071
1072 return (1);
1073 }
1074