xref: /netbsd/sbin/scsictl/scsictl.c (revision bf9ec67e)
1 /*	$NetBSD: scsictl.c,v 1.15 2002/04/14 03:24:42 tsutsui 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 	const char *arg_names;
66 	void (*cmd_func) __P((int, char *[]));
67 };
68 
69 int	main __P((int, char *[]));
70 void	usage __P((void));
71 
72 int	fd;				/* file descriptor for device */
73 const	char *dvname;			/* device name */
74 char	dvname_store[MAXPATHLEN];	/* for opendisk(3) */
75 const	char *cmdname;			/* command user issued */
76 const	char *argnames;			/* helpstring: expected arguments */
77 struct	scsi_addr dvaddr;		/* SCSI device's address */
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",	"blkno [blkno [...]]",	device_reassign },
88 	{ "reset",	"",			device_reset },
89 	{ NULL,		NULL,			NULL },
90 };
91 
92 void	bus_reset __P((int, char *[]));
93 void	bus_scan __P((int, char *[]));
94 void	bus_detach __P((int, char *[]));
95 
96 struct 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 
103 int
104 main(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 
167 void
168 usage()
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  */
199 void
200 device_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, 6 * 60 * 60 * 1000, 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  */
238 void
239 device_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  */
283 void
284 device_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  */
338 void
339 device_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  */
363 void
364 bus_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  */
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 		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  */
423 void
424 bus_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