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