xref: /titanic_51/usr/src/cmd/format/menu_scsi.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
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 1991-2003 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 "checkmount.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]);
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 	/*
570 	 * Let the user choose between formatting with either
571 	 * the P, or the P&G lists.  Note that yes is 0, no is 1.
572 	 */
573 	deflt = 0;
574 	ioparam.io_charlist = confirm_list;
575 	grown_list = !input(FIO_MSTR, "Format with the Grown Defects list",
576 		'?', &ioparam, &deflt, DATA_INPUT);
577 
578 	/*
579 	 * Construct the uscsi format ioctl.
580 	 * To format with the P and G list, we set the fmtData
581 	 * and cmpLst bits to zero.  To format with just the
582 	 * P list, we set the fmtData bit (meaning that we will
583 	 * send down a defect list in the data phase) and the
584 	 * cmpLst bit (meaning that the list we send is the
585 	 * complete G list), and a defect list header with
586 	 * a defect list length of zero.
587 	 */
588 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
589 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
590 	cdb.scc_cmd = SCMD_FORMAT;
591 	ucmd.uscsi_cdb = (caddr_t)&cdb;
592 	ucmd.uscsi_cdblen = CDB_GROUP0;
593 	if (!grown_list) {
594 		/*
595 		 * No G list.   Send empty defect list to replace it.
596 		 */
597 		cdb.cdb_opaque[1] = FPB_DATA | FPB_CMPLT | FPB_BFI;
598 		(void) memset((char *)&defect_hdr, 0, sizeof (defect_hdr));
599 		ucmd.uscsi_bufaddr = (caddr_t)&defect_hdr;
600 		ucmd.uscsi_buflen = sizeof (defect_hdr);
601 	}
602 
603 	/*
604 	 * Issue the format ioctl
605 	 */
606 	fmt_print("Formatting...\n");
607 	(void) fflush(stdout);
608 	status = uscsi_cmd(cur_file, &ucmd, F_NORMAL);
609 	fmt_print(status ? "Format failed\n\n" : "Format ok\n\n");
610 	return (status);
611 }
612 
613 
614 /*
615  * List common SCSI-2 mode pages
616  */
617 static void
618 do_list()
619 {
620 	fmt_print("\n\
621 Common SCSI-2 pages applicable to direct-access devices:\n\n");
622 	fmt_print("Page 0x1   - Read-Write Error Recovery Page\n");
623 	fmt_print("Page 0x2   - Disconnect-Reconnect Page\n");
624 	fmt_print("Page 0x3   - Format Device Page\n");
625 	fmt_print("Page 0x4   - Rigid Disk Geometry Page\n");
626 	fmt_print("Page 0x7   - Verify Error Recovery Page\n");
627 	fmt_print("Page 0x8   - Caching Page\n");
628 	fmt_print("Page 0xA   - Control Mode Page\n");
629 	fmt_print("\n");
630 }
631 
632 
633 /*
634  * Labels for the various fields of the scsi_inquiry structure
635  */
636 static char *scsi_inquiry_labels[] = {
637 	"Vendor:                     ",
638 	"Product:                    ",
639 	"Revision:                   ",
640 	"Removable media:            ",
641 	"Device type:                ",
642 	"ISO version:                ",
643 	"ECMA version:               ",
644 	"ANSI version:               ",
645 	"Async event notification:   ",
646 	"Terminate i/o process msg:  ",
647 	"Response data format:       ",
648 	"Additional length:          ",
649 	"Relative addressing:        ",
650 	"32 bit transfers:           ",
651 	"16 bit transfers:           ",
652 	"Synchronous transfers:      ",
653 	"Linked commands:            ",
654 	"Command queueing:           ",
655 	"Soft reset option:          "
656 };
657 
658 
659 /*
660  * Dump the full inquiry as returned by the device
661  */
662 static int
663 do_inquiry()
664 {
665 	char			inqbuf[255];
666 	struct scsi_inquiry	*inq;
667 	char			**p;
668 
669 	inq = (struct scsi_inquiry *)inqbuf;
670 
671 	if (uscsi_inquiry(cur_file, inqbuf, sizeof (inqbuf))) {
672 		err_print("\nInquiry failed\n");
673 		return (1);
674 	}
675 
676 	fmt_print("\nInquiry:\n");
677 	/*
678 	 * The SCSI-2 spec defines "Additional length" as (n-4) bytes,
679 	 * where n is the last byte of the INQUIRY data.  Thus
680 	 * there are n+1 bytes of INQUIRY data.  We need to add 5 to
681 	 * inq_len in order to get all the INQUIRY data.
682 	 */
683 	dump("    ", inqbuf, inq->inq_len + 5, HEX_ASCII);
684 	fmt_print("\n");
685 
686 	p = scsi_inquiry_labels;
687 
688 	fmt_print("%s", *p++);
689 	print_buf(inq->inq_vid, sizeof (inq->inq_vid));
690 	fmt_print("\n%s", *p++);
691 	print_buf(inq->inq_pid, sizeof (inq->inq_pid));
692 	fmt_print("\n%s", *p++);
693 	print_buf(inq->inq_revision, sizeof (inq->inq_revision));
694 
695 	fmt_print("\n%s%s\n", *p++, inq->inq_rmb ? "yes" : "no");
696 	fmt_print("%s%d\n", *p++, inq->inq_qual);
697 	fmt_print("%s%d\n", *p++, inq->inq_iso);
698 	fmt_print("%s%d\n", *p++, inq->inq_ecma);
699 	fmt_print("%s%d\n", *p++, inq->inq_ansi);
700 	fmt_print("%s%s\n", *p++, inq->inq_aenc ? "yes" : "no");
701 	fmt_print("%s%s\n", *p++, inq->inq_trmiop ? "yes" : "no");
702 	fmt_print("%s%d\n", *p++, inq->inq_rdf);
703 	fmt_print("%s%d\n", *p++, inq->inq_len);
704 	fmt_print("%s%s\n", *p++, inq->inq_reladdr ? "yes" : "no");
705 	fmt_print("%s%s\n", *p++, inq->inq_wbus32 ? "yes" : "no");
706 	fmt_print("%s%s\n", *p++, inq->inq_wbus16 ? "yes" : "no");
707 	fmt_print("%s%s\n", *p++, inq->inq_sync ? "yes" : "no");
708 	fmt_print("%s%s\n", *p++, inq->inq_linked ? "yes" : "no");
709 	fmt_print("%s%s\n", *p++, inq->inq_cmdque ? "yes" : "no");
710 	fmt_print("%s%s\n", *p++, inq->inq_sftre ? "yes" : "no");
711 
712 	fmt_print("\n");
713 	return (0);
714 }
715 
716 
717 static void
718 do_apply()
719 {
720 	if (change_list == NULL) {
721 		fmt_print("\nlist empty.\n");
722 	} else {
723 		(void) do_mode_select(change_list);
724 		free_change_list();
725 	}
726 }
727 
728 
729 static void
730 do_cancel()
731 {
732 	if (change_list == NULL) {
733 		fmt_print("\nlist empty.\n");
734 	} else {
735 		free_change_list();
736 	}
737 }
738 
739 
740 static void
741 do_display()
742 {
743 	struct chg_list	*cp;
744 
745 	if (change_list == NULL) {
746 		fmt_print("\nlist empty.\n");
747 	} else {
748 		fmt_print("\nPage 0x%x\n", current_page);
749 		for (cp = change_list; cp != NULL; cp = cp->next) {
750 			fmt_print("   b0x%x ", cp->byteno);
751 			switch (cp->mode) {
752 			case CHG_MODE_ABS:
753 				fmt_print("= 0x%x\n", cp->value);
754 				break;
755 			case CHG_MODE_SET:
756 				fmt_print("|= 0x%x\n", cp->value);
757 				break;
758 			case CHG_MODE_CLR:
759 				fmt_print("&= ~0x%x\n",
760 					(~(cp->value)) & 0xff);
761 				break;
762 			default:
763 				impossible("do_display");
764 				/*NOTREACHED*/
765 			}
766 		}
767 		fmt_print("\n");
768 	}
769 }
770 
771 
772 static int
773 parse_change_spec(full_input, input, pageno, chg_item)
774 	char		*full_input;
775 	char		*input;
776 	int		pageno;
777 	struct chg_list	*chg_item;
778 {
779 	char		*p;
780 	int		tilde;
781 
782 	assert(*input == 'b');
783 
784 	chg_item->pageno = pageno;
785 	chg_item->next = NULL;
786 
787 	input++;
788 	chg_item->byteno = (int)strtol(input, &p, 0);
789 	if (p == input) {
790 		err_print("Syntax error: %s\n", full_input);
791 		return (0);
792 	}
793 	if (chg_item->byteno < 2) {
794 		err_print(" Unsupported byte offset: %d\n",
795 			chg_item->byteno);
796 		return (0);
797 	}
798 	for (input = p; *input == ' '; input++)
799 		;
800 	chg_item->mode = CHG_MODE_UNDEFINED;
801 	switch (*input++) {
802 	case '=':
803 		chg_item->mode = CHG_MODE_ABS;
804 		break;
805 	case '|':
806 		if (*input++ == '=') {
807 			chg_item->mode = CHG_MODE_SET;
808 		}
809 		break;
810 	case '&':
811 		if (*input++ == '=') {
812 			chg_item->mode = CHG_MODE_CLR;
813 		}
814 		break;
815 	}
816 	if (chg_item->mode == CHG_MODE_UNDEFINED) {
817 		err_print("Syntax error: %s\n", full_input);
818 		return (0);
819 	}
820 	for (; *input == ' '; input++)
821 		;
822 	if (*input == '~') {
823 		tilde = 1;
824 		for (input++; *input == ' '; input++)
825 			;
826 	} else {
827 		tilde = 0;
828 	}
829 	chg_item->value = (int)strtol(input, &p, 0);
830 	if (p == input || *p != 0) {
831 		err_print("Syntax error: %s\n", full_input);
832 		return (0);
833 	}
834 	/*
835 	 * Apply complement if selected.
836 	 * Constrain to a byte value.
837 	 */
838 	if (tilde) {
839 		chg_item->value = ~chg_item->value;
840 	}
841 	chg_item->value &= 0xff;
842 
843 	return (1);
844 }
845 
846 
847 static void
848 add_new_change_list_item(chg_item)
849 	struct chg_list		*chg_item;
850 {
851 	struct chg_list	*cp;
852 
853 	if (change_list == NULL) {
854 		change_list = chg_item;
855 	} else {
856 		for (cp = change_list; cp->next != NULL; cp = cp->next)
857 			;
858 		cp->next = chg_item;
859 	}
860 	chg_item->next = NULL;
861 }
862 
863 
864 static void
865 free_change_list()
866 {
867 	struct chg_list	*cp;
868 	struct chg_list	*cp2;
869 
870 	cp = change_list;
871 	while (cp != NULL) {
872 		cp2 = cp->next;
873 		destroy_data((char *)cp);
874 		cp = cp2;
875 	}
876 	change_list = NULL;
877 }
878 
879 
880 static void
881 do_default(input)
882 	char		*input;
883 {
884 	char		*s = input;
885 	char		*p;
886 	int		n;
887 
888 	/*
889 	 * Reset current page indicator
890 	 */
891 	current_page = -1;
892 
893 	/*
894 	 * Skip the leading "default" command, which we
895 	 * must have, or we wouldn't have come here,
896 	 * and any white space.
897 	 */
898 	while (isspace(*s)) {
899 		s++;
900 	}
901 
902 	while (*s && isascii(*s) && isalpha(*s)) {
903 		s++;
904 	}
905 
906 	while (isspace(*s)) {
907 		s++;
908 	}
909 
910 	/*
911 	 * Subsequent modifier must be either "p<n>", or "all".
912 	 */
913 	if (*s == 'p') {
914 		s++;
915 		n = (int)strtol(s, &p, 0);
916 		if (p == s || *p != 0) {
917 			err_print("Syntax error: %s\n", input);
918 		} else {
919 			fmt_print("\n");
920 			(void) default_page(n);
921 			fmt_print("\n");
922 		}
923 	} else if (*s == 'a') {
924 		default_all_pages();
925 	} else {
926 		err_print("Syntax error: %s\n", input);
927 	}
928 }
929 
930 
931 static void
932 default_all_pages()
933 {
934 	char			*p;
935 	struct mode_header	*mh;
936 	struct mode_page	*mp;
937 	int			n;
938 	struct uscsi_cmd	ucmd;
939 	union scsi_cdb		cdb;
940 	char			msbuf[MAX_MODE_SENSE_SIZE];
941 	int			nbytes = sizeof (msbuf);
942 	int			status;
943 
944 	/*
945 	 * Build and execute the uscsi ioctl.  Note that
946 	 * we cannot simply call uscsi_mode_sense() here,
947 	 * since that function attempts to valididate the
948 	 * returned data, and the page 0x3f has a unique
949 	 * format.
950 	 */
951 	nbytes = MAX_MODE_SENSE_SIZE;
952 	(void) memset(msbuf, 0, nbytes);
953 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
954 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
955 	cdb.scc_cmd = SCMD_MODE_SENSE;
956 	FORMG0COUNT(&cdb, (uchar_t)nbytes);
957 	cdb.cdb_opaque[2] = MODE_SENSE_PC_DEFAULT | 0x3f;
958 	ucmd.uscsi_cdb = (caddr_t)&cdb;
959 	ucmd.uscsi_cdblen = CDB_GROUP0;
960 	ucmd.uscsi_bufaddr = msbuf;
961 	ucmd.uscsi_buflen = nbytes;
962 	status = uscsi_cmd(cur_file, &ucmd,
963 		(option_msg) ? F_NORMAL : F_SILENT);
964 	if (status) {
965 		if (!option_msg) {
966 			err_print("\nMode sense page 0x3f failed\n");
967 		}
968 		return;
969 	}
970 
971 	fmt_print("\n");
972 
973 	/*
974 	 * Now parse the page 0x3f
975 	 */
976 	mh = (struct mode_header *)msbuf;
977 	nbytes = mh->length - sizeof (struct mode_header) -
978 			mh->bdesc_length + 1;
979 	p = msbuf + sizeof (struct mode_header) + mh->bdesc_length;
980 
981 	while (nbytes > 0) {
982 		mp = (struct mode_page *)p;
983 		n = mp->length + sizeof (struct mode_page);
984 		nbytes -= n;
985 		if (nbytes < 0)
986 			break;
987 		if (default_page(mp->code) == 0) {
988 			goto error;
989 		}
990 		p += n;
991 	}
992 
993 	if (nbytes < 0) {
994 		err_print("Mode sense page 0x3f formatted incorrectly:\n");
995 	}
996 error:
997 	fmt_print("\n");
998 }
999 
1000 
1001 static int
1002 default_page(pageno)
1003 	int		pageno;
1004 {
1005 	struct scsi_ms_header	header;
1006 	char			saved[MAX_MODE_SENSE_SIZE];
1007 	char			current[MAX_MODE_SENSE_SIZE];
1008 	char			dfault[MAX_MODE_SENSE_SIZE];
1009 	struct mode_page	*sp;
1010 	struct mode_page	*cp;
1011 	struct mode_page	*dp;
1012 	int			length;
1013 	int			flags;
1014 	int			i;
1015 	int			need_mode_select;
1016 
1017 	/*
1018 	 * Get default mode sense
1019 	 */
1020 	if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_DEFAULT,
1021 			dfault, MAX_MODE_SENSE_SIZE, &header)) {
1022 		err_print("Mode sense on page %x (dfault) failed\n",
1023 			pageno);
1024 		return (0);
1025 	}
1026 
1027 	/*
1028 	 * Get the current mode sense.
1029 	 */
1030 	if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_CURRENT,
1031 			current, MAX_MODE_SENSE_SIZE, &header)) {
1032 		err_print("Mode sense on page %x (current) failed\n",
1033 			pageno);
1034 		return (0);
1035 	}
1036 
1037 	/*
1038 	 * Get saved mode sense.  If this fails, assume it is
1039 	 * the same as the current.
1040 	 */
1041 	if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_SAVED,
1042 			saved, MAX_MODE_SENSE_SIZE, &header)) {
1043 		(void) memcpy(saved, current, MAX_MODE_SENSE_SIZE);
1044 	}
1045 
1046 	/*
1047 	 * Determine if we need a mode select on this page.
1048 	 * Just deal with the intersection of the three pages.
1049 	 */
1050 	sp = (struct mode_page *)saved;
1051 	cp = (struct mode_page *)current;
1052 	dp = (struct mode_page *)dfault;
1053 	length = min(MODESENSE_PAGE_LEN(sp), MODESENSE_PAGE_LEN(cp));
1054 	length = min(length, MODESENSE_PAGE_LEN(dp));
1055 
1056 	need_mode_select = 0;
1057 	for (i = 2; i < length; i++) {
1058 		if (current[i] != dfault[i] || saved[i] != dfault[i]) {
1059 			current[i] = dfault[i];
1060 			need_mode_select = 1;
1061 		}
1062 	}
1063 
1064 	if (need_mode_select == 0) {
1065 		fmt_print("Defaulting page 0x%x: ok\n",
1066 			pageno);
1067 		return (1);
1068 	}
1069 
1070 	/*
1071 	 * A change was made.  Do a mode select
1072 	 * We always want to set the Page Format bit.
1073 	 * Set the Save Page bit if the drive indicates
1074 	 * that it can save this page.
1075 	 */
1076 	length = MODESENSE_PAGE_LEN(cp);
1077 	flags = MODE_SELECT_PF;
1078 	if (cp->ps) {
1079 		flags |= MODE_SELECT_SP;
1080 	}
1081 	cp->ps = 0;
1082 	header.mode_header.length = 0;
1083 	header.mode_header.device_specific = 0;
1084 	if (uscsi_mode_select(cur_file, pageno, flags,
1085 			current, length, &header)) {
1086 		/*
1087 		 * Failed - try not saving parameters,
1088 		 * if possible.
1089 		 */
1090 		if (flags & MODE_SELECT_SP) {
1091 			flags &= ~MODE_SELECT_SP;
1092 			if (uscsi_mode_select(cur_file, pageno, flags,
1093 					saved, length, &header)) {
1094 				fmt_print("Defaulting page 0x%x: failed\n",
1095 					pageno);
1096 			} else {
1097 				fmt_print("Defaulting page 0x%x: ",
1098 					pageno);
1099 				fmt_print("cannot save page permanently\n");
1100 			}
1101 		} else {
1102 			fmt_print("Defaulting page 0x%x: ", pageno);
1103 			fmt_print("cannot save page permanently\n");
1104 		}
1105 	} else {
1106 		fmt_print("Defaulting page 0x%x: mode select ok\n", pageno);
1107 	}
1108 
1109 	return (1);
1110 }
1111