xref: /netbsd-src/sbin/scsictl/scsictl.c (revision 7eee88ed732a0783fde5e60b947e898502a47adb)
1*7eee88edSriastradh /*	$NetBSD: scsictl.c,v 1.42 2024/11/10 01:55:06 riastradh Exp $	*/
2c9a47c22Sthorpej 
3c9a47c22Sthorpej /*-
40683880eSthorpej  * Copyright (c) 1998, 2002 The NetBSD Foundation, Inc.
5c9a47c22Sthorpej  * All rights reserved.
6c9a47c22Sthorpej  *
7c9a47c22Sthorpej  * This code is derived from software contributed to The NetBSD Foundation
8c9a47c22Sthorpej  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9c9a47c22Sthorpej  * NASA Ames Research Center.
10c9a47c22Sthorpej  *
11c9a47c22Sthorpej  * Redistribution and use in source and binary forms, with or without
12c9a47c22Sthorpej  * modification, are permitted provided that the following conditions
13c9a47c22Sthorpej  * are met:
14c9a47c22Sthorpej  * 1. Redistributions of source code must retain the above copyright
15c9a47c22Sthorpej  *    notice, this list of conditions and the following disclaimer.
16c9a47c22Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
17c9a47c22Sthorpej  *    notice, this list of conditions and the following disclaimer in the
18c9a47c22Sthorpej  *    documentation and/or other materials provided with the distribution.
19c9a47c22Sthorpej  *
20c9a47c22Sthorpej  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21c9a47c22Sthorpej  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22c9a47c22Sthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23c9a47c22Sthorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24c9a47c22Sthorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25c9a47c22Sthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26c9a47c22Sthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27c9a47c22Sthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28c9a47c22Sthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29c9a47c22Sthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30c9a47c22Sthorpej  * POSSIBILITY OF SUCH DAMAGE.
31c9a47c22Sthorpej  */
32c9a47c22Sthorpej 
33c9a47c22Sthorpej /*
34c9a47c22Sthorpej  * scsictl(8) - a program to manipulate SCSI devices and busses.
35c9a47c22Sthorpej  */
36c2a3b5ecSagc #include <sys/cdefs.h>
37c2a3b5ecSagc 
38c2a3b5ecSagc #ifndef lint
39*7eee88edSriastradh __RCSID("$NetBSD: scsictl.c,v 1.42 2024/11/10 01:55:06 riastradh Exp $");
40c2a3b5ecSagc #endif
41c2a3b5ecSagc 
42c9a47c22Sthorpej #include <sys/param.h>
43c9a47c22Sthorpej #include <sys/ioctl.h>
44c9a47c22Sthorpej #include <sys/scsiio.h>
45c9a47c22Sthorpej #include <err.h>
46c9a47c22Sthorpej #include <errno.h>
47c9a47c22Sthorpej #include <fcntl.h>
48661eb28dSginsbach #include <limits.h>
49c9a47c22Sthorpej #include <stdio.h>
50c9a47c22Sthorpej #include <stdlib.h>
51f17194a0Smlelstv #include <stdbool.h>
52c9a47c22Sthorpej #include <string.h>
53c9a47c22Sthorpej #include <unistd.h>
54c9a47c22Sthorpej #include <util.h>
55c9a47c22Sthorpej 
56df9803ceSthorpej #include <dev/scsipi/scsi_spc.h>
57c9a47c22Sthorpej #include <dev/scsipi/scsipi_all.h>
58c9a47c22Sthorpej #include <dev/scsipi/scsi_disk.h>
59c9a47c22Sthorpej #include <dev/scsipi/scsipiconf.h>
60c9a47c22Sthorpej 
61c9a47c22Sthorpej #include "extern.h"
62c9a47c22Sthorpej 
63c9a47c22Sthorpej struct command {
64c9a47c22Sthorpej 	const char *cmd_name;
65958ed46fShubertf 	const char *arg_names;
669bab889dSxtraeme 	void (*cmd_func)(int, char *[]);
67c9a47c22Sthorpej };
68c9a47c22Sthorpej 
69baa8e84bSjoerg __dead static void	usage(void);
70c9a47c22Sthorpej 
719211d764Sjakllsch static int	fd;				/* file descriptor for device */
72c9a47c22Sthorpej const  char	*dvname;			/* device name */
739211d764Sjakllsch static char	dvname_store[MAXPATHLEN];	/* for opendisk(3) */
749211d764Sjakllsch static const	char *cmdname;			/* command user issued */
759211d764Sjakllsch static struct	scsi_addr dvaddr;		/* SCSI device's address */
76c9a47c22Sthorpej 
779211d764Sjakllsch static void	device_defects(int, char *[]);
789211d764Sjakllsch static void	device_format(int, char *[]);
799211d764Sjakllsch static void	device_identify(int, char *[]);
809211d764Sjakllsch static void	device_reassign(int, char *[]);
819211d764Sjakllsch static void	device_release(int, char *[]);
829211d764Sjakllsch static void	device_reserve(int, char *[]);
839211d764Sjakllsch static void	device_reset(int, char *[]);
849211d764Sjakllsch static void	device_debug(int, char *[]);
859211d764Sjakllsch static void	device_prevent(int, char *[]);
869211d764Sjakllsch static void	device_allow(int, char *[]);
879211d764Sjakllsch static void	device_start(int, char *[]);
889211d764Sjakllsch static void	device_stop(int, char *[]);
899211d764Sjakllsch static void	device_tur(int, char *[]);
909211d764Sjakllsch static void	device_getcache(int, char *[]);
919211d764Sjakllsch static void	device_setcache(int, char *[]);
929211d764Sjakllsch static void	device_flushcache(int, char *[]);
939211d764Sjakllsch static void	device_setspeed(int, char *[]);
94b0be1b67Sflxd static void	device_getrealloc(int, char *[]);
95b0be1b67Sflxd static void	device_setrealloc(int, char *[]);
96100b2229Smlelstv static void	device_reportluns(int, char *[]);
97c9a47c22Sthorpej 
989211d764Sjakllsch static struct command device_commands[] = {
99661eb28dSginsbach 	{ "defects",	"[primary] [grown] [block|byte|physical]",
100661eb28dSginsbach 						device_defects },
10141194c0eSmjacob 	{ "format",	"[blocksize [immediate]]", 	device_format },
102f17194a0Smlelstv 	{ "identify",	"[vpd]",		device_identify },
10389fa8aefSmjl 	{ "reassign",	"blkno [blkno [...]]",	device_reassign },
10441194c0eSmjacob 	{ "release",	"",			device_release },
10541194c0eSmjacob 	{ "reserve",	"",			device_reserve },
10689fa8aefSmjl 	{ "reset",	"",			device_reset },
1071b08fb13Spetrov 	{ "debug",	"level",		device_debug },
10846743e21Smycroft 	{ "prevent",	"",			device_prevent },
10946743e21Smycroft 	{ "allow",	"",			device_allow },
11041194c0eSmjacob 	{ "start",	"",			device_start },
11141194c0eSmjacob 	{ "stop",	"",			device_stop },
11241194c0eSmjacob 	{ "tur",	"",			device_tur },
1130683880eSthorpej 	{ "getcache",	"",			device_getcache },
1140683880eSthorpej 	{ "setcache",	"none|r|w|rw [save]",	device_setcache },
1154950c2c9Smycroft 	{ "flushcache",	"",			device_flushcache },
116e47fe82aSbouyer 	{ "setspeed",	"[speed]",		device_setspeed },
117b0be1b67Sflxd 	{ "getrealloc",	"",			device_getrealloc },
118b0be1b67Sflxd 	{ "setrealloc",	"none|r|w|rw [save]",	device_setrealloc },
119100b2229Smlelstv 	{ "reportluns",	"normal|wellknown|all|#",	device_reportluns },
120958ed46fShubertf 	{ NULL,		NULL,			NULL },
121c9a47c22Sthorpej };
122c9a47c22Sthorpej 
1239211d764Sjakllsch static void	bus_reset(int, char *[]);
1249211d764Sjakllsch static void	bus_scan(int, char *[]);
1259211d764Sjakllsch static void	bus_detach(int, char *[]);
126c9a47c22Sthorpej 
1279211d764Sjakllsch static struct command bus_commands[] = {
12889fa8aefSmjl 	{ "reset",	"",			bus_reset },
12989fa8aefSmjl 	{ "scan",	"target lun",		bus_scan },
13003fd5e67Sbouyer 	{ "detach",	"target lun",		bus_detach },
131958ed46fShubertf 	{ NULL,		NULL,				NULL },
132c9a47c22Sthorpej };
133c9a47c22Sthorpej 
134c9a47c22Sthorpej int
1359bab889dSxtraeme main(int argc, char *argv[])
136c9a47c22Sthorpej {
137c9a47c22Sthorpej 	struct command *commands;
138c9a47c22Sthorpej 	int i;
139c9a47c22Sthorpej 
140c9a47c22Sthorpej 	/* Must have at least: device command */
141c9a47c22Sthorpej 	if (argc < 3)
142c9a47c22Sthorpej 		usage();
143c9a47c22Sthorpej 
144c9a47c22Sthorpej 	/* Skip program name, get and skip device name and command. */
145c9a47c22Sthorpej 	dvname = argv[1];
146c9a47c22Sthorpej 	cmdname = argv[2];
147c9a47c22Sthorpej 	argv += 3;
148c9a47c22Sthorpej 	argc -= 3;
149c9a47c22Sthorpej 
150c9a47c22Sthorpej 	/*
151c9a47c22Sthorpej 	 * Open the device and determine if it's a scsibus or an actual
152c9a47c22Sthorpej 	 * device.  Devices respond to the SCIOCIDENTIFY ioctl.
153c9a47c22Sthorpej 	 */
154c9a47c22Sthorpej 	fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0);
155c9a47c22Sthorpej 	if (fd == -1) {
156c9a47c22Sthorpej 		if (errno == ENOENT) {
157c9a47c22Sthorpej 			/*
158c9a47c22Sthorpej 			 * Device doesn't exist.  Probably trying to open
159c9a47c22Sthorpej 			 * a device which doesn't use disk semantics for
160c0b197f9Sthorpej 			 * device name.  Try again, specifying "cooked",
161c0b197f9Sthorpej 			 * which leaves off the "r" in front of the device's
162c0b197f9Sthorpej 			 * name.
163c9a47c22Sthorpej 			 */
164c0b197f9Sthorpej 			fd = opendisk(dvname, O_RDWR, dvname_store,
165c0b197f9Sthorpej 			    sizeof(dvname_store), 1);
166c9a47c22Sthorpej 			if (fd == -1)
167c9a47c22Sthorpej 				err(1, "%s", dvname);
168d3c740d7Sjwise 		} else
169c9a47c22Sthorpej 			err(1, "%s", dvname);
170c0b197f9Sthorpej 	}
171c0b197f9Sthorpej 
172c9a47c22Sthorpej 	/*
173c0b197f9Sthorpej 	 * Point the dvname at the actual device name that opendisk() opened.
174c9a47c22Sthorpej 	 */
175c9a47c22Sthorpej 	dvname = dvname_store;
176c9a47c22Sthorpej 
177c9a47c22Sthorpej 	if (ioctl(fd, SCIOCIDENTIFY, &dvaddr) < 0)
178c9a47c22Sthorpej 		commands = bus_commands;
179c9a47c22Sthorpej 	else
180c9a47c22Sthorpej 		commands = device_commands;
181c9a47c22Sthorpej 
182c9a47c22Sthorpej 	/* Look up and call the command. */
183c9a47c22Sthorpej 	for (i = 0; commands[i].cmd_name != NULL; i++)
184c9a47c22Sthorpej 		if (strcmp(cmdname, commands[i].cmd_name) == 0)
185c9a47c22Sthorpej 			break;
186c9a47c22Sthorpej 	if (commands[i].cmd_name == NULL)
18793e37412Sad 		errx(1, "unknown %s command: %s",
188c9a47c22Sthorpej 		    commands == bus_commands ? "bus" : "device", cmdname);
189c9a47c22Sthorpej 
190c9a47c22Sthorpej 	(*commands[i].cmd_func)(argc, argv);
191c9a47c22Sthorpej 	exit(0);
192c9a47c22Sthorpej }
193c9a47c22Sthorpej 
194baa8e84bSjoerg static void
1959bab889dSxtraeme usage(void)
196c9a47c22Sthorpej {
197958ed46fShubertf 	int i;
198c9a47c22Sthorpej 
199b635f565Sjmmv 	fprintf(stderr, "usage: %s device command [arg [...]]\n",
2008a986b2eScgd 	    getprogname());
201958ed46fShubertf 
20289fa8aefSmjl 	fprintf(stderr, "   Commands pertaining to scsi devices:\n");
203958ed46fShubertf 	for (i = 0; device_commands[i].cmd_name != NULL; i++)
204958ed46fShubertf 		fprintf(stderr, "\t%s %s\n", device_commands[i].cmd_name,
205958ed46fShubertf 					    device_commands[i].arg_names);
20689fa8aefSmjl 	fprintf(stderr, "   Commands pertaining to scsi busses:\n");
207958ed46fShubertf 	for (i = 0; bus_commands[i].cmd_name != NULL; i++)
208958ed46fShubertf 		fprintf(stderr, "\t%s %s\n", bus_commands[i].cmd_name,
209958ed46fShubertf 					    bus_commands[i].arg_names);
2102829eb17Sad 	fprintf(stderr, "   Use `any' or `all' to wildcard target or lun\n");
211958ed46fShubertf 
212c9a47c22Sthorpej 	exit(1);
213c9a47c22Sthorpej }
214c9a47c22Sthorpej 
215c9a47c22Sthorpej /*
216c9a47c22Sthorpej  * DEVICE COMMANDS
217c9a47c22Sthorpej  */
218c9a47c22Sthorpej 
219c9a47c22Sthorpej /*
220661eb28dSginsbach  * device_read_defect:
221661eb28dSginsbach  *
222661eb28dSginsbach  *	Read primary and/or growth defect list in physical or block
223661eb28dSginsbach  *	format from a direct access device.
224661eb28dSginsbach  *
225661eb28dSginsbach  *	XXX Does not handle very large defect lists. Needs SCSI3 12
226661eb28dSginsbach  *	    byte READ DEFECT DATA command.
227661eb28dSginsbach  */
228661eb28dSginsbach 
2299211d764Sjakllsch static void	print_bf_dd(union scsi_defect_descriptor *);
2309211d764Sjakllsch static void	print_bfif_dd(union scsi_defect_descriptor *);
2319211d764Sjakllsch static void	print_psf_dd(union scsi_defect_descriptor *);
232661eb28dSginsbach 
2339211d764Sjakllsch static void
2349bab889dSxtraeme device_defects(int argc, char *argv[])
235661eb28dSginsbach {
236661eb28dSginsbach 	struct scsi_read_defect_data cmd;
237661eb28dSginsbach 	struct scsi_read_defect_data_data *data;
238661eb28dSginsbach 	size_t dlen;
239661eb28dSginsbach 	int i, dlfmt = -1;
240661eb28dSginsbach 	int defects;
241661eb28dSginsbach 	char msg[256];
2429bab889dSxtraeme 	void (*pfunc)(union scsi_defect_descriptor *);
243661eb28dSginsbach #define RDD_P_G_MASK	0x18
244661eb28dSginsbach #define RDD_DLF_MASK	0x7
245661eb28dSginsbach 
246661eb28dSginsbach 	dlen = USHRT_MAX; 		/* XXX - this may not be enough room
247661eb28dSginsbach 					 * for all of the defects.
248661eb28dSginsbach 					 */
249661eb28dSginsbach 	data = malloc(dlen);
250661eb28dSginsbach 	if (data == NULL)
251661eb28dSginsbach 		errx(1, "unable to allocate defect list");
252661eb28dSginsbach 	memset(data, 0, dlen);
253661eb28dSginsbach 	memset(&cmd, 0, sizeof(cmd));
254925bd672Slukem 	defects = 0;
255925bd672Slukem 	pfunc = NULL;
256661eb28dSginsbach 
257661eb28dSginsbach 	/* determine which defect list(s) to read. */
258661eb28dSginsbach 	for (i = 0; i < argc; i++) {
259661eb28dSginsbach 		if (strncmp("primary", argv[i], 7) == 0) {
260661eb28dSginsbach 			cmd.flags |= RDD_PRIMARY;
261661eb28dSginsbach 			continue;
262661eb28dSginsbach 		}
263661eb28dSginsbach 		if (strncmp("grown", argv[i], 5) == 0) {
264661eb28dSginsbach 			cmd.flags |= RDD_GROWN;
265661eb28dSginsbach 			continue;
266661eb28dSginsbach 		}
267661eb28dSginsbach 		break;
268661eb28dSginsbach 	}
269661eb28dSginsbach 
270661eb28dSginsbach 	/* no defect list sepecified, assume both. */
271661eb28dSginsbach 	if ((cmd.flags & (RDD_PRIMARY|RDD_GROWN)) == 0)
272661eb28dSginsbach 		cmd.flags |= (RDD_PRIMARY|RDD_GROWN);
273661eb28dSginsbach 
274661eb28dSginsbach 	/* list format option. */
275661eb28dSginsbach 	if (i < argc) {
276661eb28dSginsbach 		if (strncmp("block", argv[i], 5) == 0) {
277661eb28dSginsbach 			cmd.flags |= RDD_BF;
278661eb28dSginsbach 			dlfmt = RDD_BF;
279661eb28dSginsbach 		}
280661eb28dSginsbach 		else if (strncmp("byte", argv[i], 4) == 0) {
281661eb28dSginsbach 			cmd.flags |= RDD_BFIF;
282661eb28dSginsbach 			dlfmt = RDD_BFIF;
283661eb28dSginsbach 		}
284661eb28dSginsbach 		else if (strncmp("physical", argv[i], 4) == 0) {
285661eb28dSginsbach 			cmd.flags |= RDD_PSF;
286661eb28dSginsbach 			dlfmt = RDD_PSF;
287661eb28dSginsbach 		}
288661eb28dSginsbach 		else {
289661eb28dSginsbach 			usage();
290661eb28dSginsbach 		}
291661eb28dSginsbach 	}
292661eb28dSginsbach 
293661eb28dSginsbach 	/*
294661eb28dSginsbach 	 * no list format specified; since block format not
295661eb28dSginsbach 	 * recommended use physical sector format as default.
296661eb28dSginsbach 	 */
297661eb28dSginsbach 	if (dlfmt < 0) {
298661eb28dSginsbach 		cmd.flags |= RDD_PSF;
299661eb28dSginsbach 		dlfmt = RDD_PSF;
300661eb28dSginsbach 	}
301661eb28dSginsbach 
302661eb28dSginsbach 	cmd.opcode = SCSI_READ_DEFECT_DATA;
303661eb28dSginsbach 	_lto2b(dlen, &cmd.length[0]);
304661eb28dSginsbach 
305661eb28dSginsbach 	scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_READ);
306661eb28dSginsbach 
307661eb28dSginsbach 	msg[0] = '\0';
308661eb28dSginsbach 
309661eb28dSginsbach 	/* is the defect list in the format asked for? */
310661eb28dSginsbach 	if ((data->flags & RDD_DLF_MASK) != dlfmt) {
311661eb28dSginsbach 		strcpy(msg, "\n\tnotice:"
312661eb28dSginsbach 		       "requested defect list format not supported by device\n\n");
313661eb28dSginsbach 		dlfmt = (data->flags & RDD_DLF_MASK);
314661eb28dSginsbach 	}
315661eb28dSginsbach 
316661eb28dSginsbach 	if (data->flags & RDD_PRIMARY)
317661eb28dSginsbach 		strcat(msg, "primary");
318661eb28dSginsbach 
319661eb28dSginsbach 	if (data->flags & RDD_GROWN) {
320661eb28dSginsbach 		if (data->flags & RDD_PRIMARY)
321661eb28dSginsbach 			strcat(msg, " and ");
322661eb28dSginsbach 		strcat(msg, "grown");
323661eb28dSginsbach 	}
324661eb28dSginsbach 
325661eb28dSginsbach 	strcat(msg, " defects");
326661eb28dSginsbach 
327661eb28dSginsbach 	if ((data->flags & RDD_P_G_MASK) == 0)
328661eb28dSginsbach 		strcat(msg, ": none reported\n");
329661eb28dSginsbach 
330661eb28dSginsbach 	printf("%s: scsibus%d target %d lun %d %s",
331661eb28dSginsbach 	       dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target,
332661eb28dSginsbach 	       dvaddr.addr.scsi.lun, msg);
333661eb28dSginsbach 
334661eb28dSginsbach 	/* device did not return either defect list. */
335661eb28dSginsbach 	if ((data->flags & RDD_P_G_MASK) == 0)
336661eb28dSginsbach 		return;
337661eb28dSginsbach 
338661eb28dSginsbach 	switch (dlfmt) {
339661eb28dSginsbach 	case RDD_BF:
340661eb28dSginsbach 		defects = _2btol(data->length) /
341661eb28dSginsbach 				sizeof(struct scsi_defect_descriptor_bf);
342661eb28dSginsbach 		pfunc = print_bf_dd;
343661eb28dSginsbach 		strcpy(msg, "block address\n"
344661eb28dSginsbach 			    "-------------\n");
345661eb28dSginsbach 		break;
346661eb28dSginsbach 	case RDD_BFIF:
347661eb28dSginsbach 		defects = _2btol(data->length) /
348661eb28dSginsbach 				sizeof(struct scsi_defect_descriptor_bfif);
349661eb28dSginsbach 		pfunc = print_bfif_dd;
350661eb28dSginsbach 		strcpy(msg, "              bytes from\n"
351661eb28dSginsbach 			    "cylinder head   index\n"
352661eb28dSginsbach 			    "-------- ---- ----------\n");
353661eb28dSginsbach 		break;
354661eb28dSginsbach 	case RDD_PSF:
355661eb28dSginsbach 		defects = _2btol(data->length) /
356661eb28dSginsbach 				sizeof(struct scsi_defect_descriptor_psf);
357661eb28dSginsbach 		pfunc = print_psf_dd;
358661eb28dSginsbach 		strcpy(msg, "cylinder head   sector\n"
359661eb28dSginsbach 			    "-------- ---- ----------\n");
360661eb28dSginsbach 		break;
361661eb28dSginsbach 	}
362661eb28dSginsbach 
363661eb28dSginsbach 	/* device did not return any defects. */
364661eb28dSginsbach 	if (defects == 0) {
365661eb28dSginsbach 		printf(": none\n");
366661eb28dSginsbach 		return;
367661eb28dSginsbach 	}
368661eb28dSginsbach 
369661eb28dSginsbach 	printf(": %d\n", defects);
370661eb28dSginsbach 
371661eb28dSginsbach 	/* print heading. */
372661eb28dSginsbach 	printf("%s", msg);
373661eb28dSginsbach 
374661eb28dSginsbach 	/* print defect list. */
375661eb28dSginsbach 	for (i = 0 ; i < defects; i++) {
376661eb28dSginsbach 		pfunc(&data->defect_descriptor[i]);
377661eb28dSginsbach 	}
378661eb28dSginsbach 
379661eb28dSginsbach 	free(data);
380661eb28dSginsbach 	return;
381661eb28dSginsbach }
382661eb28dSginsbach 
383661eb28dSginsbach /*
384661eb28dSginsbach  * print_bf_dd:
385661eb28dSginsbach  *
386661eb28dSginsbach  *	Print a block format defect descriptor.
387661eb28dSginsbach  */
3889211d764Sjakllsch static void
3899bab889dSxtraeme print_bf_dd(union scsi_defect_descriptor *dd)
390661eb28dSginsbach {
391661eb28dSginsbach 	u_int32_t block;
392661eb28dSginsbach 
393661eb28dSginsbach 	block = _4btol(dd->bf.block_address);
394661eb28dSginsbach 
395661eb28dSginsbach 	printf("%13u\n", block);
396661eb28dSginsbach }
397661eb28dSginsbach 
398661eb28dSginsbach #define DEFECTIVE_TRACK	0xffffffff
399661eb28dSginsbach 
400661eb28dSginsbach /*
401661eb28dSginsbach  * print_bfif_dd:
402661eb28dSginsbach  *
403661eb28dSginsbach  *	Print a bytes from index format defect descriptor.
404661eb28dSginsbach  */
4059211d764Sjakllsch static void
4069bab889dSxtraeme print_bfif_dd(union scsi_defect_descriptor *dd)
407661eb28dSginsbach {
408661eb28dSginsbach 	u_int32_t cylinder;
409661eb28dSginsbach 	u_int32_t head;
410661eb28dSginsbach 	u_int32_t bytes_from_index;
411661eb28dSginsbach 
412661eb28dSginsbach 	cylinder = _3btol(dd->bfif.cylinder);
413661eb28dSginsbach 	head = dd->bfif.head;
414661eb28dSginsbach 	bytes_from_index = _4btol(dd->bfif.bytes_from_index);
415661eb28dSginsbach 
416661eb28dSginsbach 	printf("%8u %4u ", cylinder, head);
417661eb28dSginsbach 
418661eb28dSginsbach 	if (bytes_from_index == DEFECTIVE_TRACK)
419661eb28dSginsbach 		printf("entire track defective\n");
420661eb28dSginsbach 	else
421661eb28dSginsbach 		printf("%10u\n", bytes_from_index);
422661eb28dSginsbach }
423661eb28dSginsbach 
424661eb28dSginsbach /*
425661eb28dSginsbach  * print_psf_dd:
426661eb28dSginsbach  *
427661eb28dSginsbach  *	Print a physical sector format defect descriptor.
428661eb28dSginsbach  */
4299211d764Sjakllsch static void
4309bab889dSxtraeme print_psf_dd(union scsi_defect_descriptor *dd)
431661eb28dSginsbach {
432661eb28dSginsbach 	u_int32_t cylinder;
433661eb28dSginsbach 	u_int32_t head;
434661eb28dSginsbach 	u_int32_t sector;
435661eb28dSginsbach 
436661eb28dSginsbach 	cylinder = _3btol(dd->psf.cylinder);
437661eb28dSginsbach 	head = dd->psf.head;
438661eb28dSginsbach 	sector = _4btol(dd->psf.sector);
439661eb28dSginsbach 
440661eb28dSginsbach 	printf("%8u %4u ", cylinder, head);
441661eb28dSginsbach 
442661eb28dSginsbach 	if (sector == DEFECTIVE_TRACK)
443661eb28dSginsbach 		printf("entire track defective\n");
444661eb28dSginsbach 	else
445661eb28dSginsbach 		printf("%10u\n", sector);
446661eb28dSginsbach }
447661eb28dSginsbach 
448661eb28dSginsbach /*
449c1425134Sthorpej  * device_format:
450c1425134Sthorpej  *
451c1425134Sthorpej  *	Format a direct access device.
452c1425134Sthorpej  */
4539211d764Sjakllsch static void
4549bab889dSxtraeme device_format(int argc, char *argv[])
455c1425134Sthorpej {
45641194c0eSmjacob 	u_int32_t blksize;
45741194c0eSmjacob 	int i, j, immediate;
45841194c0eSmjacob #define	PC	(65536/10)
45941194c0eSmjacob 	static int complete[] = {
46041194c0eSmjacob 	    PC*1, PC*2, PC*3, PC*4, PC*5, PC*6, PC*7, PC*8, PC*9, 65536
46141194c0eSmjacob 	};
46241194c0eSmjacob 	char *cp, buffer[64];
463df9803ceSthorpej 	struct scsi_sense_data sense;
464c1425134Sthorpej 	struct scsi_format_unit cmd;
465c1425134Sthorpej 	struct {
46641194c0eSmjacob 		struct scsi_format_unit_defect_list_header header;
46741194c0eSmjacob 		/* optional initialization pattern */
46841194c0eSmjacob 		/* optional defect list */
46941194c0eSmjacob 	} dfl;
47041194c0eSmjacob 	struct {
471df9803ceSthorpej 		struct scsi_mode_parameter_header_6 header;
472df9803ceSthorpej 		struct scsi_general_block_descriptor blk_desc;
473c1425134Sthorpej 		struct page_disk_format format_page;
47441194c0eSmjacob 	} mode_page;
47541194c0eSmjacob 	struct {
476df9803ceSthorpej 		struct scsi_mode_parameter_header_6 header;
477df9803ceSthorpej 		struct scsi_general_block_descriptor blk_desc;
47841194c0eSmjacob 	} data_select;
479c1425134Sthorpej 
48041194c0eSmjacob 	/* Blocksize is an optional argument. */
48141194c0eSmjacob 	if (argc > 2)
482958ed46fShubertf 		usage();
483c1425134Sthorpej 
484c1425134Sthorpej 	/*
48541194c0eSmjacob 	 * Loop doing Request Sense to clear any pending Unit Attention.
48641194c0eSmjacob 	 *
48741194c0eSmjacob 	 * Multiple conditions may exist on the drive which are returned
48841194c0eSmjacob 	 * in priority order.
48941194c0eSmjacob 	 */
49041194c0eSmjacob 	for (i = 0; i < 8; i++) {
49141194c0eSmjacob 		scsi_request_sense(fd, &sense, sizeof (sense));
492df9803ceSthorpej 		if ((j = SSD_SENSE_KEY(sense.flags)) == SKEY_NO_SENSE)
49341194c0eSmjacob 			break;
49441194c0eSmjacob 	}
49541194c0eSmjacob 	/*
49641194c0eSmjacob 	 * Make sure we cleared any pending Unit Attention
49741194c0eSmjacob 	 */
49841194c0eSmjacob 	if (j != SKEY_NO_SENSE) {
49941194c0eSmjacob 		cp = scsi_decode_sense((const unsigned char *) &sense, 2,
50041194c0eSmjacob 		    buffer, sizeof (buffer));
5016742cb18Sgrant 		errx(1, "failed to clean Unit Attention: %s", cp);
50241194c0eSmjacob 	}
50341194c0eSmjacob 
50441194c0eSmjacob 	/*
505c1425134Sthorpej 	 * Get the DISK FORMAT mode page.  SCSI-2 recommends specifying the
506c1425134Sthorpej 	 * interleave read from this page in the FORMAT UNIT command.
507c1425134Sthorpej 	 */
50841194c0eSmjacob 	scsi_mode_sense(fd, 0x03, 0x00, &mode_page, sizeof(mode_page));
50941194c0eSmjacob 
51041194c0eSmjacob 	j = (mode_page.format_page.bytes_s[0] << 8) |
51141194c0eSmjacob 	    (mode_page.format_page.bytes_s[1]);
51241194c0eSmjacob 
51341194c0eSmjacob 	if (j != DEV_BSIZE)
51439a0f733Sjoerg 		printf("current disk sector size: %d\n", j);
515c1425134Sthorpej 
516c1425134Sthorpej 	memset(&cmd, 0, sizeof(cmd));
517c1425134Sthorpej 
518c1425134Sthorpej 	cmd.opcode = SCSI_FORMAT_UNIT;
51941194c0eSmjacob 	memcpy(cmd.interleave, mode_page.format_page.interleave,
520c1425134Sthorpej 	    sizeof(cmd.interleave));
521c1425134Sthorpej 
52241194c0eSmjacob 	/*
52341194c0eSmjacob 	 * The blocksize on the device is only changed if the user
52441194c0eSmjacob 	 * specified a new blocksize. If not specified the blocksize
52541194c0eSmjacob 	 * used for the device will be the Default value in the device.
52641194c0eSmjacob 	 * We don't specify the number of blocks since the format
52741194c0eSmjacob 	 * command will always reformat the entire drive.  Also by
52841194c0eSmjacob 	 * not specifying a block count the drive will reset the
52941194c0eSmjacob 	 * block count to the maximum available after the format
53041194c0eSmjacob 	 * completes if the blocksize was changed in the format.
53141194c0eSmjacob 	 * Finally, the new disk geometry will not but updated on
53241194c0eSmjacob 	 * the drive in permanent storage until _AFTER_ the format
53341194c0eSmjacob 	 * completes successfully.
53441194c0eSmjacob 	 */
53541194c0eSmjacob 	if (argc > 0) {
53641194c0eSmjacob 		blksize = strtoul(argv[0], &cp, 10);
53741194c0eSmjacob 		if (*cp != '\0')
5386742cb18Sgrant 			errx(1, "invalid block size: %s", argv[0]);
539c1425134Sthorpej 
54041194c0eSmjacob 		memset(&data_select, 0, sizeof(data_select));
54141194c0eSmjacob 
542df9803ceSthorpej 		data_select.header.blk_desc_len =
543df9803ceSthorpej 		    sizeof(struct scsi_general_block_descriptor);
54441194c0eSmjacob 		/*
54541194c0eSmjacob 		 * blklen in desc is 3 bytes with a leading reserved byte
54641194c0eSmjacob 		 */
54741194c0eSmjacob 		_lto4b(blksize, &data_select.blk_desc.reserved);
54841194c0eSmjacob 
54941194c0eSmjacob 		/*
55041194c0eSmjacob 		 * Issue Mode Select to modify the device blocksize to be
55141194c0eSmjacob 		 * used on the Format.  The modified device geometry will
55241194c0eSmjacob 		 * be stored as Current and Saved Page 3 parameters when
55341194c0eSmjacob 		 * the Format completes.
55441194c0eSmjacob 		 */
55541194c0eSmjacob 		scsi_mode_select(fd, 0, &data_select, sizeof(data_select));
55641194c0eSmjacob 
55741194c0eSmjacob 		/*
55841194c0eSmjacob 		 * Since user specified a specific block size make sure it
55941194c0eSmjacob 		 * gets stored in the device when the format completes.
56041194c0eSmjacob 		 *
56141194c0eSmjacob 		 * Also scrub the defect list back to the manufacturers
56241194c0eSmjacob 		 * original.
56341194c0eSmjacob 		 */
56441194c0eSmjacob 		cmd.flags = SFU_CMPLST | SFU_FMTDATA;
56541194c0eSmjacob 	}
56641194c0eSmjacob 
56741194c0eSmjacob 	memset(&dfl, 0, sizeof(dfl));
56841194c0eSmjacob 
56941194c0eSmjacob 	if (argc > 1 && strncmp(argv[1], "imm", 3) == 0) {
57041194c0eSmjacob 		/*
57141194c0eSmjacob 		 * Signal target for an immediate return from Format.
57241194c0eSmjacob 		 *
57341194c0eSmjacob 		 * We'll poll for completion status.
57441194c0eSmjacob 		 */
57541194c0eSmjacob 		dfl.header.flags = DLH_IMMED;
57641194c0eSmjacob 		immediate = 1;
57741194c0eSmjacob 	} else {
57841194c0eSmjacob 		immediate = 0;
57941194c0eSmjacob 	}
58041194c0eSmjacob 
58141194c0eSmjacob 	scsi_command(fd, &cmd, sizeof(cmd), &dfl, sizeof(dfl),
5827528c09bSjakllsch 	    8 * 60 * 60 * 1000, SCCMD_WRITE);
58341194c0eSmjacob 
58441194c0eSmjacob 	/*
58541194c0eSmjacob 	 * Poll device for completion of Format
58641194c0eSmjacob 	 */
58741194c0eSmjacob 	if (immediate) {
58841194c0eSmjacob 		i = 0;
58941194c0eSmjacob 		printf("formatting.");
59041194c0eSmjacob 		fflush(stdout);
59141194c0eSmjacob 		do {
59241194c0eSmjacob 			scsireq_t req;
593df9803ceSthorpej 			struct scsi_test_unit_ready tcmd;
59441194c0eSmjacob 
5958c34b2a3Sjakllsch 			memset(&tcmd, 0, sizeof(tcmd));
596df9803ceSthorpej 			tcmd.opcode = SCSI_TEST_UNIT_READY;
59741194c0eSmjacob 
59841194c0eSmjacob 			memset(&req, 0, sizeof(req));
59941194c0eSmjacob 			memcpy(req.cmd, &tcmd, 6);
60041194c0eSmjacob 			req.cmdlen = 6;
60141194c0eSmjacob 			req.timeout = 10000;
60241194c0eSmjacob 			req.senselen = SENSEBUFLEN;
60341194c0eSmjacob 
60441194c0eSmjacob 			if (ioctl(fd, SCIOCCOMMAND, &req) == -1) {
60541194c0eSmjacob 				err(1, "SCIOCCOMMAND");
60641194c0eSmjacob 			}
60741194c0eSmjacob 
60841194c0eSmjacob 			if (req.retsts == SCCMD_OK) {
60941194c0eSmjacob 				break;
61041194c0eSmjacob 			} else if (req.retsts == SCCMD_TIMEOUT) {
61141194c0eSmjacob 				fprintf(stderr, "%s: SCSI command timed out",
61241194c0eSmjacob 				    dvname);
61341194c0eSmjacob 				break;
61441194c0eSmjacob 			} else if (req.retsts == SCCMD_BUSY) {
61541194c0eSmjacob 				fprintf(stderr, "%s: device is busy",
61641194c0eSmjacob 				    dvname);
61741194c0eSmjacob 				break;
61841194c0eSmjacob 			} else if (req.retsts != SCCMD_SENSE) {
61941194c0eSmjacob 				fprintf(stderr,
62041194c0eSmjacob 				    "%s: device had unknown status %x", dvname,
62141194c0eSmjacob 				    req.retsts);
62241194c0eSmjacob 				break;
62341194c0eSmjacob 			}
62418d6bea5Schristos 			memcpy(&sense, req.sense, sizeof(sense));
625df9803ceSthorpej 			if (sense.sks.sks_bytes[0] & SSD_SKSV) {
626df9803ceSthorpej 				j = (sense.sks.sks_bytes[1] << 8) |
627df9803ceSthorpej 				    (sense.sks.sks_bytes[2]);
62841194c0eSmjacob 				if (j >= complete[i]) {
62941194c0eSmjacob 					printf(".%d0%%.", ++i);
63041194c0eSmjacob 					fflush(stdout);
63141194c0eSmjacob 				}
63241194c0eSmjacob 			}
63341194c0eSmjacob 			sleep(10);
634df9803ceSthorpej 		} while (SSD_SENSE_KEY(sense.flags) == SKEY_NOT_READY);
63541194c0eSmjacob 		printf(".100%%..done.\n");
63641194c0eSmjacob 	}
637c1425134Sthorpej 	return;
638c1425134Sthorpej }
639c1425134Sthorpej 
640f17194a0Smlelstv static void
641f17194a0Smlelstv print_designator(const char *pre, struct scsipi_inquiry_evpd_device_id *did)
642f17194a0Smlelstv {
643f17194a0Smlelstv 	char buf[252 * 4 + 1];
644f17194a0Smlelstv 	unsigned assoc, proto, code, type;
645f17194a0Smlelstv 	static const char *typestr[] = {
646f17194a0Smlelstv 		"vendor",
647f17194a0Smlelstv 		"t10",
648f17194a0Smlelstv 		"eui64",
649f17194a0Smlelstv 		"naa",
650f17194a0Smlelstv 		"target port",
651f17194a0Smlelstv 		"port group",
652f17194a0Smlelstv 		"lun group",
653f17194a0Smlelstv 		"md5",
654f17194a0Smlelstv 		"scsi",
655f17194a0Smlelstv 		"res9",
656f17194a0Smlelstv 		"res10",
657f17194a0Smlelstv 		"res11",
658f17194a0Smlelstv 		"res12",
659f17194a0Smlelstv 		"res13",
660f17194a0Smlelstv 		"res14",
661f17194a0Smlelstv 		"res15"
662f17194a0Smlelstv 	};
663f17194a0Smlelstv 	static const char *assocstr[] = {
664f17194a0Smlelstv 		"lun",
665f17194a0Smlelstv 		"port",
666f17194a0Smlelstv 		"target",
667f17194a0Smlelstv 		"reserved"
668f17194a0Smlelstv 	};
669f17194a0Smlelstv 	static const char *protostr[] = {
670f17194a0Smlelstv 		"fibre channel",
671f17194a0Smlelstv 		"obsolete",
672f17194a0Smlelstv 		"ssa",
673f17194a0Smlelstv 		"ieee1394",
674f17194a0Smlelstv 		"rdma",
675f17194a0Smlelstv 		"iSCSI",
676f17194a0Smlelstv 		"SAS"
677f17194a0Smlelstv 	};
678f17194a0Smlelstv 	const unsigned maxproto = __arraycount(protostr) - 1;
679f17194a0Smlelstv 	const unsigned isbinary =
680f17194a0Smlelstv 	    __SHIFTOUT(SINQ_DEVICE_ID_CODESET_BINARY, SINQ_DEVICE_ID_CODESET);
681f17194a0Smlelstv 	unsigned k;
682f17194a0Smlelstv 
683f17194a0Smlelstv 	assoc = __SHIFTOUT(did->flags, SINQ_DEVICE_ID_ASSOCIATION);
684f17194a0Smlelstv 	proto = __SHIFTOUT(did->pc, SINQ_DEVICE_ID_PROTOCOL);
685f17194a0Smlelstv 	code = __SHIFTOUT(did->pc, SINQ_DEVICE_ID_CODESET);
686f17194a0Smlelstv 	type = __SHIFTOUT(did->flags, SINQ_DEVICE_ID_TYPE);
687f17194a0Smlelstv 
688f17194a0Smlelstv 	printf("%s%s", pre, assocstr[assoc]);
689f17194a0Smlelstv 	if (did->flags & SINQ_DEVICE_ID_PIV) {
690f17194a0Smlelstv 		if (proto > maxproto)
691f17194a0Smlelstv 			printf(" proto%u", proto);
692f17194a0Smlelstv 		else
693f17194a0Smlelstv 			printf(" %s", protostr[proto]);
694f17194a0Smlelstv 	}
695f17194a0Smlelstv 	printf(" %s: ", typestr[type]);
696f17194a0Smlelstv 
697f17194a0Smlelstv 	if (code == isbinary) {
698*7eee88edSriastradh 		for (k = 0; k < did->designator_length; k++) {
699f17194a0Smlelstv 			printf("%02x", did->designator[k]);
700f17194a0Smlelstv 		}
701f17194a0Smlelstv 		printf("\n");
702f17194a0Smlelstv 	} else {
703f17194a0Smlelstv 		scsi_strvis(buf, sizeof(buf), (char *)did->designator,
704f17194a0Smlelstv 		    did->designator_length);
705f17194a0Smlelstv 		printf("%s\n", buf);
706f17194a0Smlelstv 	}
707f17194a0Smlelstv }
708f17194a0Smlelstv 
709c1425134Sthorpej /*
710c9a47c22Sthorpej  * device_identify:
711c9a47c22Sthorpej  *
712f0a7346dSsnj  *	Display the identity of the device, including its SCSI bus,
713f0a7346dSsnj  *	target, lun, and its vendor/product/revision information.
714f17194a0Smlelstv  *      Optionally query and display vpd identification data.
715c9a47c22Sthorpej  */
7169211d764Sjakllsch static void
7179bab889dSxtraeme device_identify(int argc, char *argv[])
718c9a47c22Sthorpej {
719c9a47c22Sthorpej 	struct scsipi_inquiry_data inqbuf;
720f17194a0Smlelstv 	struct {
721f17194a0Smlelstv 		struct scsipi_inquiry_evpd_header h;
722f17194a0Smlelstv 		uint8_t d[255 - sizeof(struct scsipi_inquiry_evpd_header)];
723f17194a0Smlelstv 	} evpdbuf;
724c9a47c22Sthorpej 	struct scsipi_inquiry cmd;
725f17194a0Smlelstv 	unsigned len, rlen;
726f17194a0Smlelstv 	struct scsipi_inquiry_evpd_serial *ser;
727f17194a0Smlelstv 	struct scsipi_inquiry_evpd_device_id *did;
728f17194a0Smlelstv 	int has_serial;
729f17194a0Smlelstv 	int has_device_id;
730f17194a0Smlelstv 	bool getvpd = false;
731f17194a0Smlelstv 	int i;
732c9a47c22Sthorpej 
733c9a47c22Sthorpej 	/* x4 in case every character is escaped, +1 for NUL. */
734c9a47c22Sthorpej 	char vendor[(sizeof(inqbuf.vendor) * 4) + 1],
735c9a47c22Sthorpej 	     product[(sizeof(inqbuf.product) * 4) + 1],
736f17194a0Smlelstv 	     revision[(sizeof(inqbuf.revision) * 4) + 1],
737f17194a0Smlelstv 	     ident[252 * 4 + 1];
738c9a47c22Sthorpej 
739f17194a0Smlelstv 	/* Check optional arguments */
740f17194a0Smlelstv 	for (i = 0; i < argc; i++) {
741f17194a0Smlelstv 		if (strncmp("vpd", argv[i], 3) == 0) {
742f17194a0Smlelstv 			getvpd = true;
743f17194a0Smlelstv 			continue;
744f17194a0Smlelstv 		}
745958ed46fShubertf 		usage();
746f17194a0Smlelstv 	}
747c9a47c22Sthorpej 
748c9a47c22Sthorpej 	memset(&cmd, 0, sizeof(cmd));
749c9a47c22Sthorpej 	memset(&inqbuf, 0, sizeof(inqbuf));
750c9a47c22Sthorpej 
751c9a47c22Sthorpej 	cmd.opcode = INQUIRY;
752c9a47c22Sthorpej 	cmd.length = sizeof(inqbuf);
753c9a47c22Sthorpej 
754c9a47c22Sthorpej 	scsi_command(fd, &cmd, sizeof(cmd), &inqbuf, sizeof(inqbuf),
755c9a47c22Sthorpej 	    10000, SCCMD_READ);
756c9a47c22Sthorpej 
757c9a47c22Sthorpej 	scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor,
758c9a47c22Sthorpej 	    sizeof(inqbuf.vendor));
759c9a47c22Sthorpej 	scsi_strvis(product, sizeof(product), inqbuf.product,
760c9a47c22Sthorpej 	    sizeof(inqbuf.product));
761c9a47c22Sthorpej 	scsi_strvis(revision, sizeof(revision), inqbuf.revision,
762c9a47c22Sthorpej 	    sizeof(inqbuf.revision));
763c9a47c22Sthorpej 
764c9a47c22Sthorpej 	printf("%s: scsibus%d target %d lun %d <%s, %s, %s>\n",
765c9a47c22Sthorpej 	    dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target,
766c9a47c22Sthorpej 	    dvaddr.addr.scsi.lun, vendor, product, revision);
767c9a47c22Sthorpej 
768f17194a0Smlelstv 	if (!getvpd)
769f17194a0Smlelstv 		return;
770f17194a0Smlelstv 
771f17194a0Smlelstv 	cmd.byte2 |= SINQ_EVPD;
772f17194a0Smlelstv 	cmd.pagecode = SINQ_VPD_PAGES;
773f17194a0Smlelstv 
774f17194a0Smlelstv 	scsi_command(fd, &cmd, sizeof(cmd), &evpdbuf, sizeof(evpdbuf),
775f17194a0Smlelstv 	    10000, SCCMD_READ);
776f17194a0Smlelstv 
777f17194a0Smlelstv 	len = be16dec(evpdbuf.h.length);
778f17194a0Smlelstv 	if (len > sizeof(evpdbuf.d))
779f17194a0Smlelstv 		len = 0;
780f17194a0Smlelstv 
781f17194a0Smlelstv 	has_serial = memchr(evpdbuf.d, SINQ_VPD_SERIAL, len) != NULL;
782f17194a0Smlelstv 	has_device_id = memchr(evpdbuf.d, SINQ_VPD_DEVICE_ID, len) != NULL;
783f17194a0Smlelstv 
784f17194a0Smlelstv 	if (has_serial) {
785f17194a0Smlelstv 		cmd.byte2 |= SINQ_EVPD;
786f17194a0Smlelstv 		cmd.pagecode = SINQ_VPD_SERIAL;
787f17194a0Smlelstv 
788f17194a0Smlelstv 		scsi_command(fd, &cmd, sizeof(cmd), &evpdbuf, sizeof(evpdbuf),
789f17194a0Smlelstv 		    10000, SCCMD_READ);
790f17194a0Smlelstv 
791f17194a0Smlelstv 		len = be16dec(evpdbuf.h.length);
792f17194a0Smlelstv 		if (len > sizeof(evpdbuf.d))
793f17194a0Smlelstv 			len = 0;
794f17194a0Smlelstv 
795f17194a0Smlelstv 		ser = (struct scsipi_inquiry_evpd_serial *)&evpdbuf.d;
796*7eee88edSriastradh 		scsi_strvis(ident, sizeof(ident), (char *)ser->serial_number,
797*7eee88edSriastradh 		    len);
798f17194a0Smlelstv 		printf("VPD Serial:\n");
799f17194a0Smlelstv 		printf("\t%s\n", ident);
800f17194a0Smlelstv 	}
801f17194a0Smlelstv 
802f17194a0Smlelstv 	if (has_device_id) {
803f17194a0Smlelstv 		cmd.byte2 |= SINQ_EVPD;
804f17194a0Smlelstv 		cmd.pagecode = SINQ_VPD_DEVICE_ID;
805f17194a0Smlelstv 
806f17194a0Smlelstv 		scsi_command(fd, &cmd, sizeof(cmd), &evpdbuf, sizeof(evpdbuf),
807f17194a0Smlelstv 		    10000, SCCMD_READ);
808f17194a0Smlelstv 
809f17194a0Smlelstv 		len = be16dec(evpdbuf.h.length);
810f17194a0Smlelstv 		if (len > sizeof(evpdbuf.d))
811f17194a0Smlelstv 			len = 0;
812f17194a0Smlelstv 
813f17194a0Smlelstv 		printf("VPD Device IDs:\n");
814f17194a0Smlelstv 
815f17194a0Smlelstv 		for (unsigned off = 0; off < len - sizeof(*did); off += rlen) {
816*7eee88edSriastradh 			void *p = &evpdbuf.d[off];
817*7eee88edSriastradh 			did = (struct scsipi_inquiry_evpd_device_id *)p;
818f17194a0Smlelstv 			rlen = sizeof(*did) + did->designator_length - 1;
819f17194a0Smlelstv 			if (off + rlen > len)
820f17194a0Smlelstv 				break;
821f17194a0Smlelstv 
822f17194a0Smlelstv 			print_designator("\t", did);
823f17194a0Smlelstv 		}
824f17194a0Smlelstv 	}
825f17194a0Smlelstv 
826c9a47c22Sthorpej 	return;
827c9a47c22Sthorpej }
828c9a47c22Sthorpej 
829c9a47c22Sthorpej /*
830c9a47c22Sthorpej  * device_reassign:
831c9a47c22Sthorpej  *
832c9a47c22Sthorpej  *	Reassign bad blocks on a direct access device.
833c9a47c22Sthorpej  */
8349211d764Sjakllsch static void
8359bab889dSxtraeme device_reassign(int argc, char *argv[])
836c9a47c22Sthorpej {
837c9a47c22Sthorpej 	struct scsi_reassign_blocks cmd;
838c9a47c22Sthorpej 	struct scsi_reassign_blocks_data *data;
839c9a47c22Sthorpej 	size_t dlen;
840c9a47c22Sthorpej 	u_int32_t blkno;
841c9a47c22Sthorpej 	int i;
842c9a47c22Sthorpej 	char *cp;
843c9a47c22Sthorpej 
844c9a47c22Sthorpej 	/* We get a list of block numbers. */
845c9a47c22Sthorpej 	if (argc < 1)
846958ed46fShubertf 		usage();
847c9a47c22Sthorpej 
848c9a47c22Sthorpej 	/*
849c9a47c22Sthorpej 	 * Allocate the reassign blocks descriptor.  The 4 comes from the
850c9a47c22Sthorpej 	 * size of the block address in the defect descriptor.
851c9a47c22Sthorpej 	 */
852c9a47c22Sthorpej 	dlen = sizeof(struct scsi_reassign_blocks_data) + ((argc - 1) * 4);
853c9a47c22Sthorpej 	data = malloc(dlen);
854c9a47c22Sthorpej 	if (data == NULL)
855c9a47c22Sthorpej 		errx(1, "unable to allocate defect descriptor");
856c9a47c22Sthorpej 	memset(data, 0, dlen);
857c9a47c22Sthorpej 
858c9a47c22Sthorpej 	cmd.opcode = SCSI_REASSIGN_BLOCKS;
859f6624fbbSmycroft 	cmd.byte2 = 0;
860f6624fbbSmycroft 	cmd.unused[0] = 0;
861f6624fbbSmycroft 	cmd.unused[1] = 0;
862f6624fbbSmycroft 	cmd.unused[2] = 0;
863f6624fbbSmycroft 	cmd.control = 0;
864c9a47c22Sthorpej 
865c9a47c22Sthorpej 	/* Defect descriptor length. */
866f6624fbbSmycroft 	_lto2b(argc * 4, data->length);
867c9a47c22Sthorpej 
868c9a47c22Sthorpej 	/* Build the defect descriptor list. */
869c9a47c22Sthorpej 	for (i = 0; i < argc; i++) {
870c9a47c22Sthorpej 		blkno = strtoul(argv[i], &cp, 10);
871c9a47c22Sthorpej 		if (*cp != '\0')
87293e37412Sad 			errx(1, "invalid block number: %s", argv[i]);
873f6624fbbSmycroft 		_lto4b(blkno, data->defect_descriptor[i].dlbaddr);
874c9a47c22Sthorpej 	}
875c9a47c22Sthorpej 
876c9a47c22Sthorpej 	scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_WRITE);
877c9a47c22Sthorpej 
878c9a47c22Sthorpej 	free(data);
879c9a47c22Sthorpej 	return;
880c9a47c22Sthorpej }
881c9a47c22Sthorpej 
882c9a47c22Sthorpej /*
88341194c0eSmjacob  * device_release:
88441194c0eSmjacob  *
885e47fe82aSbouyer  *	Issue a RELEASE command to a SCSI device.
88641194c0eSmjacob  */
88741194c0eSmjacob #ifndef	SCSI_RELEASE
88841194c0eSmjacob #define	SCSI_RELEASE	0x17
88941194c0eSmjacob #endif
8909211d764Sjakllsch static void
8919bab889dSxtraeme device_release(int argc, char *argv[])
89241194c0eSmjacob {
893df9803ceSthorpej 	struct scsi_test_unit_ready cmd;	/* close enough */
89441194c0eSmjacob 
89541194c0eSmjacob 	/* No arguments. */
89641194c0eSmjacob 	if (argc != 0)
89741194c0eSmjacob 		usage();
89841194c0eSmjacob 
89941194c0eSmjacob 	memset(&cmd, 0, sizeof(cmd));
90041194c0eSmjacob 
90141194c0eSmjacob 	cmd.opcode = SCSI_RELEASE;
90241194c0eSmjacob 
90341194c0eSmjacob 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
90441194c0eSmjacob 
90541194c0eSmjacob 	return;
90641194c0eSmjacob }
90741194c0eSmjacob 
90841194c0eSmjacob /*
90941194c0eSmjacob  * device_reserve:
91041194c0eSmjacob  *
911e47fe82aSbouyer  *	Issue a RESERVE command to a SCSI device.
91241194c0eSmjacob  */
91341194c0eSmjacob #ifndef	SCSI_RESERVE
91441194c0eSmjacob #define	SCSI_RESERVE	0x16
91541194c0eSmjacob #endif
9169211d764Sjakllsch static void
9179bab889dSxtraeme device_reserve(int argc, char *argv[])
91841194c0eSmjacob {
919df9803ceSthorpej 	struct scsi_test_unit_ready cmd;	/* close enough */
92041194c0eSmjacob 
92141194c0eSmjacob 	/* No arguments. */
92241194c0eSmjacob 	if (argc != 0)
92341194c0eSmjacob 		usage();
92441194c0eSmjacob 
92541194c0eSmjacob 	memset(&cmd, 0, sizeof(cmd));
92641194c0eSmjacob 
92741194c0eSmjacob 	cmd.opcode = SCSI_RESERVE;
92841194c0eSmjacob 
92941194c0eSmjacob 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
93041194c0eSmjacob 
93141194c0eSmjacob 	return;
93241194c0eSmjacob }
93341194c0eSmjacob 
93441194c0eSmjacob /*
935c9a47c22Sthorpej  * device_reset:
936c9a47c22Sthorpej  *
937c9a47c22Sthorpej  *	Issue a reset to a SCSI device.
938c9a47c22Sthorpej  */
9399211d764Sjakllsch static void
9409bab889dSxtraeme device_reset(int argc, char *argv[])
941c9a47c22Sthorpej {
942c9a47c22Sthorpej 
943c9a47c22Sthorpej 	/* No arguments. */
944c9a47c22Sthorpej 	if (argc != 0)
945958ed46fShubertf 		usage();
946c9a47c22Sthorpej 
947c9a47c22Sthorpej 	if (ioctl(fd, SCIOCRESET, NULL) != 0)
948c9a47c22Sthorpej 		err(1, "SCIOCRESET");
949c9a47c22Sthorpej 
950c9a47c22Sthorpej 	return;
951c9a47c22Sthorpej }
952c9a47c22Sthorpej 
953c9a47c22Sthorpej /*
9541b08fb13Spetrov  * device_debug:
9551b08fb13Spetrov  *
9561b08fb13Spetrov  *	Set debug level to a SCSI device.
9571b08fb13Spetrov  *	scsipi will print anything iff SCSIPI_DEBUG set in config.
9581b08fb13Spetrov  */
9599211d764Sjakllsch static void
9609bab889dSxtraeme device_debug(int argc, char *argv[])
9611b08fb13Spetrov {
9621b08fb13Spetrov 	int lvl;
9631b08fb13Spetrov 
9641b08fb13Spetrov 	if (argc < 1)
9651b08fb13Spetrov 		usage();
9661b08fb13Spetrov 
9671b08fb13Spetrov 	lvl = atoi(argv[0]);
9681b08fb13Spetrov 
9691b08fb13Spetrov 	if (ioctl(fd, SCIOCDEBUG, &lvl) != 0)
9701b08fb13Spetrov 		err(1, "SCIOCDEBUG");
9711b08fb13Spetrov 
9721b08fb13Spetrov 	return;
9731b08fb13Spetrov }
9741b08fb13Spetrov 
9751b08fb13Spetrov /*
9760683880eSthorpej  * device_getcache:
977c9a47c22Sthorpej  *
9780683880eSthorpej  *	Get the caching parameters for a SCSI disk.
979c9a47c22Sthorpej  */
9809211d764Sjakllsch static void
9819bab889dSxtraeme device_getcache(int argc, char *argv[])
982c9a47c22Sthorpej {
9830683880eSthorpej 	struct {
984df9803ceSthorpej 		struct scsi_mode_parameter_header_6 header;
985df9803ceSthorpej 		struct scsi_general_block_descriptor blk_desc;
9860683880eSthorpej 		struct page_caching caching_params;
9870683880eSthorpej 	} data;
988c9a47c22Sthorpej 
989c9a47c22Sthorpej 	/* No arguments. */
990c9a47c22Sthorpej 	if (argc != 0)
991958ed46fShubertf 		usage();
992c9a47c22Sthorpej 
9930683880eSthorpej 	scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data));
994c9a47c22Sthorpej 
9950683880eSthorpej 	if ((data.caching_params.flags & (CACHING_RCD|CACHING_WCE)) ==
9960683880eSthorpej 	    CACHING_RCD)
9970683880eSthorpej 		printf("%s: no caches enabled\n", dvname);
9980683880eSthorpej 	else {
9990683880eSthorpej 		printf("%s: read cache %senabled\n", dvname,
10000683880eSthorpej 		    (data.caching_params.flags & CACHING_RCD) ? "not " : "");
10010683880eSthorpej 		printf("%s: write-back cache %senabled\n", dvname,
10020683880eSthorpej 		    (data.caching_params.flags & CACHING_WCE) ? "" : "not ");
10030683880eSthorpej 	}
10040683880eSthorpej 	printf("%s: caching parameters are %ssavable\n", dvname,
10050683880eSthorpej 	    (data.caching_params.pg_code & PGCODE_PS) ? "" : "not ");
10060683880eSthorpej }
10070683880eSthorpej 
10080683880eSthorpej /*
10090683880eSthorpej  * device_setcache:
10100683880eSthorpej  *
10110683880eSthorpej  *	Set cache enables for a SCSI disk.
10120683880eSthorpej  */
10139211d764Sjakllsch static void
10149bab889dSxtraeme device_setcache(int argc, char *argv[])
10150683880eSthorpej {
10160683880eSthorpej 	struct {
1017df9803ceSthorpej 		struct scsi_mode_parameter_header_6 header;
1018df9803ceSthorpej 		struct scsi_general_block_descriptor blk_desc;
10190683880eSthorpej 		struct page_caching caching_params;
10200683880eSthorpej 	} data;
10210683880eSthorpej 	int dlen;
10220683880eSthorpej 	u_int8_t flags, byte2;
10230683880eSthorpej 
10240683880eSthorpej 	if (argc > 2 || argc == 0)
10250683880eSthorpej 		usage();
10260683880eSthorpej 
1027925bd672Slukem 	flags = 0;
1028925bd672Slukem 	byte2 = 0;
10290683880eSthorpej 	if (strcmp(argv[0], "none") == 0)
10300683880eSthorpej 		flags = CACHING_RCD;
10310683880eSthorpej 	else if (strcmp(argv[0], "r") == 0)
10320683880eSthorpej 		flags = 0;
10330683880eSthorpej 	else if (strcmp(argv[0], "w") == 0)
10340683880eSthorpej 		flags = CACHING_RCD|CACHING_WCE;
10350683880eSthorpej 	else if (strcmp(argv[0], "rw") == 0)
10360683880eSthorpej 		flags = CACHING_WCE;
10370683880eSthorpej 	else
10380683880eSthorpej 		usage();
10390683880eSthorpej 
10400683880eSthorpej 	if (argc == 2) {
10410683880eSthorpej 		if (strcmp(argv[1], "save") == 0)
10420683880eSthorpej 			byte2 = SMS_SP;
10430683880eSthorpej 		else
10440683880eSthorpej 			usage();
10450683880eSthorpej 	}
10460683880eSthorpej 
10470683880eSthorpej 	scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data));
10480683880eSthorpej 
10490683880eSthorpej 	data.caching_params.pg_code &= PGCODE_MASK;
10500683880eSthorpej 	data.caching_params.flags =
10510683880eSthorpej 	    (data.caching_params.flags & ~(CACHING_RCD|CACHING_WCE)) | flags;
10520683880eSthorpej 
10530683880eSthorpej 	data.caching_params.cache_segment_size[0] = 0;
10540683880eSthorpej 	data.caching_params.cache_segment_size[1] = 0;
10550683880eSthorpej 
10560683880eSthorpej 	data.header.data_length = 0;
10570683880eSthorpej 
10580683880eSthorpej 	dlen = sizeof(data.header) + sizeof(data.blk_desc) + 2 +
10590683880eSthorpej 	    data.caching_params.pg_length;
10600683880eSthorpej 
10610683880eSthorpej 	scsi_mode_select(fd, byte2, &data, dlen);
1062c9a47c22Sthorpej }
1063c9a47c22Sthorpej 
1064c9a47c22Sthorpej /*
10654950c2c9Smycroft  * device_flushcache:
10664950c2c9Smycroft  *
1067e47fe82aSbouyer  *	Issue a FLUSH CACHE command to a SCSI device.
10684950c2c9Smycroft  */
10694950c2c9Smycroft #ifndef	SCSI_FLUSHCACHE
10704950c2c9Smycroft #define	SCSI_FLUSHCACHE	0x35
10714950c2c9Smycroft #endif
10729211d764Sjakllsch static void
10739bab889dSxtraeme device_flushcache(int argc, char *argv[])
10744950c2c9Smycroft {
1075df9803ceSthorpej 	struct scsi_test_unit_ready cmd;	/* close enough */
10764950c2c9Smycroft 
10774950c2c9Smycroft 	/* No arguments. */
10784950c2c9Smycroft 	if (argc != 0)
10794950c2c9Smycroft 		usage();
10804950c2c9Smycroft 
10814950c2c9Smycroft 	memset(&cmd, 0, sizeof(cmd));
10824950c2c9Smycroft 
10834950c2c9Smycroft 	cmd.opcode = SCSI_FLUSHCACHE;
10844950c2c9Smycroft 
10854950c2c9Smycroft 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
10864950c2c9Smycroft 
10874950c2c9Smycroft 	return;
10884950c2c9Smycroft }
10894950c2c9Smycroft 
10904950c2c9Smycroft /*
1091e47fe82aSbouyer  * device_setspeed:
1092e47fe82aSbouyer  *
1093e47fe82aSbouyer  *	Set rotation speed to a CD/DVD drive.
1094e47fe82aSbouyer  */
10959211d764Sjakllsch static void
1096e47fe82aSbouyer device_setspeed(int argc, char *argv[])
1097e47fe82aSbouyer {
1098e47fe82aSbouyer 	u_char cmd[11];
1099e47fe82aSbouyer 	u_char pd[28];
1100e47fe82aSbouyer 	u_int32_t speed;
1101e47fe82aSbouyer 
1102e47fe82aSbouyer 	if (argc != 1)
1103e47fe82aSbouyer 		usage();
1104e47fe82aSbouyer 
1105e47fe82aSbouyer 	speed = atoi(argv[0]) * 177;
1106e47fe82aSbouyer 
1107e47fe82aSbouyer 	memset(&pd, 0, sizeof(pd));
1108e47fe82aSbouyer 	if (speed == 0)
1109e47fe82aSbouyer 		pd[0] = 4; /* restore drive defaults */
1110e47fe82aSbouyer 	pd[8] = 0xff;
1111e47fe82aSbouyer 	pd[9] = 0xff;
1112e47fe82aSbouyer 	pd[10] = 0xff;
1113e47fe82aSbouyer 	pd[11] = 0xff;
1114e47fe82aSbouyer 	pd[12] = pd[20] = (speed >> 24) & 0xff;
1115e47fe82aSbouyer 	pd[13] = pd[21] = (speed >> 16) & 0xff;
1116e47fe82aSbouyer 	pd[14] = pd[22] = (speed >> 8) & 0xff;
1117e47fe82aSbouyer 	pd[15] = pd[23] = speed & 0xff;
1118e47fe82aSbouyer 	pd[18] = pd[26] = 1000 >> 8;
1119e47fe82aSbouyer 	pd[19] = pd[27] = 1000 & 0xff;
1120e47fe82aSbouyer 
1121e47fe82aSbouyer 	memset(&cmd, 0, sizeof(cmd));
1122e47fe82aSbouyer 	cmd[0] = 0xb6;
1123e47fe82aSbouyer 	cmd[10] = sizeof(pd);
1124e47fe82aSbouyer 
1125e47fe82aSbouyer 	scsi_command(fd, &cmd, sizeof(cmd), pd, sizeof(pd), 10000, SCCMD_WRITE);
1126e47fe82aSbouyer 
1127e47fe82aSbouyer 	return;
1128e47fe82aSbouyer }
1129e47fe82aSbouyer 
1130e47fe82aSbouyer /*
1131100b2229Smlelstv  * device_reportluns:
1132100b2229Smlelstv  *
1133100b2229Smlelstv  *	Report the known LUNs to which the initiator can send commands
1134100b2229Smlelstv  */
1135100b2229Smlelstv static void
1136100b2229Smlelstv device_reportluns(int argc, char *argv[])
1137100b2229Smlelstv {
1138100b2229Smlelstv 	struct scsi_report_luns cmd;
1139100b2229Smlelstv 	struct {
1140100b2229Smlelstv 		struct scsi_report_luns_header header;
1141100b2229Smlelstv 		struct scsi_report_luns_lun desc[1];
1142100b2229Smlelstv 	} *data;
1143100b2229Smlelstv 	u_int32_t dlen, len;
1144100b2229Smlelstv 	u_int64_t lun;
1145100b2229Smlelstv 	size_t count, idx;
1146100b2229Smlelstv 	unsigned long sel;
1147100b2229Smlelstv 	char *endp;
1148100b2229Smlelstv 	int i;
1149100b2229Smlelstv 
1150100b2229Smlelstv 	dlen = USHRT_MAX; /* good for > 8000 LUNs */
1151100b2229Smlelstv 	data = malloc(dlen);
1152100b2229Smlelstv 	if (data == NULL)
1153100b2229Smlelstv 		errx(1, "unable to allocate lun report");
1154100b2229Smlelstv 
1155100b2229Smlelstv 	memset(&cmd, 0, sizeof(cmd));
1156100b2229Smlelstv 	cmd.opcode = SCSI_REPORT_LUNS;
1157100b2229Smlelstv 	cmd.selectreport = SELECTREPORT_NORMAL;
1158100b2229Smlelstv 
1159100b2229Smlelstv 	/* determine which report to read. */
1160100b2229Smlelstv 	for (i = 0; i < argc; i++) {
1161100b2229Smlelstv 		if (strcmp("normal", argv[i]) == 0) {
1162100b2229Smlelstv 			cmd.selectreport = SELECTREPORT_NORMAL;
1163100b2229Smlelstv 			continue;
1164100b2229Smlelstv 		}
1165100b2229Smlelstv 		if (strcmp("wellknown", argv[i]) == 0) {
1166100b2229Smlelstv 			cmd.selectreport = SELECTREPORT_WELLKNOWN;
1167100b2229Smlelstv 			continue;
1168100b2229Smlelstv 		}
1169100b2229Smlelstv 		if (strcmp("all", argv[i]) == 0) {
1170100b2229Smlelstv 			cmd.selectreport = SELECTREPORT_ALL;
1171100b2229Smlelstv 			continue;
1172100b2229Smlelstv 		}
1173100b2229Smlelstv 		sel = strtoul(argv[i], &endp, 0);
1174100b2229Smlelstv 		if (*endp != '\0' || sel > 255)
1175100b2229Smlelstv 			errx(1, "Unknown select report '%s'", argv[i]);
1176100b2229Smlelstv 		cmd.selectreport = sel;
1177100b2229Smlelstv 	}
1178100b2229Smlelstv 
1179100b2229Smlelstv 	_lto4b(dlen, &cmd.alloclen[0]);
1180100b2229Smlelstv 	cmd.control = 0x00;
1181100b2229Smlelstv 
1182100b2229Smlelstv 	scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_READ);
1183100b2229Smlelstv 
1184100b2229Smlelstv 	len = _4btol(data->header.length);
1185100b2229Smlelstv 	if (len > dlen) {
1186100b2229Smlelstv 		/* XXX reallocate and retry */
1187100b2229Smlelstv 		printf("%s: report truncated %" PRIu32 "to %" PRIu32 "\n",
1188100b2229Smlelstv 		    dvname, len, dlen);
1189100b2229Smlelstv 		len = dlen;
1190100b2229Smlelstv 	}
1191100b2229Smlelstv 
1192100b2229Smlelstv 	count = len / sizeof(data->desc[0]);
1193100b2229Smlelstv 
1194*7eee88edSriastradh 	for (idx = 0; idx < count; idx++) {
1195100b2229Smlelstv 		lun = _8btol(data->desc[idx].lun);
1196100b2229Smlelstv 
1197100b2229Smlelstv 		/*
1198100b2229Smlelstv 		 * swizzle bits so that LUNs 0..255 are
1199100b2229Smlelstv 		 * mapped to numbers 0..255
1200100b2229Smlelstv 		 */
1201100b2229Smlelstv 		lun = (lun & 0xffff000000000000ull) >> 48
1202100b2229Smlelstv 		    | (lun & 0x0000ffff00000000ull) >> 16
1203100b2229Smlelstv 		    | (lun & 0x00000000ffff0000ull) << 16
1204100b2229Smlelstv 		    | (lun & 0x000000000000ffffull) << 48;
1205100b2229Smlelstv 
1206100b2229Smlelstv 		printf("%s: lun %" PRIu64 "\n", dvname, lun);
1207100b2229Smlelstv 	}
1208100b2229Smlelstv 
1209100b2229Smlelstv 	free(data);
1210100b2229Smlelstv }
1211100b2229Smlelstv 
1212100b2229Smlelstv /*
1213b0be1b67Sflxd  * device_getrealloc:
1214b0be1b67Sflxd  *
1215b0be1b67Sflxd  *	Get the automatic reallocation parameters for a SCSI disk.
1216b0be1b67Sflxd  */
1217b0be1b67Sflxd static void
1218b0be1b67Sflxd device_getrealloc(int argc, char *argv[])
1219b0be1b67Sflxd {
1220b0be1b67Sflxd 	struct {
1221b0be1b67Sflxd 		struct scsi_mode_parameter_header_6 header;
1222b0be1b67Sflxd 		struct scsi_general_block_descriptor blk_desc;
1223b0be1b67Sflxd 		struct page_err_recov err_recov_params;
1224b0be1b67Sflxd 	} data;
1225b0be1b67Sflxd 	u_int8_t flags;
1226b0be1b67Sflxd 
1227b0be1b67Sflxd 	/* No arguments. */
1228b0be1b67Sflxd 	if (argc != 0)
1229b0be1b67Sflxd 		usage();
1230b0be1b67Sflxd 
1231b0be1b67Sflxd 	scsi_mode_sense(fd, 0x01, 0x00, &data, sizeof(data));
1232b0be1b67Sflxd 
1233b0be1b67Sflxd 	flags = data.err_recov_params.flags;
1234b0be1b67Sflxd 	if ((flags & (ERR_RECOV_ARRE | ERR_RECOV_AWRE)) == 0)
1235b0be1b67Sflxd 		printf("%s: no automatic reallocation enabled\n", dvname);
1236b0be1b67Sflxd 	else {
1237b0be1b67Sflxd 		printf("%s: automatic read reallocation %senabled\n", dvname,
1238b0be1b67Sflxd 		    (flags & ERR_RECOV_ARRE) ? "" : "not ");
1239b0be1b67Sflxd 		printf("%s: automatic write reallocation %senabled\n", dvname,
1240b0be1b67Sflxd 		    (flags & ERR_RECOV_AWRE) ? "" : "not ");
1241b0be1b67Sflxd 	}
1242b0be1b67Sflxd 	printf("%s: error recovery parameters are %ssavable\n", dvname,
1243b0be1b67Sflxd 	    (data.err_recov_params.pg_code & PGCODE_PS) ? "" : "not ");
1244b0be1b67Sflxd }
1245b0be1b67Sflxd 
1246b0be1b67Sflxd /*
1247b0be1b67Sflxd  * device_setrealloc:
1248b0be1b67Sflxd  *
1249b0be1b67Sflxd  *	Set the automatic reallocation parameters for a SCSI disk.
1250b0be1b67Sflxd  */
1251b0be1b67Sflxd static void
1252b0be1b67Sflxd device_setrealloc(int argc, char *argv[])
1253b0be1b67Sflxd {
1254b0be1b67Sflxd 	struct {
1255b0be1b67Sflxd 		struct scsi_mode_parameter_header_6 header;
1256b0be1b67Sflxd 		struct scsi_general_block_descriptor blk_desc;
1257b0be1b67Sflxd 		struct page_err_recov err_recov_params;
1258b0be1b67Sflxd 	} data;
1259b0be1b67Sflxd 	int dlen;
1260b0be1b67Sflxd 	u_int8_t flags, byte2;
1261b0be1b67Sflxd 
1262b0be1b67Sflxd 	if (argc > 2 || argc == 0)
1263b0be1b67Sflxd 		usage();
1264b0be1b67Sflxd 
1265b0be1b67Sflxd 	flags = 0;
1266b0be1b67Sflxd 	byte2 = 0;
1267b0be1b67Sflxd 	if (strcmp(argv[0], "none") == 0)
1268b0be1b67Sflxd 		flags = 0;
1269b0be1b67Sflxd 	else if (strcmp(argv[0], "r") == 0)
1270b0be1b67Sflxd 		flags = ERR_RECOV_ARRE;
1271b0be1b67Sflxd 	else if (strcmp(argv[0], "w") == 0)
1272b0be1b67Sflxd 		flags = ERR_RECOV_AWRE;
1273b0be1b67Sflxd 	else if (strcmp(argv[0], "rw") == 0)
1274b0be1b67Sflxd 		flags = ERR_RECOV_ARRE | ERR_RECOV_AWRE;
1275b0be1b67Sflxd 	else
1276b0be1b67Sflxd 		usage();
1277b0be1b67Sflxd 
1278b0be1b67Sflxd 	if (argc == 2) {
1279b0be1b67Sflxd 		if (strcmp(argv[1], "save") == 0)
1280b0be1b67Sflxd 			byte2 = SMS_SP;
1281b0be1b67Sflxd 		else
1282b0be1b67Sflxd 			usage();
1283b0be1b67Sflxd 	}
1284b0be1b67Sflxd 
1285b0be1b67Sflxd 	scsi_mode_sense(fd, 0x01, 0x00, &data, sizeof(data));
1286b0be1b67Sflxd 
1287b0be1b67Sflxd 	data.err_recov_params.pg_code &= PGCODE_MASK;
1288b0be1b67Sflxd 	data.err_recov_params.flags &= ~(ERR_RECOV_ARRE | ERR_RECOV_AWRE);
1289b0be1b67Sflxd 	data.err_recov_params.flags |= flags;
1290b0be1b67Sflxd 
1291b0be1b67Sflxd 	data.header.data_length = 0;
1292b0be1b67Sflxd 
1293b0be1b67Sflxd 	dlen = sizeof(data.header) + sizeof(data.blk_desc) + 2 +
1294b0be1b67Sflxd 	    data.err_recov_params.pg_length;
1295b0be1b67Sflxd 
1296b0be1b67Sflxd 	scsi_mode_select(fd, byte2, &data, dlen);
1297b0be1b67Sflxd }
1298b0be1b67Sflxd 
1299b0be1b67Sflxd /*
130046743e21Smycroft  * device_prevent:
130146743e21Smycroft  *
130246743e21Smycroft  *      Issue a prevent to a SCSI device.
130346743e21Smycroft  */
13049211d764Sjakllsch static void
13059bab889dSxtraeme device_prevent(int argc, char *argv[])
130646743e21Smycroft {
1307df9803ceSthorpej 	struct scsi_prevent_allow_medium_removal cmd;
130846743e21Smycroft 
130946743e21Smycroft 	/* No arguments. */
131046743e21Smycroft 	if (argc != 0)
131146743e21Smycroft 		usage();
131246743e21Smycroft 
131346743e21Smycroft 	memset(&cmd, 0, sizeof(cmd));
131446743e21Smycroft 
1315df9803ceSthorpej 	cmd.opcode = SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL;
1316df9803ceSthorpej 	cmd.how = SPAMR_PREVENT_DT;	/* XXX SMAMR_PREVENT_ALL? */
131746743e21Smycroft 
131846743e21Smycroft 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
131946743e21Smycroft 
132046743e21Smycroft 	return;
132146743e21Smycroft }
132246743e21Smycroft 
132346743e21Smycroft /*
132446743e21Smycroft  * device_allow:
132546743e21Smycroft  *
132646743e21Smycroft  *      Issue a stop to a SCSI device.
132746743e21Smycroft  */
13289211d764Sjakllsch static void
13299bab889dSxtraeme device_allow(int argc, char *argv[])
133046743e21Smycroft {
1331df9803ceSthorpej 	struct scsi_prevent_allow_medium_removal cmd;
133246743e21Smycroft 
133346743e21Smycroft 	/* No arguments. */
133446743e21Smycroft 	if (argc != 0)
133546743e21Smycroft 		usage();
133646743e21Smycroft 
133746743e21Smycroft 	memset(&cmd, 0, sizeof(cmd));
133846743e21Smycroft 
1339df9803ceSthorpej 	cmd.opcode = SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL;
1340df9803ceSthorpej 	cmd.how = SPAMR_ALLOW;
134146743e21Smycroft 
134246743e21Smycroft 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
134346743e21Smycroft 
134446743e21Smycroft 	return;
134546743e21Smycroft }
134646743e21Smycroft 
134746743e21Smycroft /*
134841194c0eSmjacob  * device_start:
134941194c0eSmjacob  *
135041194c0eSmjacob  *      Issue a start to a SCSI device.
135141194c0eSmjacob  */
13529211d764Sjakllsch static void
13539bab889dSxtraeme device_start(int argc, char *argv[])
135441194c0eSmjacob {
135541194c0eSmjacob 	struct scsipi_start_stop cmd;
135641194c0eSmjacob 
135741194c0eSmjacob 	/* No arguments. */
135841194c0eSmjacob 	if (argc != 0)
135941194c0eSmjacob 		usage();
136041194c0eSmjacob 
136141194c0eSmjacob 	memset(&cmd, 0, sizeof(cmd));
136241194c0eSmjacob 
136341194c0eSmjacob 	cmd.opcode = START_STOP;
136441194c0eSmjacob 	cmd.how = SSS_START;
136541194c0eSmjacob 
136676b92d1aSfair 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 30000, 0);
136741194c0eSmjacob 
136841194c0eSmjacob 	return;
136941194c0eSmjacob }
137041194c0eSmjacob 
137141194c0eSmjacob /*
137241194c0eSmjacob  * device_stop:
137341194c0eSmjacob  *
137441194c0eSmjacob  *      Issue a stop to a SCSI device.
137541194c0eSmjacob  */
13769211d764Sjakllsch static void
13779bab889dSxtraeme device_stop(int argc, char *argv[])
137841194c0eSmjacob {
137941194c0eSmjacob 	struct scsipi_start_stop cmd;
138041194c0eSmjacob 
138141194c0eSmjacob 	/* No arguments. */
138241194c0eSmjacob 	if (argc != 0)
138341194c0eSmjacob 		usage();
138441194c0eSmjacob 
138541194c0eSmjacob 	memset(&cmd, 0, sizeof(cmd));
138641194c0eSmjacob 
138741194c0eSmjacob 	cmd.opcode = START_STOP;
138841194c0eSmjacob 	cmd.how = SSS_STOP;
138941194c0eSmjacob 
139076b92d1aSfair 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 30000, 0);
139141194c0eSmjacob 
139241194c0eSmjacob 	return;
139341194c0eSmjacob }
139441194c0eSmjacob 
139541194c0eSmjacob /*
139641194c0eSmjacob  * device_tur:
139741194c0eSmjacob  *
1398e47fe82aSbouyer  *	Issue a TEST UNIT READY to a SCSI device.
139941194c0eSmjacob  */
14009211d764Sjakllsch static void
14019bab889dSxtraeme device_tur(int argc, char *argv[])
140241194c0eSmjacob {
1403df9803ceSthorpej 	struct scsi_test_unit_ready cmd;
140441194c0eSmjacob 
140541194c0eSmjacob 	/* No arguments. */
140641194c0eSmjacob 	if (argc != 0)
140741194c0eSmjacob 		usage();
140841194c0eSmjacob 
140941194c0eSmjacob 	memset(&cmd, 0, sizeof(cmd));
141041194c0eSmjacob 
1411df9803ceSthorpej 	cmd.opcode = SCSI_TEST_UNIT_READY;
141241194c0eSmjacob 
141341194c0eSmjacob 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
141441194c0eSmjacob 
141541194c0eSmjacob 	return;
141641194c0eSmjacob }
141741194c0eSmjacob 
14180683880eSthorpej /*
14190683880eSthorpej  * BUS COMMANDS
14200683880eSthorpej  */
142141194c0eSmjacob 
14220683880eSthorpej /*
14230683880eSthorpej  * bus_reset:
14240683880eSthorpej  *
14250683880eSthorpej  *	Issue a reset to a SCSI bus.
14260683880eSthorpej  */
14279211d764Sjakllsch static void
14289bab889dSxtraeme bus_reset(int argc, char *argv[])
14290683880eSthorpej {
14300683880eSthorpej 
14310683880eSthorpej 	/* No arguments. */
14320683880eSthorpej 	if (argc != 0)
14330683880eSthorpej 		usage();
14340683880eSthorpej 
14350683880eSthorpej 	if (ioctl(fd, SCBUSIORESET, NULL) != 0)
14360683880eSthorpej 		err(1, "SCBUSIORESET");
14370683880eSthorpej 
14380683880eSthorpej 	return;
14390683880eSthorpej }
144041194c0eSmjacob 
144141194c0eSmjacob /*
1442c9a47c22Sthorpej  * bus_scan:
1443c9a47c22Sthorpej  *
1444c9a47c22Sthorpej  *	Rescan a SCSI bus for new devices.
1445c9a47c22Sthorpej  */
14469211d764Sjakllsch static void
14479bab889dSxtraeme bus_scan(int argc, char *argv[])
1448c9a47c22Sthorpej {
1449c9a47c22Sthorpej 	struct scbusioscan_args args;
1450c9a47c22Sthorpej 	char *cp;
1451c9a47c22Sthorpej 
1452c9a47c22Sthorpej 	/* Must have two args: target lun */
1453c9a47c22Sthorpej 	if (argc != 2)
1454958ed46fShubertf 		usage();
1455c9a47c22Sthorpej 
14562829eb17Sad 	if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0)
1457c9a47c22Sthorpej 		args.sa_target = -1;
1458c9a47c22Sthorpej 	else {
1459c9a47c22Sthorpej 		args.sa_target = strtol(argv[0], &cp, 10);
1460c9a47c22Sthorpej 		if (*cp != '\0' || args.sa_target < 0)
146193e37412Sad 			errx(1, "invalid target: %s", argv[0]);
1462c9a47c22Sthorpej 	}
1463c9a47c22Sthorpej 
14642829eb17Sad 	if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0)
1465c9a47c22Sthorpej 		args.sa_lun = -1;
1466c9a47c22Sthorpej 	else {
1467c9a47c22Sthorpej 		args.sa_lun = strtol(argv[1], &cp, 10);
1468c9a47c22Sthorpej 		if (*cp != '\0' || args.sa_lun < 0)
146993e37412Sad 			errx(1, "invalid lun: %s", argv[1]);
1470c9a47c22Sthorpej 	}
1471c9a47c22Sthorpej 
1472c9a47c22Sthorpej 	if (ioctl(fd, SCBUSIOSCAN, &args) != 0)
1473c9a47c22Sthorpej 		err(1, "SCBUSIOSCAN");
1474c9a47c22Sthorpej 
1475c9a47c22Sthorpej 	return;
1476c9a47c22Sthorpej }
147703fd5e67Sbouyer 
147803fd5e67Sbouyer /*
147903fd5e67Sbouyer  * bus_detach:
148003fd5e67Sbouyer  *
148103fd5e67Sbouyer  *	detach SCSI devices from a bus.
148203fd5e67Sbouyer  */
14839211d764Sjakllsch static void
14849bab889dSxtraeme bus_detach(int argc, char *argv[])
148503fd5e67Sbouyer {
148603fd5e67Sbouyer 	struct scbusiodetach_args args;
148703fd5e67Sbouyer 	char *cp;
148803fd5e67Sbouyer 
148903fd5e67Sbouyer 	/* Must have two args: target lun */
149003fd5e67Sbouyer 	if (argc != 2)
149103fd5e67Sbouyer 		usage();
149203fd5e67Sbouyer 
149303fd5e67Sbouyer 	if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0)
149403fd5e67Sbouyer 		args.sa_target = -1;
149503fd5e67Sbouyer 	else {
149603fd5e67Sbouyer 		args.sa_target = strtol(argv[0], &cp, 10);
149703fd5e67Sbouyer 		if (*cp != '\0' || args.sa_target < 0)
14986742cb18Sgrant 			errx(1, "invalid target: %s", argv[0]);
149903fd5e67Sbouyer 	}
150003fd5e67Sbouyer 
150103fd5e67Sbouyer 	if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0)
150203fd5e67Sbouyer 		args.sa_lun = -1;
150303fd5e67Sbouyer 	else {
150403fd5e67Sbouyer 		args.sa_lun = strtol(argv[1], &cp, 10);
150503fd5e67Sbouyer 		if (*cp != '\0' || args.sa_lun < 0)
15066742cb18Sgrant 			errx(1, "invalid lun: %s", argv[1]);
150703fd5e67Sbouyer 	}
150803fd5e67Sbouyer 
150903fd5e67Sbouyer 	if (ioctl(fd, SCBUSIODETACH, &args) != 0)
151003fd5e67Sbouyer 		err(1, "SCBUSIODETACH");
151103fd5e67Sbouyer 
151203fd5e67Sbouyer 	return;
151303fd5e67Sbouyer }
1514