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