scsictl.c revision 1.9 1 /* $NetBSD: scsictl.c,v 1.9 1999/10/04 17:10:56 mycroft 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 extern const char *__progname; /* from crt0.o */
80
81 void device_format __P((int, char *[]));
82 void device_identify __P((int, char *[]));
83 void device_reassign __P((int, char *[]));
84 void device_reset __P((int, char *[]));
85
86 struct command device_commands[] = {
87 { "format", "", device_format },
88 { "identify", "", device_identify },
89 { "reassign", "blkno [blkno [...]]", device_reassign },
90 { "reset", "", device_reset },
91 { NULL, NULL, NULL },
92 };
93
94 void bus_reset __P((int, char *[]));
95 void bus_scan __P((int, char *[]));
96
97 struct command bus_commands[] = {
98 { "reset", "", bus_reset },
99 { "scan", "target lun", bus_scan },
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\n",
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 __progname);
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 scsi_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, 60000, 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\n", 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\n", 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\n", argv[1]);
410 }
411
412 if (ioctl(fd, SCBUSIOSCAN, &args) != 0)
413 err(1, "SCBUSIOSCAN");
414
415 return;
416 }
417