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