xref: /netbsd-src/sbin/scsictl/scsictl.c (revision de1dfb1250df962f1ff3a011772cf58e605aed11)
1 /*	$NetBSD: scsictl.c,v 1.24 2004/06/01 02:40:00 fair Exp $	*/
2 
3 /*-
4  * Copyright (c) 1998, 2002 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9  * NASA Ames Research Center.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *	This product includes software developed by the NetBSD
22  *	Foundation, Inc. and its contributors.
23  * 4. Neither the name of The NetBSD Foundation nor the names of its
24  *    contributors may be used to endorse or promote products derived
25  *    from this software without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 
40 /*
41  * scsictl(8) - a program to manipulate SCSI devices and busses.
42  */
43 #include <sys/cdefs.h>
44 
45 #ifndef lint
46 __RCSID("$NetBSD: scsictl.c,v 1.24 2004/06/01 02:40:00 fair Exp $");
47 #endif
48 
49 
50 #include <sys/param.h>
51 #include <sys/ioctl.h>
52 #include <sys/scsiio.h>
53 #include <err.h>
54 #include <errno.h>
55 #include <fcntl.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60 #include <util.h>
61 
62 #include <dev/scsipi/scsipi_all.h>
63 #include <dev/scsipi/scsi_all.h>
64 #include <dev/scsipi/scsi_disk.h>
65 #include <dev/scsipi/scsipiconf.h>
66 
67 #include "extern.h"
68 
69 struct command {
70 	const char *cmd_name;
71 	const char *arg_names;
72 	void (*cmd_func) __P((int, char *[]));
73 };
74 
75 int	main __P((int, char *[]));
76 void	usage __P((void));
77 
78 int	fd;				/* file descriptor for device */
79 const	char *dvname;			/* device name */
80 char	dvname_store[MAXPATHLEN];	/* for opendisk(3) */
81 const	char *cmdname;			/* command user issued */
82 const	char *argnames;			/* helpstring: expected arguments */
83 struct	scsi_addr dvaddr;		/* SCSI device's address */
84 
85 void	device_format __P((int, char *[]));
86 void	device_identify __P((int, char *[]));
87 void	device_reassign __P((int, char *[]));
88 void	device_release __P((int, char *[]));
89 void	device_reserve __P((int, char *[]));
90 void	device_reset __P((int, char *[]));
91 void	device_debug __P((int, char *[]));
92 void	device_prevent __P((int, char *[]));
93 void	device_allow __P((int, char *[]));
94 void	device_start __P((int, char *[]));
95 void	device_stop __P((int, char *[]));
96 void	device_tur __P((int, char *[]));
97 void	device_getcache __P((int, char *[]));
98 void	device_setcache __P((int, char *[]));
99 void	device_flushcache __P((int, char *[]));
100 
101 struct command device_commands[] = {
102 	{ "format",	"[blocksize [immediate]]", 	device_format },
103 	{ "identify",	"",			device_identify },
104 	{ "reassign",	"blkno [blkno [...]]",	device_reassign },
105 	{ "release",	"",			device_release },
106 	{ "reserve",	"",			device_reserve },
107 	{ "reset",	"",			device_reset },
108 	{ "debug",	"level",		device_debug },
109 	{ "prevent",	"",			device_prevent },
110 	{ "allow",	"",			device_allow },
111 	{ "start",	"",			device_start },
112 	{ "stop",	"",			device_stop },
113 	{ "tur",	"",			device_tur },
114 	{ "getcache",	"",			device_getcache },
115 	{ "setcache",	"none|r|w|rw [save]",	device_setcache },
116 	{ "flushcache",	"",			device_flushcache },
117 	{ NULL,		NULL,			NULL },
118 };
119 
120 void	bus_reset __P((int, char *[]));
121 void	bus_scan __P((int, char *[]));
122 void	bus_detach __P((int, char *[]));
123 
124 struct command bus_commands[] = {
125 	{ "reset",	"",			bus_reset },
126 	{ "scan",	"target lun",		bus_scan },
127 	{ "detach",	"target lun",		bus_detach },
128 	{ NULL,		NULL,				NULL },
129 };
130 
131 int
132 main(argc, argv)
133 	int argc;
134 	char *argv[];
135 {
136 	struct command *commands;
137 	int i;
138 
139 	/* Must have at least: device command */
140 	if (argc < 3)
141 		usage();
142 
143 	/* Skip program name, get and skip device name and command. */
144 	dvname = argv[1];
145 	cmdname = argv[2];
146 	argv += 3;
147 	argc -= 3;
148 
149 	/*
150 	 * Open the device and determine if it's a scsibus or an actual
151 	 * device.  Devices respond to the SCIOCIDENTIFY ioctl.
152 	 */
153 	fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0);
154 	if (fd == -1) {
155 		if (errno == ENOENT) {
156 			/*
157 			 * Device doesn't exist.  Probably trying to open
158 			 * a device which doesn't use disk semantics for
159 			 * device name.  Try again, specifying "cooked",
160 			 * which leaves off the "r" in front of the device's
161 			 * name.
162 			 */
163 			fd = opendisk(dvname, O_RDWR, dvname_store,
164 			    sizeof(dvname_store), 1);
165 			if (fd == -1)
166 				err(1, "%s", dvname);
167 		} else
168 			err(1, "%s", dvname);
169 	}
170 
171 	/*
172 	 * Point the dvname at the actual device name that opendisk() opened.
173 	 */
174 	dvname = dvname_store;
175 
176 	if (ioctl(fd, SCIOCIDENTIFY, &dvaddr) < 0)
177 		commands = bus_commands;
178 	else
179 		commands = device_commands;
180 
181 	/* Look up and call the command. */
182 	for (i = 0; commands[i].cmd_name != NULL; i++)
183 		if (strcmp(cmdname, commands[i].cmd_name) == 0)
184 			break;
185 	if (commands[i].cmd_name == NULL)
186 		errx(1, "unknown %s command: %s",
187 		    commands == bus_commands ? "bus" : "device", cmdname);
188 
189 	argnames = commands[i].arg_names;
190 
191 	(*commands[i].cmd_func)(argc, argv);
192 	exit(0);
193 }
194 
195 void
196 usage()
197 {
198 	int i;
199 
200 	fprintf(stderr, "usage: %s device command [arg [...]]\n",
201 	    getprogname());
202 
203 	fprintf(stderr, "   Commands pertaining to scsi devices:\n");
204 	for (i=0; device_commands[i].cmd_name != NULL; i++)
205 		fprintf(stderr, "\t%s %s\n", device_commands[i].cmd_name,
206 					    device_commands[i].arg_names);
207 	fprintf(stderr, "   Commands pertaining to scsi busses:\n");
208 	for (i=0; bus_commands[i].cmd_name != NULL; i++)
209 		fprintf(stderr, "\t%s %s\n", bus_commands[i].cmd_name,
210 					    bus_commands[i].arg_names);
211 	fprintf(stderr, "   Use `any' or `all' to wildcard target or lun\n");
212 
213 	exit(1);
214 }
215 
216 /*
217  * DEVICE COMMANDS
218  */
219 
220 /*
221  * device_format:
222  *
223  *	Format a direct access device.
224  */
225 void
226 device_format(argc, argv)
227 	int argc;
228 	char *argv[];
229 {
230 	u_int32_t blksize;
231 	int i, j, immediate;
232 #define	PC	(65536/10)
233 	static int complete[] = {
234 	    PC*1, PC*2, PC*3, PC*4, PC*5, PC*6, PC*7, PC*8, PC*9, 65536
235 	};
236 	char *cp, buffer[64];
237 	struct scsipi_sense_data sense;
238 	struct scsi_format_unit cmd;
239 	struct {
240 		struct scsi_format_unit_defect_list_header header;
241 		/* optional initialization pattern */
242 		/* optional defect list */
243 	} dfl;
244 	struct {
245 		struct scsipi_mode_header header;
246 		struct scsi_blk_desc blk_desc;
247 		struct page_disk_format format_page;
248 	} mode_page;
249 	struct {
250 		struct scsipi_mode_header header;
251 		struct scsi_blk_desc blk_desc;
252 	} data_select;
253 
254 
255 	/* Blocksize is an optional argument. */
256 	if (argc > 2)
257 		usage();
258 
259 	/*
260 	 * Loop doing Request Sense to clear any pending Unit Attention.
261 	 *
262 	 * Multiple conditions may exist on the drive which are returned
263 	 * in priority order.
264 	 */
265 	for (i = 0; i < 8; i++) {
266 		scsi_request_sense(fd, &sense, sizeof (sense));
267 		if ((j = sense.flags & SSD_KEY) == SKEY_NO_SENSE)
268 			break;
269 	}
270 	/*
271 	 * Make sure we cleared any pending Unit Attention
272 	 */
273 	if (j != SKEY_NO_SENSE) {
274 		cp = scsi_decode_sense((const unsigned char *) &sense, 2,
275 		    buffer, sizeof (buffer));
276 		errx(1, "failed to clean Unit Attention: %s", cp);
277 	}
278 
279 	/*
280 	 * Get the DISK FORMAT mode page.  SCSI-2 recommends specifying the
281 	 * interleave read from this page in the FORMAT UNIT command.
282 	 */
283 	scsi_mode_sense(fd, 0x03, 0x00, &mode_page, sizeof(mode_page));
284 
285 	j = (mode_page.format_page.bytes_s[0] << 8) |
286 	    (mode_page.format_page.bytes_s[1]);
287 
288 	if (j != DEV_BSIZE)
289 		printf("current disk sector size: %hd\n", j);
290 
291 	memset(&cmd, 0, sizeof(cmd));
292 
293 	cmd.opcode = SCSI_FORMAT_UNIT;
294 	memcpy(cmd.interleave, mode_page.format_page.interleave,
295 	    sizeof(cmd.interleave));
296 
297 	/*
298 	 * The blocksize on the device is only changed if the user
299 	 * specified a new blocksize. If not specified the blocksize
300 	 * used for the device will be the Default value in the device.
301 	 * We don't specify the number of blocks since the format
302 	 * command will always reformat the entire drive.  Also by
303 	 * not specifying a block count the drive will reset the
304 	 * block count to the maximum available after the format
305 	 * completes if the blocksize was changed in the format.
306 	 * Finally, the new disk geometry will not but updated on
307 	 * the drive in permanent storage until _AFTER_ the format
308 	 * completes successfully.
309 	 */
310 	if (argc > 0) {
311 		blksize = strtoul(argv[0], &cp, 10);
312 		if (*cp != '\0')
313 			errx(1, "invalid block size: %s", argv[0]);
314 
315 		memset(&data_select, 0, sizeof(data_select));
316 
317 		data_select.header.blk_desc_len = sizeof(struct scsi_blk_desc);
318 		/*
319 		 * blklen in desc is 3 bytes with a leading reserved byte
320 		 */
321 		_lto4b(blksize, &data_select.blk_desc.reserved);
322 
323 		/*
324 		 * Issue Mode Select to modify the device blocksize to be
325 		 * used on the Format.  The modified device geometry will
326 		 * be stored as Current and Saved Page 3 parameters when
327 		 * the Format completes.
328 		 */
329 		scsi_mode_select(fd, 0, &data_select, sizeof(data_select));
330 
331 		/*
332 		 * Since user specified a specific block size make sure it
333 		 * gets stored in the device when the format completes.
334 		 *
335 		 * Also scrub the defect list back to the manufacturers
336 		 * original.
337 		 */
338 		cmd.flags = SFU_CMPLST | SFU_FMTDATA;
339 	}
340 
341 	memset(&dfl, 0, sizeof(dfl));
342 
343 	if (argc > 1 && strncmp(argv[1], "imm", 3) == 0) {
344 		/*
345 		 * Signal target for an immediate return from Format.
346 		 *
347 		 * We'll poll for completion status.
348 		 */
349 		dfl.header.flags = DLH_IMMED;
350 		immediate = 1;
351 	} else {
352 		immediate = 0;
353 	}
354 
355 	scsi_command(fd, &cmd, sizeof(cmd), &dfl, sizeof(dfl),
356 	    8 * 60 * 60 * 1000, 0);
357 
358 	/*
359 	 * Poll device for completion of Format
360 	 */
361 	if (immediate) {
362 		i = 0;
363 		printf("formatting.");
364 		fflush(stdout);
365 		do {
366 			scsireq_t req;
367 			struct scsipi_test_unit_ready tcmd;
368 
369 			memset(&tcmd, 0, sizeof(cmd));
370 			tcmd.opcode = TEST_UNIT_READY;
371 
372 			memset(&req, 0, sizeof(req));
373 			memcpy(req.cmd, &tcmd, 6);
374 			req.cmdlen = 6;
375 			req.timeout = 10000;
376 			req.senselen = SENSEBUFLEN;
377 
378 			if (ioctl(fd, SCIOCCOMMAND, &req) == -1) {
379 				err(1, "SCIOCCOMMAND");
380 			}
381 
382 			if (req.retsts == SCCMD_OK) {
383 				break;
384 			} else if (req.retsts == SCCMD_TIMEOUT) {
385 				fprintf(stderr, "%s: SCSI command timed out",
386 				    dvname);
387 				break;
388 			} else if (req.retsts == SCCMD_BUSY) {
389 				fprintf(stderr, "%s: device is busy",
390 				    dvname);
391 				break;
392 			} else if (req.retsts != SCCMD_SENSE) {
393 				fprintf(stderr,
394 				    "%s: device had unknown status %x", dvname,
395 				    req.retsts);
396 				break;
397 			}
398 			memcpy(&sense, req.sense, SENSEBUFLEN);
399 			if (sense.sense_key_spec_1 == SSD_SCS_VALID) {
400 				j = (sense.sense_key_spec_2 << 8) |
401 				    (sense.sense_key_spec_3);
402 				if (j >= complete[i]) {
403 					printf(".%d0%%.", ++i);
404 					fflush(stdout);
405 				}
406 			}
407 			sleep(10);
408 		} while ((sense.flags & SSD_KEY) == SKEY_NOT_READY);
409 		printf(".100%%..done.\n");
410 	}
411 	return;
412 }
413 
414 /*
415  * device_identify:
416  *
417  *	Display the identity of the device, including it's SCSI bus,
418  *	target, lun, and it's vendor/product/revision information.
419  */
420 void
421 device_identify(argc, argv)
422 	int argc;
423 	char *argv[];
424 {
425 	struct scsipi_inquiry_data inqbuf;
426 	struct scsipi_inquiry cmd;
427 
428 	/* x4 in case every character is escaped, +1 for NUL. */
429 	char vendor[(sizeof(inqbuf.vendor) * 4) + 1],
430 	     product[(sizeof(inqbuf.product) * 4) + 1],
431 	     revision[(sizeof(inqbuf.revision) * 4) + 1];
432 
433 	/* No arguments. */
434 	if (argc != 0)
435 		usage();
436 
437 	memset(&cmd, 0, sizeof(cmd));
438 	memset(&inqbuf, 0, sizeof(inqbuf));
439 
440 	cmd.opcode = INQUIRY;
441 	cmd.length = sizeof(inqbuf);
442 
443 	scsi_command(fd, &cmd, sizeof(cmd), &inqbuf, sizeof(inqbuf),
444 	    10000, SCCMD_READ);
445 
446 	scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor,
447 	    sizeof(inqbuf.vendor));
448 	scsi_strvis(product, sizeof(product), inqbuf.product,
449 	    sizeof(inqbuf.product));
450 	scsi_strvis(revision, sizeof(revision), inqbuf.revision,
451 	    sizeof(inqbuf.revision));
452 
453 	printf("%s: scsibus%d target %d lun %d <%s, %s, %s>\n",
454 	    dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target,
455 	    dvaddr.addr.scsi.lun, vendor, product, revision);
456 
457 	return;
458 }
459 
460 /*
461  * device_reassign:
462  *
463  *	Reassign bad blocks on a direct access device.
464  */
465 void
466 device_reassign(argc, argv)
467 	int argc;
468 	char *argv[];
469 {
470 	struct scsi_reassign_blocks cmd;
471 	struct scsi_reassign_blocks_data *data;
472 	size_t dlen;
473 	u_int32_t blkno;
474 	int i;
475 	char *cp;
476 
477 	/* We get a list of block numbers. */
478 	if (argc < 1)
479 		usage();
480 
481 	/*
482 	 * Allocate the reassign blocks descriptor.  The 4 comes from the
483 	 * size of the block address in the defect descriptor.
484 	 */
485 	dlen = sizeof(struct scsi_reassign_blocks_data) + ((argc - 1) * 4);
486 	data = malloc(dlen);
487 	if (data == NULL)
488 		errx(1, "unable to allocate defect descriptor");
489 	memset(data, 0, dlen);
490 
491 	cmd.opcode = SCSI_REASSIGN_BLOCKS;
492 	cmd.byte2 = 0;
493 	cmd.unused[0] = 0;
494 	cmd.unused[1] = 0;
495 	cmd.unused[2] = 0;
496 	cmd.control = 0;
497 
498 	/* Defect descriptor length. */
499 	_lto2b(argc * 4, data->length);
500 
501 	/* Build the defect descriptor list. */
502 	for (i = 0; i < argc; i++) {
503 		blkno = strtoul(argv[i], &cp, 10);
504 		if (*cp != '\0')
505 			errx(1, "invalid block number: %s", argv[i]);
506 		_lto4b(blkno, data->defect_descriptor[i].dlbaddr);
507 	}
508 
509 	scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_WRITE);
510 
511 	free(data);
512 	return;
513 }
514 
515 /*
516  * device_release:
517  *
518  *      Issue a RELEASE command to a SCSI drevice
519  */
520 #ifndef	SCSI_RELEASE
521 #define	SCSI_RELEASE	0x17
522 #endif
523 void
524 device_release(argc, argv)
525 	int argc;
526 	char *argv[];
527 {
528 	struct scsipi_test_unit_ready cmd;	/* close enough */
529 
530 	/* No arguments. */
531 	if (argc != 0)
532 		usage();
533 
534 	memset(&cmd, 0, sizeof(cmd));
535 
536 	cmd.opcode = SCSI_RELEASE;
537 
538 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
539 
540 	return;
541 }
542 
543 
544 
545 /*
546  * device_reserve:
547  *
548  *      Issue a RESERVE command to a SCSI drevice
549  */
550 #ifndef	SCSI_RESERVE
551 #define	SCSI_RESERVE	0x16
552 #endif
553 void
554 device_reserve(argc, argv)
555 	int argc;
556 	char *argv[];
557 {
558 	struct scsipi_test_unit_ready cmd;	/* close enough */
559 
560 	/* No arguments. */
561 	if (argc != 0)
562 		usage();
563 
564 	memset(&cmd, 0, sizeof(cmd));
565 
566 	cmd.opcode = SCSI_RESERVE;
567 
568 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
569 
570 	return;
571 }
572 
573 /*
574  * device_reset:
575  *
576  *	Issue a reset to a SCSI device.
577  */
578 void
579 device_reset(argc, argv)
580 	int argc;
581 	char *argv[];
582 {
583 
584 	/* No arguments. */
585 	if (argc != 0)
586 		usage();
587 
588 	if (ioctl(fd, SCIOCRESET, NULL) != 0)
589 		err(1, "SCIOCRESET");
590 
591 	return;
592 }
593 
594 /*
595  * device_debug:
596  *
597  *	Set debug level to a SCSI device.
598  *	scsipi will print anything iff SCSIPI_DEBUG set in config.
599  */
600 void
601 device_debug(argc, argv)
602 	int argc;
603 	char *argv[];
604 {
605 	int lvl;
606 
607 	if (argc < 1)
608 		usage();
609 
610 	lvl = atoi(argv[0]);
611 
612 	if (ioctl(fd, SCIOCDEBUG, &lvl) != 0)
613 		err(1, "SCIOCDEBUG");
614 
615 	return;
616 }
617 
618 /*
619  * device_getcache:
620  *
621  *	Get the caching parameters for a SCSI disk.
622  */
623 void
624 device_getcache(argc, argv)
625 	int argc;
626 	char *argv[];
627 {
628 	struct {
629 		struct scsipi_mode_header header;
630 		struct scsi_blk_desc blk_desc;
631 		struct page_caching caching_params;
632 	} data;
633 
634 	/* No arguments. */
635 	if (argc != 0)
636 		usage();
637 
638 	scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data));
639 
640 	if ((data.caching_params.flags & (CACHING_RCD|CACHING_WCE)) ==
641 	    CACHING_RCD)
642 		printf("%s: no caches enabled\n", dvname);
643 	else {
644 		printf("%s: read cache %senabled\n", dvname,
645 		    (data.caching_params.flags & CACHING_RCD) ? "not " : "");
646 		printf("%s: write-back cache %senabled\n", dvname,
647 		    (data.caching_params.flags & CACHING_WCE) ? "" : "not ");
648 	}
649 	printf("%s: caching parameters are %ssavable\n", dvname,
650 	    (data.caching_params.pg_code & PGCODE_PS) ? "" : "not ");
651 }
652 
653 /*
654  * device_setcache:
655  *
656  *	Set cache enables for a SCSI disk.
657  */
658 void
659 device_setcache(argc, argv)
660 	int argc;
661 	char *argv[];
662 {
663 	struct {
664 		struct scsipi_mode_header header;
665 		struct scsi_blk_desc blk_desc;
666 		struct page_caching caching_params;
667 	} data;
668 	int dlen;
669 	u_int8_t flags, byte2;
670 
671 	if (argc > 2 || argc == 0)
672 		usage();
673 
674 	if (strcmp(argv[0], "none") == 0)
675 		flags = CACHING_RCD;
676 	else if (strcmp(argv[0], "r") == 0)
677 		flags = 0;
678 	else if (strcmp(argv[0], "w") == 0)
679 		flags = CACHING_RCD|CACHING_WCE;
680 	else if (strcmp(argv[0], "rw") == 0)
681 		flags = CACHING_WCE;
682 	else
683 		usage();
684 
685 	if (argc == 2) {
686 		if (strcmp(argv[1], "save") == 0)
687 			byte2 = SMS_SP;
688 		else
689 			usage();
690 	}
691 
692 	scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data));
693 
694 	data.caching_params.pg_code &= PGCODE_MASK;
695 	data.caching_params.flags =
696 	    (data.caching_params.flags & ~(CACHING_RCD|CACHING_WCE)) | flags;
697 
698 	data.caching_params.cache_segment_size[0] = 0;
699 	data.caching_params.cache_segment_size[1] = 0;
700 
701 	data.header.data_length = 0;
702 
703 	dlen = sizeof(data.header) + sizeof(data.blk_desc) + 2 +
704 	    data.caching_params.pg_length;
705 
706 	scsi_mode_select(fd, byte2, &data, dlen);
707 }
708 
709 /*
710  * device_flushcache:
711  *
712  *      Issue a FLUSH CACHE command to a SCSI drevice
713  */
714 #ifndef	SCSI_FLUSHCACHE
715 #define	SCSI_FLUSHCACHE	0x35
716 #endif
717 void
718 device_flushcache(argc, argv)
719 	int argc;
720 	char *argv[];
721 {
722 	struct scsipi_test_unit_ready cmd;	/* close enough */
723 
724 	/* No arguments. */
725 	if (argc != 0)
726 		usage();
727 
728 	memset(&cmd, 0, sizeof(cmd));
729 
730 	cmd.opcode = SCSI_FLUSHCACHE;
731 
732 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
733 
734 	return;
735 }
736 
737 /*
738  * device_prevent:
739  *
740  *      Issue a prevent to a SCSI device.
741  */
742 void
743 device_prevent(argc, argv)
744 	int argc;
745 	char *argv[];
746 {
747 	struct scsipi_prevent cmd;
748 
749 	/* No arguments. */
750 	if (argc != 0)
751 		usage();
752 
753 	memset(&cmd, 0, sizeof(cmd));
754 
755 	cmd.opcode = PREVENT_ALLOW;
756 	cmd.how = PR_PREVENT;
757 
758 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
759 
760 	return;
761 }
762 
763 /*
764  * device_allow:
765  *
766  *      Issue a stop to a SCSI device.
767  */
768 void
769 device_allow(argc, argv)
770 	int argc;
771 	char *argv[];
772 {
773 	struct scsipi_prevent cmd;
774 
775 	/* No arguments. */
776 	if (argc != 0)
777 		usage();
778 
779 	memset(&cmd, 0, sizeof(cmd));
780 
781 	cmd.opcode = PREVENT_ALLOW;
782 	cmd.how = PR_ALLOW;
783 
784 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
785 
786 	return;
787 }
788 
789 /*
790  * device_start:
791  *
792  *      Issue a start to a SCSI device.
793  */
794 void
795 device_start(argc, argv)
796 	int argc;
797 	char *argv[];
798 {
799 	struct scsipi_start_stop cmd;
800 
801 	/* No arguments. */
802 	if (argc != 0)
803 		usage();
804 
805 	memset(&cmd, 0, sizeof(cmd));
806 
807 	cmd.opcode = START_STOP;
808 	cmd.how = SSS_START;
809 
810 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 30000, 0);
811 
812 	return;
813 }
814 
815 /*
816  * device_stop:
817  *
818  *      Issue a stop to a SCSI device.
819  */
820 void
821 device_stop(argc, argv)
822 	int argc;
823 	char *argv[];
824 {
825 	struct scsipi_start_stop cmd;
826 
827 	/* No arguments. */
828 	if (argc != 0)
829 		usage();
830 
831 	memset(&cmd, 0, sizeof(cmd));
832 
833 	cmd.opcode = START_STOP;
834 	cmd.how = SSS_STOP;
835 
836 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 30000, 0);
837 
838 	return;
839 }
840 
841 /*
842  * device_tur:
843  *
844  *      Issue a TEST UNIT READY to a SCSI drevice
845  */
846 void
847 device_tur(argc, argv)
848 	int argc;
849 	char *argv[];
850 {
851 	struct scsipi_test_unit_ready cmd;
852 
853 	/* No arguments. */
854 	if (argc != 0)
855 		usage();
856 
857 	memset(&cmd, 0, sizeof(cmd));
858 
859 	cmd.opcode = TEST_UNIT_READY;
860 
861 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
862 
863 	return;
864 }
865 
866 /*
867  * BUS COMMANDS
868  */
869 
870 /*
871  * bus_reset:
872  *
873  *	Issue a reset to a SCSI bus.
874  */
875 void
876 bus_reset(argc, argv)
877 	int argc;
878 	char *argv[];
879 {
880 
881 	/* No arguments. */
882 	if (argc != 0)
883 		usage();
884 
885 	if (ioctl(fd, SCBUSIORESET, NULL) != 0)
886 		err(1, "SCBUSIORESET");
887 
888 	return;
889 }
890 
891 /*
892  * bus_scan:
893  *
894  *	Rescan a SCSI bus for new devices.
895  */
896 void
897 bus_scan(argc, argv)
898 	int argc;
899 	char *argv[];
900 {
901 	struct scbusioscan_args args;
902 	char *cp;
903 
904 	/* Must have two args: target lun */
905 	if (argc != 2)
906 		usage();
907 
908 	if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0)
909 		args.sa_target = -1;
910 	else {
911 		args.sa_target = strtol(argv[0], &cp, 10);
912 		if (*cp != '\0' || args.sa_target < 0)
913 			errx(1, "invalid target: %s", argv[0]);
914 	}
915 
916 	if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0)
917 		args.sa_lun = -1;
918 	else {
919 		args.sa_lun = strtol(argv[1], &cp, 10);
920 		if (*cp != '\0' || args.sa_lun < 0)
921 			errx(1, "invalid lun: %s", argv[1]);
922 	}
923 
924 	if (ioctl(fd, SCBUSIOSCAN, &args) != 0)
925 		err(1, "SCBUSIOSCAN");
926 
927 	return;
928 }
929 
930 /*
931  * bus_detach:
932  *
933  *	detach SCSI devices from a bus.
934  */
935 void
936 bus_detach(argc, argv)
937 	int argc;
938 	char *argv[];
939 {
940 	struct scbusiodetach_args args;
941 	char *cp;
942 
943 	/* Must have two args: target lun */
944 	if (argc != 2)
945 		usage();
946 
947 	if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0)
948 		args.sa_target = -1;
949 	else {
950 		args.sa_target = strtol(argv[0], &cp, 10);
951 		if (*cp != '\0' || args.sa_target < 0)
952 			errx(1, "invalid target: %s", argv[0]);
953 	}
954 
955 	if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0)
956 		args.sa_lun = -1;
957 	else {
958 		args.sa_lun = strtol(argv[1], &cp, 10);
959 		if (*cp != '\0' || args.sa_lun < 0)
960 			errx(1, "invalid lun: %s", argv[1]);
961 	}
962 
963 	if (ioctl(fd, SCBUSIODETACH, &args) != 0)
964 		err(1, "SCBUSIODETACH");
965 
966 	return;
967 }
968