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
62 struct command {
63 const char *cmd_name;
64 void (*cmd_func) __P((int, char *[]));
65 };
66
67 int main __P((int, char *[]));
68 void usage __P((void));
69
70 int fd; /* file descriptor for device */
71 const char *dvname; /* device name */
72 char dvname_store[MAXPATHLEN]; /* for opendisk(3) */
73 const char *cmdname; /* command user issued */
74 struct scsi_addr dvaddr; /* SCSI device's address */
75
76 extern const char *__progname; /* from crt0.o */
77
78 void device_identify __P((int, char *[]));
79 void device_reassign __P((int, char *[]));
80 void device_reset __P((int, char *[]));
81
82 struct command device_commands[] = {
83 { "identify", device_identify },
84 { "reassign", device_reassign },
85 { "reset", device_reset },
86 { NULL, NULL },
87 };
88
89 void bus_reset __P((int, char *[]));
90 void bus_scan __P((int, char *[]));
91
92 struct command bus_commands[] = {
93 { "reset", bus_reset },
94 { "scan", bus_scan },
95 { NULL, NULL },
96 };
97
98 int
99 main(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
160 void
161 usage()
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 */
179 void
180 device_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 */
228 void
229 device_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 */
283 void
284 device_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 */
312 void
313 bus_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 */
337 void
338 bus_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