scsictl.c revision 1.3
1/*	$NetBSD: scsictl.c,v 1.3 1998/10/17 05:08:27 thorpej 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_disk.h>
58#include <dev/scsipi/scsipiconf.h>
59
60#include "extern.h"
61
62struct command {
63	const char *cmd_name;
64	void (*cmd_func) __P((int, char *[]));
65};
66
67int	main __P((int, char *[]));
68void	usage __P((void));
69
70int	fd;				/* file descriptor for device */
71const	char *dvname;			/* device name */
72char	dvname_store[MAXPATHLEN];	/* for opendisk(3) */
73const	char *cmdname;			/* command user issued */
74struct	scsi_addr dvaddr;		/* SCSI device's address */
75
76extern const char *__progname;		/* from crt0.o */
77
78void	device_identify __P((int, char *[]));
79void	device_reassign __P((int, char *[]));
80void	device_reset __P((int, char *[]));
81
82struct command device_commands[] = {
83	{ "identify",	device_identify },
84	{ "reassign",	device_reassign },
85	{ "reset",	device_reset },
86	{ NULL,		NULL },
87};
88
89void	bus_reset __P((int, char *[]));
90void	bus_scan __P((int, char *[]));
91
92struct command bus_commands[] = {
93	{ "reset",	bus_reset },
94	{ "scan",	bus_scan },
95	{ NULL,		NULL },
96};
97
98int
99main(argc, argv)
100	int argc;
101	char *argv[];
102{
103	struct command *commands;
104	int i;
105
106	/* Must have at least: device command */
107	if (argc < 3)
108		usage();
109
110	/* Skip program name, get and skip device name and command. */
111	dvname = argv[1];
112	cmdname = argv[2];
113	argv += 3;
114	argc -= 3;
115
116	/*
117	 * Open the device and determine if it's a scsibus or an actual
118	 * device.  Devices respond to the SCIOCIDENTIFY ioctl.
119	 */
120	fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0);
121	if (fd == -1) {
122		if (errno == ENOENT) {
123			/*
124			 * Device doesn't exist.  Probably trying to open
125			 * a device which doesn't use disk semantics for
126			 * device name.  Try again, specifying "cooked",
127			 * which leaves off the "r" in front of the device's
128			 * name.
129			 */
130			fd = opendisk(dvname, O_RDWR, dvname_store,
131			    sizeof(dvname_store), 1);
132			if (fd == -1)
133				err(1, "%s", dvname);
134		}
135		err(1, "%s", dvname);
136	}
137
138	/*
139	 * Point the dvname at the actual device name that opendisk() opened.
140	 */
141	dvname = dvname_store;
142
143	if (ioctl(fd, SCIOCIDENTIFY, &dvaddr) < 0)
144		commands = bus_commands;
145	else
146		commands = device_commands;
147
148	/* Look up and call the command. */
149	for (i = 0; commands[i].cmd_name != NULL; i++)
150		if (strcmp(cmdname, commands[i].cmd_name) == 0)
151			break;
152	if (commands[i].cmd_name == NULL)
153		errx(1, "unknown %s command: %s\n",
154		    commands == bus_commands ? "bus" : "device", cmdname);
155
156	(*commands[i].cmd_func)(argc, argv);
157	exit(0);
158}
159
160void
161usage()
162{
163
164	fprintf(stderr, "usage: %s device command [arg [...]]\n",
165	    __progname);
166	exit(1);
167}
168
169/*
170 * DEVICE COMMANDS
171 */
172
173/*
174 * device_identify:
175 *
176 *	Display the identity of the device, including it's SCSI bus,
177 *	target, lun, and it's vendor/product/revision information.
178 */
179void
180device_identify(argc, argv)
181	int argc;
182	char *argv[];
183{
184	struct scsipi_inquiry_data inqbuf;
185	struct scsipi_inquiry cmd;
186
187	/* x4 in case every character is escaped, +1 for NUL. */
188	char vendor[(sizeof(inqbuf.vendor) * 4) + 1],
189	     product[(sizeof(inqbuf.product) * 4) + 1],
190	     revision[(sizeof(inqbuf.revision) * 4) + 1];
191
192	/* No arguments. */
193	if (argc != 0)
194		goto usage;
195
196	memset(&cmd, 0, sizeof(cmd));
197	memset(&inqbuf, 0, sizeof(inqbuf));
198
199	cmd.opcode = INQUIRY;
200	cmd.length = sizeof(inqbuf);
201
202	scsi_command(fd, &cmd, sizeof(cmd), &inqbuf, sizeof(inqbuf),
203	    10000, SCCMD_READ);
204
205	scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor,
206	    sizeof(inqbuf.vendor));
207	scsi_strvis(product, sizeof(product), inqbuf.product,
208	    sizeof(inqbuf.product));
209	scsi_strvis(revision, sizeof(revision), inqbuf.revision,
210	    sizeof(inqbuf.revision));
211
212	printf("%s: scsibus%d target %d lun %d <%s, %s, %s>\n",
213	    dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target,
214	    dvaddr.addr.scsi.lun, vendor, product, revision);
215
216	return;
217
218 usage:
219	fprintf(stderr, "usage: %s device %s\n", __progname, cmdname);
220	exit(1);
221}
222
223/*
224 * device_reassign:
225 *
226 *	Reassign bad blocks on a direct access device.
227 */
228void
229device_reassign(argc, argv)
230	int argc;
231	char *argv[];
232{
233	struct scsi_reassign_blocks cmd;
234	struct scsi_reassign_blocks_data *data;
235	size_t dlen;
236	u_int32_t blkno;
237	int i;
238	char *cp;
239
240	/* We get a list of block numbers. */
241	if (argc < 1)
242		goto usage;
243
244	/*
245	 * Allocate the reassign blocks descriptor.  The 4 comes from the
246	 * size of the block address in the defect descriptor.
247	 */
248	dlen = sizeof(struct scsi_reassign_blocks_data) + ((argc - 1) * 4);
249	data = malloc(dlen);
250	if (data == NULL)
251		errx(1, "unable to allocate defect descriptor");
252	memset(data, 0, dlen);
253
254	cmd.opcode = SCSI_REASSIGN_BLOCKS;
255
256	/* Defect descriptor length. */
257	_lto2l(argc * 4, data->length);
258
259	/* Build the defect descriptor list. */
260	for (i = 0; i < argc; i++) {
261		blkno = strtoul(argv[i], &cp, 10);
262		if (*cp != '\0')
263			errx(1, "invalid block number: %s\n", argv[i]);
264		_lto4l(blkno, data->defect_descriptor[i].dlbaddr);
265	}
266
267	scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_WRITE);
268
269	free(data);
270	return;
271
272 usage:
273	fprintf(stderr, "usage: %s device %s blkno [blkno [...]]\n",
274	    __progname, cmdname);
275	exit(1);
276}
277
278/*
279 * device_reset:
280 *
281 *	Issue a reset to a SCSI device.
282 */
283void
284device_reset(argc, argv)
285	int argc;
286	char *argv[];
287{
288
289	/* No arguments. */
290	if (argc != 0)
291		goto usage;
292
293	if (ioctl(fd, SCIOCRESET, NULL) != 0)
294		err(1, "SCIOCRESET");
295
296	return;
297
298 usage:
299	fprintf(stderr, "usage: %s device %s\n", __progname, cmdname);
300	exit(1);
301}
302
303/*
304 * BUS COMMANDS
305 */
306
307/*
308 * bus_reset:
309 *
310 *	Issue a reset to a SCSI bus.
311 */
312void
313bus_reset(argc, argv)
314	int argc;
315	char *argv[];
316{
317
318	/* No arguments. */
319	if (argc != 0)
320		goto usage;
321
322	if (ioctl(fd, SCBUSIORESET, NULL) != 0)
323		err(1, "SCBUSIORESET");
324
325	return;
326
327 usage:
328	fprintf(stderr, "usage: %s device %s\n", __progname, cmdname);
329	exit(1);
330}
331
332/*
333 * bus_scan:
334 *
335 *	Rescan a SCSI bus for new devices.
336 */
337void
338bus_scan(argc, argv)
339	int argc;
340	char *argv[];
341{
342	struct scbusioscan_args args;
343	char *cp;
344
345	/* Must have two args: target lun */
346	if (argc != 2)
347		goto usage;
348
349	if (strcmp(argv[0], "any") == 0)
350		args.sa_target = -1;
351	else {
352		args.sa_target = strtol(argv[0], &cp, 10);
353		if (*cp != '\0' || args.sa_target < 0)
354			errx(1, "invalid target: %s\n", argv[0]);
355	}
356
357	if (strcmp(argv[1], "any") == 0)
358		args.sa_lun = -1;
359	else {
360		args.sa_lun = strtol(argv[1], &cp, 10);
361		if (*cp != '\0' || args.sa_lun < 0)
362			errx(1, "invalid lun: %s\n", argv[1]);
363	}
364
365	if (ioctl(fd, SCBUSIOSCAN, &args) != 0)
366		err(1, "SCBUSIOSCAN");
367
368	return;
369
370 usage:
371	fprintf(stderr, "usage: %s device %s target lun\n", __progname,
372	    cmdname);
373	fprintf(stderr, "       use `any' to wildcard target or lun\n");
374	exit(1);
375}
376