Home | History | Annotate | Line # | Download | only in scsictl
      1 /*	$NetBSD: scsictl.c,v 1.44 2025/12/26 09:50:08 nia 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  *
     20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     30  * POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 /*
     34  * scsictl(8) - a program to manipulate SCSI devices and busses.
     35  */
     36 #include <sys/cdefs.h>
     37 
     38 #ifndef lint
     39 __RCSID("$NetBSD: scsictl.c,v 1.44 2025/12/26 09:50:08 nia Exp $");
     40 #endif
     41 
     42 #include <sys/param.h>
     43 #include <sys/ioctl.h>
     44 #include <sys/scsiio.h>
     45 #include <err.h>
     46 #include <errno.h>
     47 #include <endian.h>
     48 #include <fcntl.h>
     49 #include <limits.h>
     50 #include <stdio.h>
     51 #include <stdlib.h>
     52 #include <stdbool.h>
     53 #include <string.h>
     54 #include <unistd.h>
     55 #include <util.h>
     56 
     57 #include <dev/scsipi/scsi_spc.h>
     58 #include <dev/scsipi/scsipi_all.h>
     59 #include <dev/scsipi/scsi_disk.h>
     60 #include <dev/scsipi/scsipiconf.h>
     61 
     62 #include "extern.h"
     63 
     64 struct command {
     65 	const char *cmd_name;
     66 	const char *arg_names;
     67 	void (*cmd_func)(int, char *[]);
     68 };
     69 
     70 __dead static void	usage(void);
     71 
     72 static int	fd;				/* file descriptor for device */
     73 const  char	*dvname;			/* device name */
     74 static char	dvname_store[MAXPATHLEN];	/* for opendisk(3) */
     75 static const	char *cmdname;			/* command user issued */
     76 static struct	scsi_addr dvaddr;		/* SCSI device's address */
     77 
     78 static void	device_defects(int, char *[]);
     79 static void	device_format(int, char *[]);
     80 static void	device_identify(int, char *[]);
     81 static void	device_reassign(int, char *[]);
     82 static void	device_release(int, char *[]);
     83 static void	device_reserve(int, char *[]);
     84 static void	device_reset(int, char *[]);
     85 static void	device_debug(int, char *[]);
     86 static void	device_prevent(int, char *[]);
     87 static void	device_allow(int, char *[]);
     88 static void	device_start(int, char *[]);
     89 static void	device_stop(int, char *[]);
     90 static void	device_tur(int, char *[]);
     91 static void	device_getcache(int, char *[]);
     92 static void	device_setcache(int, char *[]);
     93 static void	device_flushcache(int, char *[]);
     94 static void	device_setspeed(int, char *[]);
     95 static void	device_getrealloc(int, char *[]);
     96 static void	device_setrealloc(int, char *[]);
     97 static void	device_reportluns(int, char *[]);
     98 
     99 static struct command device_commands[] = {
    100 	{ "defects",	"[primary] [grown] [block|byte|physical]",
    101 						device_defects },
    102 	{ "format",	"[blocksize [immediate]]", 	device_format },
    103 	{ "identify",	"[vpd]",		device_identify },
    104 	{ "reassign",	"blkno [blkno [...]]",	device_reassign },
    105 	{ "release",	"",			device_release },
    106 	{ "reserve",	"",			device_reserve },
    107 	{ "reset",	"",			device_reset },
    108 	{ "debug",	"level",		device_debug },
    109 	{ "prevent",	"",			device_prevent },
    110 	{ "allow",	"",			device_allow },
    111 	{ "start",	"",			device_start },
    112 	{ "stop",	"",			device_stop },
    113 	{ "tur",	"",			device_tur },
    114 	{ "getcache",	"",			device_getcache },
    115 	{ "setcache",	"none|r|w|rw [save]",	device_setcache },
    116 	{ "flushcache",	"",			device_flushcache },
    117 	{ "setspeed",	"[speed]",		device_setspeed },
    118 	{ "getrealloc",	"",			device_getrealloc },
    119 	{ "setrealloc",	"none|r|w|rw [save]",	device_setrealloc },
    120 	{ "reportluns",	"normal|wellknown|all|#",	device_reportluns },
    121 	{ NULL,		NULL,			NULL },
    122 };
    123 
    124 static void	bus_reset(int, char *[]);
    125 static void	bus_scan(int, char *[]);
    126 static void	bus_detach(int, char *[]);
    127 
    128 static 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(int argc, char *argv[])
    137 {
    138 	struct command *commands;
    139 	int i;
    140 
    141 	/* Must have at least: device command */
    142 	if (argc < 3)
    143 		usage();
    144 
    145 	/* Skip program name, get and skip device name and command. */
    146 	dvname = argv[1];
    147 	cmdname = argv[2];
    148 	argv += 3;
    149 	argc -= 3;
    150 
    151 	/*
    152 	 * Open the device and determine if it's a scsibus or an actual
    153 	 * device.  Devices respond to the SCIOCIDENTIFY ioctl.
    154 	 */
    155 	fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0);
    156 	if (fd == -1) {
    157 		if (errno == ENOENT) {
    158 			/*
    159 			 * Device doesn't exist.  Probably trying to open
    160 			 * a device which doesn't use disk semantics for
    161 			 * device name.  Try again, specifying "cooked",
    162 			 * which leaves off the "r" in front of the device's
    163 			 * name.
    164 			 */
    165 			fd = opendisk(dvname, O_RDWR, dvname_store,
    166 			    sizeof(dvname_store), 1);
    167 			if (fd == -1)
    168 				err(1, "%s", dvname);
    169 		} else
    170 			err(1, "%s", dvname);
    171 	}
    172 
    173 	/*
    174 	 * Point the dvname at the actual device name that opendisk() opened.
    175 	 */
    176 	dvname = dvname_store;
    177 
    178 	if (ioctl(fd, SCIOCIDENTIFY, &dvaddr) < 0)
    179 		commands = bus_commands;
    180 	else
    181 		commands = device_commands;
    182 
    183 	/* Look up and call the command. */
    184 	for (i = 0; commands[i].cmd_name != NULL; i++)
    185 		if (strcmp(cmdname, commands[i].cmd_name) == 0)
    186 			break;
    187 	if (commands[i].cmd_name == NULL)
    188 		errx(1, "unknown %s command: %s",
    189 		    commands == bus_commands ? "bus" : "device", cmdname);
    190 
    191 	(*commands[i].cmd_func)(argc, argv);
    192 	exit(0);
    193 }
    194 
    195 static void
    196 usage(void)
    197 {
    198 	int i;
    199 
    200 	fprintf(stderr, "usage: %s device command [arg [...]]\n",
    201 	    getprogname());
    202 
    203 	fprintf(stderr, "   Commands pertaining to scsi devices:\n");
    204 	for (i = 0; device_commands[i].cmd_name != NULL; i++)
    205 		fprintf(stderr, "\t%s %s\n", device_commands[i].cmd_name,
    206 					    device_commands[i].arg_names);
    207 	fprintf(stderr, "   Commands pertaining to scsi busses:\n");
    208 	for (i = 0; bus_commands[i].cmd_name != NULL; i++)
    209 		fprintf(stderr, "\t%s %s\n", bus_commands[i].cmd_name,
    210 					    bus_commands[i].arg_names);
    211 	fprintf(stderr, "   Use `any' or `all' to wildcard target or lun\n");
    212 
    213 	exit(1);
    214 }
    215 
    216 /*
    217  * DEVICE COMMANDS
    218  */
    219 
    220 /*
    221  * device_read_defect:
    222  *
    223  *	Read primary and/or growth defect list in physical or block
    224  *	format from a direct access device.
    225  *
    226  *	XXX Does not handle very large defect lists. Needs SCSI3 12
    227  *	    byte READ DEFECT DATA command.
    228  */
    229 
    230 static void	print_bf_dd(union scsi_defect_descriptor *);
    231 static void	print_bfif_dd(union scsi_defect_descriptor *);
    232 static void	print_psf_dd(union scsi_defect_descriptor *);
    233 
    234 static void
    235 device_defects(int argc, char *argv[])
    236 {
    237 	struct scsi_read_defect_data cmd;
    238 	struct scsi_read_defect_data_data *data;
    239 	size_t dlen;
    240 	int i, dlfmt = -1;
    241 	int defects;
    242 	char msg[256];
    243 	void (*pfunc)(union scsi_defect_descriptor *);
    244 #define RDD_P_G_MASK	0x18
    245 #define RDD_DLF_MASK	0x7
    246 
    247 	dlen = USHRT_MAX; 		/* XXX - this may not be enough room
    248 					 * for all of the defects.
    249 					 */
    250 	data = malloc(dlen);
    251 	if (data == NULL)
    252 		errx(1, "unable to allocate defect list");
    253 	memset(data, 0, dlen);
    254 	memset(&cmd, 0, sizeof(cmd));
    255 	defects = 0;
    256 	pfunc = NULL;
    257 
    258 	/* determine which defect list(s) to read. */
    259 	for (i = 0; i < argc; i++) {
    260 		if (strncmp("primary", argv[i], 7) == 0) {
    261 			cmd.flags |= RDD_PRIMARY;
    262 			continue;
    263 		}
    264 		if (strncmp("grown", argv[i], 5) == 0) {
    265 			cmd.flags |= RDD_GROWN;
    266 			continue;
    267 		}
    268 		break;
    269 	}
    270 
    271 	/* no defect list specified, assume both. */
    272 	if ((cmd.flags & (RDD_PRIMARY|RDD_GROWN)) == 0)
    273 		cmd.flags |= (RDD_PRIMARY|RDD_GROWN);
    274 
    275 	/* list format option. */
    276 	if (i < argc) {
    277 		if (strncmp("block", argv[i], 5) == 0) {
    278 			cmd.flags |= RDD_BF;
    279 			dlfmt = RDD_BF;
    280 		}
    281 		else if (strncmp("byte", argv[i], 4) == 0) {
    282 			cmd.flags |= RDD_BFIF;
    283 			dlfmt = RDD_BFIF;
    284 		}
    285 		else if (strncmp("physical", argv[i], 4) == 0) {
    286 			cmd.flags |= RDD_PSF;
    287 			dlfmt = RDD_PSF;
    288 		}
    289 		else {
    290 			usage();
    291 		}
    292 	}
    293 
    294 	/*
    295 	 * no list format specified; since block format not
    296 	 * recommended use physical sector format as default.
    297 	 */
    298 	if (dlfmt < 0) {
    299 		cmd.flags |= RDD_PSF;
    300 		dlfmt = RDD_PSF;
    301 	}
    302 
    303 	cmd.opcode = SCSI_READ_DEFECT_DATA;
    304 	_lto2b(dlen, &cmd.length[0]);
    305 
    306 	scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_READ);
    307 
    308 	msg[0] = '\0';
    309 
    310 	/* is the defect list in the format asked for? */
    311 	if ((data->flags & RDD_DLF_MASK) != dlfmt) {
    312 		strcpy(msg, "\n\tnotice:"
    313 		       "requested defect list format not supported by device\n\n");
    314 		dlfmt = (data->flags & RDD_DLF_MASK);
    315 	}
    316 
    317 	if (data->flags & RDD_PRIMARY)
    318 		strcat(msg, "primary");
    319 
    320 	if (data->flags & RDD_GROWN) {
    321 		if (data->flags & RDD_PRIMARY)
    322 			strcat(msg, " and ");
    323 		strcat(msg, "grown");
    324 	}
    325 
    326 	strcat(msg, " defects");
    327 
    328 	if ((data->flags & RDD_P_G_MASK) == 0)
    329 		strcat(msg, ": none reported\n");
    330 
    331 	printf("%s: scsibus%d target %d lun %d %s",
    332 	       dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target,
    333 	       dvaddr.addr.scsi.lun, msg);
    334 
    335 	/* device did not return either defect list. */
    336 	if ((data->flags & RDD_P_G_MASK) == 0)
    337 		return;
    338 
    339 	switch (dlfmt) {
    340 	case RDD_BF:
    341 		defects = _2btol(data->length) /
    342 				sizeof(struct scsi_defect_descriptor_bf);
    343 		pfunc = print_bf_dd;
    344 		strcpy(msg, "block address\n"
    345 			    "-------------\n");
    346 		break;
    347 	case RDD_BFIF:
    348 		defects = _2btol(data->length) /
    349 				sizeof(struct scsi_defect_descriptor_bfif);
    350 		pfunc = print_bfif_dd;
    351 		strcpy(msg, "              bytes from\n"
    352 			    "cylinder head   index\n"
    353 			    "-------- ---- ----------\n");
    354 		break;
    355 	case RDD_PSF:
    356 		defects = _2btol(data->length) /
    357 				sizeof(struct scsi_defect_descriptor_psf);
    358 		pfunc = print_psf_dd;
    359 		strcpy(msg, "cylinder head   sector\n"
    360 			    "-------- ---- ----------\n");
    361 		break;
    362 	}
    363 
    364 	/* device did not return any defects. */
    365 	if (defects == 0) {
    366 		printf(": none\n");
    367 		return;
    368 	}
    369 
    370 	printf(": %d\n", defects);
    371 
    372 	/* print heading. */
    373 	printf("%s", msg);
    374 
    375 	/* print defect list. */
    376 	for (i = 0 ; i < defects; i++) {
    377 		pfunc(&data->defect_descriptor[i]);
    378 	}
    379 
    380 	free(data);
    381 	return;
    382 }
    383 
    384 /*
    385  * print_bf_dd:
    386  *
    387  *	Print a block format defect descriptor.
    388  */
    389 static void
    390 print_bf_dd(union scsi_defect_descriptor *dd)
    391 {
    392 	u_int32_t block;
    393 
    394 	block = _4btol(dd->bf.block_address);
    395 
    396 	printf("%13u\n", block);
    397 }
    398 
    399 #define DEFECTIVE_TRACK	0xffffffff
    400 
    401 /*
    402  * print_bfif_dd:
    403  *
    404  *	Print a bytes from index format defect descriptor.
    405  */
    406 static void
    407 print_bfif_dd(union scsi_defect_descriptor *dd)
    408 {
    409 	u_int32_t cylinder;
    410 	u_int32_t head;
    411 	u_int32_t bytes_from_index;
    412 
    413 	cylinder = _3btol(dd->bfif.cylinder);
    414 	head = dd->bfif.head;
    415 	bytes_from_index = _4btol(dd->bfif.bytes_from_index);
    416 
    417 	printf("%8u %4u ", cylinder, head);
    418 
    419 	if (bytes_from_index == DEFECTIVE_TRACK)
    420 		printf("entire track defective\n");
    421 	else
    422 		printf("%10u\n", bytes_from_index);
    423 }
    424 
    425 /*
    426  * print_psf_dd:
    427  *
    428  *	Print a physical sector format defect descriptor.
    429  */
    430 static void
    431 print_psf_dd(union scsi_defect_descriptor *dd)
    432 {
    433 	u_int32_t cylinder;
    434 	u_int32_t head;
    435 	u_int32_t sector;
    436 
    437 	cylinder = _3btol(dd->psf.cylinder);
    438 	head = dd->psf.head;
    439 	sector = _4btol(dd->psf.sector);
    440 
    441 	printf("%8u %4u ", cylinder, head);
    442 
    443 	if (sector == DEFECTIVE_TRACK)
    444 		printf("entire track defective\n");
    445 	else
    446 		printf("%10u\n", sector);
    447 }
    448 
    449 /*
    450  * device_format:
    451  *
    452  *	Format a direct access device.
    453  */
    454 static void
    455 device_format(int argc, char *argv[])
    456 {
    457 	u_int32_t blksize;
    458 	int i, j, immediate;
    459 #define	PC	(65536/10)
    460 	static int complete[] = {
    461 	    PC*1, PC*2, PC*3, PC*4, PC*5, PC*6, PC*7, PC*8, PC*9, 65536
    462 	};
    463 	char *cp, buffer[64];
    464 	struct scsi_sense_data sense;
    465 	struct scsi_format_unit cmd;
    466 	struct {
    467 		struct scsi_format_unit_defect_list_header header;
    468 		/* optional initialization pattern */
    469 		/* optional defect list */
    470 	} dfl;
    471 	struct {
    472 		struct scsi_mode_parameter_header_6 header;
    473 		struct scsi_general_block_descriptor blk_desc;
    474 		struct page_disk_format format_page;
    475 	} mode_page;
    476 	struct {
    477 		struct scsi_mode_parameter_header_6 header;
    478 		struct scsi_general_block_descriptor blk_desc;
    479 	} data_select;
    480 
    481 	/* Blocksize is an optional argument. */
    482 	if (argc > 2)
    483 		usage();
    484 
    485 	/*
    486 	 * Loop doing Request Sense to clear any pending Unit Attention.
    487 	 *
    488 	 * Multiple conditions may exist on the drive which are returned
    489 	 * in priority order.
    490 	 */
    491 	for (i = 0; i < 8; i++) {
    492 		scsi_request_sense(fd, &sense, sizeof (sense));
    493 		if ((j = SSD_SENSE_KEY(sense.flags)) == SKEY_NO_SENSE)
    494 			break;
    495 	}
    496 	/*
    497 	 * Make sure we cleared any pending Unit Attention
    498 	 */
    499 	if (j != SKEY_NO_SENSE) {
    500 		cp = scsi_decode_sense((const unsigned char *) &sense, 2,
    501 		    buffer, sizeof (buffer));
    502 		errx(1, "failed to clean Unit Attention: %s", cp);
    503 	}
    504 
    505 	/*
    506 	 * Get the DISK FORMAT mode page.  SCSI-2 recommends specifying the
    507 	 * interleave read from this page in the FORMAT UNIT command.
    508 	 */
    509 	scsi_mode_sense(fd, 0x03, 0x00, &mode_page, sizeof(mode_page));
    510 
    511 	j = (mode_page.format_page.bytes_s[0] << 8) |
    512 	    (mode_page.format_page.bytes_s[1]);
    513 
    514 	if (j != DEV_BSIZE)
    515 		printf("current disk sector size: %d\n", j);
    516 
    517 	memset(&cmd, 0, sizeof(cmd));
    518 
    519 	cmd.opcode = SCSI_FORMAT_UNIT;
    520 	memcpy(cmd.interleave, mode_page.format_page.interleave,
    521 	    sizeof(cmd.interleave));
    522 
    523 	/*
    524 	 * The blocksize on the device is only changed if the user
    525 	 * specified a new blocksize. If not specified the blocksize
    526 	 * used for the device will be the Default value in the device.
    527 	 * We don't specify the number of blocks since the format
    528 	 * command will always reformat the entire drive.  Also by
    529 	 * not specifying a block count the drive will reset the
    530 	 * block count to the maximum available after the format
    531 	 * completes if the blocksize was changed in the format.
    532 	 * Finally, the new disk geometry will not but updated on
    533 	 * the drive in permanent storage until _AFTER_ the format
    534 	 * completes successfully.
    535 	 */
    536 	if (argc > 0) {
    537 		blksize = strtoul(argv[0], &cp, 10);
    538 		if (*cp != '\0')
    539 			errx(1, "invalid block size: %s", argv[0]);
    540 
    541 		memset(&data_select, 0, sizeof(data_select));
    542 
    543 		data_select.header.blk_desc_len =
    544 		    sizeof(struct scsi_general_block_descriptor);
    545 		/*
    546 		 * blklen in desc is 3 bytes with a leading reserved byte
    547 		 */
    548 		_lto4b(blksize, &data_select.blk_desc.reserved);
    549 
    550 		/*
    551 		 * Issue Mode Select to modify the device blocksize to be
    552 		 * used on the Format.  The modified device geometry will
    553 		 * be stored as Current and Saved Page 3 parameters when
    554 		 * the Format completes.
    555 		 */
    556 		scsi_mode_select(fd, 0, &data_select, sizeof(data_select));
    557 
    558 		/*
    559 		 * Since user specified a specific block size make sure it
    560 		 * gets stored in the device when the format completes.
    561 		 *
    562 		 * Also scrub the defect list back to the manufacturers
    563 		 * original.
    564 		 */
    565 		cmd.flags = SFU_CMPLST | SFU_FMTDATA;
    566 	}
    567 
    568 	memset(&dfl, 0, sizeof(dfl));
    569 
    570 	if (argc > 1 && strncmp(argv[1], "imm", 3) == 0) {
    571 		/*
    572 		 * Signal target for an immediate return from Format.
    573 		 *
    574 		 * We'll poll for completion status.
    575 		 */
    576 		dfl.header.flags = DLH_IMMED;
    577 		immediate = 1;
    578 	} else {
    579 		immediate = 0;
    580 	}
    581 
    582 	scsi_command(fd, &cmd, sizeof(cmd), &dfl, sizeof(dfl),
    583 	    8 * 60 * 60 * 1000, SCCMD_WRITE);
    584 
    585 	/*
    586 	 * Poll device for completion of Format
    587 	 */
    588 	if (immediate) {
    589 		i = 0;
    590 		printf("formatting.");
    591 		fflush(stdout);
    592 		do {
    593 			scsireq_t req;
    594 			struct scsi_test_unit_ready tcmd;
    595 
    596 			memset(&tcmd, 0, sizeof(tcmd));
    597 			tcmd.opcode = SCSI_TEST_UNIT_READY;
    598 
    599 			memset(&req, 0, sizeof(req));
    600 			memcpy(req.cmd, &tcmd, 6);
    601 			req.cmdlen = 6;
    602 			req.timeout = 10000;
    603 			req.senselen = SENSEBUFLEN;
    604 
    605 			if (ioctl(fd, SCIOCCOMMAND, &req) == -1) {
    606 				err(1, "SCIOCCOMMAND");
    607 			}
    608 
    609 			if (req.retsts == SCCMD_OK) {
    610 				break;
    611 			} else if (req.retsts == SCCMD_TIMEOUT) {
    612 				fprintf(stderr, "%s: SCSI command timed out",
    613 				    dvname);
    614 				break;
    615 			} else if (req.retsts == SCCMD_BUSY) {
    616 				fprintf(stderr, "%s: device is busy",
    617 				    dvname);
    618 				break;
    619 			} else if (req.retsts != SCCMD_SENSE) {
    620 				fprintf(stderr,
    621 				    "%s: device had unknown status %x", dvname,
    622 				    req.retsts);
    623 				break;
    624 			}
    625 			memcpy(&sense, req.sense, sizeof(sense));
    626 			if (sense.sks.sks_bytes[0] & SSD_SKSV) {
    627 				j = (sense.sks.sks_bytes[1] << 8) |
    628 				    (sense.sks.sks_bytes[2]);
    629 				if (j >= complete[i]) {
    630 					printf(".%d0%%.", ++i);
    631 					fflush(stdout);
    632 				}
    633 			}
    634 			sleep(10);
    635 		} while (SSD_SENSE_KEY(sense.flags) == SKEY_NOT_READY);
    636 		printf(".100%%..done.\n");
    637 	}
    638 	return;
    639 }
    640 
    641 static void
    642 print_designator(const char *pre, struct scsipi_inquiry_evpd_device_id *did)
    643 {
    644 	char buf[252 * 4 + 1];
    645 	unsigned assoc, proto, code, type;
    646 	static const char *typestr[] = {
    647 		"vendor",
    648 		"t10",
    649 		"eui64",
    650 		"naa",
    651 		"target port",
    652 		"port group",
    653 		"lun group",
    654 		"md5",
    655 		"scsi",
    656 		"res9",
    657 		"res10",
    658 		"res11",
    659 		"res12",
    660 		"res13",
    661 		"res14",
    662 		"res15"
    663 	};
    664 	static const char *assocstr[] = {
    665 		"lun",
    666 		"port",
    667 		"target",
    668 		"reserved"
    669 	};
    670 	static const char *protostr[] = {
    671 		"fibre channel",
    672 		"obsolete",
    673 		"ssa",
    674 		"ieee1394",
    675 		"rdma",
    676 		"iSCSI",
    677 		"SAS"
    678 	};
    679 	const unsigned maxproto = __arraycount(protostr) - 1;
    680 	const unsigned isbinary =
    681 	    __SHIFTOUT(SINQ_DEVICE_ID_CODESET_BINARY, SINQ_DEVICE_ID_CODESET);
    682 	unsigned k;
    683 
    684 	assoc = __SHIFTOUT(did->flags, SINQ_DEVICE_ID_ASSOCIATION);
    685 	proto = __SHIFTOUT(did->pc, SINQ_DEVICE_ID_PROTOCOL);
    686 	code = __SHIFTOUT(did->pc, SINQ_DEVICE_ID_CODESET);
    687 	type = __SHIFTOUT(did->flags, SINQ_DEVICE_ID_TYPE);
    688 
    689 	printf("%s%s", pre, assocstr[assoc]);
    690 	if (did->flags & SINQ_DEVICE_ID_PIV) {
    691 		if (proto > maxproto)
    692 			printf(" proto%u", proto);
    693 		else
    694 			printf(" %s", protostr[proto]);
    695 	}
    696 	printf(" %s: ", typestr[type]);
    697 
    698 	if (code == isbinary) {
    699 		for (k = 0; k < did->designator_length; k++) {
    700 			printf("%02x", did->designator[k]);
    701 		}
    702 		printf("\n");
    703 	} else {
    704 		scsi_strvis(buf, sizeof(buf), (char *)did->designator,
    705 		    did->designator_length);
    706 		printf("%s\n", buf);
    707 	}
    708 }
    709 
    710 /*
    711  * device_identify:
    712  *
    713  *	Display the identity of the device, including its SCSI bus,
    714  *	target, lun, and its vendor/product/revision information.
    715  *      Optionally query and display vpd identification data.
    716  */
    717 static void
    718 device_identify(int argc, char *argv[])
    719 {
    720 	struct scsipi_inquiry_data inqbuf;
    721 	struct {
    722 		struct scsipi_inquiry_evpd_header h;
    723 		uint8_t d[255 - sizeof(struct scsipi_inquiry_evpd_header)];
    724 	} evpdbuf;
    725 	struct scsipi_inquiry cmd;
    726 	unsigned len, rlen;
    727 	struct scsipi_inquiry_evpd_serial *ser;
    728 	struct scsipi_inquiry_evpd_device_id *did;
    729 	int has_serial;
    730 	int has_device_id;
    731 	bool getvpd = false;
    732 	int i;
    733 
    734 	/* x4 in case every character is escaped, +1 for NUL. */
    735 	char vendor[(sizeof(inqbuf.vendor) * 4) + 1],
    736 	     product[(sizeof(inqbuf.product) * 4) + 1],
    737 	     revision[(sizeof(inqbuf.revision) * 4) + 1],
    738 	     ident[252 * 4 + 1];
    739 
    740 	/* Check optional arguments */
    741 	for (i = 0; i < argc; i++) {
    742 		if (strncmp("vpd", argv[i], 3) == 0) {
    743 			getvpd = true;
    744 			continue;
    745 		}
    746 		usage();
    747 	}
    748 
    749 	memset(&cmd, 0, sizeof(cmd));
    750 	memset(&inqbuf, 0, sizeof(inqbuf));
    751 
    752 	cmd.opcode = INQUIRY;
    753 	cmd.length = sizeof(inqbuf);
    754 
    755 	scsi_command(fd, &cmd, sizeof(cmd), &inqbuf, sizeof(inqbuf),
    756 	    10000, SCCMD_READ);
    757 
    758 	scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor,
    759 	    sizeof(inqbuf.vendor));
    760 	scsi_strvis(product, sizeof(product), inqbuf.product,
    761 	    sizeof(inqbuf.product));
    762 	scsi_strvis(revision, sizeof(revision), inqbuf.revision,
    763 	    sizeof(inqbuf.revision));
    764 
    765 	printf("%s: scsibus%d target %d lun %d <%s, %s, %s>\n",
    766 	    dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target,
    767 	    dvaddr.addr.scsi.lun, vendor, product, revision);
    768 
    769 	if (!getvpd)
    770 		return;
    771 
    772 	cmd.byte2 |= SINQ_EVPD;
    773 	cmd.pagecode = SINQ_VPD_PAGES;
    774 
    775 	scsi_command(fd, &cmd, sizeof(cmd), &evpdbuf, sizeof(evpdbuf),
    776 	    10000, SCCMD_READ);
    777 
    778 	len = be16dec(evpdbuf.h.length);
    779 	if (len > sizeof(evpdbuf.d))
    780 		len = 0;
    781 
    782 	has_serial = memchr(evpdbuf.d, SINQ_VPD_SERIAL, len) != NULL;
    783 	has_device_id = memchr(evpdbuf.d, SINQ_VPD_DEVICE_ID, len) != NULL;
    784 
    785 	if (has_serial) {
    786 		cmd.byte2 |= SINQ_EVPD;
    787 		cmd.pagecode = SINQ_VPD_SERIAL;
    788 
    789 		scsi_command(fd, &cmd, sizeof(cmd), &evpdbuf, sizeof(evpdbuf),
    790 		    10000, SCCMD_READ);
    791 
    792 		len = be16dec(evpdbuf.h.length);
    793 		if (len > sizeof(evpdbuf.d))
    794 			len = 0;
    795 
    796 		ser = (struct scsipi_inquiry_evpd_serial *)&evpdbuf.d;
    797 		scsi_strvis(ident, sizeof(ident), (char *)ser->serial_number,
    798 		    len);
    799 		printf("VPD Serial:\n");
    800 		printf("\t%s\n", ident);
    801 	}
    802 
    803 	if (has_device_id) {
    804 		cmd.byte2 |= SINQ_EVPD;
    805 		cmd.pagecode = SINQ_VPD_DEVICE_ID;
    806 
    807 		scsi_command(fd, &cmd, sizeof(cmd), &evpdbuf, sizeof(evpdbuf),
    808 		    10000, SCCMD_READ);
    809 
    810 		len = be16dec(evpdbuf.h.length);
    811 		if (len > sizeof(evpdbuf.d))
    812 			len = 0;
    813 
    814 		printf("VPD Device IDs:\n");
    815 
    816 		for (unsigned off = 0; off < len - sizeof(*did); off += rlen) {
    817 			void *p = &evpdbuf.d[off];
    818 			did = (struct scsipi_inquiry_evpd_device_id *)p;
    819 			rlen = sizeof(*did) + did->designator_length - 1;
    820 			if (off + rlen > len)
    821 				break;
    822 
    823 			print_designator("\t", did);
    824 		}
    825 	}
    826 
    827 	return;
    828 }
    829 
    830 /*
    831  * device_reassign:
    832  *
    833  *	Reassign bad blocks on a direct access device.
    834  */
    835 static void
    836 device_reassign(int argc, char *argv[])
    837 {
    838 	struct scsi_reassign_blocks cmd;
    839 	struct scsi_reassign_blocks_data *data;
    840 	size_t dlen;
    841 	u_int32_t blkno;
    842 	int i;
    843 	char *cp;
    844 
    845 	/* We get a list of block numbers. */
    846 	if (argc < 1)
    847 		usage();
    848 
    849 	/*
    850 	 * Allocate the reassign blocks descriptor.  The 4 comes from the
    851 	 * size of the block address in the defect descriptor.
    852 	 */
    853 	dlen = sizeof(struct scsi_reassign_blocks_data) + ((argc - 1) * 4);
    854 	data = malloc(dlen);
    855 	if (data == NULL)
    856 		errx(1, "unable to allocate defect descriptor");
    857 	memset(data, 0, dlen);
    858 
    859 	cmd.opcode = SCSI_REASSIGN_BLOCKS;
    860 	cmd.byte2 = 0;
    861 	cmd.unused[0] = 0;
    862 	cmd.unused[1] = 0;
    863 	cmd.unused[2] = 0;
    864 	cmd.control = 0;
    865 
    866 	/* Defect descriptor length. */
    867 	_lto2b(argc * 4, data->length);
    868 
    869 	/* Build the defect descriptor list. */
    870 	for (i = 0; i < argc; i++) {
    871 		blkno = strtoul(argv[i], &cp, 10);
    872 		if (*cp != '\0')
    873 			errx(1, "invalid block number: %s", argv[i]);
    874 		_lto4b(blkno, data->defect_descriptor[i].dlbaddr);
    875 	}
    876 
    877 	scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_WRITE);
    878 
    879 	free(data);
    880 	return;
    881 }
    882 
    883 /*
    884  * device_release:
    885  *
    886  *	Issue a RELEASE command to a SCSI device.
    887  */
    888 #ifndef	SCSI_RELEASE
    889 #define	SCSI_RELEASE	0x17
    890 #endif
    891 static void
    892 device_release(int argc, char *argv[])
    893 {
    894 	struct scsi_test_unit_ready cmd;	/* close enough */
    895 
    896 	/* No arguments. */
    897 	if (argc != 0)
    898 		usage();
    899 
    900 	memset(&cmd, 0, sizeof(cmd));
    901 
    902 	cmd.opcode = SCSI_RELEASE;
    903 
    904 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
    905 
    906 	return;
    907 }
    908 
    909 /*
    910  * device_reserve:
    911  *
    912  *	Issue a RESERVE command to a SCSI device.
    913  */
    914 #ifndef	SCSI_RESERVE
    915 #define	SCSI_RESERVE	0x16
    916 #endif
    917 static void
    918 device_reserve(int argc, char *argv[])
    919 {
    920 	struct scsi_test_unit_ready cmd;	/* close enough */
    921 
    922 	/* No arguments. */
    923 	if (argc != 0)
    924 		usage();
    925 
    926 	memset(&cmd, 0, sizeof(cmd));
    927 
    928 	cmd.opcode = SCSI_RESERVE;
    929 
    930 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
    931 
    932 	return;
    933 }
    934 
    935 /*
    936  * device_reset:
    937  *
    938  *	Issue a reset to a SCSI device.
    939  */
    940 static void
    941 device_reset(int argc, char *argv[])
    942 {
    943 
    944 	/* No arguments. */
    945 	if (argc != 0)
    946 		usage();
    947 
    948 	if (ioctl(fd, SCIOCRESET, NULL) != 0)
    949 		err(1, "SCIOCRESET");
    950 
    951 	return;
    952 }
    953 
    954 /*
    955  * device_debug:
    956  *
    957  *	Set debug level to a SCSI device.
    958  *	scsipi will print anything iff SCSIPI_DEBUG set in config.
    959  */
    960 static void
    961 device_debug(int argc, char *argv[])
    962 {
    963 	int lvl;
    964 
    965 	if (argc < 1)
    966 		usage();
    967 
    968 	lvl = atoi(argv[0]);
    969 
    970 	if (ioctl(fd, SCIOCDEBUG, &lvl) != 0)
    971 		err(1, "SCIOCDEBUG");
    972 
    973 	return;
    974 }
    975 
    976 /*
    977  * device_getcache:
    978  *
    979  *	Get the caching parameters for a SCSI disk.
    980  */
    981 static void
    982 device_getcache(int argc, char *argv[])
    983 {
    984 	struct {
    985 		struct scsi_mode_parameter_header_6 header;
    986 		struct scsi_general_block_descriptor blk_desc;
    987 		struct page_caching caching_params;
    988 	} data;
    989 
    990 	/* No arguments. */
    991 	if (argc != 0)
    992 		usage();
    993 
    994 	scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data));
    995 
    996 	if ((data.caching_params.flags & (CACHING_RCD|CACHING_WCE)) ==
    997 	    CACHING_RCD)
    998 		printf("%s: no caches enabled\n", dvname);
    999 	else {
   1000 		printf("%s: read cache %senabled\n", dvname,
   1001 		    (data.caching_params.flags & CACHING_RCD) ? "not " : "");
   1002 		printf("%s: write-back cache %senabled\n", dvname,
   1003 		    (data.caching_params.flags & CACHING_WCE) ? "" : "not ");
   1004 	}
   1005 	printf("%s: caching parameters are %ssavable\n", dvname,
   1006 	    (data.caching_params.pg_code & PGCODE_PS) ? "" : "not ");
   1007 }
   1008 
   1009 /*
   1010  * device_setcache:
   1011  *
   1012  *	Set cache enables for a SCSI disk.
   1013  */
   1014 static void
   1015 device_setcache(int argc, char *argv[])
   1016 {
   1017 	struct {
   1018 		struct scsi_mode_parameter_header_6 header;
   1019 		struct scsi_general_block_descriptor blk_desc;
   1020 		struct page_caching caching_params;
   1021 	} data;
   1022 	int dlen;
   1023 	u_int8_t flags, byte2;
   1024 
   1025 	if (argc > 2 || argc == 0)
   1026 		usage();
   1027 
   1028 	flags = 0;
   1029 	byte2 = 0;
   1030 	if (strcmp(argv[0], "none") == 0)
   1031 		flags = CACHING_RCD;
   1032 	else if (strcmp(argv[0], "r") == 0)
   1033 		flags = 0;
   1034 	else if (strcmp(argv[0], "w") == 0)
   1035 		flags = CACHING_RCD|CACHING_WCE;
   1036 	else if (strcmp(argv[0], "rw") == 0)
   1037 		flags = CACHING_WCE;
   1038 	else
   1039 		usage();
   1040 
   1041 	if (argc == 2) {
   1042 		if (strcmp(argv[1], "save") == 0)
   1043 			byte2 = SMS_SP;
   1044 		else
   1045 			usage();
   1046 	}
   1047 
   1048 	scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data));
   1049 
   1050 	data.caching_params.pg_code &= PGCODE_MASK;
   1051 	data.caching_params.flags =
   1052 	    (data.caching_params.flags & ~(CACHING_RCD|CACHING_WCE)) | flags;
   1053 
   1054 	data.caching_params.cache_segment_size[0] = 0;
   1055 	data.caching_params.cache_segment_size[1] = 0;
   1056 
   1057 	data.header.data_length = 0;
   1058 
   1059 	dlen = sizeof(data.header) + sizeof(data.blk_desc) + 2 +
   1060 	    data.caching_params.pg_length;
   1061 
   1062 	scsi_mode_select(fd, byte2, &data, dlen);
   1063 }
   1064 
   1065 /*
   1066  * device_flushcache:
   1067  *
   1068  *	Issue a FLUSH CACHE command to a SCSI device.
   1069  */
   1070 #ifndef	SCSI_FLUSHCACHE
   1071 #define	SCSI_FLUSHCACHE	0x35
   1072 #endif
   1073 static void
   1074 device_flushcache(int argc, char *argv[])
   1075 {
   1076 	struct scsi_test_unit_ready cmd;	/* close enough */
   1077 
   1078 	/* No arguments. */
   1079 	if (argc != 0)
   1080 		usage();
   1081 
   1082 	memset(&cmd, 0, sizeof(cmd));
   1083 
   1084 	cmd.opcode = SCSI_FLUSHCACHE;
   1085 
   1086 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
   1087 
   1088 	return;
   1089 }
   1090 
   1091 /*
   1092  * device_setspeed:
   1093  *
   1094  *	Set rotation speed to a CD/DVD drive.
   1095  */
   1096 static void
   1097 device_setspeed(int argc, char *argv[])
   1098 {
   1099 	u_char cmd[11];
   1100 	u_char pd[28];
   1101 	u_int32_t speed;
   1102 
   1103 	if (argc != 1)
   1104 		usage();
   1105 
   1106 	speed = atoi(argv[0]) * 177;
   1107 
   1108 	memset(&pd, 0, sizeof(pd));
   1109 	if (speed == 0)
   1110 		pd[0] = 4; /* restore drive defaults */
   1111 	pd[8] = 0xff;
   1112 	pd[9] = 0xff;
   1113 	pd[10] = 0xff;
   1114 	pd[11] = 0xff;
   1115 	pd[12] = pd[20] = (speed >> 24) & 0xff;
   1116 	pd[13] = pd[21] = (speed >> 16) & 0xff;
   1117 	pd[14] = pd[22] = (speed >> 8) & 0xff;
   1118 	pd[15] = pd[23] = speed & 0xff;
   1119 	pd[18] = pd[26] = 1000 >> 8;
   1120 	pd[19] = pd[27] = 1000 & 0xff;
   1121 
   1122 	memset(&cmd, 0, sizeof(cmd));
   1123 	cmd[0] = 0xb6;
   1124 	cmd[10] = sizeof(pd);
   1125 
   1126 	scsi_command(fd, &cmd, sizeof(cmd), pd, sizeof(pd), 10000, SCCMD_WRITE);
   1127 
   1128 	return;
   1129 }
   1130 
   1131 /*
   1132  * device_reportluns:
   1133  *
   1134  *	Report the known LUNs to which the initiator can send commands
   1135  */
   1136 static void
   1137 device_reportluns(int argc, char *argv[])
   1138 {
   1139 	struct scsi_report_luns cmd;
   1140 	struct {
   1141 		struct scsi_report_luns_header header;
   1142 		struct scsi_report_luns_lun desc[1];
   1143 	} *data;
   1144 	u_int32_t dlen, len;
   1145 	u_int64_t lun;
   1146 	size_t count, idx;
   1147 	unsigned long sel;
   1148 	char *endp;
   1149 	int i;
   1150 
   1151 	dlen = USHRT_MAX; /* good for > 8000 LUNs */
   1152 	data = malloc(dlen);
   1153 	if (data == NULL)
   1154 		errx(1, "unable to allocate lun report");
   1155 
   1156 	memset(&cmd, 0, sizeof(cmd));
   1157 	cmd.opcode = SCSI_REPORT_LUNS;
   1158 	cmd.selectreport = SELECTREPORT_NORMAL;
   1159 
   1160 	/* determine which report to read. */
   1161 	for (i = 0; i < argc; i++) {
   1162 		if (strcmp("normal", argv[i]) == 0) {
   1163 			cmd.selectreport = SELECTREPORT_NORMAL;
   1164 			continue;
   1165 		}
   1166 		if (strcmp("wellknown", argv[i]) == 0) {
   1167 			cmd.selectreport = SELECTREPORT_WELLKNOWN;
   1168 			continue;
   1169 		}
   1170 		if (strcmp("all", argv[i]) == 0) {
   1171 			cmd.selectreport = SELECTREPORT_ALL;
   1172 			continue;
   1173 		}
   1174 		sel = strtoul(argv[i], &endp, 0);
   1175 		if (*endp != '\0' || sel > 255)
   1176 			errx(1, "Unknown select report '%s'", argv[i]);
   1177 		cmd.selectreport = sel;
   1178 	}
   1179 
   1180 	_lto4b(dlen, &cmd.alloclen[0]);
   1181 	cmd.control = 0x00;
   1182 
   1183 	scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_READ);
   1184 
   1185 	len = _4btol(data->header.length);
   1186 	if (len > dlen) {
   1187 		/* XXX reallocate and retry */
   1188 		printf("%s: report truncated %" PRIu32 "to %" PRIu32 "\n",
   1189 		    dvname, len, dlen);
   1190 		len = dlen;
   1191 	}
   1192 
   1193 	count = len / sizeof(data->desc[0]);
   1194 
   1195 	for (idx = 0; idx < count; idx++) {
   1196 		lun = _8btol(data->desc[idx].lun);
   1197 
   1198 		/*
   1199 		 * swizzle bits so that LUNs 0..255 are
   1200 		 * mapped to numbers 0..255
   1201 		 */
   1202 		lun = (lun & 0xffff000000000000ull) >> 48
   1203 		    | (lun & 0x0000ffff00000000ull) >> 16
   1204 		    | (lun & 0x00000000ffff0000ull) << 16
   1205 		    | (lun & 0x000000000000ffffull) << 48;
   1206 
   1207 		printf("%s: lun %" PRIu64 "\n", dvname, lun);
   1208 	}
   1209 
   1210 	free(data);
   1211 }
   1212 
   1213 /*
   1214  * device_getrealloc:
   1215  *
   1216  *	Get the automatic reallocation parameters for a SCSI disk.
   1217  */
   1218 static void
   1219 device_getrealloc(int argc, char *argv[])
   1220 {
   1221 	struct {
   1222 		struct scsi_mode_parameter_header_6 header;
   1223 		struct scsi_general_block_descriptor blk_desc;
   1224 		struct page_err_recov err_recov_params;
   1225 	} data;
   1226 	u_int8_t flags;
   1227 
   1228 	/* No arguments. */
   1229 	if (argc != 0)
   1230 		usage();
   1231 
   1232 	scsi_mode_sense(fd, 0x01, 0x00, &data, sizeof(data));
   1233 
   1234 	flags = data.err_recov_params.flags;
   1235 	if ((flags & (ERR_RECOV_ARRE | ERR_RECOV_AWRE)) == 0)
   1236 		printf("%s: no automatic reallocation enabled\n", dvname);
   1237 	else {
   1238 		printf("%s: automatic read reallocation %senabled\n", dvname,
   1239 		    (flags & ERR_RECOV_ARRE) ? "" : "not ");
   1240 		printf("%s: automatic write reallocation %senabled\n", dvname,
   1241 		    (flags & ERR_RECOV_AWRE) ? "" : "not ");
   1242 	}
   1243 	printf("%s: error recovery parameters are %ssavable\n", dvname,
   1244 	    (data.err_recov_params.pg_code & PGCODE_PS) ? "" : "not ");
   1245 }
   1246 
   1247 /*
   1248  * device_setrealloc:
   1249  *
   1250  *	Set the automatic reallocation parameters for a SCSI disk.
   1251  */
   1252 static void
   1253 device_setrealloc(int argc, char *argv[])
   1254 {
   1255 	struct {
   1256 		struct scsi_mode_parameter_header_6 header;
   1257 		struct scsi_general_block_descriptor blk_desc;
   1258 		struct page_err_recov err_recov_params;
   1259 	} data;
   1260 	int dlen;
   1261 	u_int8_t flags, byte2;
   1262 
   1263 	if (argc > 2 || argc == 0)
   1264 		usage();
   1265 
   1266 	flags = 0;
   1267 	byte2 = 0;
   1268 	if (strcmp(argv[0], "none") == 0)
   1269 		flags = 0;
   1270 	else if (strcmp(argv[0], "r") == 0)
   1271 		flags = ERR_RECOV_ARRE;
   1272 	else if (strcmp(argv[0], "w") == 0)
   1273 		flags = ERR_RECOV_AWRE;
   1274 	else if (strcmp(argv[0], "rw") == 0)
   1275 		flags = ERR_RECOV_ARRE | ERR_RECOV_AWRE;
   1276 	else
   1277 		usage();
   1278 
   1279 	if (argc == 2) {
   1280 		if (strcmp(argv[1], "save") == 0)
   1281 			byte2 = SMS_SP;
   1282 		else
   1283 			usage();
   1284 	}
   1285 
   1286 	scsi_mode_sense(fd, 0x01, 0x00, &data, sizeof(data));
   1287 
   1288 	data.err_recov_params.pg_code &= PGCODE_MASK;
   1289 	data.err_recov_params.flags &= ~(ERR_RECOV_ARRE | ERR_RECOV_AWRE);
   1290 	data.err_recov_params.flags |= flags;
   1291 
   1292 	data.header.data_length = 0;
   1293 
   1294 	dlen = sizeof(data.header) + sizeof(data.blk_desc) + 2 +
   1295 	    data.err_recov_params.pg_length;
   1296 
   1297 	scsi_mode_select(fd, byte2, &data, dlen);
   1298 }
   1299 
   1300 /*
   1301  * device_prevent:
   1302  *
   1303  *      Issue a prevent to a SCSI device.
   1304  */
   1305 static void
   1306 device_prevent(int argc, char *argv[])
   1307 {
   1308 	struct scsi_prevent_allow_medium_removal cmd;
   1309 
   1310 	/* No arguments. */
   1311 	if (argc != 0)
   1312 		usage();
   1313 
   1314 	memset(&cmd, 0, sizeof(cmd));
   1315 
   1316 	cmd.opcode = SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL;
   1317 	cmd.how = SPAMR_PREVENT_DT;	/* XXX SMAMR_PREVENT_ALL? */
   1318 
   1319 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
   1320 
   1321 	return;
   1322 }
   1323 
   1324 /*
   1325  * device_allow:
   1326  *
   1327  *      Issue a stop to a SCSI device.
   1328  */
   1329 static void
   1330 device_allow(int argc, char *argv[])
   1331 {
   1332 	struct scsi_prevent_allow_medium_removal cmd;
   1333 
   1334 	/* No arguments. */
   1335 	if (argc != 0)
   1336 		usage();
   1337 
   1338 	memset(&cmd, 0, sizeof(cmd));
   1339 
   1340 	cmd.opcode = SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL;
   1341 	cmd.how = SPAMR_ALLOW;
   1342 
   1343 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
   1344 
   1345 	return;
   1346 }
   1347 
   1348 /*
   1349  * device_start:
   1350  *
   1351  *      Issue a start to a SCSI device.
   1352  */
   1353 static void
   1354 device_start(int argc, char *argv[])
   1355 {
   1356 	struct scsipi_start_stop cmd;
   1357 
   1358 	/* No arguments. */
   1359 	if (argc != 0)
   1360 		usage();
   1361 
   1362 	memset(&cmd, 0, sizeof(cmd));
   1363 
   1364 	cmd.opcode = START_STOP;
   1365 	cmd.how = SSS_START;
   1366 
   1367 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 30000, 0);
   1368 
   1369 	return;
   1370 }
   1371 
   1372 /*
   1373  * device_stop:
   1374  *
   1375  *      Issue a stop to a SCSI device.
   1376  */
   1377 static void
   1378 device_stop(int argc, char *argv[])
   1379 {
   1380 	struct scsipi_start_stop cmd;
   1381 
   1382 	/* No arguments. */
   1383 	if (argc != 0)
   1384 		usage();
   1385 
   1386 	memset(&cmd, 0, sizeof(cmd));
   1387 
   1388 	cmd.opcode = START_STOP;
   1389 	cmd.how = SSS_STOP;
   1390 
   1391 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 30000, 0);
   1392 
   1393 	return;
   1394 }
   1395 
   1396 /*
   1397  * device_tur:
   1398  *
   1399  *	Issue a TEST UNIT READY to a SCSI device.
   1400  */
   1401 static void
   1402 device_tur(int argc, char *argv[])
   1403 {
   1404 	struct scsi_test_unit_ready cmd;
   1405 
   1406 	/* No arguments. */
   1407 	if (argc != 0)
   1408 		usage();
   1409 
   1410 	memset(&cmd, 0, sizeof(cmd));
   1411 
   1412 	cmd.opcode = SCSI_TEST_UNIT_READY;
   1413 
   1414 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
   1415 
   1416 	return;
   1417 }
   1418 
   1419 /*
   1420  * BUS COMMANDS
   1421  */
   1422 
   1423 /*
   1424  * bus_reset:
   1425  *
   1426  *	Issue a reset to a SCSI bus.
   1427  */
   1428 static void
   1429 bus_reset(int argc, char *argv[])
   1430 {
   1431 
   1432 	/* No arguments. */
   1433 	if (argc != 0)
   1434 		usage();
   1435 
   1436 	if (ioctl(fd, SCBUSIORESET, NULL) != 0)
   1437 		err(1, "SCBUSIORESET");
   1438 
   1439 	return;
   1440 }
   1441 
   1442 /*
   1443  * bus_scan:
   1444  *
   1445  *	Rescan a SCSI bus for new devices.
   1446  */
   1447 static void
   1448 bus_scan(int argc, char *argv[])
   1449 {
   1450 	struct scbusioscan_args args;
   1451 	char *cp;
   1452 
   1453 	/* Must have two args: target lun */
   1454 	if (argc != 2)
   1455 		usage();
   1456 
   1457 	if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0)
   1458 		args.sa_target = -1;
   1459 	else {
   1460 		args.sa_target = strtol(argv[0], &cp, 10);
   1461 		if (*cp != '\0' || args.sa_target < 0)
   1462 			errx(1, "invalid target: %s", argv[0]);
   1463 	}
   1464 
   1465 	if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0)
   1466 		args.sa_lun = -1;
   1467 	else {
   1468 		args.sa_lun = strtol(argv[1], &cp, 10);
   1469 		if (*cp != '\0' || args.sa_lun < 0)
   1470 			errx(1, "invalid lun: %s", argv[1]);
   1471 	}
   1472 
   1473 	if (ioctl(fd, SCBUSIOSCAN, &args) != 0)
   1474 		err(1, "SCBUSIOSCAN");
   1475 
   1476 	return;
   1477 }
   1478 
   1479 /*
   1480  * bus_detach:
   1481  *
   1482  *	detach SCSI devices from a bus.
   1483  */
   1484 static void
   1485 bus_detach(int argc, char *argv[])
   1486 {
   1487 	struct scbusiodetach_args args;
   1488 	char *cp;
   1489 
   1490 	/* Must have two args: target lun */
   1491 	if (argc != 2)
   1492 		usage();
   1493 
   1494 	if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0)
   1495 		args.sa_target = -1;
   1496 	else {
   1497 		args.sa_target = strtol(argv[0], &cp, 10);
   1498 		if (*cp != '\0' || args.sa_target < 0)
   1499 			errx(1, "invalid target: %s", argv[0]);
   1500 	}
   1501 
   1502 	if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0)
   1503 		args.sa_lun = -1;
   1504 	else {
   1505 		args.sa_lun = strtol(argv[1], &cp, 10);
   1506 		if (*cp != '\0' || args.sa_lun < 0)
   1507 			errx(1, "invalid lun: %s", argv[1]);
   1508 	}
   1509 
   1510 	if (ioctl(fd, SCBUSIODETACH, &args) != 0)
   1511 		err(1, "SCBUSIODETACH");
   1512 
   1513 	return;
   1514 }
   1515