PageRenderTime 77ms CodeModel.GetById 15ms app.highlight 48ms RepoModel.GetById 1ms app.codeStats 1ms

/bin/chio/chio.c

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