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