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