scsictl.c revision 1.20
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
69struct command {
70	const char *cmd_name;
71	const char *arg_names;
72	void (*cmd_func) __P((int, char *[]));
73};
74
75int	main __P((int, char *[]));
76void	usage __P((void));
77
78int	fd;				/* file descriptor for device */
79const	char *dvname;			/* device name */
80char	dvname_store[MAXPATHLEN];	/* for opendisk(3) */
81const	char *cmdname;			/* command user issued */
82const	char *argnames;			/* helpstring: expected arguments */
83struct	scsi_addr dvaddr;		/* SCSI device's address */
84
85void	device_format __P((int, char *[]));
86void	device_identify __P((int, char *[]));
87void	device_reassign __P((int, char *[]));
88void	device_release __P((int, char *[]));
89void	device_reserve __P((int, char *[]));
90void	device_reset __P((int, char *[]));
91void	device_debug __P((int, char *[]));
92void	device_start __P((int, char *[]));
93void	device_stop __P((int, char *[]));
94void	device_tur __P((int, char *[]));
95void	device_getcache __P((int, char *[]));
96void	device_setcache __P((int, char *[]));
97
98struct 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
114void	bus_reset __P((int, char *[]));
115void	bus_scan __P((int, char *[]));
116void	bus_detach __P((int, char *[]));
117
118struct 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
125int
126main(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
189void
190usage()
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 */
219void
220device_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 */
414void
415device_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 */
459void
460device_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
517void
518device_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
547void
548device_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 */
572void
573device_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 */
594void
595device_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 */
617void
618device_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 */
652void
653device_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 */
708void
709device_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 */
734void
735device_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 */
760void
761device_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 */
789void
790bus_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 */
810void
811bus_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 */
849void
850bus_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