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