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