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