chio.c revision 1.9 1 /* $NetBSD: chio.c,v 1.9 1998/07/28 03:47:14 mycroft Exp $ */
2
3 /*
4 * Copyright (c) 1996, 1998 Jason R. Thorpe <thorpej (at) and.com>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgements:
17 * This product includes software developed by Jason R. Thorpe
18 * for And Communications, http://www.and.com/
19 * 4. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34 /*
35 * Additional Copyright (c) 1997, by Matthew Jacob, for NASA/Ames Research Ctr.
36 */
37
38 #include <sys/cdefs.h>
39 #ifndef lint
40 __COPYRIGHT(
41 "@(#) Copyright (c) 1996, 1998 Jason R. Thorpe. All rights reserved.");
42 __RCSID("$NetBSD: chio.c,v 1.9 1998/07/28 03:47:14 mycroft Exp $");
43 #endif
44
45 #include <sys/param.h>
46 #include <sys/ioctl.h>
47 #include <sys/chio.h>
48 #include <sys/cdio.h> /* for ATAPI CD changer; too bad it uses a lame API */
49 #include <err.h>
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <limits.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57
58 #include "defs.h"
59 #include "pathnames.h"
60
61 extern char *__progname; /* from crt0.o */
62
63 int main __P((int, char *[]));
64 static void usage __P((void));
65 static void cleanup __P((void));
66 static int parse_element_type __P((char *));
67 static int parse_element_unit __P((char *));
68 static int parse_special __P((char *));
69 static int is_special __P((char *));
70 static const char *bits_to_string __P((int, const char *));
71
72 static int do_move __P((char *, int, char **));
73 static int do_exchange __P((char *, int, char **));
74 static int do_position __P((char *, int, char **));
75 static int do_params __P((char *, int, char **));
76 static int do_getpicker __P((char *, int, char **));
77 static int do_setpicker __P((char *, int, char **));
78 static int do_status __P((char *, int, char **));
79 static int do_ielem __P((char *, int, char **));
80 static int do_cdlu __P((char *, int, char **));
81
82 /* Valid changer element types. */
83 const struct element_type elements[] = {
84 { "picker", CHET_MT },
85 { "slot", CHET_ST },
86 { "portal", CHET_IE },
87 { "drive", CHET_DT },
88 { NULL, 0 },
89 };
90
91 /* Valid commands. */
92 const struct changer_command commands[] = {
93 { "move", do_move },
94 { "exchange", do_exchange },
95 { "position", do_position },
96 { "params", do_params },
97 { "getpicker", do_getpicker },
98 { "setpicker", do_setpicker },
99 { "status", do_status },
100 { "ielem", do_ielem },
101 { "cdlu", do_cdlu },
102 { NULL, 0 },
103 };
104
105 /* Valid special words. */
106 const struct special_word specials[] = {
107 { "inv", SW_INVERT },
108 { "inv1", SW_INVERT1 },
109 { "inv2", SW_INVERT2 },
110 { NULL, 0 },
111 };
112
113 static int changer_fd;
114 static const char *changer_name;
115
116 int
117 main(argc, argv)
118 int argc;
119 char *argv[];
120 {
121 int ch, i;
122
123 while ((ch = getopt(argc, argv, "f:")) != -1) {
124 switch (ch) {
125 case 'f':
126 changer_name = optarg;
127 break;
128
129 default:
130 usage();
131 }
132 }
133 argc -= optind;
134 argv += optind;
135
136 if (argc == 0)
137 usage();
138
139 /* Get the default changer if not already specified. */
140 if (changer_name == NULL)
141 if ((changer_name = getenv(CHANGER_ENV_VAR)) == NULL)
142 changer_name = _PATH_CH;
143
144 /* Open the changer device. */
145 if ((changer_fd = open(changer_name, O_RDWR, 0600)) == -1)
146 err(1, "%s: open", changer_name);
147
148 /* Register cleanup function. */
149 if (atexit(cleanup))
150 err(1, "can't register cleanup function");
151
152 /* Find the specified command. */
153 for (i = 0; commands[i].cc_name != NULL; ++i)
154 if (strcmp(*argv, commands[i].cc_name) == 0)
155 break;
156 if (commands[i].cc_name == NULL)
157 errx(1, "unknown command: %s", *argv);
158
159 /* Skip over the command name and call handler. */
160 ++argv; --argc;
161 exit ((*commands[i].cc_handler)(commands[i].cc_name, argc, argv));
162 /* NOTREACHED */
163 }
164
165 static int
166 do_move(cname, argc, argv)
167 char *cname;
168 int argc;
169 char **argv;
170 {
171 struct changer_move cmd;
172 int val;
173
174 /*
175 * On a move command, we expect the following:
176 *
177 * <from ET> <from EU> <to ET> <to EU> [inv]
178 *
179 * where ET == element type and EU == element unit.
180 */
181 if (argc < 4) {
182 warnx("%s: too few arguments", cname);
183 goto usage;
184 } else if (argc > 5) {
185 warnx("%s: too many arguments", cname);
186 goto usage;
187 }
188 (void) memset(&cmd, 0, sizeof(cmd));
189
190 /* <from ET> */
191 cmd.cm_fromtype = parse_element_type(*argv);
192 ++argv; --argc;
193
194 /* <from EU> */
195 cmd.cm_fromunit = parse_element_unit(*argv);
196 ++argv; --argc;
197
198 /* <to ET> */
199 cmd.cm_totype = parse_element_type(*argv);
200 ++argv; --argc;
201
202 /* <to EU> */
203 cmd.cm_tounit = parse_element_unit(*argv);
204 ++argv; --argc;
205
206 /* Deal with optional command modifier. */
207 if (argc) {
208 val = parse_special(*argv);
209 switch (val) {
210 case SW_INVERT:
211 cmd.cm_flags |= CM_INVERT;
212 break;
213
214 default:
215 errx(1, "%s: inappropriate modifier `%s'",
216 cname, *argv);
217 /* NOTREACHED */
218 }
219 }
220
221 /* Send command to changer. */
222 if (ioctl(changer_fd, CHIOMOVE, &cmd))
223 err(1, "%s: CHIOMOVE", changer_name);
224
225 return (0);
226
227 usage:
228 (void) fprintf(stderr, "usage: %s %s "
229 "<from ET> <from EU> <to ET> <to EU> [inv]\n", __progname, cname);
230 return (1);
231 }
232
233 static int
234 do_exchange(cname, argc, argv)
235 char *cname;
236 int argc;
237 char **argv;
238 {
239 struct changer_exchange cmd;
240 int val;
241
242 /*
243 * On an exchange command, we expect the following:
244 *
245 * <src ET> <src EU> <dst1 ET> <dst1 EU> [<dst2 ET> <dst2 EU>] [inv1] [inv2]
246 *
247 * where ET == element type and EU == element unit.
248 */
249 if (argc < 4) {
250 warnx("%s: too few arguments", cname);
251 goto usage;
252 } else if (argc > 8) {
253 warnx("%s: too many arguments", cname);
254 goto usage;
255 }
256 (void) memset(&cmd, 0, sizeof(cmd));
257
258 /* <src ET> */
259 cmd.ce_srctype = parse_element_type(*argv);
260 ++argv; --argc;
261
262 /* <src EU> */
263 cmd.ce_srcunit = parse_element_unit(*argv);
264 ++argv; --argc;
265
266 /* <dst1 ET> */
267 cmd.ce_fdsttype = parse_element_type(*argv);
268 ++argv; --argc;
269
270 /* <dst1 EU> */
271 cmd.ce_fdstunit = parse_element_unit(*argv);
272 ++argv; --argc;
273
274 /*
275 * If the next token is a special word or there are no more
276 * arguments, then this is a case of simple exchange.
277 * dst2 == src.
278 */
279 if ((argc == 0) || is_special(*argv)) {
280 cmd.ce_sdsttype = cmd.ce_srctype;
281 cmd.ce_sdstunit = cmd.ce_srcunit;
282 goto do_special;
283 }
284
285 /* <dst2 ET> */
286 cmd.ce_sdsttype = parse_element_type(*argv);
287 ++argv; --argc;
288
289 /* <dst2 EU> */
290 cmd.ce_sdstunit = parse_element_unit(*argv);
291 ++argv; --argc;
292
293 do_special:
294 /* Deal with optional command modifiers. */
295 while (argc) {
296 val = parse_special(*argv);
297 ++argv; --argc;
298 switch (val) {
299 case SW_INVERT1:
300 cmd.ce_flags |= CE_INVERT1;
301 break;
302
303 case SW_INVERT2:
304 cmd.ce_flags |= CE_INVERT2;
305 break;
306
307 default:
308 errx(1, "%s: inappropriate modifier `%s'",
309 cname, *argv);
310 /* NOTREACHED */
311 }
312 }
313
314 /* Send command to changer. */
315 if (ioctl(changer_fd, CHIOEXCHANGE, &cmd))
316 err(1, "%s: CHIOEXCHANGE", changer_name);
317
318 return (0);
319
320 usage:
321 (void) fprintf(stderr,
322 "usage: %s %s <src ET> <src EU> <dst1 ET> <dst1 EU>\n"
323 " [<dst2 ET> <dst2 EU>] [inv1] [inv2]\n",
324 __progname, cname);
325 return (1);
326 }
327
328 static int
329 do_position(cname, argc, argv)
330 char *cname;
331 int argc;
332 char **argv;
333 {
334 struct changer_position cmd;
335 int val;
336
337 /*
338 * On a position command, we expect the following:
339 *
340 * <to ET> <to EU> [inv]
341 *
342 * where ET == element type and EU == element unit.
343 */
344 if (argc < 2) {
345 warnx("%s: too few arguments", cname);
346 goto usage;
347 } else if (argc > 3) {
348 warnx("%s: too many arguments", cname);
349 goto usage;
350 }
351 (void) memset(&cmd, 0, sizeof(cmd));
352
353 /* <to ET> */
354 cmd.cp_type = parse_element_type(*argv);
355 ++argv; --argc;
356
357 /* <to EU> */
358 cmd.cp_unit = parse_element_unit(*argv);
359 ++argv; --argc;
360
361 /* Deal with optional command modifier. */
362 if (argc) {
363 val = parse_special(*argv);
364 switch (val) {
365 case SW_INVERT:
366 cmd.cp_flags |= CP_INVERT;
367 break;
368
369 default:
370 errx(1, "%s: inappropriate modifier `%s'",
371 cname, *argv);
372 /* NOTREACHED */
373 }
374 }
375
376 /* Send command to changer. */
377 if (ioctl(changer_fd, CHIOPOSITION, &cmd))
378 err(1, "%s: CHIOPOSITION", changer_name);
379
380 return (0);
381
382 usage:
383 (void) fprintf(stderr, "usage: %s %s <to ET> <to EU> [inv]\n",
384 __progname, cname);
385 return (1);
386 }
387
388 /* ARGSUSED */
389 static int
390 do_params(cname, argc, argv)
391 char *cname;
392 int argc;
393 char **argv;
394 {
395 struct changer_params data;
396
397 /* No arguments to this command. */
398 if (argc) {
399 warnx("%s: no arguements expected", cname);
400 goto usage;
401 }
402
403 /* Get params from changer and display them. */
404 (void) memset(&data, 0, sizeof(data));
405 if (ioctl(changer_fd, CHIOGPARAMS, &data))
406 err(1, "%s: CHIOGPARAMS", changer_name);
407
408 (void) printf("%s: %d slot%s, %d drive%s, %d picker%s",
409 changer_name,
410 data.cp_nslots, (data.cp_nslots > 1) ? "s" : "",
411 data.cp_ndrives, (data.cp_ndrives > 1) ? "s" : "",
412 data.cp_npickers, (data.cp_npickers > 1) ? "s" : "");
413 if (data.cp_nportals)
414 (void) printf(", %d portal%s", data.cp_nportals,
415 (data.cp_nportals > 1) ? "s" : "");
416 (void) printf("\n%s: current picker: %d\n", changer_name,
417 data.cp_curpicker);
418
419 return (0);
420
421 usage:
422 (void) fprintf(stderr, "usage: %s %s\n", __progname, cname);
423 return (1);
424 }
425
426 /* ARGSUSED */
427 static int
428 do_getpicker(cname, argc, argv)
429 char *cname;
430 int argc;
431 char **argv;
432 {
433 int picker;
434
435 /* No arguments to this command. */
436 if (argc) {
437 warnx("%s: no arguments expected", cname);
438 goto usage;
439 }
440
441 /* Get current picker from changer and display it. */
442 if (ioctl(changer_fd, CHIOGPICKER, &picker))
443 err(1, "%s: CHIOGPICKER", changer_name);
444
445 (void) printf("%s: current picker: %d\n", changer_name, picker);
446
447 return (0);
448
449 usage:
450 (void) fprintf(stderr, "usage: %s %s\n", __progname, cname);
451 return (1);
452 }
453
454 static int
455 do_setpicker(cname, argc, argv)
456 char *cname;
457 int argc;
458 char **argv;
459 {
460 int picker;
461
462 if (argc < 1) {
463 warnx("%s: too few arguments", cname);
464 goto usage;
465 } else if (argc > 1) {
466 warnx("%s: too many arguments", cname);
467 goto usage;
468 }
469
470 picker = parse_element_unit(*argv);
471
472 /* Set the changer picker. */
473 if (ioctl(changer_fd, CHIOSPICKER, &picker))
474 err(1, "%s: CHIOSPICKER", changer_name);
475
476 return (0);
477
478 usage:
479 (void) fprintf(stderr, "usage: %s %s <picker>\n", __progname, cname);
480 return (1);
481 }
482
483 static int
484 do_status(cname, argc, argv)
485 char *cname;
486 int argc;
487 char **argv;
488 {
489 struct changer_element_status cmd;
490 struct changer_params data;
491 u_int8_t *statusp;
492 int i, chet, schet, echet;
493 size_t count;
494 char *description;
495
496 /*
497 * On a status command, we expect the following:
498 *
499 * [<ET>]
500 *
501 * where ET == element type.
502 *
503 * If we get no arguments, we get the status of all
504 * known element types.
505 */
506 if (argc > 1) {
507 warnx("%s: too many arguments", cname);
508 goto usage;
509 }
510
511 /*
512 * Get params from changer. Specifically, we need the element
513 * counts.
514 */
515 (void) memset(&data, 0, sizeof(data));
516 if (ioctl(changer_fd, CHIOGPARAMS, &data))
517 err(1, "%s: CHIOGPARAMS", changer_name);
518
519 if (argc)
520 schet = echet = parse_element_type(*argv);
521 else {
522 schet = CHET_MT;
523 echet = CHET_DT;
524 }
525
526 for (chet = schet; chet <= echet; ++chet) {
527 switch (chet) {
528 case CHET_MT:
529 count = data.cp_npickers;
530 description = "picker";
531 break;
532
533 case CHET_ST:
534 count = data.cp_nslots;
535 description = "slot";
536 break;
537
538 case CHET_IE:
539 count = data.cp_nportals;
540 description = "portal";
541 break;
542
543 case CHET_DT:
544 count = data.cp_ndrives;
545 description = "drive";
546 break;
547
548 default:
549 /* To appease gcc -Wuninitialized. */
550 count = 0;
551 description = NULL;
552 }
553
554 if (count == 0) {
555 if (argc == 0)
556 continue;
557 else {
558 (void) printf("%s: no %s elements\n",
559 changer_name, description);
560 return (0);
561 }
562 }
563
564 /* Allocate storage for the status bytes. */
565 if ((statusp = (u_int8_t *)malloc(count)) == NULL)
566 errx(1, "can't allocate status storage");
567
568 (void) memset(statusp, 0, count);
569 (void) memset(&cmd, 0, sizeof(cmd));
570
571 cmd.ces_type = chet;
572 cmd.ces_data = statusp;
573
574 if (ioctl(changer_fd, CHIOGSTATUS, &cmd)) {
575 free(statusp);
576 err(1, "%s: CHIOGSTATUS", changer_name);
577 }
578
579 /* Dump the status for each element of this type. */
580 for (i = 0; i < count; ++i) {
581 (void) printf("%s %d: %s\n", description, i,
582 bits_to_string(statusp[i], CESTATUS_BITS));
583 }
584
585 free(statusp);
586 }
587
588 return (0);
589
590 usage:
591 (void) fprintf(stderr, "usage: %s %s [<element type>]\n", __progname,
592 cname);
593 return (1);
594 }
595
596 /* ARGSUSED */
597 static int
598 do_ielem(cname, argc, argv)
599 char *cname;
600 int argc;
601 char **argv;
602 {
603 if (ioctl(changer_fd, CHIOIELEM, NULL))
604 err(1, "%s: CHIOIELEM", changer_name);
605
606 return (0);
607 }
608
609 static int
610 do_cdlu(cname, argc, argv)
611 char *cname;
612 int argc;
613 char **argv;
614 {
615 struct ioc_load_unload cmd;
616 int i;
617 static const struct special_word cdlu_subcmds[] = {
618 { "load", CD_LU_LOAD },
619 { "unload", CD_LU_UNLOAD },
620 { "abort", CD_LU_ABORT },
621 { NULL, 0 },
622 };
623
624 /*
625 * This command is a little different, since we are mostly dealing
626 * with ATAPI CD changers, which have a lame API (since ATAPI doesn't
627 * have LUNs).
628 *
629 * We have 3 sub-commands: "load", "unload", and "abort". The
630 * first two take a slot number. The latter does not.
631 */
632
633 if (argc < 1 || argc > 2)
634 goto usage;
635
636 for (i = 0; cdlu_subcmds[i].sw_name != NULL; i++) {
637 if (strcmp(argv[0], cdlu_subcmds[i].sw_name) == 0) {
638 cmd.options = cdlu_subcmds[i].sw_value;
639 break;
640 }
641 }
642 if (cdlu_subcmds[i].sw_name == NULL)
643 goto usage;
644
645 if (strcmp(argv[0], "abort") == 0)
646 cmd.slot = 0;
647 else
648 cmd.slot = parse_element_unit(argv[1]);
649
650 /*
651 * XXX Should maybe do something different with the device
652 * XXX handling for cdlu; think about this some more.
653 */
654 if (ioctl(changer_fd, CDIOCLOADUNLOAD, &cmd))
655 err(1, "%s: CDIOCLOADUNLOAD", changer_name);
656
657 return (0);
658
659 usage:
660 (void) fprintf(stderr, "usage: %s %s load|unload <slot>\n",
661 __progname, cname);
662 (void) fprintf(stderr, " %s %s abort\n", __progname, cname);
663 return (1);
664 }
665
666 static int
667 parse_element_type(cp)
668 char *cp;
669 {
670 int i;
671
672 for (i = 0; elements[i].et_name != NULL; ++i)
673 if (strcmp(elements[i].et_name, cp) == 0)
674 return (elements[i].et_type);
675
676 errx(1, "invalid element type `%s'", cp);
677 /* NOTREACHED */
678 }
679
680 static int
681 parse_element_unit(cp)
682 char *cp;
683 {
684 int i;
685 char *p;
686
687 i = (int)strtol(cp, &p, 10);
688 if ((i < 0) || (*p != '\0'))
689 errx(1, "invalid unit number `%s'", cp);
690
691 return (i);
692 }
693
694 static int
695 parse_special(cp)
696 char *cp;
697 {
698 int val;
699
700 val = is_special(cp);
701 if (val)
702 return (val);
703
704 errx(1, "invalid modifier `%s'", cp);
705 /* NOTREACHED */
706 }
707
708 static int
709 is_special(cp)
710 char *cp;
711 {
712 int i;
713
714 for (i = 0; specials[i].sw_name != NULL; ++i)
715 if (strcmp(specials[i].sw_name, cp) == 0)
716 return (specials[i].sw_value);
717
718 return (0);
719 }
720
721 static const char *
722 bits_to_string(v, cp)
723 int v;
724 const char *cp;
725 {
726 const char *np;
727 char f, *bp;
728 int first;
729 static char buf[128];
730
731 bp = buf;
732 *bp++ = '<';
733 for (first = 1; (f = *cp++) != 0; cp = np) {
734 for (np = cp; *np >= ' ';)
735 np++;
736 if ((v & (1 << (f - 1))) == 0)
737 continue;
738 if (first)
739 first = 0;
740 else
741 *bp++ = ',';
742 (void) memcpy(bp, cp, np - cp);
743 bp += np - cp;
744 }
745 *bp++ = '>';
746 *bp = '\0';
747
748 return (buf);
749 }
750
751 static void
752 cleanup()
753 {
754 /* Simple enough... */
755 (void)close(changer_fd);
756 }
757
758 static void
759 usage()
760 {
761
762 (void) fprintf(stderr, "usage: %s command arg1 arg2 ...\n", __progname);
763 (void) fprintf(stderr, "Examples:\n");
764 (void) fprintf(stderr, "\tchio -f /dev/ch0 move slot 1 drive 0\n");
765 (void) fprintf(stderr, "\tchio ielem\n");
766 (void) fprintf(stderr, "\tchio -f /dev/ch1 status\n");
767 (void) fprintf(stderr, "\tchio -f /dev/cd0a cdlu load 1\n");
768 exit(1);
769 }
770