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