scsictl.c revision 1.3
1/* $NetBSD: scsictl.c,v 1.3 1998/10/17 05:08:27 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_disk.h> 58#include <dev/scsipi/scsipiconf.h> 59 60#include "extern.h" 61 62struct command { 63 const char *cmd_name; 64 void (*cmd_func) __P((int, char *[])); 65}; 66 67int main __P((int, char *[])); 68void usage __P((void)); 69 70int fd; /* file descriptor for device */ 71const char *dvname; /* device name */ 72char dvname_store[MAXPATHLEN]; /* for opendisk(3) */ 73const char *cmdname; /* command user issued */ 74struct scsi_addr dvaddr; /* SCSI device's address */ 75 76extern const char *__progname; /* from crt0.o */ 77 78void device_identify __P((int, char *[])); 79void device_reassign __P((int, char *[])); 80void device_reset __P((int, char *[])); 81 82struct command device_commands[] = { 83 { "identify", device_identify }, 84 { "reassign", device_reassign }, 85 { "reset", device_reset }, 86 { NULL, NULL }, 87}; 88 89void bus_reset __P((int, char *[])); 90void bus_scan __P((int, char *[])); 91 92struct command bus_commands[] = { 93 { "reset", bus_reset }, 94 { "scan", bus_scan }, 95 { NULL, NULL }, 96}; 97 98int 99main(argc, argv) 100 int argc; 101 char *argv[]; 102{ 103 struct command *commands; 104 int i; 105 106 /* Must have at least: device command */ 107 if (argc < 3) 108 usage(); 109 110 /* Skip program name, get and skip device name and command. */ 111 dvname = argv[1]; 112 cmdname = argv[2]; 113 argv += 3; 114 argc -= 3; 115 116 /* 117 * Open the device and determine if it's a scsibus or an actual 118 * device. Devices respond to the SCIOCIDENTIFY ioctl. 119 */ 120 fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0); 121 if (fd == -1) { 122 if (errno == ENOENT) { 123 /* 124 * Device doesn't exist. Probably trying to open 125 * a device which doesn't use disk semantics for 126 * device name. Try again, specifying "cooked", 127 * which leaves off the "r" in front of the device's 128 * name. 129 */ 130 fd = opendisk(dvname, O_RDWR, dvname_store, 131 sizeof(dvname_store), 1); 132 if (fd == -1) 133 err(1, "%s", dvname); 134 } 135 err(1, "%s", dvname); 136 } 137 138 /* 139 * Point the dvname at the actual device name that opendisk() opened. 140 */ 141 dvname = dvname_store; 142 143 if (ioctl(fd, SCIOCIDENTIFY, &dvaddr) < 0) 144 commands = bus_commands; 145 else 146 commands = device_commands; 147 148 /* Look up and call the command. */ 149 for (i = 0; commands[i].cmd_name != NULL; i++) 150 if (strcmp(cmdname, commands[i].cmd_name) == 0) 151 break; 152 if (commands[i].cmd_name == NULL) 153 errx(1, "unknown %s command: %s\n", 154 commands == bus_commands ? "bus" : "device", cmdname); 155 156 (*commands[i].cmd_func)(argc, argv); 157 exit(0); 158} 159 160void 161usage() 162{ 163 164 fprintf(stderr, "usage: %s device command [arg [...]]\n", 165 __progname); 166 exit(1); 167} 168 169/* 170 * DEVICE COMMANDS 171 */ 172 173/* 174 * device_identify: 175 * 176 * Display the identity of the device, including it's SCSI bus, 177 * target, lun, and it's vendor/product/revision information. 178 */ 179void 180device_identify(argc, argv) 181 int argc; 182 char *argv[]; 183{ 184 struct scsipi_inquiry_data inqbuf; 185 struct scsipi_inquiry cmd; 186 187 /* x4 in case every character is escaped, +1 for NUL. */ 188 char vendor[(sizeof(inqbuf.vendor) * 4) + 1], 189 product[(sizeof(inqbuf.product) * 4) + 1], 190 revision[(sizeof(inqbuf.revision) * 4) + 1]; 191 192 /* No arguments. */ 193 if (argc != 0) 194 goto usage; 195 196 memset(&cmd, 0, sizeof(cmd)); 197 memset(&inqbuf, 0, sizeof(inqbuf)); 198 199 cmd.opcode = INQUIRY; 200 cmd.length = sizeof(inqbuf); 201 202 scsi_command(fd, &cmd, sizeof(cmd), &inqbuf, sizeof(inqbuf), 203 10000, SCCMD_READ); 204 205 scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor, 206 sizeof(inqbuf.vendor)); 207 scsi_strvis(product, sizeof(product), inqbuf.product, 208 sizeof(inqbuf.product)); 209 scsi_strvis(revision, sizeof(revision), inqbuf.revision, 210 sizeof(inqbuf.revision)); 211 212 printf("%s: scsibus%d target %d lun %d <%s, %s, %s>\n", 213 dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target, 214 dvaddr.addr.scsi.lun, vendor, product, revision); 215 216 return; 217 218 usage: 219 fprintf(stderr, "usage: %s device %s\n", __progname, cmdname); 220 exit(1); 221} 222 223/* 224 * device_reassign: 225 * 226 * Reassign bad blocks on a direct access device. 227 */ 228void 229device_reassign(argc, argv) 230 int argc; 231 char *argv[]; 232{ 233 struct scsi_reassign_blocks cmd; 234 struct scsi_reassign_blocks_data *data; 235 size_t dlen; 236 u_int32_t blkno; 237 int i; 238 char *cp; 239 240 /* We get a list of block numbers. */ 241 if (argc < 1) 242 goto usage; 243 244 /* 245 * Allocate the reassign blocks descriptor. The 4 comes from the 246 * size of the block address in the defect descriptor. 247 */ 248 dlen = sizeof(struct scsi_reassign_blocks_data) + ((argc - 1) * 4); 249 data = malloc(dlen); 250 if (data == NULL) 251 errx(1, "unable to allocate defect descriptor"); 252 memset(data, 0, dlen); 253 254 cmd.opcode = SCSI_REASSIGN_BLOCKS; 255 256 /* Defect descriptor length. */ 257 _lto2l(argc * 4, data->length); 258 259 /* Build the defect descriptor list. */ 260 for (i = 0; i < argc; i++) { 261 blkno = strtoul(argv[i], &cp, 10); 262 if (*cp != '\0') 263 errx(1, "invalid block number: %s\n", argv[i]); 264 _lto4l(blkno, data->defect_descriptor[i].dlbaddr); 265 } 266 267 scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_WRITE); 268 269 free(data); 270 return; 271 272 usage: 273 fprintf(stderr, "usage: %s device %s blkno [blkno [...]]\n", 274 __progname, cmdname); 275 exit(1); 276} 277 278/* 279 * device_reset: 280 * 281 * Issue a reset to a SCSI device. 282 */ 283void 284device_reset(argc, argv) 285 int argc; 286 char *argv[]; 287{ 288 289 /* No arguments. */ 290 if (argc != 0) 291 goto usage; 292 293 if (ioctl(fd, SCIOCRESET, NULL) != 0) 294 err(1, "SCIOCRESET"); 295 296 return; 297 298 usage: 299 fprintf(stderr, "usage: %s device %s\n", __progname, cmdname); 300 exit(1); 301} 302 303/* 304 * BUS COMMANDS 305 */ 306 307/* 308 * bus_reset: 309 * 310 * Issue a reset to a SCSI bus. 311 */ 312void 313bus_reset(argc, argv) 314 int argc; 315 char *argv[]; 316{ 317 318 /* No arguments. */ 319 if (argc != 0) 320 goto usage; 321 322 if (ioctl(fd, SCBUSIORESET, NULL) != 0) 323 err(1, "SCBUSIORESET"); 324 325 return; 326 327 usage: 328 fprintf(stderr, "usage: %s device %s\n", __progname, cmdname); 329 exit(1); 330} 331 332/* 333 * bus_scan: 334 * 335 * Rescan a SCSI bus for new devices. 336 */ 337void 338bus_scan(argc, argv) 339 int argc; 340 char *argv[]; 341{ 342 struct scbusioscan_args args; 343 char *cp; 344 345 /* Must have two args: target lun */ 346 if (argc != 2) 347 goto usage; 348 349 if (strcmp(argv[0], "any") == 0) 350 args.sa_target = -1; 351 else { 352 args.sa_target = strtol(argv[0], &cp, 10); 353 if (*cp != '\0' || args.sa_target < 0) 354 errx(1, "invalid target: %s\n", argv[0]); 355 } 356 357 if (strcmp(argv[1], "any") == 0) 358 args.sa_lun = -1; 359 else { 360 args.sa_lun = strtol(argv[1], &cp, 10); 361 if (*cp != '\0' || args.sa_lun < 0) 362 errx(1, "invalid lun: %s\n", argv[1]); 363 } 364 365 if (ioctl(fd, SCBUSIOSCAN, &args) != 0) 366 err(1, "SCBUSIOSCAN"); 367 368 return; 369 370 usage: 371 fprintf(stderr, "usage: %s device %s target lun\n", __progname, 372 cmdname); 373 fprintf(stderr, " use `any' to wildcard target or lun\n"); 374 exit(1); 375} 376