1 /* $NetBSD: chio.c,v 1.29 2008/04/28 20:22:51 martin Exp $ */
4 * Copyright (c) 1996, 1998, 1999 The NetBSD Foundation, Inc.
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.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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.
34 * Additional Copyright (c) 1997, by Matthew Jacob, for NASA/Ames Research Ctr.
37 #include <sys/cdefs.h>
40 "@(#) Copyright (c) 1996, 1998, 1999\
41 The NetBSD Foundation, Inc. All rights reserved.");
42 __RCSID("$NetBSD: chio.c,v 1.29 2008/04/28 20:22:51 martin Exp $");
45 #include <sys/param.h>
46 #include <sys/ioctl.h>
48 #include <sys/cdio.h> /* for ATAPI CD changer; too bad it uses a lame API */
61 #include "pathnames.h"
63 int main(int, char *[]);
64 static void usage(void);
65 static void cleanup(void);
66 static int parse_element_type(const char *);
67 static int parse_element_unit(const char *);
68 static int parse_special(const char *);
69 static int is_special(const char *);
70 static const char *bits_to_string(int, const char *);
72 static int do_move(const char *, int, char **);
73 static int do_exchange(const char *, int, char **);
74 static int do_position(const char *, int, char **);
75 static int do_params(const char *, int, char **);
76 static int do_getpicker(const char *, int, char **);
77 static int do_setpicker(const char *, int, char **);
78 static int do_status(const char *, int, char **);
79 static int do_ielem(const char *, int, char **);
80 static int do_cdlu(const char *, int, char **);
82 /* Valid changer element types. */
83 const struct element_type elements
[] = {
84 { "picker", CHET_MT
},
86 { "portal", CHET_IE
},
92 const struct changer_command commands
[] = {
93 { "move", " <from ET> <from EU> <to ET> <to EU> [inv]",
96 { "exchange", " <src ET> <src EU> <dst1 ET> <dst1 EU>\n"
97 "\t\t [<dst2 ET> <dst2 EU>] [inv1] [inv2]",
100 { "position", " <to ET> <to EU> [inv]", do_position
},
108 { "setpicker", " <picker>",
111 { "status", " [<ET> [unit [count]]] [voltags]",
117 { "cdlu", " load|unload <slot>\n"
125 /* Valid special words. */
126 const struct special_word specials
[] = {
127 { "inv", SW_INVERT
},
128 { "inv1", SW_INVERT1
},
129 { "inv2", SW_INVERT2
},
130 { "voltags", SW_VOLTAGS
},
134 static const char *changer_name
;
135 static int changer_fd
;
138 main(int argc
, char *argv
[])
142 setprogname(argv
[0]);
143 while ((ch
= getopt(argc
, argv
, "f:")) != -1) {
146 changer_name
= optarg
;
160 /* Get the default changer if not already specified. */
161 if (changer_name
== NULL
)
162 if ((changer_name
= getenv(CHANGER_ENV_VAR
)) == NULL
)
163 changer_name
= _PATH_CH
;
165 /* Open the changer device. */
166 if ((changer_fd
= open(changer_name
, O_RDWR
, 0600)) == -1)
167 err(EXIT_FAILURE
, "%s: open", changer_name
);
170 /* Register cleanup function. */
172 err(EXIT_FAILURE
, "can't register cleanup function");
175 /* Find the specified command. */
176 for (i
= 0; commands
[i
].cc_name
!= NULL
; ++i
)
177 if (strcmp(*argv
, commands
[i
].cc_name
) == 0)
179 if (commands
[i
].cc_name
== NULL
)
180 errx(EXIT_FAILURE
, "unknown command: %s", *argv
);
183 /* Skip over the command name and call handler. */
185 exit((*commands
[i
].cc_handler
)(commands
[i
].cc_name
, argc
, argv
));
190 do_move(const char *cname
, int argc
, char **argv
)
192 struct changer_move_request cmd
;
196 * On a move command, we expect the following:
198 * <from ET> <from EU> <to ET> <to EU> [inv]
200 * where ET == element type and EU == element unit.
203 warnx("%s: too few arguments", cname
);
206 } else if (argc
> 5) {
207 warnx("%s: too many arguments", cname
);
211 (void)memset(&cmd
, 0, sizeof(cmd
));
214 cmd
.cm_fromtype
= parse_element_type(*argv
);
218 cmd
.cm_fromunit
= parse_element_unit(*argv
);
222 cmd
.cm_totype
= parse_element_type(*argv
);
226 cmd
.cm_tounit
= parse_element_unit(*argv
);
229 /* Deal with optional command modifier. */
231 val
= parse_special(*argv
);
234 cmd
.cm_flags
|= CM_INVERT
;
237 errx(EXIT_FAILURE
, "%s: inappropriate modifier `%s'",
243 /* Send command to changer. */
244 if (ioctl(changer_fd
, CHIOMOVE
, &cmd
))
245 err(EXIT_FAILURE
, "%s: CHIOMOVE", changer_name
);
252 do_exchange(const char *cname
, int argc
, char **argv
)
254 struct changer_exchange_request cmd
;
258 * On an exchange command, we expect the following:
260 * <src ET> <src EU> <dst1 ET> <dst1 EU> [<dst2 ET> <dst2 EU>] [inv1] [inv2]
262 * where ET == element type and EU == element unit.
265 warnx("%s: too few arguments", cname
);
268 } else if (argc
> 8) {
269 warnx("%s: too many arguments", cname
);
273 (void)memset(&cmd
, 0, sizeof(cmd
));
276 cmd
.ce_srctype
= parse_element_type(*argv
);
280 cmd
.ce_srcunit
= parse_element_unit(*argv
);
284 cmd
.ce_fdsttype
= parse_element_type(*argv
);
288 cmd
.ce_fdstunit
= parse_element_unit(*argv
);
292 * If the next token is a special word or there are no more
293 * arguments, then this is a case of simple exchange.
296 if ((argc
== 0) || is_special(*argv
)) {
297 cmd
.ce_sdsttype
= cmd
.ce_srctype
;
298 cmd
.ce_sdstunit
= cmd
.ce_srcunit
;
303 cmd
.ce_sdsttype
= parse_element_type(*argv
);
307 cmd
.ce_sdstunit
= parse_element_unit(*argv
);
311 /* Deal with optional command modifiers. */
313 val
= parse_special(*argv
);
317 cmd
.ce_flags
|= CE_INVERT1
;
320 cmd
.ce_flags
|= CE_INVERT2
;
323 errx(EXIT_FAILURE
, "%s: inappropriate modifier `%s'",
329 /* Send command to changer. */
330 if (ioctl(changer_fd
, CHIOEXCHANGE
, &cmd
))
331 err(EXIT_FAILURE
, "%s: CHIOEXCHANGE", changer_name
);
338 do_position(const char *cname
, int argc
, char **argv
)
340 struct changer_position_request cmd
;
344 * On a position command, we expect the following:
346 * <to ET> <to EU> [inv]
348 * where ET == element type and EU == element unit.
351 warnx("%s: too few arguments", cname
);
354 } else if (argc
> 3) {
355 warnx("%s: too many arguments", cname
);
359 (void)memset(&cmd
, 0, sizeof(cmd
));
362 cmd
.cp_type
= parse_element_type(*argv
);
366 cmd
.cp_unit
= parse_element_unit(*argv
);
369 /* Deal with optional command modifier. */
371 val
= parse_special(*argv
);
374 cmd
.cp_flags
|= CP_INVERT
;
377 errx(EXIT_FAILURE
, "%s: inappropriate modifier `%s'",
383 /* Send command to changer. */
384 if (ioctl(changer_fd
, CHIOPOSITION
, &cmd
))
385 err(EXIT_FAILURE
, "%s: CHIOPOSITION", changer_name
);
393 do_params(const char *cname
, int argc
, char **argv
)
395 struct changer_params data
;
397 /* No arguments to this command. */
399 warnx("%s: no arguments expected", cname
);
404 /* Get params from changer and display them. */
405 (void)memset(&data
, 0, sizeof(data
));
406 if (ioctl(changer_fd
, CHIOGPARAMS
, &data
))
407 err(EXIT_FAILURE
, "%s: CHIOGPARAMS", changer_name
);
410 #define PLURAL(n) (n) > 1 ? "s" : ""
412 (void)printf("%s: %d slot%s, %d drive%s, %d picker%s",
414 data
.cp_nslots
, PLURAL(data
.cp_nslots
),
415 data
.cp_ndrives
, PLURAL(data
.cp_ndrives
),
416 data
.cp_npickers
, PLURAL(data
.cp_npickers
));
417 if (data
.cp_nportals
)
418 (void)printf(", %d portal%s", data
.cp_nportals
,
419 PLURAL(data
.cp_nportals
));
423 (void)printf("\n%s: current picker: %d\n", changer_name
, data
.cp_curpicker
);
430 do_getpicker(const char *cname
, int argc
, char **argv
)
434 /* No arguments to this command. */
436 warnx("%s: no arguments expected", cname
);
441 /* Get current picker from changer and display it. */
442 if (ioctl(changer_fd
, CHIOGPICKER
, &picker
))
443 err(EXIT_FAILURE
, "%s: CHIOGPICKER", changer_name
);
446 (void)printf("%s: current picker: %d\n", changer_name
, picker
);
452 do_setpicker(const char *cname
, int argc
, char **argv
)
457 warnx("%s: too few arguments", cname
);
460 } else if (argc
> 1) {
461 warnx("%s: too many arguments", cname
);
466 picker
= parse_element_unit(*argv
);
468 /* Set the changer picker. */
469 if (ioctl(changer_fd
, CHIOSPICKER
, &picker
))
470 err(EXIT_FAILURE
, "%s: CHIOSPICKER", changer_name
);
476 do_status(const char *cname
, int argc
, char **argv
)
478 struct changer_element_status_request cmd
;
479 struct changer_params data
;
480 struct changer_element_status
*ces
;
481 int i
, chet
, count
, echet
, flags
, have_ucount
, have_unit
;
482 int schet
, ucount
, unit
;
492 * On a status command, we expect the following:
494 * [<ET> [unit [count]]] [voltags]
496 * where ET == element type.
498 * If we get no element-related arguments, we get the status of all
499 * known element types.
502 warnx("%s: too many arguments", cname
);
508 * Get params from changer. Specifically, we need the element
511 (void)memset(&data
, 0, sizeof(data
));
512 if (ioctl(changer_fd
, CHIOGPARAMS
, &data
))
513 err(EXIT_FAILURE
, "%s: CHIOGPARAMS", changer_name
);
519 for (; argc
!= 0; argc
--, argv
++) {
521 * If we have the voltags modifier, it must be the
524 if (is_special(argv
[0])) {
526 warnx("%s: malformed command line", cname
);
530 if (parse_special(argv
[0]) != SW_VOLTAGS
)
532 "%s: inappropriate special word: %s",
535 flags
|= CESR_VOLTAGS
;
540 * If we get an element type, we can't have specified
543 if (isdigit((unsigned char)*argv
[0]) == 0) {
544 if (schet
== echet
|| flags
!= 0 || have_unit
||
546 warnx("%s: malformed command line", cname
);
550 schet
= echet
= parse_element_type(argv
[0]);
555 * We know we have a digit here. If we do, we must
556 * have specified an element type.
558 if (schet
!= echet
) {
559 warnx("%s: malformed command line", cname
);
564 i
= parse_element_unit(argv
[0]);
566 if (have_unit
== 0) {
569 } else if (have_ucount
== 0) {
573 warnx("%s: malformed command line", cname
);
579 for (chet
= schet
; chet
<= echet
; ++chet
) {
582 count
= data
.cp_npickers
;
585 count
= data
.cp_nslots
;
588 count
= data
.cp_nportals
;
591 count
= data
.cp_ndrives
;
594 /* To appease gcc -Wuninitialized. */
602 (void)printf("%s: no %s elements\n",
604 elements
[chet
].et_name
);
610 * If we have a unit, we may or may not have a count.
611 * If we don't have a unit, we don't have a count, either.
613 * Make sure both are initialized.
616 if (have_ucount
== 0)
623 if ((unit
+ ucount
) > count
)
624 errx(EXIT_FAILURE
, "%s: unvalid unit/count %d/%d",
625 cname
, unit
, ucount
);
628 size
= ucount
* sizeof(struct changer_element_status
);
630 /* Allocate storage for the status bytes. */
631 if ((ces
= malloc(size
)) == NULL
)
632 errx(EXIT_FAILURE
, "can't allocate status storage");
635 (void)memset(ces
, 0, size
);
636 (void)memset(&cmd
, 0, sizeof(cmd
));
638 cmd
.cesr_type
= chet
;
639 cmd
.cesr_unit
= unit
;
640 cmd
.cesr_count
= ucount
;
641 cmd
.cesr_flags
= flags
;
645 * Should we deal with this eventually?
647 cmd
.cesr_vendor_data
= NULL
;
649 if (ioctl(changer_fd
, CHIOGSTATUS
, &cmd
)) {
651 err(EXIT_FAILURE
, "%s: CHIOGSTATUS", changer_name
);
655 /* Dump the status for each element of this type. */
656 for (i
= 0; i
< ucount
; i
++) {
657 (void)printf("%s %d: ", elements
[chet
].et_name
,
659 if ((ces
[i
].ces_flags
& CESTATUS_STATUS_VALID
) == 0) {
660 (void)printf("status not available\n");
663 (void)printf("%s", bits_to_string(ces
[i
].ces_flags
,
665 if (ces
[i
].ces_flags
& CESTATUS_XNAME_VALID
)
666 (void)printf(" (%s)", ces
[i
].ces_xname
);
668 if (ces
[i
].ces_flags
& CESTATUS_PVOL_VALID
)
669 (void)printf("\tPrimary volume tag: %s "
671 ces
[i
].ces_pvoltag
.cv_tag
,
672 ces
[i
].ces_pvoltag
.cv_serial
);
673 if (ces
[i
].ces_flags
& CESTATUS_AVOL_VALID
)
674 (void)printf("\tAlternate volume tag: %s "
676 ces
[i
].ces_avoltag
.cv_tag
,
677 ces
[i
].ces_avoltag
.cv_serial
);
678 if (ces
[i
].ces_flags
& CESTATUS_FROM_VALID
)
679 (void)printf("\tFrom: %s %d\n",
680 elements
[ces
[i
].ces_from_type
].et_name
,
681 ces
[i
].ces_from_unit
);
682 if (ces
[i
].ces_vendor_len
)
683 (void)printf("\tVendor-specific data size: "
684 "%lu\n", (u_long
)ces
[i
].ces_vendor_len
);
694 do_ielem(const char *cname
, int argc
, char **argv
)
697 if (ioctl(changer_fd
, CHIOIELEM
, NULL
))
698 err(EXIT_FAILURE
, "%s: CHIOIELEM", changer_name
);
706 do_cdlu(const char *cname
, int argc
, char **argv
)
708 static const struct special_word cdlu_subcmds
[] = {
709 { "load", CD_LU_LOAD
},
710 { "unload", CD_LU_UNLOAD
},
711 { "abort", CD_LU_ABORT
},
714 struct ioc_load_unload cmd
;
718 * This command is a little different, since we are mostly dealing
719 * with ATAPI CD changers, which have a lame API (since ATAPI doesn't
722 * We have 3 sub-commands: "load", "unload", and "abort". The
723 * first two take a slot number. The latter does not.
726 if (argc
< 1 || argc
> 2)
730 for (i
= 0; cdlu_subcmds
[i
].sw_name
!= NULL
; i
++) {
731 if (strcmp(argv
[0], cdlu_subcmds
[i
].sw_name
) == 0) {
732 cmd
.options
= cdlu_subcmds
[i
].sw_value
;
736 if (cdlu_subcmds
[i
].sw_name
== NULL
)
740 if (strcmp(argv
[0], "abort") == 0)
743 cmd
.slot
= parse_element_unit(argv
[1]);
746 * XXX Should maybe do something different with the device
747 * XXX handling for cdlu; think about this some more.
749 if (ioctl(changer_fd
, CDIOCLOADUNLOAD
, &cmd
))
750 err(EXIT_FAILURE
, "%s: CDIOCLOADUNLOAD", changer_name
);
757 parse_element_type(const char *cp
)
761 for (i
= 0; elements
[i
].et_name
!= NULL
; ++i
)
762 if (strcmp(elements
[i
].et_name
, cp
) == 0)
763 return (elements
[i
].et_type
);
765 errx(EXIT_FAILURE
, "invalid element type `%s'", cp
);
770 parse_element_unit(const char *cp
)
775 i
= (int)strtol(cp
, &p
, 10);
776 if ((i
< 0) || (*p
!= '\0'))
777 errx(EXIT_FAILURE
, "invalid unit number `%s'", cp
);
783 parse_special(const char *cp
)
787 val
= is_special(cp
);
791 errx(EXIT_FAILURE
, "invalid modifier `%s'", cp
);
796 is_special(const char *cp
)
800 for (i
= 0; specials
[i
].sw_name
!= NULL
; ++i
)
801 if (strcmp(specials
[i
].sw_name
, cp
) == 0)
802 return (specials
[i
].sw_value
);
808 bits_to_string(int v
, const char *cp
)
810 static char buf
[128];
817 for (first
= 1; (f
= *cp
++) != 0; cp
= np
) {
818 for (np
= cp
; *np
>= ' ';)
820 if ((v
& (1 << (f
- 1))) == 0)
826 (void)memcpy(bp
, cp
, np
- cp
);
839 /* Simple enough... */
840 (void)close(changer_fd
);
848 (void)fprintf(stderr
,
849 "usage: %s [-f changer] command arg1 arg2 [arg3 [...]]\n",
852 (void)fprintf(stderr
, "Where command (and args) are:\n");
853 for (i
= 0; commands
[i
].cc_name
!= NULL
; i
++)
854 (void)fprintf(stderr
, "\t%s%s\n", commands
[i
].cc_name
,
855 commands
[i
].cc_args
);