xref: /illumos-gate/usr/src/cmd/format/menu_scsi.c (revision dbed73cbda2229fd1aa6dc5743993cae7f0a7ee9)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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