1 /* $NetBSD: scsictl.c,v 1.4 1998/11/12 01:16:09 thorpej 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 void (*cmd_func) __P((int, char *[])); 66 }; 67 68 int main __P((int, char *[])); 69 void usage __P((void)); 70 71 int fd; /* file descriptor for device */ 72 const char *dvname; /* device name */ 73 char dvname_store[MAXPATHLEN]; /* for opendisk(3) */ 74 const char *cmdname; /* command user issued */ 75 struct scsi_addr dvaddr; /* SCSI device's address */ 76 77 extern const char *__progname; /* from crt0.o */ 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", device_reassign }, 88 { "reset", device_reset }, 89 { 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", bus_scan }, 98 { 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 } 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\n", 157 commands == bus_commands ? "bus" : "device", cmdname); 158 159 (*commands[i].cmd_func)(argc, argv); 160 exit(0); 161 } 162 163 void 164 usage() 165 { 166 167 fprintf(stderr, "usage: %s device command [arg [...]]\n", 168 __progname); 169 exit(1); 170 } 171 172 /* 173 * DEVICE COMMANDS 174 */ 175 176 /* 177 * device_format: 178 * 179 * Format a direct access device. 180 * 181 * XXX Does not handle defect list management or geometry settings. 182 */ 183 void 184 device_format(argc, argv) 185 int argc; 186 char *argv[]; 187 { 188 struct scsi_format_unit cmd; 189 struct { 190 struct scsi_mode_header header; 191 struct scsi_blk_desc blk_desc; 192 struct page_disk_format format_page; 193 } data; 194 195 /* No arguments. */ 196 if (argc != 0) 197 goto usage; 198 199 /* 200 * Get the DISK FORMAT mode page. SCSI-2 recommends specifying the 201 * interleave read from this page in the FORMAT UNIT command. 202 */ 203 scsi_mode_sense(fd, 0x03, 0x00, &data, sizeof(data)); 204 205 memset(&cmd, 0, sizeof(cmd)); 206 207 cmd.opcode = SCSI_FORMAT_UNIT; 208 memcpy(cmd.interleave, data.format_page.interleave, 209 sizeof(cmd.interleave)); 210 211 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 60000, 0); 212 213 return; 214 215 usage: 216 fprintf(stderr, "usage: %s device %s\n", __progname, cmdname); 217 exit(1); 218 } 219 220 /* 221 * device_identify: 222 * 223 * Display the identity of the device, including it's SCSI bus, 224 * target, lun, and it's vendor/product/revision information. 225 */ 226 void 227 device_identify(argc, argv) 228 int argc; 229 char *argv[]; 230 { 231 struct scsipi_inquiry_data inqbuf; 232 struct scsipi_inquiry cmd; 233 234 /* x4 in case every character is escaped, +1 for NUL. */ 235 char vendor[(sizeof(inqbuf.vendor) * 4) + 1], 236 product[(sizeof(inqbuf.product) * 4) + 1], 237 revision[(sizeof(inqbuf.revision) * 4) + 1]; 238 239 /* No arguments. */ 240 if (argc != 0) 241 goto usage; 242 243 memset(&cmd, 0, sizeof(cmd)); 244 memset(&inqbuf, 0, sizeof(inqbuf)); 245 246 cmd.opcode = INQUIRY; 247 cmd.length = sizeof(inqbuf); 248 249 scsi_command(fd, &cmd, sizeof(cmd), &inqbuf, sizeof(inqbuf), 250 10000, SCCMD_READ); 251 252 scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor, 253 sizeof(inqbuf.vendor)); 254 scsi_strvis(product, sizeof(product), inqbuf.product, 255 sizeof(inqbuf.product)); 256 scsi_strvis(revision, sizeof(revision), inqbuf.revision, 257 sizeof(inqbuf.revision)); 258 259 printf("%s: scsibus%d target %d lun %d <%s, %s, %s>\n", 260 dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target, 261 dvaddr.addr.scsi.lun, vendor, product, revision); 262 263 return; 264 265 usage: 266 fprintf(stderr, "usage: %s device %s\n", __progname, cmdname); 267 exit(1); 268 } 269 270 /* 271 * device_reassign: 272 * 273 * Reassign bad blocks on a direct access device. 274 */ 275 void 276 device_reassign(argc, argv) 277 int argc; 278 char *argv[]; 279 { 280 struct scsi_reassign_blocks cmd; 281 struct scsi_reassign_blocks_data *data; 282 size_t dlen; 283 u_int32_t blkno; 284 int i; 285 char *cp; 286 287 /* We get a list of block numbers. */ 288 if (argc < 1) 289 goto usage; 290 291 /* 292 * Allocate the reassign blocks descriptor. The 4 comes from the 293 * size of the block address in the defect descriptor. 294 */ 295 dlen = sizeof(struct scsi_reassign_blocks_data) + ((argc - 1) * 4); 296 data = malloc(dlen); 297 if (data == NULL) 298 errx(1, "unable to allocate defect descriptor"); 299 memset(data, 0, dlen); 300 301 cmd.opcode = SCSI_REASSIGN_BLOCKS; 302 303 /* Defect descriptor length. */ 304 _lto2l(argc * 4, data->length); 305 306 /* Build the defect descriptor list. */ 307 for (i = 0; i < argc; i++) { 308 blkno = strtoul(argv[i], &cp, 10); 309 if (*cp != '\0') 310 errx(1, "invalid block number: %s\n", argv[i]); 311 _lto4l(blkno, data->defect_descriptor[i].dlbaddr); 312 } 313 314 scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_WRITE); 315 316 free(data); 317 return; 318 319 usage: 320 fprintf(stderr, "usage: %s device %s blkno [blkno [...]]\n", 321 __progname, cmdname); 322 exit(1); 323 } 324 325 /* 326 * device_reset: 327 * 328 * Issue a reset to a SCSI device. 329 */ 330 void 331 device_reset(argc, argv) 332 int argc; 333 char *argv[]; 334 { 335 336 /* No arguments. */ 337 if (argc != 0) 338 goto usage; 339 340 if (ioctl(fd, SCIOCRESET, NULL) != 0) 341 err(1, "SCIOCRESET"); 342 343 return; 344 345 usage: 346 fprintf(stderr, "usage: %s device %s\n", __progname, cmdname); 347 exit(1); 348 } 349 350 /* 351 * BUS COMMANDS 352 */ 353 354 /* 355 * bus_reset: 356 * 357 * Issue a reset to a SCSI bus. 358 */ 359 void 360 bus_reset(argc, argv) 361 int argc; 362 char *argv[]; 363 { 364 365 /* No arguments. */ 366 if (argc != 0) 367 goto usage; 368 369 if (ioctl(fd, SCBUSIORESET, NULL) != 0) 370 err(1, "SCBUSIORESET"); 371 372 return; 373 374 usage: 375 fprintf(stderr, "usage: %s device %s\n", __progname, cmdname); 376 exit(1); 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 goto usage; 395 396 if (strcmp(argv[0], "any") == 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\n", argv[0]); 402 } 403 404 if (strcmp(argv[1], "any") == 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\n", argv[1]); 410 } 411 412 if (ioctl(fd, SCBUSIOSCAN, &args) != 0) 413 err(1, "SCBUSIOSCAN"); 414 415 return; 416 417 usage: 418 fprintf(stderr, "usage: %s device %s target lun\n", __progname, 419 cmdname); 420 fprintf(stderr, " use `any' to wildcard target or lun\n"); 421 exit(1); 422 } 423