1 /* $NetBSD: scsictl.c,v 1.44 2025/12/26 09:50:08 nia 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.44 2025/12/26 09:50:08 nia Exp $"); 40 #endif 41 42 #include <sys/param.h> 43 #include <sys/ioctl.h> 44 #include <sys/scsiio.h> 45 #include <err.h> 46 #include <errno.h> 47 #include <endian.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 specified, 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 printf("%s: scsibus%d target %d lun %d %s", 332 dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target, 333 dvaddr.addr.scsi.lun, msg); 334 335 /* device did not return either defect list. */ 336 if ((data->flags & RDD_P_G_MASK) == 0) 337 return; 338 339 switch (dlfmt) { 340 case RDD_BF: 341 defects = _2btol(data->length) / 342 sizeof(struct scsi_defect_descriptor_bf); 343 pfunc = print_bf_dd; 344 strcpy(msg, "block address\n" 345 "-------------\n"); 346 break; 347 case RDD_BFIF: 348 defects = _2btol(data->length) / 349 sizeof(struct scsi_defect_descriptor_bfif); 350 pfunc = print_bfif_dd; 351 strcpy(msg, " bytes from\n" 352 "cylinder head index\n" 353 "-------- ---- ----------\n"); 354 break; 355 case RDD_PSF: 356 defects = _2btol(data->length) / 357 sizeof(struct scsi_defect_descriptor_psf); 358 pfunc = print_psf_dd; 359 strcpy(msg, "cylinder head sector\n" 360 "-------- ---- ----------\n"); 361 break; 362 } 363 364 /* device did not return any defects. */ 365 if (defects == 0) { 366 printf(": none\n"); 367 return; 368 } 369 370 printf(": %d\n", defects); 371 372 /* print heading. */ 373 printf("%s", msg); 374 375 /* print defect list. */ 376 for (i = 0 ; i < defects; i++) { 377 pfunc(&data->defect_descriptor[i]); 378 } 379 380 free(data); 381 return; 382 } 383 384 /* 385 * print_bf_dd: 386 * 387 * Print a block format defect descriptor. 388 */ 389 static void 390 print_bf_dd(union scsi_defect_descriptor *dd) 391 { 392 u_int32_t block; 393 394 block = _4btol(dd->bf.block_address); 395 396 printf("%13u\n", block); 397 } 398 399 #define DEFECTIVE_TRACK 0xffffffff 400 401 /* 402 * print_bfif_dd: 403 * 404 * Print a bytes from index format defect descriptor. 405 */ 406 static void 407 print_bfif_dd(union scsi_defect_descriptor *dd) 408 { 409 u_int32_t cylinder; 410 u_int32_t head; 411 u_int32_t bytes_from_index; 412 413 cylinder = _3btol(dd->bfif.cylinder); 414 head = dd->bfif.head; 415 bytes_from_index = _4btol(dd->bfif.bytes_from_index); 416 417 printf("%8u %4u ", cylinder, head); 418 419 if (bytes_from_index == DEFECTIVE_TRACK) 420 printf("entire track defective\n"); 421 else 422 printf("%10u\n", bytes_from_index); 423 } 424 425 /* 426 * print_psf_dd: 427 * 428 * Print a physical sector format defect descriptor. 429 */ 430 static void 431 print_psf_dd(union scsi_defect_descriptor *dd) 432 { 433 u_int32_t cylinder; 434 u_int32_t head; 435 u_int32_t sector; 436 437 cylinder = _3btol(dd->psf.cylinder); 438 head = dd->psf.head; 439 sector = _4btol(dd->psf.sector); 440 441 printf("%8u %4u ", cylinder, head); 442 443 if (sector == DEFECTIVE_TRACK) 444 printf("entire track defective\n"); 445 else 446 printf("%10u\n", sector); 447 } 448 449 /* 450 * device_format: 451 * 452 * Format a direct access device. 453 */ 454 static void 455 device_format(int argc, char *argv[]) 456 { 457 u_int32_t blksize; 458 int i, j, immediate; 459 #define PC (65536/10) 460 static int complete[] = { 461 PC*1, PC*2, PC*3, PC*4, PC*5, PC*6, PC*7, PC*8, PC*9, 65536 462 }; 463 char *cp, buffer[64]; 464 struct scsi_sense_data sense; 465 struct scsi_format_unit cmd; 466 struct { 467 struct scsi_format_unit_defect_list_header header; 468 /* optional initialization pattern */ 469 /* optional defect list */ 470 } dfl; 471 struct { 472 struct scsi_mode_parameter_header_6 header; 473 struct scsi_general_block_descriptor blk_desc; 474 struct page_disk_format format_page; 475 } mode_page; 476 struct { 477 struct scsi_mode_parameter_header_6 header; 478 struct scsi_general_block_descriptor blk_desc; 479 } data_select; 480 481 /* Blocksize is an optional argument. */ 482 if (argc > 2) 483 usage(); 484 485 /* 486 * Loop doing Request Sense to clear any pending Unit Attention. 487 * 488 * Multiple conditions may exist on the drive which are returned 489 * in priority order. 490 */ 491 for (i = 0; i < 8; i++) { 492 scsi_request_sense(fd, &sense, sizeof (sense)); 493 if ((j = SSD_SENSE_KEY(sense.flags)) == SKEY_NO_SENSE) 494 break; 495 } 496 /* 497 * Make sure we cleared any pending Unit Attention 498 */ 499 if (j != SKEY_NO_SENSE) { 500 cp = scsi_decode_sense((const unsigned char *) &sense, 2, 501 buffer, sizeof (buffer)); 502 errx(1, "failed to clean Unit Attention: %s", cp); 503 } 504 505 /* 506 * Get the DISK FORMAT mode page. SCSI-2 recommends specifying the 507 * interleave read from this page in the FORMAT UNIT command. 508 */ 509 scsi_mode_sense(fd, 0x03, 0x00, &mode_page, sizeof(mode_page)); 510 511 j = (mode_page.format_page.bytes_s[0] << 8) | 512 (mode_page.format_page.bytes_s[1]); 513 514 if (j != DEV_BSIZE) 515 printf("current disk sector size: %d\n", j); 516 517 memset(&cmd, 0, sizeof(cmd)); 518 519 cmd.opcode = SCSI_FORMAT_UNIT; 520 memcpy(cmd.interleave, mode_page.format_page.interleave, 521 sizeof(cmd.interleave)); 522 523 /* 524 * The blocksize on the device is only changed if the user 525 * specified a new blocksize. If not specified the blocksize 526 * used for the device will be the Default value in the device. 527 * We don't specify the number of blocks since the format 528 * command will always reformat the entire drive. Also by 529 * not specifying a block count the drive will reset the 530 * block count to the maximum available after the format 531 * completes if the blocksize was changed in the format. 532 * Finally, the new disk geometry will not but updated on 533 * the drive in permanent storage until _AFTER_ the format 534 * completes successfully. 535 */ 536 if (argc > 0) { 537 blksize = strtoul(argv[0], &cp, 10); 538 if (*cp != '\0') 539 errx(1, "invalid block size: %s", argv[0]); 540 541 memset(&data_select, 0, sizeof(data_select)); 542 543 data_select.header.blk_desc_len = 544 sizeof(struct scsi_general_block_descriptor); 545 /* 546 * blklen in desc is 3 bytes with a leading reserved byte 547 */ 548 _lto4b(blksize, &data_select.blk_desc.reserved); 549 550 /* 551 * Issue Mode Select to modify the device blocksize to be 552 * used on the Format. The modified device geometry will 553 * be stored as Current and Saved Page 3 parameters when 554 * the Format completes. 555 */ 556 scsi_mode_select(fd, 0, &data_select, sizeof(data_select)); 557 558 /* 559 * Since user specified a specific block size make sure it 560 * gets stored in the device when the format completes. 561 * 562 * Also scrub the defect list back to the manufacturers 563 * original. 564 */ 565 cmd.flags = SFU_CMPLST | SFU_FMTDATA; 566 } 567 568 memset(&dfl, 0, sizeof(dfl)); 569 570 if (argc > 1 && strncmp(argv[1], "imm", 3) == 0) { 571 /* 572 * Signal target for an immediate return from Format. 573 * 574 * We'll poll for completion status. 575 */ 576 dfl.header.flags = DLH_IMMED; 577 immediate = 1; 578 } else { 579 immediate = 0; 580 } 581 582 scsi_command(fd, &cmd, sizeof(cmd), &dfl, sizeof(dfl), 583 8 * 60 * 60 * 1000, SCCMD_WRITE); 584 585 /* 586 * Poll device for completion of Format 587 */ 588 if (immediate) { 589 i = 0; 590 printf("formatting."); 591 fflush(stdout); 592 do { 593 scsireq_t req; 594 struct scsi_test_unit_ready tcmd; 595 596 memset(&tcmd, 0, sizeof(tcmd)); 597 tcmd.opcode = SCSI_TEST_UNIT_READY; 598 599 memset(&req, 0, sizeof(req)); 600 memcpy(req.cmd, &tcmd, 6); 601 req.cmdlen = 6; 602 req.timeout = 10000; 603 req.senselen = SENSEBUFLEN; 604 605 if (ioctl(fd, SCIOCCOMMAND, &req) == -1) { 606 err(1, "SCIOCCOMMAND"); 607 } 608 609 if (req.retsts == SCCMD_OK) { 610 break; 611 } else if (req.retsts == SCCMD_TIMEOUT) { 612 fprintf(stderr, "%s: SCSI command timed out", 613 dvname); 614 break; 615 } else if (req.retsts == SCCMD_BUSY) { 616 fprintf(stderr, "%s: device is busy", 617 dvname); 618 break; 619 } else if (req.retsts != SCCMD_SENSE) { 620 fprintf(stderr, 621 "%s: device had unknown status %x", dvname, 622 req.retsts); 623 break; 624 } 625 memcpy(&sense, req.sense, sizeof(sense)); 626 if (sense.sks.sks_bytes[0] & SSD_SKSV) { 627 j = (sense.sks.sks_bytes[1] << 8) | 628 (sense.sks.sks_bytes[2]); 629 if (j >= complete[i]) { 630 printf(".%d0%%.", ++i); 631 fflush(stdout); 632 } 633 } 634 sleep(10); 635 } while (SSD_SENSE_KEY(sense.flags) == SKEY_NOT_READY); 636 printf(".100%%..done.\n"); 637 } 638 return; 639 } 640 641 static void 642 print_designator(const char *pre, struct scsipi_inquiry_evpd_device_id *did) 643 { 644 char buf[252 * 4 + 1]; 645 unsigned assoc, proto, code, type; 646 static const char *typestr[] = { 647 "vendor", 648 "t10", 649 "eui64", 650 "naa", 651 "target port", 652 "port group", 653 "lun group", 654 "md5", 655 "scsi", 656 "res9", 657 "res10", 658 "res11", 659 "res12", 660 "res13", 661 "res14", 662 "res15" 663 }; 664 static const char *assocstr[] = { 665 "lun", 666 "port", 667 "target", 668 "reserved" 669 }; 670 static const char *protostr[] = { 671 "fibre channel", 672 "obsolete", 673 "ssa", 674 "ieee1394", 675 "rdma", 676 "iSCSI", 677 "SAS" 678 }; 679 const unsigned maxproto = __arraycount(protostr) - 1; 680 const unsigned isbinary = 681 __SHIFTOUT(SINQ_DEVICE_ID_CODESET_BINARY, SINQ_DEVICE_ID_CODESET); 682 unsigned k; 683 684 assoc = __SHIFTOUT(did->flags, SINQ_DEVICE_ID_ASSOCIATION); 685 proto = __SHIFTOUT(did->pc, SINQ_DEVICE_ID_PROTOCOL); 686 code = __SHIFTOUT(did->pc, SINQ_DEVICE_ID_CODESET); 687 type = __SHIFTOUT(did->flags, SINQ_DEVICE_ID_TYPE); 688 689 printf("%s%s", pre, assocstr[assoc]); 690 if (did->flags & SINQ_DEVICE_ID_PIV) { 691 if (proto > maxproto) 692 printf(" proto%u", proto); 693 else 694 printf(" %s", protostr[proto]); 695 } 696 printf(" %s: ", typestr[type]); 697 698 if (code == isbinary) { 699 for (k = 0; k < did->designator_length; k++) { 700 printf("%02x", did->designator[k]); 701 } 702 printf("\n"); 703 } else { 704 scsi_strvis(buf, sizeof(buf), (char *)did->designator, 705 did->designator_length); 706 printf("%s\n", buf); 707 } 708 } 709 710 /* 711 * device_identify: 712 * 713 * Display the identity of the device, including its SCSI bus, 714 * target, lun, and its vendor/product/revision information. 715 * Optionally query and display vpd identification data. 716 */ 717 static void 718 device_identify(int argc, char *argv[]) 719 { 720 struct scsipi_inquiry_data inqbuf; 721 struct { 722 struct scsipi_inquiry_evpd_header h; 723 uint8_t d[255 - sizeof(struct scsipi_inquiry_evpd_header)]; 724 } evpdbuf; 725 struct scsipi_inquiry cmd; 726 unsigned len, rlen; 727 struct scsipi_inquiry_evpd_serial *ser; 728 struct scsipi_inquiry_evpd_device_id *did; 729 int has_serial; 730 int has_device_id; 731 bool getvpd = false; 732 int i; 733 734 /* x4 in case every character is escaped, +1 for NUL. */ 735 char vendor[(sizeof(inqbuf.vendor) * 4) + 1], 736 product[(sizeof(inqbuf.product) * 4) + 1], 737 revision[(sizeof(inqbuf.revision) * 4) + 1], 738 ident[252 * 4 + 1]; 739 740 /* Check optional arguments */ 741 for (i = 0; i < argc; i++) { 742 if (strncmp("vpd", argv[i], 3) == 0) { 743 getvpd = true; 744 continue; 745 } 746 usage(); 747 } 748 749 memset(&cmd, 0, sizeof(cmd)); 750 memset(&inqbuf, 0, sizeof(inqbuf)); 751 752 cmd.opcode = INQUIRY; 753 cmd.length = sizeof(inqbuf); 754 755 scsi_command(fd, &cmd, sizeof(cmd), &inqbuf, sizeof(inqbuf), 756 10000, SCCMD_READ); 757 758 scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor, 759 sizeof(inqbuf.vendor)); 760 scsi_strvis(product, sizeof(product), inqbuf.product, 761 sizeof(inqbuf.product)); 762 scsi_strvis(revision, sizeof(revision), inqbuf.revision, 763 sizeof(inqbuf.revision)); 764 765 printf("%s: scsibus%d target %d lun %d <%s, %s, %s>\n", 766 dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target, 767 dvaddr.addr.scsi.lun, vendor, product, revision); 768 769 if (!getvpd) 770 return; 771 772 cmd.byte2 |= SINQ_EVPD; 773 cmd.pagecode = SINQ_VPD_PAGES; 774 775 scsi_command(fd, &cmd, sizeof(cmd), &evpdbuf, sizeof(evpdbuf), 776 10000, SCCMD_READ); 777 778 len = be16dec(evpdbuf.h.length); 779 if (len > sizeof(evpdbuf.d)) 780 len = 0; 781 782 has_serial = memchr(evpdbuf.d, SINQ_VPD_SERIAL, len) != NULL; 783 has_device_id = memchr(evpdbuf.d, SINQ_VPD_DEVICE_ID, len) != NULL; 784 785 if (has_serial) { 786 cmd.byte2 |= SINQ_EVPD; 787 cmd.pagecode = SINQ_VPD_SERIAL; 788 789 scsi_command(fd, &cmd, sizeof(cmd), &evpdbuf, sizeof(evpdbuf), 790 10000, SCCMD_READ); 791 792 len = be16dec(evpdbuf.h.length); 793 if (len > sizeof(evpdbuf.d)) 794 len = 0; 795 796 ser = (struct scsipi_inquiry_evpd_serial *)&evpdbuf.d; 797 scsi_strvis(ident, sizeof(ident), (char *)ser->serial_number, 798 len); 799 printf("VPD Serial:\n"); 800 printf("\t%s\n", ident); 801 } 802 803 if (has_device_id) { 804 cmd.byte2 |= SINQ_EVPD; 805 cmd.pagecode = SINQ_VPD_DEVICE_ID; 806 807 scsi_command(fd, &cmd, sizeof(cmd), &evpdbuf, sizeof(evpdbuf), 808 10000, SCCMD_READ); 809 810 len = be16dec(evpdbuf.h.length); 811 if (len > sizeof(evpdbuf.d)) 812 len = 0; 813 814 printf("VPD Device IDs:\n"); 815 816 for (unsigned off = 0; off < len - sizeof(*did); off += rlen) { 817 void *p = &evpdbuf.d[off]; 818 did = (struct scsipi_inquiry_evpd_device_id *)p; 819 rlen = sizeof(*did) + did->designator_length - 1; 820 if (off + rlen > len) 821 break; 822 823 print_designator("\t", did); 824 } 825 } 826 827 return; 828 } 829 830 /* 831 * device_reassign: 832 * 833 * Reassign bad blocks on a direct access device. 834 */ 835 static void 836 device_reassign(int argc, char *argv[]) 837 { 838 struct scsi_reassign_blocks cmd; 839 struct scsi_reassign_blocks_data *data; 840 size_t dlen; 841 u_int32_t blkno; 842 int i; 843 char *cp; 844 845 /* We get a list of block numbers. */ 846 if (argc < 1) 847 usage(); 848 849 /* 850 * Allocate the reassign blocks descriptor. The 4 comes from the 851 * size of the block address in the defect descriptor. 852 */ 853 dlen = sizeof(struct scsi_reassign_blocks_data) + ((argc - 1) * 4); 854 data = malloc(dlen); 855 if (data == NULL) 856 errx(1, "unable to allocate defect descriptor"); 857 memset(data, 0, dlen); 858 859 cmd.opcode = SCSI_REASSIGN_BLOCKS; 860 cmd.byte2 = 0; 861 cmd.unused[0] = 0; 862 cmd.unused[1] = 0; 863 cmd.unused[2] = 0; 864 cmd.control = 0; 865 866 /* Defect descriptor length. */ 867 _lto2b(argc * 4, data->length); 868 869 /* Build the defect descriptor list. */ 870 for (i = 0; i < argc; i++) { 871 blkno = strtoul(argv[i], &cp, 10); 872 if (*cp != '\0') 873 errx(1, "invalid block number: %s", argv[i]); 874 _lto4b(blkno, data->defect_descriptor[i].dlbaddr); 875 } 876 877 scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_WRITE); 878 879 free(data); 880 return; 881 } 882 883 /* 884 * device_release: 885 * 886 * Issue a RELEASE command to a SCSI device. 887 */ 888 #ifndef SCSI_RELEASE 889 #define SCSI_RELEASE 0x17 890 #endif 891 static void 892 device_release(int argc, char *argv[]) 893 { 894 struct scsi_test_unit_ready cmd; /* close enough */ 895 896 /* No arguments. */ 897 if (argc != 0) 898 usage(); 899 900 memset(&cmd, 0, sizeof(cmd)); 901 902 cmd.opcode = SCSI_RELEASE; 903 904 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 905 906 return; 907 } 908 909 /* 910 * device_reserve: 911 * 912 * Issue a RESERVE command to a SCSI device. 913 */ 914 #ifndef SCSI_RESERVE 915 #define SCSI_RESERVE 0x16 916 #endif 917 static void 918 device_reserve(int argc, char *argv[]) 919 { 920 struct scsi_test_unit_ready cmd; /* close enough */ 921 922 /* No arguments. */ 923 if (argc != 0) 924 usage(); 925 926 memset(&cmd, 0, sizeof(cmd)); 927 928 cmd.opcode = SCSI_RESERVE; 929 930 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 931 932 return; 933 } 934 935 /* 936 * device_reset: 937 * 938 * Issue a reset to a SCSI device. 939 */ 940 static void 941 device_reset(int argc, char *argv[]) 942 { 943 944 /* No arguments. */ 945 if (argc != 0) 946 usage(); 947 948 if (ioctl(fd, SCIOCRESET, NULL) != 0) 949 err(1, "SCIOCRESET"); 950 951 return; 952 } 953 954 /* 955 * device_debug: 956 * 957 * Set debug level to a SCSI device. 958 * scsipi will print anything iff SCSIPI_DEBUG set in config. 959 */ 960 static void 961 device_debug(int argc, char *argv[]) 962 { 963 int lvl; 964 965 if (argc < 1) 966 usage(); 967 968 lvl = atoi(argv[0]); 969 970 if (ioctl(fd, SCIOCDEBUG, &lvl) != 0) 971 err(1, "SCIOCDEBUG"); 972 973 return; 974 } 975 976 /* 977 * device_getcache: 978 * 979 * Get the caching parameters for a SCSI disk. 980 */ 981 static void 982 device_getcache(int argc, char *argv[]) 983 { 984 struct { 985 struct scsi_mode_parameter_header_6 header; 986 struct scsi_general_block_descriptor blk_desc; 987 struct page_caching caching_params; 988 } data; 989 990 /* No arguments. */ 991 if (argc != 0) 992 usage(); 993 994 scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data)); 995 996 if ((data.caching_params.flags & (CACHING_RCD|CACHING_WCE)) == 997 CACHING_RCD) 998 printf("%s: no caches enabled\n", dvname); 999 else { 1000 printf("%s: read cache %senabled\n", dvname, 1001 (data.caching_params.flags & CACHING_RCD) ? "not " : ""); 1002 printf("%s: write-back cache %senabled\n", dvname, 1003 (data.caching_params.flags & CACHING_WCE) ? "" : "not "); 1004 } 1005 printf("%s: caching parameters are %ssavable\n", dvname, 1006 (data.caching_params.pg_code & PGCODE_PS) ? "" : "not "); 1007 } 1008 1009 /* 1010 * device_setcache: 1011 * 1012 * Set cache enables for a SCSI disk. 1013 */ 1014 static void 1015 device_setcache(int argc, char *argv[]) 1016 { 1017 struct { 1018 struct scsi_mode_parameter_header_6 header; 1019 struct scsi_general_block_descriptor blk_desc; 1020 struct page_caching caching_params; 1021 } data; 1022 int dlen; 1023 u_int8_t flags, byte2; 1024 1025 if (argc > 2 || argc == 0) 1026 usage(); 1027 1028 flags = 0; 1029 byte2 = 0; 1030 if (strcmp(argv[0], "none") == 0) 1031 flags = CACHING_RCD; 1032 else if (strcmp(argv[0], "r") == 0) 1033 flags = 0; 1034 else if (strcmp(argv[0], "w") == 0) 1035 flags = CACHING_RCD|CACHING_WCE; 1036 else if (strcmp(argv[0], "rw") == 0) 1037 flags = CACHING_WCE; 1038 else 1039 usage(); 1040 1041 if (argc == 2) { 1042 if (strcmp(argv[1], "save") == 0) 1043 byte2 = SMS_SP; 1044 else 1045 usage(); 1046 } 1047 1048 scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data)); 1049 1050 data.caching_params.pg_code &= PGCODE_MASK; 1051 data.caching_params.flags = 1052 (data.caching_params.flags & ~(CACHING_RCD|CACHING_WCE)) | flags; 1053 1054 data.caching_params.cache_segment_size[0] = 0; 1055 data.caching_params.cache_segment_size[1] = 0; 1056 1057 data.header.data_length = 0; 1058 1059 dlen = sizeof(data.header) + sizeof(data.blk_desc) + 2 + 1060 data.caching_params.pg_length; 1061 1062 scsi_mode_select(fd, byte2, &data, dlen); 1063 } 1064 1065 /* 1066 * device_flushcache: 1067 * 1068 * Issue a FLUSH CACHE command to a SCSI device. 1069 */ 1070 #ifndef SCSI_FLUSHCACHE 1071 #define SCSI_FLUSHCACHE 0x35 1072 #endif 1073 static void 1074 device_flushcache(int argc, char *argv[]) 1075 { 1076 struct scsi_test_unit_ready cmd; /* close enough */ 1077 1078 /* No arguments. */ 1079 if (argc != 0) 1080 usage(); 1081 1082 memset(&cmd, 0, sizeof(cmd)); 1083 1084 cmd.opcode = SCSI_FLUSHCACHE; 1085 1086 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 1087 1088 return; 1089 } 1090 1091 /* 1092 * device_setspeed: 1093 * 1094 * Set rotation speed to a CD/DVD drive. 1095 */ 1096 static void 1097 device_setspeed(int argc, char *argv[]) 1098 { 1099 u_char cmd[11]; 1100 u_char pd[28]; 1101 u_int32_t speed; 1102 1103 if (argc != 1) 1104 usage(); 1105 1106 speed = atoi(argv[0]) * 177; 1107 1108 memset(&pd, 0, sizeof(pd)); 1109 if (speed == 0) 1110 pd[0] = 4; /* restore drive defaults */ 1111 pd[8] = 0xff; 1112 pd[9] = 0xff; 1113 pd[10] = 0xff; 1114 pd[11] = 0xff; 1115 pd[12] = pd[20] = (speed >> 24) & 0xff; 1116 pd[13] = pd[21] = (speed >> 16) & 0xff; 1117 pd[14] = pd[22] = (speed >> 8) & 0xff; 1118 pd[15] = pd[23] = speed & 0xff; 1119 pd[18] = pd[26] = 1000 >> 8; 1120 pd[19] = pd[27] = 1000 & 0xff; 1121 1122 memset(&cmd, 0, sizeof(cmd)); 1123 cmd[0] = 0xb6; 1124 cmd[10] = sizeof(pd); 1125 1126 scsi_command(fd, &cmd, sizeof(cmd), pd, sizeof(pd), 10000, SCCMD_WRITE); 1127 1128 return; 1129 } 1130 1131 /* 1132 * device_reportluns: 1133 * 1134 * Report the known LUNs to which the initiator can send commands 1135 */ 1136 static void 1137 device_reportluns(int argc, char *argv[]) 1138 { 1139 struct scsi_report_luns cmd; 1140 struct { 1141 struct scsi_report_luns_header header; 1142 struct scsi_report_luns_lun desc[1]; 1143 } *data; 1144 u_int32_t dlen, len; 1145 u_int64_t lun; 1146 size_t count, idx; 1147 unsigned long sel; 1148 char *endp; 1149 int i; 1150 1151 dlen = USHRT_MAX; /* good for > 8000 LUNs */ 1152 data = malloc(dlen); 1153 if (data == NULL) 1154 errx(1, "unable to allocate lun report"); 1155 1156 memset(&cmd, 0, sizeof(cmd)); 1157 cmd.opcode = SCSI_REPORT_LUNS; 1158 cmd.selectreport = SELECTREPORT_NORMAL; 1159 1160 /* determine which report to read. */ 1161 for (i = 0; i < argc; i++) { 1162 if (strcmp("normal", argv[i]) == 0) { 1163 cmd.selectreport = SELECTREPORT_NORMAL; 1164 continue; 1165 } 1166 if (strcmp("wellknown", argv[i]) == 0) { 1167 cmd.selectreport = SELECTREPORT_WELLKNOWN; 1168 continue; 1169 } 1170 if (strcmp("all", argv[i]) == 0) { 1171 cmd.selectreport = SELECTREPORT_ALL; 1172 continue; 1173 } 1174 sel = strtoul(argv[i], &endp, 0); 1175 if (*endp != '\0' || sel > 255) 1176 errx(1, "Unknown select report '%s'", argv[i]); 1177 cmd.selectreport = sel; 1178 } 1179 1180 _lto4b(dlen, &cmd.alloclen[0]); 1181 cmd.control = 0x00; 1182 1183 scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_READ); 1184 1185 len = _4btol(data->header.length); 1186 if (len > dlen) { 1187 /* XXX reallocate and retry */ 1188 printf("%s: report truncated %" PRIu32 "to %" PRIu32 "\n", 1189 dvname, len, dlen); 1190 len = dlen; 1191 } 1192 1193 count = len / sizeof(data->desc[0]); 1194 1195 for (idx = 0; idx < count; idx++) { 1196 lun = _8btol(data->desc[idx].lun); 1197 1198 /* 1199 * swizzle bits so that LUNs 0..255 are 1200 * mapped to numbers 0..255 1201 */ 1202 lun = (lun & 0xffff000000000000ull) >> 48 1203 | (lun & 0x0000ffff00000000ull) >> 16 1204 | (lun & 0x00000000ffff0000ull) << 16 1205 | (lun & 0x000000000000ffffull) << 48; 1206 1207 printf("%s: lun %" PRIu64 "\n", dvname, lun); 1208 } 1209 1210 free(data); 1211 } 1212 1213 /* 1214 * device_getrealloc: 1215 * 1216 * Get the automatic reallocation parameters for a SCSI disk. 1217 */ 1218 static void 1219 device_getrealloc(int argc, char *argv[]) 1220 { 1221 struct { 1222 struct scsi_mode_parameter_header_6 header; 1223 struct scsi_general_block_descriptor blk_desc; 1224 struct page_err_recov err_recov_params; 1225 } data; 1226 u_int8_t flags; 1227 1228 /* No arguments. */ 1229 if (argc != 0) 1230 usage(); 1231 1232 scsi_mode_sense(fd, 0x01, 0x00, &data, sizeof(data)); 1233 1234 flags = data.err_recov_params.flags; 1235 if ((flags & (ERR_RECOV_ARRE | ERR_RECOV_AWRE)) == 0) 1236 printf("%s: no automatic reallocation enabled\n", dvname); 1237 else { 1238 printf("%s: automatic read reallocation %senabled\n", dvname, 1239 (flags & ERR_RECOV_ARRE) ? "" : "not "); 1240 printf("%s: automatic write reallocation %senabled\n", dvname, 1241 (flags & ERR_RECOV_AWRE) ? "" : "not "); 1242 } 1243 printf("%s: error recovery parameters are %ssavable\n", dvname, 1244 (data.err_recov_params.pg_code & PGCODE_PS) ? "" : "not "); 1245 } 1246 1247 /* 1248 * device_setrealloc: 1249 * 1250 * Set the automatic reallocation parameters for a SCSI disk. 1251 */ 1252 static void 1253 device_setrealloc(int argc, char *argv[]) 1254 { 1255 struct { 1256 struct scsi_mode_parameter_header_6 header; 1257 struct scsi_general_block_descriptor blk_desc; 1258 struct page_err_recov err_recov_params; 1259 } data; 1260 int dlen; 1261 u_int8_t flags, byte2; 1262 1263 if (argc > 2 || argc == 0) 1264 usage(); 1265 1266 flags = 0; 1267 byte2 = 0; 1268 if (strcmp(argv[0], "none") == 0) 1269 flags = 0; 1270 else if (strcmp(argv[0], "r") == 0) 1271 flags = ERR_RECOV_ARRE; 1272 else if (strcmp(argv[0], "w") == 0) 1273 flags = ERR_RECOV_AWRE; 1274 else if (strcmp(argv[0], "rw") == 0) 1275 flags = ERR_RECOV_ARRE | ERR_RECOV_AWRE; 1276 else 1277 usage(); 1278 1279 if (argc == 2) { 1280 if (strcmp(argv[1], "save") == 0) 1281 byte2 = SMS_SP; 1282 else 1283 usage(); 1284 } 1285 1286 scsi_mode_sense(fd, 0x01, 0x00, &data, sizeof(data)); 1287 1288 data.err_recov_params.pg_code &= PGCODE_MASK; 1289 data.err_recov_params.flags &= ~(ERR_RECOV_ARRE | ERR_RECOV_AWRE); 1290 data.err_recov_params.flags |= flags; 1291 1292 data.header.data_length = 0; 1293 1294 dlen = sizeof(data.header) + sizeof(data.blk_desc) + 2 + 1295 data.err_recov_params.pg_length; 1296 1297 scsi_mode_select(fd, byte2, &data, dlen); 1298 } 1299 1300 /* 1301 * device_prevent: 1302 * 1303 * Issue a prevent to a SCSI device. 1304 */ 1305 static void 1306 device_prevent(int argc, char *argv[]) 1307 { 1308 struct scsi_prevent_allow_medium_removal cmd; 1309 1310 /* No arguments. */ 1311 if (argc != 0) 1312 usage(); 1313 1314 memset(&cmd, 0, sizeof(cmd)); 1315 1316 cmd.opcode = SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL; 1317 cmd.how = SPAMR_PREVENT_DT; /* XXX SMAMR_PREVENT_ALL? */ 1318 1319 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 1320 1321 return; 1322 } 1323 1324 /* 1325 * device_allow: 1326 * 1327 * Issue a stop to a SCSI device. 1328 */ 1329 static void 1330 device_allow(int argc, char *argv[]) 1331 { 1332 struct scsi_prevent_allow_medium_removal cmd; 1333 1334 /* No arguments. */ 1335 if (argc != 0) 1336 usage(); 1337 1338 memset(&cmd, 0, sizeof(cmd)); 1339 1340 cmd.opcode = SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL; 1341 cmd.how = SPAMR_ALLOW; 1342 1343 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 1344 1345 return; 1346 } 1347 1348 /* 1349 * device_start: 1350 * 1351 * Issue a start to a SCSI device. 1352 */ 1353 static void 1354 device_start(int argc, char *argv[]) 1355 { 1356 struct scsipi_start_stop cmd; 1357 1358 /* No arguments. */ 1359 if (argc != 0) 1360 usage(); 1361 1362 memset(&cmd, 0, sizeof(cmd)); 1363 1364 cmd.opcode = START_STOP; 1365 cmd.how = SSS_START; 1366 1367 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 30000, 0); 1368 1369 return; 1370 } 1371 1372 /* 1373 * device_stop: 1374 * 1375 * Issue a stop to a SCSI device. 1376 */ 1377 static void 1378 device_stop(int argc, char *argv[]) 1379 { 1380 struct scsipi_start_stop cmd; 1381 1382 /* No arguments. */ 1383 if (argc != 0) 1384 usage(); 1385 1386 memset(&cmd, 0, sizeof(cmd)); 1387 1388 cmd.opcode = START_STOP; 1389 cmd.how = SSS_STOP; 1390 1391 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 30000, 0); 1392 1393 return; 1394 } 1395 1396 /* 1397 * device_tur: 1398 * 1399 * Issue a TEST UNIT READY to a SCSI device. 1400 */ 1401 static void 1402 device_tur(int argc, char *argv[]) 1403 { 1404 struct scsi_test_unit_ready cmd; 1405 1406 /* No arguments. */ 1407 if (argc != 0) 1408 usage(); 1409 1410 memset(&cmd, 0, sizeof(cmd)); 1411 1412 cmd.opcode = SCSI_TEST_UNIT_READY; 1413 1414 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 1415 1416 return; 1417 } 1418 1419 /* 1420 * BUS COMMANDS 1421 */ 1422 1423 /* 1424 * bus_reset: 1425 * 1426 * Issue a reset to a SCSI bus. 1427 */ 1428 static void 1429 bus_reset(int argc, char *argv[]) 1430 { 1431 1432 /* No arguments. */ 1433 if (argc != 0) 1434 usage(); 1435 1436 if (ioctl(fd, SCBUSIORESET, NULL) != 0) 1437 err(1, "SCBUSIORESET"); 1438 1439 return; 1440 } 1441 1442 /* 1443 * bus_scan: 1444 * 1445 * Rescan a SCSI bus for new devices. 1446 */ 1447 static void 1448 bus_scan(int argc, char *argv[]) 1449 { 1450 struct scbusioscan_args args; 1451 char *cp; 1452 1453 /* Must have two args: target lun */ 1454 if (argc != 2) 1455 usage(); 1456 1457 if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0) 1458 args.sa_target = -1; 1459 else { 1460 args.sa_target = strtol(argv[0], &cp, 10); 1461 if (*cp != '\0' || args.sa_target < 0) 1462 errx(1, "invalid target: %s", argv[0]); 1463 } 1464 1465 if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0) 1466 args.sa_lun = -1; 1467 else { 1468 args.sa_lun = strtol(argv[1], &cp, 10); 1469 if (*cp != '\0' || args.sa_lun < 0) 1470 errx(1, "invalid lun: %s", argv[1]); 1471 } 1472 1473 if (ioctl(fd, SCBUSIOSCAN, &args) != 0) 1474 err(1, "SCBUSIOSCAN"); 1475 1476 return; 1477 } 1478 1479 /* 1480 * bus_detach: 1481 * 1482 * detach SCSI devices from a bus. 1483 */ 1484 static void 1485 bus_detach(int argc, char *argv[]) 1486 { 1487 struct scbusiodetach_args args; 1488 char *cp; 1489 1490 /* Must have two args: target lun */ 1491 if (argc != 2) 1492 usage(); 1493 1494 if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0) 1495 args.sa_target = -1; 1496 else { 1497 args.sa_target = strtol(argv[0], &cp, 10); 1498 if (*cp != '\0' || args.sa_target < 0) 1499 errx(1, "invalid target: %s", argv[0]); 1500 } 1501 1502 if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0) 1503 args.sa_lun = -1; 1504 else { 1505 args.sa_lun = strtol(argv[1], &cp, 10); 1506 if (*cp != '\0' || args.sa_lun < 0) 1507 errx(1, "invalid lun: %s", argv[1]); 1508 } 1509 1510 if (ioctl(fd, SCBUSIODETACH, &args) != 0) 1511 err(1, "SCBUSIODETACH"); 1512 1513 return; 1514 } 1515