1 /* $NetBSD: chio.c,v 1.10 1998/07/28 05:31:22 mycroft Exp $ */ 2 3 /* 4 * Copyright (c) 1996, 1998 Jason R. Thorpe <thorpej@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.10 1998/07/28 05:31:22 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 /* NOTREACHED */ 770 } 771