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