1 /* $NetBSD: scsictl.c,v 1.15 2002/04/14 03:24:42 tsutsui 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 63 struct command { 64 const char *cmd_name; 65 const char *arg_names; 66 void (*cmd_func) __P((int, char *[])); 67 }; 68 69 int main __P((int, char *[])); 70 void usage __P((void)); 71 72 int fd; /* file descriptor for device */ 73 const char *dvname; /* device name */ 74 char dvname_store[MAXPATHLEN]; /* for opendisk(3) */ 75 const char *cmdname; /* command user issued */ 76 const char *argnames; /* helpstring: expected arguments */ 77 struct scsi_addr dvaddr; /* SCSI device's address */ 78 79 void device_format __P((int, char *[])); 80 void device_identify __P((int, char *[])); 81 void device_reassign __P((int, char *[])); 82 void device_reset __P((int, char *[])); 83 84 struct 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 92 void bus_reset __P((int, char *[])); 93 void bus_scan __P((int, char *[])); 94 void bus_detach __P((int, char *[])); 95 96 struct 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 103 int 104 main(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 167 void 168 usage() 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 */ 199 void 200 device_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, 6 * 60 * 60 * 1000, 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 */ 238 void 239 device_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 */ 283 void 284 device_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 */ 338 void 339 device_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 */ 363 void 364 bus_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 */ 384 void 385 bus_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 */ 423 void 424 bus_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