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