Home | History | Annotate | Line # | Download | only in scsictl
scsictl.c revision 1.25
      1 /*	$NetBSD: scsictl.c,v 1.25 2005/01/07 02:08:34 ginsbach Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1998, 2002 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 #include <sys/cdefs.h>
     44 
     45 #ifndef lint
     46 __RCSID("$NetBSD: scsictl.c,v 1.25 2005/01/07 02:08:34 ginsbach Exp $");
     47 #endif
     48 
     49 
     50 #include <sys/param.h>
     51 #include <sys/ioctl.h>
     52 #include <sys/scsiio.h>
     53 #include <err.h>
     54 #include <errno.h>
     55 #include <fcntl.h>
     56 #include <limits.h>
     57 #include <stdio.h>
     58 #include <stdlib.h>
     59 #include <string.h>
     60 #include <unistd.h>
     61 #include <util.h>
     62 
     63 #include <dev/scsipi/scsipi_all.h>
     64 #include <dev/scsipi/scsi_all.h>
     65 #include <dev/scsipi/scsi_disk.h>
     66 #include <dev/scsipi/scsipiconf.h>
     67 
     68 #include "extern.h"
     69 
     70 struct command {
     71 	const char *cmd_name;
     72 	const char *arg_names;
     73 	void (*cmd_func) __P((int, char *[]));
     74 };
     75 
     76 int	main __P((int, char *[]));
     77 void	usage __P((void));
     78 
     79 int	fd;				/* file descriptor for device */
     80 const	char *dvname;			/* device name */
     81 char	dvname_store[MAXPATHLEN];	/* for opendisk(3) */
     82 const	char *cmdname;			/* command user issued */
     83 const	char *argnames;			/* helpstring: expected arguments */
     84 struct	scsi_addr dvaddr;		/* SCSI device's address */
     85 
     86 void	device_defects __P((int, char *[]));
     87 void	device_format __P((int, char *[]));
     88 void	device_identify __P((int, char *[]));
     89 void	device_reassign __P((int, char *[]));
     90 void	device_release __P((int, char *[]));
     91 void	device_reserve __P((int, char *[]));
     92 void	device_reset __P((int, char *[]));
     93 void	device_debug __P((int, char *[]));
     94 void	device_prevent __P((int, char *[]));
     95 void	device_allow __P((int, char *[]));
     96 void	device_start __P((int, char *[]));
     97 void	device_stop __P((int, char *[]));
     98 void	device_tur __P((int, char *[]));
     99 void	device_getcache __P((int, char *[]));
    100 void	device_setcache __P((int, char *[]));
    101 void	device_flushcache __P((int, char *[]));
    102 
    103 struct command device_commands[] = {
    104 	{ "defects",	"[primary] [grown] [block|byte|physical]",
    105 						device_defects },
    106 	{ "format",	"[blocksize [immediate]]", 	device_format },
    107 	{ "identify",	"",			device_identify },
    108 	{ "reassign",	"blkno [blkno [...]]",	device_reassign },
    109 	{ "release",	"",			device_release },
    110 	{ "reserve",	"",			device_reserve },
    111 	{ "reset",	"",			device_reset },
    112 	{ "debug",	"level",		device_debug },
    113 	{ "prevent",	"",			device_prevent },
    114 	{ "allow",	"",			device_allow },
    115 	{ "start",	"",			device_start },
    116 	{ "stop",	"",			device_stop },
    117 	{ "tur",	"",			device_tur },
    118 	{ "getcache",	"",			device_getcache },
    119 	{ "setcache",	"none|r|w|rw [save]",	device_setcache },
    120 	{ "flushcache",	"",			device_flushcache },
    121 	{ NULL,		NULL,			NULL },
    122 };
    123 
    124 void	bus_reset __P((int, char *[]));
    125 void	bus_scan __P((int, char *[]));
    126 void	bus_detach __P((int, char *[]));
    127 
    128 struct command bus_commands[] = {
    129 	{ "reset",	"",			bus_reset },
    130 	{ "scan",	"target lun",		bus_scan },
    131 	{ "detach",	"target lun",		bus_detach },
    132 	{ NULL,		NULL,				NULL },
    133 };
    134 
    135 int
    136 main(argc, argv)
    137 	int argc;
    138 	char *argv[];
    139 {
    140 	struct command *commands;
    141 	int i;
    142 
    143 	/* Must have at least: device command */
    144 	if (argc < 3)
    145 		usage();
    146 
    147 	/* Skip program name, get and skip device name and command. */
    148 	dvname = argv[1];
    149 	cmdname = argv[2];
    150 	argv += 3;
    151 	argc -= 3;
    152 
    153 	/*
    154 	 * Open the device and determine if it's a scsibus or an actual
    155 	 * device.  Devices respond to the SCIOCIDENTIFY ioctl.
    156 	 */
    157 	fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0);
    158 	if (fd == -1) {
    159 		if (errno == ENOENT) {
    160 			/*
    161 			 * Device doesn't exist.  Probably trying to open
    162 			 * a device which doesn't use disk semantics for
    163 			 * device name.  Try again, specifying "cooked",
    164 			 * which leaves off the "r" in front of the device's
    165 			 * name.
    166 			 */
    167 			fd = opendisk(dvname, O_RDWR, dvname_store,
    168 			    sizeof(dvname_store), 1);
    169 			if (fd == -1)
    170 				err(1, "%s", dvname);
    171 		} else
    172 			err(1, "%s", dvname);
    173 	}
    174 
    175 	/*
    176 	 * Point the dvname at the actual device name that opendisk() opened.
    177 	 */
    178 	dvname = dvname_store;
    179 
    180 	if (ioctl(fd, SCIOCIDENTIFY, &dvaddr) < 0)
    181 		commands = bus_commands;
    182 	else
    183 		commands = device_commands;
    184 
    185 	/* Look up and call the command. */
    186 	for (i = 0; commands[i].cmd_name != NULL; i++)
    187 		if (strcmp(cmdname, commands[i].cmd_name) == 0)
    188 			break;
    189 	if (commands[i].cmd_name == NULL)
    190 		errx(1, "unknown %s command: %s",
    191 		    commands == bus_commands ? "bus" : "device", cmdname);
    192 
    193 	argnames = commands[i].arg_names;
    194 
    195 	(*commands[i].cmd_func)(argc, argv);
    196 	exit(0);
    197 }
    198 
    199 void
    200 usage()
    201 {
    202 	int i;
    203 
    204 	fprintf(stderr, "usage: %s device command [arg [...]]\n",
    205 	    getprogname());
    206 
    207 	fprintf(stderr, "   Commands pertaining to scsi devices:\n");
    208 	for (i=0; device_commands[i].cmd_name != NULL; i++)
    209 		fprintf(stderr, "\t%s %s\n", device_commands[i].cmd_name,
    210 					    device_commands[i].arg_names);
    211 	fprintf(stderr, "   Commands pertaining to scsi busses:\n");
    212 	for (i=0; bus_commands[i].cmd_name != NULL; i++)
    213 		fprintf(stderr, "\t%s %s\n", bus_commands[i].cmd_name,
    214 					    bus_commands[i].arg_names);
    215 	fprintf(stderr, "   Use `any' or `all' to wildcard target or lun\n");
    216 
    217 	exit(1);
    218 }
    219 
    220 /*
    221  * DEVICE COMMANDS
    222  */
    223 
    224 /*
    225  * device_read_defect:
    226  *
    227  *	Read primary and/or growth defect list in physical or block
    228  *	format from a direct access device.
    229  *
    230  *	XXX Does not handle very large defect lists. Needs SCSI3 12
    231  *	    byte READ DEFECT DATA command.
    232  */
    233 
    234 void	print_bf_dd __P((union scsi_defect_descriptor *));
    235 void	print_bfif_dd __P((union scsi_defect_descriptor *));
    236 void	print_psf_dd __P((union scsi_defect_descriptor *));
    237 
    238 void
    239 device_defects(argc, argv)
    240 	int argc;
    241 	char *argv[];
    242 {
    243 	struct scsi_read_defect_data cmd;
    244 	struct scsi_read_defect_data_data *data;
    245 	size_t dlen;
    246 	int i, dlfmt = -1;
    247 	int defects;
    248 	char msg[256];
    249 	void (*pfunc) __P((union scsi_defect_descriptor *));
    250 #define RDD_P_G_MASK	0x18
    251 #define RDD_DLF_MASK	0x7
    252 
    253 	dlen = USHRT_MAX; 		/* XXX - this may not be enough room
    254 					 * for all of the defects.
    255 					 */
    256 	data = malloc(dlen);
    257 	if (data == NULL)
    258 		errx(1, "unable to allocate defect list");
    259 	memset(data, 0, dlen);
    260 	memset(&cmd, 0, sizeof(cmd));
    261 
    262 	/* determine which defect list(s) to read. */
    263 	for (i = 0; i < argc; i++) {
    264 		if (strncmp("primary", argv[i], 7) == 0) {
    265 			cmd.flags |= RDD_PRIMARY;
    266 			continue;
    267 		}
    268 		if (strncmp("grown", argv[i], 5) == 0) {
    269 			cmd.flags |= RDD_GROWN;
    270 			continue;
    271 		}
    272 		break;
    273 	}
    274 
    275 	/* no defect list sepecified, assume both. */
    276 	if ((cmd.flags & (RDD_PRIMARY|RDD_GROWN)) == 0)
    277 		cmd.flags |= (RDD_PRIMARY|RDD_GROWN);
    278 
    279 	/* list format option. */
    280 	if (i < argc) {
    281 		if (strncmp("block", argv[i], 5) == 0) {
    282 			cmd.flags |= RDD_BF;
    283 			dlfmt = RDD_BF;
    284 		}
    285 		else if (strncmp("byte", argv[i], 4) == 0) {
    286 			cmd.flags |= RDD_BFIF;
    287 			dlfmt = RDD_BFIF;
    288 		}
    289 		else if (strncmp("physical", argv[i], 4) == 0) {
    290 			cmd.flags |= RDD_PSF;
    291 			dlfmt = RDD_PSF;
    292 		}
    293 		else {
    294 			usage();
    295 		}
    296 	}
    297 
    298 	/*
    299 	 * no list format specified; since block format not
    300 	 * recommended use physical sector format as default.
    301 	 */
    302 	if (dlfmt < 0) {
    303 		cmd.flags |= RDD_PSF;
    304 		dlfmt = RDD_PSF;
    305 	}
    306 
    307 	cmd.opcode = SCSI_READ_DEFECT_DATA;
    308 	_lto2b(dlen, &cmd.length[0]);
    309 
    310 	scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_READ);
    311 
    312 	msg[0] = '\0';
    313 
    314 	/* is the defect list in the format asked for? */
    315 	if ((data->flags & RDD_DLF_MASK) != dlfmt) {
    316 		strcpy(msg, "\n\tnotice:"
    317 		       "requested defect list format not supported by device\n\n");
    318 		dlfmt = (data->flags & RDD_DLF_MASK);
    319 	}
    320 
    321 	if (data->flags & RDD_PRIMARY)
    322 		strcat(msg, "primary");
    323 
    324 	if (data->flags & RDD_GROWN) {
    325 		if (data->flags & RDD_PRIMARY)
    326 			strcat(msg, " and ");
    327 		strcat(msg, "grown");
    328 	}
    329 
    330 	strcat(msg, " defects");
    331 
    332 	if ((data->flags & RDD_P_G_MASK) == 0)
    333 		strcat(msg, ": none reported\n");
    334 
    335 
    336 	printf("%s: scsibus%d target %d lun %d %s",
    337 	       dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target,
    338 	       dvaddr.addr.scsi.lun, msg);
    339 
    340 	/* device did not return either defect list. */
    341 	if ((data->flags & RDD_P_G_MASK) == 0)
    342 		return;
    343 
    344 	switch (dlfmt) {
    345 	case RDD_BF:
    346 		defects = _2btol(data->length) /
    347 				sizeof(struct scsi_defect_descriptor_bf);
    348 		pfunc = print_bf_dd;
    349 		strcpy(msg, "block address\n"
    350 			    "-------------\n");
    351 		break;
    352 	case RDD_BFIF:
    353 		defects = _2btol(data->length) /
    354 				sizeof(struct scsi_defect_descriptor_bfif);
    355 		pfunc = print_bfif_dd;
    356 		strcpy(msg, "              bytes from\n"
    357 			    "cylinder head   index\n"
    358 			    "-------- ---- ----------\n");
    359 		break;
    360 	case RDD_PSF:
    361 		defects = _2btol(data->length) /
    362 				sizeof(struct scsi_defect_descriptor_psf);
    363 		pfunc = print_psf_dd;
    364 		strcpy(msg, "cylinder head   sector\n"
    365 			    "-------- ---- ----------\n");
    366 		break;
    367 	}
    368 
    369 	/* device did not return any defects. */
    370 	if (defects == 0) {
    371 		printf(": none\n");
    372 		return;
    373 	}
    374 
    375 	printf(": %d\n", defects);
    376 
    377 	/* print heading. */
    378 	printf("%s", msg);
    379 
    380 	/* print defect list. */
    381 	for (i = 0 ; i < defects; i++) {
    382 		pfunc(&data->defect_descriptor[i]);
    383 	}
    384 
    385 	free(data);
    386 	return;
    387 }
    388 
    389 /*
    390  * print_bf_dd:
    391  *
    392  *	Print a block format defect descriptor.
    393  */
    394 void
    395 print_bf_dd(dd)
    396 	union scsi_defect_descriptor *dd;
    397 {
    398 	u_int32_t block;
    399 
    400 	block = _4btol(dd->bf.block_address);
    401 
    402 	printf("%13u\n", block);
    403 }
    404 
    405 #define DEFECTIVE_TRACK	0xffffffff
    406 
    407 /*
    408  * print_bfif_dd:
    409  *
    410  *	Print a bytes from index format defect descriptor.
    411  */
    412 void
    413 print_bfif_dd(dd)
    414 	union scsi_defect_descriptor *dd;
    415 {
    416 	u_int32_t cylinder;
    417 	u_int32_t head;
    418 	u_int32_t bytes_from_index;
    419 
    420 	cylinder = _3btol(dd->bfif.cylinder);
    421 	head = dd->bfif.head;
    422 	bytes_from_index = _4btol(dd->bfif.bytes_from_index);
    423 
    424 	printf("%8u %4u ", cylinder, head);
    425 
    426 	if (bytes_from_index == DEFECTIVE_TRACK)
    427 		printf("entire track defective\n");
    428 	else
    429 		printf("%10u\n", bytes_from_index);
    430 }
    431 
    432 /*
    433  * print_psf_dd:
    434  *
    435  *	Print a physical sector format defect descriptor.
    436  */
    437 void
    438 print_psf_dd(dd)
    439 	union scsi_defect_descriptor *dd;
    440 {
    441 	u_int32_t cylinder;
    442 	u_int32_t head;
    443 	u_int32_t sector;
    444 
    445 	cylinder = _3btol(dd->psf.cylinder);
    446 	head = dd->psf.head;
    447 	sector = _4btol(dd->psf.sector);
    448 
    449 	printf("%8u %4u ", cylinder, head);
    450 
    451 	if (sector == DEFECTIVE_TRACK)
    452 		printf("entire track defective\n");
    453 	else
    454 		printf("%10u\n", sector);
    455 }
    456 
    457 /*
    458  * device_format:
    459  *
    460  *	Format a direct access device.
    461  */
    462 void
    463 device_format(argc, argv)
    464 	int argc;
    465 	char *argv[];
    466 {
    467 	u_int32_t blksize;
    468 	int i, j, immediate;
    469 #define	PC	(65536/10)
    470 	static int complete[] = {
    471 	    PC*1, PC*2, PC*3, PC*4, PC*5, PC*6, PC*7, PC*8, PC*9, 65536
    472 	};
    473 	char *cp, buffer[64];
    474 	struct scsipi_sense_data sense;
    475 	struct scsi_format_unit cmd;
    476 	struct {
    477 		struct scsi_format_unit_defect_list_header header;
    478 		/* optional initialization pattern */
    479 		/* optional defect list */
    480 	} dfl;
    481 	struct {
    482 		struct scsipi_mode_header header;
    483 		struct scsi_blk_desc blk_desc;
    484 		struct page_disk_format format_page;
    485 	} mode_page;
    486 	struct {
    487 		struct scsipi_mode_header header;
    488 		struct scsi_blk_desc blk_desc;
    489 	} data_select;
    490 
    491 
    492 	/* Blocksize is an optional argument. */
    493 	if (argc > 2)
    494 		usage();
    495 
    496 	/*
    497 	 * Loop doing Request Sense to clear any pending Unit Attention.
    498 	 *
    499 	 * Multiple conditions may exist on the drive which are returned
    500 	 * in priority order.
    501 	 */
    502 	for (i = 0; i < 8; i++) {
    503 		scsi_request_sense(fd, &sense, sizeof (sense));
    504 		if ((j = sense.flags & SSD_KEY) == SKEY_NO_SENSE)
    505 			break;
    506 	}
    507 	/*
    508 	 * Make sure we cleared any pending Unit Attention
    509 	 */
    510 	if (j != SKEY_NO_SENSE) {
    511 		cp = scsi_decode_sense((const unsigned char *) &sense, 2,
    512 		    buffer, sizeof (buffer));
    513 		errx(1, "failed to clean Unit Attention: %s", cp);
    514 	}
    515 
    516 	/*
    517 	 * Get the DISK FORMAT mode page.  SCSI-2 recommends specifying the
    518 	 * interleave read from this page in the FORMAT UNIT command.
    519 	 */
    520 	scsi_mode_sense(fd, 0x03, 0x00, &mode_page, sizeof(mode_page));
    521 
    522 	j = (mode_page.format_page.bytes_s[0] << 8) |
    523 	    (mode_page.format_page.bytes_s[1]);
    524 
    525 	if (j != DEV_BSIZE)
    526 		printf("current disk sector size: %hd\n", j);
    527 
    528 	memset(&cmd, 0, sizeof(cmd));
    529 
    530 	cmd.opcode = SCSI_FORMAT_UNIT;
    531 	memcpy(cmd.interleave, mode_page.format_page.interleave,
    532 	    sizeof(cmd.interleave));
    533 
    534 	/*
    535 	 * The blocksize on the device is only changed if the user
    536 	 * specified a new blocksize. If not specified the blocksize
    537 	 * used for the device will be the Default value in the device.
    538 	 * We don't specify the number of blocks since the format
    539 	 * command will always reformat the entire drive.  Also by
    540 	 * not specifying a block count the drive will reset the
    541 	 * block count to the maximum available after the format
    542 	 * completes if the blocksize was changed in the format.
    543 	 * Finally, the new disk geometry will not but updated on
    544 	 * the drive in permanent storage until _AFTER_ the format
    545 	 * completes successfully.
    546 	 */
    547 	if (argc > 0) {
    548 		blksize = strtoul(argv[0], &cp, 10);
    549 		if (*cp != '\0')
    550 			errx(1, "invalid block size: %s", argv[0]);
    551 
    552 		memset(&data_select, 0, sizeof(data_select));
    553 
    554 		data_select.header.blk_desc_len = sizeof(struct scsi_blk_desc);
    555 		/*
    556 		 * blklen in desc is 3 bytes with a leading reserved byte
    557 		 */
    558 		_lto4b(blksize, &data_select.blk_desc.reserved);
    559 
    560 		/*
    561 		 * Issue Mode Select to modify the device blocksize to be
    562 		 * used on the Format.  The modified device geometry will
    563 		 * be stored as Current and Saved Page 3 parameters when
    564 		 * the Format completes.
    565 		 */
    566 		scsi_mode_select(fd, 0, &data_select, sizeof(data_select));
    567 
    568 		/*
    569 		 * Since user specified a specific block size make sure it
    570 		 * gets stored in the device when the format completes.
    571 		 *
    572 		 * Also scrub the defect list back to the manufacturers
    573 		 * original.
    574 		 */
    575 		cmd.flags = SFU_CMPLST | SFU_FMTDATA;
    576 	}
    577 
    578 	memset(&dfl, 0, sizeof(dfl));
    579 
    580 	if (argc > 1 && strncmp(argv[1], "imm", 3) == 0) {
    581 		/*
    582 		 * Signal target for an immediate return from Format.
    583 		 *
    584 		 * We'll poll for completion status.
    585 		 */
    586 		dfl.header.flags = DLH_IMMED;
    587 		immediate = 1;
    588 	} else {
    589 		immediate = 0;
    590 	}
    591 
    592 	scsi_command(fd, &cmd, sizeof(cmd), &dfl, sizeof(dfl),
    593 	    8 * 60 * 60 * 1000, 0);
    594 
    595 	/*
    596 	 * Poll device for completion of Format
    597 	 */
    598 	if (immediate) {
    599 		i = 0;
    600 		printf("formatting.");
    601 		fflush(stdout);
    602 		do {
    603 			scsireq_t req;
    604 			struct scsipi_test_unit_ready tcmd;
    605 
    606 			memset(&tcmd, 0, sizeof(cmd));
    607 			tcmd.opcode = TEST_UNIT_READY;
    608 
    609 			memset(&req, 0, sizeof(req));
    610 			memcpy(req.cmd, &tcmd, 6);
    611 			req.cmdlen = 6;
    612 			req.timeout = 10000;
    613 			req.senselen = SENSEBUFLEN;
    614 
    615 			if (ioctl(fd, SCIOCCOMMAND, &req) == -1) {
    616 				err(1, "SCIOCCOMMAND");
    617 			}
    618 
    619 			if (req.retsts == SCCMD_OK) {
    620 				break;
    621 			} else if (req.retsts == SCCMD_TIMEOUT) {
    622 				fprintf(stderr, "%s: SCSI command timed out",
    623 				    dvname);
    624 				break;
    625 			} else if (req.retsts == SCCMD_BUSY) {
    626 				fprintf(stderr, "%s: device is busy",
    627 				    dvname);
    628 				break;
    629 			} else if (req.retsts != SCCMD_SENSE) {
    630 				fprintf(stderr,
    631 				    "%s: device had unknown status %x", dvname,
    632 				    req.retsts);
    633 				break;
    634 			}
    635 			memcpy(&sense, req.sense, SENSEBUFLEN);
    636 			if (sense.sense_key_spec_1 == SSD_SCS_VALID) {
    637 				j = (sense.sense_key_spec_2 << 8) |
    638 				    (sense.sense_key_spec_3);
    639 				if (j >= complete[i]) {
    640 					printf(".%d0%%.", ++i);
    641 					fflush(stdout);
    642 				}
    643 			}
    644 			sleep(10);
    645 		} while ((sense.flags & SSD_KEY) == SKEY_NOT_READY);
    646 		printf(".100%%..done.\n");
    647 	}
    648 	return;
    649 }
    650 
    651 /*
    652  * device_identify:
    653  *
    654  *	Display the identity of the device, including it's SCSI bus,
    655  *	target, lun, and it's vendor/product/revision information.
    656  */
    657 void
    658 device_identify(argc, argv)
    659 	int argc;
    660 	char *argv[];
    661 {
    662 	struct scsipi_inquiry_data inqbuf;
    663 	struct scsipi_inquiry cmd;
    664 
    665 	/* x4 in case every character is escaped, +1 for NUL. */
    666 	char vendor[(sizeof(inqbuf.vendor) * 4) + 1],
    667 	     product[(sizeof(inqbuf.product) * 4) + 1],
    668 	     revision[(sizeof(inqbuf.revision) * 4) + 1];
    669 
    670 	/* No arguments. */
    671 	if (argc != 0)
    672 		usage();
    673 
    674 	memset(&cmd, 0, sizeof(cmd));
    675 	memset(&inqbuf, 0, sizeof(inqbuf));
    676 
    677 	cmd.opcode = INQUIRY;
    678 	cmd.length = sizeof(inqbuf);
    679 
    680 	scsi_command(fd, &cmd, sizeof(cmd), &inqbuf, sizeof(inqbuf),
    681 	    10000, SCCMD_READ);
    682 
    683 	scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor,
    684 	    sizeof(inqbuf.vendor));
    685 	scsi_strvis(product, sizeof(product), inqbuf.product,
    686 	    sizeof(inqbuf.product));
    687 	scsi_strvis(revision, sizeof(revision), inqbuf.revision,
    688 	    sizeof(inqbuf.revision));
    689 
    690 	printf("%s: scsibus%d target %d lun %d <%s, %s, %s>\n",
    691 	    dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target,
    692 	    dvaddr.addr.scsi.lun, vendor, product, revision);
    693 
    694 	return;
    695 }
    696 
    697 /*
    698  * device_reassign:
    699  *
    700  *	Reassign bad blocks on a direct access device.
    701  */
    702 void
    703 device_reassign(argc, argv)
    704 	int argc;
    705 	char *argv[];
    706 {
    707 	struct scsi_reassign_blocks cmd;
    708 	struct scsi_reassign_blocks_data *data;
    709 	size_t dlen;
    710 	u_int32_t blkno;
    711 	int i;
    712 	char *cp;
    713 
    714 	/* We get a list of block numbers. */
    715 	if (argc < 1)
    716 		usage();
    717 
    718 	/*
    719 	 * Allocate the reassign blocks descriptor.  The 4 comes from the
    720 	 * size of the block address in the defect descriptor.
    721 	 */
    722 	dlen = sizeof(struct scsi_reassign_blocks_data) + ((argc - 1) * 4);
    723 	data = malloc(dlen);
    724 	if (data == NULL)
    725 		errx(1, "unable to allocate defect descriptor");
    726 	memset(data, 0, dlen);
    727 
    728 	cmd.opcode = SCSI_REASSIGN_BLOCKS;
    729 	cmd.byte2 = 0;
    730 	cmd.unused[0] = 0;
    731 	cmd.unused[1] = 0;
    732 	cmd.unused[2] = 0;
    733 	cmd.control = 0;
    734 
    735 	/* Defect descriptor length. */
    736 	_lto2b(argc * 4, data->length);
    737 
    738 	/* Build the defect descriptor list. */
    739 	for (i = 0; i < argc; i++) {
    740 		blkno = strtoul(argv[i], &cp, 10);
    741 		if (*cp != '\0')
    742 			errx(1, "invalid block number: %s", argv[i]);
    743 		_lto4b(blkno, data->defect_descriptor[i].dlbaddr);
    744 	}
    745 
    746 	scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_WRITE);
    747 
    748 	free(data);
    749 	return;
    750 }
    751 
    752 /*
    753  * device_release:
    754  *
    755  *      Issue a RELEASE command to a SCSI drevice
    756  */
    757 #ifndef	SCSI_RELEASE
    758 #define	SCSI_RELEASE	0x17
    759 #endif
    760 void
    761 device_release(argc, argv)
    762 	int argc;
    763 	char *argv[];
    764 {
    765 	struct scsipi_test_unit_ready cmd;	/* close enough */
    766 
    767 	/* No arguments. */
    768 	if (argc != 0)
    769 		usage();
    770 
    771 	memset(&cmd, 0, sizeof(cmd));
    772 
    773 	cmd.opcode = SCSI_RELEASE;
    774 
    775 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
    776 
    777 	return;
    778 }
    779 
    780 
    781 
    782 /*
    783  * device_reserve:
    784  *
    785  *      Issue a RESERVE command to a SCSI drevice
    786  */
    787 #ifndef	SCSI_RESERVE
    788 #define	SCSI_RESERVE	0x16
    789 #endif
    790 void
    791 device_reserve(argc, argv)
    792 	int argc;
    793 	char *argv[];
    794 {
    795 	struct scsipi_test_unit_ready cmd;	/* close enough */
    796 
    797 	/* No arguments. */
    798 	if (argc != 0)
    799 		usage();
    800 
    801 	memset(&cmd, 0, sizeof(cmd));
    802 
    803 	cmd.opcode = SCSI_RESERVE;
    804 
    805 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
    806 
    807 	return;
    808 }
    809 
    810 /*
    811  * device_reset:
    812  *
    813  *	Issue a reset to a SCSI device.
    814  */
    815 void
    816 device_reset(argc, argv)
    817 	int argc;
    818 	char *argv[];
    819 {
    820 
    821 	/* No arguments. */
    822 	if (argc != 0)
    823 		usage();
    824 
    825 	if (ioctl(fd, SCIOCRESET, NULL) != 0)
    826 		err(1, "SCIOCRESET");
    827 
    828 	return;
    829 }
    830 
    831 /*
    832  * device_debug:
    833  *
    834  *	Set debug level to a SCSI device.
    835  *	scsipi will print anything iff SCSIPI_DEBUG set in config.
    836  */
    837 void
    838 device_debug(argc, argv)
    839 	int argc;
    840 	char *argv[];
    841 {
    842 	int lvl;
    843 
    844 	if (argc < 1)
    845 		usage();
    846 
    847 	lvl = atoi(argv[0]);
    848 
    849 	if (ioctl(fd, SCIOCDEBUG, &lvl) != 0)
    850 		err(1, "SCIOCDEBUG");
    851 
    852 	return;
    853 }
    854 
    855 /*
    856  * device_getcache:
    857  *
    858  *	Get the caching parameters for a SCSI disk.
    859  */
    860 void
    861 device_getcache(argc, argv)
    862 	int argc;
    863 	char *argv[];
    864 {
    865 	struct {
    866 		struct scsipi_mode_header header;
    867 		struct scsi_blk_desc blk_desc;
    868 		struct page_caching caching_params;
    869 	} data;
    870 
    871 	/* No arguments. */
    872 	if (argc != 0)
    873 		usage();
    874 
    875 	scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data));
    876 
    877 	if ((data.caching_params.flags & (CACHING_RCD|CACHING_WCE)) ==
    878 	    CACHING_RCD)
    879 		printf("%s: no caches enabled\n", dvname);
    880 	else {
    881 		printf("%s: read cache %senabled\n", dvname,
    882 		    (data.caching_params.flags & CACHING_RCD) ? "not " : "");
    883 		printf("%s: write-back cache %senabled\n", dvname,
    884 		    (data.caching_params.flags & CACHING_WCE) ? "" : "not ");
    885 	}
    886 	printf("%s: caching parameters are %ssavable\n", dvname,
    887 	    (data.caching_params.pg_code & PGCODE_PS) ? "" : "not ");
    888 }
    889 
    890 /*
    891  * device_setcache:
    892  *
    893  *	Set cache enables for a SCSI disk.
    894  */
    895 void
    896 device_setcache(argc, argv)
    897 	int argc;
    898 	char *argv[];
    899 {
    900 	struct {
    901 		struct scsipi_mode_header header;
    902 		struct scsi_blk_desc blk_desc;
    903 		struct page_caching caching_params;
    904 	} data;
    905 	int dlen;
    906 	u_int8_t flags, byte2;
    907 
    908 	if (argc > 2 || argc == 0)
    909 		usage();
    910 
    911 	if (strcmp(argv[0], "none") == 0)
    912 		flags = CACHING_RCD;
    913 	else if (strcmp(argv[0], "r") == 0)
    914 		flags = 0;
    915 	else if (strcmp(argv[0], "w") == 0)
    916 		flags = CACHING_RCD|CACHING_WCE;
    917 	else if (strcmp(argv[0], "rw") == 0)
    918 		flags = CACHING_WCE;
    919 	else
    920 		usage();
    921 
    922 	if (argc == 2) {
    923 		if (strcmp(argv[1], "save") == 0)
    924 			byte2 = SMS_SP;
    925 		else
    926 			usage();
    927 	}
    928 
    929 	scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data));
    930 
    931 	data.caching_params.pg_code &= PGCODE_MASK;
    932 	data.caching_params.flags =
    933 	    (data.caching_params.flags & ~(CACHING_RCD|CACHING_WCE)) | flags;
    934 
    935 	data.caching_params.cache_segment_size[0] = 0;
    936 	data.caching_params.cache_segment_size[1] = 0;
    937 
    938 	data.header.data_length = 0;
    939 
    940 	dlen = sizeof(data.header) + sizeof(data.blk_desc) + 2 +
    941 	    data.caching_params.pg_length;
    942 
    943 	scsi_mode_select(fd, byte2, &data, dlen);
    944 }
    945 
    946 /*
    947  * device_flushcache:
    948  *
    949  *      Issue a FLUSH CACHE command to a SCSI drevice
    950  */
    951 #ifndef	SCSI_FLUSHCACHE
    952 #define	SCSI_FLUSHCACHE	0x35
    953 #endif
    954 void
    955 device_flushcache(argc, argv)
    956 	int argc;
    957 	char *argv[];
    958 {
    959 	struct scsipi_test_unit_ready cmd;	/* close enough */
    960 
    961 	/* No arguments. */
    962 	if (argc != 0)
    963 		usage();
    964 
    965 	memset(&cmd, 0, sizeof(cmd));
    966 
    967 	cmd.opcode = SCSI_FLUSHCACHE;
    968 
    969 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
    970 
    971 	return;
    972 }
    973 
    974 /*
    975  * device_prevent:
    976  *
    977  *      Issue a prevent to a SCSI device.
    978  */
    979 void
    980 device_prevent(argc, argv)
    981 	int argc;
    982 	char *argv[];
    983 {
    984 	struct scsipi_prevent cmd;
    985 
    986 	/* No arguments. */
    987 	if (argc != 0)
    988 		usage();
    989 
    990 	memset(&cmd, 0, sizeof(cmd));
    991 
    992 	cmd.opcode = PREVENT_ALLOW;
    993 	cmd.how = PR_PREVENT;
    994 
    995 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
    996 
    997 	return;
    998 }
    999 
   1000 /*
   1001  * device_allow:
   1002  *
   1003  *      Issue a stop to a SCSI device.
   1004  */
   1005 void
   1006 device_allow(argc, argv)
   1007 	int argc;
   1008 	char *argv[];
   1009 {
   1010 	struct scsipi_prevent cmd;
   1011 
   1012 	/* No arguments. */
   1013 	if (argc != 0)
   1014 		usage();
   1015 
   1016 	memset(&cmd, 0, sizeof(cmd));
   1017 
   1018 	cmd.opcode = PREVENT_ALLOW;
   1019 	cmd.how = PR_ALLOW;
   1020 
   1021 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
   1022 
   1023 	return;
   1024 }
   1025 
   1026 /*
   1027  * device_start:
   1028  *
   1029  *      Issue a start to a SCSI device.
   1030  */
   1031 void
   1032 device_start(argc, argv)
   1033 	int argc;
   1034 	char *argv[];
   1035 {
   1036 	struct scsipi_start_stop cmd;
   1037 
   1038 	/* No arguments. */
   1039 	if (argc != 0)
   1040 		usage();
   1041 
   1042 	memset(&cmd, 0, sizeof(cmd));
   1043 
   1044 	cmd.opcode = START_STOP;
   1045 	cmd.how = SSS_START;
   1046 
   1047 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 30000, 0);
   1048 
   1049 	return;
   1050 }
   1051 
   1052 /*
   1053  * device_stop:
   1054  *
   1055  *      Issue a stop to a SCSI device.
   1056  */
   1057 void
   1058 device_stop(argc, argv)
   1059 	int argc;
   1060 	char *argv[];
   1061 {
   1062 	struct scsipi_start_stop cmd;
   1063 
   1064 	/* No arguments. */
   1065 	if (argc != 0)
   1066 		usage();
   1067 
   1068 	memset(&cmd, 0, sizeof(cmd));
   1069 
   1070 	cmd.opcode = START_STOP;
   1071 	cmd.how = SSS_STOP;
   1072 
   1073 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 30000, 0);
   1074 
   1075 	return;
   1076 }
   1077 
   1078 /*
   1079  * device_tur:
   1080  *
   1081  *      Issue a TEST UNIT READY to a SCSI drevice
   1082  */
   1083 void
   1084 device_tur(argc, argv)
   1085 	int argc;
   1086 	char *argv[];
   1087 {
   1088 	struct scsipi_test_unit_ready cmd;
   1089 
   1090 	/* No arguments. */
   1091 	if (argc != 0)
   1092 		usage();
   1093 
   1094 	memset(&cmd, 0, sizeof(cmd));
   1095 
   1096 	cmd.opcode = TEST_UNIT_READY;
   1097 
   1098 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
   1099 
   1100 	return;
   1101 }
   1102 
   1103 /*
   1104  * BUS COMMANDS
   1105  */
   1106 
   1107 /*
   1108  * bus_reset:
   1109  *
   1110  *	Issue a reset to a SCSI bus.
   1111  */
   1112 void
   1113 bus_reset(argc, argv)
   1114 	int argc;
   1115 	char *argv[];
   1116 {
   1117 
   1118 	/* No arguments. */
   1119 	if (argc != 0)
   1120 		usage();
   1121 
   1122 	if (ioctl(fd, SCBUSIORESET, NULL) != 0)
   1123 		err(1, "SCBUSIORESET");
   1124 
   1125 	return;
   1126 }
   1127 
   1128 /*
   1129  * bus_scan:
   1130  *
   1131  *	Rescan a SCSI bus for new devices.
   1132  */
   1133 void
   1134 bus_scan(argc, argv)
   1135 	int argc;
   1136 	char *argv[];
   1137 {
   1138 	struct scbusioscan_args args;
   1139 	char *cp;
   1140 
   1141 	/* Must have two args: target lun */
   1142 	if (argc != 2)
   1143 		usage();
   1144 
   1145 	if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0)
   1146 		args.sa_target = -1;
   1147 	else {
   1148 		args.sa_target = strtol(argv[0], &cp, 10);
   1149 		if (*cp != '\0' || args.sa_target < 0)
   1150 			errx(1, "invalid target: %s", argv[0]);
   1151 	}
   1152 
   1153 	if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0)
   1154 		args.sa_lun = -1;
   1155 	else {
   1156 		args.sa_lun = strtol(argv[1], &cp, 10);
   1157 		if (*cp != '\0' || args.sa_lun < 0)
   1158 			errx(1, "invalid lun: %s", argv[1]);
   1159 	}
   1160 
   1161 	if (ioctl(fd, SCBUSIOSCAN, &args) != 0)
   1162 		err(1, "SCBUSIOSCAN");
   1163 
   1164 	return;
   1165 }
   1166 
   1167 /*
   1168  * bus_detach:
   1169  *
   1170  *	detach SCSI devices from a bus.
   1171  */
   1172 void
   1173 bus_detach(argc, argv)
   1174 	int argc;
   1175 	char *argv[];
   1176 {
   1177 	struct scbusiodetach_args args;
   1178 	char *cp;
   1179 
   1180 	/* Must have two args: target lun */
   1181 	if (argc != 2)
   1182 		usage();
   1183 
   1184 	if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0)
   1185 		args.sa_target = -1;
   1186 	else {
   1187 		args.sa_target = strtol(argv[0], &cp, 10);
   1188 		if (*cp != '\0' || args.sa_target < 0)
   1189 			errx(1, "invalid target: %s", argv[0]);
   1190 	}
   1191 
   1192 	if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0)
   1193 		args.sa_lun = -1;
   1194 	else {
   1195 		args.sa_lun = strtol(argv[1], &cp, 10);
   1196 		if (*cp != '\0' || args.sa_lun < 0)
   1197 			errx(1, "invalid lun: %s", argv[1]);
   1198 	}
   1199 
   1200 	if (ioctl(fd, SCBUSIODETACH, &args) != 0)
   1201 		err(1, "SCBUSIODETACH");
   1202 
   1203 	return;
   1204 }
   1205