Home | History | Annotate | Line # | Download | only in scsictl
scsictl.c revision 1.9
      1 /*	$NetBSD: scsictl.c,v 1.9 1999/10/04 17:10:56 mycroft 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 extern const char *__progname;		/* from crt0.o */
     80 
     81 void	device_format __P((int, char *[]));
     82 void	device_identify __P((int, char *[]));
     83 void	device_reassign __P((int, char *[]));
     84 void	device_reset __P((int, char *[]));
     85 
     86 struct command device_commands[] = {
     87 	{ "format",	"",			device_format },
     88 	{ "identify",	"",			device_identify },
     89 	{ "reassign",	"blkno [blkno [...]]",	device_reassign },
     90 	{ "reset",	"",			device_reset },
     91 	{ NULL,		NULL,			NULL },
     92 };
     93 
     94 void	bus_reset __P((int, char *[]));
     95 void	bus_scan __P((int, char *[]));
     96 
     97 struct command bus_commands[] = {
     98 	{ "reset",	"",			bus_reset },
     99 	{ "scan",	"target lun",		bus_scan },
    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\n",
    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 	    __progname);
    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 scsi_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, 60000, 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\n", 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\n", 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\n", argv[1]);
    410 	}
    411 
    412 	if (ioctl(fd, SCBUSIOSCAN, &args) != 0)
    413 		err(1, "SCBUSIOSCAN");
    414 
    415 	return;
    416 }
    417