scsictl.c revision 1.17 1 /* $NetBSD: scsictl.c,v 1.17 2002/07/20 08:36:28 grant 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_release __P((int, char *[]));
83 void device_reserve __P((int, char *[]));
84 void device_reset __P((int, char *[]));
85 void device_start __P((int, char *[]));
86 void device_stop __P((int, char *[]));
87 void device_tur __P((int, char *[]));
88
89 struct command device_commands[] = {
90 { "format", "[blocksize [immediate]]", device_format },
91 { "identify", "", device_identify },
92 { "reassign", "blkno [blkno [...]]", device_reassign },
93 { "release", "", device_release },
94 { "reserve", "", device_reserve },
95 { "reset", "", device_reset },
96 { "start", "", device_start },
97 { "stop", "", device_stop },
98 { "tur", "", device_tur },
99 { NULL, NULL, NULL },
100 };
101
102 void bus_reset __P((int, char *[]));
103 void bus_scan __P((int, char *[]));
104 void bus_detach __P((int, char *[]));
105
106 struct command bus_commands[] = {
107 { "reset", "", bus_reset },
108 { "scan", "target lun", bus_scan },
109 { "detach", "target lun", bus_detach },
110 { NULL, NULL, NULL },
111 };
112
113 int
114 main(argc, argv)
115 int argc;
116 char *argv[];
117 {
118 struct command *commands;
119 int i;
120
121 /* Must have at least: device command */
122 if (argc < 3)
123 usage();
124
125 /* Skip program name, get and skip device name and command. */
126 dvname = argv[1];
127 cmdname = argv[2];
128 argv += 3;
129 argc -= 3;
130
131 /*
132 * Open the device and determine if it's a scsibus or an actual
133 * device. Devices respond to the SCIOCIDENTIFY ioctl.
134 */
135 fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0);
136 if (fd == -1) {
137 if (errno == ENOENT) {
138 /*
139 * Device doesn't exist. Probably trying to open
140 * a device which doesn't use disk semantics for
141 * device name. Try again, specifying "cooked",
142 * which leaves off the "r" in front of the device's
143 * name.
144 */
145 fd = opendisk(dvname, O_RDWR, dvname_store,
146 sizeof(dvname_store), 1);
147 if (fd == -1)
148 err(1, "%s", dvname);
149 } else
150 err(1, "%s", dvname);
151 }
152
153 /*
154 * Point the dvname at the actual device name that opendisk() opened.
155 */
156 dvname = dvname_store;
157
158 if (ioctl(fd, SCIOCIDENTIFY, &dvaddr) < 0)
159 commands = bus_commands;
160 else
161 commands = device_commands;
162
163 /* Look up and call the command. */
164 for (i = 0; commands[i].cmd_name != NULL; i++)
165 if (strcmp(cmdname, commands[i].cmd_name) == 0)
166 break;
167 if (commands[i].cmd_name == NULL)
168 errx(1, "unknown %s command: %s",
169 commands == bus_commands ? "bus" : "device", cmdname);
170
171 argnames = commands[i].arg_names;
172
173 (*commands[i].cmd_func)(argc, argv);
174 exit(0);
175 }
176
177 void
178 usage()
179 {
180 int i;
181
182 fprintf(stderr, "Usage: %s device command [arg [...]]\n",
183 getprogname());
184
185 fprintf(stderr, " Commands pertaining to scsi devices:\n");
186 for (i=0; device_commands[i].cmd_name != NULL; i++)
187 fprintf(stderr, "\t%s %s\n", device_commands[i].cmd_name,
188 device_commands[i].arg_names);
189 fprintf(stderr, " Commands pertaining to scsi busses:\n");
190 for (i=0; bus_commands[i].cmd_name != NULL; i++)
191 fprintf(stderr, "\t%s %s\n", bus_commands[i].cmd_name,
192 bus_commands[i].arg_names);
193 fprintf(stderr, " Use `any' or `all' to wildcard target or lun\n");
194
195 exit(1);
196 }
197
198 /*
199 * DEVICE COMMANDS
200 */
201
202 /*
203 * device_format:
204 *
205 * Format a direct access device.
206 */
207 void
208 device_format(argc, argv)
209 int argc;
210 char *argv[];
211 {
212 u_int32_t blksize;
213 int i, j, immediate;
214 #define PC (65536/10)
215 static int complete[] = {
216 PC*1, PC*2, PC*3, PC*4, PC*5, PC*6, PC*7, PC*8, PC*9, 65536
217 };
218 char *cp, buffer[64];
219 struct scsipi_sense_data sense;
220 struct scsi_format_unit cmd;
221 struct {
222 struct scsi_format_unit_defect_list_header header;
223 /* optional initialization pattern */
224 /* optional defect list */
225 } dfl;
226 struct {
227 struct scsipi_mode_header header;
228 struct scsi_blk_desc blk_desc;
229 struct page_disk_format format_page;
230 } mode_page;
231 struct {
232 struct scsipi_mode_header header;
233 struct scsi_blk_desc blk_desc;
234 } data_select;
235
236
237 /* Blocksize is an optional argument. */
238 if (argc > 2)
239 usage();
240
241 /*
242 * Loop doing Request Sense to clear any pending Unit Attention.
243 *
244 * Multiple conditions may exist on the drive which are returned
245 * in priority order.
246 */
247 for (i = 0; i < 8; i++) {
248 scsi_request_sense(fd, &sense, sizeof (sense));
249 if ((j = sense.flags & SSD_KEY) == SKEY_NO_SENSE)
250 break;
251 }
252 /*
253 * Make sure we cleared any pending Unit Attention
254 */
255 if (j != SKEY_NO_SENSE) {
256 cp = scsi_decode_sense((const unsigned char *) &sense, 2,
257 buffer, sizeof (buffer));
258 errx(1, "failed to clean Unit Attention: %s", cp);
259 }
260
261 /*
262 * Get the DISK FORMAT mode page. SCSI-2 recommends specifying the
263 * interleave read from this page in the FORMAT UNIT command.
264 */
265 scsi_mode_sense(fd, 0x03, 0x00, &mode_page, sizeof(mode_page));
266
267 j = (mode_page.format_page.bytes_s[0] << 8) |
268 (mode_page.format_page.bytes_s[1]);
269
270 if (j != DEV_BSIZE)
271 printf("current disk sector size: %hd\n", j);
272
273 memset(&cmd, 0, sizeof(cmd));
274
275 cmd.opcode = SCSI_FORMAT_UNIT;
276 memcpy(cmd.interleave, mode_page.format_page.interleave,
277 sizeof(cmd.interleave));
278
279 /*
280 * The blocksize on the device is only changed if the user
281 * specified a new blocksize. If not specified the blocksize
282 * used for the device will be the Default value in the device.
283 * We don't specify the number of blocks since the format
284 * command will always reformat the entire drive. Also by
285 * not specifying a block count the drive will reset the
286 * block count to the maximum available after the format
287 * completes if the blocksize was changed in the format.
288 * Finally, the new disk geometry will not but updated on
289 * the drive in permanent storage until _AFTER_ the format
290 * completes successfully.
291 */
292 if (argc > 0) {
293 blksize = strtoul(argv[0], &cp, 10);
294 if (*cp != '\0')
295 errx(1, "invalid block size: %s", argv[0]);
296
297 memset(&data_select, 0, sizeof(data_select));
298
299 data_select.header.blk_desc_len = sizeof(struct scsi_blk_desc);
300 /*
301 * blklen in desc is 3 bytes with a leading reserved byte
302 */
303 _lto4b(blksize, &data_select.blk_desc.reserved);
304
305 /*
306 * Issue Mode Select to modify the device blocksize to be
307 * used on the Format. The modified device geometry will
308 * be stored as Current and Saved Page 3 parameters when
309 * the Format completes.
310 */
311 scsi_mode_select(fd, 0, &data_select, sizeof(data_select));
312
313 /*
314 * Since user specified a specific block size make sure it
315 * gets stored in the device when the format completes.
316 *
317 * Also scrub the defect list back to the manufacturers
318 * original.
319 */
320 cmd.flags = SFU_CMPLST | SFU_FMTDATA;
321 }
322
323 memset(&dfl, 0, sizeof(dfl));
324
325 if (argc > 1 && strncmp(argv[1], "imm", 3) == 0) {
326 /*
327 * Signal target for an immediate return from Format.
328 *
329 * We'll poll for completion status.
330 */
331 dfl.header.flags = DLH_IMMED;
332 immediate = 1;
333 } else {
334 immediate = 0;
335 }
336
337 scsi_command(fd, &cmd, sizeof(cmd), &dfl, sizeof(dfl),
338 8 * 60 * 60 * 1000, 0);
339
340 /*
341 * Poll device for completion of Format
342 */
343 if (immediate) {
344 i = 0;
345 printf("formatting.");
346 fflush(stdout);
347 do {
348 scsireq_t req;
349 struct scsipi_test_unit_ready tcmd;
350
351 memset(&tcmd, 0, sizeof(cmd));
352 tcmd.opcode = TEST_UNIT_READY;
353
354 memset(&req, 0, sizeof(req));
355 memcpy(req.cmd, &tcmd, 6);
356 req.cmdlen = 6;
357 req.timeout = 10000;
358 req.senselen = SENSEBUFLEN;
359
360 if (ioctl(fd, SCIOCCOMMAND, &req) == -1) {
361 err(1, "SCIOCCOMMAND");
362 }
363
364 if (req.retsts == SCCMD_OK) {
365 break;
366 } else if (req.retsts == SCCMD_TIMEOUT) {
367 fprintf(stderr, "%s: SCSI command timed out",
368 dvname);
369 break;
370 } else if (req.retsts == SCCMD_BUSY) {
371 fprintf(stderr, "%s: device is busy",
372 dvname);
373 break;
374 } else if (req.retsts != SCCMD_SENSE) {
375 fprintf(stderr,
376 "%s: device had unknown status %x", dvname,
377 req.retsts);
378 break;
379 }
380 memcpy(&sense, req.sense, SENSEBUFLEN);
381 if (sense.sense_key_spec_1 == SSD_SCS_VALID) {
382 j = (sense.sense_key_spec_2 << 8) |
383 (sense.sense_key_spec_3);
384 if (j >= complete[i]) {
385 printf(".%d0%%.", ++i);
386 fflush(stdout);
387 }
388 }
389 sleep(10);
390 } while ((sense.flags & SSD_KEY) == SKEY_NOT_READY);
391 printf(".100%%..done.\n");
392 }
393 return;
394 }
395
396 /*
397 * device_identify:
398 *
399 * Display the identity of the device, including it's SCSI bus,
400 * target, lun, and it's vendor/product/revision information.
401 */
402 void
403 device_identify(argc, argv)
404 int argc;
405 char *argv[];
406 {
407 struct scsipi_inquiry_data inqbuf;
408 struct scsipi_inquiry cmd;
409
410 /* x4 in case every character is escaped, +1 for NUL. */
411 char vendor[(sizeof(inqbuf.vendor) * 4) + 1],
412 product[(sizeof(inqbuf.product) * 4) + 1],
413 revision[(sizeof(inqbuf.revision) * 4) + 1];
414
415 /* No arguments. */
416 if (argc != 0)
417 usage();
418
419 memset(&cmd, 0, sizeof(cmd));
420 memset(&inqbuf, 0, sizeof(inqbuf));
421
422 cmd.opcode = INQUIRY;
423 cmd.length = sizeof(inqbuf);
424
425 scsi_command(fd, &cmd, sizeof(cmd), &inqbuf, sizeof(inqbuf),
426 10000, SCCMD_READ);
427
428 scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor,
429 sizeof(inqbuf.vendor));
430 scsi_strvis(product, sizeof(product), inqbuf.product,
431 sizeof(inqbuf.product));
432 scsi_strvis(revision, sizeof(revision), inqbuf.revision,
433 sizeof(inqbuf.revision));
434
435 printf("%s: scsibus%d target %d lun %d <%s, %s, %s>\n",
436 dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target,
437 dvaddr.addr.scsi.lun, vendor, product, revision);
438
439 return;
440 }
441
442 /*
443 * device_reassign:
444 *
445 * Reassign bad blocks on a direct access device.
446 */
447 void
448 device_reassign(argc, argv)
449 int argc;
450 char *argv[];
451 {
452 struct scsi_reassign_blocks cmd;
453 struct scsi_reassign_blocks_data *data;
454 size_t dlen;
455 u_int32_t blkno;
456 int i;
457 char *cp;
458
459 /* We get a list of block numbers. */
460 if (argc < 1)
461 usage();
462
463 /*
464 * Allocate the reassign blocks descriptor. The 4 comes from the
465 * size of the block address in the defect descriptor.
466 */
467 dlen = sizeof(struct scsi_reassign_blocks_data) + ((argc - 1) * 4);
468 data = malloc(dlen);
469 if (data == NULL)
470 errx(1, "unable to allocate defect descriptor");
471 memset(data, 0, dlen);
472
473 cmd.opcode = SCSI_REASSIGN_BLOCKS;
474 cmd.byte2 = 0;
475 cmd.unused[0] = 0;
476 cmd.unused[1] = 0;
477 cmd.unused[2] = 0;
478 cmd.control = 0;
479
480 /* Defect descriptor length. */
481 _lto2b(argc * 4, data->length);
482
483 /* Build the defect descriptor list. */
484 for (i = 0; i < argc; i++) {
485 blkno = strtoul(argv[i], &cp, 10);
486 if (*cp != '\0')
487 errx(1, "invalid block number: %s", argv[i]);
488 _lto4b(blkno, data->defect_descriptor[i].dlbaddr);
489 }
490
491 scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_WRITE);
492
493 free(data);
494 return;
495 }
496
497 /*
498 * device_release:
499 *
500 * Issue a RELEASE command to a SCSI drevice
501 */
502 #ifndef SCSI_RELEASE
503 #define SCSI_RELEASE 0x17
504 #endif
505 void
506 device_release(argc, argv)
507 int argc;
508 char *argv[];
509 {
510 struct scsipi_test_unit_ready cmd; /* close enough */
511
512 /* No arguments. */
513 if (argc != 0)
514 usage();
515
516 memset(&cmd, 0, sizeof(cmd));
517
518 cmd.opcode = SCSI_RELEASE;
519
520 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
521
522 return;
523 }
524
525
526
527 /*
528 * device_reserve:
529 *
530 * Issue a RESERVE command to a SCSI drevice
531 */
532 #ifndef SCSI_RESERVE
533 #define SCSI_RESERVE 0x16
534 #endif
535 void
536 device_reserve(argc, argv)
537 int argc;
538 char *argv[];
539 {
540 struct scsipi_test_unit_ready cmd; /* close enough */
541
542 /* No arguments. */
543 if (argc != 0)
544 usage();
545
546 memset(&cmd, 0, sizeof(cmd));
547
548 cmd.opcode = SCSI_RESERVE;
549
550 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
551
552 return;
553 }
554
555
556
557 /*
558 * device_reset:
559 *
560 * Issue a reset to a SCSI device.
561 */
562 void
563 device_reset(argc, argv)
564 int argc;
565 char *argv[];
566 {
567
568 /* No arguments. */
569 if (argc != 0)
570 usage();
571
572 if (ioctl(fd, SCIOCRESET, NULL) != 0)
573 err(1, "SCIOCRESET");
574
575 return;
576 }
577
578 /*
579 * BUS COMMANDS
580 */
581
582 /*
583 * bus_reset:
584 *
585 * Issue a reset to a SCSI bus.
586 */
587 void
588 bus_reset(argc, argv)
589 int argc;
590 char *argv[];
591 {
592
593 /* No arguments. */
594 if (argc != 0)
595 usage();
596
597 if (ioctl(fd, SCBUSIORESET, NULL) != 0)
598 err(1, "SCBUSIORESET");
599
600 return;
601 }
602
603 /*
604 * device_start:
605 *
606 * Issue a start to a SCSI device.
607 */
608 void
609 device_start(argc, argv)
610 int argc;
611 char *argv[];
612 {
613 struct scsipi_start_stop cmd;
614
615 /* No arguments. */
616 if (argc != 0)
617 usage();
618
619 memset(&cmd, 0, sizeof(cmd));
620
621 cmd.opcode = START_STOP;
622 cmd.how = SSS_START;
623
624 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
625
626 return;
627 }
628
629 /*
630 * device_stop:
631 *
632 * Issue a stop to a SCSI device.
633 */
634 void
635 device_stop(argc, argv)
636 int argc;
637 char *argv[];
638 {
639 struct scsipi_start_stop cmd;
640
641 /* No arguments. */
642 if (argc != 0)
643 usage();
644
645 memset(&cmd, 0, sizeof(cmd));
646
647 cmd.opcode = START_STOP;
648 cmd.how = SSS_STOP;
649
650 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
651
652 return;
653 }
654
655 /*
656 * device_tur:
657 *
658 * Issue a TEST UNIT READY to a SCSI drevice
659 */
660 void
661 device_tur(argc, argv)
662 int argc;
663 char *argv[];
664 {
665 struct scsipi_test_unit_ready cmd;
666
667 /* No arguments. */
668 if (argc != 0)
669 usage();
670
671 memset(&cmd, 0, sizeof(cmd));
672
673 cmd.opcode = TEST_UNIT_READY;
674
675 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
676
677 return;
678 }
679
680
681
682 /*
683 * bus_scan:
684 *
685 * Rescan a SCSI bus for new devices.
686 */
687 void
688 bus_scan(argc, argv)
689 int argc;
690 char *argv[];
691 {
692 struct scbusioscan_args args;
693 char *cp;
694
695 /* Must have two args: target lun */
696 if (argc != 2)
697 usage();
698
699 if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0)
700 args.sa_target = -1;
701 else {
702 args.sa_target = strtol(argv[0], &cp, 10);
703 if (*cp != '\0' || args.sa_target < 0)
704 errx(1, "invalid target: %s", argv[0]);
705 }
706
707 if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0)
708 args.sa_lun = -1;
709 else {
710 args.sa_lun = strtol(argv[1], &cp, 10);
711 if (*cp != '\0' || args.sa_lun < 0)
712 errx(1, "invalid lun: %s", argv[1]);
713 }
714
715 if (ioctl(fd, SCBUSIOSCAN, &args) != 0)
716 err(1, "SCBUSIOSCAN");
717
718 return;
719 }
720
721 /*
722 * bus_detach:
723 *
724 * detach SCSI devices from a bus.
725 */
726 void
727 bus_detach(argc, argv)
728 int argc;
729 char *argv[];
730 {
731 struct scbusiodetach_args args;
732 char *cp;
733
734 /* Must have two args: target lun */
735 if (argc != 2)
736 usage();
737
738 if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0)
739 args.sa_target = -1;
740 else {
741 args.sa_target = strtol(argv[0], &cp, 10);
742 if (*cp != '\0' || args.sa_target < 0)
743 errx(1, "invalid target: %s", argv[0]);
744 }
745
746 if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0)
747 args.sa_lun = -1;
748 else {
749 args.sa_lun = strtol(argv[1], &cp, 10);
750 if (*cp != '\0' || args.sa_lun < 0)
751 errx(1, "invalid lun: %s", argv[1]);
752 }
753
754 if (ioctl(fd, SCBUSIODETACH, &args) != 0)
755 err(1, "SCBUSIODETACH");
756
757 return;
758 }
759