xref: /netbsd-src/sbin/scsictl/scsictl.c (revision f8cf1a9151c7af1cb0bd8b09c13c66bca599c027)
1 /*	$NetBSD: scsictl.c,v 1.41 2024/11/09 11:09:40 mlelstv 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.41 2024/11/09 11:09:40 mlelstv Exp $");
40 #endif
41 
42 
43 #include <sys/param.h>
44 #include <sys/ioctl.h>
45 #include <sys/scsiio.h>
46 #include <err.h>
47 #include <errno.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 sepecified, 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 
332 	printf("%s: scsibus%d target %d lun %d %s",
333 	       dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target,
334 	       dvaddr.addr.scsi.lun, msg);
335 
336 	/* device did not return either defect list. */
337 	if ((data->flags & RDD_P_G_MASK) == 0)
338 		return;
339 
340 	switch (dlfmt) {
341 	case RDD_BF:
342 		defects = _2btol(data->length) /
343 				sizeof(struct scsi_defect_descriptor_bf);
344 		pfunc = print_bf_dd;
345 		strcpy(msg, "block address\n"
346 			    "-------------\n");
347 		break;
348 	case RDD_BFIF:
349 		defects = _2btol(data->length) /
350 				sizeof(struct scsi_defect_descriptor_bfif);
351 		pfunc = print_bfif_dd;
352 		strcpy(msg, "              bytes from\n"
353 			    "cylinder head   index\n"
354 			    "-------- ---- ----------\n");
355 		break;
356 	case RDD_PSF:
357 		defects = _2btol(data->length) /
358 				sizeof(struct scsi_defect_descriptor_psf);
359 		pfunc = print_psf_dd;
360 		strcpy(msg, "cylinder head   sector\n"
361 			    "-------- ---- ----------\n");
362 		break;
363 	}
364 
365 	/* device did not return any defects. */
366 	if (defects == 0) {
367 		printf(": none\n");
368 		return;
369 	}
370 
371 	printf(": %d\n", defects);
372 
373 	/* print heading. */
374 	printf("%s", msg);
375 
376 	/* print defect list. */
377 	for (i = 0 ; i < defects; i++) {
378 		pfunc(&data->defect_descriptor[i]);
379 	}
380 
381 	free(data);
382 	return;
383 }
384 
385 /*
386  * print_bf_dd:
387  *
388  *	Print a block format defect descriptor.
389  */
390 static void
391 print_bf_dd(union scsi_defect_descriptor *dd)
392 {
393 	u_int32_t block;
394 
395 	block = _4btol(dd->bf.block_address);
396 
397 	printf("%13u\n", block);
398 }
399 
400 #define DEFECTIVE_TRACK	0xffffffff
401 
402 /*
403  * print_bfif_dd:
404  *
405  *	Print a bytes from index format defect descriptor.
406  */
407 static void
408 print_bfif_dd(union scsi_defect_descriptor *dd)
409 {
410 	u_int32_t cylinder;
411 	u_int32_t head;
412 	u_int32_t bytes_from_index;
413 
414 	cylinder = _3btol(dd->bfif.cylinder);
415 	head = dd->bfif.head;
416 	bytes_from_index = _4btol(dd->bfif.bytes_from_index);
417 
418 	printf("%8u %4u ", cylinder, head);
419 
420 	if (bytes_from_index == DEFECTIVE_TRACK)
421 		printf("entire track defective\n");
422 	else
423 		printf("%10u\n", bytes_from_index);
424 }
425 
426 /*
427  * print_psf_dd:
428  *
429  *	Print a physical sector format defect descriptor.
430  */
431 static void
432 print_psf_dd(union scsi_defect_descriptor *dd)
433 {
434 	u_int32_t cylinder;
435 	u_int32_t head;
436 	u_int32_t sector;
437 
438 	cylinder = _3btol(dd->psf.cylinder);
439 	head = dd->psf.head;
440 	sector = _4btol(dd->psf.sector);
441 
442 	printf("%8u %4u ", cylinder, head);
443 
444 	if (sector == DEFECTIVE_TRACK)
445 		printf("entire track defective\n");
446 	else
447 		printf("%10u\n", sector);
448 }
449 
450 /*
451  * device_format:
452  *
453  *	Format a direct access device.
454  */
455 static void
456 device_format(int argc, char *argv[])
457 {
458 	u_int32_t blksize;
459 	int i, j, immediate;
460 #define	PC	(65536/10)
461 	static int complete[] = {
462 	    PC*1, PC*2, PC*3, PC*4, PC*5, PC*6, PC*7, PC*8, PC*9, 65536
463 	};
464 	char *cp, buffer[64];
465 	struct scsi_sense_data sense;
466 	struct scsi_format_unit cmd;
467 	struct {
468 		struct scsi_format_unit_defect_list_header header;
469 		/* optional initialization pattern */
470 		/* optional defect list */
471 	} dfl;
472 	struct {
473 		struct scsi_mode_parameter_header_6 header;
474 		struct scsi_general_block_descriptor blk_desc;
475 		struct page_disk_format format_page;
476 	} mode_page;
477 	struct {
478 		struct scsi_mode_parameter_header_6 header;
479 		struct scsi_general_block_descriptor blk_desc;
480 	} data_select;
481 
482 
483 	/* Blocksize is an optional argument. */
484 	if (argc > 2)
485 		usage();
486 
487 	/*
488 	 * Loop doing Request Sense to clear any pending Unit Attention.
489 	 *
490 	 * Multiple conditions may exist on the drive which are returned
491 	 * in priority order.
492 	 */
493 	for (i = 0; i < 8; i++) {
494 		scsi_request_sense(fd, &sense, sizeof (sense));
495 		if ((j = SSD_SENSE_KEY(sense.flags)) == SKEY_NO_SENSE)
496 			break;
497 	}
498 	/*
499 	 * Make sure we cleared any pending Unit Attention
500 	 */
501 	if (j != SKEY_NO_SENSE) {
502 		cp = scsi_decode_sense((const unsigned char *) &sense, 2,
503 		    buffer, sizeof (buffer));
504 		errx(1, "failed to clean Unit Attention: %s", cp);
505 	}
506 
507 	/*
508 	 * Get the DISK FORMAT mode page.  SCSI-2 recommends specifying the
509 	 * interleave read from this page in the FORMAT UNIT command.
510 	 */
511 	scsi_mode_sense(fd, 0x03, 0x00, &mode_page, sizeof(mode_page));
512 
513 	j = (mode_page.format_page.bytes_s[0] << 8) |
514 	    (mode_page.format_page.bytes_s[1]);
515 
516 	if (j != DEV_BSIZE)
517 		printf("current disk sector size: %d\n", j);
518 
519 	memset(&cmd, 0, sizeof(cmd));
520 
521 	cmd.opcode = SCSI_FORMAT_UNIT;
522 	memcpy(cmd.interleave, mode_page.format_page.interleave,
523 	    sizeof(cmd.interleave));
524 
525 	/*
526 	 * The blocksize on the device is only changed if the user
527 	 * specified a new blocksize. If not specified the blocksize
528 	 * used for the device will be the Default value in the device.
529 	 * We don't specify the number of blocks since the format
530 	 * command will always reformat the entire drive.  Also by
531 	 * not specifying a block count the drive will reset the
532 	 * block count to the maximum available after the format
533 	 * completes if the blocksize was changed in the format.
534 	 * Finally, the new disk geometry will not but updated on
535 	 * the drive in permanent storage until _AFTER_ the format
536 	 * completes successfully.
537 	 */
538 	if (argc > 0) {
539 		blksize = strtoul(argv[0], &cp, 10);
540 		if (*cp != '\0')
541 			errx(1, "invalid block size: %s", argv[0]);
542 
543 		memset(&data_select, 0, sizeof(data_select));
544 
545 		data_select.header.blk_desc_len =
546 		    sizeof(struct scsi_general_block_descriptor);
547 		/*
548 		 * blklen in desc is 3 bytes with a leading reserved byte
549 		 */
550 		_lto4b(blksize, &data_select.blk_desc.reserved);
551 
552 		/*
553 		 * Issue Mode Select to modify the device blocksize to be
554 		 * used on the Format.  The modified device geometry will
555 		 * be stored as Current and Saved Page 3 parameters when
556 		 * the Format completes.
557 		 */
558 		scsi_mode_select(fd, 0, &data_select, sizeof(data_select));
559 
560 		/*
561 		 * Since user specified a specific block size make sure it
562 		 * gets stored in the device when the format completes.
563 		 *
564 		 * Also scrub the defect list back to the manufacturers
565 		 * original.
566 		 */
567 		cmd.flags = SFU_CMPLST | SFU_FMTDATA;
568 	}
569 
570 	memset(&dfl, 0, sizeof(dfl));
571 
572 	if (argc > 1 && strncmp(argv[1], "imm", 3) == 0) {
573 		/*
574 		 * Signal target for an immediate return from Format.
575 		 *
576 		 * We'll poll for completion status.
577 		 */
578 		dfl.header.flags = DLH_IMMED;
579 		immediate = 1;
580 	} else {
581 		immediate = 0;
582 	}
583 
584 	scsi_command(fd, &cmd, sizeof(cmd), &dfl, sizeof(dfl),
585 	    8 * 60 * 60 * 1000, SCCMD_WRITE);
586 
587 	/*
588 	 * Poll device for completion of Format
589 	 */
590 	if (immediate) {
591 		i = 0;
592 		printf("formatting.");
593 		fflush(stdout);
594 		do {
595 			scsireq_t req;
596 			struct scsi_test_unit_ready tcmd;
597 
598 			memset(&tcmd, 0, sizeof(tcmd));
599 			tcmd.opcode = SCSI_TEST_UNIT_READY;
600 
601 			memset(&req, 0, sizeof(req));
602 			memcpy(req.cmd, &tcmd, 6);
603 			req.cmdlen = 6;
604 			req.timeout = 10000;
605 			req.senselen = SENSEBUFLEN;
606 
607 			if (ioctl(fd, SCIOCCOMMAND, &req) == -1) {
608 				err(1, "SCIOCCOMMAND");
609 			}
610 
611 			if (req.retsts == SCCMD_OK) {
612 				break;
613 			} else if (req.retsts == SCCMD_TIMEOUT) {
614 				fprintf(stderr, "%s: SCSI command timed out",
615 				    dvname);
616 				break;
617 			} else if (req.retsts == SCCMD_BUSY) {
618 				fprintf(stderr, "%s: device is busy",
619 				    dvname);
620 				break;
621 			} else if (req.retsts != SCCMD_SENSE) {
622 				fprintf(stderr,
623 				    "%s: device had unknown status %x", dvname,
624 				    req.retsts);
625 				break;
626 			}
627 			memcpy(&sense, req.sense, sizeof(sense));
628 			if (sense.sks.sks_bytes[0] & SSD_SKSV) {
629 				j = (sense.sks.sks_bytes[1] << 8) |
630 				    (sense.sks.sks_bytes[2]);
631 				if (j >= complete[i]) {
632 					printf(".%d0%%.", ++i);
633 					fflush(stdout);
634 				}
635 			}
636 			sleep(10);
637 		} while (SSD_SENSE_KEY(sense.flags) == SKEY_NOT_READY);
638 		printf(".100%%..done.\n");
639 	}
640 	return;
641 }
642 
643 static void
644 print_designator(const char *pre, struct scsipi_inquiry_evpd_device_id *did)
645 {
646 	char buf[252 * 4 + 1];
647 	unsigned assoc, proto, code, type;
648 	static const char *typestr[] = {
649 		"vendor",
650 		"t10",
651 		"eui64",
652 		"naa",
653 		"target port",
654 		"port group",
655 		"lun group",
656 		"md5",
657 		"scsi",
658 		"res9",
659 		"res10",
660 		"res11",
661 		"res12",
662 		"res13",
663 		"res14",
664 		"res15"
665 	};
666 	static const char *assocstr[] = {
667 		"lun",
668 		"port",
669 		"target",
670 		"reserved"
671 	};
672 	static const char *protostr[] = {
673 		"fibre channel",
674 		"obsolete",
675 		"ssa",
676 		"ieee1394",
677 		"rdma",
678 		"iSCSI",
679 		"SAS"
680 	};
681 	const unsigned maxproto = __arraycount(protostr) - 1;
682 	const unsigned isbinary =
683 	    __SHIFTOUT(SINQ_DEVICE_ID_CODESET_BINARY, SINQ_DEVICE_ID_CODESET);
684 	unsigned k;
685 
686 	assoc = __SHIFTOUT(did->flags, SINQ_DEVICE_ID_ASSOCIATION);
687 	proto = __SHIFTOUT(did->pc, SINQ_DEVICE_ID_PROTOCOL);
688 	code = __SHIFTOUT(did->pc, SINQ_DEVICE_ID_CODESET);
689 	type = __SHIFTOUT(did->flags, SINQ_DEVICE_ID_TYPE);
690 
691 	printf("%s%s", pre, assocstr[assoc]);
692 	if (did->flags & SINQ_DEVICE_ID_PIV) {
693 		if (proto > maxproto)
694 			printf(" proto%u", proto);
695 		else
696 			printf(" %s", protostr[proto]);
697 	}
698 	printf(" %s: ", typestr[type]);
699 
700 	if (code == isbinary) {
701 		for (k=0; k<did->designator_length; ++k) {
702 			printf("%02x", did->designator[k]);
703 		}
704 		printf("\n");
705 	} else {
706 		scsi_strvis(buf, sizeof(buf), (char *)did->designator,
707 		    did->designator_length);
708 		printf("%s\n", buf);
709 	}
710 }
711 
712 /*
713  * device_identify:
714  *
715  *	Display the identity of the device, including its SCSI bus,
716  *	target, lun, and its vendor/product/revision information.
717  *      Optionally query and display vpd identification data.
718  */
719 static void
720 device_identify(int argc, char *argv[])
721 {
722 	struct scsipi_inquiry_data inqbuf;
723 	struct {
724 		struct scsipi_inquiry_evpd_header h;
725 		uint8_t d[255 - sizeof(struct scsipi_inquiry_evpd_header)];
726 	} evpdbuf;
727 	struct scsipi_inquiry cmd;
728 	unsigned len, rlen;
729 	struct scsipi_inquiry_evpd_serial *ser;
730 	struct scsipi_inquiry_evpd_device_id *did;
731 	int has_serial;
732 	int has_device_id;
733 	bool getvpd = false;
734 	int i;
735 
736 	/* x4 in case every character is escaped, +1 for NUL. */
737 	char vendor[(sizeof(inqbuf.vendor) * 4) + 1],
738 	     product[(sizeof(inqbuf.product) * 4) + 1],
739 	     revision[(sizeof(inqbuf.revision) * 4) + 1],
740 	     ident[252 * 4 + 1];
741 
742 	/* Check optional arguments */
743 	for (i = 0; i < argc; i++) {
744 		if (strncmp("vpd", argv[i], 3) == 0) {
745 			getvpd = true;
746 			continue;
747 		}
748 		usage();
749 	}
750 
751 	memset(&cmd, 0, sizeof(cmd));
752 	memset(&inqbuf, 0, sizeof(inqbuf));
753 
754 	cmd.opcode = INQUIRY;
755 	cmd.length = sizeof(inqbuf);
756 
757 	scsi_command(fd, &cmd, sizeof(cmd), &inqbuf, sizeof(inqbuf),
758 	    10000, SCCMD_READ);
759 
760 	scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor,
761 	    sizeof(inqbuf.vendor));
762 	scsi_strvis(product, sizeof(product), inqbuf.product,
763 	    sizeof(inqbuf.product));
764 	scsi_strvis(revision, sizeof(revision), inqbuf.revision,
765 	    sizeof(inqbuf.revision));
766 
767 	printf("%s: scsibus%d target %d lun %d <%s, %s, %s>\n",
768 	    dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target,
769 	    dvaddr.addr.scsi.lun, vendor, product, revision);
770 
771 	if (!getvpd)
772 		return;
773 
774 	cmd.byte2 |= SINQ_EVPD;
775 	cmd.pagecode = SINQ_VPD_PAGES;
776 
777 	scsi_command(fd, &cmd, sizeof(cmd), &evpdbuf, sizeof(evpdbuf),
778 	    10000, SCCMD_READ);
779 
780 	len = be16dec(evpdbuf.h.length);
781 	if (len > sizeof(evpdbuf.d))
782 		len = 0;
783 
784 	has_serial = memchr(evpdbuf.d, SINQ_VPD_SERIAL, len) != NULL;
785 	has_device_id = memchr(evpdbuf.d, SINQ_VPD_DEVICE_ID, len) != NULL;
786 
787 	if (has_serial) {
788 
789 		cmd.byte2 |= SINQ_EVPD;
790 		cmd.pagecode = SINQ_VPD_SERIAL;
791 
792 		scsi_command(fd, &cmd, sizeof(cmd), &evpdbuf, sizeof(evpdbuf),
793 		    10000, SCCMD_READ);
794 
795 		len = be16dec(evpdbuf.h.length);
796 		if (len > sizeof(evpdbuf.d))
797 			len = 0;
798 
799 		ser = (struct scsipi_inquiry_evpd_serial *)&evpdbuf.d;
800 		scsi_strvis(ident, sizeof(ident), (char *)ser->serial_number, len);
801 		printf("VPD Serial:\n");
802 		printf("\t%s\n", ident);
803 
804 	}
805 
806 	if (has_device_id) {
807 
808 		cmd.byte2 |= SINQ_EVPD;
809 		cmd.pagecode = SINQ_VPD_DEVICE_ID;
810 
811 		scsi_command(fd, &cmd, sizeof(cmd), &evpdbuf, sizeof(evpdbuf),
812 		    10000, SCCMD_READ);
813 
814 		len = be16dec(evpdbuf.h.length);
815 		if (len > sizeof(evpdbuf.d))
816 			len = 0;
817 
818 		printf("VPD Device IDs:\n");
819 
820 		for (unsigned off=0; off<len - sizeof(*did); off += rlen) {
821 			did = (struct scsipi_inquiry_evpd_device_id *)&evpdbuf.d[off];
822 			rlen = sizeof(*did) + did->designator_length - 1;
823 			if (off + rlen > len)
824 				break;
825 
826 			print_designator("\t", did);
827 		}
828 	}
829 
830 	return;
831 }
832 
833 /*
834  * device_reassign:
835  *
836  *	Reassign bad blocks on a direct access device.
837  */
838 static void
839 device_reassign(int argc, char *argv[])
840 {
841 	struct scsi_reassign_blocks cmd;
842 	struct scsi_reassign_blocks_data *data;
843 	size_t dlen;
844 	u_int32_t blkno;
845 	int i;
846 	char *cp;
847 
848 	/* We get a list of block numbers. */
849 	if (argc < 1)
850 		usage();
851 
852 	/*
853 	 * Allocate the reassign blocks descriptor.  The 4 comes from the
854 	 * size of the block address in the defect descriptor.
855 	 */
856 	dlen = sizeof(struct scsi_reassign_blocks_data) + ((argc - 1) * 4);
857 	data = malloc(dlen);
858 	if (data == NULL)
859 		errx(1, "unable to allocate defect descriptor");
860 	memset(data, 0, dlen);
861 
862 	cmd.opcode = SCSI_REASSIGN_BLOCKS;
863 	cmd.byte2 = 0;
864 	cmd.unused[0] = 0;
865 	cmd.unused[1] = 0;
866 	cmd.unused[2] = 0;
867 	cmd.control = 0;
868 
869 	/* Defect descriptor length. */
870 	_lto2b(argc * 4, data->length);
871 
872 	/* Build the defect descriptor list. */
873 	for (i = 0; i < argc; i++) {
874 		blkno = strtoul(argv[i], &cp, 10);
875 		if (*cp != '\0')
876 			errx(1, "invalid block number: %s", argv[i]);
877 		_lto4b(blkno, data->defect_descriptor[i].dlbaddr);
878 	}
879 
880 	scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_WRITE);
881 
882 	free(data);
883 	return;
884 }
885 
886 /*
887  * device_release:
888  *
889  *	Issue a RELEASE command to a SCSI device.
890  */
891 #ifndef	SCSI_RELEASE
892 #define	SCSI_RELEASE	0x17
893 #endif
894 static void
895 device_release(int argc, char *argv[])
896 {
897 	struct scsi_test_unit_ready cmd;	/* close enough */
898 
899 	/* No arguments. */
900 	if (argc != 0)
901 		usage();
902 
903 	memset(&cmd, 0, sizeof(cmd));
904 
905 	cmd.opcode = SCSI_RELEASE;
906 
907 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
908 
909 	return;
910 }
911 
912 
913 
914 /*
915  * device_reserve:
916  *
917  *	Issue a RESERVE command to a SCSI device.
918  */
919 #ifndef	SCSI_RESERVE
920 #define	SCSI_RESERVE	0x16
921 #endif
922 static void
923 device_reserve(int argc, char *argv[])
924 {
925 	struct scsi_test_unit_ready cmd;	/* close enough */
926 
927 	/* No arguments. */
928 	if (argc != 0)
929 		usage();
930 
931 	memset(&cmd, 0, sizeof(cmd));
932 
933 	cmd.opcode = SCSI_RESERVE;
934 
935 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
936 
937 	return;
938 }
939 
940 /*
941  * device_reset:
942  *
943  *	Issue a reset to a SCSI device.
944  */
945 static void
946 device_reset(int argc, char *argv[])
947 {
948 
949 	/* No arguments. */
950 	if (argc != 0)
951 		usage();
952 
953 	if (ioctl(fd, SCIOCRESET, NULL) != 0)
954 		err(1, "SCIOCRESET");
955 
956 	return;
957 }
958 
959 /*
960  * device_debug:
961  *
962  *	Set debug level to a SCSI device.
963  *	scsipi will print anything iff SCSIPI_DEBUG set in config.
964  */
965 static void
966 device_debug(int argc, char *argv[])
967 {
968 	int lvl;
969 
970 	if (argc < 1)
971 		usage();
972 
973 	lvl = atoi(argv[0]);
974 
975 	if (ioctl(fd, SCIOCDEBUG, &lvl) != 0)
976 		err(1, "SCIOCDEBUG");
977 
978 	return;
979 }
980 
981 /*
982  * device_getcache:
983  *
984  *	Get the caching parameters for a SCSI disk.
985  */
986 static void
987 device_getcache(int argc, char *argv[])
988 {
989 	struct {
990 		struct scsi_mode_parameter_header_6 header;
991 		struct scsi_general_block_descriptor blk_desc;
992 		struct page_caching caching_params;
993 	} data;
994 
995 	/* No arguments. */
996 	if (argc != 0)
997 		usage();
998 
999 	scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data));
1000 
1001 	if ((data.caching_params.flags & (CACHING_RCD|CACHING_WCE)) ==
1002 	    CACHING_RCD)
1003 		printf("%s: no caches enabled\n", dvname);
1004 	else {
1005 		printf("%s: read cache %senabled\n", dvname,
1006 		    (data.caching_params.flags & CACHING_RCD) ? "not " : "");
1007 		printf("%s: write-back cache %senabled\n", dvname,
1008 		    (data.caching_params.flags & CACHING_WCE) ? "" : "not ");
1009 	}
1010 	printf("%s: caching parameters are %ssavable\n", dvname,
1011 	    (data.caching_params.pg_code & PGCODE_PS) ? "" : "not ");
1012 }
1013 
1014 /*
1015  * device_setcache:
1016  *
1017  *	Set cache enables for a SCSI disk.
1018  */
1019 static void
1020 device_setcache(int argc, char *argv[])
1021 {
1022 	struct {
1023 		struct scsi_mode_parameter_header_6 header;
1024 		struct scsi_general_block_descriptor blk_desc;
1025 		struct page_caching caching_params;
1026 	} data;
1027 	int dlen;
1028 	u_int8_t flags, byte2;
1029 
1030 	if (argc > 2 || argc == 0)
1031 		usage();
1032 
1033 	flags = 0;
1034 	byte2 = 0;
1035 	if (strcmp(argv[0], "none") == 0)
1036 		flags = CACHING_RCD;
1037 	else if (strcmp(argv[0], "r") == 0)
1038 		flags = 0;
1039 	else if (strcmp(argv[0], "w") == 0)
1040 		flags = CACHING_RCD|CACHING_WCE;
1041 	else if (strcmp(argv[0], "rw") == 0)
1042 		flags = CACHING_WCE;
1043 	else
1044 		usage();
1045 
1046 	if (argc == 2) {
1047 		if (strcmp(argv[1], "save") == 0)
1048 			byte2 = SMS_SP;
1049 		else
1050 			usage();
1051 	}
1052 
1053 	scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data));
1054 
1055 	data.caching_params.pg_code &= PGCODE_MASK;
1056 	data.caching_params.flags =
1057 	    (data.caching_params.flags & ~(CACHING_RCD|CACHING_WCE)) | flags;
1058 
1059 	data.caching_params.cache_segment_size[0] = 0;
1060 	data.caching_params.cache_segment_size[1] = 0;
1061 
1062 	data.header.data_length = 0;
1063 
1064 	dlen = sizeof(data.header) + sizeof(data.blk_desc) + 2 +
1065 	    data.caching_params.pg_length;
1066 
1067 	scsi_mode_select(fd, byte2, &data, dlen);
1068 }
1069 
1070 /*
1071  * device_flushcache:
1072  *
1073  *	Issue a FLUSH CACHE command to a SCSI device.
1074  */
1075 #ifndef	SCSI_FLUSHCACHE
1076 #define	SCSI_FLUSHCACHE	0x35
1077 #endif
1078 static void
1079 device_flushcache(int argc, char *argv[])
1080 {
1081 	struct scsi_test_unit_ready cmd;	/* close enough */
1082 
1083 	/* No arguments. */
1084 	if (argc != 0)
1085 		usage();
1086 
1087 	memset(&cmd, 0, sizeof(cmd));
1088 
1089 	cmd.opcode = SCSI_FLUSHCACHE;
1090 
1091 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
1092 
1093 	return;
1094 }
1095 
1096 /*
1097  * device_setspeed:
1098  *
1099  *	Set rotation speed to a CD/DVD drive.
1100  */
1101 static void
1102 device_setspeed(int argc, char *argv[])
1103 {
1104 	u_char cmd[11];
1105 	u_char pd[28];
1106 	u_int32_t speed;
1107 
1108 	if (argc != 1)
1109 		usage();
1110 
1111 	speed = atoi(argv[0]) * 177;
1112 
1113 	memset(&pd, 0, sizeof(pd));
1114 	if (speed == 0)
1115 		pd[0] = 4; /* restore drive defaults */
1116 	pd[8] = 0xff;
1117 	pd[9] = 0xff;
1118 	pd[10] = 0xff;
1119 	pd[11] = 0xff;
1120 	pd[12] = pd[20] = (speed >> 24) & 0xff;
1121 	pd[13] = pd[21] = (speed >> 16) & 0xff;
1122 	pd[14] = pd[22] = (speed >> 8) & 0xff;
1123 	pd[15] = pd[23] = speed & 0xff;
1124 	pd[18] = pd[26] = 1000 >> 8;
1125 	pd[19] = pd[27] = 1000 & 0xff;
1126 
1127 	memset(&cmd, 0, sizeof(cmd));
1128 	cmd[0] = 0xb6;
1129 	cmd[10] = sizeof(pd);
1130 
1131 	scsi_command(fd, &cmd, sizeof(cmd), pd, sizeof(pd), 10000, SCCMD_WRITE);
1132 
1133 	return;
1134 }
1135 
1136 /*
1137  * device_reportluns:
1138  *
1139  *	Report the known LUNs to which the initiator can send commands
1140  */
1141 static void
1142 device_reportluns(int argc, char *argv[])
1143 {
1144 	struct scsi_report_luns cmd;
1145 	struct {
1146 		struct scsi_report_luns_header header;
1147 		struct scsi_report_luns_lun desc[1];
1148 	} *data;
1149 	u_int32_t dlen, len;
1150 	u_int64_t lun;
1151 	size_t count, idx;
1152 	unsigned long sel;
1153 	char *endp;
1154 	int i;
1155 
1156 	dlen = USHRT_MAX; /* good for > 8000 LUNs */
1157 	data = malloc(dlen);
1158 	if (data == NULL)
1159 		errx(1, "unable to allocate lun report");
1160 
1161 	memset(&cmd, 0, sizeof(cmd));
1162 	cmd.opcode = SCSI_REPORT_LUNS;
1163 	cmd.selectreport = SELECTREPORT_NORMAL;
1164 
1165 	/* determine which report to read. */
1166 	for (i = 0; i < argc; i++) {
1167 		if (strcmp("normal", argv[i]) == 0) {
1168 			cmd.selectreport = SELECTREPORT_NORMAL;
1169 			continue;
1170 		}
1171 		if (strcmp("wellknown", argv[i]) == 0) {
1172 			cmd.selectreport = SELECTREPORT_WELLKNOWN;
1173 			continue;
1174 		}
1175 		if (strcmp("all", argv[i]) == 0) {
1176 			cmd.selectreport = SELECTREPORT_ALL;
1177 			continue;
1178 		}
1179 		sel = strtoul(argv[i], &endp, 0);
1180 		if (*endp != '\0' || sel > 255)
1181 			errx(1, "Unknown select report '%s'", argv[i]);
1182 		cmd.selectreport = sel;
1183 	}
1184 
1185 	_lto4b(dlen, &cmd.alloclen[0]);
1186 	cmd.control = 0x00;
1187 
1188 	scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_READ);
1189 
1190 	len = _4btol(data->header.length);
1191 	if (len > dlen) {
1192 		/* XXX reallocate and retry */
1193 		printf("%s: report truncated %" PRIu32 "to %" PRIu32 "\n",
1194 		    dvname, len, dlen);
1195 		len = dlen;
1196 	}
1197 
1198 	count = len / sizeof(data->desc[0]);
1199 
1200 	for (idx=0; idx<count; ++idx) {
1201 		lun = _8btol(data->desc[idx].lun);
1202 
1203 		/*
1204 		 * swizzle bits so that LUNs 0..255 are
1205 		 * mapped to numbers 0..255
1206 		 */
1207 		lun = (lun & 0xffff000000000000ull) >> 48
1208 		    | (lun & 0x0000ffff00000000ull) >> 16
1209 		    | (lun & 0x00000000ffff0000ull) << 16
1210 		    | (lun & 0x000000000000ffffull) << 48;
1211 
1212 		printf("%s: lun %" PRIu64 "\n", dvname, lun);
1213 	}
1214 
1215 	free(data);
1216 }
1217 
1218 /*
1219  * device_getrealloc:
1220  *
1221  *	Get the automatic reallocation parameters for a SCSI disk.
1222  */
1223 static void
1224 device_getrealloc(int argc, char *argv[])
1225 {
1226 	struct {
1227 		struct scsi_mode_parameter_header_6 header;
1228 		struct scsi_general_block_descriptor blk_desc;
1229 		struct page_err_recov err_recov_params;
1230 	} data;
1231 	u_int8_t flags;
1232 
1233 	/* No arguments. */
1234 	if (argc != 0)
1235 		usage();
1236 
1237 	scsi_mode_sense(fd, 0x01, 0x00, &data, sizeof(data));
1238 
1239 	flags = data.err_recov_params.flags;
1240 	if ((flags & (ERR_RECOV_ARRE | ERR_RECOV_AWRE)) == 0)
1241 		printf("%s: no automatic reallocation enabled\n", dvname);
1242 	else {
1243 		printf("%s: automatic read reallocation %senabled\n", dvname,
1244 		    (flags & ERR_RECOV_ARRE) ? "" : "not ");
1245 		printf("%s: automatic write reallocation %senabled\n", dvname,
1246 		    (flags & ERR_RECOV_AWRE) ? "" : "not ");
1247 	}
1248 	printf("%s: error recovery parameters are %ssavable\n", dvname,
1249 	    (data.err_recov_params.pg_code & PGCODE_PS) ? "" : "not ");
1250 }
1251 
1252 /*
1253  * device_setrealloc:
1254  *
1255  *	Set the automatic reallocation parameters for a SCSI disk.
1256  */
1257 static void
1258 device_setrealloc(int argc, char *argv[])
1259 {
1260 	struct {
1261 		struct scsi_mode_parameter_header_6 header;
1262 		struct scsi_general_block_descriptor blk_desc;
1263 		struct page_err_recov err_recov_params;
1264 	} data;
1265 	int dlen;
1266 	u_int8_t flags, byte2;
1267 
1268 	if (argc > 2 || argc == 0)
1269 		usage();
1270 
1271 	flags = 0;
1272 	byte2 = 0;
1273 	if (strcmp(argv[0], "none") == 0)
1274 		flags = 0;
1275 	else if (strcmp(argv[0], "r") == 0)
1276 		flags = ERR_RECOV_ARRE;
1277 	else if (strcmp(argv[0], "w") == 0)
1278 		flags = ERR_RECOV_AWRE;
1279 	else if (strcmp(argv[0], "rw") == 0)
1280 		flags = ERR_RECOV_ARRE | ERR_RECOV_AWRE;
1281 	else
1282 		usage();
1283 
1284 	if (argc == 2) {
1285 		if (strcmp(argv[1], "save") == 0)
1286 			byte2 = SMS_SP;
1287 		else
1288 			usage();
1289 	}
1290 
1291 	scsi_mode_sense(fd, 0x01, 0x00, &data, sizeof(data));
1292 
1293 	data.err_recov_params.pg_code &= PGCODE_MASK;
1294 	data.err_recov_params.flags &= ~(ERR_RECOV_ARRE | ERR_RECOV_AWRE);
1295 	data.err_recov_params.flags |= flags;
1296 
1297 	data.header.data_length = 0;
1298 
1299 	dlen = sizeof(data.header) + sizeof(data.blk_desc) + 2 +
1300 	    data.err_recov_params.pg_length;
1301 
1302 	scsi_mode_select(fd, byte2, &data, dlen);
1303 }
1304 
1305 /*
1306  * device_prevent:
1307  *
1308  *      Issue a prevent to a SCSI device.
1309  */
1310 static void
1311 device_prevent(int argc, char *argv[])
1312 {
1313 	struct scsi_prevent_allow_medium_removal cmd;
1314 
1315 	/* No arguments. */
1316 	if (argc != 0)
1317 		usage();
1318 
1319 	memset(&cmd, 0, sizeof(cmd));
1320 
1321 	cmd.opcode = SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL;
1322 	cmd.how = SPAMR_PREVENT_DT;	/* XXX SMAMR_PREVENT_ALL? */
1323 
1324 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
1325 
1326 	return;
1327 }
1328 
1329 /*
1330  * device_allow:
1331  *
1332  *      Issue a stop to a SCSI device.
1333  */
1334 static void
1335 device_allow(int argc, char *argv[])
1336 {
1337 	struct scsi_prevent_allow_medium_removal cmd;
1338 
1339 	/* No arguments. */
1340 	if (argc != 0)
1341 		usage();
1342 
1343 	memset(&cmd, 0, sizeof(cmd));
1344 
1345 	cmd.opcode = SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL;
1346 	cmd.how = SPAMR_ALLOW;
1347 
1348 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
1349 
1350 	return;
1351 }
1352 
1353 /*
1354  * device_start:
1355  *
1356  *      Issue a start to a SCSI device.
1357  */
1358 static void
1359 device_start(int argc, char *argv[])
1360 {
1361 	struct scsipi_start_stop cmd;
1362 
1363 	/* No arguments. */
1364 	if (argc != 0)
1365 		usage();
1366 
1367 	memset(&cmd, 0, sizeof(cmd));
1368 
1369 	cmd.opcode = START_STOP;
1370 	cmd.how = SSS_START;
1371 
1372 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 30000, 0);
1373 
1374 	return;
1375 }
1376 
1377 /*
1378  * device_stop:
1379  *
1380  *      Issue a stop to a SCSI device.
1381  */
1382 static void
1383 device_stop(int argc, char *argv[])
1384 {
1385 	struct scsipi_start_stop cmd;
1386 
1387 	/* No arguments. */
1388 	if (argc != 0)
1389 		usage();
1390 
1391 	memset(&cmd, 0, sizeof(cmd));
1392 
1393 	cmd.opcode = START_STOP;
1394 	cmd.how = SSS_STOP;
1395 
1396 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 30000, 0);
1397 
1398 	return;
1399 }
1400 
1401 /*
1402  * device_tur:
1403  *
1404  *	Issue a TEST UNIT READY to a SCSI device.
1405  */
1406 static void
1407 device_tur(int argc, char *argv[])
1408 {
1409 	struct scsi_test_unit_ready cmd;
1410 
1411 	/* No arguments. */
1412 	if (argc != 0)
1413 		usage();
1414 
1415 	memset(&cmd, 0, sizeof(cmd));
1416 
1417 	cmd.opcode = SCSI_TEST_UNIT_READY;
1418 
1419 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
1420 
1421 	return;
1422 }
1423 
1424 /*
1425  * BUS COMMANDS
1426  */
1427 
1428 /*
1429  * bus_reset:
1430  *
1431  *	Issue a reset to a SCSI bus.
1432  */
1433 static void
1434 bus_reset(int argc, char *argv[])
1435 {
1436 
1437 	/* No arguments. */
1438 	if (argc != 0)
1439 		usage();
1440 
1441 	if (ioctl(fd, SCBUSIORESET, NULL) != 0)
1442 		err(1, "SCBUSIORESET");
1443 
1444 	return;
1445 }
1446 
1447 /*
1448  * bus_scan:
1449  *
1450  *	Rescan a SCSI bus for new devices.
1451  */
1452 static void
1453 bus_scan(int argc, char *argv[])
1454 {
1455 	struct scbusioscan_args args;
1456 	char *cp;
1457 
1458 	/* Must have two args: target lun */
1459 	if (argc != 2)
1460 		usage();
1461 
1462 	if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0)
1463 		args.sa_target = -1;
1464 	else {
1465 		args.sa_target = strtol(argv[0], &cp, 10);
1466 		if (*cp != '\0' || args.sa_target < 0)
1467 			errx(1, "invalid target: %s", argv[0]);
1468 	}
1469 
1470 	if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0)
1471 		args.sa_lun = -1;
1472 	else {
1473 		args.sa_lun = strtol(argv[1], &cp, 10);
1474 		if (*cp != '\0' || args.sa_lun < 0)
1475 			errx(1, "invalid lun: %s", argv[1]);
1476 	}
1477 
1478 	if (ioctl(fd, SCBUSIOSCAN, &args) != 0)
1479 		err(1, "SCBUSIOSCAN");
1480 
1481 	return;
1482 }
1483 
1484 /*
1485  * bus_detach:
1486  *
1487  *	detach SCSI devices from a bus.
1488  */
1489 static void
1490 bus_detach(int argc, char *argv[])
1491 {
1492 	struct scbusiodetach_args args;
1493 	char *cp;
1494 
1495 	/* Must have two args: target lun */
1496 	if (argc != 2)
1497 		usage();
1498 
1499 	if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0)
1500 		args.sa_target = -1;
1501 	else {
1502 		args.sa_target = strtol(argv[0], &cp, 10);
1503 		if (*cp != '\0' || args.sa_target < 0)
1504 			errx(1, "invalid target: %s", argv[0]);
1505 	}
1506 
1507 	if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0)
1508 		args.sa_lun = -1;
1509 	else {
1510 		args.sa_lun = strtol(argv[1], &cp, 10);
1511 		if (*cp != '\0' || args.sa_lun < 0)
1512 			errx(1, "invalid lun: %s", argv[1]);
1513 	}
1514 
1515 	if (ioctl(fd, SCBUSIODETACH, &args) != 0)
1516 		err(1, "SCBUSIODETACH");
1517 
1518 	return;
1519 }
1520