scsictl.c revision 1.4 1 /* $NetBSD: scsictl.c,v 1.4 1998/11/12 01:16:09 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_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 void (*cmd_func) __P((int, char *[]));
66 };
67
68 int main __P((int, char *[]));
69 void usage __P((void));
70
71 int fd; /* file descriptor for device */
72 const char *dvname; /* device name */
73 char dvname_store[MAXPATHLEN]; /* for opendisk(3) */
74 const char *cmdname; /* command user issued */
75 struct scsi_addr dvaddr; /* SCSI device's address */
76
77 extern const char *__progname; /* from crt0.o */
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", device_reassign },
88 { "reset", device_reset },
89 { 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", bus_scan },
98 { 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 }
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\n",
157 commands == bus_commands ? "bus" : "device", cmdname);
158
159 (*commands[i].cmd_func)(argc, argv);
160 exit(0);
161 }
162
163 void
164 usage()
165 {
166
167 fprintf(stderr, "usage: %s device command [arg [...]]\n",
168 __progname);
169 exit(1);
170 }
171
172 /*
173 * DEVICE COMMANDS
174 */
175
176 /*
177 * device_format:
178 *
179 * Format a direct access device.
180 *
181 * XXX Does not handle defect list management or geometry settings.
182 */
183 void
184 device_format(argc, argv)
185 int argc;
186 char *argv[];
187 {
188 struct scsi_format_unit cmd;
189 struct {
190 struct scsi_mode_header header;
191 struct scsi_blk_desc blk_desc;
192 struct page_disk_format format_page;
193 } data;
194
195 /* No arguments. */
196 if (argc != 0)
197 goto usage;
198
199 /*
200 * Get the DISK FORMAT mode page. SCSI-2 recommends specifying the
201 * interleave read from this page in the FORMAT UNIT command.
202 */
203 scsi_mode_sense(fd, 0x03, 0x00, &data, sizeof(data));
204
205 memset(&cmd, 0, sizeof(cmd));
206
207 cmd.opcode = SCSI_FORMAT_UNIT;
208 memcpy(cmd.interleave, data.format_page.interleave,
209 sizeof(cmd.interleave));
210
211 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 60000, 0);
212
213 return;
214
215 usage:
216 fprintf(stderr, "usage: %s device %s\n", __progname, cmdname);
217 exit(1);
218 }
219
220 /*
221 * device_identify:
222 *
223 * Display the identity of the device, including it's SCSI bus,
224 * target, lun, and it's vendor/product/revision information.
225 */
226 void
227 device_identify(argc, argv)
228 int argc;
229 char *argv[];
230 {
231 struct scsipi_inquiry_data inqbuf;
232 struct scsipi_inquiry cmd;
233
234 /* x4 in case every character is escaped, +1 for NUL. */
235 char vendor[(sizeof(inqbuf.vendor) * 4) + 1],
236 product[(sizeof(inqbuf.product) * 4) + 1],
237 revision[(sizeof(inqbuf.revision) * 4) + 1];
238
239 /* No arguments. */
240 if (argc != 0)
241 goto usage;
242
243 memset(&cmd, 0, sizeof(cmd));
244 memset(&inqbuf, 0, sizeof(inqbuf));
245
246 cmd.opcode = INQUIRY;
247 cmd.length = sizeof(inqbuf);
248
249 scsi_command(fd, &cmd, sizeof(cmd), &inqbuf, sizeof(inqbuf),
250 10000, SCCMD_READ);
251
252 scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor,
253 sizeof(inqbuf.vendor));
254 scsi_strvis(product, sizeof(product), inqbuf.product,
255 sizeof(inqbuf.product));
256 scsi_strvis(revision, sizeof(revision), inqbuf.revision,
257 sizeof(inqbuf.revision));
258
259 printf("%s: scsibus%d target %d lun %d <%s, %s, %s>\n",
260 dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target,
261 dvaddr.addr.scsi.lun, vendor, product, revision);
262
263 return;
264
265 usage:
266 fprintf(stderr, "usage: %s device %s\n", __progname, cmdname);
267 exit(1);
268 }
269
270 /*
271 * device_reassign:
272 *
273 * Reassign bad blocks on a direct access device.
274 */
275 void
276 device_reassign(argc, argv)
277 int argc;
278 char *argv[];
279 {
280 struct scsi_reassign_blocks cmd;
281 struct scsi_reassign_blocks_data *data;
282 size_t dlen;
283 u_int32_t blkno;
284 int i;
285 char *cp;
286
287 /* We get a list of block numbers. */
288 if (argc < 1)
289 goto usage;
290
291 /*
292 * Allocate the reassign blocks descriptor. The 4 comes from the
293 * size of the block address in the defect descriptor.
294 */
295 dlen = sizeof(struct scsi_reassign_blocks_data) + ((argc - 1) * 4);
296 data = malloc(dlen);
297 if (data == NULL)
298 errx(1, "unable to allocate defect descriptor");
299 memset(data, 0, dlen);
300
301 cmd.opcode = SCSI_REASSIGN_BLOCKS;
302
303 /* Defect descriptor length. */
304 _lto2l(argc * 4, data->length);
305
306 /* Build the defect descriptor list. */
307 for (i = 0; i < argc; i++) {
308 blkno = strtoul(argv[i], &cp, 10);
309 if (*cp != '\0')
310 errx(1, "invalid block number: %s\n", argv[i]);
311 _lto4l(blkno, data->defect_descriptor[i].dlbaddr);
312 }
313
314 scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_WRITE);
315
316 free(data);
317 return;
318
319 usage:
320 fprintf(stderr, "usage: %s device %s blkno [blkno [...]]\n",
321 __progname, cmdname);
322 exit(1);
323 }
324
325 /*
326 * device_reset:
327 *
328 * Issue a reset to a SCSI device.
329 */
330 void
331 device_reset(argc, argv)
332 int argc;
333 char *argv[];
334 {
335
336 /* No arguments. */
337 if (argc != 0)
338 goto usage;
339
340 if (ioctl(fd, SCIOCRESET, NULL) != 0)
341 err(1, "SCIOCRESET");
342
343 return;
344
345 usage:
346 fprintf(stderr, "usage: %s device %s\n", __progname, cmdname);
347 exit(1);
348 }
349
350 /*
351 * BUS COMMANDS
352 */
353
354 /*
355 * bus_reset:
356 *
357 * Issue a reset to a SCSI bus.
358 */
359 void
360 bus_reset(argc, argv)
361 int argc;
362 char *argv[];
363 {
364
365 /* No arguments. */
366 if (argc != 0)
367 goto usage;
368
369 if (ioctl(fd, SCBUSIORESET, NULL) != 0)
370 err(1, "SCBUSIORESET");
371
372 return;
373
374 usage:
375 fprintf(stderr, "usage: %s device %s\n", __progname, cmdname);
376 exit(1);
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 goto usage;
395
396 if (strcmp(argv[0], "any") == 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)
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 usage:
418 fprintf(stderr, "usage: %s device %s target lun\n", __progname,
419 cmdname);
420 fprintf(stderr, " use `any' to wildcard target or lun\n");
421 exit(1);
422 }
423