1 /* $NetBSD: scsictl.c,v 1.41 2024/11/09 11:09:40 mlelstv 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 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* 34 * scsictl(8) - a program to manipulate SCSI devices and busses. 35 */ 36 #include <sys/cdefs.h> 37 38 #ifndef lint 39 __RCSID("$NetBSD: scsictl.c,v 1.41 2024/11/09 11:09:40 mlelstv Exp $"); 40 #endif 41 42 43 #include <sys/param.h> 44 #include <sys/ioctl.h> 45 #include <sys/scsiio.h> 46 #include <err.h> 47 #include <errno.h> 48 #include <fcntl.h> 49 #include <limits.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <stdbool.h> 53 #include <string.h> 54 #include <unistd.h> 55 #include <util.h> 56 57 #include <dev/scsipi/scsi_spc.h> 58 #include <dev/scsipi/scsipi_all.h> 59 #include <dev/scsipi/scsi_disk.h> 60 #include <dev/scsipi/scsipiconf.h> 61 62 #include "extern.h" 63 64 struct command { 65 const char *cmd_name; 66 const char *arg_names; 67 void (*cmd_func)(int, char *[]); 68 }; 69 70 __dead static void usage(void); 71 72 static int fd; /* file descriptor for device */ 73 const char *dvname; /* device name */ 74 static char dvname_store[MAXPATHLEN]; /* for opendisk(3) */ 75 static const char *cmdname; /* command user issued */ 76 static struct scsi_addr dvaddr; /* SCSI device's address */ 77 78 static void device_defects(int, char *[]); 79 static void device_format(int, char *[]); 80 static void device_identify(int, char *[]); 81 static void device_reassign(int, char *[]); 82 static void device_release(int, char *[]); 83 static void device_reserve(int, char *[]); 84 static void device_reset(int, char *[]); 85 static void device_debug(int, char *[]); 86 static void device_prevent(int, char *[]); 87 static void device_allow(int, char *[]); 88 static void device_start(int, char *[]); 89 static void device_stop(int, char *[]); 90 static void device_tur(int, char *[]); 91 static void device_getcache(int, char *[]); 92 static void device_setcache(int, char *[]); 93 static void device_flushcache(int, char *[]); 94 static void device_setspeed(int, char *[]); 95 static void device_getrealloc(int, char *[]); 96 static void device_setrealloc(int, char *[]); 97 static void device_reportluns(int, char *[]); 98 99 static struct command device_commands[] = { 100 { "defects", "[primary] [grown] [block|byte|physical]", 101 device_defects }, 102 { "format", "[blocksize [immediate]]", device_format }, 103 { "identify", "[vpd]", device_identify }, 104 { "reassign", "blkno [blkno [...]]", device_reassign }, 105 { "release", "", device_release }, 106 { "reserve", "", device_reserve }, 107 { "reset", "", device_reset }, 108 { "debug", "level", device_debug }, 109 { "prevent", "", device_prevent }, 110 { "allow", "", device_allow }, 111 { "start", "", device_start }, 112 { "stop", "", device_stop }, 113 { "tur", "", device_tur }, 114 { "getcache", "", device_getcache }, 115 { "setcache", "none|r|w|rw [save]", device_setcache }, 116 { "flushcache", "", device_flushcache }, 117 { "setspeed", "[speed]", device_setspeed }, 118 { "getrealloc", "", device_getrealloc }, 119 { "setrealloc", "none|r|w|rw [save]", device_setrealloc }, 120 { "reportluns", "normal|wellknown|all|#", device_reportluns }, 121 { NULL, NULL, NULL }, 122 }; 123 124 static void bus_reset(int, char *[]); 125 static void bus_scan(int, char *[]); 126 static void bus_detach(int, char *[]); 127 128 static struct command bus_commands[] = { 129 { "reset", "", bus_reset }, 130 { "scan", "target lun", bus_scan }, 131 { "detach", "target lun", bus_detach }, 132 { NULL, NULL, NULL }, 133 }; 134 135 int 136 main(int argc, char *argv[]) 137 { 138 struct command *commands; 139 int i; 140 141 /* Must have at least: device command */ 142 if (argc < 3) 143 usage(); 144 145 /* Skip program name, get and skip device name and command. */ 146 dvname = argv[1]; 147 cmdname = argv[2]; 148 argv += 3; 149 argc -= 3; 150 151 /* 152 * Open the device and determine if it's a scsibus or an actual 153 * device. Devices respond to the SCIOCIDENTIFY ioctl. 154 */ 155 fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0); 156 if (fd == -1) { 157 if (errno == ENOENT) { 158 /* 159 * Device doesn't exist. Probably trying to open 160 * a device which doesn't use disk semantics for 161 * device name. Try again, specifying "cooked", 162 * which leaves off the "r" in front of the device's 163 * name. 164 */ 165 fd = opendisk(dvname, O_RDWR, dvname_store, 166 sizeof(dvname_store), 1); 167 if (fd == -1) 168 err(1, "%s", dvname); 169 } else 170 err(1, "%s", dvname); 171 } 172 173 /* 174 * Point the dvname at the actual device name that opendisk() opened. 175 */ 176 dvname = dvname_store; 177 178 if (ioctl(fd, SCIOCIDENTIFY, &dvaddr) < 0) 179 commands = bus_commands; 180 else 181 commands = device_commands; 182 183 /* Look up and call the command. */ 184 for (i = 0; commands[i].cmd_name != NULL; i++) 185 if (strcmp(cmdname, commands[i].cmd_name) == 0) 186 break; 187 if (commands[i].cmd_name == NULL) 188 errx(1, "unknown %s command: %s", 189 commands == bus_commands ? "bus" : "device", cmdname); 190 191 (*commands[i].cmd_func)(argc, argv); 192 exit(0); 193 } 194 195 static void 196 usage(void) 197 { 198 int i; 199 200 fprintf(stderr, "usage: %s device command [arg [...]]\n", 201 getprogname()); 202 203 fprintf(stderr, " Commands pertaining to scsi devices:\n"); 204 for (i=0; device_commands[i].cmd_name != NULL; i++) 205 fprintf(stderr, "\t%s %s\n", device_commands[i].cmd_name, 206 device_commands[i].arg_names); 207 fprintf(stderr, " Commands pertaining to scsi busses:\n"); 208 for (i=0; bus_commands[i].cmd_name != NULL; i++) 209 fprintf(stderr, "\t%s %s\n", bus_commands[i].cmd_name, 210 bus_commands[i].arg_names); 211 fprintf(stderr, " Use `any' or `all' to wildcard target or lun\n"); 212 213 exit(1); 214 } 215 216 /* 217 * DEVICE COMMANDS 218 */ 219 220 /* 221 * device_read_defect: 222 * 223 * Read primary and/or growth defect list in physical or block 224 * format from a direct access device. 225 * 226 * XXX Does not handle very large defect lists. Needs SCSI3 12 227 * byte READ DEFECT DATA command. 228 */ 229 230 static void print_bf_dd(union scsi_defect_descriptor *); 231 static void print_bfif_dd(union scsi_defect_descriptor *); 232 static void print_psf_dd(union scsi_defect_descriptor *); 233 234 static void 235 device_defects(int argc, char *argv[]) 236 { 237 struct scsi_read_defect_data cmd; 238 struct scsi_read_defect_data_data *data; 239 size_t dlen; 240 int i, dlfmt = -1; 241 int defects; 242 char msg[256]; 243 void (*pfunc)(union scsi_defect_descriptor *); 244 #define RDD_P_G_MASK 0x18 245 #define RDD_DLF_MASK 0x7 246 247 dlen = USHRT_MAX; /* XXX - this may not be enough room 248 * for all of the defects. 249 */ 250 data = malloc(dlen); 251 if (data == NULL) 252 errx(1, "unable to allocate defect list"); 253 memset(data, 0, dlen); 254 memset(&cmd, 0, sizeof(cmd)); 255 defects = 0; 256 pfunc = NULL; 257 258 /* determine which defect list(s) to read. */ 259 for (i = 0; i < argc; i++) { 260 if (strncmp("primary", argv[i], 7) == 0) { 261 cmd.flags |= RDD_PRIMARY; 262 continue; 263 } 264 if (strncmp("grown", argv[i], 5) == 0) { 265 cmd.flags |= RDD_GROWN; 266 continue; 267 } 268 break; 269 } 270 271 /* no defect list sepecified, assume both. */ 272 if ((cmd.flags & (RDD_PRIMARY|RDD_GROWN)) == 0) 273 cmd.flags |= (RDD_PRIMARY|RDD_GROWN); 274 275 /* list format option. */ 276 if (i < argc) { 277 if (strncmp("block", argv[i], 5) == 0) { 278 cmd.flags |= RDD_BF; 279 dlfmt = RDD_BF; 280 } 281 else if (strncmp("byte", argv[i], 4) == 0) { 282 cmd.flags |= RDD_BFIF; 283 dlfmt = RDD_BFIF; 284 } 285 else if (strncmp("physical", argv[i], 4) == 0) { 286 cmd.flags |= RDD_PSF; 287 dlfmt = RDD_PSF; 288 } 289 else { 290 usage(); 291 } 292 } 293 294 /* 295 * no list format specified; since block format not 296 * recommended use physical sector format as default. 297 */ 298 if (dlfmt < 0) { 299 cmd.flags |= RDD_PSF; 300 dlfmt = RDD_PSF; 301 } 302 303 cmd.opcode = SCSI_READ_DEFECT_DATA; 304 _lto2b(dlen, &cmd.length[0]); 305 306 scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_READ); 307 308 msg[0] = '\0'; 309 310 /* is the defect list in the format asked for? */ 311 if ((data->flags & RDD_DLF_MASK) != dlfmt) { 312 strcpy(msg, "\n\tnotice:" 313 "requested defect list format not supported by device\n\n"); 314 dlfmt = (data->flags & RDD_DLF_MASK); 315 } 316 317 if (data->flags & RDD_PRIMARY) 318 strcat(msg, "primary"); 319 320 if (data->flags & RDD_GROWN) { 321 if (data->flags & RDD_PRIMARY) 322 strcat(msg, " and "); 323 strcat(msg, "grown"); 324 } 325 326 strcat(msg, " defects"); 327 328 if ((data->flags & RDD_P_G_MASK) == 0) 329 strcat(msg, ": none reported\n"); 330 331 332 printf("%s: scsibus%d target %d lun %d %s", 333 dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target, 334 dvaddr.addr.scsi.lun, msg); 335 336 /* device did not return either defect list. */ 337 if ((data->flags & RDD_P_G_MASK) == 0) 338 return; 339 340 switch (dlfmt) { 341 case RDD_BF: 342 defects = _2btol(data->length) / 343 sizeof(struct scsi_defect_descriptor_bf); 344 pfunc = print_bf_dd; 345 strcpy(msg, "block address\n" 346 "-------------\n"); 347 break; 348 case RDD_BFIF: 349 defects = _2btol(data->length) / 350 sizeof(struct scsi_defect_descriptor_bfif); 351 pfunc = print_bfif_dd; 352 strcpy(msg, " bytes from\n" 353 "cylinder head index\n" 354 "-------- ---- ----------\n"); 355 break; 356 case RDD_PSF: 357 defects = _2btol(data->length) / 358 sizeof(struct scsi_defect_descriptor_psf); 359 pfunc = print_psf_dd; 360 strcpy(msg, "cylinder head sector\n" 361 "-------- ---- ----------\n"); 362 break; 363 } 364 365 /* device did not return any defects. */ 366 if (defects == 0) { 367 printf(": none\n"); 368 return; 369 } 370 371 printf(": %d\n", defects); 372 373 /* print heading. */ 374 printf("%s", msg); 375 376 /* print defect list. */ 377 for (i = 0 ; i < defects; i++) { 378 pfunc(&data->defect_descriptor[i]); 379 } 380 381 free(data); 382 return; 383 } 384 385 /* 386 * print_bf_dd: 387 * 388 * Print a block format defect descriptor. 389 */ 390 static void 391 print_bf_dd(union scsi_defect_descriptor *dd) 392 { 393 u_int32_t block; 394 395 block = _4btol(dd->bf.block_address); 396 397 printf("%13u\n", block); 398 } 399 400 #define DEFECTIVE_TRACK 0xffffffff 401 402 /* 403 * print_bfif_dd: 404 * 405 * Print a bytes from index format defect descriptor. 406 */ 407 static void 408 print_bfif_dd(union scsi_defect_descriptor *dd) 409 { 410 u_int32_t cylinder; 411 u_int32_t head; 412 u_int32_t bytes_from_index; 413 414 cylinder = _3btol(dd->bfif.cylinder); 415 head = dd->bfif.head; 416 bytes_from_index = _4btol(dd->bfif.bytes_from_index); 417 418 printf("%8u %4u ", cylinder, head); 419 420 if (bytes_from_index == DEFECTIVE_TRACK) 421 printf("entire track defective\n"); 422 else 423 printf("%10u\n", bytes_from_index); 424 } 425 426 /* 427 * print_psf_dd: 428 * 429 * Print a physical sector format defect descriptor. 430 */ 431 static void 432 print_psf_dd(union scsi_defect_descriptor *dd) 433 { 434 u_int32_t cylinder; 435 u_int32_t head; 436 u_int32_t sector; 437 438 cylinder = _3btol(dd->psf.cylinder); 439 head = dd->psf.head; 440 sector = _4btol(dd->psf.sector); 441 442 printf("%8u %4u ", cylinder, head); 443 444 if (sector == DEFECTIVE_TRACK) 445 printf("entire track defective\n"); 446 else 447 printf("%10u\n", sector); 448 } 449 450 /* 451 * device_format: 452 * 453 * Format a direct access device. 454 */ 455 static void 456 device_format(int argc, char *argv[]) 457 { 458 u_int32_t blksize; 459 int i, j, immediate; 460 #define PC (65536/10) 461 static int complete[] = { 462 PC*1, PC*2, PC*3, PC*4, PC*5, PC*6, PC*7, PC*8, PC*9, 65536 463 }; 464 char *cp, buffer[64]; 465 struct scsi_sense_data sense; 466 struct scsi_format_unit cmd; 467 struct { 468 struct scsi_format_unit_defect_list_header header; 469 /* optional initialization pattern */ 470 /* optional defect list */ 471 } dfl; 472 struct { 473 struct scsi_mode_parameter_header_6 header; 474 struct scsi_general_block_descriptor blk_desc; 475 struct page_disk_format format_page; 476 } mode_page; 477 struct { 478 struct scsi_mode_parameter_header_6 header; 479 struct scsi_general_block_descriptor blk_desc; 480 } data_select; 481 482 483 /* Blocksize is an optional argument. */ 484 if (argc > 2) 485 usage(); 486 487 /* 488 * Loop doing Request Sense to clear any pending Unit Attention. 489 * 490 * Multiple conditions may exist on the drive which are returned 491 * in priority order. 492 */ 493 for (i = 0; i < 8; i++) { 494 scsi_request_sense(fd, &sense, sizeof (sense)); 495 if ((j = SSD_SENSE_KEY(sense.flags)) == SKEY_NO_SENSE) 496 break; 497 } 498 /* 499 * Make sure we cleared any pending Unit Attention 500 */ 501 if (j != SKEY_NO_SENSE) { 502 cp = scsi_decode_sense((const unsigned char *) &sense, 2, 503 buffer, sizeof (buffer)); 504 errx(1, "failed to clean Unit Attention: %s", cp); 505 } 506 507 /* 508 * Get the DISK FORMAT mode page. SCSI-2 recommends specifying the 509 * interleave read from this page in the FORMAT UNIT command. 510 */ 511 scsi_mode_sense(fd, 0x03, 0x00, &mode_page, sizeof(mode_page)); 512 513 j = (mode_page.format_page.bytes_s[0] << 8) | 514 (mode_page.format_page.bytes_s[1]); 515 516 if (j != DEV_BSIZE) 517 printf("current disk sector size: %d\n", j); 518 519 memset(&cmd, 0, sizeof(cmd)); 520 521 cmd.opcode = SCSI_FORMAT_UNIT; 522 memcpy(cmd.interleave, mode_page.format_page.interleave, 523 sizeof(cmd.interleave)); 524 525 /* 526 * The blocksize on the device is only changed if the user 527 * specified a new blocksize. If not specified the blocksize 528 * used for the device will be the Default value in the device. 529 * We don't specify the number of blocks since the format 530 * command will always reformat the entire drive. Also by 531 * not specifying a block count the drive will reset the 532 * block count to the maximum available after the format 533 * completes if the blocksize was changed in the format. 534 * Finally, the new disk geometry will not but updated on 535 * the drive in permanent storage until _AFTER_ the format 536 * completes successfully. 537 */ 538 if (argc > 0) { 539 blksize = strtoul(argv[0], &cp, 10); 540 if (*cp != '\0') 541 errx(1, "invalid block size: %s", argv[0]); 542 543 memset(&data_select, 0, sizeof(data_select)); 544 545 data_select.header.blk_desc_len = 546 sizeof(struct scsi_general_block_descriptor); 547 /* 548 * blklen in desc is 3 bytes with a leading reserved byte 549 */ 550 _lto4b(blksize, &data_select.blk_desc.reserved); 551 552 /* 553 * Issue Mode Select to modify the device blocksize to be 554 * used on the Format. The modified device geometry will 555 * be stored as Current and Saved Page 3 parameters when 556 * the Format completes. 557 */ 558 scsi_mode_select(fd, 0, &data_select, sizeof(data_select)); 559 560 /* 561 * Since user specified a specific block size make sure it 562 * gets stored in the device when the format completes. 563 * 564 * Also scrub the defect list back to the manufacturers 565 * original. 566 */ 567 cmd.flags = SFU_CMPLST | SFU_FMTDATA; 568 } 569 570 memset(&dfl, 0, sizeof(dfl)); 571 572 if (argc > 1 && strncmp(argv[1], "imm", 3) == 0) { 573 /* 574 * Signal target for an immediate return from Format. 575 * 576 * We'll poll for completion status. 577 */ 578 dfl.header.flags = DLH_IMMED; 579 immediate = 1; 580 } else { 581 immediate = 0; 582 } 583 584 scsi_command(fd, &cmd, sizeof(cmd), &dfl, sizeof(dfl), 585 8 * 60 * 60 * 1000, SCCMD_WRITE); 586 587 /* 588 * Poll device for completion of Format 589 */ 590 if (immediate) { 591 i = 0; 592 printf("formatting."); 593 fflush(stdout); 594 do { 595 scsireq_t req; 596 struct scsi_test_unit_ready tcmd; 597 598 memset(&tcmd, 0, sizeof(tcmd)); 599 tcmd.opcode = SCSI_TEST_UNIT_READY; 600 601 memset(&req, 0, sizeof(req)); 602 memcpy(req.cmd, &tcmd, 6); 603 req.cmdlen = 6; 604 req.timeout = 10000; 605 req.senselen = SENSEBUFLEN; 606 607 if (ioctl(fd, SCIOCCOMMAND, &req) == -1) { 608 err(1, "SCIOCCOMMAND"); 609 } 610 611 if (req.retsts == SCCMD_OK) { 612 break; 613 } else if (req.retsts == SCCMD_TIMEOUT) { 614 fprintf(stderr, "%s: SCSI command timed out", 615 dvname); 616 break; 617 } else if (req.retsts == SCCMD_BUSY) { 618 fprintf(stderr, "%s: device is busy", 619 dvname); 620 break; 621 } else if (req.retsts != SCCMD_SENSE) { 622 fprintf(stderr, 623 "%s: device had unknown status %x", dvname, 624 req.retsts); 625 break; 626 } 627 memcpy(&sense, req.sense, sizeof(sense)); 628 if (sense.sks.sks_bytes[0] & SSD_SKSV) { 629 j = (sense.sks.sks_bytes[1] << 8) | 630 (sense.sks.sks_bytes[2]); 631 if (j >= complete[i]) { 632 printf(".%d0%%.", ++i); 633 fflush(stdout); 634 } 635 } 636 sleep(10); 637 } while (SSD_SENSE_KEY(sense.flags) == SKEY_NOT_READY); 638 printf(".100%%..done.\n"); 639 } 640 return; 641 } 642 643 static void 644 print_designator(const char *pre, struct scsipi_inquiry_evpd_device_id *did) 645 { 646 char buf[252 * 4 + 1]; 647 unsigned assoc, proto, code, type; 648 static const char *typestr[] = { 649 "vendor", 650 "t10", 651 "eui64", 652 "naa", 653 "target port", 654 "port group", 655 "lun group", 656 "md5", 657 "scsi", 658 "res9", 659 "res10", 660 "res11", 661 "res12", 662 "res13", 663 "res14", 664 "res15" 665 }; 666 static const char *assocstr[] = { 667 "lun", 668 "port", 669 "target", 670 "reserved" 671 }; 672 static const char *protostr[] = { 673 "fibre channel", 674 "obsolete", 675 "ssa", 676 "ieee1394", 677 "rdma", 678 "iSCSI", 679 "SAS" 680 }; 681 const unsigned maxproto = __arraycount(protostr) - 1; 682 const unsigned isbinary = 683 __SHIFTOUT(SINQ_DEVICE_ID_CODESET_BINARY, SINQ_DEVICE_ID_CODESET); 684 unsigned k; 685 686 assoc = __SHIFTOUT(did->flags, SINQ_DEVICE_ID_ASSOCIATION); 687 proto = __SHIFTOUT(did->pc, SINQ_DEVICE_ID_PROTOCOL); 688 code = __SHIFTOUT(did->pc, SINQ_DEVICE_ID_CODESET); 689 type = __SHIFTOUT(did->flags, SINQ_DEVICE_ID_TYPE); 690 691 printf("%s%s", pre, assocstr[assoc]); 692 if (did->flags & SINQ_DEVICE_ID_PIV) { 693 if (proto > maxproto) 694 printf(" proto%u", proto); 695 else 696 printf(" %s", protostr[proto]); 697 } 698 printf(" %s: ", typestr[type]); 699 700 if (code == isbinary) { 701 for (k=0; k<did->designator_length; ++k) { 702 printf("%02x", did->designator[k]); 703 } 704 printf("\n"); 705 } else { 706 scsi_strvis(buf, sizeof(buf), (char *)did->designator, 707 did->designator_length); 708 printf("%s\n", buf); 709 } 710 } 711 712 /* 713 * device_identify: 714 * 715 * Display the identity of the device, including its SCSI bus, 716 * target, lun, and its vendor/product/revision information. 717 * Optionally query and display vpd identification data. 718 */ 719 static void 720 device_identify(int argc, char *argv[]) 721 { 722 struct scsipi_inquiry_data inqbuf; 723 struct { 724 struct scsipi_inquiry_evpd_header h; 725 uint8_t d[255 - sizeof(struct scsipi_inquiry_evpd_header)]; 726 } evpdbuf; 727 struct scsipi_inquiry cmd; 728 unsigned len, rlen; 729 struct scsipi_inquiry_evpd_serial *ser; 730 struct scsipi_inquiry_evpd_device_id *did; 731 int has_serial; 732 int has_device_id; 733 bool getvpd = false; 734 int i; 735 736 /* x4 in case every character is escaped, +1 for NUL. */ 737 char vendor[(sizeof(inqbuf.vendor) * 4) + 1], 738 product[(sizeof(inqbuf.product) * 4) + 1], 739 revision[(sizeof(inqbuf.revision) * 4) + 1], 740 ident[252 * 4 + 1]; 741 742 /* Check optional arguments */ 743 for (i = 0; i < argc; i++) { 744 if (strncmp("vpd", argv[i], 3) == 0) { 745 getvpd = true; 746 continue; 747 } 748 usage(); 749 } 750 751 memset(&cmd, 0, sizeof(cmd)); 752 memset(&inqbuf, 0, sizeof(inqbuf)); 753 754 cmd.opcode = INQUIRY; 755 cmd.length = sizeof(inqbuf); 756 757 scsi_command(fd, &cmd, sizeof(cmd), &inqbuf, sizeof(inqbuf), 758 10000, SCCMD_READ); 759 760 scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor, 761 sizeof(inqbuf.vendor)); 762 scsi_strvis(product, sizeof(product), inqbuf.product, 763 sizeof(inqbuf.product)); 764 scsi_strvis(revision, sizeof(revision), inqbuf.revision, 765 sizeof(inqbuf.revision)); 766 767 printf("%s: scsibus%d target %d lun %d <%s, %s, %s>\n", 768 dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target, 769 dvaddr.addr.scsi.lun, vendor, product, revision); 770 771 if (!getvpd) 772 return; 773 774 cmd.byte2 |= SINQ_EVPD; 775 cmd.pagecode = SINQ_VPD_PAGES; 776 777 scsi_command(fd, &cmd, sizeof(cmd), &evpdbuf, sizeof(evpdbuf), 778 10000, SCCMD_READ); 779 780 len = be16dec(evpdbuf.h.length); 781 if (len > sizeof(evpdbuf.d)) 782 len = 0; 783 784 has_serial = memchr(evpdbuf.d, SINQ_VPD_SERIAL, len) != NULL; 785 has_device_id = memchr(evpdbuf.d, SINQ_VPD_DEVICE_ID, len) != NULL; 786 787 if (has_serial) { 788 789 cmd.byte2 |= SINQ_EVPD; 790 cmd.pagecode = SINQ_VPD_SERIAL; 791 792 scsi_command(fd, &cmd, sizeof(cmd), &evpdbuf, sizeof(evpdbuf), 793 10000, SCCMD_READ); 794 795 len = be16dec(evpdbuf.h.length); 796 if (len > sizeof(evpdbuf.d)) 797 len = 0; 798 799 ser = (struct scsipi_inquiry_evpd_serial *)&evpdbuf.d; 800 scsi_strvis(ident, sizeof(ident), (char *)ser->serial_number, len); 801 printf("VPD Serial:\n"); 802 printf("\t%s\n", ident); 803 804 } 805 806 if (has_device_id) { 807 808 cmd.byte2 |= SINQ_EVPD; 809 cmd.pagecode = SINQ_VPD_DEVICE_ID; 810 811 scsi_command(fd, &cmd, sizeof(cmd), &evpdbuf, sizeof(evpdbuf), 812 10000, SCCMD_READ); 813 814 len = be16dec(evpdbuf.h.length); 815 if (len > sizeof(evpdbuf.d)) 816 len = 0; 817 818 printf("VPD Device IDs:\n"); 819 820 for (unsigned off=0; off<len - sizeof(*did); off += rlen) { 821 did = (struct scsipi_inquiry_evpd_device_id *)&evpdbuf.d[off]; 822 rlen = sizeof(*did) + did->designator_length - 1; 823 if (off + rlen > len) 824 break; 825 826 print_designator("\t", did); 827 } 828 } 829 830 return; 831 } 832 833 /* 834 * device_reassign: 835 * 836 * Reassign bad blocks on a direct access device. 837 */ 838 static void 839 device_reassign(int argc, char *argv[]) 840 { 841 struct scsi_reassign_blocks cmd; 842 struct scsi_reassign_blocks_data *data; 843 size_t dlen; 844 u_int32_t blkno; 845 int i; 846 char *cp; 847 848 /* We get a list of block numbers. */ 849 if (argc < 1) 850 usage(); 851 852 /* 853 * Allocate the reassign blocks descriptor. The 4 comes from the 854 * size of the block address in the defect descriptor. 855 */ 856 dlen = sizeof(struct scsi_reassign_blocks_data) + ((argc - 1) * 4); 857 data = malloc(dlen); 858 if (data == NULL) 859 errx(1, "unable to allocate defect descriptor"); 860 memset(data, 0, dlen); 861 862 cmd.opcode = SCSI_REASSIGN_BLOCKS; 863 cmd.byte2 = 0; 864 cmd.unused[0] = 0; 865 cmd.unused[1] = 0; 866 cmd.unused[2] = 0; 867 cmd.control = 0; 868 869 /* Defect descriptor length. */ 870 _lto2b(argc * 4, data->length); 871 872 /* Build the defect descriptor list. */ 873 for (i = 0; i < argc; i++) { 874 blkno = strtoul(argv[i], &cp, 10); 875 if (*cp != '\0') 876 errx(1, "invalid block number: %s", argv[i]); 877 _lto4b(blkno, data->defect_descriptor[i].dlbaddr); 878 } 879 880 scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_WRITE); 881 882 free(data); 883 return; 884 } 885 886 /* 887 * device_release: 888 * 889 * Issue a RELEASE command to a SCSI device. 890 */ 891 #ifndef SCSI_RELEASE 892 #define SCSI_RELEASE 0x17 893 #endif 894 static void 895 device_release(int argc, char *argv[]) 896 { 897 struct scsi_test_unit_ready cmd; /* close enough */ 898 899 /* No arguments. */ 900 if (argc != 0) 901 usage(); 902 903 memset(&cmd, 0, sizeof(cmd)); 904 905 cmd.opcode = SCSI_RELEASE; 906 907 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 908 909 return; 910 } 911 912 913 914 /* 915 * device_reserve: 916 * 917 * Issue a RESERVE command to a SCSI device. 918 */ 919 #ifndef SCSI_RESERVE 920 #define SCSI_RESERVE 0x16 921 #endif 922 static void 923 device_reserve(int argc, char *argv[]) 924 { 925 struct scsi_test_unit_ready cmd; /* close enough */ 926 927 /* No arguments. */ 928 if (argc != 0) 929 usage(); 930 931 memset(&cmd, 0, sizeof(cmd)); 932 933 cmd.opcode = SCSI_RESERVE; 934 935 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 936 937 return; 938 } 939 940 /* 941 * device_reset: 942 * 943 * Issue a reset to a SCSI device. 944 */ 945 static void 946 device_reset(int argc, char *argv[]) 947 { 948 949 /* No arguments. */ 950 if (argc != 0) 951 usage(); 952 953 if (ioctl(fd, SCIOCRESET, NULL) != 0) 954 err(1, "SCIOCRESET"); 955 956 return; 957 } 958 959 /* 960 * device_debug: 961 * 962 * Set debug level to a SCSI device. 963 * scsipi will print anything iff SCSIPI_DEBUG set in config. 964 */ 965 static void 966 device_debug(int argc, char *argv[]) 967 { 968 int lvl; 969 970 if (argc < 1) 971 usage(); 972 973 lvl = atoi(argv[0]); 974 975 if (ioctl(fd, SCIOCDEBUG, &lvl) != 0) 976 err(1, "SCIOCDEBUG"); 977 978 return; 979 } 980 981 /* 982 * device_getcache: 983 * 984 * Get the caching parameters for a SCSI disk. 985 */ 986 static void 987 device_getcache(int argc, char *argv[]) 988 { 989 struct { 990 struct scsi_mode_parameter_header_6 header; 991 struct scsi_general_block_descriptor blk_desc; 992 struct page_caching caching_params; 993 } data; 994 995 /* No arguments. */ 996 if (argc != 0) 997 usage(); 998 999 scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data)); 1000 1001 if ((data.caching_params.flags & (CACHING_RCD|CACHING_WCE)) == 1002 CACHING_RCD) 1003 printf("%s: no caches enabled\n", dvname); 1004 else { 1005 printf("%s: read cache %senabled\n", dvname, 1006 (data.caching_params.flags & CACHING_RCD) ? "not " : ""); 1007 printf("%s: write-back cache %senabled\n", dvname, 1008 (data.caching_params.flags & CACHING_WCE) ? "" : "not "); 1009 } 1010 printf("%s: caching parameters are %ssavable\n", dvname, 1011 (data.caching_params.pg_code & PGCODE_PS) ? "" : "not "); 1012 } 1013 1014 /* 1015 * device_setcache: 1016 * 1017 * Set cache enables for a SCSI disk. 1018 */ 1019 static void 1020 device_setcache(int argc, char *argv[]) 1021 { 1022 struct { 1023 struct scsi_mode_parameter_header_6 header; 1024 struct scsi_general_block_descriptor blk_desc; 1025 struct page_caching caching_params; 1026 } data; 1027 int dlen; 1028 u_int8_t flags, byte2; 1029 1030 if (argc > 2 || argc == 0) 1031 usage(); 1032 1033 flags = 0; 1034 byte2 = 0; 1035 if (strcmp(argv[0], "none") == 0) 1036 flags = CACHING_RCD; 1037 else if (strcmp(argv[0], "r") == 0) 1038 flags = 0; 1039 else if (strcmp(argv[0], "w") == 0) 1040 flags = CACHING_RCD|CACHING_WCE; 1041 else if (strcmp(argv[0], "rw") == 0) 1042 flags = CACHING_WCE; 1043 else 1044 usage(); 1045 1046 if (argc == 2) { 1047 if (strcmp(argv[1], "save") == 0) 1048 byte2 = SMS_SP; 1049 else 1050 usage(); 1051 } 1052 1053 scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data)); 1054 1055 data.caching_params.pg_code &= PGCODE_MASK; 1056 data.caching_params.flags = 1057 (data.caching_params.flags & ~(CACHING_RCD|CACHING_WCE)) | flags; 1058 1059 data.caching_params.cache_segment_size[0] = 0; 1060 data.caching_params.cache_segment_size[1] = 0; 1061 1062 data.header.data_length = 0; 1063 1064 dlen = sizeof(data.header) + sizeof(data.blk_desc) + 2 + 1065 data.caching_params.pg_length; 1066 1067 scsi_mode_select(fd, byte2, &data, dlen); 1068 } 1069 1070 /* 1071 * device_flushcache: 1072 * 1073 * Issue a FLUSH CACHE command to a SCSI device. 1074 */ 1075 #ifndef SCSI_FLUSHCACHE 1076 #define SCSI_FLUSHCACHE 0x35 1077 #endif 1078 static void 1079 device_flushcache(int argc, char *argv[]) 1080 { 1081 struct scsi_test_unit_ready cmd; /* close enough */ 1082 1083 /* No arguments. */ 1084 if (argc != 0) 1085 usage(); 1086 1087 memset(&cmd, 0, sizeof(cmd)); 1088 1089 cmd.opcode = SCSI_FLUSHCACHE; 1090 1091 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 1092 1093 return; 1094 } 1095 1096 /* 1097 * device_setspeed: 1098 * 1099 * Set rotation speed to a CD/DVD drive. 1100 */ 1101 static void 1102 device_setspeed(int argc, char *argv[]) 1103 { 1104 u_char cmd[11]; 1105 u_char pd[28]; 1106 u_int32_t speed; 1107 1108 if (argc != 1) 1109 usage(); 1110 1111 speed = atoi(argv[0]) * 177; 1112 1113 memset(&pd, 0, sizeof(pd)); 1114 if (speed == 0) 1115 pd[0] = 4; /* restore drive defaults */ 1116 pd[8] = 0xff; 1117 pd[9] = 0xff; 1118 pd[10] = 0xff; 1119 pd[11] = 0xff; 1120 pd[12] = pd[20] = (speed >> 24) & 0xff; 1121 pd[13] = pd[21] = (speed >> 16) & 0xff; 1122 pd[14] = pd[22] = (speed >> 8) & 0xff; 1123 pd[15] = pd[23] = speed & 0xff; 1124 pd[18] = pd[26] = 1000 >> 8; 1125 pd[19] = pd[27] = 1000 & 0xff; 1126 1127 memset(&cmd, 0, sizeof(cmd)); 1128 cmd[0] = 0xb6; 1129 cmd[10] = sizeof(pd); 1130 1131 scsi_command(fd, &cmd, sizeof(cmd), pd, sizeof(pd), 10000, SCCMD_WRITE); 1132 1133 return; 1134 } 1135 1136 /* 1137 * device_reportluns: 1138 * 1139 * Report the known LUNs to which the initiator can send commands 1140 */ 1141 static void 1142 device_reportluns(int argc, char *argv[]) 1143 { 1144 struct scsi_report_luns cmd; 1145 struct { 1146 struct scsi_report_luns_header header; 1147 struct scsi_report_luns_lun desc[1]; 1148 } *data; 1149 u_int32_t dlen, len; 1150 u_int64_t lun; 1151 size_t count, idx; 1152 unsigned long sel; 1153 char *endp; 1154 int i; 1155 1156 dlen = USHRT_MAX; /* good for > 8000 LUNs */ 1157 data = malloc(dlen); 1158 if (data == NULL) 1159 errx(1, "unable to allocate lun report"); 1160 1161 memset(&cmd, 0, sizeof(cmd)); 1162 cmd.opcode = SCSI_REPORT_LUNS; 1163 cmd.selectreport = SELECTREPORT_NORMAL; 1164 1165 /* determine which report to read. */ 1166 for (i = 0; i < argc; i++) { 1167 if (strcmp("normal", argv[i]) == 0) { 1168 cmd.selectreport = SELECTREPORT_NORMAL; 1169 continue; 1170 } 1171 if (strcmp("wellknown", argv[i]) == 0) { 1172 cmd.selectreport = SELECTREPORT_WELLKNOWN; 1173 continue; 1174 } 1175 if (strcmp("all", argv[i]) == 0) { 1176 cmd.selectreport = SELECTREPORT_ALL; 1177 continue; 1178 } 1179 sel = strtoul(argv[i], &endp, 0); 1180 if (*endp != '\0' || sel > 255) 1181 errx(1, "Unknown select report '%s'", argv[i]); 1182 cmd.selectreport = sel; 1183 } 1184 1185 _lto4b(dlen, &cmd.alloclen[0]); 1186 cmd.control = 0x00; 1187 1188 scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_READ); 1189 1190 len = _4btol(data->header.length); 1191 if (len > dlen) { 1192 /* XXX reallocate and retry */ 1193 printf("%s: report truncated %" PRIu32 "to %" PRIu32 "\n", 1194 dvname, len, dlen); 1195 len = dlen; 1196 } 1197 1198 count = len / sizeof(data->desc[0]); 1199 1200 for (idx=0; idx<count; ++idx) { 1201 lun = _8btol(data->desc[idx].lun); 1202 1203 /* 1204 * swizzle bits so that LUNs 0..255 are 1205 * mapped to numbers 0..255 1206 */ 1207 lun = (lun & 0xffff000000000000ull) >> 48 1208 | (lun & 0x0000ffff00000000ull) >> 16 1209 | (lun & 0x00000000ffff0000ull) << 16 1210 | (lun & 0x000000000000ffffull) << 48; 1211 1212 printf("%s: lun %" PRIu64 "\n", dvname, lun); 1213 } 1214 1215 free(data); 1216 } 1217 1218 /* 1219 * device_getrealloc: 1220 * 1221 * Get the automatic reallocation parameters for a SCSI disk. 1222 */ 1223 static void 1224 device_getrealloc(int argc, char *argv[]) 1225 { 1226 struct { 1227 struct scsi_mode_parameter_header_6 header; 1228 struct scsi_general_block_descriptor blk_desc; 1229 struct page_err_recov err_recov_params; 1230 } data; 1231 u_int8_t flags; 1232 1233 /* No arguments. */ 1234 if (argc != 0) 1235 usage(); 1236 1237 scsi_mode_sense(fd, 0x01, 0x00, &data, sizeof(data)); 1238 1239 flags = data.err_recov_params.flags; 1240 if ((flags & (ERR_RECOV_ARRE | ERR_RECOV_AWRE)) == 0) 1241 printf("%s: no automatic reallocation enabled\n", dvname); 1242 else { 1243 printf("%s: automatic read reallocation %senabled\n", dvname, 1244 (flags & ERR_RECOV_ARRE) ? "" : "not "); 1245 printf("%s: automatic write reallocation %senabled\n", dvname, 1246 (flags & ERR_RECOV_AWRE) ? "" : "not "); 1247 } 1248 printf("%s: error recovery parameters are %ssavable\n", dvname, 1249 (data.err_recov_params.pg_code & PGCODE_PS) ? "" : "not "); 1250 } 1251 1252 /* 1253 * device_setrealloc: 1254 * 1255 * Set the automatic reallocation parameters for a SCSI disk. 1256 */ 1257 static void 1258 device_setrealloc(int argc, char *argv[]) 1259 { 1260 struct { 1261 struct scsi_mode_parameter_header_6 header; 1262 struct scsi_general_block_descriptor blk_desc; 1263 struct page_err_recov err_recov_params; 1264 } data; 1265 int dlen; 1266 u_int8_t flags, byte2; 1267 1268 if (argc > 2 || argc == 0) 1269 usage(); 1270 1271 flags = 0; 1272 byte2 = 0; 1273 if (strcmp(argv[0], "none") == 0) 1274 flags = 0; 1275 else if (strcmp(argv[0], "r") == 0) 1276 flags = ERR_RECOV_ARRE; 1277 else if (strcmp(argv[0], "w") == 0) 1278 flags = ERR_RECOV_AWRE; 1279 else if (strcmp(argv[0], "rw") == 0) 1280 flags = ERR_RECOV_ARRE | ERR_RECOV_AWRE; 1281 else 1282 usage(); 1283 1284 if (argc == 2) { 1285 if (strcmp(argv[1], "save") == 0) 1286 byte2 = SMS_SP; 1287 else 1288 usage(); 1289 } 1290 1291 scsi_mode_sense(fd, 0x01, 0x00, &data, sizeof(data)); 1292 1293 data.err_recov_params.pg_code &= PGCODE_MASK; 1294 data.err_recov_params.flags &= ~(ERR_RECOV_ARRE | ERR_RECOV_AWRE); 1295 data.err_recov_params.flags |= flags; 1296 1297 data.header.data_length = 0; 1298 1299 dlen = sizeof(data.header) + sizeof(data.blk_desc) + 2 + 1300 data.err_recov_params.pg_length; 1301 1302 scsi_mode_select(fd, byte2, &data, dlen); 1303 } 1304 1305 /* 1306 * device_prevent: 1307 * 1308 * Issue a prevent to a SCSI device. 1309 */ 1310 static void 1311 device_prevent(int argc, char *argv[]) 1312 { 1313 struct scsi_prevent_allow_medium_removal cmd; 1314 1315 /* No arguments. */ 1316 if (argc != 0) 1317 usage(); 1318 1319 memset(&cmd, 0, sizeof(cmd)); 1320 1321 cmd.opcode = SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL; 1322 cmd.how = SPAMR_PREVENT_DT; /* XXX SMAMR_PREVENT_ALL? */ 1323 1324 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 1325 1326 return; 1327 } 1328 1329 /* 1330 * device_allow: 1331 * 1332 * Issue a stop to a SCSI device. 1333 */ 1334 static void 1335 device_allow(int argc, char *argv[]) 1336 { 1337 struct scsi_prevent_allow_medium_removal cmd; 1338 1339 /* No arguments. */ 1340 if (argc != 0) 1341 usage(); 1342 1343 memset(&cmd, 0, sizeof(cmd)); 1344 1345 cmd.opcode = SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL; 1346 cmd.how = SPAMR_ALLOW; 1347 1348 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 1349 1350 return; 1351 } 1352 1353 /* 1354 * device_start: 1355 * 1356 * Issue a start to a SCSI device. 1357 */ 1358 static void 1359 device_start(int argc, char *argv[]) 1360 { 1361 struct scsipi_start_stop cmd; 1362 1363 /* No arguments. */ 1364 if (argc != 0) 1365 usage(); 1366 1367 memset(&cmd, 0, sizeof(cmd)); 1368 1369 cmd.opcode = START_STOP; 1370 cmd.how = SSS_START; 1371 1372 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 30000, 0); 1373 1374 return; 1375 } 1376 1377 /* 1378 * device_stop: 1379 * 1380 * Issue a stop to a SCSI device. 1381 */ 1382 static void 1383 device_stop(int argc, char *argv[]) 1384 { 1385 struct scsipi_start_stop cmd; 1386 1387 /* No arguments. */ 1388 if (argc != 0) 1389 usage(); 1390 1391 memset(&cmd, 0, sizeof(cmd)); 1392 1393 cmd.opcode = START_STOP; 1394 cmd.how = SSS_STOP; 1395 1396 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 30000, 0); 1397 1398 return; 1399 } 1400 1401 /* 1402 * device_tur: 1403 * 1404 * Issue a TEST UNIT READY to a SCSI device. 1405 */ 1406 static void 1407 device_tur(int argc, char *argv[]) 1408 { 1409 struct scsi_test_unit_ready cmd; 1410 1411 /* No arguments. */ 1412 if (argc != 0) 1413 usage(); 1414 1415 memset(&cmd, 0, sizeof(cmd)); 1416 1417 cmd.opcode = SCSI_TEST_UNIT_READY; 1418 1419 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 1420 1421 return; 1422 } 1423 1424 /* 1425 * BUS COMMANDS 1426 */ 1427 1428 /* 1429 * bus_reset: 1430 * 1431 * Issue a reset to a SCSI bus. 1432 */ 1433 static void 1434 bus_reset(int argc, char *argv[]) 1435 { 1436 1437 /* No arguments. */ 1438 if (argc != 0) 1439 usage(); 1440 1441 if (ioctl(fd, SCBUSIORESET, NULL) != 0) 1442 err(1, "SCBUSIORESET"); 1443 1444 return; 1445 } 1446 1447 /* 1448 * bus_scan: 1449 * 1450 * Rescan a SCSI bus for new devices. 1451 */ 1452 static void 1453 bus_scan(int argc, char *argv[]) 1454 { 1455 struct scbusioscan_args args; 1456 char *cp; 1457 1458 /* Must have two args: target lun */ 1459 if (argc != 2) 1460 usage(); 1461 1462 if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0) 1463 args.sa_target = -1; 1464 else { 1465 args.sa_target = strtol(argv[0], &cp, 10); 1466 if (*cp != '\0' || args.sa_target < 0) 1467 errx(1, "invalid target: %s", argv[0]); 1468 } 1469 1470 if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0) 1471 args.sa_lun = -1; 1472 else { 1473 args.sa_lun = strtol(argv[1], &cp, 10); 1474 if (*cp != '\0' || args.sa_lun < 0) 1475 errx(1, "invalid lun: %s", argv[1]); 1476 } 1477 1478 if (ioctl(fd, SCBUSIOSCAN, &args) != 0) 1479 err(1, "SCBUSIOSCAN"); 1480 1481 return; 1482 } 1483 1484 /* 1485 * bus_detach: 1486 * 1487 * detach SCSI devices from a bus. 1488 */ 1489 static void 1490 bus_detach(int argc, char *argv[]) 1491 { 1492 struct scbusiodetach_args args; 1493 char *cp; 1494 1495 /* Must have two args: target lun */ 1496 if (argc != 2) 1497 usage(); 1498 1499 if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0) 1500 args.sa_target = -1; 1501 else { 1502 args.sa_target = strtol(argv[0], &cp, 10); 1503 if (*cp != '\0' || args.sa_target < 0) 1504 errx(1, "invalid target: %s", argv[0]); 1505 } 1506 1507 if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0) 1508 args.sa_lun = -1; 1509 else { 1510 args.sa_lun = strtol(argv[1], &cp, 10); 1511 if (*cp != '\0' || args.sa_lun < 0) 1512 errx(1, "invalid lun: %s", argv[1]); 1513 } 1514 1515 if (ioctl(fd, SCBUSIODETACH, &args) != 0) 1516 err(1, "SCBUSIODETACH"); 1517 1518 return; 1519 } 1520