scsictl.c revision 1.14
1/*	$NetBSD: scsictl.c,v 1.14 2001/07/18 20:36:36 bouyer 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_reset __P((int, char *[]));
83
84struct command device_commands[] = {
85	{ "format",	"",			device_format },
86	{ "identify",	"",			device_identify },
87	{ "reassign",	"blkno [blkno [...]]",	device_reassign },
88	{ "reset",	"",			device_reset },
89	{ NULL,		NULL,			NULL },
90};
91
92void	bus_reset __P((int, char *[]));
93void	bus_scan __P((int, char *[]));
94void	bus_detach __P((int, char *[]));
95
96struct command bus_commands[] = {
97	{ "reset",	"",			bus_reset },
98	{ "scan",	"target lun",		bus_scan },
99	{ "detach",	"target lun",		bus_detach },
100	{ NULL,		NULL,				NULL },
101};
102
103int
104main(argc, argv)
105	int argc;
106	char *argv[];
107{
108	struct command *commands;
109	int i;
110
111	/* Must have at least: device command */
112	if (argc < 3)
113		usage();
114
115	/* Skip program name, get and skip device name and command. */
116	dvname = argv[1];
117	cmdname = argv[2];
118	argv += 3;
119	argc -= 3;
120
121	/*
122	 * Open the device and determine if it's a scsibus or an actual
123	 * device.  Devices respond to the SCIOCIDENTIFY ioctl.
124	 */
125	fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0);
126	if (fd == -1) {
127		if (errno == ENOENT) {
128			/*
129			 * Device doesn't exist.  Probably trying to open
130			 * a device which doesn't use disk semantics for
131			 * device name.  Try again, specifying "cooked",
132			 * which leaves off the "r" in front of the device's
133			 * name.
134			 */
135			fd = opendisk(dvname, O_RDWR, dvname_store,
136			    sizeof(dvname_store), 1);
137			if (fd == -1)
138				err(1, "%s", dvname);
139		} else
140			err(1, "%s", dvname);
141	}
142
143	/*
144	 * Point the dvname at the actual device name that opendisk() opened.
145	 */
146	dvname = dvname_store;
147
148	if (ioctl(fd, SCIOCIDENTIFY, &dvaddr) < 0)
149		commands = bus_commands;
150	else
151		commands = device_commands;
152
153	/* Look up and call the command. */
154	for (i = 0; commands[i].cmd_name != NULL; i++)
155		if (strcmp(cmdname, commands[i].cmd_name) == 0)
156			break;
157	if (commands[i].cmd_name == NULL)
158		errx(1, "unknown %s command: %s",
159		    commands == bus_commands ? "bus" : "device", cmdname);
160
161	argnames = commands[i].arg_names;
162
163	(*commands[i].cmd_func)(argc, argv);
164	exit(0);
165}
166
167void
168usage()
169{
170	int i;
171
172	fprintf(stderr, "Usage: %s device command [arg [...]]\n",
173	    getprogname());
174
175	fprintf(stderr, "   Commands pertaining to scsi devices:\n");
176	for (i=0; device_commands[i].cmd_name != NULL; i++)
177		fprintf(stderr, "\t%s %s\n", device_commands[i].cmd_name,
178					    device_commands[i].arg_names);
179	fprintf(stderr, "   Commands pertaining to scsi busses:\n");
180	for (i=0; bus_commands[i].cmd_name != NULL; i++)
181		fprintf(stderr, "\t%s %s\n", bus_commands[i].cmd_name,
182					    bus_commands[i].arg_names);
183	fprintf(stderr, "   Use `any' or `all' to wildcard target or lun\n");
184
185	exit(1);
186}
187
188/*
189 * DEVICE COMMANDS
190 */
191
192/*
193 * device_format:
194 *
195 *	Format a direct access device.
196 *
197 *	XXX Does not handle defect list management or geometry settings.
198 */
199void
200device_format(argc, argv)
201	int argc;
202	char *argv[];
203{
204	struct scsi_format_unit cmd;
205	struct {
206		struct scsipi_mode_header header;
207		struct scsi_blk_desc blk_desc;
208		struct page_disk_format format_page;
209	} data;
210
211	/* No arguments. */
212	if (argc != 0)
213		usage();
214
215	/*
216	 * Get the DISK FORMAT mode page.  SCSI-2 recommends specifying the
217	 * interleave read from this page in the FORMAT UNIT command.
218	 */
219	scsi_mode_sense(fd, 0x03, 0x00, &data, sizeof(data));
220
221	memset(&cmd, 0, sizeof(cmd));
222
223	cmd.opcode = SCSI_FORMAT_UNIT;
224	memcpy(cmd.interleave, data.format_page.interleave,
225	    sizeof(cmd.interleave));
226
227	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 21600000, 0);
228
229	return;
230}
231
232/*
233 * device_identify:
234 *
235 *	Display the identity of the device, including it's SCSI bus,
236 *	target, lun, and it's vendor/product/revision information.
237 */
238void
239device_identify(argc, argv)
240	int argc;
241	char *argv[];
242{
243	struct scsipi_inquiry_data inqbuf;
244	struct scsipi_inquiry cmd;
245
246	/* x4 in case every character is escaped, +1 for NUL. */
247	char vendor[(sizeof(inqbuf.vendor) * 4) + 1],
248	     product[(sizeof(inqbuf.product) * 4) + 1],
249	     revision[(sizeof(inqbuf.revision) * 4) + 1];
250
251	/* No arguments. */
252	if (argc != 0)
253		usage();
254
255	memset(&cmd, 0, sizeof(cmd));
256	memset(&inqbuf, 0, sizeof(inqbuf));
257
258	cmd.opcode = INQUIRY;
259	cmd.length = sizeof(inqbuf);
260
261	scsi_command(fd, &cmd, sizeof(cmd), &inqbuf, sizeof(inqbuf),
262	    10000, SCCMD_READ);
263
264	scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor,
265	    sizeof(inqbuf.vendor));
266	scsi_strvis(product, sizeof(product), inqbuf.product,
267	    sizeof(inqbuf.product));
268	scsi_strvis(revision, sizeof(revision), inqbuf.revision,
269	    sizeof(inqbuf.revision));
270
271	printf("%s: scsibus%d target %d lun %d <%s, %s, %s>\n",
272	    dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target,
273	    dvaddr.addr.scsi.lun, vendor, product, revision);
274
275	return;
276}
277
278/*
279 * device_reassign:
280 *
281 *	Reassign bad blocks on a direct access device.
282 */
283void
284device_reassign(argc, argv)
285	int argc;
286	char *argv[];
287{
288	struct scsi_reassign_blocks cmd;
289	struct scsi_reassign_blocks_data *data;
290	size_t dlen;
291	u_int32_t blkno;
292	int i;
293	char *cp;
294
295	/* We get a list of block numbers. */
296	if (argc < 1)
297		usage();
298
299	/*
300	 * Allocate the reassign blocks descriptor.  The 4 comes from the
301	 * size of the block address in the defect descriptor.
302	 */
303	dlen = sizeof(struct scsi_reassign_blocks_data) + ((argc - 1) * 4);
304	data = malloc(dlen);
305	if (data == NULL)
306		errx(1, "unable to allocate defect descriptor");
307	memset(data, 0, dlen);
308
309	cmd.opcode = SCSI_REASSIGN_BLOCKS;
310	cmd.byte2 = 0;
311	cmd.unused[0] = 0;
312	cmd.unused[1] = 0;
313	cmd.unused[2] = 0;
314	cmd.control = 0;
315
316	/* Defect descriptor length. */
317	_lto2b(argc * 4, data->length);
318
319	/* Build the defect descriptor list. */
320	for (i = 0; i < argc; i++) {
321		blkno = strtoul(argv[i], &cp, 10);
322		if (*cp != '\0')
323			errx(1, "invalid block number: %s", argv[i]);
324		_lto4b(blkno, data->defect_descriptor[i].dlbaddr);
325	}
326
327	scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_WRITE);
328
329	free(data);
330	return;
331}
332
333/*
334 * device_reset:
335 *
336 *	Issue a reset to a SCSI device.
337 */
338void
339device_reset(argc, argv)
340	int argc;
341	char *argv[];
342{
343
344	/* No arguments. */
345	if (argc != 0)
346		usage();
347
348	if (ioctl(fd, SCIOCRESET, NULL) != 0)
349		err(1, "SCIOCRESET");
350
351	return;
352}
353
354/*
355 * BUS COMMANDS
356 */
357
358/*
359 * bus_reset:
360 *
361 *	Issue a reset to a SCSI bus.
362 */
363void
364bus_reset(argc, argv)
365	int argc;
366	char *argv[];
367{
368
369	/* No arguments. */
370	if (argc != 0)
371		usage();
372
373	if (ioctl(fd, SCBUSIORESET, NULL) != 0)
374		err(1, "SCBUSIORESET");
375
376	return;
377}
378
379/*
380 * bus_scan:
381 *
382 *	Rescan a SCSI bus for new devices.
383 */
384void
385bus_scan(argc, argv)
386	int argc;
387	char *argv[];
388{
389	struct scbusioscan_args args;
390	char *cp;
391
392	/* Must have two args: target lun */
393	if (argc != 2)
394		usage();
395
396	if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0)
397		args.sa_target = -1;
398	else {
399		args.sa_target = strtol(argv[0], &cp, 10);
400		if (*cp != '\0' || args.sa_target < 0)
401			errx(1, "invalid target: %s", argv[0]);
402	}
403
404	if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0)
405		args.sa_lun = -1;
406	else {
407		args.sa_lun = strtol(argv[1], &cp, 10);
408		if (*cp != '\0' || args.sa_lun < 0)
409			errx(1, "invalid lun: %s", argv[1]);
410	}
411
412	if (ioctl(fd, SCBUSIOSCAN, &args) != 0)
413		err(1, "SCBUSIOSCAN");
414
415	return;
416}
417
418/*
419 * bus_detach:
420 *
421 *	detach SCSI devices from a bus.
422 */
423void
424bus_detach(argc, argv)
425	int argc;
426	char *argv[];
427{
428	struct scbusiodetach_args args;
429	char *cp;
430
431	/* Must have two args: target lun */
432	if (argc != 2)
433		usage();
434
435	if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0)
436		args.sa_target = -1;
437	else {
438		args.sa_target = strtol(argv[0], &cp, 10);
439		if (*cp != '\0' || args.sa_target < 0)
440			errx(1, "invalid target: %s\n", argv[0]);
441	}
442
443	if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0)
444		args.sa_lun = -1;
445	else {
446		args.sa_lun = strtol(argv[1], &cp, 10);
447		if (*cp != '\0' || args.sa_lun < 0)
448			errx(1, "invalid lun: %s\n", argv[1]);
449	}
450
451	if (ioctl(fd, SCBUSIODETACH, &args) != 0)
452		err(1, "SCBUSIODETACH");
453
454	return;
455}
456