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