xref: /netbsd-src/sbin/scsictl/scsictl.c (revision 17dd36da8292193180754d5047c0926dbb56818c)
1 /*	$NetBSD: scsictl.c,v 1.12 2001/04/01 14:59:56 ad 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 
95 struct command bus_commands[] = {
96 	{ "reset",	"",			bus_reset },
97 	{ "scan",	"target lun",		bus_scan },
98 	{ NULL,		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 		} else
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",
157 		    commands == bus_commands ? "bus" : "device", cmdname);
158 
159 	argnames = commands[i].arg_names;
160 
161 	(*commands[i].cmd_func)(argc, argv);
162 	exit(0);
163 }
164 
165 void
166 usage()
167 {
168 	int i;
169 
170 	fprintf(stderr, "Usage: %s device command [arg [...]]\n",
171 	    getprogname());
172 
173 	fprintf(stderr, "   Commands pertaining to scsi devices:\n");
174 	for (i=0; device_commands[i].cmd_name != NULL; i++)
175 		fprintf(stderr, "\t%s %s\n", device_commands[i].cmd_name,
176 					    device_commands[i].arg_names);
177 	fprintf(stderr, "   Commands pertaining to scsi busses:\n");
178 	for (i=0; bus_commands[i].cmd_name != NULL; i++)
179 		fprintf(stderr, "\t%s %s\n", bus_commands[i].cmd_name,
180 					    bus_commands[i].arg_names);
181 	fprintf(stderr, "   Use `any' or `all' to wildcard target or lun\n");
182 
183 	exit(1);
184 }
185 
186 /*
187  * DEVICE COMMANDS
188  */
189 
190 /*
191  * device_format:
192  *
193  *	Format a direct access device.
194  *
195  *	XXX Does not handle defect list management or geometry settings.
196  */
197 void
198 device_format(argc, argv)
199 	int argc;
200 	char *argv[];
201 {
202 	struct scsi_format_unit cmd;
203 	struct {
204 		struct scsi_mode_header header;
205 		struct scsi_blk_desc blk_desc;
206 		struct page_disk_format format_page;
207 	} data;
208 
209 	/* No arguments. */
210 	if (argc != 0)
211 		usage();
212 
213 	/*
214 	 * Get the DISK FORMAT mode page.  SCSI-2 recommends specifying the
215 	 * interleave read from this page in the FORMAT UNIT command.
216 	 */
217 	scsi_mode_sense(fd, 0x03, 0x00, &data, sizeof(data));
218 
219 	memset(&cmd, 0, sizeof(cmd));
220 
221 	cmd.opcode = SCSI_FORMAT_UNIT;
222 	memcpy(cmd.interleave, data.format_page.interleave,
223 	    sizeof(cmd.interleave));
224 
225 	scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 21600000, 0);
226 
227 	return;
228 }
229 
230 /*
231  * device_identify:
232  *
233  *	Display the identity of the device, including it's SCSI bus,
234  *	target, lun, and it's vendor/product/revision information.
235  */
236 void
237 device_identify(argc, argv)
238 	int argc;
239 	char *argv[];
240 {
241 	struct scsipi_inquiry_data inqbuf;
242 	struct scsipi_inquiry cmd;
243 
244 	/* x4 in case every character is escaped, +1 for NUL. */
245 	char vendor[(sizeof(inqbuf.vendor) * 4) + 1],
246 	     product[(sizeof(inqbuf.product) * 4) + 1],
247 	     revision[(sizeof(inqbuf.revision) * 4) + 1];
248 
249 	/* No arguments. */
250 	if (argc != 0)
251 		usage();
252 
253 	memset(&cmd, 0, sizeof(cmd));
254 	memset(&inqbuf, 0, sizeof(inqbuf));
255 
256 	cmd.opcode = INQUIRY;
257 	cmd.length = sizeof(inqbuf);
258 
259 	scsi_command(fd, &cmd, sizeof(cmd), &inqbuf, sizeof(inqbuf),
260 	    10000, SCCMD_READ);
261 
262 	scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor,
263 	    sizeof(inqbuf.vendor));
264 	scsi_strvis(product, sizeof(product), inqbuf.product,
265 	    sizeof(inqbuf.product));
266 	scsi_strvis(revision, sizeof(revision), inqbuf.revision,
267 	    sizeof(inqbuf.revision));
268 
269 	printf("%s: scsibus%d target %d lun %d <%s, %s, %s>\n",
270 	    dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target,
271 	    dvaddr.addr.scsi.lun, vendor, product, revision);
272 
273 	return;
274 }
275 
276 /*
277  * device_reassign:
278  *
279  *	Reassign bad blocks on a direct access device.
280  */
281 void
282 device_reassign(argc, argv)
283 	int argc;
284 	char *argv[];
285 {
286 	struct scsi_reassign_blocks cmd;
287 	struct scsi_reassign_blocks_data *data;
288 	size_t dlen;
289 	u_int32_t blkno;
290 	int i;
291 	char *cp;
292 
293 	/* We get a list of block numbers. */
294 	if (argc < 1)
295 		usage();
296 
297 	/*
298 	 * Allocate the reassign blocks descriptor.  The 4 comes from the
299 	 * size of the block address in the defect descriptor.
300 	 */
301 	dlen = sizeof(struct scsi_reassign_blocks_data) + ((argc - 1) * 4);
302 	data = malloc(dlen);
303 	if (data == NULL)
304 		errx(1, "unable to allocate defect descriptor");
305 	memset(data, 0, dlen);
306 
307 	cmd.opcode = SCSI_REASSIGN_BLOCKS;
308 	cmd.byte2 = 0;
309 	cmd.unused[0] = 0;
310 	cmd.unused[1] = 0;
311 	cmd.unused[2] = 0;
312 	cmd.control = 0;
313 
314 	/* Defect descriptor length. */
315 	_lto2b(argc * 4, data->length);
316 
317 	/* Build the defect descriptor list. */
318 	for (i = 0; i < argc; i++) {
319 		blkno = strtoul(argv[i], &cp, 10);
320 		if (*cp != '\0')
321 			errx(1, "invalid block number: %s", argv[i]);
322 		_lto4b(blkno, data->defect_descriptor[i].dlbaddr);
323 	}
324 
325 	scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_WRITE);
326 
327 	free(data);
328 	return;
329 }
330 
331 /*
332  * device_reset:
333  *
334  *	Issue a reset to a SCSI device.
335  */
336 void
337 device_reset(argc, argv)
338 	int argc;
339 	char *argv[];
340 {
341 
342 	/* No arguments. */
343 	if (argc != 0)
344 		usage();
345 
346 	if (ioctl(fd, SCIOCRESET, NULL) != 0)
347 		err(1, "SCIOCRESET");
348 
349 	return;
350 }
351 
352 /*
353  * BUS COMMANDS
354  */
355 
356 /*
357  * bus_reset:
358  *
359  *	Issue a reset to a SCSI bus.
360  */
361 void
362 bus_reset(argc, argv)
363 	int argc;
364 	char *argv[];
365 {
366 
367 	/* No arguments. */
368 	if (argc != 0)
369 		usage();
370 
371 	if (ioctl(fd, SCBUSIORESET, NULL) != 0)
372 		err(1, "SCBUSIORESET");
373 
374 	return;
375 }
376 
377 /*
378  * bus_scan:
379  *
380  *	Rescan a SCSI bus for new devices.
381  */
382 void
383 bus_scan(argc, argv)
384 	int argc;
385 	char *argv[];
386 {
387 	struct scbusioscan_args args;
388 	char *cp;
389 
390 	/* Must have two args: target lun */
391 	if (argc != 2)
392 		usage();
393 
394 	if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0)
395 		args.sa_target = -1;
396 	else {
397 		args.sa_target = strtol(argv[0], &cp, 10);
398 		if (*cp != '\0' || args.sa_target < 0)
399 			errx(1, "invalid target: %s", argv[0]);
400 	}
401 
402 	if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0)
403 		args.sa_lun = -1;
404 	else {
405 		args.sa_lun = strtol(argv[1], &cp, 10);
406 		if (*cp != '\0' || args.sa_lun < 0)
407 			errx(1, "invalid lun: %s", argv[1]);
408 	}
409 
410 	if (ioctl(fd, SCBUSIOSCAN, &args) != 0)
411 		err(1, "SCBUSIOSCAN");
412 
413 	return;
414 }
415