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