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