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
63struct command {
64	const char *cmd_name;
65	const char *arg_names;
66	void (*cmd_func) __P((int, char *[]));
67};
68
69int	main __P((int, char *[]));
70void	usage __P((void));
71
72int	fd;				/* file descriptor for device */
73const	char *dvname;			/* device name */
74char	dvname_store[MAXPATHLEN];	/* for opendisk(3) */
75const	char *cmdname;			/* command user issued */
76const	char *argnames;			/* helpstring: expected arguments */
77struct	scsi_addr dvaddr;		/* SCSI device's address */
78
79void	device_format __P((int, char *[]));
80void	device_identify __P((int, char *[]));
81void	device_reassign __P((int, char *[]));
82void	device_release __P((int, char *[]));
83void	device_reserve __P((int, char *[]));
84void	device_reset __P((int, char *[]));
85void	device_start __P((int, char *[]));
86void	device_stop __P((int, char *[]));
87void	device_tur __P((int, char *[]));
88
89struct 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
102void	bus_reset __P((int, char *[]));
103void	bus_scan __P((int, char *[]));
104void	bus_detach __P((int, char *[]));
105
106struct 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
113int
114main(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
177void
178usage()
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 */
207void
208device_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 */
402void
403device_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 */
447void
448device_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
505void
506device_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
535void
536device_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 */
562void
563device_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 */
587void
588bus_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 */
608void
609device_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 */
634void
635device_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 */
660void
661device_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 */
687void
688bus_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 */
726void
727bus_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