Home | History | Annotate | Line # | Download | only in scsictl
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 
     62 struct command {
     63 	const char *cmd_name;
     64 	void (*cmd_func) __P((int, char *[]));
     65 };
     66 
     67 int	main __P((int, char *[]));
     68 void	usage __P((void));
     69 
     70 int	fd;				/* file descriptor for device */
     71 const	char *dvname;			/* device name */
     72 char	dvname_store[MAXPATHLEN];	/* for opendisk(3) */
     73 const	char *cmdname;			/* command user issued */
     74 struct	scsi_addr dvaddr;		/* SCSI device's address */
     75 
     76 extern const char *__progname;		/* from crt0.o */
     77 
     78 void	device_identify __P((int, char *[]));
     79 void	device_reassign __P((int, char *[]));
     80 void	device_reset __P((int, char *[]));
     81 
     82 struct command device_commands[] = {
     83 	{ "identify",	device_identify },
     84 	{ "reassign",	device_reassign },
     85 	{ "reset",	device_reset },
     86 	{ NULL,		NULL },
     87 };
     88 
     89 void	bus_reset __P((int, char *[]));
     90 void	bus_scan __P((int, char *[]));
     91 
     92 struct command bus_commands[] = {
     93 	{ "reset",	bus_reset },
     94 	{ "scan",	bus_scan },
     95 	{ NULL,		NULL },
     96 };
     97 
     98 int
     99 main(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 
    160 void
    161 usage()
    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  */
    179 void
    180 device_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  */
    228 void
    229 device_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  */
    283 void
    284 device_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  */
    312 void
    313 bus_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  */
    337 void
    338 bus_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