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