1 /* $NetBSD: scsictl.c,v 1.19 2002/09/26 06:15:38 petrov Exp $ */ 2 3 /*- 4 * Copyright (c) 1998, 2002 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 * NASA Ames Research Center. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 /* 41 * scsictl(8) - a program to manipulate SCSI devices and busses. 42 */ 43 44 #include <sys/param.h> 45 #include <sys/ioctl.h> 46 #include <sys/scsiio.h> 47 #include <err.h> 48 #include <errno.h> 49 #include <fcntl.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <unistd.h> 54 #include <util.h> 55 56 #include <dev/scsipi/scsipi_all.h> 57 #include <dev/scsipi/scsi_all.h> 58 #include <dev/scsipi/scsi_disk.h> 59 #include <dev/scsipi/scsipiconf.h> 60 61 #include "extern.h" 62 63 struct command { 64 const char *cmd_name; 65 const char *arg_names; 66 void (*cmd_func) __P((int, char *[])); 67 }; 68 69 int main __P((int, char *[])); 70 void usage __P((void)); 71 72 int fd; /* file descriptor for device */ 73 const char *dvname; /* device name */ 74 char dvname_store[MAXPATHLEN]; /* for opendisk(3) */ 75 const char *cmdname; /* command user issued */ 76 const char *argnames; /* helpstring: expected arguments */ 77 struct scsi_addr dvaddr; /* SCSI device's address */ 78 79 void device_format __P((int, char *[])); 80 void device_identify __P((int, char *[])); 81 void device_reassign __P((int, char *[])); 82 void device_release __P((int, char *[])); 83 void device_reserve __P((int, char *[])); 84 void device_reset __P((int, char *[])); 85 void device_debug __P((int, char *[])); 86 void device_start __P((int, char *[])); 87 void device_stop __P((int, char *[])); 88 void device_tur __P((int, char *[])); 89 void device_getcache __P((int, char *[])); 90 void device_setcache __P((int, char *[])); 91 92 struct command device_commands[] = { 93 { "format", "[blocksize [immediate]]", device_format }, 94 { "identify", "", device_identify }, 95 { "reassign", "blkno [blkno [...]]", device_reassign }, 96 { "release", "", device_release }, 97 { "reserve", "", device_reserve }, 98 { "reset", "", device_reset }, 99 { "debug", "level", device_debug }, 100 { "start", "", device_start }, 101 { "stop", "", device_stop }, 102 { "tur", "", device_tur }, 103 { "getcache", "", device_getcache }, 104 { "setcache", "none|r|w|rw [save]", device_setcache }, 105 { NULL, NULL, NULL }, 106 }; 107 108 void bus_reset __P((int, char *[])); 109 void bus_scan __P((int, char *[])); 110 void bus_detach __P((int, char *[])); 111 112 struct command bus_commands[] = { 113 { "reset", "", bus_reset }, 114 { "scan", "target lun", bus_scan }, 115 { "detach", "target lun", bus_detach }, 116 { NULL, NULL, NULL }, 117 }; 118 119 int 120 main(argc, argv) 121 int argc; 122 char *argv[]; 123 { 124 struct command *commands; 125 int i; 126 127 /* Must have at least: device command */ 128 if (argc < 3) 129 usage(); 130 131 /* Skip program name, get and skip device name and command. */ 132 dvname = argv[1]; 133 cmdname = argv[2]; 134 argv += 3; 135 argc -= 3; 136 137 /* 138 * Open the device and determine if it's a scsibus or an actual 139 * device. Devices respond to the SCIOCIDENTIFY ioctl. 140 */ 141 fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0); 142 if (fd == -1) { 143 if (errno == ENOENT) { 144 /* 145 * Device doesn't exist. Probably trying to open 146 * a device which doesn't use disk semantics for 147 * device name. Try again, specifying "cooked", 148 * which leaves off the "r" in front of the device's 149 * name. 150 */ 151 fd = opendisk(dvname, O_RDWR, dvname_store, 152 sizeof(dvname_store), 1); 153 if (fd == -1) 154 err(1, "%s", dvname); 155 } else 156 err(1, "%s", dvname); 157 } 158 159 /* 160 * Point the dvname at the actual device name that opendisk() opened. 161 */ 162 dvname = dvname_store; 163 164 if (ioctl(fd, SCIOCIDENTIFY, &dvaddr) < 0) 165 commands = bus_commands; 166 else 167 commands = device_commands; 168 169 /* Look up and call the command. */ 170 for (i = 0; commands[i].cmd_name != NULL; i++) 171 if (strcmp(cmdname, commands[i].cmd_name) == 0) 172 break; 173 if (commands[i].cmd_name == NULL) 174 errx(1, "unknown %s command: %s", 175 commands == bus_commands ? "bus" : "device", cmdname); 176 177 argnames = commands[i].arg_names; 178 179 (*commands[i].cmd_func)(argc, argv); 180 exit(0); 181 } 182 183 void 184 usage() 185 { 186 int i; 187 188 fprintf(stderr, "Usage: %s device command [arg [...]]\n", 189 getprogname()); 190 191 fprintf(stderr, " Commands pertaining to scsi devices:\n"); 192 for (i=0; device_commands[i].cmd_name != NULL; i++) 193 fprintf(stderr, "\t%s %s\n", device_commands[i].cmd_name, 194 device_commands[i].arg_names); 195 fprintf(stderr, " Commands pertaining to scsi busses:\n"); 196 for (i=0; bus_commands[i].cmd_name != NULL; i++) 197 fprintf(stderr, "\t%s %s\n", bus_commands[i].cmd_name, 198 bus_commands[i].arg_names); 199 fprintf(stderr, " Use `any' or `all' to wildcard target or lun\n"); 200 201 exit(1); 202 } 203 204 /* 205 * DEVICE COMMANDS 206 */ 207 208 /* 209 * device_format: 210 * 211 * Format a direct access device. 212 */ 213 void 214 device_format(argc, argv) 215 int argc; 216 char *argv[]; 217 { 218 u_int32_t blksize; 219 int i, j, immediate; 220 #define PC (65536/10) 221 static int complete[] = { 222 PC*1, PC*2, PC*3, PC*4, PC*5, PC*6, PC*7, PC*8, PC*9, 65536 223 }; 224 char *cp, buffer[64]; 225 struct scsipi_sense_data sense; 226 struct scsi_format_unit cmd; 227 struct { 228 struct scsi_format_unit_defect_list_header header; 229 /* optional initialization pattern */ 230 /* optional defect list */ 231 } dfl; 232 struct { 233 struct scsipi_mode_header header; 234 struct scsi_blk_desc blk_desc; 235 struct page_disk_format format_page; 236 } mode_page; 237 struct { 238 struct scsipi_mode_header header; 239 struct scsi_blk_desc blk_desc; 240 } data_select; 241 242 243 /* Blocksize is an optional argument. */ 244 if (argc > 2) 245 usage(); 246 247 /* 248 * Loop doing Request Sense to clear any pending Unit Attention. 249 * 250 * Multiple conditions may exist on the drive which are returned 251 * in priority order. 252 */ 253 for (i = 0; i < 8; i++) { 254 scsi_request_sense(fd, &sense, sizeof (sense)); 255 if ((j = sense.flags & SSD_KEY) == SKEY_NO_SENSE) 256 break; 257 } 258 /* 259 * Make sure we cleared any pending Unit Attention 260 */ 261 if (j != SKEY_NO_SENSE) { 262 cp = scsi_decode_sense((const unsigned char *) &sense, 2, 263 buffer, sizeof (buffer)); 264 errx(1, "failed to clean Unit Attention: %s", cp); 265 } 266 267 /* 268 * Get the DISK FORMAT mode page. SCSI-2 recommends specifying the 269 * interleave read from this page in the FORMAT UNIT command. 270 */ 271 scsi_mode_sense(fd, 0x03, 0x00, &mode_page, sizeof(mode_page)); 272 273 j = (mode_page.format_page.bytes_s[0] << 8) | 274 (mode_page.format_page.bytes_s[1]); 275 276 if (j != DEV_BSIZE) 277 printf("current disk sector size: %hd\n", j); 278 279 memset(&cmd, 0, sizeof(cmd)); 280 281 cmd.opcode = SCSI_FORMAT_UNIT; 282 memcpy(cmd.interleave, mode_page.format_page.interleave, 283 sizeof(cmd.interleave)); 284 285 /* 286 * The blocksize on the device is only changed if the user 287 * specified a new blocksize. If not specified the blocksize 288 * used for the device will be the Default value in the device. 289 * We don't specify the number of blocks since the format 290 * command will always reformat the entire drive. Also by 291 * not specifying a block count the drive will reset the 292 * block count to the maximum available after the format 293 * completes if the blocksize was changed in the format. 294 * Finally, the new disk geometry will not but updated on 295 * the drive in permanent storage until _AFTER_ the format 296 * completes successfully. 297 */ 298 if (argc > 0) { 299 blksize = strtoul(argv[0], &cp, 10); 300 if (*cp != '\0') 301 errx(1, "invalid block size: %s", argv[0]); 302 303 memset(&data_select, 0, sizeof(data_select)); 304 305 data_select.header.blk_desc_len = sizeof(struct scsi_blk_desc); 306 /* 307 * blklen in desc is 3 bytes with a leading reserved byte 308 */ 309 _lto4b(blksize, &data_select.blk_desc.reserved); 310 311 /* 312 * Issue Mode Select to modify the device blocksize to be 313 * used on the Format. The modified device geometry will 314 * be stored as Current and Saved Page 3 parameters when 315 * the Format completes. 316 */ 317 scsi_mode_select(fd, 0, &data_select, sizeof(data_select)); 318 319 /* 320 * Since user specified a specific block size make sure it 321 * gets stored in the device when the format completes. 322 * 323 * Also scrub the defect list back to the manufacturers 324 * original. 325 */ 326 cmd.flags = SFU_CMPLST | SFU_FMTDATA; 327 } 328 329 memset(&dfl, 0, sizeof(dfl)); 330 331 if (argc > 1 && strncmp(argv[1], "imm", 3) == 0) { 332 /* 333 * Signal target for an immediate return from Format. 334 * 335 * We'll poll for completion status. 336 */ 337 dfl.header.flags = DLH_IMMED; 338 immediate = 1; 339 } else { 340 immediate = 0; 341 } 342 343 scsi_command(fd, &cmd, sizeof(cmd), &dfl, sizeof(dfl), 344 8 * 60 * 60 * 1000, 0); 345 346 /* 347 * Poll device for completion of Format 348 */ 349 if (immediate) { 350 i = 0; 351 printf("formatting."); 352 fflush(stdout); 353 do { 354 scsireq_t req; 355 struct scsipi_test_unit_ready tcmd; 356 357 memset(&tcmd, 0, sizeof(cmd)); 358 tcmd.opcode = TEST_UNIT_READY; 359 360 memset(&req, 0, sizeof(req)); 361 memcpy(req.cmd, &tcmd, 6); 362 req.cmdlen = 6; 363 req.timeout = 10000; 364 req.senselen = SENSEBUFLEN; 365 366 if (ioctl(fd, SCIOCCOMMAND, &req) == -1) { 367 err(1, "SCIOCCOMMAND"); 368 } 369 370 if (req.retsts == SCCMD_OK) { 371 break; 372 } else if (req.retsts == SCCMD_TIMEOUT) { 373 fprintf(stderr, "%s: SCSI command timed out", 374 dvname); 375 break; 376 } else if (req.retsts == SCCMD_BUSY) { 377 fprintf(stderr, "%s: device is busy", 378 dvname); 379 break; 380 } else if (req.retsts != SCCMD_SENSE) { 381 fprintf(stderr, 382 "%s: device had unknown status %x", dvname, 383 req.retsts); 384 break; 385 } 386 memcpy(&sense, req.sense, SENSEBUFLEN); 387 if (sense.sense_key_spec_1 == SSD_SCS_VALID) { 388 j = (sense.sense_key_spec_2 << 8) | 389 (sense.sense_key_spec_3); 390 if (j >= complete[i]) { 391 printf(".%d0%%.", ++i); 392 fflush(stdout); 393 } 394 } 395 sleep(10); 396 } while ((sense.flags & SSD_KEY) == SKEY_NOT_READY); 397 printf(".100%%..done.\n"); 398 } 399 return; 400 } 401 402 /* 403 * device_identify: 404 * 405 * Display the identity of the device, including it's SCSI bus, 406 * target, lun, and it's vendor/product/revision information. 407 */ 408 void 409 device_identify(argc, argv) 410 int argc; 411 char *argv[]; 412 { 413 struct scsipi_inquiry_data inqbuf; 414 struct scsipi_inquiry cmd; 415 416 /* x4 in case every character is escaped, +1 for NUL. */ 417 char vendor[(sizeof(inqbuf.vendor) * 4) + 1], 418 product[(sizeof(inqbuf.product) * 4) + 1], 419 revision[(sizeof(inqbuf.revision) * 4) + 1]; 420 421 /* No arguments. */ 422 if (argc != 0) 423 usage(); 424 425 memset(&cmd, 0, sizeof(cmd)); 426 memset(&inqbuf, 0, sizeof(inqbuf)); 427 428 cmd.opcode = INQUIRY; 429 cmd.length = sizeof(inqbuf); 430 431 scsi_command(fd, &cmd, sizeof(cmd), &inqbuf, sizeof(inqbuf), 432 10000, SCCMD_READ); 433 434 scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor, 435 sizeof(inqbuf.vendor)); 436 scsi_strvis(product, sizeof(product), inqbuf.product, 437 sizeof(inqbuf.product)); 438 scsi_strvis(revision, sizeof(revision), inqbuf.revision, 439 sizeof(inqbuf.revision)); 440 441 printf("%s: scsibus%d target %d lun %d <%s, %s, %s>\n", 442 dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target, 443 dvaddr.addr.scsi.lun, vendor, product, revision); 444 445 return; 446 } 447 448 /* 449 * device_reassign: 450 * 451 * Reassign bad blocks on a direct access device. 452 */ 453 void 454 device_reassign(argc, argv) 455 int argc; 456 char *argv[]; 457 { 458 struct scsi_reassign_blocks cmd; 459 struct scsi_reassign_blocks_data *data; 460 size_t dlen; 461 u_int32_t blkno; 462 int i; 463 char *cp; 464 465 /* We get a list of block numbers. */ 466 if (argc < 1) 467 usage(); 468 469 /* 470 * Allocate the reassign blocks descriptor. The 4 comes from the 471 * size of the block address in the defect descriptor. 472 */ 473 dlen = sizeof(struct scsi_reassign_blocks_data) + ((argc - 1) * 4); 474 data = malloc(dlen); 475 if (data == NULL) 476 errx(1, "unable to allocate defect descriptor"); 477 memset(data, 0, dlen); 478 479 cmd.opcode = SCSI_REASSIGN_BLOCKS; 480 cmd.byte2 = 0; 481 cmd.unused[0] = 0; 482 cmd.unused[1] = 0; 483 cmd.unused[2] = 0; 484 cmd.control = 0; 485 486 /* Defect descriptor length. */ 487 _lto2b(argc * 4, data->length); 488 489 /* Build the defect descriptor list. */ 490 for (i = 0; i < argc; i++) { 491 blkno = strtoul(argv[i], &cp, 10); 492 if (*cp != '\0') 493 errx(1, "invalid block number: %s", argv[i]); 494 _lto4b(blkno, data->defect_descriptor[i].dlbaddr); 495 } 496 497 scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_WRITE); 498 499 free(data); 500 return; 501 } 502 503 /* 504 * device_release: 505 * 506 * Issue a RELEASE command to a SCSI drevice 507 */ 508 #ifndef SCSI_RELEASE 509 #define SCSI_RELEASE 0x17 510 #endif 511 void 512 device_release(argc, argv) 513 int argc; 514 char *argv[]; 515 { 516 struct scsipi_test_unit_ready cmd; /* close enough */ 517 518 /* No arguments. */ 519 if (argc != 0) 520 usage(); 521 522 memset(&cmd, 0, sizeof(cmd)); 523 524 cmd.opcode = SCSI_RELEASE; 525 526 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 527 528 return; 529 } 530 531 532 533 /* 534 * device_reserve: 535 * 536 * Issue a RESERVE command to a SCSI drevice 537 */ 538 #ifndef SCSI_RESERVE 539 #define SCSI_RESERVE 0x16 540 #endif 541 void 542 device_reserve(argc, argv) 543 int argc; 544 char *argv[]; 545 { 546 struct scsipi_test_unit_ready cmd; /* close enough */ 547 548 /* No arguments. */ 549 if (argc != 0) 550 usage(); 551 552 memset(&cmd, 0, sizeof(cmd)); 553 554 cmd.opcode = SCSI_RESERVE; 555 556 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 557 558 return; 559 } 560 561 /* 562 * device_reset: 563 * 564 * Issue a reset to a SCSI device. 565 */ 566 void 567 device_reset(argc, argv) 568 int argc; 569 char *argv[]; 570 { 571 572 /* No arguments. */ 573 if (argc != 0) 574 usage(); 575 576 if (ioctl(fd, SCIOCRESET, NULL) != 0) 577 err(1, "SCIOCRESET"); 578 579 return; 580 } 581 582 /* 583 * device_debug: 584 * 585 * Set debug level to a SCSI device. 586 * scsipi will print anything iff SCSIPI_DEBUG set in config. 587 */ 588 void 589 device_debug(argc, argv) 590 int argc; 591 char *argv[]; 592 { 593 int lvl; 594 595 if (argc < 1) 596 usage(); 597 598 lvl = atoi(argv[0]); 599 600 if (ioctl(fd, SCIOCDEBUG, &lvl) != 0) 601 err(1, "SCIOCDEBUG"); 602 603 return; 604 } 605 606 /* 607 * device_getcache: 608 * 609 * Get the caching parameters for a SCSI disk. 610 */ 611 void 612 device_getcache(argc, argv) 613 int argc; 614 char *argv[]; 615 { 616 struct { 617 struct scsipi_mode_header header; 618 struct scsi_blk_desc blk_desc; 619 struct page_caching caching_params; 620 } data; 621 622 /* No arguments. */ 623 if (argc != 0) 624 usage(); 625 626 scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data)); 627 628 if ((data.caching_params.flags & (CACHING_RCD|CACHING_WCE)) == 629 CACHING_RCD) 630 printf("%s: no caches enabled\n", dvname); 631 else { 632 printf("%s: read cache %senabled\n", dvname, 633 (data.caching_params.flags & CACHING_RCD) ? "not " : ""); 634 printf("%s: write-back cache %senabled\n", dvname, 635 (data.caching_params.flags & CACHING_WCE) ? "" : "not "); 636 } 637 printf("%s: caching parameters are %ssavable\n", dvname, 638 (data.caching_params.pg_code & PGCODE_PS) ? "" : "not "); 639 } 640 641 /* 642 * device_setcache: 643 * 644 * Set cache enables for a SCSI disk. 645 */ 646 void 647 device_setcache(argc, argv) 648 int argc; 649 char *argv[]; 650 { 651 struct { 652 struct scsipi_mode_header header; 653 struct scsi_blk_desc blk_desc; 654 struct page_caching caching_params; 655 } data; 656 int dlen; 657 u_int8_t flags, byte2; 658 659 if (argc > 2 || argc == 0) 660 usage(); 661 662 if (strcmp(argv[0], "none") == 0) 663 flags = CACHING_RCD; 664 else if (strcmp(argv[0], "r") == 0) 665 flags = 0; 666 else if (strcmp(argv[0], "w") == 0) 667 flags = CACHING_RCD|CACHING_WCE; 668 else if (strcmp(argv[0], "rw") == 0) 669 flags = CACHING_WCE; 670 else 671 usage(); 672 673 if (argc == 2) { 674 if (strcmp(argv[1], "save") == 0) 675 byte2 = SMS_SP; 676 else 677 usage(); 678 } 679 680 scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data)); 681 682 data.caching_params.pg_code &= PGCODE_MASK; 683 data.caching_params.flags = 684 (data.caching_params.flags & ~(CACHING_RCD|CACHING_WCE)) | flags; 685 686 data.caching_params.cache_segment_size[0] = 0; 687 data.caching_params.cache_segment_size[1] = 0; 688 689 data.header.data_length = 0; 690 691 dlen = sizeof(data.header) + sizeof(data.blk_desc) + 2 + 692 data.caching_params.pg_length; 693 694 scsi_mode_select(fd, byte2, &data, dlen); 695 } 696 697 /* 698 * device_start: 699 * 700 * Issue a start to a SCSI device. 701 */ 702 void 703 device_start(argc, argv) 704 int argc; 705 char *argv[]; 706 { 707 struct scsipi_start_stop cmd; 708 709 /* No arguments. */ 710 if (argc != 0) 711 usage(); 712 713 memset(&cmd, 0, sizeof(cmd)); 714 715 cmd.opcode = START_STOP; 716 cmd.how = SSS_START; 717 718 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 719 720 return; 721 } 722 723 /* 724 * device_stop: 725 * 726 * Issue a stop to a SCSI device. 727 */ 728 void 729 device_stop(argc, argv) 730 int argc; 731 char *argv[]; 732 { 733 struct scsipi_start_stop cmd; 734 735 /* No arguments. */ 736 if (argc != 0) 737 usage(); 738 739 memset(&cmd, 0, sizeof(cmd)); 740 741 cmd.opcode = START_STOP; 742 cmd.how = SSS_STOP; 743 744 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 745 746 return; 747 } 748 749 /* 750 * device_tur: 751 * 752 * Issue a TEST UNIT READY to a SCSI drevice 753 */ 754 void 755 device_tur(argc, argv) 756 int argc; 757 char *argv[]; 758 { 759 struct scsipi_test_unit_ready cmd; 760 761 /* No arguments. */ 762 if (argc != 0) 763 usage(); 764 765 memset(&cmd, 0, sizeof(cmd)); 766 767 cmd.opcode = TEST_UNIT_READY; 768 769 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 770 771 return; 772 } 773 774 /* 775 * BUS COMMANDS 776 */ 777 778 /* 779 * bus_reset: 780 * 781 * Issue a reset to a SCSI bus. 782 */ 783 void 784 bus_reset(argc, argv) 785 int argc; 786 char *argv[]; 787 { 788 789 /* No arguments. */ 790 if (argc != 0) 791 usage(); 792 793 if (ioctl(fd, SCBUSIORESET, NULL) != 0) 794 err(1, "SCBUSIORESET"); 795 796 return; 797 } 798 799 /* 800 * bus_scan: 801 * 802 * Rescan a SCSI bus for new devices. 803 */ 804 void 805 bus_scan(argc, argv) 806 int argc; 807 char *argv[]; 808 { 809 struct scbusioscan_args args; 810 char *cp; 811 812 /* Must have two args: target lun */ 813 if (argc != 2) 814 usage(); 815 816 if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0) 817 args.sa_target = -1; 818 else { 819 args.sa_target = strtol(argv[0], &cp, 10); 820 if (*cp != '\0' || args.sa_target < 0) 821 errx(1, "invalid target: %s", argv[0]); 822 } 823 824 if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0) 825 args.sa_lun = -1; 826 else { 827 args.sa_lun = strtol(argv[1], &cp, 10); 828 if (*cp != '\0' || args.sa_lun < 0) 829 errx(1, "invalid lun: %s", argv[1]); 830 } 831 832 if (ioctl(fd, SCBUSIOSCAN, &args) != 0) 833 err(1, "SCBUSIOSCAN"); 834 835 return; 836 } 837 838 /* 839 * bus_detach: 840 * 841 * detach SCSI devices from a bus. 842 */ 843 void 844 bus_detach(argc, argv) 845 int argc; 846 char *argv[]; 847 { 848 struct scbusiodetach_args args; 849 char *cp; 850 851 /* Must have two args: target lun */ 852 if (argc != 2) 853 usage(); 854 855 if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0) 856 args.sa_target = -1; 857 else { 858 args.sa_target = strtol(argv[0], &cp, 10); 859 if (*cp != '\0' || args.sa_target < 0) 860 errx(1, "invalid target: %s", argv[0]); 861 } 862 863 if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0) 864 args.sa_lun = -1; 865 else { 866 args.sa_lun = strtol(argv[1], &cp, 10); 867 if (*cp != '\0' || args.sa_lun < 0) 868 errx(1, "invalid lun: %s", argv[1]); 869 } 870 871 if (ioctl(fd, SCBUSIODETACH, &args) != 0) 872 err(1, "SCBUSIODETACH"); 873 874 return; 875 } 876