scsictl.c revision 1.14
1/* $NetBSD: scsictl.c,v 1.14 2001/07/18 20:36:36 bouyer 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_reset __P((int, char *[])); 83 84struct command device_commands[] = { 85 { "format", "", device_format }, 86 { "identify", "", device_identify }, 87 { "reassign", "blkno [blkno [...]]", device_reassign }, 88 { "reset", "", device_reset }, 89 { NULL, NULL, NULL }, 90}; 91 92void bus_reset __P((int, char *[])); 93void bus_scan __P((int, char *[])); 94void bus_detach __P((int, char *[])); 95 96struct command bus_commands[] = { 97 { "reset", "", bus_reset }, 98 { "scan", "target lun", bus_scan }, 99 { "detach", "target lun", bus_detach }, 100 { NULL, NULL, NULL }, 101}; 102 103int 104main(argc, argv) 105 int argc; 106 char *argv[]; 107{ 108 struct command *commands; 109 int i; 110 111 /* Must have at least: device command */ 112 if (argc < 3) 113 usage(); 114 115 /* Skip program name, get and skip device name and command. */ 116 dvname = argv[1]; 117 cmdname = argv[2]; 118 argv += 3; 119 argc -= 3; 120 121 /* 122 * Open the device and determine if it's a scsibus or an actual 123 * device. Devices respond to the SCIOCIDENTIFY ioctl. 124 */ 125 fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0); 126 if (fd == -1) { 127 if (errno == ENOENT) { 128 /* 129 * Device doesn't exist. Probably trying to open 130 * a device which doesn't use disk semantics for 131 * device name. Try again, specifying "cooked", 132 * which leaves off the "r" in front of the device's 133 * name. 134 */ 135 fd = opendisk(dvname, O_RDWR, dvname_store, 136 sizeof(dvname_store), 1); 137 if (fd == -1) 138 err(1, "%s", dvname); 139 } else 140 err(1, "%s", dvname); 141 } 142 143 /* 144 * Point the dvname at the actual device name that opendisk() opened. 145 */ 146 dvname = dvname_store; 147 148 if (ioctl(fd, SCIOCIDENTIFY, &dvaddr) < 0) 149 commands = bus_commands; 150 else 151 commands = device_commands; 152 153 /* Look up and call the command. */ 154 for (i = 0; commands[i].cmd_name != NULL; i++) 155 if (strcmp(cmdname, commands[i].cmd_name) == 0) 156 break; 157 if (commands[i].cmd_name == NULL) 158 errx(1, "unknown %s command: %s", 159 commands == bus_commands ? "bus" : "device", cmdname); 160 161 argnames = commands[i].arg_names; 162 163 (*commands[i].cmd_func)(argc, argv); 164 exit(0); 165} 166 167void 168usage() 169{ 170 int i; 171 172 fprintf(stderr, "Usage: %s device command [arg [...]]\n", 173 getprogname()); 174 175 fprintf(stderr, " Commands pertaining to scsi devices:\n"); 176 for (i=0; device_commands[i].cmd_name != NULL; i++) 177 fprintf(stderr, "\t%s %s\n", device_commands[i].cmd_name, 178 device_commands[i].arg_names); 179 fprintf(stderr, " Commands pertaining to scsi busses:\n"); 180 for (i=0; bus_commands[i].cmd_name != NULL; i++) 181 fprintf(stderr, "\t%s %s\n", bus_commands[i].cmd_name, 182 bus_commands[i].arg_names); 183 fprintf(stderr, " Use `any' or `all' to wildcard target or lun\n"); 184 185 exit(1); 186} 187 188/* 189 * DEVICE COMMANDS 190 */ 191 192/* 193 * device_format: 194 * 195 * Format a direct access device. 196 * 197 * XXX Does not handle defect list management or geometry settings. 198 */ 199void 200device_format(argc, argv) 201 int argc; 202 char *argv[]; 203{ 204 struct scsi_format_unit cmd; 205 struct { 206 struct scsipi_mode_header header; 207 struct scsi_blk_desc blk_desc; 208 struct page_disk_format format_page; 209 } data; 210 211 /* No arguments. */ 212 if (argc != 0) 213 usage(); 214 215 /* 216 * Get the DISK FORMAT mode page. SCSI-2 recommends specifying the 217 * interleave read from this page in the FORMAT UNIT command. 218 */ 219 scsi_mode_sense(fd, 0x03, 0x00, &data, sizeof(data)); 220 221 memset(&cmd, 0, sizeof(cmd)); 222 223 cmd.opcode = SCSI_FORMAT_UNIT; 224 memcpy(cmd.interleave, data.format_page.interleave, 225 sizeof(cmd.interleave)); 226 227 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 21600000, 0); 228 229 return; 230} 231 232/* 233 * device_identify: 234 * 235 * Display the identity of the device, including it's SCSI bus, 236 * target, lun, and it's vendor/product/revision information. 237 */ 238void 239device_identify(argc, argv) 240 int argc; 241 char *argv[]; 242{ 243 struct scsipi_inquiry_data inqbuf; 244 struct scsipi_inquiry cmd; 245 246 /* x4 in case every character is escaped, +1 for NUL. */ 247 char vendor[(sizeof(inqbuf.vendor) * 4) + 1], 248 product[(sizeof(inqbuf.product) * 4) + 1], 249 revision[(sizeof(inqbuf.revision) * 4) + 1]; 250 251 /* No arguments. */ 252 if (argc != 0) 253 usage(); 254 255 memset(&cmd, 0, sizeof(cmd)); 256 memset(&inqbuf, 0, sizeof(inqbuf)); 257 258 cmd.opcode = INQUIRY; 259 cmd.length = sizeof(inqbuf); 260 261 scsi_command(fd, &cmd, sizeof(cmd), &inqbuf, sizeof(inqbuf), 262 10000, SCCMD_READ); 263 264 scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor, 265 sizeof(inqbuf.vendor)); 266 scsi_strvis(product, sizeof(product), inqbuf.product, 267 sizeof(inqbuf.product)); 268 scsi_strvis(revision, sizeof(revision), inqbuf.revision, 269 sizeof(inqbuf.revision)); 270 271 printf("%s: scsibus%d target %d lun %d <%s, %s, %s>\n", 272 dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target, 273 dvaddr.addr.scsi.lun, vendor, product, revision); 274 275 return; 276} 277 278/* 279 * device_reassign: 280 * 281 * Reassign bad blocks on a direct access device. 282 */ 283void 284device_reassign(argc, argv) 285 int argc; 286 char *argv[]; 287{ 288 struct scsi_reassign_blocks cmd; 289 struct scsi_reassign_blocks_data *data; 290 size_t dlen; 291 u_int32_t blkno; 292 int i; 293 char *cp; 294 295 /* We get a list of block numbers. */ 296 if (argc < 1) 297 usage(); 298 299 /* 300 * Allocate the reassign blocks descriptor. The 4 comes from the 301 * size of the block address in the defect descriptor. 302 */ 303 dlen = sizeof(struct scsi_reassign_blocks_data) + ((argc - 1) * 4); 304 data = malloc(dlen); 305 if (data == NULL) 306 errx(1, "unable to allocate defect descriptor"); 307 memset(data, 0, dlen); 308 309 cmd.opcode = SCSI_REASSIGN_BLOCKS; 310 cmd.byte2 = 0; 311 cmd.unused[0] = 0; 312 cmd.unused[1] = 0; 313 cmd.unused[2] = 0; 314 cmd.control = 0; 315 316 /* Defect descriptor length. */ 317 _lto2b(argc * 4, data->length); 318 319 /* Build the defect descriptor list. */ 320 for (i = 0; i < argc; i++) { 321 blkno = strtoul(argv[i], &cp, 10); 322 if (*cp != '\0') 323 errx(1, "invalid block number: %s", argv[i]); 324 _lto4b(blkno, data->defect_descriptor[i].dlbaddr); 325 } 326 327 scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_WRITE); 328 329 free(data); 330 return; 331} 332 333/* 334 * device_reset: 335 * 336 * Issue a reset to a SCSI device. 337 */ 338void 339device_reset(argc, argv) 340 int argc; 341 char *argv[]; 342{ 343 344 /* No arguments. */ 345 if (argc != 0) 346 usage(); 347 348 if (ioctl(fd, SCIOCRESET, NULL) != 0) 349 err(1, "SCIOCRESET"); 350 351 return; 352} 353 354/* 355 * BUS COMMANDS 356 */ 357 358/* 359 * bus_reset: 360 * 361 * Issue a reset to a SCSI bus. 362 */ 363void 364bus_reset(argc, argv) 365 int argc; 366 char *argv[]; 367{ 368 369 /* No arguments. */ 370 if (argc != 0) 371 usage(); 372 373 if (ioctl(fd, SCBUSIORESET, NULL) != 0) 374 err(1, "SCBUSIORESET"); 375 376 return; 377} 378 379/* 380 * bus_scan: 381 * 382 * Rescan a SCSI bus for new devices. 383 */ 384void 385bus_scan(argc, argv) 386 int argc; 387 char *argv[]; 388{ 389 struct scbusioscan_args args; 390 char *cp; 391 392 /* Must have two args: target lun */ 393 if (argc != 2) 394 usage(); 395 396 if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0) 397 args.sa_target = -1; 398 else { 399 args.sa_target = strtol(argv[0], &cp, 10); 400 if (*cp != '\0' || args.sa_target < 0) 401 errx(1, "invalid target: %s", argv[0]); 402 } 403 404 if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0) 405 args.sa_lun = -1; 406 else { 407 args.sa_lun = strtol(argv[1], &cp, 10); 408 if (*cp != '\0' || args.sa_lun < 0) 409 errx(1, "invalid lun: %s", argv[1]); 410 } 411 412 if (ioctl(fd, SCBUSIOSCAN, &args) != 0) 413 err(1, "SCBUSIOSCAN"); 414 415 return; 416} 417 418/* 419 * bus_detach: 420 * 421 * detach SCSI devices from a bus. 422 */ 423void 424bus_detach(argc, argv) 425 int argc; 426 char *argv[]; 427{ 428 struct scbusiodetach_args args; 429 char *cp; 430 431 /* Must have two args: target lun */ 432 if (argc != 2) 433 usage(); 434 435 if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0) 436 args.sa_target = -1; 437 else { 438 args.sa_target = strtol(argv[0], &cp, 10); 439 if (*cp != '\0' || args.sa_target < 0) 440 errx(1, "invalid target: %s\n", argv[0]); 441 } 442 443 if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0) 444 args.sa_lun = -1; 445 else { 446 args.sa_lun = strtol(argv[1], &cp, 10); 447 if (*cp != '\0' || args.sa_lun < 0) 448 errx(1, "invalid lun: %s\n", argv[1]); 449 } 450 451 if (ioctl(fd, SCBUSIODETACH, &args) != 0) 452 err(1, "SCBUSIODETACH"); 453 454 return; 455} 456