xref: /netbsd-src/sbin/scsictl/scsictl.c (revision d710132b4b8ce7f7cccaaf660cb16aa16b4077a0)
1 /*	$NetBSD: scsictl.c,v 1.20 2003/06/23 11:53:43 agc 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.20 2003/06/23 11:53:43 agc 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_start __P((int, char *[]));
93 void	device_stop __P((int, char *[]));
94 void	device_tur __P((int, char *[]));
95 void	device_getcache __P((int, char *[]));
96 void	device_setcache __P((int, char *[]));
97 
98 struct command device_commands[] = {
99 	{ "format",	"[blocksize [immediate]]", 	device_format },
100 	{ "identify",	"",			device_identify },
101 	{ "reassign",	"blkno [blkno [...]]",	device_reassign },
102 	{ "release",	"",			device_release },
103 	{ "reserve",	"",			device_reserve },
104 	{ "reset",	"",			device_reset },
105 	{ "debug",	"level",		device_debug },
106 	{ "start",	"",			device_start },
107 	{ "stop",	"",			device_stop },
108 	{ "tur",	"",			device_tur },
109 	{ "getcache",	"",			device_getcache },
110 	{ "setcache",	"none|r|w|rw [save]",	device_setcache },
111 	{ NULL,		NULL,			NULL },
112 };
113 
114 void	bus_reset __P((int, char *[]));
115 void	bus_scan __P((int, char *[]));
116 void	bus_detach __P((int, char *[]));
117 
118 struct command bus_commands[] = {
119 	{ "reset",	"",			bus_reset },
120 	{ "scan",	"target lun",		bus_scan },
121 	{ "detach",	"target lun",		bus_detach },
122 	{ NULL,		NULL,				NULL },
123 };
124 
125 int
126 main(argc, argv)
127 	int argc;
128 	char *argv[];
129 {
130 	struct command *commands;
131 	int i;
132 
133 	/* Must have at least: device command */
134 	if (argc < 3)
135 		usage();
136 
137 	/* Skip program name, get and skip device name and command. */
138 	dvname = argv[1];
139 	cmdname = argv[2];
140 	argv += 3;
141 	argc -= 3;
142 
143 	/*
144 	 * Open the device and determine if it's a scsibus or an actual
145 	 * device.  Devices respond to the SCIOCIDENTIFY ioctl.
146 	 */
147 	fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0);
148 	if (fd == -1) {
149 		if (errno == ENOENT) {
150 			/*
151 			 * Device doesn't exist.  Probably trying to open
152 			 * a device which doesn't use disk semantics for
153 			 * device name.  Try again, specifying "cooked",
154 			 * which leaves off the "r" in front of the device's
155 			 * name.
156 			 */
157 			fd = opendisk(dvname, O_RDWR, dvname_store,
158 			    sizeof(dvname_store), 1);
159 			if (fd == -1)
160 				err(1, "%s", dvname);
161 		} else
162 			err(1, "%s", dvname);
163 	}
164 
165 	/*
166 	 * Point the dvname at the actual device name that opendisk() opened.
167 	 */
168 	dvname = dvname_store;
169 
170 	if (ioctl(fd, SCIOCIDENTIFY, &dvaddr) < 0)
171 		commands = bus_commands;
172 	else
173 		commands = device_commands;
174 
175 	/* Look up and call the command. */
176 	for (i = 0; commands[i].cmd_name != NULL; i++)
177 		if (strcmp(cmdname, commands[i].cmd_name) == 0)
178 			break;
179 	if (commands[i].cmd_name == NULL)
180 		errx(1, "unknown %s command: %s",
181 		    commands == bus_commands ? "bus" : "device", cmdname);
182 
183 	argnames = commands[i].arg_names;
184 
185 	(*commands[i].cmd_func)(argc, argv);
186 	exit(0);
187 }
188 
189 void
190 usage()
191 {
192 	int i;
193 
194 	fprintf(stderr, "Usage: %s device command [arg [...]]\n",
195 	    getprogname());
196 
197 	fprintf(stderr, "   Commands pertaining to scsi devices:\n");
198 	for (i=0; device_commands[i].cmd_name != NULL; i++)
199 		fprintf(stderr, "\t%s %s\n", device_commands[i].cmd_name,
200 					    device_commands[i].arg_names);
201 	fprintf(stderr, "   Commands pertaining to scsi busses:\n");
202 	for (i=0; bus_commands[i].cmd_name != NULL; i++)
203 		fprintf(stderr, "\t%s %s\n", bus_commands[i].cmd_name,
204 					    bus_commands[i].arg_names);
205 	fprintf(stderr, "   Use `any' or `all' to wildcard target or lun\n");
206 
207 	exit(1);
208 }
209 
210 /*
211  * DEVICE COMMANDS
212  */
213 
214 /*
215  * device_format:
216  *
217  *	Format a direct access device.
218  */
219 void
220 device_format(argc, argv)
221 	int argc;
222 	char *argv[];
223 {
224 	u_int32_t blksize;
225 	int i, j, immediate;
226 #define	PC	(65536/10)
227 	static int complete[] = {
228 	    PC*1, PC*2, PC*3, PC*4, PC*5, PC*6, PC*7, PC*8, PC*9, 65536
229 	};
230 	char *cp, buffer[64];
231 	struct scsipi_sense_data sense;
232 	struct scsi_format_unit cmd;
233 	struct {
234 		struct scsi_format_unit_defect_list_header header;
235 		/* optional initialization pattern */
236 		/* optional defect list */
237 	} dfl;
238 	struct {
239 		struct scsipi_mode_header header;
240 		struct scsi_blk_desc blk_desc;
241 		struct page_disk_format format_page;
242 	} mode_page;
243 	struct {
244 		struct scsipi_mode_header header;
245 		struct scsi_blk_desc blk_desc;
246 	} data_select;
247 
248 
249 	/* Blocksize is an optional argument. */
250 	if (argc > 2)
251 		usage();
252 
253 	/*
254 	 * Loop doing Request Sense to clear any pending Unit Attention.
255 	 *
256 	 * Multiple conditions may exist on the drive which are returned
257 	 * in priority order.
258 	 */
259 	for (i = 0; i < 8; i++) {
260 		scsi_request_sense(fd, &sense, sizeof (sense));
261 		if ((j = sense.flags & SSD_KEY) == SKEY_NO_SENSE)
262 			break;
263 	}
264 	/*
265 	 * Make sure we cleared any pending Unit Attention
266 	 */
267 	if (j != SKEY_NO_SENSE) {
268 		cp = scsi_decode_sense((const unsigned char *) &sense, 2,
269 		    buffer, sizeof (buffer));
270 		errx(1, "failed to clean Unit Attention: %s", cp);
271 	}
272 
273 	/*
274 	 * Get the DISK FORMAT mode page.  SCSI-2 recommends specifying the
275 	 * interleave read from this page in the FORMAT UNIT command.
276 	 */
277 	scsi_mode_sense(fd, 0x03, 0x00, &mode_page, sizeof(mode_page));
278 
279 	j = (mode_page.format_page.bytes_s[0] << 8) |
280 	    (mode_page.format_page.bytes_s[1]);
281 
282 	if (j != DEV_BSIZE)
283 		printf("current disk sector size: %hd\n", j);
284 
285 	memset(&cmd, 0, sizeof(cmd));
286 
287 	cmd.opcode = SCSI_FORMAT_UNIT;
288 	memcpy(cmd.interleave, mode_page.format_page.interleave,
289 	    sizeof(cmd.interleave));
290 
291 	/*
292 	 * The blocksize on the device is only changed if the user
293 	 * specified a new blocksize. If not specified the blocksize
294 	 * used for the device will be the Default value in the device.
295 	 * We don't specify the number of blocks since the format
296 	 * command will always reformat the entire drive.  Also by
297 	 * not specifying a block count the drive will reset the
298 	 * block count to the maximum available after the format
299 	 * completes if the blocksize was changed in the format.
300 	 * Finally, the new disk geometry will not but updated on
301 	 * the drive in permanent storage until _AFTER_ the format
302 	 * completes successfully.
303 	 */
304 	if (argc > 0) {
305 		blksize = strtoul(argv[0], &cp, 10);
306 		if (*cp != '\0')
307 			errx(1, "invalid block size: %s", argv[0]);
308 
309 		memset(&data_select, 0, sizeof(data_select));
310 
311 		data_select.header.blk_desc_len = sizeof(struct scsi_blk_desc);
312 		/*
313 		 * blklen in desc is 3 bytes with a leading reserved byte
314 		 */
315 		_lto4b(blksize, &data_select.blk_desc.reserved);
316 
317 		/*
318 		 * Issue Mode Select to modify the device blocksize to be
319 		 * used on the Format.  The modified device geometry will
320 		 * be stored as Current and Saved Page 3 parameters when
321 		 * the Format completes.
322 		 */
323 		scsi_mode_select(fd, 0, &data_select, sizeof(data_select));
324 
325 		/*
326 		 * Since user specified a specific block size make sure it
327 		 * gets stored in the device when the format completes.
328 		 *
329 		 * Also scrub the defect list back to the manufacturers
330 		 * original.
331 		 */
332 		cmd.flags = SFU_CMPLST | SFU_FMTDATA;
333 	}
334 
335 	memset(&dfl, 0, sizeof(dfl));
336 
337 	if (argc > 1 && strncmp(argv[1], "imm", 3) == 0) {
338 		/*
339 		 * Signal target for an immediate return from Format.
340 		 *
341 		 * We'll poll for completion status.
342 		 */
343 		dfl.header.flags = DLH_IMMED;
344 		immediate = 1;
345 	} else {
346 		immediate = 0;
347 	}
348 
349 	scsi_command(fd, &cmd, sizeof(cmd), &dfl, sizeof(dfl),
350 	    8 * 60 * 60 * 1000, 0);
351 
352 	/*
353 	 * Poll device for completion of Format
354 	 */
355 	if (immediate) {
356 		i = 0;
357 		printf("formatting.");
358 		fflush(stdout);
359 		do {
360 			scsireq_t req;
361 			struct scsipi_test_unit_ready tcmd;
362 
363 			memset(&tcmd, 0, sizeof(cmd));
364 			tcmd.opcode = TEST_UNIT_READY;
365 
366 			memset(&req, 0, sizeof(req));
367 			memcpy(req.cmd, &tcmd, 6);
368 			req.cmdlen = 6;
369 			req.timeout = 10000;
370 			req.senselen = SENSEBUFLEN;
371 
372 			if (ioctl(fd, SCIOCCOMMAND, &req) == -1) {
373 				err(1, "SCIOCCOMMAND");
374 			}
375 
376 			if (req.retsts == SCCMD_OK) {
377 				break;
378 			} else if (req.retsts == SCCMD_TIMEOUT) {
379 				fprintf(stderr, "%s: SCSI command timed out",
380 				    dvname);
381 				break;
382 			} else if (req.retsts == SCCMD_BUSY) {
383 				fprintf(stderr, "%s: device is busy",
384 				    dvname);
385 				break;
386 			} else if (req.retsts != SCCMD_SENSE) {
387 				fprintf(stderr,
388 				    "%s: device had unknown status %x", dvname,
389 				    req.retsts);
390 				break;
391 			}
392 			memcpy(&sense, req.sense, SENSEBUFLEN);
393 			if (sense.sense_key_spec_1 == SSD_SCS_VALID) {
394 				j = (sense.sense_key_spec_2 << 8) |
395 				    (sense.sense_key_spec_3);
396 				if (j >= complete[i]) {
397 					printf(".%d0%%.", ++i);
398 					fflush(stdout);
399 				}
400 			}
401 			sleep(10);
402 		} while ((sense.flags & SSD_KEY) == SKEY_NOT_READY);
403 		printf(".100%%..done.\n");
404 	}
405 	return;
406 }
407 
408 /*
409  * device_identify:
410  *
411  *	Display the identity of the device, including it's SCSI bus,
412  *	target, lun, and it's vendor/product/revision information.
413  */
414 void
415 device_identify(argc, argv)
416 	int argc;
417 	char *argv[];
418 {
419 	struct scsipi_inquiry_data inqbuf;
420 	struct scsipi_inquiry cmd;
421 
422 	/* x4 in case every character is escaped, +1 for NUL. */
423 	char vendor[(sizeof(inqbuf.vendor) * 4) + 1],
424 	     product[(sizeof(inqbuf.product) * 4) + 1],
425 	     revision[(sizeof(inqbuf.revision) * 4) + 1];
426 
427 	/* No arguments. */
428 	if (argc != 0)
429 		usage();
430 
431 	memset(&cmd, 0, sizeof(cmd));
432 	memset(&inqbuf, 0, sizeof(inqbuf));
433 
434 	cmd.opcode = INQUIRY;
435 	cmd.length = sizeof(inqbuf);
436 
437 	scsi_command(fd, &cmd, sizeof(cmd), &inqbuf, sizeof(inqbuf),
438 	    10000, SCCMD_READ);
439 
440 	scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor,
441 	    sizeof(inqbuf.vendor));
442 	scsi_strvis(product, sizeof(product), inqbuf.product,
443 	    sizeof(inqbuf.product));
444 	scsi_strvis(revision, sizeof(revision), inqbuf.revision,
445 	    sizeof(inqbuf.revision));
446 
447 	printf("%s: scsibus%d target %d lun %d <%s, %s, %s>\n",
448 	    dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target,
449 	    dvaddr.addr.scsi.lun, vendor, product, revision);
450 
451 	return;
452 }
453 
454 /*
455  * device_reassign:
456  *
457  *	Reassign bad blocks on a direct access device.
458  */
459 void
460 device_reassign(argc, argv)
461 	int argc;
462 	char *argv[];
463 {
464 	struct scsi_reassign_blocks cmd;
465 	struct scsi_reassign_blocks_data *data;
466 	size_t dlen;
467 	u_int32_t blkno;
468 	int i;
469 	char *cp;
470 
471 	/* We get a list of block numbers. */
472 	if (argc < 1)
473 		usage();
474 
475 	/*
476 	 * Allocate the reassign blocks descriptor.  The 4 comes from the
477 	 * size of the block address in the defect descriptor.
478 	 */
479 	dlen = sizeof(struct scsi_reassign_blocks_data) + ((argc - 1) * 4);
480 	data = malloc(dlen);
481 	if (data == NULL)
482 		errx(1, "unable to allocate defect descriptor");
483 	memset(data, 0, dlen);
484 
485 	cmd.opcode = SCSI_REASSIGN_BLOCKS;
486 	cmd.byte2 = 0;
487 	cmd.unused[0] = 0;
488 	cmd.unused[1] = 0;
489 	cmd.unused[2] = 0;
490 	cmd.control = 0;
491 
492 	/* Defect descriptor length. */
493 	_lto2b(argc * 4, data->length);
494 
495 	/* Build the defect descriptor list. */
496 	for (i = 0; i < argc; i++) {
497 		blkno = strtoul(argv[i], &cp, 10);
498 		if (*cp != '\0')
499 			errx(1, "invalid block number: %s", argv[i]);
500 		_lto4b(blkno, data->defect_descriptor[i].dlbaddr);
501 	}
502 
503 	scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_WRITE);
504 
505 	free(data);
506 	return;
507 }
508 
509 /*
510  * device_release:
511  *
512  *      Issue a RELEASE command to a SCSI drevice
513  */
514 #ifndef	SCSI_RELEASE
515 #define	SCSI_RELEASE	0x17
516 #endif
517 void
518 device_release(argc, argv)
519 	int argc;
520 	char *argv[];
521 {
522 	struct scsipi_test_unit_ready cmd;	/* close enough */
523 
524 	/* No arguments. */
525 	if (argc != 0)
526 		usage();
527 
528 	memset(&cmd, 0, sizeof(cmd));
529 
530 	cmd.opcode = SCSI_RELEASE;
531 
532 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
533 
534 	return;
535 }
536 
537 
538 
539 /*
540  * device_reserve:
541  *
542  *      Issue a RESERVE command to a SCSI drevice
543  */
544 #ifndef	SCSI_RESERVE
545 #define	SCSI_RESERVE	0x16
546 #endif
547 void
548 device_reserve(argc, argv)
549 	int argc;
550 	char *argv[];
551 {
552 	struct scsipi_test_unit_ready cmd;	/* close enough */
553 
554 	/* No arguments. */
555 	if (argc != 0)
556 		usage();
557 
558 	memset(&cmd, 0, sizeof(cmd));
559 
560 	cmd.opcode = SCSI_RESERVE;
561 
562 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
563 
564 	return;
565 }
566 
567 /*
568  * device_reset:
569  *
570  *	Issue a reset to a SCSI device.
571  */
572 void
573 device_reset(argc, argv)
574 	int argc;
575 	char *argv[];
576 {
577 
578 	/* No arguments. */
579 	if (argc != 0)
580 		usage();
581 
582 	if (ioctl(fd, SCIOCRESET, NULL) != 0)
583 		err(1, "SCIOCRESET");
584 
585 	return;
586 }
587 
588 /*
589  * device_debug:
590  *
591  *	Set debug level to a SCSI device.
592  *	scsipi will print anything iff SCSIPI_DEBUG set in config.
593  */
594 void
595 device_debug(argc, argv)
596 	int argc;
597 	char *argv[];
598 {
599 	int lvl;
600 
601 	if (argc < 1)
602 		usage();
603 
604 	lvl = atoi(argv[0]);
605 
606 	if (ioctl(fd, SCIOCDEBUG, &lvl) != 0)
607 		err(1, "SCIOCDEBUG");
608 
609 	return;
610 }
611 
612 /*
613  * device_getcache:
614  *
615  *	Get the caching parameters for a SCSI disk.
616  */
617 void
618 device_getcache(argc, argv)
619 	int argc;
620 	char *argv[];
621 {
622 	struct {
623 		struct scsipi_mode_header header;
624 		struct scsi_blk_desc blk_desc;
625 		struct page_caching caching_params;
626 	} data;
627 
628 	/* No arguments. */
629 	if (argc != 0)
630 		usage();
631 
632 	scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data));
633 
634 	if ((data.caching_params.flags & (CACHING_RCD|CACHING_WCE)) ==
635 	    CACHING_RCD)
636 		printf("%s: no caches enabled\n", dvname);
637 	else {
638 		printf("%s: read cache %senabled\n", dvname,
639 		    (data.caching_params.flags & CACHING_RCD) ? "not " : "");
640 		printf("%s: write-back cache %senabled\n", dvname,
641 		    (data.caching_params.flags & CACHING_WCE) ? "" : "not ");
642 	}
643 	printf("%s: caching parameters are %ssavable\n", dvname,
644 	    (data.caching_params.pg_code & PGCODE_PS) ? "" : "not ");
645 }
646 
647 /*
648  * device_setcache:
649  *
650  *	Set cache enables for a SCSI disk.
651  */
652 void
653 device_setcache(argc, argv)
654 	int argc;
655 	char *argv[];
656 {
657 	struct {
658 		struct scsipi_mode_header header;
659 		struct scsi_blk_desc blk_desc;
660 		struct page_caching caching_params;
661 	} data;
662 	int dlen;
663 	u_int8_t flags, byte2;
664 
665 	if (argc > 2 || argc == 0)
666 		usage();
667 
668 	if (strcmp(argv[0], "none") == 0)
669 		flags = CACHING_RCD;
670 	else if (strcmp(argv[0], "r") == 0)
671 		flags = 0;
672 	else if (strcmp(argv[0], "w") == 0)
673 		flags = CACHING_RCD|CACHING_WCE;
674 	else if (strcmp(argv[0], "rw") == 0)
675 		flags = CACHING_WCE;
676 	else
677 		usage();
678 
679 	if (argc == 2) {
680 		if (strcmp(argv[1], "save") == 0)
681 			byte2 = SMS_SP;
682 		else
683 			usage();
684 	}
685 
686 	scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data));
687 
688 	data.caching_params.pg_code &= PGCODE_MASK;
689 	data.caching_params.flags =
690 	    (data.caching_params.flags & ~(CACHING_RCD|CACHING_WCE)) | flags;
691 
692 	data.caching_params.cache_segment_size[0] = 0;
693 	data.caching_params.cache_segment_size[1] = 0;
694 
695 	data.header.data_length = 0;
696 
697 	dlen = sizeof(data.header) + sizeof(data.blk_desc) + 2 +
698 	    data.caching_params.pg_length;
699 
700 	scsi_mode_select(fd, byte2, &data, dlen);
701 }
702 
703 /*
704  * device_start:
705  *
706  *      Issue a start to a SCSI device.
707  */
708 void
709 device_start(argc, argv)
710 	int argc;
711 	char *argv[];
712 {
713 	struct scsipi_start_stop cmd;
714 
715 	/* No arguments. */
716 	if (argc != 0)
717 		usage();
718 
719 	memset(&cmd, 0, sizeof(cmd));
720 
721 	cmd.opcode = START_STOP;
722 	cmd.how = SSS_START;
723 
724 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
725 
726 	return;
727 }
728 
729 /*
730  * device_stop:
731  *
732  *      Issue a stop to a SCSI device.
733  */
734 void
735 device_stop(argc, argv)
736 	int argc;
737 	char *argv[];
738 {
739 	struct scsipi_start_stop cmd;
740 
741 	/* No arguments. */
742 	if (argc != 0)
743 		usage();
744 
745 	memset(&cmd, 0, sizeof(cmd));
746 
747 	cmd.opcode = START_STOP;
748 	cmd.how = SSS_STOP;
749 
750 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
751 
752 	return;
753 }
754 
755 /*
756  * device_tur:
757  *
758  *      Issue a TEST UNIT READY to a SCSI drevice
759  */
760 void
761 device_tur(argc, argv)
762 	int argc;
763 	char *argv[];
764 {
765 	struct scsipi_test_unit_ready cmd;
766 
767 	/* No arguments. */
768 	if (argc != 0)
769 		usage();
770 
771 	memset(&cmd, 0, sizeof(cmd));
772 
773 	cmd.opcode = TEST_UNIT_READY;
774 
775 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
776 
777 	return;
778 }
779 
780 /*
781  * BUS COMMANDS
782  */
783 
784 /*
785  * bus_reset:
786  *
787  *	Issue a reset to a SCSI bus.
788  */
789 void
790 bus_reset(argc, argv)
791 	int argc;
792 	char *argv[];
793 {
794 
795 	/* No arguments. */
796 	if (argc != 0)
797 		usage();
798 
799 	if (ioctl(fd, SCBUSIORESET, NULL) != 0)
800 		err(1, "SCBUSIORESET");
801 
802 	return;
803 }
804 
805 /*
806  * bus_scan:
807  *
808  *	Rescan a SCSI bus for new devices.
809  */
810 void
811 bus_scan(argc, argv)
812 	int argc;
813 	char *argv[];
814 {
815 	struct scbusioscan_args args;
816 	char *cp;
817 
818 	/* Must have two args: target lun */
819 	if (argc != 2)
820 		usage();
821 
822 	if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0)
823 		args.sa_target = -1;
824 	else {
825 		args.sa_target = strtol(argv[0], &cp, 10);
826 		if (*cp != '\0' || args.sa_target < 0)
827 			errx(1, "invalid target: %s", argv[0]);
828 	}
829 
830 	if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0)
831 		args.sa_lun = -1;
832 	else {
833 		args.sa_lun = strtol(argv[1], &cp, 10);
834 		if (*cp != '\0' || args.sa_lun < 0)
835 			errx(1, "invalid lun: %s", argv[1]);
836 	}
837 
838 	if (ioctl(fd, SCBUSIOSCAN, &args) != 0)
839 		err(1, "SCBUSIOSCAN");
840 
841 	return;
842 }
843 
844 /*
845  * bus_detach:
846  *
847  *	detach SCSI devices from a bus.
848  */
849 void
850 bus_detach(argc, argv)
851 	int argc;
852 	char *argv[];
853 {
854 	struct scbusiodetach_args args;
855 	char *cp;
856 
857 	/* Must have two args: target lun */
858 	if (argc != 2)
859 		usage();
860 
861 	if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0)
862 		args.sa_target = -1;
863 	else {
864 		args.sa_target = strtol(argv[0], &cp, 10);
865 		if (*cp != '\0' || args.sa_target < 0)
866 			errx(1, "invalid target: %s", argv[0]);
867 	}
868 
869 	if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0)
870 		args.sa_lun = -1;
871 	else {
872 		args.sa_lun = strtol(argv[1], &cp, 10);
873 		if (*cp != '\0' || args.sa_lun < 0)
874 			errx(1, "invalid lun: %s", argv[1]);
875 	}
876 
877 	if (ioctl(fd, SCBUSIODETACH, &args) != 0)
878 		err(1, "SCBUSIODETACH");
879 
880 	return;
881 }
882