scsictl.c revision 1.28
1/*	$NetBSD: scsictl.c,v 1.28 2005/06/02 00:13:10 lukem 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.28 2005/06/02 00:13:10 lukem 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 <limits.h>
57#include <stdio.h>
58#include <stdlib.h>
59#include <string.h>
60#include <unistd.h>
61#include <util.h>
62
63#include <dev/scsipi/scsi_spc.h>
64#include <dev/scsipi/scsipi_all.h>
65#include <dev/scsipi/scsi_disk.h>
66#include <dev/scsipi/scsipiconf.h>
67
68#include "extern.h"
69
70struct command {
71	const char *cmd_name;
72	const char *arg_names;
73	void (*cmd_func)(int, char *[]);
74};
75
76void	usage(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_defects(int, char *[]);
86void	device_format(int, char *[]);
87void	device_identify(int, char *[]);
88void	device_reassign(int, char *[]);
89void	device_release(int, char *[]);
90void	device_reserve(int, char *[]);
91void	device_reset(int, char *[]);
92void	device_debug(int, char *[]);
93void	device_prevent(int, char *[]);
94void	device_allow(int, char *[]);
95void	device_start(int, char *[]);
96void	device_stop(int, char *[]);
97void	device_tur(int, char *[]);
98void	device_getcache(int, char *[]);
99void	device_setcache(int, char *[]);
100void	device_flushcache(int, char *[]);
101
102struct command device_commands[] = {
103	{ "defects",	"[primary] [grown] [block|byte|physical]",
104						device_defects },
105	{ "format",	"[blocksize [immediate]]", 	device_format },
106	{ "identify",	"",			device_identify },
107	{ "reassign",	"blkno [blkno [...]]",	device_reassign },
108	{ "release",	"",			device_release },
109	{ "reserve",	"",			device_reserve },
110	{ "reset",	"",			device_reset },
111	{ "debug",	"level",		device_debug },
112	{ "prevent",	"",			device_prevent },
113	{ "allow",	"",			device_allow },
114	{ "start",	"",			device_start },
115	{ "stop",	"",			device_stop },
116	{ "tur",	"",			device_tur },
117	{ "getcache",	"",			device_getcache },
118	{ "setcache",	"none|r|w|rw [save]",	device_setcache },
119	{ "flushcache",	"",			device_flushcache },
120	{ NULL,		NULL,			NULL },
121};
122
123void	bus_reset(int, char *[]);
124void	bus_scan(int, char *[]);
125void	bus_detach(int, char *[]);
126
127struct command bus_commands[] = {
128	{ "reset",	"",			bus_reset },
129	{ "scan",	"target lun",		bus_scan },
130	{ "detach",	"target lun",		bus_detach },
131	{ NULL,		NULL,				NULL },
132};
133
134int
135main(int argc, char *argv[])
136{
137	struct command *commands;
138	int i;
139
140	/* Must have at least: device command */
141	if (argc < 3)
142		usage();
143
144	/* Skip program name, get and skip device name and command. */
145	dvname = argv[1];
146	cmdname = argv[2];
147	argv += 3;
148	argc -= 3;
149
150	/*
151	 * Open the device and determine if it's a scsibus or an actual
152	 * device.  Devices respond to the SCIOCIDENTIFY ioctl.
153	 */
154	fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0);
155	if (fd == -1) {
156		if (errno == ENOENT) {
157			/*
158			 * Device doesn't exist.  Probably trying to open
159			 * a device which doesn't use disk semantics for
160			 * device name.  Try again, specifying "cooked",
161			 * which leaves off the "r" in front of the device's
162			 * name.
163			 */
164			fd = opendisk(dvname, O_RDWR, dvname_store,
165			    sizeof(dvname_store), 1);
166			if (fd == -1)
167				err(1, "%s", dvname);
168		} else
169			err(1, "%s", dvname);
170	}
171
172	/*
173	 * Point the dvname at the actual device name that opendisk() opened.
174	 */
175	dvname = dvname_store;
176
177	if (ioctl(fd, SCIOCIDENTIFY, &dvaddr) < 0)
178		commands = bus_commands;
179	else
180		commands = device_commands;
181
182	/* Look up and call the command. */
183	for (i = 0; commands[i].cmd_name != NULL; i++)
184		if (strcmp(cmdname, commands[i].cmd_name) == 0)
185			break;
186	if (commands[i].cmd_name == NULL)
187		errx(1, "unknown %s command: %s",
188		    commands == bus_commands ? "bus" : "device", cmdname);
189
190	argnames = commands[i].arg_names;
191
192	(*commands[i].cmd_func)(argc, argv);
193	exit(0);
194}
195
196void
197usage(void)
198{
199	int i;
200
201	fprintf(stderr, "usage: %s device command [arg [...]]\n",
202	    getprogname());
203
204	fprintf(stderr, "   Commands pertaining to scsi devices:\n");
205	for (i=0; device_commands[i].cmd_name != NULL; i++)
206		fprintf(stderr, "\t%s %s\n", device_commands[i].cmd_name,
207					    device_commands[i].arg_names);
208	fprintf(stderr, "   Commands pertaining to scsi busses:\n");
209	for (i=0; bus_commands[i].cmd_name != NULL; i++)
210		fprintf(stderr, "\t%s %s\n", bus_commands[i].cmd_name,
211					    bus_commands[i].arg_names);
212	fprintf(stderr, "   Use `any' or `all' to wildcard target or lun\n");
213
214	exit(1);
215}
216
217/*
218 * DEVICE COMMANDS
219 */
220
221/*
222 * device_read_defect:
223 *
224 *	Read primary and/or growth defect list in physical or block
225 *	format from a direct access device.
226 *
227 *	XXX Does not handle very large defect lists. Needs SCSI3 12
228 *	    byte READ DEFECT DATA command.
229 */
230
231void	print_bf_dd(union scsi_defect_descriptor *);
232void	print_bfif_dd(union scsi_defect_descriptor *);
233void	print_psf_dd(union scsi_defect_descriptor *);
234
235void
236device_defects(int argc, char *argv[])
237{
238	struct scsi_read_defect_data cmd;
239	struct scsi_read_defect_data_data *data;
240	size_t dlen;
241	int i, dlfmt = -1;
242	int defects;
243	char msg[256];
244	void (*pfunc)(union scsi_defect_descriptor *);
245#define RDD_P_G_MASK	0x18
246#define RDD_DLF_MASK	0x7
247
248	dlen = USHRT_MAX; 		/* XXX - this may not be enough room
249					 * for all of the defects.
250					 */
251	data = malloc(dlen);
252	if (data == NULL)
253		errx(1, "unable to allocate defect list");
254	memset(data, 0, dlen);
255	memset(&cmd, 0, sizeof(cmd));
256	defects = 0;
257	pfunc = NULL;
258
259	/* determine which defect list(s) to read. */
260	for (i = 0; i < argc; i++) {
261		if (strncmp("primary", argv[i], 7) == 0) {
262			cmd.flags |= RDD_PRIMARY;
263			continue;
264		}
265		if (strncmp("grown", argv[i], 5) == 0) {
266			cmd.flags |= RDD_GROWN;
267			continue;
268		}
269		break;
270	}
271
272	/* no defect list sepecified, assume both. */
273	if ((cmd.flags & (RDD_PRIMARY|RDD_GROWN)) == 0)
274		cmd.flags |= (RDD_PRIMARY|RDD_GROWN);
275
276	/* list format option. */
277	if (i < argc) {
278		if (strncmp("block", argv[i], 5) == 0) {
279			cmd.flags |= RDD_BF;
280			dlfmt = RDD_BF;
281		}
282		else if (strncmp("byte", argv[i], 4) == 0) {
283			cmd.flags |= RDD_BFIF;
284			dlfmt = RDD_BFIF;
285		}
286		else if (strncmp("physical", argv[i], 4) == 0) {
287			cmd.flags |= RDD_PSF;
288			dlfmt = RDD_PSF;
289		}
290		else {
291			usage();
292		}
293	}
294
295	/*
296	 * no list format specified; since block format not
297	 * recommended use physical sector format as default.
298	 */
299	if (dlfmt < 0) {
300		cmd.flags |= RDD_PSF;
301		dlfmt = RDD_PSF;
302	}
303
304	cmd.opcode = SCSI_READ_DEFECT_DATA;
305	_lto2b(dlen, &cmd.length[0]);
306
307	scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_READ);
308
309	msg[0] = '\0';
310
311	/* is the defect list in the format asked for? */
312	if ((data->flags & RDD_DLF_MASK) != dlfmt) {
313		strcpy(msg, "\n\tnotice:"
314		       "requested defect list format not supported by device\n\n");
315		dlfmt = (data->flags & RDD_DLF_MASK);
316	}
317
318	if (data->flags & RDD_PRIMARY)
319		strcat(msg, "primary");
320
321	if (data->flags & RDD_GROWN) {
322		if (data->flags & RDD_PRIMARY)
323			strcat(msg, " and ");
324		strcat(msg, "grown");
325	}
326
327	strcat(msg, " defects");
328
329	if ((data->flags & RDD_P_G_MASK) == 0)
330		strcat(msg, ": none reported\n");
331
332
333	printf("%s: scsibus%d target %d lun %d %s",
334	       dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target,
335	       dvaddr.addr.scsi.lun, msg);
336
337	/* device did not return either defect list. */
338	if ((data->flags & RDD_P_G_MASK) == 0)
339		return;
340
341	switch (dlfmt) {
342	case RDD_BF:
343		defects = _2btol(data->length) /
344				sizeof(struct scsi_defect_descriptor_bf);
345		pfunc = print_bf_dd;
346		strcpy(msg, "block address\n"
347			    "-------------\n");
348		break;
349	case RDD_BFIF:
350		defects = _2btol(data->length) /
351				sizeof(struct scsi_defect_descriptor_bfif);
352		pfunc = print_bfif_dd;
353		strcpy(msg, "              bytes from\n"
354			    "cylinder head   index\n"
355			    "-------- ---- ----------\n");
356		break;
357	case RDD_PSF:
358		defects = _2btol(data->length) /
359				sizeof(struct scsi_defect_descriptor_psf);
360		pfunc = print_psf_dd;
361		strcpy(msg, "cylinder head   sector\n"
362			    "-------- ---- ----------\n");
363		break;
364	}
365
366	/* device did not return any defects. */
367	if (defects == 0) {
368		printf(": none\n");
369		return;
370	}
371
372	printf(": %d\n", defects);
373
374	/* print heading. */
375	printf("%s", msg);
376
377	/* print defect list. */
378	for (i = 0 ; i < defects; i++) {
379		pfunc(&data->defect_descriptor[i]);
380	}
381
382	free(data);
383	return;
384}
385
386/*
387 * print_bf_dd:
388 *
389 *	Print a block format defect descriptor.
390 */
391void
392print_bf_dd(union scsi_defect_descriptor *dd)
393{
394	u_int32_t block;
395
396	block = _4btol(dd->bf.block_address);
397
398	printf("%13u\n", block);
399}
400
401#define DEFECTIVE_TRACK	0xffffffff
402
403/*
404 * print_bfif_dd:
405 *
406 *	Print a bytes from index format defect descriptor.
407 */
408void
409print_bfif_dd(union scsi_defect_descriptor *dd)
410{
411	u_int32_t cylinder;
412	u_int32_t head;
413	u_int32_t bytes_from_index;
414
415	cylinder = _3btol(dd->bfif.cylinder);
416	head = dd->bfif.head;
417	bytes_from_index = _4btol(dd->bfif.bytes_from_index);
418
419	printf("%8u %4u ", cylinder, head);
420
421	if (bytes_from_index == DEFECTIVE_TRACK)
422		printf("entire track defective\n");
423	else
424		printf("%10u\n", bytes_from_index);
425}
426
427/*
428 * print_psf_dd:
429 *
430 *	Print a physical sector format defect descriptor.
431 */
432void
433print_psf_dd(union scsi_defect_descriptor *dd)
434{
435	u_int32_t cylinder;
436	u_int32_t head;
437	u_int32_t sector;
438
439	cylinder = _3btol(dd->psf.cylinder);
440	head = dd->psf.head;
441	sector = _4btol(dd->psf.sector);
442
443	printf("%8u %4u ", cylinder, head);
444
445	if (sector == DEFECTIVE_TRACK)
446		printf("entire track defective\n");
447	else
448		printf("%10u\n", sector);
449}
450
451/*
452 * device_format:
453 *
454 *	Format a direct access device.
455 */
456void
457device_format(int argc, char *argv[])
458{
459	u_int32_t blksize;
460	int i, j, immediate;
461#define	PC	(65536/10)
462	static int complete[] = {
463	    PC*1, PC*2, PC*3, PC*4, PC*5, PC*6, PC*7, PC*8, PC*9, 65536
464	};
465	char *cp, buffer[64];
466	struct scsi_sense_data sense;
467	struct scsi_format_unit cmd;
468	struct {
469		struct scsi_format_unit_defect_list_header header;
470		/* optional initialization pattern */
471		/* optional defect list */
472	} dfl;
473	struct {
474		struct scsi_mode_parameter_header_6 header;
475		struct scsi_general_block_descriptor blk_desc;
476		struct page_disk_format format_page;
477	} mode_page;
478	struct {
479		struct scsi_mode_parameter_header_6 header;
480		struct scsi_general_block_descriptor blk_desc;
481	} data_select;
482
483
484	/* Blocksize is an optional argument. */
485	if (argc > 2)
486		usage();
487
488	/*
489	 * Loop doing Request Sense to clear any pending Unit Attention.
490	 *
491	 * Multiple conditions may exist on the drive which are returned
492	 * in priority order.
493	 */
494	for (i = 0; i < 8; i++) {
495		scsi_request_sense(fd, &sense, sizeof (sense));
496		if ((j = SSD_SENSE_KEY(sense.flags)) == SKEY_NO_SENSE)
497			break;
498	}
499	/*
500	 * Make sure we cleared any pending Unit Attention
501	 */
502	if (j != SKEY_NO_SENSE) {
503		cp = scsi_decode_sense((const unsigned char *) &sense, 2,
504		    buffer, sizeof (buffer));
505		errx(1, "failed to clean Unit Attention: %s", cp);
506	}
507
508	/*
509	 * Get the DISK FORMAT mode page.  SCSI-2 recommends specifying the
510	 * interleave read from this page in the FORMAT UNIT command.
511	 */
512	scsi_mode_sense(fd, 0x03, 0x00, &mode_page, sizeof(mode_page));
513
514	j = (mode_page.format_page.bytes_s[0] << 8) |
515	    (mode_page.format_page.bytes_s[1]);
516
517	if (j != DEV_BSIZE)
518		printf("current disk sector size: %hd\n", j);
519
520	memset(&cmd, 0, sizeof(cmd));
521
522	cmd.opcode = SCSI_FORMAT_UNIT;
523	memcpy(cmd.interleave, mode_page.format_page.interleave,
524	    sizeof(cmd.interleave));
525
526	/*
527	 * The blocksize on the device is only changed if the user
528	 * specified a new blocksize. If not specified the blocksize
529	 * used for the device will be the Default value in the device.
530	 * We don't specify the number of blocks since the format
531	 * command will always reformat the entire drive.  Also by
532	 * not specifying a block count the drive will reset the
533	 * block count to the maximum available after the format
534	 * completes if the blocksize was changed in the format.
535	 * Finally, the new disk geometry will not but updated on
536	 * the drive in permanent storage until _AFTER_ the format
537	 * completes successfully.
538	 */
539	if (argc > 0) {
540		blksize = strtoul(argv[0], &cp, 10);
541		if (*cp != '\0')
542			errx(1, "invalid block size: %s", argv[0]);
543
544		memset(&data_select, 0, sizeof(data_select));
545
546		data_select.header.blk_desc_len =
547		    sizeof(struct scsi_general_block_descriptor);
548		/*
549		 * blklen in desc is 3 bytes with a leading reserved byte
550		 */
551		_lto4b(blksize, &data_select.blk_desc.reserved);
552
553		/*
554		 * Issue Mode Select to modify the device blocksize to be
555		 * used on the Format.  The modified device geometry will
556		 * be stored as Current and Saved Page 3 parameters when
557		 * the Format completes.
558		 */
559		scsi_mode_select(fd, 0, &data_select, sizeof(data_select));
560
561		/*
562		 * Since user specified a specific block size make sure it
563		 * gets stored in the device when the format completes.
564		 *
565		 * Also scrub the defect list back to the manufacturers
566		 * original.
567		 */
568		cmd.flags = SFU_CMPLST | SFU_FMTDATA;
569	}
570
571	memset(&dfl, 0, sizeof(dfl));
572
573	if (argc > 1 && strncmp(argv[1], "imm", 3) == 0) {
574		/*
575		 * Signal target for an immediate return from Format.
576		 *
577		 * We'll poll for completion status.
578		 */
579		dfl.header.flags = DLH_IMMED;
580		immediate = 1;
581	} else {
582		immediate = 0;
583	}
584
585	scsi_command(fd, &cmd, sizeof(cmd), &dfl, sizeof(dfl),
586	    8 * 60 * 60 * 1000, 0);
587
588	/*
589	 * Poll device for completion of Format
590	 */
591	if (immediate) {
592		i = 0;
593		printf("formatting.");
594		fflush(stdout);
595		do {
596			scsireq_t req;
597			struct scsi_test_unit_ready tcmd;
598
599			memset(&tcmd, 0, sizeof(cmd));
600			tcmd.opcode = SCSI_TEST_UNIT_READY;
601
602			memset(&req, 0, sizeof(req));
603			memcpy(req.cmd, &tcmd, 6);
604			req.cmdlen = 6;
605			req.timeout = 10000;
606			req.senselen = SENSEBUFLEN;
607
608			if (ioctl(fd, SCIOCCOMMAND, &req) == -1) {
609				err(1, "SCIOCCOMMAND");
610			}
611
612			if (req.retsts == SCCMD_OK) {
613				break;
614			} else if (req.retsts == SCCMD_TIMEOUT) {
615				fprintf(stderr, "%s: SCSI command timed out",
616				    dvname);
617				break;
618			} else if (req.retsts == SCCMD_BUSY) {
619				fprintf(stderr, "%s: device is busy",
620				    dvname);
621				break;
622			} else if (req.retsts != SCCMD_SENSE) {
623				fprintf(stderr,
624				    "%s: device had unknown status %x", dvname,
625				    req.retsts);
626				break;
627			}
628			memcpy(&sense, req.sense, SENSEBUFLEN);
629			if (sense.sks.sks_bytes[0] & SSD_SKSV) {
630				j = (sense.sks.sks_bytes[1] << 8) |
631				    (sense.sks.sks_bytes[2]);
632				if (j >= complete[i]) {
633					printf(".%d0%%.", ++i);
634					fflush(stdout);
635				}
636			}
637			sleep(10);
638		} while (SSD_SENSE_KEY(sense.flags) == SKEY_NOT_READY);
639		printf(".100%%..done.\n");
640	}
641	return;
642}
643
644/*
645 * device_identify:
646 *
647 *	Display the identity of the device, including it's SCSI bus,
648 *	target, lun, and it's vendor/product/revision information.
649 */
650void
651device_identify(int argc, char *argv[])
652{
653	struct scsipi_inquiry_data inqbuf;
654	struct scsipi_inquiry cmd;
655
656	/* x4 in case every character is escaped, +1 for NUL. */
657	char vendor[(sizeof(inqbuf.vendor) * 4) + 1],
658	     product[(sizeof(inqbuf.product) * 4) + 1],
659	     revision[(sizeof(inqbuf.revision) * 4) + 1];
660
661	/* No arguments. */
662	if (argc != 0)
663		usage();
664
665	memset(&cmd, 0, sizeof(cmd));
666	memset(&inqbuf, 0, sizeof(inqbuf));
667
668	cmd.opcode = INQUIRY;
669	cmd.length = sizeof(inqbuf);
670
671	scsi_command(fd, &cmd, sizeof(cmd), &inqbuf, sizeof(inqbuf),
672	    10000, SCCMD_READ);
673
674	scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor,
675	    sizeof(inqbuf.vendor));
676	scsi_strvis(product, sizeof(product), inqbuf.product,
677	    sizeof(inqbuf.product));
678	scsi_strvis(revision, sizeof(revision), inqbuf.revision,
679	    sizeof(inqbuf.revision));
680
681	printf("%s: scsibus%d target %d lun %d <%s, %s, %s>\n",
682	    dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target,
683	    dvaddr.addr.scsi.lun, vendor, product, revision);
684
685	return;
686}
687
688/*
689 * device_reassign:
690 *
691 *	Reassign bad blocks on a direct access device.
692 */
693void
694device_reassign(int argc, char *argv[])
695{
696	struct scsi_reassign_blocks cmd;
697	struct scsi_reassign_blocks_data *data;
698	size_t dlen;
699	u_int32_t blkno;
700	int i;
701	char *cp;
702
703	/* We get a list of block numbers. */
704	if (argc < 1)
705		usage();
706
707	/*
708	 * Allocate the reassign blocks descriptor.  The 4 comes from the
709	 * size of the block address in the defect descriptor.
710	 */
711	dlen = sizeof(struct scsi_reassign_blocks_data) + ((argc - 1) * 4);
712	data = malloc(dlen);
713	if (data == NULL)
714		errx(1, "unable to allocate defect descriptor");
715	memset(data, 0, dlen);
716
717	cmd.opcode = SCSI_REASSIGN_BLOCKS;
718	cmd.byte2 = 0;
719	cmd.unused[0] = 0;
720	cmd.unused[1] = 0;
721	cmd.unused[2] = 0;
722	cmd.control = 0;
723
724	/* Defect descriptor length. */
725	_lto2b(argc * 4, data->length);
726
727	/* Build the defect descriptor list. */
728	for (i = 0; i < argc; i++) {
729		blkno = strtoul(argv[i], &cp, 10);
730		if (*cp != '\0')
731			errx(1, "invalid block number: %s", argv[i]);
732		_lto4b(blkno, data->defect_descriptor[i].dlbaddr);
733	}
734
735	scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_WRITE);
736
737	free(data);
738	return;
739}
740
741/*
742 * device_release:
743 *
744 *      Issue a RELEASE command to a SCSI drevice
745 */
746#ifndef	SCSI_RELEASE
747#define	SCSI_RELEASE	0x17
748#endif
749void
750device_release(int argc, char *argv[])
751{
752	struct scsi_test_unit_ready cmd;	/* close enough */
753
754	/* No arguments. */
755	if (argc != 0)
756		usage();
757
758	memset(&cmd, 0, sizeof(cmd));
759
760	cmd.opcode = SCSI_RELEASE;
761
762	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
763
764	return;
765}
766
767
768
769/*
770 * device_reserve:
771 *
772 *      Issue a RESERVE command to a SCSI drevice
773 */
774#ifndef	SCSI_RESERVE
775#define	SCSI_RESERVE	0x16
776#endif
777void
778device_reserve(int argc, char *argv[])
779{
780	struct scsi_test_unit_ready cmd;	/* close enough */
781
782	/* No arguments. */
783	if (argc != 0)
784		usage();
785
786	memset(&cmd, 0, sizeof(cmd));
787
788	cmd.opcode = SCSI_RESERVE;
789
790	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
791
792	return;
793}
794
795/*
796 * device_reset:
797 *
798 *	Issue a reset to a SCSI device.
799 */
800void
801device_reset(int argc, char *argv[])
802{
803
804	/* No arguments. */
805	if (argc != 0)
806		usage();
807
808	if (ioctl(fd, SCIOCRESET, NULL) != 0)
809		err(1, "SCIOCRESET");
810
811	return;
812}
813
814/*
815 * device_debug:
816 *
817 *	Set debug level to a SCSI device.
818 *	scsipi will print anything iff SCSIPI_DEBUG set in config.
819 */
820void
821device_debug(int argc, char *argv[])
822{
823	int lvl;
824
825	if (argc < 1)
826		usage();
827
828	lvl = atoi(argv[0]);
829
830	if (ioctl(fd, SCIOCDEBUG, &lvl) != 0)
831		err(1, "SCIOCDEBUG");
832
833	return;
834}
835
836/*
837 * device_getcache:
838 *
839 *	Get the caching parameters for a SCSI disk.
840 */
841void
842device_getcache(int argc, char *argv[])
843{
844	struct {
845		struct scsi_mode_parameter_header_6 header;
846		struct scsi_general_block_descriptor blk_desc;
847		struct page_caching caching_params;
848	} data;
849
850	/* No arguments. */
851	if (argc != 0)
852		usage();
853
854	scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data));
855
856	if ((data.caching_params.flags & (CACHING_RCD|CACHING_WCE)) ==
857	    CACHING_RCD)
858		printf("%s: no caches enabled\n", dvname);
859	else {
860		printf("%s: read cache %senabled\n", dvname,
861		    (data.caching_params.flags & CACHING_RCD) ? "not " : "");
862		printf("%s: write-back cache %senabled\n", dvname,
863		    (data.caching_params.flags & CACHING_WCE) ? "" : "not ");
864	}
865	printf("%s: caching parameters are %ssavable\n", dvname,
866	    (data.caching_params.pg_code & PGCODE_PS) ? "" : "not ");
867}
868
869/*
870 * device_setcache:
871 *
872 *	Set cache enables for a SCSI disk.
873 */
874void
875device_setcache(int argc, char *argv[])
876{
877	struct {
878		struct scsi_mode_parameter_header_6 header;
879		struct scsi_general_block_descriptor blk_desc;
880		struct page_caching caching_params;
881	} data;
882	int dlen;
883	u_int8_t flags, byte2;
884
885	if (argc > 2 || argc == 0)
886		usage();
887
888	flags = 0;
889	byte2 = 0;
890	if (strcmp(argv[0], "none") == 0)
891		flags = CACHING_RCD;
892	else if (strcmp(argv[0], "r") == 0)
893		flags = 0;
894	else if (strcmp(argv[0], "w") == 0)
895		flags = CACHING_RCD|CACHING_WCE;
896	else if (strcmp(argv[0], "rw") == 0)
897		flags = CACHING_WCE;
898	else
899		usage();
900
901	if (argc == 2) {
902		if (strcmp(argv[1], "save") == 0)
903			byte2 = SMS_SP;
904		else
905			usage();
906	}
907
908	scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data));
909
910	data.caching_params.pg_code &= PGCODE_MASK;
911	data.caching_params.flags =
912	    (data.caching_params.flags & ~(CACHING_RCD|CACHING_WCE)) | flags;
913
914	data.caching_params.cache_segment_size[0] = 0;
915	data.caching_params.cache_segment_size[1] = 0;
916
917	data.header.data_length = 0;
918
919	dlen = sizeof(data.header) + sizeof(data.blk_desc) + 2 +
920	    data.caching_params.pg_length;
921
922	scsi_mode_select(fd, byte2, &data, dlen);
923}
924
925/*
926 * device_flushcache:
927 *
928 *      Issue a FLUSH CACHE command to a SCSI drevice
929 */
930#ifndef	SCSI_FLUSHCACHE
931#define	SCSI_FLUSHCACHE	0x35
932#endif
933void
934device_flushcache(int argc, char *argv[])
935{
936	struct scsi_test_unit_ready cmd;	/* close enough */
937
938	/* No arguments. */
939	if (argc != 0)
940		usage();
941
942	memset(&cmd, 0, sizeof(cmd));
943
944	cmd.opcode = SCSI_FLUSHCACHE;
945
946	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
947
948	return;
949}
950
951/*
952 * device_prevent:
953 *
954 *      Issue a prevent to a SCSI device.
955 */
956void
957device_prevent(int argc, char *argv[])
958{
959	struct scsi_prevent_allow_medium_removal cmd;
960
961	/* No arguments. */
962	if (argc != 0)
963		usage();
964
965	memset(&cmd, 0, sizeof(cmd));
966
967	cmd.opcode = SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL;
968	cmd.how = SPAMR_PREVENT_DT;	/* XXX SMAMR_PREVENT_ALL? */
969
970	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
971
972	return;
973}
974
975/*
976 * device_allow:
977 *
978 *      Issue a stop to a SCSI device.
979 */
980void
981device_allow(int argc, char *argv[])
982{
983	struct scsi_prevent_allow_medium_removal cmd;
984
985	/* No arguments. */
986	if (argc != 0)
987		usage();
988
989	memset(&cmd, 0, sizeof(cmd));
990
991	cmd.opcode = SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL;
992	cmd.how = SPAMR_ALLOW;
993
994	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
995
996	return;
997}
998
999/*
1000 * device_start:
1001 *
1002 *      Issue a start to a SCSI device.
1003 */
1004void
1005device_start(int argc, char *argv[])
1006{
1007	struct scsipi_start_stop cmd;
1008
1009	/* No arguments. */
1010	if (argc != 0)
1011		usage();
1012
1013	memset(&cmd, 0, sizeof(cmd));
1014
1015	cmd.opcode = START_STOP;
1016	cmd.how = SSS_START;
1017
1018	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 30000, 0);
1019
1020	return;
1021}
1022
1023/*
1024 * device_stop:
1025 *
1026 *      Issue a stop to a SCSI device.
1027 */
1028void
1029device_stop(int argc, char *argv[])
1030{
1031	struct scsipi_start_stop cmd;
1032
1033	/* No arguments. */
1034	if (argc != 0)
1035		usage();
1036
1037	memset(&cmd, 0, sizeof(cmd));
1038
1039	cmd.opcode = START_STOP;
1040	cmd.how = SSS_STOP;
1041
1042	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 30000, 0);
1043
1044	return;
1045}
1046
1047/*
1048 * device_tur:
1049 *
1050 *      Issue a TEST UNIT READY to a SCSI drevice
1051 */
1052void
1053device_tur(int argc, char *argv[])
1054{
1055	struct scsi_test_unit_ready cmd;
1056
1057	/* No arguments. */
1058	if (argc != 0)
1059		usage();
1060
1061	memset(&cmd, 0, sizeof(cmd));
1062
1063	cmd.opcode = SCSI_TEST_UNIT_READY;
1064
1065	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
1066
1067	return;
1068}
1069
1070/*
1071 * BUS COMMANDS
1072 */
1073
1074/*
1075 * bus_reset:
1076 *
1077 *	Issue a reset to a SCSI bus.
1078 */
1079void
1080bus_reset(int argc, char *argv[])
1081{
1082
1083	/* No arguments. */
1084	if (argc != 0)
1085		usage();
1086
1087	if (ioctl(fd, SCBUSIORESET, NULL) != 0)
1088		err(1, "SCBUSIORESET");
1089
1090	return;
1091}
1092
1093/*
1094 * bus_scan:
1095 *
1096 *	Rescan a SCSI bus for new devices.
1097 */
1098void
1099bus_scan(int argc, char *argv[])
1100{
1101	struct scbusioscan_args args;
1102	char *cp;
1103
1104	/* Must have two args: target lun */
1105	if (argc != 2)
1106		usage();
1107
1108	if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0)
1109		args.sa_target = -1;
1110	else {
1111		args.sa_target = strtol(argv[0], &cp, 10);
1112		if (*cp != '\0' || args.sa_target < 0)
1113			errx(1, "invalid target: %s", argv[0]);
1114	}
1115
1116	if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0)
1117		args.sa_lun = -1;
1118	else {
1119		args.sa_lun = strtol(argv[1], &cp, 10);
1120		if (*cp != '\0' || args.sa_lun < 0)
1121			errx(1, "invalid lun: %s", argv[1]);
1122	}
1123
1124	if (ioctl(fd, SCBUSIOSCAN, &args) != 0)
1125		err(1, "SCBUSIOSCAN");
1126
1127	return;
1128}
1129
1130/*
1131 * bus_detach:
1132 *
1133 *	detach SCSI devices from a bus.
1134 */
1135void
1136bus_detach(int argc, char *argv[])
1137{
1138	struct scbusiodetach_args args;
1139	char *cp;
1140
1141	/* Must have two args: target lun */
1142	if (argc != 2)
1143		usage();
1144
1145	if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0)
1146		args.sa_target = -1;
1147	else {
1148		args.sa_target = strtol(argv[0], &cp, 10);
1149		if (*cp != '\0' || args.sa_target < 0)
1150			errx(1, "invalid target: %s", argv[0]);
1151	}
1152
1153	if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0)
1154		args.sa_lun = -1;
1155	else {
1156		args.sa_lun = strtol(argv[1], &cp, 10);
1157		if (*cp != '\0' || args.sa_lun < 0)
1158			errx(1, "invalid lun: %s", argv[1]);
1159	}
1160
1161	if (ioctl(fd, SCBUSIODETACH, &args) != 0)
1162		err(1, "SCBUSIODETACH");
1163
1164	return;
1165}
1166