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