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