scsictl.c revision 1.13 1 /* $NetBSD: scsictl.c,v 1.13 2001/05/15 15:11:02 bouyer 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 scsipi_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