148983Skris/*	$NetBSD: chio.c,v 1.6 1998/01/04 23:53:58 thorpej Exp $ */
2139969Simp/*-
323449Sjoerg * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.com>
423449Sjoerg * All rights reserved.
523449Sjoerg *
623449Sjoerg * Redistribution and use in source and binary forms, with or without
723449Sjoerg * modification, are permitted provided that the following conditions
823449Sjoerg * are met:
923449Sjoerg * 1. Redistributions of source code must retain the above copyright
1023449Sjoerg *    notice, this list of conditions and the following disclaimer.
1123449Sjoerg * 2. Redistributions in binary form must reproduce the above copyright
1223449Sjoerg *    notice, this list of conditions and the following disclaimer in the
1323449Sjoerg *    documentation and/or other materials provided with the distribution.
1423449Sjoerg * 3. All advertising materials mentioning features or use of this software
1523449Sjoerg *    must display the following acknowledgements:
1623449Sjoerg *	This product includes software developed by Jason R. Thorpe
1723449Sjoerg *	for And Communications, http://www.and.com/
1823449Sjoerg * 4. The name of the author may not be used to endorse or promote products
1923449Sjoerg *    derived from this software without specific prior written permission.
2023449Sjoerg *
2123449Sjoerg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2223449Sjoerg * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2323449Sjoerg * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2423449Sjoerg * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2523449Sjoerg * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2623449Sjoerg * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2723449Sjoerg * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2823449Sjoerg * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2923449Sjoerg * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3023449Sjoerg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3123449Sjoerg * SUCH DAMAGE.
3223449Sjoerg */
3339227Sgibbs/*
3439227Sgibbs * Additional Copyright (c) 1997, by Matthew Jacob, for NASA/Ames Research Ctr.
3566019Sken * Addidional Copyright (c) 2000, by C. Stephen Gunn, Waterspout Communications
3639227Sgibbs */
3723449Sjoerg
38114433Sobrien#if 0
3935773Scharnier#ifndef lint
4048073Skrisstatic const char copyright[] =
4148073Skris	"@(#) Copyright (c) 1996 Jason R. Thorpe.  All rights reserved.";
4248073Skris#endif /* not lint */
43114433Sobrien#endif
4435773Scharnier
4593101Smarkm#include <sys/cdefs.h>
4693101Smarkm__FBSDID("$FreeBSD$");
4793101Smarkm
4836001Scharnier#include <sys/param.h>
4923449Sjoerg#include <sys/chio.h>
5023449Sjoerg#include <err.h>
5123449Sjoerg#include <fcntl.h>
5223449Sjoerg#include <stdio.h>
5391665Simp#include <stdint.h>
5423449Sjoerg#include <stdlib.h>
5523449Sjoerg#include <string.h>
5623449Sjoerg#include <unistd.h>
57252214Sken#include <langinfo.h>
58252214Sken#include <locale.h>
5923449Sjoerg
6023449Sjoerg#include "defs.h"
6123449Sjoerg#include "pathnames.h"
6223449Sjoerg
6390107Simpstatic	void usage(void);
6490107Simpstatic	void cleanup(void);
6591086Smarkmstatic	u_int16_t parse_element_type(char *);
6691086Smarkmstatic	u_int16_t parse_element_unit(char *);
6790107Simpstatic	const char * element_type_name(int et);
6890107Simpstatic	int parse_special(char *);
6990107Simpstatic	int is_special(char *);
7090107Simpstatic	const char *bits_to_string(ces_status_flags, const char *);
7123449Sjoerg
7291665Simpstatic	void find_element(char *, uint16_t *, uint16_t *);
7379253Smikehstatic	struct changer_element_status *get_element_status
74184484Sjoerg	   (unsigned int, unsigned int, int);
7566019Sken
7690107Simpstatic	int do_move(const char *, int, char **);
7790107Simpstatic	int do_exchange(const char *, int, char **);
7890107Simpstatic	int do_position(const char *, int, char **);
7990107Simpstatic	int do_params(const char *, int, char **);
8090107Simpstatic	int do_getpicker(const char *, int, char **);
8190107Simpstatic	int do_setpicker(const char *, int, char **);
8290107Simpstatic	int do_status(const char *, int, char **);
8390107Simpstatic	int do_ielem(const char *, int, char **);
8490107Simpstatic	int do_return(const char *, int, char **);
8590107Simpstatic	int do_voltag(const char *, int, char **);
86252214Skenstatic	void print_designator(const char *, u_int8_t, u_int8_t);
8739227Sgibbs
8866019Sken#ifndef CHET_VT
8966019Sken#define	CHET_VT		10			/* Completely Arbitrary */
9066019Sken#endif
9166019Sken
9223449Sjoerg/* Valid changer element types. */
9323449Sjoergconst struct element_type elements[] = {
9447816Skris	{ "drive",		CHET_DT },
9523449Sjoerg	{ "picker",		CHET_MT },
9647816Skris	{ "portal",		CHET_IE },
9723449Sjoerg	{ "slot",		CHET_ST },
9866019Sken	{ "voltag",		CHET_VT },	/* Select tapes by barcode */
9923449Sjoerg	{ NULL,			0 },
10023449Sjoerg};
10123449Sjoerg
10223449Sjoerg/* Valid commands. */
10323449Sjoergconst struct changer_command commands[] = {
10447816Skris	{ "exchange",		do_exchange },
10547816Skris	{ "getpicker",		do_getpicker },
10647816Skris	{ "ielem", 		do_ielem },
10723449Sjoerg	{ "move",		do_move },
10847816Skris	{ "params",		do_params },
10923449Sjoerg	{ "position",		do_position },
11023449Sjoerg	{ "setpicker",		do_setpicker },
11123449Sjoerg	{ "status",		do_status },
11266019Sken	{ "return",		do_return },
11339227Sgibbs	{ "voltag",		do_voltag },
11423449Sjoerg	{ NULL,			0 },
11523449Sjoerg};
11623449Sjoerg
11723449Sjoerg/* Valid special words. */
11823449Sjoergconst struct special_word specials[] = {
11923449Sjoerg	{ "inv",		SW_INVERT },
12023449Sjoerg	{ "inv1",		SW_INVERT1 },
12123449Sjoerg	{ "inv2",		SW_INVERT2 },
12223449Sjoerg	{ NULL,			0 },
12323449Sjoerg};
12423449Sjoerg
12523449Sjoergstatic	int changer_fd;
12648073Skrisstatic	const char *changer_name;
12723449Sjoerg
12823449Sjoergint
12990107Simpmain(int argc, char **argv)
13023449Sjoerg{
13123449Sjoerg	int ch, i;
13223449Sjoerg
13323449Sjoerg	while ((ch = getopt(argc, argv, "f:")) != -1) {
13423449Sjoerg		switch (ch) {
13523449Sjoerg		case 'f':
13623449Sjoerg			changer_name = optarg;
13723449Sjoerg			break;
13823449Sjoerg
13923449Sjoerg		default:
14023449Sjoerg			usage();
14123449Sjoerg		}
14223449Sjoerg	}
14323449Sjoerg	argc -= optind;
14423449Sjoerg	argv += optind;
14523449Sjoerg
14623449Sjoerg	if (argc == 0)
14723449Sjoerg		usage();
14823449Sjoerg
14923449Sjoerg	/* Get the default changer if not already specified. */
15023449Sjoerg	if (changer_name == NULL)
15123449Sjoerg		if ((changer_name = getenv(CHANGER_ENV_VAR)) == NULL)
15223449Sjoerg			changer_name = _PATH_CH;
15323449Sjoerg
15423449Sjoerg	/* Open the changer device. */
15523449Sjoerg	if ((changer_fd = open(changer_name, O_RDWR, 0600)) == -1)
15623449Sjoerg		err(1, "%s: open", changer_name);
15723449Sjoerg
15823449Sjoerg	/* Register cleanup function. */
15923449Sjoerg	if (atexit(cleanup))
16023449Sjoerg		err(1, "can't register cleanup function");
16123449Sjoerg
16223449Sjoerg	/* Find the specified command. */
16323449Sjoerg	for (i = 0; commands[i].cc_name != NULL; ++i)
16423449Sjoerg		if (strcmp(*argv, commands[i].cc_name) == 0)
16523449Sjoerg			break;
16647816Skris	if (commands[i].cc_name == NULL) {
16747816Skris		/* look for abbreviation */
16847816Skris		for (i = 0; commands[i].cc_name != NULL; ++i)
16947816Skris			if (strncmp(*argv, commands[i].cc_name,
17047816Skris			    strlen(*argv)) == 0)
17147816Skris				break;
17248073Skris	}
17347816Skris
17423449Sjoerg	if (commands[i].cc_name == NULL)
17523449Sjoerg		errx(1, "unknown command: %s", *argv);
17623449Sjoerg
17748073Skris	exit ((*commands[i].cc_handler)(commands[i].cc_name, argc, argv));
17839227Sgibbs	/* NOTREACHED */
17923449Sjoerg}
18023449Sjoerg
18123449Sjoergstatic int
18290107Simpdo_move(const char *cname, int argc, char **argv)
18323449Sjoerg{
18423449Sjoerg	struct changer_move cmd;
18523449Sjoerg	int val;
18623449Sjoerg
18723449Sjoerg	/*
18823449Sjoerg	 * On a move command, we expect the following:
18923449Sjoerg	 *
19023449Sjoerg	 * <from ET> <from EU> <to ET> <to EU> [inv]
19123449Sjoerg	 *
19223449Sjoerg	 * where ET == element type and EU == element unit.
19323449Sjoerg	 */
19439227Sgibbs
19539227Sgibbs	++argv; --argc;
19639227Sgibbs
19723449Sjoerg	if (argc < 4) {
19823449Sjoerg		warnx("%s: too few arguments", cname);
19923449Sjoerg		goto usage;
20023449Sjoerg	} else if (argc > 5) {
20123449Sjoerg		warnx("%s: too many arguments", cname);
20223449Sjoerg		goto usage;
20323449Sjoerg	}
20439227Sgibbs	(void) memset(&cmd, 0, sizeof(cmd));
20523449Sjoerg
20623449Sjoerg	/* <from ET>  */
20723449Sjoerg	cmd.cm_fromtype = parse_element_type(*argv);
20823449Sjoerg	++argv; --argc;
20923449Sjoerg
21066019Sken	/* Check for voltag virtual type */
21166019Sken	if (CHET_VT == cmd.cm_fromtype) {
21266019Sken		find_element(*argv, &cmd.cm_fromtype, &cmd.cm_fromunit);
21366019Sken	} else {
21466019Sken		/* <from EU> */
21566019Sken		cmd.cm_fromunit = parse_element_unit(*argv);
21666019Sken	}
21723449Sjoerg	++argv; --argc;
21823449Sjoerg
21923449Sjoerg	/* <to ET> */
22023449Sjoerg	cmd.cm_totype = parse_element_type(*argv);
22123449Sjoerg	++argv; --argc;
22223449Sjoerg
22366019Sken	/* Check for voltag virtual type, and report error */
22466019Sken	if (CHET_VT == cmd.cm_totype)
22566019Sken		errx(1,"%s: voltag only makes sense as an element source",
22666019Sken		     cname);
22766019Sken
22823449Sjoerg	/* <to EU> */
22923449Sjoerg	cmd.cm_tounit = parse_element_unit(*argv);
23023449Sjoerg	++argv; --argc;
23123449Sjoerg
23223449Sjoerg	/* Deal with optional command modifier. */
23323449Sjoerg	if (argc) {
23423449Sjoerg		val = parse_special(*argv);
23523449Sjoerg		switch (val) {
23623449Sjoerg		case SW_INVERT:
23723449Sjoerg			cmd.cm_flags |= CM_INVERT;
23823449Sjoerg			break;
23923449Sjoerg
24023449Sjoerg		default:
24123449Sjoerg			errx(1, "%s: inappropriate modifier `%s'",
24223449Sjoerg			    cname, *argv);
24323449Sjoerg			/* NOTREACHED */
24423449Sjoerg		}
24523449Sjoerg	}
24623449Sjoerg
24723449Sjoerg	/* Send command to changer. */
24839227Sgibbs	if (ioctl(changer_fd, CHIOMOVE, &cmd))
24923449Sjoerg		err(1, "%s: CHIOMOVE", changer_name);
25023449Sjoerg
25123449Sjoerg	return (0);
25223449Sjoerg
25323449Sjoerg usage:
25439227Sgibbs	(void) fprintf(stderr, "usage: %s %s "
25593101Smarkm	    "<from ET> <from EU> <to ET> <to EU> [inv]\n", getprogname(), cname);
25623449Sjoerg	return (1);
25723449Sjoerg}
25823449Sjoerg
25923449Sjoergstatic int
26090107Simpdo_exchange(const char *cname, int argc, char **argv)
26123449Sjoerg{
26223449Sjoerg	struct changer_exchange cmd;
26323449Sjoerg	int val;
26423449Sjoerg
26523449Sjoerg	/*
26623449Sjoerg	 * On an exchange command, we expect the following:
26723449Sjoerg	 *
26823449Sjoerg  * <src ET> <src EU> <dst1 ET> <dst1 EU> [<dst2 ET> <dst2 EU>] [inv1] [inv2]
26923449Sjoerg	 *
27023449Sjoerg	 * where ET == element type and EU == element unit.
27123449Sjoerg	 */
27239227Sgibbs
27339227Sgibbs	++argv; --argc;
27439227Sgibbs
27523449Sjoerg	if (argc < 4) {
27623449Sjoerg		warnx("%s: too few arguments", cname);
27723449Sjoerg		goto usage;
27823449Sjoerg	} else if (argc > 8) {
27923449Sjoerg		warnx("%s: too many arguments", cname);
28023449Sjoerg		goto usage;
28123449Sjoerg	}
28239227Sgibbs	(void) memset(&cmd, 0, sizeof(cmd));
28323449Sjoerg
28423449Sjoerg	/* <src ET>  */
28523449Sjoerg	cmd.ce_srctype = parse_element_type(*argv);
28623449Sjoerg	++argv; --argc;
28723449Sjoerg
28866019Sken	/* Check for voltag virtual type */
28966019Sken	if (CHET_VT == cmd.ce_srctype) {
29066019Sken		find_element(*argv, &cmd.ce_srctype, &cmd.ce_srcunit);
29166019Sken	} else {
29266019Sken		/* <from EU> */
29366019Sken		cmd.ce_srcunit = parse_element_unit(*argv);
29466019Sken	}
29523449Sjoerg	++argv; --argc;
29623449Sjoerg
29723449Sjoerg	/* <dst1 ET> */
29823449Sjoerg	cmd.ce_fdsttype = parse_element_type(*argv);
29923449Sjoerg	++argv; --argc;
30023449Sjoerg
30166019Sken	/* Check for voltag virtual type */
30266019Sken	if (CHET_VT == cmd.ce_fdsttype) {
30366019Sken		find_element(*argv, &cmd.ce_fdsttype, &cmd.ce_fdstunit);
30466019Sken	} else {
30566019Sken		/* <from EU> */
30666019Sken		cmd.ce_fdstunit = parse_element_unit(*argv);
30766019Sken	}
30823449Sjoerg	++argv; --argc;
30923449Sjoerg
31023449Sjoerg	/*
31123449Sjoerg	 * If the next token is a special word or there are no more
31223449Sjoerg	 * arguments, then this is a case of simple exchange.
31323449Sjoerg	 * dst2 == src.
31423449Sjoerg	 */
31523449Sjoerg	if ((argc == 0) || is_special(*argv)) {
31623449Sjoerg		cmd.ce_sdsttype = cmd.ce_srctype;
31723449Sjoerg		cmd.ce_sdstunit = cmd.ce_srcunit;
31823449Sjoerg		goto do_special;
31923449Sjoerg	}
32023449Sjoerg
32123449Sjoerg	/* <dst2 ET> */
32223449Sjoerg	cmd.ce_sdsttype = parse_element_type(*argv);
32323449Sjoerg	++argv; --argc;
32423449Sjoerg
32566019Sken	if (CHET_VT == cmd.ce_sdsttype)
32666019Sken		errx(1,"%s %s: voltag only makes sense as an element source",
32766019Sken		     cname, *argv);
32866019Sken
32923449Sjoerg	/* <dst2 EU> */
33023449Sjoerg	cmd.ce_sdstunit = parse_element_unit(*argv);
33123449Sjoerg	++argv; --argc;
33223449Sjoerg
33323449Sjoerg do_special:
33423449Sjoerg	/* Deal with optional command modifiers. */
33523449Sjoerg	while (argc) {
33623449Sjoerg		val = parse_special(*argv);
33723449Sjoerg		++argv; --argc;
33823449Sjoerg		switch (val) {
33923449Sjoerg		case SW_INVERT1:
34023449Sjoerg			cmd.ce_flags |= CE_INVERT1;
34123449Sjoerg			break;
34223449Sjoerg
34323449Sjoerg		case SW_INVERT2:
34423449Sjoerg			cmd.ce_flags |= CE_INVERT2;
34523449Sjoerg			break;
34623449Sjoerg
34723449Sjoerg		default:
34823449Sjoerg			errx(1, "%s: inappropriate modifier `%s'",
34923449Sjoerg			    cname, *argv);
35023449Sjoerg			/* NOTREACHED */
35123449Sjoerg		}
35223449Sjoerg	}
35323449Sjoerg
35423449Sjoerg	/* Send command to changer. */
35539227Sgibbs	if (ioctl(changer_fd, CHIOEXCHANGE, &cmd))
35623449Sjoerg		err(1, "%s: CHIOEXCHANGE", changer_name);
35723449Sjoerg
35823449Sjoerg	return (0);
35923449Sjoerg
36023449Sjoerg usage:
36139227Sgibbs	(void) fprintf(stderr,
36239227Sgibbs	    "usage: %s %s <src ET> <src EU> <dst1 ET> <dst1 EU>\n"
36339227Sgibbs	    "       [<dst2 ET> <dst2 EU>] [inv1] [inv2]\n",
36493101Smarkm	    getprogname(), cname);
36523449Sjoerg	return (1);
36623449Sjoerg}
36723449Sjoerg
36823449Sjoergstatic int
36990107Simpdo_position(const char *cname, int argc, char **argv)
37023449Sjoerg{
37123449Sjoerg	struct changer_position cmd;
37223449Sjoerg	int val;
37323449Sjoerg
37423449Sjoerg	/*
37523449Sjoerg	 * On a position command, we expect the following:
37623449Sjoerg	 *
37723449Sjoerg	 * <to ET> <to EU> [inv]
37823449Sjoerg	 *
37923449Sjoerg	 * where ET == element type and EU == element unit.
38023449Sjoerg	 */
38139227Sgibbs
38239227Sgibbs	++argv; --argc;
38339227Sgibbs
38423449Sjoerg	if (argc < 2) {
38523449Sjoerg		warnx("%s: too few arguments", cname);
38623449Sjoerg		goto usage;
38723449Sjoerg	} else if (argc > 3) {
38823449Sjoerg		warnx("%s: too many arguments", cname);
38923449Sjoerg		goto usage;
39023449Sjoerg	}
39139227Sgibbs	(void) memset(&cmd, 0, sizeof(cmd));
39223449Sjoerg
39323449Sjoerg	/* <to ET>  */
39423449Sjoerg	cmd.cp_type = parse_element_type(*argv);
39523449Sjoerg	++argv; --argc;
39623449Sjoerg
39723449Sjoerg	/* <to EU> */
39823449Sjoerg	cmd.cp_unit = parse_element_unit(*argv);
39923449Sjoerg	++argv; --argc;
40023449Sjoerg
40123449Sjoerg	/* Deal with optional command modifier. */
40223449Sjoerg	if (argc) {
40323449Sjoerg		val = parse_special(*argv);
40423449Sjoerg		switch (val) {
40523449Sjoerg		case SW_INVERT:
40623449Sjoerg			cmd.cp_flags |= CP_INVERT;
40723449Sjoerg			break;
40823449Sjoerg
40923449Sjoerg		default:
41023449Sjoerg			errx(1, "%s: inappropriate modifier `%s'",
41123449Sjoerg			    cname, *argv);
41223449Sjoerg			/* NOTREACHED */
41323449Sjoerg		}
41423449Sjoerg	}
41523449Sjoerg
41623449Sjoerg	/* Send command to changer. */
41739227Sgibbs	if (ioctl(changer_fd, CHIOPOSITION, &cmd))
41823449Sjoerg		err(1, "%s: CHIOPOSITION", changer_name);
41923449Sjoerg
42023449Sjoerg	return (0);
42123449Sjoerg
42223449Sjoerg usage:
42339227Sgibbs	(void) fprintf(stderr, "usage: %s %s <to ET> <to EU> [inv]\n",
42493101Smarkm	    getprogname(), cname);
42523449Sjoerg	return (1);
42623449Sjoerg}
42723449Sjoerg
42839227Sgibbs/* ARGSUSED */
42923449Sjoergstatic int
43090107Simpdo_params(const char *cname, int argc, char **argv)
43123449Sjoerg{
43223449Sjoerg	struct changer_params data;
43339876Sken	int picker;
43423449Sjoerg
43523449Sjoerg	/* No arguments to this command. */
43639227Sgibbs
43739227Sgibbs	++argv; --argc;
43839227Sgibbs
43923449Sjoerg	if (argc) {
44041579Sbde		warnx("%s: no arguments expected", cname);
44123449Sjoerg		goto usage;
44223449Sjoerg	}
44323449Sjoerg
44423449Sjoerg	/* Get params from changer and display them. */
44539227Sgibbs	(void) memset(&data, 0, sizeof(data));
44639227Sgibbs	if (ioctl(changer_fd, CHIOGPARAMS, &data))
44723449Sjoerg		err(1, "%s: CHIOGPARAMS", changer_name);
44823449Sjoerg
44939227Sgibbs	(void) printf("%s: %d slot%s, %d drive%s, %d picker%s",
45023449Sjoerg	    changer_name,
45123449Sjoerg	    data.cp_nslots, (data.cp_nslots > 1) ? "s" : "",
45223449Sjoerg	    data.cp_ndrives, (data.cp_ndrives > 1) ? "s" : "",
45323449Sjoerg	    data.cp_npickers, (data.cp_npickers > 1) ? "s" : "");
45423449Sjoerg	if (data.cp_nportals)
45539227Sgibbs		(void) printf(", %d portal%s", data.cp_nportals,
45623449Sjoerg		    (data.cp_nportals > 1) ? "s" : "");
45723449Sjoerg
45839876Sken	/* Get current picker from changer and display it. */
45939876Sken	if (ioctl(changer_fd, CHIOGPICKER, &picker))
46039876Sken		err(1, "%s: CHIOGPICKER", changer_name);
46139876Sken
46239876Sken	(void) printf("\n%s: current picker: %d\n", changer_name, picker);
46339876Sken
46423449Sjoerg	return (0);
46523449Sjoerg
46623449Sjoerg usage:
46793101Smarkm	(void) fprintf(stderr, "usage: %s %s\n", getprogname(), cname);
46823449Sjoerg	return (1);
46923449Sjoerg}
47023449Sjoerg
47139227Sgibbs/* ARGSUSED */
47223449Sjoergstatic int
47390107Simpdo_getpicker(const char *cname, int argc, char **argv)
47423449Sjoerg{
47523449Sjoerg	int picker;
47623449Sjoerg
47723449Sjoerg	/* No arguments to this command. */
47839227Sgibbs
47939227Sgibbs	++argv; --argc;
48039227Sgibbs
48123449Sjoerg	if (argc) {
48223449Sjoerg		warnx("%s: no arguments expected", cname);
48323449Sjoerg		goto usage;
48423449Sjoerg	}
48523449Sjoerg
48623449Sjoerg	/* Get current picker from changer and display it. */
48739227Sgibbs	if (ioctl(changer_fd, CHIOGPICKER, &picker))
48823449Sjoerg		err(1, "%s: CHIOGPICKER", changer_name);
48923449Sjoerg
49039227Sgibbs	(void) printf("%s: current picker: %d\n", changer_name, picker);
49123449Sjoerg
49223449Sjoerg	return (0);
49323449Sjoerg
49423449Sjoerg usage:
49593101Smarkm	(void) fprintf(stderr, "usage: %s %s\n", getprogname(), cname);
49623449Sjoerg	return (1);
49723449Sjoerg}
49823449Sjoerg
49923449Sjoergstatic int
50090107Simpdo_setpicker(const char *cname, int argc, char **argv)
50123449Sjoerg{
50223449Sjoerg	int picker;
50323449Sjoerg
50439227Sgibbs	++argv; --argc;
50539227Sgibbs
50623449Sjoerg	if (argc < 1) {
50723449Sjoerg		warnx("%s: too few arguments", cname);
50823449Sjoerg		goto usage;
50923449Sjoerg	} else if (argc > 1) {
51023449Sjoerg		warnx("%s: too many arguments", cname);
51123449Sjoerg		goto usage;
51223449Sjoerg	}
51323449Sjoerg
51423449Sjoerg	picker = parse_element_unit(*argv);
51523449Sjoerg
51623449Sjoerg	/* Set the changer picker. */
51739227Sgibbs	if (ioctl(changer_fd, CHIOSPICKER, &picker))
51823449Sjoerg		err(1, "%s: CHIOSPICKER", changer_name);
51923449Sjoerg
52023449Sjoerg	return (0);
52123449Sjoerg
52223449Sjoerg usage:
52393101Smarkm	(void) fprintf(stderr, "usage: %s %s <picker>\n", getprogname(), cname);
52423449Sjoerg	return (1);
52523449Sjoerg}
52623449Sjoerg
52723449Sjoergstatic int
52890107Simpdo_status(const char *cname, int argc, char **argv)
52923449Sjoerg{
53039227Sgibbs	struct changer_params cp;
53139227Sgibbs	struct changer_element_status_request cesr;
53291086Smarkm	int i;
53391086Smarkm	u_int16_t base, count, chet, schet, echet;
53479121Smikeh	const char *description;
53539227Sgibbs	int pvoltag = 0;
53639227Sgibbs	int avoltag = 0;
53739227Sgibbs	int sense = 0;
53839227Sgibbs	int scsi = 0;
53939227Sgibbs	int source = 0;
54039227Sgibbs	int intaddr = 0;
54139227Sgibbs	int c;
54223449Sjoerg
54326361Scharnier	count = 0;
54439227Sgibbs	base = 0;
54526361Scharnier	description = NULL;
54626361Scharnier
54739227Sgibbs	optind = optreset = 1;
54847442Simp	while ((c = getopt(argc, argv, "vVsSbaI")) != -1) {
54939227Sgibbs		switch (c) {
55039227Sgibbs		case 'v':
55139227Sgibbs			pvoltag = 1;
55239227Sgibbs			break;
55339227Sgibbs		case 'V':
55439227Sgibbs			avoltag = 1;
55539227Sgibbs			break;
55639227Sgibbs		case 's':
55739227Sgibbs			sense = 1;
55839227Sgibbs			break;
55939227Sgibbs		case 'S':
56039227Sgibbs			source = 1;
56139227Sgibbs			break;
56239227Sgibbs		case 'b':
56339227Sgibbs			scsi = 1;
56439227Sgibbs			break;
56539227Sgibbs		case 'I':
56639227Sgibbs			intaddr = 1;
56739227Sgibbs			break;
56839227Sgibbs		case 'a':
56939227Sgibbs			pvoltag = avoltag = source = sense = scsi = intaddr = 1;
57039227Sgibbs			break;
57139227Sgibbs		default:
57241579Sbde			warnx("%s: bad option", cname);
57339227Sgibbs			goto usage;
57439227Sgibbs		}
57539227Sgibbs	}
57639227Sgibbs
57739227Sgibbs	argc -= optind;
57839227Sgibbs	argv += optind;
57939227Sgibbs
58023449Sjoerg	/*
58123449Sjoerg	 * On a status command, we expect the following:
58223449Sjoerg	 *
58339227Sgibbs	 * [<ET> [<start> [<end>] ] ]
58423449Sjoerg	 *
58539227Sgibbs	 * where ET == element type, start == first element to report,
58639227Sgibbs	 * end == number of elements to report
58723449Sjoerg	 *
58823449Sjoerg	 * If we get no arguments, we get the status of all
58923449Sjoerg	 * known element types.
59023449Sjoerg	 */
59139227Sgibbs	if (argc > 3) {
59223449Sjoerg		warnx("%s: too many arguments", cname);
59323449Sjoerg		goto usage;
59423449Sjoerg	}
59523449Sjoerg
59623449Sjoerg	/*
59723449Sjoerg	 * Get params from changer.  Specifically, we need the element
59823449Sjoerg	 * counts.
59923449Sjoerg	 */
60039227Sgibbs	if (ioctl(changer_fd, CHIOGPARAMS, (char *)&cp))
60123449Sjoerg		err(1, "%s: CHIOGPARAMS", changer_name);
60223449Sjoerg
60339227Sgibbs	if (argc > 0)
60439227Sgibbs		schet = echet = parse_element_type(argv[0]);
60523449Sjoerg	else {
60623449Sjoerg		schet = CHET_MT;
60723449Sjoerg		echet = CHET_DT;
60823449Sjoerg	}
60939227Sgibbs	if (argc > 1) {
61091086Smarkm		base = (u_int16_t)atol(argv[1]);
61139227Sgibbs		count = 1;
61239227Sgibbs	}
61339227Sgibbs	if (argc > 2)
61491086Smarkm		count = (u_int16_t)atol(argv[2]) - base + 1;
61523449Sjoerg
61623449Sjoerg	for (chet = schet; chet <= echet; ++chet) {
61723449Sjoerg		switch (chet) {
61823449Sjoerg		case CHET_MT:
61939227Sgibbs			if (count == 0)
62039227Sgibbs				count = cp.cp_npickers;
62139227Sgibbs			else if (count > cp.cp_npickers)
62239227Sgibbs				errx(1, "not that many pickers in device");
62323449Sjoerg			description = "picker";
62423449Sjoerg			break;
62523449Sjoerg
62623449Sjoerg		case CHET_ST:
62739227Sgibbs			if (count == 0)
62839227Sgibbs				count = cp.cp_nslots;
62939227Sgibbs			else if (count > cp.cp_nslots)
63039227Sgibbs				errx(1, "not that many slots in device");
63123449Sjoerg			description = "slot";
63223449Sjoerg			break;
63323449Sjoerg
63423449Sjoerg		case CHET_IE:
63539227Sgibbs			if (count == 0)
63639227Sgibbs				count = cp.cp_nportals;
63739227Sgibbs			else if (count > cp.cp_nportals)
63839227Sgibbs				errx(1, "not that many portals in device");
63923449Sjoerg			description = "portal";
64023449Sjoerg			break;
64123449Sjoerg
64223449Sjoerg		case CHET_DT:
64339227Sgibbs			if (count == 0)
64439227Sgibbs				count = cp.cp_ndrives;
64539227Sgibbs			else if (count > cp.cp_ndrives)
64639227Sgibbs				errx(1, "not that many drives in device");
64723449Sjoerg			description = "drive";
64823449Sjoerg			break;
64939227Sgibbs
65039227Sgibbs 		default:
65139227Sgibbs 			/* To appease gcc -Wuninitialized. */
65239227Sgibbs 			count = 0;
65339227Sgibbs 			description = NULL;
65423449Sjoerg		}
65523449Sjoerg
65623449Sjoerg		if (count == 0) {
65723449Sjoerg			if (argc == 0)
65823449Sjoerg				continue;
65923449Sjoerg			else {
66023449Sjoerg				printf("%s: no %s elements\n",
66123449Sjoerg				    changer_name, description);
66223449Sjoerg				return (0);
66323449Sjoerg			}
66423449Sjoerg		}
66523449Sjoerg
66639227Sgibbs		bzero(&cesr, sizeof(cesr));
66739227Sgibbs		cesr.cesr_element_type = chet;
66839227Sgibbs		cesr.cesr_element_base = base;
66939227Sgibbs		cesr.cesr_element_count = count;
67039227Sgibbs		/* Allocate storage for the status structures. */
67166019Sken		cesr.cesr_element_status =
67266019Sken		  (struct changer_element_status *)
67379253Smikeh		  calloc((size_t)count, sizeof(struct changer_element_status));
67439227Sgibbs
67539227Sgibbs		if (!cesr.cesr_element_status)
67623449Sjoerg			errx(1, "can't allocate status storage");
67723449Sjoerg
67839227Sgibbs		if (avoltag || pvoltag)
67939227Sgibbs			cesr.cesr_flags |= CESR_VOLTAGS;
68023449Sjoerg
68139227Sgibbs		if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr)) {
68239227Sgibbs			free(cesr.cesr_element_status);
68323449Sjoerg			err(1, "%s: CHIOGSTATUS", changer_name);
68423449Sjoerg		}
68523449Sjoerg
68639227Sgibbs		/* Dump the status for each reported element. */
68723449Sjoerg		for (i = 0; i < count; ++i) {
68839227Sgibbs			struct changer_element_status *ces =
68939227Sgibbs			         &(cesr.cesr_element_status[i]);
69039227Sgibbs			printf("%s %d: %s", description, ces->ces_addr,
69139227Sgibbs			    bits_to_string(ces->ces_flags,
69239227Sgibbs					   CESTATUS_BITS));
69339227Sgibbs			if (sense)
69439227Sgibbs				printf(" sense: <0x%02x/0x%02x>",
69539227Sgibbs				       ces->ces_sensecode,
69639227Sgibbs				       ces->ces_sensequal);
69739227Sgibbs			if (pvoltag)
69839227Sgibbs				printf(" voltag: <%s:%d>",
69939227Sgibbs				       ces->ces_pvoltag.cv_volid,
70039227Sgibbs				       ces->ces_pvoltag.cv_serial);
70139227Sgibbs			if (avoltag)
70239227Sgibbs				printf(" avoltag: <%s:%d>",
70339227Sgibbs				       ces->ces_avoltag.cv_volid,
70439227Sgibbs				       ces->ces_avoltag.cv_serial);
70546073Simp			if (source) {
70639227Sgibbs				if (ces->ces_flags & CES_SOURCE_VALID)
70739227Sgibbs					printf(" source: <%s %d>",
70839227Sgibbs					       element_type_name(
70939227Sgibbs						       ces->ces_source_type),
71039227Sgibbs					       ces->ces_source_addr);
71139227Sgibbs				else
71239227Sgibbs					printf(" source: <>");
71346073Simp			}
71439227Sgibbs			if (intaddr)
71539227Sgibbs				printf(" intaddr: <%d>", ces->ces_int_addr);
71639227Sgibbs			if (scsi) {
71739227Sgibbs				printf(" scsi: <");
71839227Sgibbs				if (ces->ces_flags & CES_SCSIID_VALID)
71939227Sgibbs					printf("%d", ces->ces_scsi_id);
72039227Sgibbs				else
72139227Sgibbs					putchar('?');
72239227Sgibbs				putchar(':');
72339227Sgibbs				if (ces->ces_flags & CES_LUN_VALID)
72439227Sgibbs					printf("%d", ces->ces_scsi_lun);
72539227Sgibbs				else
72639227Sgibbs					putchar('?');
72739227Sgibbs				putchar('>');
72839227Sgibbs			}
729252214Sken			if (ces->ces_designator_length > 0)
730252214Sken				print_designator(ces->ces_designator,
731252214Sken						 ces->ces_code_set,
732252214Sken						 ces->ces_designator_length);
73339227Sgibbs			putchar('\n');
73423449Sjoerg		}
73523449Sjoerg
73639227Sgibbs		free(cesr.cesr_element_status);
73739227Sgibbs		count = 0;
73823449Sjoerg	}
73923449Sjoerg
74023449Sjoerg	return (0);
74123449Sjoerg
74223449Sjoerg usage:
74339227Sgibbs	(void) fprintf(stderr, "usage: %s %s [-vVsSbaA] [<element type> [<start-addr> [<end-addr>] ] ]\n",
74493101Smarkm		       getprogname(), cname);
74523449Sjoerg	return (1);
74623449Sjoerg}
74723449Sjoerg
74823449Sjoergstatic int
74990107Simpdo_ielem(const char *cname, int argc, char **argv)
75023449Sjoerg{
75139227Sgibbs	int timeout = 0;
75239227Sgibbs
75339227Sgibbs	if (argc == 2) {
75439227Sgibbs		timeout = atol(argv[1]);
75539227Sgibbs	} else if (argc > 1) {
75639227Sgibbs		warnx("%s: too many arguments", cname);
75739227Sgibbs		goto usage;
75839227Sgibbs	}
75939227Sgibbs
76039227Sgibbs	if (ioctl(changer_fd, CHIOIELEM, &timeout))
76139227Sgibbs		err(1, "%s: CHIOIELEM", changer_name);
76239227Sgibbs
76339227Sgibbs	return (0);
76439227Sgibbs
76539227Sgibbs usage:
76639227Sgibbs	(void) fprintf(stderr, "usage: %s %s [<timeout>]\n",
76793101Smarkm		       getprogname(), cname);
76839227Sgibbs	return (1);
76939227Sgibbs}
77039227Sgibbs
77139227Sgibbsstatic int
77290107Simpdo_voltag(const char *cname, int argc, char **argv)
77339227Sgibbs{
77439227Sgibbs	int force = 0;
77539227Sgibbs	int clear = 0;
77639227Sgibbs	int alternate = 0;
77739227Sgibbs	int c;
77839227Sgibbs	struct changer_set_voltag_request csvr;
77939227Sgibbs
78039227Sgibbs	bzero(&csvr, sizeof(csvr));
78139227Sgibbs
78239227Sgibbs	optind = optreset = 1;
78347442Simp	while ((c = getopt(argc, argv, "fca")) != -1) {
78439227Sgibbs		switch (c) {
78539227Sgibbs		case 'f':
78639227Sgibbs			force = 1;
78739227Sgibbs			break;
78839227Sgibbs		case 'c':
78939227Sgibbs			clear = 1;
79039227Sgibbs			break;
79139227Sgibbs		case 'a':
79239227Sgibbs			alternate = 1;
79339227Sgibbs			break;
79439227Sgibbs		default:
79541579Sbde			warnx("%s: bad option", cname);
79639227Sgibbs			goto usage;
79739227Sgibbs		}
79839227Sgibbs	}
79939227Sgibbs
80039227Sgibbs	argc -= optind;
80139227Sgibbs	argv += optind;
80239227Sgibbs
80339227Sgibbs	if (argc < 2) {
80441579Sbde		warnx("%s: missing element specification", cname);
80539227Sgibbs		goto usage;
80639227Sgibbs	}
80739227Sgibbs
80839227Sgibbs	csvr.csvr_type = parse_element_type(argv[0]);
80991086Smarkm	csvr.csvr_addr = (u_int16_t)atol(argv[1]);
81039227Sgibbs
81139227Sgibbs	if (!clear) {
81239227Sgibbs		if (argc < 3 || argc > 4) {
81341579Sbde			warnx("%s: missing argument", cname);
81439227Sgibbs			goto usage;
81539227Sgibbs		}
81639227Sgibbs
81739227Sgibbs		if (force)
81839227Sgibbs			csvr.csvr_flags = CSVR_MODE_REPLACE;
81939227Sgibbs		else
82039227Sgibbs			csvr.csvr_flags = CSVR_MODE_SET;
82139227Sgibbs
82239227Sgibbs		if (strlen(argv[2]) > sizeof(csvr.csvr_voltag.cv_volid)) {
82341579Sbde			warnx("%s: volume label too long", cname);
82439227Sgibbs			goto usage;
82539227Sgibbs		}
82639227Sgibbs
82779253Smikeh		strlcpy((char *)csvr.csvr_voltag.cv_volid, argv[2],
82839227Sgibbs		       sizeof(csvr.csvr_voltag.cv_volid));
82939227Sgibbs
83039227Sgibbs		if (argc == 4) {
83191086Smarkm			csvr.csvr_voltag.cv_serial = (u_int16_t)atol(argv[3]);
83239227Sgibbs		}
83339227Sgibbs	} else {
83439227Sgibbs		if (argc != 2) {
83541579Sbde			warnx("%s: unexpected argument", cname);
83639227Sgibbs			goto usage;
83739227Sgibbs		}
83839227Sgibbs		csvr.csvr_flags = CSVR_MODE_CLEAR;
83939227Sgibbs	}
84039227Sgibbs
84139227Sgibbs	if (alternate) {
84239227Sgibbs		csvr.csvr_flags |= CSVR_ALTERNATE;
84339227Sgibbs	}
84439227Sgibbs
84539227Sgibbs	if (ioctl(changer_fd, CHIOSETVOLTAG, &csvr))
84639227Sgibbs		err(1, "%s: CHIOSETVOLTAG", changer_name);
84739227Sgibbs
84839227Sgibbs	return 0;
84939227Sgibbs usage:
85039227Sgibbs	(void) fprintf(stderr,
85139227Sgibbs		       "usage: %s %s [-fca] <element> [<voltag> [<vsn>] ]\n",
85293101Smarkm		       getprogname(), cname);
85339227Sgibbs	return 1;
85439227Sgibbs}
85539227Sgibbs
85691086Smarkmstatic u_int16_t
85790107Simpparse_element_type(char *cp)
85839227Sgibbs{
85923449Sjoerg	int i;
86023449Sjoerg
86123449Sjoerg	for (i = 0; elements[i].et_name != NULL; ++i)
86223449Sjoerg		if (strcmp(elements[i].et_name, cp) == 0)
86391086Smarkm			return ((u_int16_t)elements[i].et_type);
86423449Sjoerg
86523449Sjoerg	errx(1, "invalid element type `%s'", cp);
86639227Sgibbs	/* NOTREACHED */
86723449Sjoerg}
86823449Sjoerg
86939227Sgibbsstatic const char *
87090107Simpelement_type_name(int et)
87139227Sgibbs{
87239227Sgibbs	int i;
87339227Sgibbs
87439227Sgibbs	for (i = 0; elements[i].et_name != NULL; i++)
87539227Sgibbs		if (elements[i].et_type == et)
87639227Sgibbs			return elements[i].et_name;
87739227Sgibbs
87839227Sgibbs	return "unknown";
87939227Sgibbs}
88039227Sgibbs
88191086Smarkmstatic u_int16_t
88290107Simpparse_element_unit(char *cp)
88323449Sjoerg{
88423449Sjoerg	int i;
88523449Sjoerg	char *p;
88623449Sjoerg
88723449Sjoerg	i = (int)strtol(cp, &p, 10);
88823449Sjoerg	if ((i < 0) || (*p != '\0'))
88923449Sjoerg		errx(1, "invalid unit number `%s'", cp);
89023449Sjoerg
89191086Smarkm	return ((u_int16_t)i);
89223449Sjoerg}
89323449Sjoerg
89423449Sjoergstatic int
89590107Simpparse_special(char *cp)
89623449Sjoerg{
89723449Sjoerg	int val;
89823449Sjoerg
89923449Sjoerg	val = is_special(cp);
90023449Sjoerg	if (val)
90123449Sjoerg		return (val);
90223449Sjoerg
90323449Sjoerg	errx(1, "invalid modifier `%s'", cp);
90439227Sgibbs	/* NOTREACHED */
90523449Sjoerg}
90623449Sjoerg
90723449Sjoergstatic int
90890107Simpis_special(char *cp)
90923449Sjoerg{
91023449Sjoerg	int i;
91123449Sjoerg
91223449Sjoerg	for (i = 0; specials[i].sw_name != NULL; ++i)
91323449Sjoerg		if (strcmp(specials[i].sw_name, cp) == 0)
91423449Sjoerg			return (specials[i].sw_value);
91523449Sjoerg
91623449Sjoerg	return (0);
91723449Sjoerg}
91823449Sjoerg
91948073Skrisstatic const char *
92090107Simpbits_to_string(ces_status_flags v, const char *cp)
92123449Sjoerg{
92223449Sjoerg	const char *np;
92323449Sjoerg	char f, sep, *bp;
92423449Sjoerg	static char buf[128];
92523449Sjoerg
92623449Sjoerg	bp = buf;
92739227Sgibbs	(void) memset(buf, 0, sizeof(buf));
92823449Sjoerg
92923449Sjoerg	for (sep = '<'; (f = *cp++) != 0; cp = np) {
93023449Sjoerg		for (np = cp; *np >= ' ';)
93123449Sjoerg			np++;
93291086Smarkm		if (((int)v & (1 << (f - 1))) == 0)
93323449Sjoerg			continue;
93491086Smarkm		(void) snprintf(bp, sizeof(buf) - (size_t)(bp - &buf[0]),
93548073Skris			"%c%.*s", sep, (int)(long)(np - cp), cp);
93647816Skris		bp += strlen(bp);
93723449Sjoerg		sep = ',';
93823449Sjoerg	}
93923449Sjoerg	if (sep != '<')
94023449Sjoerg		*bp = '>';
94123449Sjoerg
94223449Sjoerg	return (buf);
94323449Sjoerg}
94466019Sken/*
94566019Sken * do_return()
94666019Sken *
94766019Sken * Given an element reference, ask the changer/picker to move that
94866019Sken * element back to its source slot.
94966019Sken */
95066019Skenstatic int
95190107Simpdo_return(const char *cname, int argc, char **argv)
95266019Sken{
95366019Sken	struct changer_element_status *ces;
95466019Sken	struct changer_move cmd;
95591665Simp	uint16_t	type, element;
95623449Sjoerg
95766019Sken	++argv; --argc;
95866019Sken
95966019Sken	if (argc < 2) {
96066019Sken		warnx("%s: too few arguments", cname);
96166019Sken		goto usage;
96266019Sken	} else if (argc > 3) {
96366019Sken		warnx("%s: too many arguments", cname);
96466019Sken		goto usage;
96566019Sken	}
96666019Sken
96766019Sken	type = parse_element_type(*argv);
96866019Sken	++argv; --argc;
96966019Sken
97066019Sken	/* Handle voltag virtual Changer Element Type */
97166019Sken	if (CHET_VT == type) {
97266019Sken		find_element(*argv, &type, &element);
97366019Sken	} else {
97466019Sken		element = parse_element_unit(*argv);
97566019Sken	}
97666019Sken	++argv; --argc;
97766019Sken
97879253Smikeh	/* Get the status */
979184484Sjoerg	ces = get_element_status((unsigned int)type, (unsigned int)element,
980184484Sjoerg	    CHET_VT == type);
98166019Sken
98266019Sken	if (NULL == ces)
98366019Sken		errx(1, "%s: null element status pointer", cname);
98466019Sken
98566019Sken	if (!(ces->ces_flags & CES_SOURCE_VALID))
98666019Sken		errx(1, "%s: no source information", cname);
98766019Sken
98866019Sken	(void) memset(&cmd, 0, sizeof(cmd));
98966019Sken
99066019Sken	cmd.cm_fromtype = type;
99166019Sken	cmd.cm_fromunit = element;
99266019Sken	cmd.cm_totype = ces->ces_source_type;
99366019Sken	cmd.cm_tounit = ces->ces_source_addr;
99466019Sken
99566019Sken	if (ioctl(changer_fd, CHIOMOVE, &cmd) == -1)
99666019Sken		err(1, "%s: CHIOMOVE", changer_name);
99766019Sken	free(ces);
99866019Sken
99966019Sken	return(0);
100066019Sken
100166019Skenusage:
100266019Sken	(void) fprintf(stderr, "usage: %s %s "
100393101Smarkm	    "<from ET> <from EU>\n", getprogname(), cname);
100466019Sken	return(1);
100566019Sken}
100666019Sken
100766019Sken/*
100866019Sken * get_element_status()
100966019Sken *
101066019Sken * return a *cesr for the specified changer element.  This
101166019Sken * routing will malloc()/calloc() the memory.  The caller
101266019Sken * should free() it when done.
101366019Sken */
101466019Skenstatic struct changer_element_status *
1015184484Sjoergget_element_status(unsigned int type, unsigned int element, int use_voltags)
101666019Sken{
101766019Sken	struct changer_element_status_request cesr;
101866019Sken	struct changer_element_status *ces;
101966019Sken
102066019Sken	ces = (struct changer_element_status *)
102179253Smikeh	    calloc((size_t)1, sizeof(struct changer_element_status));
102266019Sken
102366019Sken	if (NULL == ces)
102466019Sken		errx(1, "can't allocate status storage");
102566019Sken
102666019Sken	(void)memset(&cesr, 0, sizeof(cesr));
102766019Sken
102891665Simp	cesr.cesr_element_type = (uint16_t)type;
102991665Simp	cesr.cesr_element_base = (uint16_t)element;
103066019Sken	cesr.cesr_element_count = 1;		/* Only this one element */
1031184484Sjoerg	if (use_voltags)
1032184484Sjoerg		cesr.cesr_flags |= CESR_VOLTAGS; /* Grab voltags as well */
103366019Sken	cesr.cesr_element_status = ces;
103466019Sken
103566019Sken	if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
103666019Sken		free(ces);
103766019Sken		err(1, "%s: CHIOGSTATUS", changer_name);
103866019Sken		/* NOTREACHED */
103966019Sken	}
104066019Sken
104166019Sken	return ces;
104266019Sken}
104366019Sken
104466019Sken
104566019Sken/*
104666019Sken * find_element()
104766019Sken *
104866019Sken * Given a <voltag> find the chager element and unit, or exit
104966019Sken * with an error if it isn't found.  We grab the changer status
105066019Sken * and iterate until we find a match, or crap out.
105166019Sken */
105223449Sjoergstatic void
105391665Simpfind_element(char *voltag, uint16_t *et, uint16_t *eu)
105466019Sken{
105566019Sken	struct changer_params cp;
105666019Sken	struct changer_element_status_request cesr;
105766019Sken	struct changer_element_status *ch_ces, *ces;
105879253Smikeh	int found = 0;
105979253Smikeh	size_t elem, total_elem;
106066019Sken
106166019Sken	/*
106266019Sken	 * Get the changer parameters, we're interested in the counts
106366019Sken	 * for all types of elements to perform our search.
106466019Sken	 */
106566019Sken	if (ioctl(changer_fd, CHIOGPARAMS, (char *)&cp))
106666019Sken		err(1, "%s: CHIOGPARAMS", changer_name);
106766019Sken
106866019Sken	/* Allocate some memory for the results */
106966019Sken	total_elem = (cp.cp_nslots + cp.cp_ndrives
107066019Sken	    + cp.cp_npickers + cp.cp_nportals);
107166019Sken
107266019Sken	ch_ces = (struct changer_element_status *)
107366019Sken	    calloc(total_elem, sizeof(struct changer_element_status));
107466019Sken
107566019Sken	if (NULL == ch_ces)
107666019Sken		errx(1, "can't allocate status storage");
107766019Sken
107866019Sken	ces = ch_ces;
107966019Sken
108066019Sken	/* Read in the changer slots */
108166019Sken	if (cp.cp_nslots > 0) {
1082208730Suqs		(void) memset(&cesr, 0, sizeof(cesr));
108366019Sken		cesr.cesr_element_type = CHET_ST;
108466019Sken		cesr.cesr_element_base = 0;
108566019Sken		cesr.cesr_element_count = cp.cp_nslots;
108666019Sken		cesr.cesr_flags |= CESR_VOLTAGS;
108766019Sken		cesr.cesr_element_status = ces;
108866019Sken
108966019Sken		if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
109066019Sken			free(ch_ces);
109166019Sken			err(1, "%s: CHIOGSTATUS", changer_name);
109266019Sken		}
109366019Sken		ces += cp.cp_nslots;
109466019Sken	}
109566019Sken
109666019Sken	/* Read in the drive information */
109766019Sken	if (cp.cp_ndrives > 0 ) {
109866019Sken
109966019Sken		(void) memset(&cesr, 0, sizeof(cesr));
110066019Sken		cesr.cesr_element_type = CHET_DT;
110166019Sken		cesr.cesr_element_base = 0;
110266019Sken		cesr.cesr_element_count = cp.cp_ndrives;
110366019Sken		cesr.cesr_flags |= CESR_VOLTAGS;
110466019Sken		cesr.cesr_element_status = ces;
110566019Sken
110666019Sken		if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
110766019Sken			free(ch_ces);
110866019Sken			err(1, "%s: CHIOGSTATUS", changer_name);
110966019Sken		}
111066019Sken		ces += cp.cp_ndrives;
111166019Sken	}
111266019Sken
111366019Sken	/* Read in the portal information */
111466019Sken	if (cp.cp_nportals > 0 ) {
111566019Sken		(void) memset(&cesr, 0, sizeof(cesr));
111666019Sken		cesr.cesr_element_type = CHET_IE;
111766019Sken		cesr.cesr_element_base = 0;
111866019Sken		cesr.cesr_element_count = cp.cp_nportals;
111966019Sken		cesr.cesr_flags |= CESR_VOLTAGS;
112066019Sken		cesr.cesr_element_status = ces;
112166019Sken
112266019Sken		if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
112366019Sken			free(ch_ces);
112466019Sken			err(1, "%s: CHIOGSTATUS", changer_name);
112566019Sken		}
112666019Sken		ces += cp.cp_nportals;
112766019Sken	}
112866019Sken
112966019Sken	/* Read in the picker information */
113066019Sken	if (cp.cp_npickers > 0) {
113166019Sken		(void) memset(&cesr, 0, sizeof(cesr));
113266019Sken		cesr.cesr_element_type = CHET_MT;
113366019Sken		cesr.cesr_element_base = 0;
113466019Sken		cesr.cesr_element_count = cp.cp_npickers;
113566019Sken		cesr.cesr_flags |= CESR_VOLTAGS;
113666019Sken		cesr.cesr_element_status = ces;
113766019Sken
113866019Sken		if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
113966019Sken			free(ch_ces);
114066019Sken			err(1, "%s: CHIOGSTATUS", changer_name);
114166019Sken		}
114266019Sken	}
114366019Sken
114466019Sken	/*
114566019Sken	 * Now search the list the specified <voltag>
114666019Sken	 */
114766019Sken	for (elem = 0; elem <= total_elem; ++elem) {
114866019Sken
114966019Sken		ces = &ch_ces[elem];
115066019Sken
115166019Sken		/* Make sure we have a tape in this element */
115266019Sken		if ((ces->ces_flags & (CES_STATUS_ACCESS|CES_STATUS_FULL))
115366019Sken		    != (CES_STATUS_ACCESS|CES_STATUS_FULL))
115466019Sken			continue;
115566019Sken
115666019Sken		/* Check to see if it is our target */
115779253Smikeh		if (strcasecmp(voltag,
115879253Smikeh		    (const char *)ces->ces_pvoltag.cv_volid) == 0) {
115966019Sken			*et = ces->ces_type;
116066019Sken			*eu = ces->ces_addr;
116166019Sken			++found;
116266019Sken			break;
116366019Sken		}
116466019Sken	}
116566019Sken	if (!found) {
116666019Sken		errx(1, "%s: unable to locate voltag: %s", changer_name,
116766019Sken		     voltag);
116866019Sken	}
116966019Sken	free(ch_ces);
117066019Sken	return;
117166019Sken}
117266019Sken
117366019Skenstatic void
117490107Simpcleanup(void)
117523449Sjoerg{
117623449Sjoerg	/* Simple enough... */
117723449Sjoerg	(void)close(changer_fd);
117823449Sjoerg}
117923449Sjoerg
118023449Sjoergstatic void
118190107Simpusage(void)
118223449Sjoerg{
118390107Simp	(void)fprintf(stderr, "usage: %s [-f changer] command [-<flags>] "
118493101Smarkm		"arg1 arg2 [arg3 [...]]\n", getprogname());
118523449Sjoerg	exit(1);
118623449Sjoerg}
1187252214Sken
1188252214Sken#define	UTF8CODESET	"UTF-8"
1189252214Sken
1190252214Skenstatic void
1191252214Skenprint_designator(const char *designator, u_int8_t code_set,
1192252214Sken    u_int8_t designator_length)
1193252214Sken{
1194252214Sken	printf(" serial number: <");
1195252214Sken	switch (code_set) {
1196252214Sken	case CES_CODE_SET_ASCII: {
1197252214Sken		/*
1198252214Sken		 * The driver insures that the string is always NUL terminated.
1199252214Sken		 */
1200252214Sken		printf("%s", designator);
1201252214Sken		break;
1202252214Sken	}
1203252214Sken	case CES_CODE_SET_UTF_8: {
1204252214Sken		char *cs_native;
1205252214Sken
1206252214Sken		setlocale(LC_ALL, "");
1207252214Sken		cs_native = nl_langinfo(CODESET);
1208252214Sken
1209252214Sken		/* See if we can natively print UTF-8 */
1210252214Sken		if (strcmp(cs_native, UTF8CODESET) == 0)
1211252214Sken			cs_native = NULL;
1212252214Sken
1213252214Sken		if (cs_native == NULL) {
1214252214Sken			/* We can natively print UTF-8, so use printf. */
1215252214Sken			printf("%s", designator);
1216252214Sken		} else {
1217252214Sken			int i;
1218252214Sken
1219252214Sken			/*
1220252214Sken			 * We can't natively print UTF-8.  We should
1221252214Sken			 * convert it to the terminal's codeset, but that
1222252214Sken			 * requires iconv(3) and FreeBSD doesn't have
1223252214Sken			 * iconv(3) in the base system yet.  So we use %XX
1224252214Sken			 * notation for non US-ASCII characters instead.
1225252214Sken			 */
1226252214Sken			for (i = 0; i < designator_length &&
1227252214Sken			    designator[i] != '\0'; i++) {
1228252214Sken				if ((unsigned char)designator[i] < 0x80)
1229252214Sken					printf("%c", designator[i]);
1230252214Sken				else
1231252214Sken					printf("%%%02x",
1232252214Sken					    (unsigned char)designator[i]);
1233252214Sken			}
1234252214Sken		}
1235252214Sken		break;
1236252214Sken	}
1237252214Sken	case CES_CODE_SET_BINARY: {
1238252214Sken		int i;
1239252214Sken
1240252214Sken		for (i = 0; i < designator_length; i++)
1241252214Sken			printf("%02X%s", designator[i],
1242252214Sken			    (i == designator_length - 1) ? "" : " ");
1243252214Sken		break;
1244252214Sken	}
1245252214Sken	default:
1246252214Sken		break;
1247252214Sken	}
1248252214Sken	printf(">");
1249252214Sken}
1250