scsictl.c revision 1.34
1/* $NetBSD: scsictl.c,v 1.34 2012/11/03 19:14:53 jakllsch 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.34 2012/11/03 19:14:53 jakllsch Exp $"); 40#endif 41 42 43#include <sys/param.h> 44#include <sys/ioctl.h> 45#include <sys/scsiio.h> 46#include <err.h> 47#include <errno.h> 48#include <fcntl.h> 49#include <limits.h> 50#include <stdio.h> 51#include <stdlib.h> 52#include <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 63struct 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 71int fd; /* file descriptor for device */ 72const char *dvname; /* device name */ 73char dvname_store[MAXPATHLEN]; /* for opendisk(3) */ 74const char *cmdname; /* command user issued */ 75struct scsi_addr dvaddr; /* SCSI device's address */ 76 77void device_defects(int, char *[]); 78void device_format(int, char *[]); 79void device_identify(int, char *[]); 80void device_reassign(int, char *[]); 81void device_release(int, char *[]); 82void device_reserve(int, char *[]); 83void device_reset(int, char *[]); 84void device_debug(int, char *[]); 85void device_prevent(int, char *[]); 86void device_allow(int, char *[]); 87void device_start(int, char *[]); 88void device_stop(int, char *[]); 89void device_tur(int, char *[]); 90void device_getcache(int, char *[]); 91void device_setcache(int, char *[]); 92void device_flushcache(int, char *[]); 93void device_setspeed(int, char *[]); 94 95struct command device_commands[] = { 96 { "defects", "[primary] [grown] [block|byte|physical]", 97 device_defects }, 98 { "format", "[blocksize [immediate]]", device_format }, 99 { "identify", "", device_identify }, 100 { "reassign", "blkno [blkno [...]]", device_reassign }, 101 { "release", "", device_release }, 102 { "reserve", "", device_reserve }, 103 { "reset", "", device_reset }, 104 { "debug", "level", device_debug }, 105 { "prevent", "", device_prevent }, 106 { "allow", "", device_allow }, 107 { "start", "", device_start }, 108 { "stop", "", device_stop }, 109 { "tur", "", device_tur }, 110 { "getcache", "", device_getcache }, 111 { "setcache", "none|r|w|rw [save]", device_setcache }, 112 { "flushcache", "", device_flushcache }, 113 { "setspeed", "[speed]", device_setspeed }, 114 { NULL, NULL, NULL }, 115}; 116 117void bus_reset(int, char *[]); 118void bus_scan(int, char *[]); 119void bus_detach(int, char *[]); 120 121struct command bus_commands[] = { 122 { "reset", "", bus_reset }, 123 { "scan", "target lun", bus_scan }, 124 { "detach", "target lun", bus_detach }, 125 { NULL, NULL, NULL }, 126}; 127 128int 129main(int argc, char *argv[]) 130{ 131 struct command *commands; 132 int i; 133 134 /* Must have at least: device command */ 135 if (argc < 3) 136 usage(); 137 138 /* Skip program name, get and skip device name and command. */ 139 dvname = argv[1]; 140 cmdname = argv[2]; 141 argv += 3; 142 argc -= 3; 143 144 /* 145 * Open the device and determine if it's a scsibus or an actual 146 * device. Devices respond to the SCIOCIDENTIFY ioctl. 147 */ 148 fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0); 149 if (fd == -1) { 150 if (errno == ENOENT) { 151 /* 152 * Device doesn't exist. Probably trying to open 153 * a device which doesn't use disk semantics for 154 * device name. Try again, specifying "cooked", 155 * which leaves off the "r" in front of the device's 156 * name. 157 */ 158 fd = opendisk(dvname, O_RDWR, dvname_store, 159 sizeof(dvname_store), 1); 160 if (fd == -1) 161 err(1, "%s", dvname); 162 } else 163 err(1, "%s", dvname); 164 } 165 166 /* 167 * Point the dvname at the actual device name that opendisk() opened. 168 */ 169 dvname = dvname_store; 170 171 if (ioctl(fd, SCIOCIDENTIFY, &dvaddr) < 0) 172 commands = bus_commands; 173 else 174 commands = device_commands; 175 176 /* Look up and call the command. */ 177 for (i = 0; commands[i].cmd_name != NULL; i++) 178 if (strcmp(cmdname, commands[i].cmd_name) == 0) 179 break; 180 if (commands[i].cmd_name == NULL) 181 errx(1, "unknown %s command: %s", 182 commands == bus_commands ? "bus" : "device", cmdname); 183 184 (*commands[i].cmd_func)(argc, argv); 185 exit(0); 186} 187 188static void 189usage(void) 190{ 191 int i; 192 193 fprintf(stderr, "usage: %s device command [arg [...]]\n", 194 getprogname()); 195 196 fprintf(stderr, " Commands pertaining to scsi devices:\n"); 197 for (i=0; device_commands[i].cmd_name != NULL; i++) 198 fprintf(stderr, "\t%s %s\n", device_commands[i].cmd_name, 199 device_commands[i].arg_names); 200 fprintf(stderr, " Commands pertaining to scsi busses:\n"); 201 for (i=0; bus_commands[i].cmd_name != NULL; i++) 202 fprintf(stderr, "\t%s %s\n", bus_commands[i].cmd_name, 203 bus_commands[i].arg_names); 204 fprintf(stderr, " Use `any' or `all' to wildcard target or lun\n"); 205 206 exit(1); 207} 208 209/* 210 * DEVICE COMMANDS 211 */ 212 213/* 214 * device_read_defect: 215 * 216 * Read primary and/or growth defect list in physical or block 217 * format from a direct access device. 218 * 219 * XXX Does not handle very large defect lists. Needs SCSI3 12 220 * byte READ DEFECT DATA command. 221 */ 222 223void print_bf_dd(union scsi_defect_descriptor *); 224void print_bfif_dd(union scsi_defect_descriptor *); 225void print_psf_dd(union scsi_defect_descriptor *); 226 227void 228device_defects(int argc, char *argv[]) 229{ 230 struct scsi_read_defect_data cmd; 231 struct scsi_read_defect_data_data *data; 232 size_t dlen; 233 int i, dlfmt = -1; 234 int defects; 235 char msg[256]; 236 void (*pfunc)(union scsi_defect_descriptor *); 237#define RDD_P_G_MASK 0x18 238#define RDD_DLF_MASK 0x7 239 240 dlen = USHRT_MAX; /* XXX - this may not be enough room 241 * for all of the defects. 242 */ 243 data = malloc(dlen); 244 if (data == NULL) 245 errx(1, "unable to allocate defect list"); 246 memset(data, 0, dlen); 247 memset(&cmd, 0, sizeof(cmd)); 248 defects = 0; 249 pfunc = NULL; 250 251 /* determine which defect list(s) to read. */ 252 for (i = 0; i < argc; i++) { 253 if (strncmp("primary", argv[i], 7) == 0) { 254 cmd.flags |= RDD_PRIMARY; 255 continue; 256 } 257 if (strncmp("grown", argv[i], 5) == 0) { 258 cmd.flags |= RDD_GROWN; 259 continue; 260 } 261 break; 262 } 263 264 /* no defect list sepecified, assume both. */ 265 if ((cmd.flags & (RDD_PRIMARY|RDD_GROWN)) == 0) 266 cmd.flags |= (RDD_PRIMARY|RDD_GROWN); 267 268 /* list format option. */ 269 if (i < argc) { 270 if (strncmp("block", argv[i], 5) == 0) { 271 cmd.flags |= RDD_BF; 272 dlfmt = RDD_BF; 273 } 274 else if (strncmp("byte", argv[i], 4) == 0) { 275 cmd.flags |= RDD_BFIF; 276 dlfmt = RDD_BFIF; 277 } 278 else if (strncmp("physical", argv[i], 4) == 0) { 279 cmd.flags |= RDD_PSF; 280 dlfmt = RDD_PSF; 281 } 282 else { 283 usage(); 284 } 285 } 286 287 /* 288 * no list format specified; since block format not 289 * recommended use physical sector format as default. 290 */ 291 if (dlfmt < 0) { 292 cmd.flags |= RDD_PSF; 293 dlfmt = RDD_PSF; 294 } 295 296 cmd.opcode = SCSI_READ_DEFECT_DATA; 297 _lto2b(dlen, &cmd.length[0]); 298 299 scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_READ); 300 301 msg[0] = '\0'; 302 303 /* is the defect list in the format asked for? */ 304 if ((data->flags & RDD_DLF_MASK) != dlfmt) { 305 strcpy(msg, "\n\tnotice:" 306 "requested defect list format not supported by device\n\n"); 307 dlfmt = (data->flags & RDD_DLF_MASK); 308 } 309 310 if (data->flags & RDD_PRIMARY) 311 strcat(msg, "primary"); 312 313 if (data->flags & RDD_GROWN) { 314 if (data->flags & RDD_PRIMARY) 315 strcat(msg, " and "); 316 strcat(msg, "grown"); 317 } 318 319 strcat(msg, " defects"); 320 321 if ((data->flags & RDD_P_G_MASK) == 0) 322 strcat(msg, ": none reported\n"); 323 324 325 printf("%s: scsibus%d target %d lun %d %s", 326 dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target, 327 dvaddr.addr.scsi.lun, msg); 328 329 /* device did not return either defect list. */ 330 if ((data->flags & RDD_P_G_MASK) == 0) 331 return; 332 333 switch (dlfmt) { 334 case RDD_BF: 335 defects = _2btol(data->length) / 336 sizeof(struct scsi_defect_descriptor_bf); 337 pfunc = print_bf_dd; 338 strcpy(msg, "block address\n" 339 "-------------\n"); 340 break; 341 case RDD_BFIF: 342 defects = _2btol(data->length) / 343 sizeof(struct scsi_defect_descriptor_bfif); 344 pfunc = print_bfif_dd; 345 strcpy(msg, " bytes from\n" 346 "cylinder head index\n" 347 "-------- ---- ----------\n"); 348 break; 349 case RDD_PSF: 350 defects = _2btol(data->length) / 351 sizeof(struct scsi_defect_descriptor_psf); 352 pfunc = print_psf_dd; 353 strcpy(msg, "cylinder head sector\n" 354 "-------- ---- ----------\n"); 355 break; 356 } 357 358 /* device did not return any defects. */ 359 if (defects == 0) { 360 printf(": none\n"); 361 return; 362 } 363 364 printf(": %d\n", defects); 365 366 /* print heading. */ 367 printf("%s", msg); 368 369 /* print defect list. */ 370 for (i = 0 ; i < defects; i++) { 371 pfunc(&data->defect_descriptor[i]); 372 } 373 374 free(data); 375 return; 376} 377 378/* 379 * print_bf_dd: 380 * 381 * Print a block format defect descriptor. 382 */ 383void 384print_bf_dd(union scsi_defect_descriptor *dd) 385{ 386 u_int32_t block; 387 388 block = _4btol(dd->bf.block_address); 389 390 printf("%13u\n", block); 391} 392 393#define DEFECTIVE_TRACK 0xffffffff 394 395/* 396 * print_bfif_dd: 397 * 398 * Print a bytes from index format defect descriptor. 399 */ 400void 401print_bfif_dd(union scsi_defect_descriptor *dd) 402{ 403 u_int32_t cylinder; 404 u_int32_t head; 405 u_int32_t bytes_from_index; 406 407 cylinder = _3btol(dd->bfif.cylinder); 408 head = dd->bfif.head; 409 bytes_from_index = _4btol(dd->bfif.bytes_from_index); 410 411 printf("%8u %4u ", cylinder, head); 412 413 if (bytes_from_index == DEFECTIVE_TRACK) 414 printf("entire track defective\n"); 415 else 416 printf("%10u\n", bytes_from_index); 417} 418 419/* 420 * print_psf_dd: 421 * 422 * Print a physical sector format defect descriptor. 423 */ 424void 425print_psf_dd(union scsi_defect_descriptor *dd) 426{ 427 u_int32_t cylinder; 428 u_int32_t head; 429 u_int32_t sector; 430 431 cylinder = _3btol(dd->psf.cylinder); 432 head = dd->psf.head; 433 sector = _4btol(dd->psf.sector); 434 435 printf("%8u %4u ", cylinder, head); 436 437 if (sector == DEFECTIVE_TRACK) 438 printf("entire track defective\n"); 439 else 440 printf("%10u\n", sector); 441} 442 443/* 444 * device_format: 445 * 446 * Format a direct access device. 447 */ 448void 449device_format(int argc, char *argv[]) 450{ 451 u_int32_t blksize; 452 int i, j, immediate; 453#define PC (65536/10) 454 static int complete[] = { 455 PC*1, PC*2, PC*3, PC*4, PC*5, PC*6, PC*7, PC*8, PC*9, 65536 456 }; 457 char *cp, buffer[64]; 458 struct scsi_sense_data sense; 459 struct scsi_format_unit cmd; 460 struct { 461 struct scsi_format_unit_defect_list_header header; 462 /* optional initialization pattern */ 463 /* optional defect list */ 464 } dfl; 465 struct { 466 struct scsi_mode_parameter_header_6 header; 467 struct scsi_general_block_descriptor blk_desc; 468 struct page_disk_format format_page; 469 } mode_page; 470 struct { 471 struct scsi_mode_parameter_header_6 header; 472 struct scsi_general_block_descriptor blk_desc; 473 } data_select; 474 475 476 /* Blocksize is an optional argument. */ 477 if (argc > 2) 478 usage(); 479 480 /* 481 * Loop doing Request Sense to clear any pending Unit Attention. 482 * 483 * Multiple conditions may exist on the drive which are returned 484 * in priority order. 485 */ 486 for (i = 0; i < 8; i++) { 487 scsi_request_sense(fd, &sense, sizeof (sense)); 488 if ((j = SSD_SENSE_KEY(sense.flags)) == SKEY_NO_SENSE) 489 break; 490 } 491 /* 492 * Make sure we cleared any pending Unit Attention 493 */ 494 if (j != SKEY_NO_SENSE) { 495 cp = scsi_decode_sense((const unsigned char *) &sense, 2, 496 buffer, sizeof (buffer)); 497 errx(1, "failed to clean Unit Attention: %s", cp); 498 } 499 500 /* 501 * Get the DISK FORMAT mode page. SCSI-2 recommends specifying the 502 * interleave read from this page in the FORMAT UNIT command. 503 */ 504 scsi_mode_sense(fd, 0x03, 0x00, &mode_page, sizeof(mode_page)); 505 506 j = (mode_page.format_page.bytes_s[0] << 8) | 507 (mode_page.format_page.bytes_s[1]); 508 509 if (j != DEV_BSIZE) 510 printf("current disk sector size: %d\n", j); 511 512 memset(&cmd, 0, sizeof(cmd)); 513 514 cmd.opcode = SCSI_FORMAT_UNIT; 515 memcpy(cmd.interleave, mode_page.format_page.interleave, 516 sizeof(cmd.interleave)); 517 518 /* 519 * The blocksize on the device is only changed if the user 520 * specified a new blocksize. If not specified the blocksize 521 * used for the device will be the Default value in the device. 522 * We don't specify the number of blocks since the format 523 * command will always reformat the entire drive. Also by 524 * not specifying a block count the drive will reset the 525 * block count to the maximum available after the format 526 * completes if the blocksize was changed in the format. 527 * Finally, the new disk geometry will not but updated on 528 * the drive in permanent storage until _AFTER_ the format 529 * completes successfully. 530 */ 531 if (argc > 0) { 532 blksize = strtoul(argv[0], &cp, 10); 533 if (*cp != '\0') 534 errx(1, "invalid block size: %s", argv[0]); 535 536 memset(&data_select, 0, sizeof(data_select)); 537 538 data_select.header.blk_desc_len = 539 sizeof(struct scsi_general_block_descriptor); 540 /* 541 * blklen in desc is 3 bytes with a leading reserved byte 542 */ 543 _lto4b(blksize, &data_select.blk_desc.reserved); 544 545 /* 546 * Issue Mode Select to modify the device blocksize to be 547 * used on the Format. The modified device geometry will 548 * be stored as Current and Saved Page 3 parameters when 549 * the Format completes. 550 */ 551 scsi_mode_select(fd, 0, &data_select, sizeof(data_select)); 552 553 /* 554 * Since user specified a specific block size make sure it 555 * gets stored in the device when the format completes. 556 * 557 * Also scrub the defect list back to the manufacturers 558 * original. 559 */ 560 cmd.flags = SFU_CMPLST | SFU_FMTDATA; 561 } 562 563 memset(&dfl, 0, sizeof(dfl)); 564 565 if (argc > 1 && strncmp(argv[1], "imm", 3) == 0) { 566 /* 567 * Signal target for an immediate return from Format. 568 * 569 * We'll poll for completion status. 570 */ 571 dfl.header.flags = DLH_IMMED; 572 immediate = 1; 573 } else { 574 immediate = 0; 575 } 576 577 scsi_command(fd, &cmd, sizeof(cmd), &dfl, sizeof(dfl), 578 8 * 60 * 60 * 1000, 0); 579 580 /* 581 * Poll device for completion of Format 582 */ 583 if (immediate) { 584 i = 0; 585 printf("formatting."); 586 fflush(stdout); 587 do { 588 scsireq_t req; 589 struct scsi_test_unit_ready tcmd; 590 591 memset(&tcmd, 0, sizeof(cmd)); 592 tcmd.opcode = SCSI_TEST_UNIT_READY; 593 594 memset(&req, 0, sizeof(req)); 595 memcpy(req.cmd, &tcmd, 6); 596 req.cmdlen = 6; 597 req.timeout = 10000; 598 req.senselen = SENSEBUFLEN; 599 600 if (ioctl(fd, SCIOCCOMMAND, &req) == -1) { 601 err(1, "SCIOCCOMMAND"); 602 } 603 604 if (req.retsts == SCCMD_OK) { 605 break; 606 } else if (req.retsts == SCCMD_TIMEOUT) { 607 fprintf(stderr, "%s: SCSI command timed out", 608 dvname); 609 break; 610 } else if (req.retsts == SCCMD_BUSY) { 611 fprintf(stderr, "%s: device is busy", 612 dvname); 613 break; 614 } else if (req.retsts != SCCMD_SENSE) { 615 fprintf(stderr, 616 "%s: device had unknown status %x", dvname, 617 req.retsts); 618 break; 619 } 620 memcpy(&sense, req.sense, sizeof(sense)); 621 if (sense.sks.sks_bytes[0] & SSD_SKSV) { 622 j = (sense.sks.sks_bytes[1] << 8) | 623 (sense.sks.sks_bytes[2]); 624 if (j >= complete[i]) { 625 printf(".%d0%%.", ++i); 626 fflush(stdout); 627 } 628 } 629 sleep(10); 630 } while (SSD_SENSE_KEY(sense.flags) == SKEY_NOT_READY); 631 printf(".100%%..done.\n"); 632 } 633 return; 634} 635 636/* 637 * device_identify: 638 * 639 * Display the identity of the device, including it's SCSI bus, 640 * target, lun, and it's vendor/product/revision information. 641 */ 642void 643device_identify(int argc, char *argv[]) 644{ 645 struct scsipi_inquiry_data inqbuf; 646 struct scsipi_inquiry cmd; 647 648 /* x4 in case every character is escaped, +1 for NUL. */ 649 char vendor[(sizeof(inqbuf.vendor) * 4) + 1], 650 product[(sizeof(inqbuf.product) * 4) + 1], 651 revision[(sizeof(inqbuf.revision) * 4) + 1]; 652 653 /* No arguments. */ 654 if (argc != 0) 655 usage(); 656 657 memset(&cmd, 0, sizeof(cmd)); 658 memset(&inqbuf, 0, sizeof(inqbuf)); 659 660 cmd.opcode = INQUIRY; 661 cmd.length = sizeof(inqbuf); 662 663 scsi_command(fd, &cmd, sizeof(cmd), &inqbuf, sizeof(inqbuf), 664 10000, SCCMD_READ); 665 666 scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor, 667 sizeof(inqbuf.vendor)); 668 scsi_strvis(product, sizeof(product), inqbuf.product, 669 sizeof(inqbuf.product)); 670 scsi_strvis(revision, sizeof(revision), inqbuf.revision, 671 sizeof(inqbuf.revision)); 672 673 printf("%s: scsibus%d target %d lun %d <%s, %s, %s>\n", 674 dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target, 675 dvaddr.addr.scsi.lun, vendor, product, revision); 676 677 return; 678} 679 680/* 681 * device_reassign: 682 * 683 * Reassign bad blocks on a direct access device. 684 */ 685void 686device_reassign(int argc, char *argv[]) 687{ 688 struct scsi_reassign_blocks cmd; 689 struct scsi_reassign_blocks_data *data; 690 size_t dlen; 691 u_int32_t blkno; 692 int i; 693 char *cp; 694 695 /* We get a list of block numbers. */ 696 if (argc < 1) 697 usage(); 698 699 /* 700 * Allocate the reassign blocks descriptor. The 4 comes from the 701 * size of the block address in the defect descriptor. 702 */ 703 dlen = sizeof(struct scsi_reassign_blocks_data) + ((argc - 1) * 4); 704 data = malloc(dlen); 705 if (data == NULL) 706 errx(1, "unable to allocate defect descriptor"); 707 memset(data, 0, dlen); 708 709 cmd.opcode = SCSI_REASSIGN_BLOCKS; 710 cmd.byte2 = 0; 711 cmd.unused[0] = 0; 712 cmd.unused[1] = 0; 713 cmd.unused[2] = 0; 714 cmd.control = 0; 715 716 /* Defect descriptor length. */ 717 _lto2b(argc * 4, data->length); 718 719 /* Build the defect descriptor list. */ 720 for (i = 0; i < argc; i++) { 721 blkno = strtoul(argv[i], &cp, 10); 722 if (*cp != '\0') 723 errx(1, "invalid block number: %s", argv[i]); 724 _lto4b(blkno, data->defect_descriptor[i].dlbaddr); 725 } 726 727 scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_WRITE); 728 729 free(data); 730 return; 731} 732 733/* 734 * device_release: 735 * 736 * Issue a RELEASE command to a SCSI device. 737 */ 738#ifndef SCSI_RELEASE 739#define SCSI_RELEASE 0x17 740#endif 741void 742device_release(int argc, char *argv[]) 743{ 744 struct scsi_test_unit_ready cmd; /* close enough */ 745 746 /* No arguments. */ 747 if (argc != 0) 748 usage(); 749 750 memset(&cmd, 0, sizeof(cmd)); 751 752 cmd.opcode = SCSI_RELEASE; 753 754 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 755 756 return; 757} 758 759 760 761/* 762 * device_reserve: 763 * 764 * Issue a RESERVE command to a SCSI device. 765 */ 766#ifndef SCSI_RESERVE 767#define SCSI_RESERVE 0x16 768#endif 769void 770device_reserve(int argc, char *argv[]) 771{ 772 struct scsi_test_unit_ready cmd; /* close enough */ 773 774 /* No arguments. */ 775 if (argc != 0) 776 usage(); 777 778 memset(&cmd, 0, sizeof(cmd)); 779 780 cmd.opcode = SCSI_RESERVE; 781 782 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 783 784 return; 785} 786 787/* 788 * device_reset: 789 * 790 * Issue a reset to a SCSI device. 791 */ 792void 793device_reset(int argc, char *argv[]) 794{ 795 796 /* No arguments. */ 797 if (argc != 0) 798 usage(); 799 800 if (ioctl(fd, SCIOCRESET, NULL) != 0) 801 err(1, "SCIOCRESET"); 802 803 return; 804} 805 806/* 807 * device_debug: 808 * 809 * Set debug level to a SCSI device. 810 * scsipi will print anything iff SCSIPI_DEBUG set in config. 811 */ 812void 813device_debug(int argc, char *argv[]) 814{ 815 int lvl; 816 817 if (argc < 1) 818 usage(); 819 820 lvl = atoi(argv[0]); 821 822 if (ioctl(fd, SCIOCDEBUG, &lvl) != 0) 823 err(1, "SCIOCDEBUG"); 824 825 return; 826} 827 828/* 829 * device_getcache: 830 * 831 * Get the caching parameters for a SCSI disk. 832 */ 833void 834device_getcache(int argc, char *argv[]) 835{ 836 struct { 837 struct scsi_mode_parameter_header_6 header; 838 struct scsi_general_block_descriptor blk_desc; 839 struct page_caching caching_params; 840 } data; 841 842 /* No arguments. */ 843 if (argc != 0) 844 usage(); 845 846 scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data)); 847 848 if ((data.caching_params.flags & (CACHING_RCD|CACHING_WCE)) == 849 CACHING_RCD) 850 printf("%s: no caches enabled\n", dvname); 851 else { 852 printf("%s: read cache %senabled\n", dvname, 853 (data.caching_params.flags & CACHING_RCD) ? "not " : ""); 854 printf("%s: write-back cache %senabled\n", dvname, 855 (data.caching_params.flags & CACHING_WCE) ? "" : "not "); 856 } 857 printf("%s: caching parameters are %ssavable\n", dvname, 858 (data.caching_params.pg_code & PGCODE_PS) ? "" : "not "); 859} 860 861/* 862 * device_setcache: 863 * 864 * Set cache enables for a SCSI disk. 865 */ 866void 867device_setcache(int argc, char *argv[]) 868{ 869 struct { 870 struct scsi_mode_parameter_header_6 header; 871 struct scsi_general_block_descriptor blk_desc; 872 struct page_caching caching_params; 873 } data; 874 int dlen; 875 u_int8_t flags, byte2; 876 877 if (argc > 2 || argc == 0) 878 usage(); 879 880 flags = 0; 881 byte2 = 0; 882 if (strcmp(argv[0], "none") == 0) 883 flags = CACHING_RCD; 884 else if (strcmp(argv[0], "r") == 0) 885 flags = 0; 886 else if (strcmp(argv[0], "w") == 0) 887 flags = CACHING_RCD|CACHING_WCE; 888 else if (strcmp(argv[0], "rw") == 0) 889 flags = CACHING_WCE; 890 else 891 usage(); 892 893 if (argc == 2) { 894 if (strcmp(argv[1], "save") == 0) 895 byte2 = SMS_SP; 896 else 897 usage(); 898 } 899 900 scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data)); 901 902 data.caching_params.pg_code &= PGCODE_MASK; 903 data.caching_params.flags = 904 (data.caching_params.flags & ~(CACHING_RCD|CACHING_WCE)) | flags; 905 906 data.caching_params.cache_segment_size[0] = 0; 907 data.caching_params.cache_segment_size[1] = 0; 908 909 data.header.data_length = 0; 910 911 dlen = sizeof(data.header) + sizeof(data.blk_desc) + 2 + 912 data.caching_params.pg_length; 913 914 scsi_mode_select(fd, byte2, &data, dlen); 915} 916 917/* 918 * device_flushcache: 919 * 920 * Issue a FLUSH CACHE command to a SCSI device. 921 */ 922#ifndef SCSI_FLUSHCACHE 923#define SCSI_FLUSHCACHE 0x35 924#endif 925void 926device_flushcache(int argc, char *argv[]) 927{ 928 struct scsi_test_unit_ready cmd; /* close enough */ 929 930 /* No arguments. */ 931 if (argc != 0) 932 usage(); 933 934 memset(&cmd, 0, sizeof(cmd)); 935 936 cmd.opcode = SCSI_FLUSHCACHE; 937 938 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 939 940 return; 941} 942 943/* 944 * device_setspeed: 945 * 946 * Set rotation speed to a CD/DVD drive. 947 */ 948void 949device_setspeed(int argc, char *argv[]) 950{ 951 u_char cmd[11]; 952 u_char pd[28]; 953 u_int32_t speed; 954 955 if (argc != 1) 956 usage(); 957 958 speed = atoi(argv[0]) * 177; 959 960 memset(&pd, 0, sizeof(pd)); 961 if (speed == 0) 962 pd[0] = 4; /* restore drive defaults */ 963 pd[8] = 0xff; 964 pd[9] = 0xff; 965 pd[10] = 0xff; 966 pd[11] = 0xff; 967 pd[12] = pd[20] = (speed >> 24) & 0xff; 968 pd[13] = pd[21] = (speed >> 16) & 0xff; 969 pd[14] = pd[22] = (speed >> 8) & 0xff; 970 pd[15] = pd[23] = speed & 0xff; 971 pd[18] = pd[26] = 1000 >> 8; 972 pd[19] = pd[27] = 1000 & 0xff; 973 974 memset(&cmd, 0, sizeof(cmd)); 975 cmd[0] = 0xb6; 976 cmd[10] = sizeof(pd); 977 978 scsi_command(fd, &cmd, sizeof(cmd), pd, sizeof(pd), 10000, SCCMD_WRITE); 979 980 return; 981} 982 983/* 984 * device_prevent: 985 * 986 * Issue a prevent to a SCSI device. 987 */ 988void 989device_prevent(int argc, char *argv[]) 990{ 991 struct scsi_prevent_allow_medium_removal cmd; 992 993 /* No arguments. */ 994 if (argc != 0) 995 usage(); 996 997 memset(&cmd, 0, sizeof(cmd)); 998 999 cmd.opcode = SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL; 1000 cmd.how = SPAMR_PREVENT_DT; /* XXX SMAMR_PREVENT_ALL? */ 1001 1002 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 1003 1004 return; 1005} 1006 1007/* 1008 * device_allow: 1009 * 1010 * Issue a stop to a SCSI device. 1011 */ 1012void 1013device_allow(int argc, char *argv[]) 1014{ 1015 struct scsi_prevent_allow_medium_removal cmd; 1016 1017 /* No arguments. */ 1018 if (argc != 0) 1019 usage(); 1020 1021 memset(&cmd, 0, sizeof(cmd)); 1022 1023 cmd.opcode = SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL; 1024 cmd.how = SPAMR_ALLOW; 1025 1026 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 1027 1028 return; 1029} 1030 1031/* 1032 * device_start: 1033 * 1034 * Issue a start to a SCSI device. 1035 */ 1036void 1037device_start(int argc, char *argv[]) 1038{ 1039 struct scsipi_start_stop cmd; 1040 1041 /* No arguments. */ 1042 if (argc != 0) 1043 usage(); 1044 1045 memset(&cmd, 0, sizeof(cmd)); 1046 1047 cmd.opcode = START_STOP; 1048 cmd.how = SSS_START; 1049 1050 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 30000, 0); 1051 1052 return; 1053} 1054 1055/* 1056 * device_stop: 1057 * 1058 * Issue a stop to a SCSI device. 1059 */ 1060void 1061device_stop(int argc, char *argv[]) 1062{ 1063 struct scsipi_start_stop cmd; 1064 1065 /* No arguments. */ 1066 if (argc != 0) 1067 usage(); 1068 1069 memset(&cmd, 0, sizeof(cmd)); 1070 1071 cmd.opcode = START_STOP; 1072 cmd.how = SSS_STOP; 1073 1074 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 30000, 0); 1075 1076 return; 1077} 1078 1079/* 1080 * device_tur: 1081 * 1082 * Issue a TEST UNIT READY to a SCSI device. 1083 */ 1084void 1085device_tur(int argc, char *argv[]) 1086{ 1087 struct scsi_test_unit_ready cmd; 1088 1089 /* No arguments. */ 1090 if (argc != 0) 1091 usage(); 1092 1093 memset(&cmd, 0, sizeof(cmd)); 1094 1095 cmd.opcode = SCSI_TEST_UNIT_READY; 1096 1097 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 1098 1099 return; 1100} 1101 1102/* 1103 * BUS COMMANDS 1104 */ 1105 1106/* 1107 * bus_reset: 1108 * 1109 * Issue a reset to a SCSI bus. 1110 */ 1111void 1112bus_reset(int argc, char *argv[]) 1113{ 1114 1115 /* No arguments. */ 1116 if (argc != 0) 1117 usage(); 1118 1119 if (ioctl(fd, SCBUSIORESET, NULL) != 0) 1120 err(1, "SCBUSIORESET"); 1121 1122 return; 1123} 1124 1125/* 1126 * bus_scan: 1127 * 1128 * Rescan a SCSI bus for new devices. 1129 */ 1130void 1131bus_scan(int argc, char *argv[]) 1132{ 1133 struct scbusioscan_args args; 1134 char *cp; 1135 1136 /* Must have two args: target lun */ 1137 if (argc != 2) 1138 usage(); 1139 1140 if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0) 1141 args.sa_target = -1; 1142 else { 1143 args.sa_target = strtol(argv[0], &cp, 10); 1144 if (*cp != '\0' || args.sa_target < 0) 1145 errx(1, "invalid target: %s", argv[0]); 1146 } 1147 1148 if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0) 1149 args.sa_lun = -1; 1150 else { 1151 args.sa_lun = strtol(argv[1], &cp, 10); 1152 if (*cp != '\0' || args.sa_lun < 0) 1153 errx(1, "invalid lun: %s", argv[1]); 1154 } 1155 1156 if (ioctl(fd, SCBUSIOSCAN, &args) != 0) 1157 err(1, "SCBUSIOSCAN"); 1158 1159 return; 1160} 1161 1162/* 1163 * bus_detach: 1164 * 1165 * detach SCSI devices from a bus. 1166 */ 1167void 1168bus_detach(int argc, char *argv[]) 1169{ 1170 struct scbusiodetach_args args; 1171 char *cp; 1172 1173 /* Must have two args: target lun */ 1174 if (argc != 2) 1175 usage(); 1176 1177 if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0) 1178 args.sa_target = -1; 1179 else { 1180 args.sa_target = strtol(argv[0], &cp, 10); 1181 if (*cp != '\0' || args.sa_target < 0) 1182 errx(1, "invalid target: %s", argv[0]); 1183 } 1184 1185 if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0) 1186 args.sa_lun = -1; 1187 else { 1188 args.sa_lun = strtol(argv[1], &cp, 10); 1189 if (*cp != '\0' || args.sa_lun < 0) 1190 errx(1, "invalid lun: %s", argv[1]); 1191 } 1192 1193 if (ioctl(fd, SCBUSIODETACH, &args) != 0) 1194 err(1, "SCBUSIODETACH"); 1195 1196 return; 1197} 1198