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