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