scsictl.c revision 1.42 1 /* $NetBSD: scsictl.c,v 1.42 2024/11/10 01:55:06 riastradh 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 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 /*
34 * scsictl(8) - a program to manipulate SCSI devices and busses.
35 */
36 #include <sys/cdefs.h>
37
38 #ifndef lint
39 __RCSID("$NetBSD: scsictl.c,v 1.42 2024/11/10 01:55:06 riastradh Exp $");
40 #endif
41
42 #include <sys/param.h>
43 #include <sys/ioctl.h>
44 #include <sys/scsiio.h>
45 #include <err.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <limits.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <stdbool.h>
52 #include <string.h>
53 #include <unistd.h>
54 #include <util.h>
55
56 #include <dev/scsipi/scsi_spc.h>
57 #include <dev/scsipi/scsipi_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)(int, char *[]);
67 };
68
69 __dead static void usage(void);
70
71 static int fd; /* file descriptor for device */
72 const char *dvname; /* device name */
73 static char dvname_store[MAXPATHLEN]; /* for opendisk(3) */
74 static const char *cmdname; /* command user issued */
75 static struct scsi_addr dvaddr; /* SCSI device's address */
76
77 static void device_defects(int, char *[]);
78 static void device_format(int, char *[]);
79 static void device_identify(int, char *[]);
80 static void device_reassign(int, char *[]);
81 static void device_release(int, char *[]);
82 static void device_reserve(int, char *[]);
83 static void device_reset(int, char *[]);
84 static void device_debug(int, char *[]);
85 static void device_prevent(int, char *[]);
86 static void device_allow(int, char *[]);
87 static void device_start(int, char *[]);
88 static void device_stop(int, char *[]);
89 static void device_tur(int, char *[]);
90 static void device_getcache(int, char *[]);
91 static void device_setcache(int, char *[]);
92 static void device_flushcache(int, char *[]);
93 static void device_setspeed(int, char *[]);
94 static void device_getrealloc(int, char *[]);
95 static void device_setrealloc(int, char *[]);
96 static void device_reportluns(int, char *[]);
97
98 static struct command device_commands[] = {
99 { "defects", "[primary] [grown] [block|byte|physical]",
100 device_defects },
101 { "format", "[blocksize [immediate]]", device_format },
102 { "identify", "[vpd]", device_identify },
103 { "reassign", "blkno [blkno [...]]", device_reassign },
104 { "release", "", device_release },
105 { "reserve", "", device_reserve },
106 { "reset", "", device_reset },
107 { "debug", "level", device_debug },
108 { "prevent", "", device_prevent },
109 { "allow", "", device_allow },
110 { "start", "", device_start },
111 { "stop", "", device_stop },
112 { "tur", "", device_tur },
113 { "getcache", "", device_getcache },
114 { "setcache", "none|r|w|rw [save]", device_setcache },
115 { "flushcache", "", device_flushcache },
116 { "setspeed", "[speed]", device_setspeed },
117 { "getrealloc", "", device_getrealloc },
118 { "setrealloc", "none|r|w|rw [save]", device_setrealloc },
119 { "reportluns", "normal|wellknown|all|#", device_reportluns },
120 { NULL, NULL, NULL },
121 };
122
123 static void bus_reset(int, char *[]);
124 static void bus_scan(int, char *[]);
125 static void bus_detach(int, char *[]);
126
127 static struct command bus_commands[] = {
128 { "reset", "", bus_reset },
129 { "scan", "target lun", bus_scan },
130 { "detach", "target lun", bus_detach },
131 { NULL, NULL, NULL },
132 };
133
134 int
135 main(int argc, char *argv[])
136 {
137 struct command *commands;
138 int i;
139
140 /* Must have at least: device command */
141 if (argc < 3)
142 usage();
143
144 /* Skip program name, get and skip device name and command. */
145 dvname = argv[1];
146 cmdname = argv[2];
147 argv += 3;
148 argc -= 3;
149
150 /*
151 * Open the device and determine if it's a scsibus or an actual
152 * device. Devices respond to the SCIOCIDENTIFY ioctl.
153 */
154 fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0);
155 if (fd == -1) {
156 if (errno == ENOENT) {
157 /*
158 * Device doesn't exist. Probably trying to open
159 * a device which doesn't use disk semantics for
160 * device name. Try again, specifying "cooked",
161 * which leaves off the "r" in front of the device's
162 * name.
163 */
164 fd = opendisk(dvname, O_RDWR, dvname_store,
165 sizeof(dvname_store), 1);
166 if (fd == -1)
167 err(1, "%s", dvname);
168 } else
169 err(1, "%s", dvname);
170 }
171
172 /*
173 * Point the dvname at the actual device name that opendisk() opened.
174 */
175 dvname = dvname_store;
176
177 if (ioctl(fd, SCIOCIDENTIFY, &dvaddr) < 0)
178 commands = bus_commands;
179 else
180 commands = device_commands;
181
182 /* Look up and call the command. */
183 for (i = 0; commands[i].cmd_name != NULL; i++)
184 if (strcmp(cmdname, commands[i].cmd_name) == 0)
185 break;
186 if (commands[i].cmd_name == NULL)
187 errx(1, "unknown %s command: %s",
188 commands == bus_commands ? "bus" : "device", cmdname);
189
190 (*commands[i].cmd_func)(argc, argv);
191 exit(0);
192 }
193
194 static void
195 usage(void)
196 {
197 int i;
198
199 fprintf(stderr, "usage: %s device command [arg [...]]\n",
200 getprogname());
201
202 fprintf(stderr, " Commands pertaining to scsi devices:\n");
203 for (i = 0; device_commands[i].cmd_name != NULL; i++)
204 fprintf(stderr, "\t%s %s\n", device_commands[i].cmd_name,
205 device_commands[i].arg_names);
206 fprintf(stderr, " Commands pertaining to scsi busses:\n");
207 for (i = 0; bus_commands[i].cmd_name != NULL; i++)
208 fprintf(stderr, "\t%s %s\n", bus_commands[i].cmd_name,
209 bus_commands[i].arg_names);
210 fprintf(stderr, " Use `any' or `all' to wildcard target or lun\n");
211
212 exit(1);
213 }
214
215 /*
216 * DEVICE COMMANDS
217 */
218
219 /*
220 * device_read_defect:
221 *
222 * Read primary and/or growth defect list in physical or block
223 * format from a direct access device.
224 *
225 * XXX Does not handle very large defect lists. Needs SCSI3 12
226 * byte READ DEFECT DATA command.
227 */
228
229 static void print_bf_dd(union scsi_defect_descriptor *);
230 static void print_bfif_dd(union scsi_defect_descriptor *);
231 static void print_psf_dd(union scsi_defect_descriptor *);
232
233 static void
234 device_defects(int argc, char *argv[])
235 {
236 struct scsi_read_defect_data cmd;
237 struct scsi_read_defect_data_data *data;
238 size_t dlen;
239 int i, dlfmt = -1;
240 int defects;
241 char msg[256];
242 void (*pfunc)(union scsi_defect_descriptor *);
243 #define RDD_P_G_MASK 0x18
244 #define RDD_DLF_MASK 0x7
245
246 dlen = USHRT_MAX; /* XXX - this may not be enough room
247 * for all of the defects.
248 */
249 data = malloc(dlen);
250 if (data == NULL)
251 errx(1, "unable to allocate defect list");
252 memset(data, 0, dlen);
253 memset(&cmd, 0, sizeof(cmd));
254 defects = 0;
255 pfunc = NULL;
256
257 /* determine which defect list(s) to read. */
258 for (i = 0; i < argc; i++) {
259 if (strncmp("primary", argv[i], 7) == 0) {
260 cmd.flags |= RDD_PRIMARY;
261 continue;
262 }
263 if (strncmp("grown", argv[i], 5) == 0) {
264 cmd.flags |= RDD_GROWN;
265 continue;
266 }
267 break;
268 }
269
270 /* no defect list sepecified, assume both. */
271 if ((cmd.flags & (RDD_PRIMARY|RDD_GROWN)) == 0)
272 cmd.flags |= (RDD_PRIMARY|RDD_GROWN);
273
274 /* list format option. */
275 if (i < argc) {
276 if (strncmp("block", argv[i], 5) == 0) {
277 cmd.flags |= RDD_BF;
278 dlfmt = RDD_BF;
279 }
280 else if (strncmp("byte", argv[i], 4) == 0) {
281 cmd.flags |= RDD_BFIF;
282 dlfmt = RDD_BFIF;
283 }
284 else if (strncmp("physical", argv[i], 4) == 0) {
285 cmd.flags |= RDD_PSF;
286 dlfmt = RDD_PSF;
287 }
288 else {
289 usage();
290 }
291 }
292
293 /*
294 * no list format specified; since block format not
295 * recommended use physical sector format as default.
296 */
297 if (dlfmt < 0) {
298 cmd.flags |= RDD_PSF;
299 dlfmt = RDD_PSF;
300 }
301
302 cmd.opcode = SCSI_READ_DEFECT_DATA;
303 _lto2b(dlen, &cmd.length[0]);
304
305 scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_READ);
306
307 msg[0] = '\0';
308
309 /* is the defect list in the format asked for? */
310 if ((data->flags & RDD_DLF_MASK) != dlfmt) {
311 strcpy(msg, "\n\tnotice:"
312 "requested defect list format not supported by device\n\n");
313 dlfmt = (data->flags & RDD_DLF_MASK);
314 }
315
316 if (data->flags & RDD_PRIMARY)
317 strcat(msg, "primary");
318
319 if (data->flags & RDD_GROWN) {
320 if (data->flags & RDD_PRIMARY)
321 strcat(msg, " and ");
322 strcat(msg, "grown");
323 }
324
325 strcat(msg, " defects");
326
327 if ((data->flags & RDD_P_G_MASK) == 0)
328 strcat(msg, ": none reported\n");
329
330 printf("%s: scsibus%d target %d lun %d %s",
331 dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target,
332 dvaddr.addr.scsi.lun, msg);
333
334 /* device did not return either defect list. */
335 if ((data->flags & RDD_P_G_MASK) == 0)
336 return;
337
338 switch (dlfmt) {
339 case RDD_BF:
340 defects = _2btol(data->length) /
341 sizeof(struct scsi_defect_descriptor_bf);
342 pfunc = print_bf_dd;
343 strcpy(msg, "block address\n"
344 "-------------\n");
345 break;
346 case RDD_BFIF:
347 defects = _2btol(data->length) /
348 sizeof(struct scsi_defect_descriptor_bfif);
349 pfunc = print_bfif_dd;
350 strcpy(msg, " bytes from\n"
351 "cylinder head index\n"
352 "-------- ---- ----------\n");
353 break;
354 case RDD_PSF:
355 defects = _2btol(data->length) /
356 sizeof(struct scsi_defect_descriptor_psf);
357 pfunc = print_psf_dd;
358 strcpy(msg, "cylinder head sector\n"
359 "-------- ---- ----------\n");
360 break;
361 }
362
363 /* device did not return any defects. */
364 if (defects == 0) {
365 printf(": none\n");
366 return;
367 }
368
369 printf(": %d\n", defects);
370
371 /* print heading. */
372 printf("%s", msg);
373
374 /* print defect list. */
375 for (i = 0 ; i < defects; i++) {
376 pfunc(&data->defect_descriptor[i]);
377 }
378
379 free(data);
380 return;
381 }
382
383 /*
384 * print_bf_dd:
385 *
386 * Print a block format defect descriptor.
387 */
388 static void
389 print_bf_dd(union scsi_defect_descriptor *dd)
390 {
391 u_int32_t block;
392
393 block = _4btol(dd->bf.block_address);
394
395 printf("%13u\n", block);
396 }
397
398 #define DEFECTIVE_TRACK 0xffffffff
399
400 /*
401 * print_bfif_dd:
402 *
403 * Print a bytes from index format defect descriptor.
404 */
405 static void
406 print_bfif_dd(union scsi_defect_descriptor *dd)
407 {
408 u_int32_t cylinder;
409 u_int32_t head;
410 u_int32_t bytes_from_index;
411
412 cylinder = _3btol(dd->bfif.cylinder);
413 head = dd->bfif.head;
414 bytes_from_index = _4btol(dd->bfif.bytes_from_index);
415
416 printf("%8u %4u ", cylinder, head);
417
418 if (bytes_from_index == DEFECTIVE_TRACK)
419 printf("entire track defective\n");
420 else
421 printf("%10u\n", bytes_from_index);
422 }
423
424 /*
425 * print_psf_dd:
426 *
427 * Print a physical sector format defect descriptor.
428 */
429 static void
430 print_psf_dd(union scsi_defect_descriptor *dd)
431 {
432 u_int32_t cylinder;
433 u_int32_t head;
434 u_int32_t sector;
435
436 cylinder = _3btol(dd->psf.cylinder);
437 head = dd->psf.head;
438 sector = _4btol(dd->psf.sector);
439
440 printf("%8u %4u ", cylinder, head);
441
442 if (sector == DEFECTIVE_TRACK)
443 printf("entire track defective\n");
444 else
445 printf("%10u\n", sector);
446 }
447
448 /*
449 * device_format:
450 *
451 * Format a direct access device.
452 */
453 static void
454 device_format(int argc, char *argv[])
455 {
456 u_int32_t blksize;
457 int i, j, immediate;
458 #define PC (65536/10)
459 static int complete[] = {
460 PC*1, PC*2, PC*3, PC*4, PC*5, PC*6, PC*7, PC*8, PC*9, 65536
461 };
462 char *cp, buffer[64];
463 struct scsi_sense_data sense;
464 struct scsi_format_unit cmd;
465 struct {
466 struct scsi_format_unit_defect_list_header header;
467 /* optional initialization pattern */
468 /* optional defect list */
469 } dfl;
470 struct {
471 struct scsi_mode_parameter_header_6 header;
472 struct scsi_general_block_descriptor blk_desc;
473 struct page_disk_format format_page;
474 } mode_page;
475 struct {
476 struct scsi_mode_parameter_header_6 header;
477 struct scsi_general_block_descriptor blk_desc;
478 } data_select;
479
480 /* Blocksize is an optional argument. */
481 if (argc > 2)
482 usage();
483
484 /*
485 * Loop doing Request Sense to clear any pending Unit Attention.
486 *
487 * Multiple conditions may exist on the drive which are returned
488 * in priority order.
489 */
490 for (i = 0; i < 8; i++) {
491 scsi_request_sense(fd, &sense, sizeof (sense));
492 if ((j = SSD_SENSE_KEY(sense.flags)) == SKEY_NO_SENSE)
493 break;
494 }
495 /*
496 * Make sure we cleared any pending Unit Attention
497 */
498 if (j != SKEY_NO_SENSE) {
499 cp = scsi_decode_sense((const unsigned char *) &sense, 2,
500 buffer, sizeof (buffer));
501 errx(1, "failed to clean Unit Attention: %s", cp);
502 }
503
504 /*
505 * Get the DISK FORMAT mode page. SCSI-2 recommends specifying the
506 * interleave read from this page in the FORMAT UNIT command.
507 */
508 scsi_mode_sense(fd, 0x03, 0x00, &mode_page, sizeof(mode_page));
509
510 j = (mode_page.format_page.bytes_s[0] << 8) |
511 (mode_page.format_page.bytes_s[1]);
512
513 if (j != DEV_BSIZE)
514 printf("current disk sector size: %d\n", j);
515
516 memset(&cmd, 0, sizeof(cmd));
517
518 cmd.opcode = SCSI_FORMAT_UNIT;
519 memcpy(cmd.interleave, mode_page.format_page.interleave,
520 sizeof(cmd.interleave));
521
522 /*
523 * The blocksize on the device is only changed if the user
524 * specified a new blocksize. If not specified the blocksize
525 * used for the device will be the Default value in the device.
526 * We don't specify the number of blocks since the format
527 * command will always reformat the entire drive. Also by
528 * not specifying a block count the drive will reset the
529 * block count to the maximum available after the format
530 * completes if the blocksize was changed in the format.
531 * Finally, the new disk geometry will not but updated on
532 * the drive in permanent storage until _AFTER_ the format
533 * completes successfully.
534 */
535 if (argc > 0) {
536 blksize = strtoul(argv[0], &cp, 10);
537 if (*cp != '\0')
538 errx(1, "invalid block size: %s", argv[0]);
539
540 memset(&data_select, 0, sizeof(data_select));
541
542 data_select.header.blk_desc_len =
543 sizeof(struct scsi_general_block_descriptor);
544 /*
545 * blklen in desc is 3 bytes with a leading reserved byte
546 */
547 _lto4b(blksize, &data_select.blk_desc.reserved);
548
549 /*
550 * Issue Mode Select to modify the device blocksize to be
551 * used on the Format. The modified device geometry will
552 * be stored as Current and Saved Page 3 parameters when
553 * the Format completes.
554 */
555 scsi_mode_select(fd, 0, &data_select, sizeof(data_select));
556
557 /*
558 * Since user specified a specific block size make sure it
559 * gets stored in the device when the format completes.
560 *
561 * Also scrub the defect list back to the manufacturers
562 * original.
563 */
564 cmd.flags = SFU_CMPLST | SFU_FMTDATA;
565 }
566
567 memset(&dfl, 0, sizeof(dfl));
568
569 if (argc > 1 && strncmp(argv[1], "imm", 3) == 0) {
570 /*
571 * Signal target for an immediate return from Format.
572 *
573 * We'll poll for completion status.
574 */
575 dfl.header.flags = DLH_IMMED;
576 immediate = 1;
577 } else {
578 immediate = 0;
579 }
580
581 scsi_command(fd, &cmd, sizeof(cmd), &dfl, sizeof(dfl),
582 8 * 60 * 60 * 1000, SCCMD_WRITE);
583
584 /*
585 * Poll device for completion of Format
586 */
587 if (immediate) {
588 i = 0;
589 printf("formatting.");
590 fflush(stdout);
591 do {
592 scsireq_t req;
593 struct scsi_test_unit_ready tcmd;
594
595 memset(&tcmd, 0, sizeof(tcmd));
596 tcmd.opcode = SCSI_TEST_UNIT_READY;
597
598 memset(&req, 0, sizeof(req));
599 memcpy(req.cmd, &tcmd, 6);
600 req.cmdlen = 6;
601 req.timeout = 10000;
602 req.senselen = SENSEBUFLEN;
603
604 if (ioctl(fd, SCIOCCOMMAND, &req) == -1) {
605 err(1, "SCIOCCOMMAND");
606 }
607
608 if (req.retsts == SCCMD_OK) {
609 break;
610 } else if (req.retsts == SCCMD_TIMEOUT) {
611 fprintf(stderr, "%s: SCSI command timed out",
612 dvname);
613 break;
614 } else if (req.retsts == SCCMD_BUSY) {
615 fprintf(stderr, "%s: device is busy",
616 dvname);
617 break;
618 } else if (req.retsts != SCCMD_SENSE) {
619 fprintf(stderr,
620 "%s: device had unknown status %x", dvname,
621 req.retsts);
622 break;
623 }
624 memcpy(&sense, req.sense, sizeof(sense));
625 if (sense.sks.sks_bytes[0] & SSD_SKSV) {
626 j = (sense.sks.sks_bytes[1] << 8) |
627 (sense.sks.sks_bytes[2]);
628 if (j >= complete[i]) {
629 printf(".%d0%%.", ++i);
630 fflush(stdout);
631 }
632 }
633 sleep(10);
634 } while (SSD_SENSE_KEY(sense.flags) == SKEY_NOT_READY);
635 printf(".100%%..done.\n");
636 }
637 return;
638 }
639
640 static void
641 print_designator(const char *pre, struct scsipi_inquiry_evpd_device_id *did)
642 {
643 char buf[252 * 4 + 1];
644 unsigned assoc, proto, code, type;
645 static const char *typestr[] = {
646 "vendor",
647 "t10",
648 "eui64",
649 "naa",
650 "target port",
651 "port group",
652 "lun group",
653 "md5",
654 "scsi",
655 "res9",
656 "res10",
657 "res11",
658 "res12",
659 "res13",
660 "res14",
661 "res15"
662 };
663 static const char *assocstr[] = {
664 "lun",
665 "port",
666 "target",
667 "reserved"
668 };
669 static const char *protostr[] = {
670 "fibre channel",
671 "obsolete",
672 "ssa",
673 "ieee1394",
674 "rdma",
675 "iSCSI",
676 "SAS"
677 };
678 const unsigned maxproto = __arraycount(protostr) - 1;
679 const unsigned isbinary =
680 __SHIFTOUT(SINQ_DEVICE_ID_CODESET_BINARY, SINQ_DEVICE_ID_CODESET);
681 unsigned k;
682
683 assoc = __SHIFTOUT(did->flags, SINQ_DEVICE_ID_ASSOCIATION);
684 proto = __SHIFTOUT(did->pc, SINQ_DEVICE_ID_PROTOCOL);
685 code = __SHIFTOUT(did->pc, SINQ_DEVICE_ID_CODESET);
686 type = __SHIFTOUT(did->flags, SINQ_DEVICE_ID_TYPE);
687
688 printf("%s%s", pre, assocstr[assoc]);
689 if (did->flags & SINQ_DEVICE_ID_PIV) {
690 if (proto > maxproto)
691 printf(" proto%u", proto);
692 else
693 printf(" %s", protostr[proto]);
694 }
695 printf(" %s: ", typestr[type]);
696
697 if (code == isbinary) {
698 for (k = 0; k < did->designator_length; k++) {
699 printf("%02x", did->designator[k]);
700 }
701 printf("\n");
702 } else {
703 scsi_strvis(buf, sizeof(buf), (char *)did->designator,
704 did->designator_length);
705 printf("%s\n", buf);
706 }
707 }
708
709 /*
710 * device_identify:
711 *
712 * Display the identity of the device, including its SCSI bus,
713 * target, lun, and its vendor/product/revision information.
714 * Optionally query and display vpd identification data.
715 */
716 static void
717 device_identify(int argc, char *argv[])
718 {
719 struct scsipi_inquiry_data inqbuf;
720 struct {
721 struct scsipi_inquiry_evpd_header h;
722 uint8_t d[255 - sizeof(struct scsipi_inquiry_evpd_header)];
723 } evpdbuf;
724 struct scsipi_inquiry cmd;
725 unsigned len, rlen;
726 struct scsipi_inquiry_evpd_serial *ser;
727 struct scsipi_inquiry_evpd_device_id *did;
728 int has_serial;
729 int has_device_id;
730 bool getvpd = false;
731 int i;
732
733 /* x4 in case every character is escaped, +1 for NUL. */
734 char vendor[(sizeof(inqbuf.vendor) * 4) + 1],
735 product[(sizeof(inqbuf.product) * 4) + 1],
736 revision[(sizeof(inqbuf.revision) * 4) + 1],
737 ident[252 * 4 + 1];
738
739 /* Check optional arguments */
740 for (i = 0; i < argc; i++) {
741 if (strncmp("vpd", argv[i], 3) == 0) {
742 getvpd = true;
743 continue;
744 }
745 usage();
746 }
747
748 memset(&cmd, 0, sizeof(cmd));
749 memset(&inqbuf, 0, sizeof(inqbuf));
750
751 cmd.opcode = INQUIRY;
752 cmd.length = sizeof(inqbuf);
753
754 scsi_command(fd, &cmd, sizeof(cmd), &inqbuf, sizeof(inqbuf),
755 10000, SCCMD_READ);
756
757 scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor,
758 sizeof(inqbuf.vendor));
759 scsi_strvis(product, sizeof(product), inqbuf.product,
760 sizeof(inqbuf.product));
761 scsi_strvis(revision, sizeof(revision), inqbuf.revision,
762 sizeof(inqbuf.revision));
763
764 printf("%s: scsibus%d target %d lun %d <%s, %s, %s>\n",
765 dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target,
766 dvaddr.addr.scsi.lun, vendor, product, revision);
767
768 if (!getvpd)
769 return;
770
771 cmd.byte2 |= SINQ_EVPD;
772 cmd.pagecode = SINQ_VPD_PAGES;
773
774 scsi_command(fd, &cmd, sizeof(cmd), &evpdbuf, sizeof(evpdbuf),
775 10000, SCCMD_READ);
776
777 len = be16dec(evpdbuf.h.length);
778 if (len > sizeof(evpdbuf.d))
779 len = 0;
780
781 has_serial = memchr(evpdbuf.d, SINQ_VPD_SERIAL, len) != NULL;
782 has_device_id = memchr(evpdbuf.d, SINQ_VPD_DEVICE_ID, len) != NULL;
783
784 if (has_serial) {
785 cmd.byte2 |= SINQ_EVPD;
786 cmd.pagecode = SINQ_VPD_SERIAL;
787
788 scsi_command(fd, &cmd, sizeof(cmd), &evpdbuf, sizeof(evpdbuf),
789 10000, SCCMD_READ);
790
791 len = be16dec(evpdbuf.h.length);
792 if (len > sizeof(evpdbuf.d))
793 len = 0;
794
795 ser = (struct scsipi_inquiry_evpd_serial *)&evpdbuf.d;
796 scsi_strvis(ident, sizeof(ident), (char *)ser->serial_number,
797 len);
798 printf("VPD Serial:\n");
799 printf("\t%s\n", ident);
800 }
801
802 if (has_device_id) {
803 cmd.byte2 |= SINQ_EVPD;
804 cmd.pagecode = SINQ_VPD_DEVICE_ID;
805
806 scsi_command(fd, &cmd, sizeof(cmd), &evpdbuf, sizeof(evpdbuf),
807 10000, SCCMD_READ);
808
809 len = be16dec(evpdbuf.h.length);
810 if (len > sizeof(evpdbuf.d))
811 len = 0;
812
813 printf("VPD Device IDs:\n");
814
815 for (unsigned off = 0; off < len - sizeof(*did); off += rlen) {
816 void *p = &evpdbuf.d[off];
817 did = (struct scsipi_inquiry_evpd_device_id *)p;
818 rlen = sizeof(*did) + did->designator_length - 1;
819 if (off + rlen > len)
820 break;
821
822 print_designator("\t", did);
823 }
824 }
825
826 return;
827 }
828
829 /*
830 * device_reassign:
831 *
832 * Reassign bad blocks on a direct access device.
833 */
834 static void
835 device_reassign(int argc, char *argv[])
836 {
837 struct scsi_reassign_blocks cmd;
838 struct scsi_reassign_blocks_data *data;
839 size_t dlen;
840 u_int32_t blkno;
841 int i;
842 char *cp;
843
844 /* We get a list of block numbers. */
845 if (argc < 1)
846 usage();
847
848 /*
849 * Allocate the reassign blocks descriptor. The 4 comes from the
850 * size of the block address in the defect descriptor.
851 */
852 dlen = sizeof(struct scsi_reassign_blocks_data) + ((argc - 1) * 4);
853 data = malloc(dlen);
854 if (data == NULL)
855 errx(1, "unable to allocate defect descriptor");
856 memset(data, 0, dlen);
857
858 cmd.opcode = SCSI_REASSIGN_BLOCKS;
859 cmd.byte2 = 0;
860 cmd.unused[0] = 0;
861 cmd.unused[1] = 0;
862 cmd.unused[2] = 0;
863 cmd.control = 0;
864
865 /* Defect descriptor length. */
866 _lto2b(argc * 4, data->length);
867
868 /* Build the defect descriptor list. */
869 for (i = 0; i < argc; i++) {
870 blkno = strtoul(argv[i], &cp, 10);
871 if (*cp != '\0')
872 errx(1, "invalid block number: %s", argv[i]);
873 _lto4b(blkno, data->defect_descriptor[i].dlbaddr);
874 }
875
876 scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_WRITE);
877
878 free(data);
879 return;
880 }
881
882 /*
883 * device_release:
884 *
885 * Issue a RELEASE command to a SCSI device.
886 */
887 #ifndef SCSI_RELEASE
888 #define SCSI_RELEASE 0x17
889 #endif
890 static void
891 device_release(int argc, char *argv[])
892 {
893 struct scsi_test_unit_ready cmd; /* close enough */
894
895 /* No arguments. */
896 if (argc != 0)
897 usage();
898
899 memset(&cmd, 0, sizeof(cmd));
900
901 cmd.opcode = SCSI_RELEASE;
902
903 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
904
905 return;
906 }
907
908 /*
909 * device_reserve:
910 *
911 * Issue a RESERVE command to a SCSI device.
912 */
913 #ifndef SCSI_RESERVE
914 #define SCSI_RESERVE 0x16
915 #endif
916 static void
917 device_reserve(int argc, char *argv[])
918 {
919 struct scsi_test_unit_ready cmd; /* close enough */
920
921 /* No arguments. */
922 if (argc != 0)
923 usage();
924
925 memset(&cmd, 0, sizeof(cmd));
926
927 cmd.opcode = SCSI_RESERVE;
928
929 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
930
931 return;
932 }
933
934 /*
935 * device_reset:
936 *
937 * Issue a reset to a SCSI device.
938 */
939 static void
940 device_reset(int argc, char *argv[])
941 {
942
943 /* No arguments. */
944 if (argc != 0)
945 usage();
946
947 if (ioctl(fd, SCIOCRESET, NULL) != 0)
948 err(1, "SCIOCRESET");
949
950 return;
951 }
952
953 /*
954 * device_debug:
955 *
956 * Set debug level to a SCSI device.
957 * scsipi will print anything iff SCSIPI_DEBUG set in config.
958 */
959 static void
960 device_debug(int argc, char *argv[])
961 {
962 int lvl;
963
964 if (argc < 1)
965 usage();
966
967 lvl = atoi(argv[0]);
968
969 if (ioctl(fd, SCIOCDEBUG, &lvl) != 0)
970 err(1, "SCIOCDEBUG");
971
972 return;
973 }
974
975 /*
976 * device_getcache:
977 *
978 * Get the caching parameters for a SCSI disk.
979 */
980 static void
981 device_getcache(int argc, char *argv[])
982 {
983 struct {
984 struct scsi_mode_parameter_header_6 header;
985 struct scsi_general_block_descriptor blk_desc;
986 struct page_caching caching_params;
987 } data;
988
989 /* No arguments. */
990 if (argc != 0)
991 usage();
992
993 scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data));
994
995 if ((data.caching_params.flags & (CACHING_RCD|CACHING_WCE)) ==
996 CACHING_RCD)
997 printf("%s: no caches enabled\n", dvname);
998 else {
999 printf("%s: read cache %senabled\n", dvname,
1000 (data.caching_params.flags & CACHING_RCD) ? "not " : "");
1001 printf("%s: write-back cache %senabled\n", dvname,
1002 (data.caching_params.flags & CACHING_WCE) ? "" : "not ");
1003 }
1004 printf("%s: caching parameters are %ssavable\n", dvname,
1005 (data.caching_params.pg_code & PGCODE_PS) ? "" : "not ");
1006 }
1007
1008 /*
1009 * device_setcache:
1010 *
1011 * Set cache enables for a SCSI disk.
1012 */
1013 static void
1014 device_setcache(int argc, char *argv[])
1015 {
1016 struct {
1017 struct scsi_mode_parameter_header_6 header;
1018 struct scsi_general_block_descriptor blk_desc;
1019 struct page_caching caching_params;
1020 } data;
1021 int dlen;
1022 u_int8_t flags, byte2;
1023
1024 if (argc > 2 || argc == 0)
1025 usage();
1026
1027 flags = 0;
1028 byte2 = 0;
1029 if (strcmp(argv[0], "none") == 0)
1030 flags = CACHING_RCD;
1031 else if (strcmp(argv[0], "r") == 0)
1032 flags = 0;
1033 else if (strcmp(argv[0], "w") == 0)
1034 flags = CACHING_RCD|CACHING_WCE;
1035 else if (strcmp(argv[0], "rw") == 0)
1036 flags = CACHING_WCE;
1037 else
1038 usage();
1039
1040 if (argc == 2) {
1041 if (strcmp(argv[1], "save") == 0)
1042 byte2 = SMS_SP;
1043 else
1044 usage();
1045 }
1046
1047 scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data));
1048
1049 data.caching_params.pg_code &= PGCODE_MASK;
1050 data.caching_params.flags =
1051 (data.caching_params.flags & ~(CACHING_RCD|CACHING_WCE)) | flags;
1052
1053 data.caching_params.cache_segment_size[0] = 0;
1054 data.caching_params.cache_segment_size[1] = 0;
1055
1056 data.header.data_length = 0;
1057
1058 dlen = sizeof(data.header) + sizeof(data.blk_desc) + 2 +
1059 data.caching_params.pg_length;
1060
1061 scsi_mode_select(fd, byte2, &data, dlen);
1062 }
1063
1064 /*
1065 * device_flushcache:
1066 *
1067 * Issue a FLUSH CACHE command to a SCSI device.
1068 */
1069 #ifndef SCSI_FLUSHCACHE
1070 #define SCSI_FLUSHCACHE 0x35
1071 #endif
1072 static void
1073 device_flushcache(int argc, char *argv[])
1074 {
1075 struct scsi_test_unit_ready cmd; /* close enough */
1076
1077 /* No arguments. */
1078 if (argc != 0)
1079 usage();
1080
1081 memset(&cmd, 0, sizeof(cmd));
1082
1083 cmd.opcode = SCSI_FLUSHCACHE;
1084
1085 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
1086
1087 return;
1088 }
1089
1090 /*
1091 * device_setspeed:
1092 *
1093 * Set rotation speed to a CD/DVD drive.
1094 */
1095 static void
1096 device_setspeed(int argc, char *argv[])
1097 {
1098 u_char cmd[11];
1099 u_char pd[28];
1100 u_int32_t speed;
1101
1102 if (argc != 1)
1103 usage();
1104
1105 speed = atoi(argv[0]) * 177;
1106
1107 memset(&pd, 0, sizeof(pd));
1108 if (speed == 0)
1109 pd[0] = 4; /* restore drive defaults */
1110 pd[8] = 0xff;
1111 pd[9] = 0xff;
1112 pd[10] = 0xff;
1113 pd[11] = 0xff;
1114 pd[12] = pd[20] = (speed >> 24) & 0xff;
1115 pd[13] = pd[21] = (speed >> 16) & 0xff;
1116 pd[14] = pd[22] = (speed >> 8) & 0xff;
1117 pd[15] = pd[23] = speed & 0xff;
1118 pd[18] = pd[26] = 1000 >> 8;
1119 pd[19] = pd[27] = 1000 & 0xff;
1120
1121 memset(&cmd, 0, sizeof(cmd));
1122 cmd[0] = 0xb6;
1123 cmd[10] = sizeof(pd);
1124
1125 scsi_command(fd, &cmd, sizeof(cmd), pd, sizeof(pd), 10000, SCCMD_WRITE);
1126
1127 return;
1128 }
1129
1130 /*
1131 * device_reportluns:
1132 *
1133 * Report the known LUNs to which the initiator can send commands
1134 */
1135 static void
1136 device_reportluns(int argc, char *argv[])
1137 {
1138 struct scsi_report_luns cmd;
1139 struct {
1140 struct scsi_report_luns_header header;
1141 struct scsi_report_luns_lun desc[1];
1142 } *data;
1143 u_int32_t dlen, len;
1144 u_int64_t lun;
1145 size_t count, idx;
1146 unsigned long sel;
1147 char *endp;
1148 int i;
1149
1150 dlen = USHRT_MAX; /* good for > 8000 LUNs */
1151 data = malloc(dlen);
1152 if (data == NULL)
1153 errx(1, "unable to allocate lun report");
1154
1155 memset(&cmd, 0, sizeof(cmd));
1156 cmd.opcode = SCSI_REPORT_LUNS;
1157 cmd.selectreport = SELECTREPORT_NORMAL;
1158
1159 /* determine which report to read. */
1160 for (i = 0; i < argc; i++) {
1161 if (strcmp("normal", argv[i]) == 0) {
1162 cmd.selectreport = SELECTREPORT_NORMAL;
1163 continue;
1164 }
1165 if (strcmp("wellknown", argv[i]) == 0) {
1166 cmd.selectreport = SELECTREPORT_WELLKNOWN;
1167 continue;
1168 }
1169 if (strcmp("all", argv[i]) == 0) {
1170 cmd.selectreport = SELECTREPORT_ALL;
1171 continue;
1172 }
1173 sel = strtoul(argv[i], &endp, 0);
1174 if (*endp != '\0' || sel > 255)
1175 errx(1, "Unknown select report '%s'", argv[i]);
1176 cmd.selectreport = sel;
1177 }
1178
1179 _lto4b(dlen, &cmd.alloclen[0]);
1180 cmd.control = 0x00;
1181
1182 scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_READ);
1183
1184 len = _4btol(data->header.length);
1185 if (len > dlen) {
1186 /* XXX reallocate and retry */
1187 printf("%s: report truncated %" PRIu32 "to %" PRIu32 "\n",
1188 dvname, len, dlen);
1189 len = dlen;
1190 }
1191
1192 count = len / sizeof(data->desc[0]);
1193
1194 for (idx = 0; idx < count; idx++) {
1195 lun = _8btol(data->desc[idx].lun);
1196
1197 /*
1198 * swizzle bits so that LUNs 0..255 are
1199 * mapped to numbers 0..255
1200 */
1201 lun = (lun & 0xffff000000000000ull) >> 48
1202 | (lun & 0x0000ffff00000000ull) >> 16
1203 | (lun & 0x00000000ffff0000ull) << 16
1204 | (lun & 0x000000000000ffffull) << 48;
1205
1206 printf("%s: lun %" PRIu64 "\n", dvname, lun);
1207 }
1208
1209 free(data);
1210 }
1211
1212 /*
1213 * device_getrealloc:
1214 *
1215 * Get the automatic reallocation parameters for a SCSI disk.
1216 */
1217 static void
1218 device_getrealloc(int argc, char *argv[])
1219 {
1220 struct {
1221 struct scsi_mode_parameter_header_6 header;
1222 struct scsi_general_block_descriptor blk_desc;
1223 struct page_err_recov err_recov_params;
1224 } data;
1225 u_int8_t flags;
1226
1227 /* No arguments. */
1228 if (argc != 0)
1229 usage();
1230
1231 scsi_mode_sense(fd, 0x01, 0x00, &data, sizeof(data));
1232
1233 flags = data.err_recov_params.flags;
1234 if ((flags & (ERR_RECOV_ARRE | ERR_RECOV_AWRE)) == 0)
1235 printf("%s: no automatic reallocation enabled\n", dvname);
1236 else {
1237 printf("%s: automatic read reallocation %senabled\n", dvname,
1238 (flags & ERR_RECOV_ARRE) ? "" : "not ");
1239 printf("%s: automatic write reallocation %senabled\n", dvname,
1240 (flags & ERR_RECOV_AWRE) ? "" : "not ");
1241 }
1242 printf("%s: error recovery parameters are %ssavable\n", dvname,
1243 (data.err_recov_params.pg_code & PGCODE_PS) ? "" : "not ");
1244 }
1245
1246 /*
1247 * device_setrealloc:
1248 *
1249 * Set the automatic reallocation parameters for a SCSI disk.
1250 */
1251 static void
1252 device_setrealloc(int argc, char *argv[])
1253 {
1254 struct {
1255 struct scsi_mode_parameter_header_6 header;
1256 struct scsi_general_block_descriptor blk_desc;
1257 struct page_err_recov err_recov_params;
1258 } data;
1259 int dlen;
1260 u_int8_t flags, byte2;
1261
1262 if (argc > 2 || argc == 0)
1263 usage();
1264
1265 flags = 0;
1266 byte2 = 0;
1267 if (strcmp(argv[0], "none") == 0)
1268 flags = 0;
1269 else if (strcmp(argv[0], "r") == 0)
1270 flags = ERR_RECOV_ARRE;
1271 else if (strcmp(argv[0], "w") == 0)
1272 flags = ERR_RECOV_AWRE;
1273 else if (strcmp(argv[0], "rw") == 0)
1274 flags = ERR_RECOV_ARRE | ERR_RECOV_AWRE;
1275 else
1276 usage();
1277
1278 if (argc == 2) {
1279 if (strcmp(argv[1], "save") == 0)
1280 byte2 = SMS_SP;
1281 else
1282 usage();
1283 }
1284
1285 scsi_mode_sense(fd, 0x01, 0x00, &data, sizeof(data));
1286
1287 data.err_recov_params.pg_code &= PGCODE_MASK;
1288 data.err_recov_params.flags &= ~(ERR_RECOV_ARRE | ERR_RECOV_AWRE);
1289 data.err_recov_params.flags |= flags;
1290
1291 data.header.data_length = 0;
1292
1293 dlen = sizeof(data.header) + sizeof(data.blk_desc) + 2 +
1294 data.err_recov_params.pg_length;
1295
1296 scsi_mode_select(fd, byte2, &data, dlen);
1297 }
1298
1299 /*
1300 * device_prevent:
1301 *
1302 * Issue a prevent to a SCSI device.
1303 */
1304 static void
1305 device_prevent(int argc, char *argv[])
1306 {
1307 struct scsi_prevent_allow_medium_removal cmd;
1308
1309 /* No arguments. */
1310 if (argc != 0)
1311 usage();
1312
1313 memset(&cmd, 0, sizeof(cmd));
1314
1315 cmd.opcode = SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL;
1316 cmd.how = SPAMR_PREVENT_DT; /* XXX SMAMR_PREVENT_ALL? */
1317
1318 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
1319
1320 return;
1321 }
1322
1323 /*
1324 * device_allow:
1325 *
1326 * Issue a stop to a SCSI device.
1327 */
1328 static void
1329 device_allow(int argc, char *argv[])
1330 {
1331 struct scsi_prevent_allow_medium_removal cmd;
1332
1333 /* No arguments. */
1334 if (argc != 0)
1335 usage();
1336
1337 memset(&cmd, 0, sizeof(cmd));
1338
1339 cmd.opcode = SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL;
1340 cmd.how = SPAMR_ALLOW;
1341
1342 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
1343
1344 return;
1345 }
1346
1347 /*
1348 * device_start:
1349 *
1350 * Issue a start to a SCSI device.
1351 */
1352 static void
1353 device_start(int argc, char *argv[])
1354 {
1355 struct scsipi_start_stop cmd;
1356
1357 /* No arguments. */
1358 if (argc != 0)
1359 usage();
1360
1361 memset(&cmd, 0, sizeof(cmd));
1362
1363 cmd.opcode = START_STOP;
1364 cmd.how = SSS_START;
1365
1366 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 30000, 0);
1367
1368 return;
1369 }
1370
1371 /*
1372 * device_stop:
1373 *
1374 * Issue a stop to a SCSI device.
1375 */
1376 static void
1377 device_stop(int argc, char *argv[])
1378 {
1379 struct scsipi_start_stop cmd;
1380
1381 /* No arguments. */
1382 if (argc != 0)
1383 usage();
1384
1385 memset(&cmd, 0, sizeof(cmd));
1386
1387 cmd.opcode = START_STOP;
1388 cmd.how = SSS_STOP;
1389
1390 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 30000, 0);
1391
1392 return;
1393 }
1394
1395 /*
1396 * device_tur:
1397 *
1398 * Issue a TEST UNIT READY to a SCSI device.
1399 */
1400 static void
1401 device_tur(int argc, char *argv[])
1402 {
1403 struct scsi_test_unit_ready cmd;
1404
1405 /* No arguments. */
1406 if (argc != 0)
1407 usage();
1408
1409 memset(&cmd, 0, sizeof(cmd));
1410
1411 cmd.opcode = SCSI_TEST_UNIT_READY;
1412
1413 scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0);
1414
1415 return;
1416 }
1417
1418 /*
1419 * BUS COMMANDS
1420 */
1421
1422 /*
1423 * bus_reset:
1424 *
1425 * Issue a reset to a SCSI bus.
1426 */
1427 static void
1428 bus_reset(int argc, char *argv[])
1429 {
1430
1431 /* No arguments. */
1432 if (argc != 0)
1433 usage();
1434
1435 if (ioctl(fd, SCBUSIORESET, NULL) != 0)
1436 err(1, "SCBUSIORESET");
1437
1438 return;
1439 }
1440
1441 /*
1442 * bus_scan:
1443 *
1444 * Rescan a SCSI bus for new devices.
1445 */
1446 static void
1447 bus_scan(int argc, char *argv[])
1448 {
1449 struct scbusioscan_args args;
1450 char *cp;
1451
1452 /* Must have two args: target lun */
1453 if (argc != 2)
1454 usage();
1455
1456 if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0)
1457 args.sa_target = -1;
1458 else {
1459 args.sa_target = strtol(argv[0], &cp, 10);
1460 if (*cp != '\0' || args.sa_target < 0)
1461 errx(1, "invalid target: %s", argv[0]);
1462 }
1463
1464 if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0)
1465 args.sa_lun = -1;
1466 else {
1467 args.sa_lun = strtol(argv[1], &cp, 10);
1468 if (*cp != '\0' || args.sa_lun < 0)
1469 errx(1, "invalid lun: %s", argv[1]);
1470 }
1471
1472 if (ioctl(fd, SCBUSIOSCAN, &args) != 0)
1473 err(1, "SCBUSIOSCAN");
1474
1475 return;
1476 }
1477
1478 /*
1479 * bus_detach:
1480 *
1481 * detach SCSI devices from a bus.
1482 */
1483 static void
1484 bus_detach(int argc, char *argv[])
1485 {
1486 struct scbusiodetach_args args;
1487 char *cp;
1488
1489 /* Must have two args: target lun */
1490 if (argc != 2)
1491 usage();
1492
1493 if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0)
1494 args.sa_target = -1;
1495 else {
1496 args.sa_target = strtol(argv[0], &cp, 10);
1497 if (*cp != '\0' || args.sa_target < 0)
1498 errx(1, "invalid target: %s", argv[0]);
1499 }
1500
1501 if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0)
1502 args.sa_lun = -1;
1503 else {
1504 args.sa_lun = strtol(argv[1], &cp, 10);
1505 if (*cp != '\0' || args.sa_lun < 0)
1506 errx(1, "invalid lun: %s", argv[1]);
1507 }
1508
1509 if (ioctl(fd, SCBUSIODETACH, &args) != 0)
1510 err(1, "SCBUSIODETACH");
1511
1512 return;
1513 }
1514