mt.c revision 289677
178189Sbrian/*
278189Sbrian * Copyright (c) 1980, 1993
378189Sbrian *	The Regents of the University of California.  All rights reserved.
478189Sbrian *
578189Sbrian * Redistribution and use in source and binary forms, with or without
66059Samurai * modification, are permitted provided that the following conditions
778189Sbrian * are met:
878189Sbrian * 1. Redistributions of source code must retain the above copyright
978189Sbrian *    notice, this list of conditions and the following disclaimer.
1078189Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1178189Sbrian *    notice, this list of conditions and the following disclaimer in the
1278189Sbrian *    documentation and/or other materials provided with the distribution.
1378189Sbrian * 4. Neither the name of the University nor the names of its contributors
1478189Sbrian *    may be used to endorse or promote products derived from this software
156059Samurai *    without specific prior written permission.
1678189Sbrian *
1778189Sbrian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1878189Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1978189Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2078189Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2178189Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2278189Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2378189Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2478189Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2578189Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2678189Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
276059Samurai * SUCH DAMAGE.
2850479Speter */
296059Samurai/*-
3078189Sbrian * Copyright (c) 2013, 2014, 2015 Spectra Logic Corporation
3143313Sbrian * All rights reserved.
3230715Sbrian *
3326031Sbrian * Redistribution and use in source and binary forms, with or without
3430715Sbrian * modification, are permitted provided that the following conditions
3526031Sbrian * are met:
3630715Sbrian * 1. Redistributions of source code must retain the above copyright
3726031Sbrian *    notice, this list of conditions, and the following disclaimer,
3830715Sbrian *    without modification.
3936285Sbrian * 2. Redistributions in binary form must reproduce at minimum a disclaimer
4030715Sbrian *    substantially similar to the "NO WARRANTY" disclaimer below
4138628Sbrian *    ("Disclaimer") and any redistribution must be conditioned upon
4230715Sbrian *    including a substantially similar Disclaimer requirement for further
4326516Sbrian *    binary redistribution.
4430715Sbrian *
4530715Sbrian * NO WARRANTY
4630715Sbrian * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4730715Sbrian * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
4830715Sbrian * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
4930715Sbrian * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
5030715Sbrian * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5130715Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5250059Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5358037Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
5458037Sbrian * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
5558037Sbrian * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
5646086Sbrian * POSSIBILITY OF SUCH DAMAGES.
5739395Sbrian *
5839395Sbrian * Authors: Ken Merry           (Spectra Logic Corporation)
5958037Sbrian */
6046686Sbrian
6137009Sbrian#ifndef lint
6231343Sbrianstatic const char copyright[] =
6330715Sbrian"@(#) Copyright (c) 1980, 1993\n\
6430715Sbrian	The Regents of the University of California.  All rights reserved.\n";
6530715Sbrian#endif /* not lint */
666059Samurai
6731690Sbrian#ifndef lint
6836285Sbrian#if 0
6936285Sbrianstatic char sccsid[] = "@(#)mt.c	8.2 (Berkeley) 5/4/95";
7038557Sbrian#endif
7138557Sbrian#endif /* not lint */
7263484Sbrian
7381634Sbrian#include <sys/cdefs.h>
7481634Sbrian__FBSDID("$FreeBSD: head/usr.bin/mt/mt.c 289677 2015-10-21 05:37:09Z eadler $");
756059Samurai
7650059Sbrian/*
7751075Sbrian * mt --
7831343Sbrian *   magnetic tape manipulation program
7925630Sbrian */
8036285Sbrian#include <sys/types.h>
8136285Sbrian#include <sys/ioctl.h>
8230715Sbrian#include <sys/mtio.h>
8330715Sbrian#include <sys/queue.h>
8430715Sbrian
8531080Sbrian#include <ctype.h>
8636285Sbrian#include <err.h>
8736285Sbrian#include <fcntl.h>
8836285Sbrian#include <stdio.h>
8936285Sbrian#include <stdlib.h>
9043313Sbrian#include <string.h>
9143313Sbrian#include <unistd.h>
9243313Sbrian#include <stdint.h>
9381634Sbrian#include <errno.h>
9481634Sbrian#include <bsdxml.h>
9536285Sbrian#include <mtlib.h>
9636285Sbrian
9736285Sbrian#include <cam/cam.h>
9836285Sbrian#include <cam/cam_ccb.h>
9936285Sbrian#include <cam/cam_periph.h>
10038174Sbrian#include <cam/scsi/scsi_all.h>
10136285Sbrian#include <cam/scsi/scsi_sa.h>
10240561Sbrian
10353298Sbrian/* the appropriate sections of <sys/mtio.h> are also #ifdef'd for FreeBSD */
10481697Sbrian/* c_flags */
1056059Samurai#define NEED_2ARGS	0x01
10636285Sbrian#define ZERO_ALLOWED	0x02
10736285Sbrian#define IS_DENSITY	0x04
10836285Sbrian#define DISABLE_THIS	0x08
10936285Sbrian#define IS_COMP		0x10
11036285Sbrian#define	USE_GETOPT	0x20
11136285Sbrian
11236285Sbrian#ifndef TRUE
11336285Sbrian#define TRUE 1
11436285Sbrian#endif
11536285Sbrian#ifndef FALSE
11636285Sbrian#define FALSE 0
11736285Sbrian#endif
11836285Sbrian#ifndef MAX
11936285Sbrian#define	MAX(a, b) (a > b) ? a : b
12036285Sbrian#endif
12136285Sbrian#define MT_PLURAL(a) (a == 1) ? "" : "s"
12236285Sbrian
12336285Sbriantypedef enum {
12436285Sbrian	MT_CMD_NONE	= MTLOAD + 1,
12536285Sbrian	MT_CMD_PROTECT,
12636285Sbrian	MT_CMD_GETDENSITY
12736285Sbrian} mt_commands;
12836285Sbrian
12936285Sbrianstatic const struct commands {
13036285Sbrian	const char *c_name;
13138174Sbrian	unsigned long c_code;
13238174Sbrian	int c_ronly;
13338544Sbrian	int c_flags;
13440665Sbrian} com[] = {
13540665Sbrian	{ "bsf",	MTBSF,	1, 0 },
13643313Sbrian	{ "bsr",	MTBSR,	1, 0 },
13744073Sbrian	/* XXX FreeBSD considered "eof" dangerous, since it's being
13846686Sbrian	   confused with "eom" (and is an alias for "weof" anyway) */
13946686Sbrian	{ "eof",	MTWEOF, 0, DISABLE_THIS },
14050867Sbrian	{ "fsf",	MTFSF,	1, 0 },
14152488Sbrian	{ "fsr",	MTFSR,	1, 0 },
14261534Sbrian	{ "offline",	MTOFFL,	1, 0 },
14378411Sbrian	{ "load",	MTLOAD, 1, 0 },
1446059Samurai	{ "rewind",	MTREW,	1, 0 },
14536285Sbrian	{ "rewoffl",	MTOFFL,	1, 0 },
14636285Sbrian	{ "ostatus",	MTNOP,	1, 0 },
14736285Sbrian	{ "weof",	MTWEOF,	0, ZERO_ALLOWED },
14836285Sbrian	{ "weofi",	MTWEOFI, 0, ZERO_ALLOWED },
14936285Sbrian	{ "erase",	MTERASE, 0, ZERO_ALLOWED},
15036285Sbrian	{ "blocksize",	MTSETBSIZ, 0, NEED_2ARGS|ZERO_ALLOWED },
15144106Sbrian	{ "density",	MTSETDNSTY, 0, NEED_2ARGS|ZERO_ALLOWED|IS_DENSITY },
15244106Sbrian	{ "eom",	MTEOD, 1, 0 },
15344106Sbrian	{ "eod",	MTEOD, 1, 0 },
15444106Sbrian	{ "smk",	MTWSS, 0, 0 },
15547858Sbrian	{ "wss",	MTWSS, 0, 0 },
15647858Sbrian	{ "fss",	MTFSS, 1, 0 },
15747858Sbrian	{ "bss",	MTBSS, 1, 0 },
15847858Sbrian	{ "comp",	MTCOMP, 0, NEED_2ARGS|ZERO_ALLOWED|IS_COMP },
15947858Sbrian	{ "retension",	MTRETENS, 1, 0 },
16047858Sbrian	{ "rdhpos",     MTIOCRDHPOS,  0, 0 },
16147858Sbrian	{ "rdspos",     MTIOCRDSPOS,  0, 0 },
16247858Sbrian	{ "sethpos",    MTIOCHLOCATE, 0, NEED_2ARGS|ZERO_ALLOWED },
16347858Sbrian	{ "setspos",    MTIOCSLOCATE, 0, NEED_2ARGS|ZERO_ALLOWED },
16467910Sbrian	{ "errstat",	MTIOCERRSTAT, 0, 0 },
16567910Sbrian	{ "setmodel",	MTIOCSETEOTMODEL, 0, NEED_2ARGS|ZERO_ALLOWED },
16636285Sbrian	{ "seteotmodel",	MTIOCSETEOTMODEL, 0, NEED_2ARGS|ZERO_ALLOWED },
16785362Sbrian	{ "getmodel",	MTIOCGETEOTMODEL, 0, 0 },
16836285Sbrian	{ "geteotmodel",	MTIOCGETEOTMODEL, 0, 0 },
16936285Sbrian	{ "rblim", 	MTIOCRBLIM, 0, 0},
17036285Sbrian	{ "getdensity",	MT_CMD_GETDENSITY, 0, USE_GETOPT},
17136285Sbrian	{ "status",	MTIOCEXTGET, 0, USE_GETOPT },
17236285Sbrian	{ "locate",	MTIOCEXTLOCATE, 0, USE_GETOPT },
17336285Sbrian	{ "param",	MTIOCPARAMGET, 0, USE_GETOPT },
17436285Sbrian	{ "protect", 	MT_CMD_PROTECT, 0, USE_GETOPT },
17536285Sbrian	{ NULL, 0, 0, 0 }
17636285Sbrian};
17736285Sbrian
17836285Sbrian
17936285Sbrianstatic const char *getblksiz(int);
18036934Sbrianstatic void printreg(const char *, u_int, const char *);
18140561Sbrianstatic void status(struct mtget *);
18240561Sbrianstatic void usage(void);
18340561Sbrianconst char *get_driver_state_str(int dsreg);
18440561Sbrianstatic void st_status (struct mtget *);
18540679Sbrianstatic int mt_locate(int argc, char **argv, int mtfd, const char *tape);
18650059Sbrianstatic int nstatus_print(int argc, char **argv, char *xml_str,
18758867Sbrian			 struct mt_status_data *status_data);
18858867Sbrianstatic int mt_xml_cmd(unsigned long cmd, int argc, char **argv, int mtfd,
18931343Sbrian		      const char *tape);
1906059Samuraistatic int mt_print_density_entry(struct mt_status_entry *density_root, int indent);
19136285Sbrianstatic int mt_print_density_report(struct mt_status_entry *report_root, int indent);
19236285Sbrianstatic int mt_print_density(struct mt_status_entry *density_root, int indent);
19336285Sbrianstatic int mt_getdensity(int argc, char **argv, char *xml_str,
19436285Sbrian			 struct mt_status_data *status_data);
19536285Sbrianstatic int mt_set_param(int mtfd, struct mt_status_data *status_data,
19636285Sbrian			char *param_name, char *param_value);
19736285Sbrianstatic int mt_protect(int argc, char **argv, int mtfd,
19836285Sbrian		      struct mt_status_data *status_data);
19936285Sbrianstatic int mt_param(int argc, char **argv, int mtfd, char *xml_str,
20036285Sbrian		    struct mt_status_data *status_data);
20136285Sbrianstatic const char *denstostring (int d);
2026059Samuraistatic u_int32_t stringtocomp(const char *s);
20331343Sbrianstatic const char *comptostring(u_int32_t comp);
2046059Samuraistatic void warn_eof(void);
20528679Sbrian
20636285Sbrianint
20736285Sbrianmain(int argc, char *argv[])
2086059Samurai{
20936285Sbrian	const struct commands *comp;
21036285Sbrian	struct mtget mt_status;
21126516Sbrian	struct mtop mt_com;
21236285Sbrian	int ch, len, mtfd;
21326516Sbrian	const char *p, *tape;
21436285Sbrian
21536285Sbrian	bzero(&mt_com, sizeof(mt_com));
21636285Sbrian
21736285Sbrian	if ((tape = getenv("TAPE")) == NULL)
21836285Sbrian		tape = DEFTAPE;
21936285Sbrian
22028679Sbrian	while ((ch = getopt(argc, argv, "f:t:")) != -1)
2216059Samurai		switch(ch) {
22226516Sbrian		case 'f':
2236059Samurai		case 't':
22436285Sbrian			tape = optarg;
22531372Sbrian			break;
22636285Sbrian		case '?':
22736285Sbrian			usage();
22836285Sbrian			break;
22931372Sbrian		default:
23031372Sbrian			break;
23131372Sbrian		}
23231372Sbrian	argc -= optind;
23331372Sbrian	argv += optind;
23431372Sbrian
2356059Samurai	if (argc < 1)
23636285Sbrian		usage();
23736285Sbrian
23836285Sbrian	len = strlen(p = *argv++);
23936285Sbrian	for (comp = com;; comp++) {
24036285Sbrian		if (comp->c_name == NULL)
24136285Sbrian			errx(1, "%s: unknown command", p);
24240482Sbrian		if (strncmp(p, comp->c_name, len) == 0)
24340482Sbrian			break;
24440482Sbrian	}
24536285Sbrian	if((comp->c_flags & NEED_2ARGS) && argc != 2)
24631372Sbrian		usage();
24736285Sbrian	if(comp->c_flags & DISABLE_THIS) {
2486059Samurai		warn_eof();
24931372Sbrian	}
25036285Sbrian	if (comp->c_flags & USE_GETOPT) {
25126516Sbrian		argc--;
25226516Sbrian		optind = 0;
2536059Samurai	}
2546059Samurai
25536285Sbrian	if ((mtfd = open(tape, comp->c_ronly ? O_RDONLY : O_RDWR)) < 0)
25663484Sbrian		err(1, "%s", tape);
25763484Sbrian	if (comp->c_code != MTNOP) {
25881902Sbrian		mt_com.mt_op = comp->c_code;
25963484Sbrian		if (*argv) {
26085991Sbrian			if (!isdigit(**argv) &&
26185991Sbrian			    (comp->c_flags & IS_DENSITY)) {
26285991Sbrian				const char *dcanon;
26363484Sbrian				mt_com.mt_count = mt_density_num(*argv);
26463484Sbrian				if (mt_com.mt_count == 0)
26563484Sbrian					errx(1, "%s: unknown density", *argv);
26663484Sbrian				dcanon = denstostring(mt_com.mt_count);
26763484Sbrian				if (strcmp(dcanon, *argv) != 0)
26863484Sbrian					printf(
26963484Sbrian					"Using \"%s\" as an alias for %s\n",
27063484Sbrian					       *argv, dcanon);
27163484Sbrian				p = "";
27263484Sbrian			} else if (!isdigit(**argv) &&
27363484Sbrian				   (comp->c_flags & IS_COMP)) {
27463484Sbrian
27563484Sbrian				mt_com.mt_count = stringtocomp(*argv);
27663484Sbrian				if ((u_int32_t)mt_com.mt_count == 0xf0f0f0f0)
27736285Sbrian					errx(1, "%s: unknown compression",
2786059Samurai					     *argv);
27936285Sbrian				p = "";
28036285Sbrian			} else if ((comp->c_flags & USE_GETOPT) == 0) {
28136285Sbrian				char *q;
2826059Samurai				/* allow for hex numbers; useful for density */
28336285Sbrian				mt_com.mt_count = strtol(*argv, &q, 0);
28436285Sbrian				p = q;
28536285Sbrian			}
28636285Sbrian			if (((comp->c_flags & USE_GETOPT) == 0)
28736285Sbrian			 && (((mt_com.mt_count <=
28836285Sbrian			     ((comp->c_flags & ZERO_ALLOWED)? -1: 0))
28936285Sbrian			   && ((comp->c_flags & IS_COMP) == 0))
29036285Sbrian			  || *p))
2916059Samurai				errx(1, "%s: illegal count", *argv);
29236285Sbrian		}
29336285Sbrian		else
2946059Samurai			mt_com.mt_count = 1;
2956059Samurai		switch (comp->c_code) {
2966059Samurai		case MTIOCERRSTAT:
29736285Sbrian		{
2986059Samurai			unsigned int i;
29936285Sbrian			union mterrstat umn;
30036285Sbrian			struct scsi_tape_errors *s = &umn.scsi_errstat;
30111336Samurai
30236285Sbrian			if (ioctl(mtfd, comp->c_code, (caddr_t)&umn) < 0)
30336285Sbrian				err(2, "%s", tape);
30436285Sbrian			(void)printf("Last I/O Residual: %u\n", s->io_resid);
3056059Samurai			(void)printf(" Last I/O Command:");
30626516Sbrian			for (i = 0; i < sizeof (s->io_cdb); i++)
30736285Sbrian				(void)printf(" %02X", s->io_cdb[i]);
30836285Sbrian			(void)printf("\n");
30936285Sbrian			(void)printf("   Last I/O Sense:\n\n\t");
31032711Sbrian			for (i = 0; i < sizeof (s->io_sense); i++) {
31136285Sbrian				(void)printf(" %02X", s->io_sense[i]);
31236285Sbrian				if (((i + 1) & 0xf) == 0) {
31336285Sbrian					(void)printf("\n\t");
31436285Sbrian				}
31536285Sbrian			}
31631121Sbrian			(void)printf("\n");
31736285Sbrian			(void)printf("Last Control Residual: %u\n",
31836285Sbrian			    s->ctl_resid);
31936285Sbrian			(void)printf(" Last Control Command:");
32036285Sbrian			for (i = 0; i < sizeof (s->ctl_cdb); i++)
32136285Sbrian				(void)printf(" %02X", s->ctl_cdb[i]);
32236285Sbrian			(void)printf("\n");
32336285Sbrian			(void)printf("   Last Control Sense:\n\n\t");
32436285Sbrian			for (i = 0; i < sizeof (s->ctl_sense); i++) {
32585991Sbrian				(void)printf(" %02X", s->ctl_sense[i]);
32636285Sbrian				if (((i + 1) & 0xf) == 0) {
32736285Sbrian					(void)printf("\n\t");
32840797Sbrian				}
32940797Sbrian			}
33036285Sbrian			(void)printf("\n\n");
33140797Sbrian			exit(0);
33236285Sbrian			/* NOTREACHED */
33340797Sbrian		}
33440797Sbrian		case MTIOCRDHPOS:
33540797Sbrian		case MTIOCRDSPOS:
33640797Sbrian		{
33740797Sbrian			u_int32_t block;
33840797Sbrian			if (ioctl(mtfd, comp->c_code, (caddr_t)&block) < 0)
33940797Sbrian				err(2, "%s", tape);
34040797Sbrian			(void)printf("%s: %s block location %u\n", tape,
34140797Sbrian			    (comp->c_code == MTIOCRDHPOS)? "hardware" :
34240797Sbrian			    "logical", block);
34340797Sbrian			exit(0);
34440797Sbrian			/* NOTREACHED */
34540797Sbrian		}
34640797Sbrian		case MTIOCSLOCATE:
34736285Sbrian		case MTIOCHLOCATE:
34836285Sbrian		{
34940797Sbrian			u_int32_t block = (u_int32_t)mt_com.mt_count;
35040797Sbrian			if (ioctl(mtfd, comp->c_code, (caddr_t)&block) < 0)
35140797Sbrian				err(2, "%s", tape);
35236285Sbrian			exit(0);
35340797Sbrian			/* NOTREACHED */
35426516Sbrian		}
3556059Samurai		case MTIOCGETEOTMODEL:
3566059Samurai		{
35785991Sbrian			u_int32_t om;
35885991Sbrian			if (ioctl(mtfd, MTIOCGETEOTMODEL, (caddr_t)&om) < 0)
35985991Sbrian				err(2, "%s", tape);
36085991Sbrian			(void)printf("%s: the model is %u filemar%s at EOT\n",
36185991Sbrian			    tape, om, (om > 1)? "ks" : "k");
36285991Sbrian			exit(0);
36385991Sbrian			/* NOTREACHED */
36485991Sbrian		}
36585991Sbrian		case MTIOCSETEOTMODEL:
36685991Sbrian		{
36785991Sbrian			u_int32_t om, nm = (u_int32_t)mt_com.mt_count;
36885991Sbrian			if (ioctl(mtfd, MTIOCGETEOTMODEL, (caddr_t)&om) < 0)
36985991Sbrian				err(2, "%s", tape);
37085991Sbrian			if (ioctl(mtfd, comp->c_code, (caddr_t)&nm) < 0)
37185991Sbrian				err(2, "%s", tape);
37285991Sbrian			(void)printf("%s: old model was %u filemar%s at EOT\n",
37385991Sbrian			    tape, om, (om > 1)? "ks" : "k");
37485991Sbrian			(void)printf("%s: new model  is %u filemar%s at EOT\n",
37585991Sbrian			    tape, nm, (nm > 1)? "ks" : "k");
37685991Sbrian			exit(0);
37785991Sbrian			/* NOTREACHED */
37885991Sbrian		}
37985991Sbrian		case MTIOCRBLIM:
38085991Sbrian		{
38185991Sbrian			struct mtrblim rblim;
38236285Sbrian
38336285Sbrian			bzero(&rblim, sizeof(rblim));
38485991Sbrian
38536285Sbrian			if (ioctl(mtfd, MTIOCRBLIM, (caddr_t)&rblim) < 0)
38636285Sbrian				err(2, "%s", tape);
38736285Sbrian			(void)printf("%s:\n"
38810528Samurai			    "    min blocksize %u byte%s\n"
38936285Sbrian			    "    max blocksize %u byte%s\n"
39028536Sbrian			    "    granularity %u byte%s\n",
39136285Sbrian			    tape, rblim.min_block_length,
39236285Sbrian			    MT_PLURAL(rblim.min_block_length),
39336465Sbrian			    rblim.max_block_length,
39436465Sbrian			    MT_PLURAL(rblim.max_block_length),
39536928Sbrian			    (1 << rblim.granularity),
39636285Sbrian			    MT_PLURAL((1 << rblim.granularity)));
39736285Sbrian			exit(0);
39836285Sbrian			/* NOTREACHED */
39934536Sbrian		}
40036285Sbrian		case MTIOCPARAMGET:
40136285Sbrian		case MTIOCEXTGET:
40236285Sbrian		case MT_CMD_PROTECT:
40336285Sbrian		case MT_CMD_GETDENSITY:
40437993Sbrian		{
40536285Sbrian			int retval = 0;
40636285Sbrian
40728536Sbrian			retval = mt_xml_cmd(comp->c_code, argc, argv, mtfd,
40828536Sbrian			    tape);
40938628Sbrian
41038628Sbrian			exit(retval);
41138628Sbrian		}
41238628Sbrian		case MTIOCEXTLOCATE:
41338628Sbrian		{
41438628Sbrian			int retval = 0;
41538628Sbrian
41638628Sbrian			retval = mt_locate(argc, argv, mtfd, tape);
41738628Sbrian
41838628Sbrian			exit(retval);
41938628Sbrian		}
42038628Sbrian		default:
42138628Sbrian			break;
42247865Sbrian		}
42347865Sbrian		if (ioctl(mtfd, MTIOCTOP, &mt_com) < 0)
42447865Sbrian			err(1, "%s: %s", tape, comp->c_name);
42547865Sbrian	} else {
42647865Sbrian		if (ioctl(mtfd, MTIOCGET, &mt_status) < 0)
42738628Sbrian			err(1, NULL);
42838628Sbrian		status(&mt_status);
42938628Sbrian	}
43038628Sbrian	exit(0);
43138628Sbrian	/* NOTREACHED */
43238628Sbrian}
43338628Sbrian
43438628Sbrianstatic const struct tape_desc {
43538628Sbrian	short	t_type;		/* type of magtape device */
43638628Sbrian	const char *t_name;	/* printing name */
43738628Sbrian	const char *t_dsbits;	/* "drive status" register */
43838628Sbrian	const char *t_erbits;	/* "error" register */
43938628Sbrian} tapes[] = {
44038628Sbrian	{ MT_ISAR,	"SCSI tape drive", 0,		0 },
44138628Sbrian	{ 0, NULL, 0, 0 }
44238628Sbrian};
44338628Sbrian
44438628Sbrian/*
44538628Sbrian * Interpret the status buffer returned
44638628Sbrian */
44738628Sbrianstatic void
44838628Sbrianstatus(struct mtget *bp)
44938628Sbrian{
45038628Sbrian	const struct tape_desc *mt;
45138628Sbrian
45238628Sbrian	for (mt = tapes;; mt++) {
45338628Sbrian		if (mt->t_type == 0) {
45438628Sbrian			(void)printf("%d: unknown tape drive type\n",
45538628Sbrian			    bp->mt_type);
45638628Sbrian			return;
45738628Sbrian		}
45838628Sbrian		if (mt->t_type == bp->mt_type)
45938628Sbrian			break;
46038628Sbrian	}
46138628Sbrian	if(mt->t_type == MT_ISAR)
46238628Sbrian		st_status(bp);
46338628Sbrian	else {
46443888Sbrian		(void)printf("%s tape drive, residual=%d\n",
46543888Sbrian		    mt->t_name, bp->mt_resid);
46647849Sbrian		printreg("ds", (unsigned short)bp->mt_dsreg, mt->t_dsbits);
46738628Sbrian		printreg("\ner", (unsigned short)bp->mt_erreg, mt->t_erbits);
46885991Sbrian		(void)putchar('\n');
46985991Sbrian	}
47047849Sbrian}
47138628Sbrian
47241755Sbrian/*
47341755Sbrian * Print a register a la the %b format of the kernel's printf.
47441755Sbrian */
47541755Sbrianstatic void
47641755Sbrianprintreg(const char *s, u_int v, const char *bits)
47741755Sbrian{
47847849Sbrian	int i, any = 0;
47941755Sbrian	char c;
48038629Sbrian
48138629Sbrian	if (bits && *bits == 8)
48238628Sbrian		printf("%s=%o", s, v);
48381634Sbrian	else
48481897Sbrian		printf("%s=%x", s, v);
48581897Sbrian	if (!bits)
48681634Sbrian		return;
48738629Sbrian	bits++;
48840561Sbrian	if (v && bits) {
48938628Sbrian		putchar('<');
49081634Sbrian		while ((i = *bits++)) {
49181897Sbrian			if (v & (1 << (i-1))) {
49281897Sbrian				if (any)
49381634Sbrian					putchar(',');
49438629Sbrian				any = 1;
49538629Sbrian				for (; (c = *bits) > 32; bits++)
49638629Sbrian					putchar(c);
49738629Sbrian			} else
49838629Sbrian				for (; *bits > 32; bits++)
49938629Sbrian					;
50038629Sbrian		}
50138629Sbrian		putchar('>');
50238629Sbrian	}
50347849Sbrian}
50438629Sbrian
50558044Sbrianstatic void
50658044Sbrianusage(void)
50758044Sbrian{
50858044Sbrian	(void)fprintf(stderr, "usage: mt [-f device] command [count]\n");
50963484Sbrian	exit(1);
51063484Sbrian}
51185991Sbrian
51285991Sbrianstatic const struct compression_types {
51385991Sbrian	u_int32_t	comp_number;
51485991Sbrian	const char 	*name;
51585991Sbrian} comp_types[] = {
51638628Sbrian	{ 0x00, "none" },
51738628Sbrian	{ 0x00, "off" },
51838628Sbrian	{ 0x10, "IDRC" },
51938628Sbrian	{ 0x20, "DCLZ" },
52085991Sbrian	{ 0xffffffff, "enable" },
52185991Sbrian	{ 0xffffffff, "on" },
52285991Sbrian	{ 0xf0f0f0f0, NULL}
52385991Sbrian};
52485991Sbrian
52585991Sbrianstatic const char *
52685991Sbriandenstostring(int d)
52785991Sbrian{
52885991Sbrian	static char buf[20];
52985991Sbrian	const char *name = mt_density_name(d);
53028536Sbrian
53131343Sbrian	if (name == NULL)
53210528Samurai		sprintf(buf, "0x%02x", d);
53310528Samurai	else
53447849Sbrian		sprintf(buf, "0x%02x:%s", d, name);
53520813Sjkh	return buf;
53618856Ssos}
53726911Sbrian
53836285Sbrianstatic const char *
53936285Sbriangetblksiz(int bs)
54026516Sbrian{
54110528Samurai	static char buf[25];
54226911Sbrian	if (bs == 0)
54328679Sbrian		return "variable";
54436285Sbrian	else {
54536285Sbrian		sprintf(buf, "%d bytes", bs);
54636285Sbrian		return buf;
54736285Sbrian	}
54828381Sbrian}
54936285Sbrian
55036285Sbrianstatic const char *
55136285Sbriancomptostring(u_int32_t comp)
55236285Sbrian{
55328381Sbrian	static char buf[20];
55436285Sbrian	const struct compression_types *ct;
55528679Sbrian
55628381Sbrian	if (comp == MT_COMP_DISABLED)
55728381Sbrian		return "disabled";
55834536Sbrian	else if (comp == MT_COMP_UNSUPP)
55934536Sbrian		return "unsupported";
56047849Sbrian
56128679Sbrian	for (ct = comp_types; ct->name; ct++)
56236285Sbrian		if (ct->comp_number == comp)
56318531Sbde			break;
56436285Sbrian
56536285Sbrian	if (ct->comp_number == 0xf0f0f0f0) {
56632017Sbrian		sprintf(buf, "0x%x", comp);
56736285Sbrian		return(buf);
56836285Sbrian	} else
56936285Sbrian		return(ct->name);
57036285Sbrian}
57136285Sbrian
57236285Sbrianstatic u_int32_t
57336285Sbrianstringtocomp(const char *s)
57428679Sbrian{
57528679Sbrian	const struct compression_types *ct;
57649976Sbrian	size_t l = strlen(s);
57749976Sbrian
57849976Sbrian	for (ct = comp_types; ct->name; ct++)
57949976Sbrian		if (strncasecmp(ct->name, s, l) == 0)
58049976Sbrian			break;
58126516Sbrian
58264802Sbrian	return(ct->comp_number);
58355252Sbrian}
58464802Sbrian
58536285Sbrianstatic struct driver_state {
58628679Sbrian	int dsreg;
58738628Sbrian	const char *desc;
58838628Sbrian} driver_states[] = {
58938628Sbrian	{ MTIO_DSREG_REST, "at rest" },
59038628Sbrian	{ MTIO_DSREG_RBSY, "Communicating with drive" },
59138628Sbrian	{ MTIO_DSREG_WR, "Writing" },
59238628Sbrian	{ MTIO_DSREG_FMK, "Writing Filemarks" },
59331343Sbrian	{ MTIO_DSREG_ZER, "Erasing" },
59447849Sbrian	{ MTIO_DSREG_RD, "Reading" },
59528679Sbrian	{ MTIO_DSREG_FWD, "Spacing Forward" },
59628679Sbrian	{ MTIO_DSREG_REV, "Spacing Reverse" },
59710528Samurai	{ MTIO_DSREG_POS, "Hardware Positioning (direction unknown)" },
59828679Sbrian	{ MTIO_DSREG_REW, "Rewinding" },
59928679Sbrian	{ MTIO_DSREG_TEN, "Retensioning" },
60036832Sbrian	{ MTIO_DSREG_UNL, "Unloading" },
60128679Sbrian	{ MTIO_DSREG_LD, "Loading" },
60228679Sbrian};
60336285Sbrian
60436285Sbrianconst char *
60531343Sbrianget_driver_state_str(int dsreg)
60630316Sbrian{
60736285Sbrian	unsigned int i;
60832017Sbrian
60936285Sbrian	for (i = 0; i < (sizeof(driver_states)/sizeof(driver_states[0])); i++) {
61079450Sbrian		if (driver_states[i].dsreg == dsreg)
61130316Sbrian			return (driver_states[i].desc);
61220813Sjkh	}
61340665Sbrian
61440665Sbrian	return (NULL);
61540665Sbrian}
61649976Sbrian
61710528Samuraistatic void
61836285Sbrianst_status(struct mtget *bp)
61936285Sbrian{
62036285Sbrian	printf("Mode      Density              Blocksize      bpi      "
62136285Sbrian	       "Compression\n"
62210528Samurai	       "Current:  %-17s    %-12s   %-7d  %s\n"
62331343Sbrian	       "---------available modes---------\n"
62410528Samurai	       "0:        %-17s    %-12s   %-7d  %s\n"
62520813Sjkh	       "1:        %-17s    %-12s   %-7d  %s\n"
62636285Sbrian	       "2:        %-17s    %-12s   %-7d  %s\n"
62736285Sbrian	       "3:        %-17s    %-12s   %-7d  %s\n",
62820813Sjkh	       denstostring(bp->mt_density), getblksiz(bp->mt_blksiz),
62936285Sbrian	       mt_density_bp(bp->mt_density, TRUE), comptostring(bp->mt_comp),
63010528Samurai	       denstostring(bp->mt_density0), getblksiz(bp->mt_blksiz0),
63110528Samurai	       mt_density_bp(bp->mt_density0, TRUE), comptostring(bp->mt_comp0),
63231343Sbrian	       denstostring(bp->mt_density1), getblksiz(bp->mt_blksiz1),
63331343Sbrian	       mt_density_bp(bp->mt_density1, TRUE), comptostring(bp->mt_comp1),
63431343Sbrian	       denstostring(bp->mt_density2), getblksiz(bp->mt_blksiz2),
63536285Sbrian	       mt_density_bp(bp->mt_density2, TRUE), comptostring(bp->mt_comp2),
63631343Sbrian	       denstostring(bp->mt_density3), getblksiz(bp->mt_blksiz3),
63731343Sbrian	       mt_density_bp(bp->mt_density3, TRUE), comptostring(bp->mt_comp3));
63831343Sbrian
63931343Sbrian	if (bp->mt_dsreg != MTIO_DSREG_NIL) {
64031343Sbrian		const char sfmt[] = "Current Driver State: %s.\n";
64131343Sbrian		printf("---------------------------------\n");
64231343Sbrian		const char *state_str;
64331343Sbrian
64431343Sbrian		state_str = get_driver_state_str(bp->mt_dsreg);
64531343Sbrian		if (state_str == NULL) {
64658044Sbrian			char foo[32];
64758044Sbrian			(void) sprintf(foo, "Unknown state 0x%x", bp->mt_dsreg);
64858044Sbrian			printf(sfmt, foo);
64958044Sbrian		} else {
65058044Sbrian			printf(sfmt, state_str);
65158044Sbrian		}
65258044Sbrian	}
65358044Sbrian	if (bp->mt_resid == 0 && bp->mt_fileno == (daddr_t) -1 &&
65458044Sbrian	    bp->mt_blkno == (daddr_t) -1)
65558044Sbrian		return;
65658044Sbrian	printf("---------------------------------\n");
65758044Sbrian	printf("File Number: %d\tRecord Number: %d\tResidual Count %d\n",
65858044Sbrian	    bp->mt_fileno, bp->mt_blkno, bp->mt_resid);
65958044Sbrian}
66058044Sbrian
66158044Sbrianstatic int
66258044Sbrianmt_locate(int argc, char **argv, int mtfd, const char *tape)
66358044Sbrian{
66458044Sbrian	struct mtlocate mtl;
66558044Sbrian	uint64_t logical_id = 0;
66658044Sbrian	mt_locate_dest_type dest_type = MT_LOCATE_DEST_FILE;
66758044Sbrian	int eod = 0, explicit = 0, immediate = 0;
66858044Sbrian	int64_t partition = 0;
66950059Sbrian	int block_addr_set = 0, partition_set = 0, file_set = 0, set_set = 0;
67058867Sbrian	int c, retval;
67140561Sbrian
67250059Sbrian	retval = 0;
67350059Sbrian	bzero(&mtl, sizeof(mtl));
67458867Sbrian
67550059Sbrian	while ((c = getopt(argc, argv, "b:eEf:ip:s:")) != -1) {
67640561Sbrian		switch (c) {
67758867Sbrian		case 'b':
67850059Sbrian			/* Block address */
67958867Sbrian			logical_id = strtoull(optarg, NULL, 0);
68050059Sbrian			dest_type = MT_LOCATE_DEST_OBJECT;
68140561Sbrian			block_addr_set = 1;
68250059Sbrian			break;
68350059Sbrian		case 'e':
68479433Sbrian			/* end of data */
68579433Sbrian			eod = 1;
68650059Sbrian			dest_type = MT_LOCATE_DEST_EOD;
68750059Sbrian			break;
68881033Sbrian		case 'E':
68981033Sbrian			/*
69081033Sbrian			 * XXX KDM explicit address mode.  Should we even
69181033Sbrian			 * allow this, since the driver doesn't operate in
69258867Sbrian			 * explicit address mode?
69350059Sbrian			 */
69440561Sbrian			explicit = 1;
69558867Sbrian			break;
69658867Sbrian		case 'f':
69758867Sbrian			/* file number */
69850059Sbrian			logical_id = strtoull(optarg, NULL, 0);
69950059Sbrian			dest_type = MT_LOCATE_DEST_FILE;
70040561Sbrian			file_set = 1;
70158867Sbrian			break;
70250059Sbrian		case 'i':
70340561Sbrian			/*
70440561Sbrian			 * Immediate address mode.  XXX KDM do we want to
70558867Sbrian			 * implement this?  The other commands in the
70640561Sbrian			 * tape driver will need to be able to handle this.
70740561Sbrian			 */
70840561Sbrian			immediate = 1;
70940561Sbrian			break;
71040561Sbrian		case 'p':
71140561Sbrian			/*
71240561Sbrian			 * Change partition to the given partition.
71340561Sbrian			 */
71440561Sbrian			partition = strtol(optarg, NULL, 0);
71540561Sbrian			partition_set = 1;
71640561Sbrian			break;
71740561Sbrian		case 's':
71840561Sbrian			/* Go to the given set mark */
71940561Sbrian			logical_id = strtoull(optarg, NULL, 0);
72040561Sbrian			dest_type = MT_LOCATE_DEST_SET;
72140561Sbrian			set_set = 1;
72240561Sbrian			break;
72340561Sbrian		default:
72440561Sbrian			break;
72540561Sbrian		}
72640561Sbrian	}
72740561Sbrian
72881634Sbrian	/*
72940561Sbrian	 * These options are mutually exclusive.  The user may only specify
73040561Sbrian	 * one.
73140561Sbrian	 */
73240561Sbrian	if ((block_addr_set + file_set + eod + set_set) != 1)
73340561Sbrian		errx(1, "You must specify only one of -b, -f, -e, or -s");
73440561Sbrian
73540561Sbrian	mtl.dest_type = dest_type;
73640561Sbrian	switch (dest_type) {
73740561Sbrian	case MT_LOCATE_DEST_OBJECT:
73850059Sbrian	case MT_LOCATE_DEST_FILE:
73940561Sbrian	case MT_LOCATE_DEST_SET:
74040561Sbrian		mtl.logical_id = logical_id;
74140561Sbrian		break;
74230715Sbrian	case MT_LOCATE_DEST_EOD:
74336285Sbrian		break;
74428679Sbrian	}
74528679Sbrian
74632109Sbrian	if (immediate != 0)
74736285Sbrian		mtl.flags |= MT_LOCATE_FLAG_IMMED;
74832109Sbrian
74940561Sbrian	if (partition_set != 0) {
75040561Sbrian		mtl.flags |= MT_LOCATE_FLAG_CHANGE_PART;
75128679Sbrian		mtl.partition = partition;
75231372Sbrian	}
75336934Sbrian
75446686Sbrian	if (explicit != 0)
75581634Sbrian		mtl.block_address_mode = MT_LOCATE_BAM_EXPLICIT;
75636285Sbrian	else
75736285Sbrian		mtl.block_address_mode = MT_LOCATE_BAM_IMPLICIT;
75836285Sbrian
75936285Sbrian	if (ioctl(mtfd, MTIOCEXTLOCATE, &mtl) == -1)
76028679Sbrian		err(1, "MTIOCEXTLOCATE ioctl failed on %s", tape);
76132109Sbrian
76236285Sbrian	return (retval);
76332109Sbrian}
76436285Sbrian
76528679Sbriantypedef enum {
76636285Sbrian	MT_PERIPH_NAME			= 0,
76740797Sbrian	MT_UNIT_NUMBER 			= 1,
76836285Sbrian	MT_VENDOR			= 2,
76928679Sbrian	MT_PRODUCT			= 3,
77036285Sbrian	MT_REVISION			= 4,
77146686Sbrian	MT_COMPRESSION_SUPPORTED	= 5,
77236285Sbrian	MT_COMPRESSION_ENABLED		= 6,
77328679Sbrian	MT_COMPRESSION_ALGORITHM	= 7,
77463484Sbrian	MT_MEDIA_DENSITY		= 8,
77563484Sbrian	MT_MEDIA_BLOCKSIZE		= 9,
77640561Sbrian	MT_CALCULATED_FILENO		= 10,
77740561Sbrian	MT_CALCULATED_REL_BLKNO		= 11,
77836285Sbrian	MT_REPORTED_FILENO		= 12,
77936285Sbrian	MT_REPORTED_BLKNO		= 13,
78037008Sbrian	MT_PARTITION			= 14,
78140797Sbrian	MT_BOP				= 15,
78285991Sbrian	MT_EOP				= 16,
78385991Sbrian	MT_BPEW				= 17,
78450059Sbrian	MT_DSREG			= 18,
78550059Sbrian	MT_RESID			= 19,
78658867Sbrian	MT_FIXED_MODE			= 20,
78750059Sbrian	MT_SERIAL_NUM			= 21,
78836285Sbrian	MT_MAXIO			= 22,
78937955Sbrian	MT_CPI_MAXIO			= 23,
79036285Sbrian	MT_MAX_BLK			= 24,
79136285Sbrian	MT_MIN_BLK			= 25,
79236285Sbrian	MT_BLK_GRAN			= 26,
79336285Sbrian	MT_MAX_EFF_IOSIZE		= 27
79436285Sbrian} status_item_index;
79536285Sbrian
79636285Sbrianstatic struct mt_status_items {
79736285Sbrian	const char *name;
79858044Sbrian	struct mt_status_entry *entry;
79958044Sbrian} req_status_items[] = {
80028679Sbrian	{ "periph_name", NULL },
80128679Sbrian	{ "unit_number", NULL },
80263484Sbrian	{ "vendor", NULL },
80363484Sbrian	{ "product", NULL },
80436285Sbrian	{ "revision", NULL },
80528679Sbrian	{ "compression_supported", NULL },
80628679Sbrian	{ "compression_enabled", NULL },
80728679Sbrian	{ "compression_algorithm", NULL },
80836285Sbrian	{ "media_density", NULL },
80931372Sbrian	{ "media_blocksize", NULL },
81036285Sbrian	{ "calculated_fileno", NULL },
81131372Sbrian	{ "calculated_rel_blkno", NULL },
81228679Sbrian	{ "reported_fileno", NULL },
81331343Sbrian	{ "reported_blkno", NULL },
81428679Sbrian	{ "partition", NULL },
8156059Samurai	{ "bop", NULL },
8166059Samurai	{ "eop", NULL },
81728536Sbrian	{ "bpew", NULL },
81831343Sbrian	{ "dsreg", NULL },
8196059Samurai	{ "residual", NULL },
82036285Sbrian	{ "fixed_mode", NULL },
82136285Sbrian	{ "serial_num", NULL },
82236285Sbrian	{ "maxio", NULL },
8236059Samurai	{ "cpi_maxio", NULL },
82426516Sbrian	{ "max_blk", NULL },
82536285Sbrian	{ "min_blk", NULL },
82628679Sbrian	{ "blk_gran", NULL },
82736285Sbrian	{ "max_effective_iosize", NULL }
82836285Sbrian};
82936285Sbrian
83036285Sbrianint
83136285Sbriannstatus_print(int argc, char **argv, char *xml_str,
8326059Samurai	      struct mt_status_data *status_data)
83331077Sbrian{
8346059Samurai	unsigned int i;
8356059Samurai	int64_t calculated_fileno, calculated_rel_blkno;
83628679Sbrian	int64_t rep_fileno, rep_blkno, partition, resid;
83736285Sbrian	char block_str[32];
8386059Samurai	const char *dens_str;
83936285Sbrian	int dsreg, bop, eop, bpew;
84031077Sbrian	int xml_dump = 0;
8416059Samurai	size_t dens_len;
8426059Samurai	unsigned int field_width;
84328679Sbrian	int verbose = 0;
84431343Sbrian	int c;
84528327Sbrian
84636285Sbrian	while ((c = getopt(argc, argv, "xv")) != -1) {
84736285Sbrian		switch (c) {
84836285Sbrian		case 'x':
84928327Sbrian			xml_dump = 1;
85036285Sbrian			break;
85136285Sbrian		case 'v':
85228461Sbrian			verbose = 1;
85336285Sbrian			break;
85436285Sbrian		default:
85536285Sbrian			break;
85628461Sbrian		}
85736285Sbrian	}
85836285Sbrian
85928461Sbrian	if (xml_dump != 0) {
86036285Sbrian		printf("%s", xml_str);
86128461Sbrian		return (0);
86231077Sbrian	}
86328327Sbrian
86428327Sbrian	for (i = 0; i < (sizeof(req_status_items)/sizeof(req_status_items[0]));
86528679Sbrian	     i++) {
86631343Sbrian		char *name;
8676059Samurai
86851026Sbrian		name = __DECONST(char *, req_status_items[i].name);
86931077Sbrian		req_status_items[i].entry = mt_status_entry_find(status_data,
8706059Samurai		    name);
8716059Samurai		if (req_status_items[i].entry == NULL) {
87228679Sbrian			errx(1, "Cannot find status entry %s",
87336285Sbrian			    req_status_items[i].name);
87426326Sbrian		}
87536285Sbrian	}
87626326Sbrian
87736285Sbrian	printf("Drive: %s%ju: <%s %s %s> Serial Number: %s\n",
87836285Sbrian	       req_status_items[MT_PERIPH_NAME].entry->value,
87931077Sbrian	       (uintmax_t)req_status_items[MT_UNIT_NUMBER].entry->value_unsigned,
88026326Sbrian	       req_status_items[MT_VENDOR].entry->value,
88126326Sbrian	       req_status_items[MT_PRODUCT].entry->value,
88230715Sbrian	       req_status_items[MT_REVISION].entry->value,
88336285Sbrian	       (req_status_items[MT_SERIAL_NUM].entry->value) ?
88436285Sbrian	       req_status_items[MT_SERIAL_NUM].entry->value : "none");
88536285Sbrian	printf("---------------------------------\n");
88636285Sbrian
88736285Sbrian	/*
88836285Sbrian	 * We check to see whether we're in fixed mode or not, and don't
88936285Sbrian	 * just believe the blocksize.  If the SILI bit is turned on, the
89036285Sbrian	 * blocksize will be set to 4, even though we're doing variable
89136285Sbrian	 * length (well, multiples of 4) blocks.
89236285Sbrian	 */
89336285Sbrian	if (req_status_items[MT_FIXED_MODE].entry->value_signed == 0)
89436285Sbrian		snprintf(block_str, sizeof(block_str), "variable");
89540561Sbrian	else
89640561Sbrian		snprintf(block_str, sizeof(block_str), "%s",
89736285Sbrian		    getblksiz(req_status_items[
89836285Sbrian			      MT_MEDIA_BLOCKSIZE].entry->value_unsigned));
89981634Sbrian
90081634Sbrian	dens_str = denstostring(req_status_items[
90181634Sbrian	    MT_MEDIA_DENSITY].entry->value_unsigned);
90281634Sbrian	if (dens_str == NULL)
90347211Sbrian		dens_len = 0;
90447211Sbrian	else
90536285Sbrian		dens_len = strlen(dens_str);
90636285Sbrian	field_width = MAX(dens_len, 17);
90736285Sbrian	printf("Mode      %-*s    Blocksize      bpi      Compression\n"
90836285Sbrian	       "Current:  %-*s    %-12s   %-7d  ",
90936285Sbrian	       field_width, "Density", field_width, dens_str, block_str,
91036285Sbrian	       mt_density_bp(req_status_items[
91136285Sbrian	       MT_MEDIA_DENSITY].entry->value_unsigned, TRUE));
91236285Sbrian
91336285Sbrian	if (req_status_items[MT_COMPRESSION_SUPPORTED].entry->value_signed == 0)
91436285Sbrian		printf("unsupported\n");
91581634Sbrian	else if (req_status_items[
91681634Sbrian		 MT_COMPRESSION_ENABLED].entry->value_signed == 0)
91746686Sbrian		printf("disabled\n");
91846686Sbrian	else {
91936285Sbrian		printf("enabled (%s)\n",
92036285Sbrian		       comptostring(req_status_items[
92136285Sbrian		       MT_COMPRESSION_ALGORITHM].entry->value_unsigned));
92236285Sbrian	}
92336285Sbrian
92436285Sbrian	dsreg = req_status_items[MT_DSREG].entry->value_signed;
92536285Sbrian	if (dsreg != MTIO_DSREG_NIL) {
92636285Sbrian		const char sfmt[] = "Current Driver State: %s.\n";
92736285Sbrian		printf("---------------------------------\n");
92836285Sbrian		const char *state_str;
92928679Sbrian
93036285Sbrian		state_str = get_driver_state_str(dsreg);
93136285Sbrian		if (state_str == NULL) {
93236285Sbrian			char foo[32];
93328679Sbrian			(void) sprintf(foo, "Unknown state 0x%x", dsreg);
93431343Sbrian			printf(sfmt, foo);
93528679Sbrian		} else {
9366059Samurai			printf(sfmt, state_str);
9376059Samurai		}
93830715Sbrian	}
93931343Sbrian	resid = req_status_items[MT_RESID].entry->value_signed;
9406059Samurai	calculated_fileno = req_status_items[
94126516Sbrian	    MT_CALCULATED_FILENO].entry->value_signed;
94226516Sbrian	calculated_rel_blkno = req_status_items[
94328679Sbrian	    MT_CALCULATED_REL_BLKNO].entry->value_signed;
9446059Samurai	rep_fileno = req_status_items[
94526516Sbrian	    MT_REPORTED_FILENO].entry->value_signed;
94626516Sbrian	rep_blkno = req_status_items[
94726516Sbrian	    MT_REPORTED_BLKNO].entry->value_signed;
9486059Samurai	bop = req_status_items[MT_BOP].entry->value_signed;
94925566Sbrian	eop = req_status_items[MT_EOP].entry->value_signed;
95026516Sbrian	bpew = req_status_items[MT_BPEW].entry->value_signed;
95128679Sbrian	partition = req_status_items[MT_PARTITION].entry->value_signed;
95228679Sbrian
95326516Sbrian	printf("---------------------------------\n");
9546059Samurai	printf("Partition: %3jd      Calc File Number: %3jd "
9556059Samurai	       "    Calc Record Number: %jd\n"
95628679Sbrian	       "Residual:  %3jd  Reported File Number: %3jd "
95726516Sbrian	       "Reported Record Number: %jd\n", partition, calculated_fileno,
95828679Sbrian	       calculated_rel_blkno, resid, rep_fileno, rep_blkno);
95928679Sbrian
96026516Sbrian	printf("Flags: ");
9616059Samurai	if (bop > 0 || eop > 0 || bpew > 0) {
9626059Samurai		int need_comma = 0;
9636059Samurai
9646059Samurai		if (bop > 0) {
9656059Samurai			printf("BOP");
9666059Samurai			need_comma = 1;
96726516Sbrian		}
9686059Samurai		if (eop > 0) {
9696059Samurai			if (need_comma != 0)
97036285Sbrian				printf(",");
97136285Sbrian			printf("EOP");
97236285Sbrian			need_comma = 1;
97336285Sbrian		}
97436285Sbrian		if (bpew > 0) {
97536285Sbrian			if (need_comma != 0)
97636285Sbrian				printf(",");
97736285Sbrian			printf("BPEW");
97836285Sbrian			need_comma = 1;
97936285Sbrian		}
98036285Sbrian	} else {
98136285Sbrian		printf("None");
98236285Sbrian	}
98336285Sbrian	printf("\n");
98436285Sbrian	if (verbose != 0) {
98536285Sbrian		printf("---------------------------------\n");
98636285Sbrian		printf("Tape I/O parameters:\n");
98736285Sbrian		for (i = MT_MAXIO; i <= MT_MAX_EFF_IOSIZE; i++) {
98836285Sbrian			printf("  %s (%s): %ju bytes\n",
98930715Sbrian			    req_status_items[i].entry->desc,
99036285Sbrian			    req_status_items[i].name,
99136285Sbrian			    req_status_items[i].entry->value_unsigned);
9926059Samurai		}
99328679Sbrian	}
9946059Samurai
9956059Samurai	return (0);
99631343Sbrian}
99736285Sbrian
9986059Samuraiint
99936285Sbrianmt_xml_cmd(unsigned long cmd, int argc, char **argv, int mtfd, const char *tape)
10006059Samurai{
100136285Sbrian	struct mt_status_data status_data;
100236285Sbrian#if 0
100336285Sbrian	struct mt_status_entry *entry;
100436285Sbrian#endif
100536285Sbrian	char *xml_str;
100636285Sbrian	int retval;
100736285Sbrian	unsigned long ioctl_cmd;
100836285Sbrian
100936285Sbrian	switch (cmd) {
101036285Sbrian	case MT_CMD_PROTECT:
101136285Sbrian	case MTIOCPARAMGET:
101236285Sbrian		ioctl_cmd = MTIOCPARAMGET;
101336285Sbrian		break;
101436285Sbrian	default:
101536285Sbrian		ioctl_cmd = MTIOCEXTGET;
101636285Sbrian		break;
101736285Sbrian	}
101836285Sbrian
101936285Sbrian	retval = mt_get_xml_str(mtfd, ioctl_cmd, &xml_str);
102036285Sbrian	if (retval != 0)
102136285Sbrian		err(1, "Couldn't get mt XML string");
102236285Sbrian
102336285Sbrian	retval = mt_get_status(xml_str, &status_data);
102436285Sbrian	if (retval != XML_STATUS_OK) {
102536285Sbrian		warn("Couldn't get mt status for %s", tape);
102636285Sbrian		goto bailout;
102731343Sbrian	}
102836285Sbrian
102936285Sbrian	/*
103026516Sbrian	 * This gets set if there are memory allocation or other errors in
103126516Sbrian	 * our parsing of the XML.
103236285Sbrian	 */
103328679Sbrian	if (status_data.error != 0) {
103436285Sbrian		warnx("%s", status_data.error_str);
103536285Sbrian		retval = 1;
103626516Sbrian		goto bailout;
103726516Sbrian	}
10386059Samurai#if 0
10396059Samurai	STAILQ_FOREACH(entry, &status_data.entries, links)
104037009Sbrian		mt_status_tree_print(entry, 0, NULL);
104158045Sbrian#endif
104258045Sbrian
104358045Sbrian	switch (cmd) {
104458045Sbrian	case MTIOCEXTGET:
104558045Sbrian		retval = nstatus_print(argc, argv, xml_str, &status_data);
104658045Sbrian		break;
104758045Sbrian	case MTIOCPARAMGET:
104858045Sbrian		retval = mt_param(argc, argv, mtfd, xml_str, &status_data);
104958045Sbrian		break;
105058045Sbrian	case MT_CMD_PROTECT:
105158045Sbrian		retval = mt_protect(argc, argv, mtfd, &status_data);
105258045Sbrian		break;
105337009Sbrian	case MT_CMD_GETDENSITY:
10546059Samurai		retval = mt_getdensity(argc, argv, xml_str, &status_data);
10556059Samurai		break;
10566059Samurai	}
10576059Samurai
10586059Samuraibailout:
10596059Samurai	if (xml_str != NULL)
10606059Samurai		free(xml_str);
106155145Sbrian
106237009Sbrian	mt_status_free(&status_data);
106337009Sbrian
106431121Sbrian	return (retval);
10656059Samurai}
106631822Sbrian
106731822Sbrianstatic int
106831822Sbrianmt_set_param(int mtfd, struct mt_status_data *status_data, char *param_name,
106931822Sbrian    char *param_value)
107031828Sbrian{
107131828Sbrian	struct mt_status_entry *entry;
107231828Sbrian	struct mtparamset param_set;
107331822Sbrian
107431822Sbrian	entry = mt_status_entry_find(status_data,
107531822Sbrian	    __DECONST(char *, "mtparamget"));
107631822Sbrian	if (entry == NULL)
107731828Sbrian		errx(1, "Cannot find parameter root node");
107831828Sbrian
107931828Sbrian	bzero(&param_set, sizeof(param_set));
108031828Sbrian	entry = mt_entry_find(entry, param_name);
108136285Sbrian	if (entry == NULL)
108236285Sbrian		errx(1, "Unknown parameter name \"%s\"", param_name);
108336285Sbrian
108436285Sbrian	strlcpy(param_set.value_name, param_name, sizeof(param_set.value_name));
108536285Sbrian
108631822Sbrian	switch (entry->var_type) {
108731822Sbrian	case MT_TYPE_INT:
108831822Sbrian		param_set.value.value_signed = strtoll(param_value, NULL, 0);
108931121Sbrian		param_set.value_type = MT_PARAM_SET_SIGNED;
109036285Sbrian		param_set.value_len = entry->size;
109137008Sbrian		break;
109231121Sbrian	case MT_TYPE_UINT:
109331156Sbrian		param_set.value.value_unsigned = strtoull(param_value, NULL, 0);
109436285Sbrian		param_set.value_type = MT_PARAM_SET_UNSIGNED;
109547844Sbrian		param_set.value_len = entry->size;
109631156Sbrian		break;
109731156Sbrian	case MT_TYPE_STRING: {
109831156Sbrian		size_t param_len;
109931962Sbrian
110031962Sbrian		param_len = strlen(param_value) + 1;
110131156Sbrian		if (param_len > sizeof(param_set.value.value_fixed_str)) {
110247844Sbrian			param_set.value_type = MT_PARAM_SET_VAR_STR;
110347844Sbrian			param_set.value.value_var_str = param_value;
110447844Sbrian		} else {
110547844Sbrian			param_set.value_type = MT_PARAM_SET_FIXED_STR;
110631156Sbrian			strlcpy(param_set.value.value_fixed_str, param_value,
110747844Sbrian			    sizeof(param_set.value.value_fixed_str));
110847844Sbrian		}
110931156Sbrian		param_set.value_len = param_len;
111031962Sbrian		break;
111131156Sbrian	}
111231822Sbrian	default:
111336285Sbrian		errx(1, "Unknown parameter type %d for %s", entry->var_type,
111431822Sbrian		    param_name);
111531962Sbrian		break;
111631156Sbrian	}
111731156Sbrian
111836285Sbrian	if (ioctl(mtfd, MTIOCPARAMSET, &param_set) == -1)
111931156Sbrian		err(1, "MTIOCPARAMSET");
112037008Sbrian
112131156Sbrian	if (param_set.status != MT_PARAM_STATUS_OK)
11226059Samurai		errx(1, "Failed to set %s: %s", param_name,
11236059Samurai		    param_set.error_str);
112454914Sbrian
112536285Sbrian	return (0);
112636285Sbrian}
112731121Sbrian
112831121Sbrian
112937009Sbriantypedef enum {
113031121Sbrian	MT_PP_LBP_R,
113158045Sbrian	MT_PP_LBP_W,
113254914Sbrian	MT_PP_RBDP,
113354914Sbrian	MT_PP_PI_LENGTH,
113437008Sbrian	MT_PP_PROT_METHOD
113554914Sbrian} mt_protect_param;
113631121Sbrian
113731121Sbrianstatic struct mt_protect_info {
11386059Samurai	const char *name;
113931343Sbrian	struct mt_status_entry *entry;
11406059Samurai	uint32_t value;
114136285Sbrian} mt_protect_list[] = {
114236285Sbrian	{ "lbp_r", NULL, 0 },
114336285Sbrian	{ "lbp_w", NULL, 0 },
114436285Sbrian	{ "rbdp", NULL, 0 },
114536285Sbrian	{ "pi_length", NULL, 0 },
11466059Samurai	{ "prot_method", NULL, 0 }
114736285Sbrian};
114826516Sbrian
114926516Sbrian#define	MT_NUM_PROTECT_PARAMS	(sizeof(mt_protect_list)/sizeof(mt_protect_list[0]))
11506059Samurai
11516059Samurai#define	MT_PROT_NAME	"protection"
11526059Samurai
115331343Sbrianstatic int
11546059Samuraimt_protect(int argc, char **argv, int mtfd, struct mt_status_data *status_data)
115536285Sbrian{
115636285Sbrian	int retval = 0;
115726516Sbrian	int do_enable = 0, do_disable = 0, do_list = 0;
11586059Samurai	int rbdp_set = 0, lbp_w_set = 0, lbp_r_set = 0;
115936285Sbrian	int prot_method_set = 0, pi_length_set = 0;
116036285Sbrian	int verbose = 0;
116136285Sbrian	uint32_t rbdp = 0, lbp_w = 0, lbp_r = 0;
116236285Sbrian	uint32_t prot_method = 0, pi_length = 0;
116336285Sbrian	struct mt_status_entry *prot_entry, *supported_entry;
11646059Samurai	struct mt_status_entry *entry;
116536285Sbrian	struct mtparamset params[MT_NUM_PROTECT_PARAMS];
116636285Sbrian	struct mtsetlist param_list;
116736285Sbrian	unsigned int i;
116836285Sbrian	int c;
11696059Samurai
11706059Samurai	while ((c = getopt(argc, argv, "b:delL:m:r:vw:")) != -1) {
11716059Samurai		switch (c) {
117231343Sbrian		case 'b':
11736059Samurai			rbdp_set = 1;
117436285Sbrian			rbdp = strtoul(optarg, NULL, 0);
117536285Sbrian			if ((rbdp != 0) && (rbdp != 1))
117636285Sbrian				errx(1, "valid values for -b are 0 and 1");
117736285Sbrian			break;
117836285Sbrian		case 'd':
117936285Sbrian			do_disable = 1;
118026516Sbrian			break;
118126516Sbrian		case 'e':
11826059Samurai			do_enable = 1;
11836059Samurai			break;
11846059Samurai		case 'l':
118536285Sbrian			do_list = 1;
11866059Samurai			break;
118737160Sbrian		case 'L':
118837993Sbrian			pi_length_set = 1;
118937160Sbrian			pi_length = strtoul(optarg, NULL, 0);
119037160Sbrian			if (pi_length > SA_CTRL_DP_PI_LENGTH_MASK)
119137385Sbrian				errx(1, "PI length %u > maximum %u",
119237385Sbrian				    pi_length, SA_CTRL_DP_PI_LENGTH_MASK);
119337385Sbrian			break;
119437385Sbrian		case 'm':
119537385Sbrian			prot_method_set = 1;
119637160Sbrian			prot_method = strtoul(optarg, NULL, 0);
119737993Sbrian			if (prot_method > SA_CTRL_DP_METHOD_MAX)
119837160Sbrian				errx(1, "Method %u > maximum %u",
119937160Sbrian				    prot_method, SA_CTRL_DP_METHOD_MAX);
120037160Sbrian			break;
120137160Sbrian		case 'r':
12026059Samurai			lbp_r_set = 1;
120337210Sbrian			lbp_r = strtoul(optarg, NULL, 0);
120437160Sbrian			if ((lbp_r != 0) && (lbp_r != 1))
120537160Sbrian				errx(1, "valid values for -r are 0 and 1");
120637160Sbrian			break;
120737160Sbrian		case 'v':
120837160Sbrian			verbose = 1;
120937160Sbrian			break;
121037160Sbrian		case 'w':
121137160Sbrian			lbp_w_set = 1;
121237160Sbrian			lbp_w = strtoul(optarg, NULL, 0);
121337160Sbrian			if ((lbp_w != 0) && (lbp_r != 1))
121437160Sbrian				errx(1, "valid values for -r are 0 and 1");
121537160Sbrian			break;
121637160Sbrian		default:
121736285Sbrian			break;
121837160Sbrian		}
121937160Sbrian	}
122037160Sbrian
122137160Sbrian	if ((rbdp_set + do_disable + do_enable + do_list + pi_length_set +
122237160Sbrian	    prot_method_set + lbp_r_set + lbp_w_set) == 0)
122337160Sbrian		errx(1, "Need an argument for protect");
122437993Sbrian
122537160Sbrian	if ((do_disable + do_enable + do_list) != 1)
122637160Sbrian		errx(1, "You must specify only one of -e, -d or -l");
122736285Sbrian
122836285Sbrian	if (do_list != 0) {
122936285Sbrian		retval = mt_protect_print(status_data, verbose);
123026516Sbrian		goto bailout;
12316059Samurai	}
12326059Samurai	if (do_enable != 0) {
123325067Sbrian		/*
123436285Sbrian		 * Enable protection, but allow the user to override
12356059Samurai		 * settings if he doesn't want everything turned on.
123637007Sbrian		 */
123737007Sbrian		if (rbdp_set == 0)
123837007Sbrian			rbdp = 1;
123937007Sbrian		if (lbp_w_set == 0)
124037007Sbrian			lbp_w = 1;
124137007Sbrian		if (lbp_r_set == 0)
124237007Sbrian			lbp_r = 1;
124337007Sbrian		/*
12446059Samurai		 * If the user doesn't override it, we default to enabling
124537210Sbrian		 * Reed-Solomon checkums.
124637007Sbrian		 */
124737007Sbrian		if (prot_method_set == 0)
124837007Sbrian			prot_method = SA_CTRL_DP_REED_SOLOMON;
124937007Sbrian		if (pi_length_set == 0)
125037007Sbrian			pi_length = SA_CTRL_DP_RS_LENGTH;
125137007Sbrian	} else if (do_disable != 0) {
125237007Sbrian		/*
125337007Sbrian		 * If the user wants to disable protection, we ignore any
125436285Sbrian		 * other parameters he has set.  Everything gets set to 0.
125536285Sbrian		 */
125636285Sbrian		rbdp = lbp_w = lbp_r = 0;
125736285Sbrian		prot_method = pi_length = 0;
125836285Sbrian	}
12596059Samurai
12606059Samurai	prot_entry = mt_status_entry_find(status_data,
126125067Sbrian	    __DECONST(char *, MT_PROT_NAME));
126236285Sbrian	if (prot_entry == NULL)
126311336Samurai		errx(1, "Unable to find protection information status");
126437018Sbrian
126537018Sbrian	supported_entry = mt_entry_find(prot_entry,
126637018Sbrian	    __DECONST(char *, "protection_supported"));
126737018Sbrian	if (supported_entry == NULL)
126837018Sbrian		errx(1, "Unable to find protection support information");
126937018Sbrian
127037018Sbrian	if (((supported_entry->var_type == MT_TYPE_INT)
127137018Sbrian	  && (supported_entry->value_signed == 0))
127237018Sbrian	 || ((supported_entry->var_type == MT_TYPE_UINT)
127337018Sbrian	  && (supported_entry->value_unsigned == 0)))
127437018Sbrian		errx(1, "This device does not support protection information");
127537018Sbrian
127637018Sbrian	mt_protect_list[MT_PP_LBP_R].value = lbp_r;
127737018Sbrian	mt_protect_list[MT_PP_LBP_W].value = lbp_w;
127837060Sbrian	mt_protect_list[MT_PP_RBDP].value = rbdp;
127937018Sbrian	mt_protect_list[MT_PP_PI_LENGTH].value = pi_length;
128037018Sbrian	mt_protect_list[MT_PP_PROT_METHOD].value = prot_method;
128136285Sbrian
128236285Sbrian	bzero(&params, sizeof(params));
128336285Sbrian	bzero(&param_list, sizeof(param_list));
128436285Sbrian
128525067Sbrian	/*
128625067Sbrian	 * Go through the list and make sure that we have this parameter,
128725067Sbrian	 * and that it is still an unsigned integer.  If not, we've got a
128836285Sbrian	 * problem.
128925067Sbrian	 */
129036285Sbrian	for (i = 0; i < MT_NUM_PROTECT_PARAMS; i++) {
129136285Sbrian		entry = mt_entry_find(prot_entry,
129211336Samurai		    __DECONST(char *, mt_protect_list[i].name));
129336285Sbrian		if (entry == NULL) {
129436285Sbrian			errx(1, "Unable to find parameter %s",
129554917Sbrian			    mt_protect_list[i].name);
129636285Sbrian		}
129711336Samurai		mt_protect_list[i].entry = entry;
129836285Sbrian
129936285Sbrian		if (entry->var_type != MT_TYPE_UINT)
130036285Sbrian			errx(1, "Parameter %s is type %d, not unsigned, "
130136285Sbrian			    "cannot proceed", mt_protect_list[i].name,
130236285Sbrian			    entry->var_type);
130336285Sbrian		snprintf(params[i].value_name, sizeof(params[i].value_name),
130436285Sbrian		    "%s.%s", MT_PROT_NAME, mt_protect_list[i].name);
130536285Sbrian		/* XXX KDM unify types here */
130636285Sbrian		params[i].value_type = MT_PARAM_SET_UNSIGNED;
130736285Sbrian		params[i].value_len = sizeof(mt_protect_list[i].value);
130836285Sbrian		params[i].value.value_unsigned = mt_protect_list[i].value;
130936285Sbrian
131036285Sbrian	}
131136285Sbrian	param_list.num_params = MT_NUM_PROTECT_PARAMS;
131236285Sbrian	param_list.param_len = sizeof(params);
131336285Sbrian	param_list.params = params;
131424939Sbrian
131526516Sbrian	if (ioctl(mtfd, MTIOCSETLIST, &param_list) == -1)
131611336Samurai		err(1, "error issuing MTIOCSETLIST ioctl");
131711336Samurai
131825067Sbrian	for (i = 0; i < MT_NUM_PROTECT_PARAMS; i++) {
131931343Sbrian		if (params[i].status != MT_PARAM_STATUS_OK) {
132028327Sbrian			warnx("%s", params[i].error_str);
132136285Sbrian			retval = 1;
132236285Sbrian		}
132336285Sbrian	}
132436285Sbrianbailout:
132536285Sbrian
132636285Sbrian	return (retval);
132736285Sbrian}
132836285Sbrian
132936285Sbrianstatic int
133028461Sbrianmt_param(int argc, char **argv, int mtfd, char *xml_str,
133128327Sbrian	 struct mt_status_data *status_data)
133228327Sbrian{
133328327Sbrian	int list = 0, do_set = 0, xml_dump = 0;
133428327Sbrian	char *param_name = NULL, *param_value = NULL;
133528327Sbrian	int retval = 0, quiet = 0;
133628327Sbrian	int c;
133731343Sbrian
133826940Sbrian	while ((c = getopt(argc, argv, "lp:qs:x")) != -1) {
133926940Sbrian		switch (c) {
134026940Sbrian		case 'l':
134136285Sbrian			list = 1;
134231081Sbrian			break;
134353125Sbrian		case 'p':
134431081Sbrian			if (param_name != NULL) {
134531081Sbrian				warnx("Only one parameter name may be "
134636285Sbrian				    "specified");
134736285Sbrian				retval = 1;
134836285Sbrian				goto bailout;
134936285Sbrian			}
135036285Sbrian			param_name = strdup(optarg);
135136285Sbrian			break;
135236285Sbrian		case 'q':
135353125Sbrian			quiet = 1;
135453125Sbrian			break;
135553125Sbrian		case 's':
135653125Sbrian			if (param_value != NULL) {
135753125Sbrian				warnx("Only one parameter value may be "
135831081Sbrian				    "specified");
135953125Sbrian				retval = 1;
136071764Sbrian				goto bailout;
136171764Sbrian			}
136271764Sbrian			param_value = strdup(optarg);
136371657Sbrian			do_set = 1;
136471657Sbrian			break;
136571657Sbrian		case 'x':
136671657Sbrian			xml_dump = 1;
136771657Sbrian			break;
136871657Sbrian		default:
136971657Sbrian			break;
137071657Sbrian		}
137171764Sbrian	}
137271657Sbrian
137371657Sbrian	if ((list + do_set + xml_dump) != 1) {
137471764Sbrian		warnx("You must specify only one of -s, -l or -x");
137571657Sbrian		retval = 1;
137671657Sbrian		goto bailout;
137771657Sbrian	}
137871657Sbrian
137971657Sbrian	if (xml_dump != 0) {
138071657Sbrian		printf("%s", xml_str);
138136285Sbrian		retval = 0;
138271657Sbrian		goto bailout;
138371657Sbrian	}
138471657Sbrian
138571657Sbrian	if (do_set != 0) {
138636285Sbrian		if (param_name == NULL)
138731081Sbrian			errx(1, "You must specify -p with -s");
138836285Sbrian
138931081Sbrian		retval = mt_set_param(mtfd, status_data, param_name,
139071657Sbrian		    param_value);
139171657Sbrian	} else if (list != 0)
139231081Sbrian		retval = mt_param_list(status_data, param_name, quiet);
139336285Sbrian
139431081Sbrianbailout:
139536285Sbrian	free(param_name);
139628679Sbrian	free(param_value);
139753125Sbrian	return (retval);
139831081Sbrian}
139953125Sbrian
140053125Sbrianint
140136285Sbrianmt_print_density_entry(struct mt_status_entry *density_root, int indent)
140236285Sbrian{
140336285Sbrian	struct mt_status_entry *entry;
140436285Sbrian	int retval = 0;
140537210Sbrian
140636285Sbrian	STAILQ_FOREACH(entry, &density_root->child_entries, links) {
140736285Sbrian		if (entry->var_type == MT_TYPE_NODE) {
140836285Sbrian			retval = mt_print_density_entry(entry, indent + 2);
140927346Sbrian			if (retval != 0)
141036285Sbrian				break;
141128679Sbrian			else
141231081Sbrian				continue;
141331081Sbrian		}
141428679Sbrian		if ((strcmp(entry->entry_name, "primary_density_code") == 0)
141536285Sbrian		 || (strcmp(entry->entry_name, "secondary_density_code") == 0)
141636285Sbrian		 || (strcmp(entry->entry_name, "density_code") == 0)) {
141736285Sbrian
141836285Sbrian			printf("%*s%s (%s): %s\n", indent, "", entry->desc ?
141931081Sbrian			    entry->desc : "", entry->entry_name,
142031081Sbrian			    denstostring(entry->value_unsigned));
142131081Sbrian		} else if (strcmp(entry->entry_name, "density_flags") == 0) {
142231081Sbrian			printf("%*sMedium Access: ", indent, "");
142331081Sbrian			if (entry->value_unsigned & MT_DENS_WRITE_OK) {
142436285Sbrian				printf("Read and Write\n");
142528679Sbrian			} else {
142631081Sbrian				printf("Read Only\n");
142727346Sbrian			}
142831081Sbrian			printf("%*sDefault Density: %s\n", indent, "",
142936285Sbrian			    (entry->value_unsigned & MT_DENS_DEFLT) ? "Yes" :
143036285Sbrian			    "No");
143136285Sbrian			printf("%*sDuplicate Density: %s\n", indent, "",
143236285Sbrian			    (entry->value_unsigned & MT_DENS_DUP) ? "Yes" :
143336285Sbrian			    "No");
143436285Sbrian		} else if (strcmp(entry->entry_name, "media_width") == 0) {
143536285Sbrian			printf("%*s%s (%s): %.1f mm\n", indent, "",
143627346Sbrian			    entry->desc ?  entry->desc : "", entry->entry_name,
143731081Sbrian			    (double)((double)entry->value_unsigned / 10));
143826940Sbrian		} else if (strcmp(entry->entry_name, "medium_length") == 0) {
143926940Sbrian			printf("%*s%s (%s): %ju m\n", indent, "",
144026940Sbrian			    entry->desc ?  entry->desc : "", entry->entry_name,
144126940Sbrian			    (uintmax_t)entry->value_unsigned);
144226940Sbrian		} else if (strcmp(entry->entry_name, "capacity") == 0) {
144331343Sbrian			printf("%*s%s (%s): %ju MB\n", indent, "", entry->desc ?
14446059Samurai			    entry->desc : "", entry->entry_name,
14456059Samurai			    (uintmax_t)entry->value_unsigned);
144636285Sbrian		} else {
144736285Sbrian			printf("%*s%s (%s): %s\n", indent, "", entry->desc ?
14486059Samurai			    entry->desc : "", entry->entry_name, entry->value);
14496059Samurai		}
145036285Sbrian	}
145131343Sbrian
14526059Samurai	return (retval);
14536059Samurai}
14546059Samurai
145536285Sbrianint
145636285Sbrianmt_print_density_report(struct mt_status_entry *report_root, int indent)
14576059Samurai{
145826516Sbrian	struct mt_status_entry *mt_report, *media_report;
14596059Samurai	struct mt_status_entry *entry;
14606059Samurai	int retval = 0;
14616059Samurai
146231343Sbrian	mt_report = mt_entry_find(report_root,
14636059Samurai	    __DECONST(char *, MT_MEDIUM_TYPE_REPORT_NAME));
146481634Sbrian	if (mt_report == NULL)
146581634Sbrian		return (1);
146632267Sbrian
146732267Sbrian	media_report = mt_entry_find(report_root,
146840561Sbrian	    __DECONST(char *, MT_MEDIA_REPORT_NAME));
146940561Sbrian	if (media_report == NULL)
147040561Sbrian		return (1);
147132267Sbrian
147281634Sbrian	if ((mt_report->value_signed == 0)
147381634Sbrian	 && (media_report->value_signed == 0)) {
147481634Sbrian		printf("%*sThis tape drive supports the following "
147581634Sbrian		    "media densities:\n", indent, "");
147681634Sbrian	} else if ((mt_report->value_signed == 0)
147728394Sbrian		&& (media_report->value_signed != 0)) {
147836285Sbrian		printf("%*sThe tape currently in this drive supports "
147981634Sbrian		    "the following media densities:\n", indent, "");
148028679Sbrian	} else if ((mt_report->value_signed != 0)
148136285Sbrian		&& (media_report->value_signed == 0)) {
148236285Sbrian		printf("%*sThis tape drive supports the following "
148336285Sbrian		    "media types:\n", indent, "");
148481634Sbrian	} else {
148581634Sbrian		printf("%*sThis tape currently in this drive supports "
148636285Sbrian		    "the following media types:\n", indent, "");
148781634Sbrian	}
148881634Sbrian
14899440Samurai	STAILQ_FOREACH(entry, &report_root->child_entries, links) {
14906059Samurai		struct mt_status_nv *nv;
14916059Samurai
14926059Samurai		if (strcmp(entry->entry_name, MT_DENSITY_ENTRY_NAME) != 0)
149328394Sbrian			continue;
149440561Sbrian
149581634Sbrian		STAILQ_FOREACH(nv, &entry->nv_list, links) {
149681634Sbrian			if (strcmp(nv->name, "num") != 0)
149781634Sbrian				continue;
149881634Sbrian
149981634Sbrian			break;
150036285Sbrian		}
150136285Sbrian
150236928Sbrian		indent += 2;
150332267Sbrian
150431121Sbrian		printf("%*sDensity Entry", indent, "");
150526516Sbrian		if (nv != NULL)
15066059Samurai			printf(" %s", nv->value);
15076059Samurai		printf(":\n");
150818752Sjkh
150944305Sbrian		retval = mt_print_density_entry(entry, indent + 2);
151044305Sbrian
151144305Sbrian		indent -= 2;
151244305Sbrian	}
151344305Sbrian
151444305Sbrian	return (retval);
151544305Sbrian}
151644305Sbrian
151744305Sbrianint
151844305Sbrianmt_print_density(struct mt_status_entry *density_root, int indent)
151944305Sbrian{
152044305Sbrian	struct mt_status_entry *entry;
152144305Sbrian	int retval = 0;
152244305Sbrian
152344305Sbrian	/*
152444305Sbrian	 * We should have this entry for every tape drive.  This particular
152544305Sbrian	 * value is reported via the mode page block header, not the
152644305Sbrian	 * SCSI REPORT DENSITY SUPPORT command.
152744305Sbrian	 */
152844305Sbrian	entry = mt_entry_find(density_root,
152944305Sbrian	    __DECONST(char *, MT_MEDIA_DENSITY_NAME));
153044305Sbrian	if (entry == NULL)
153144305Sbrian		errx(1, "Unable to find node %s", MT_MEDIA_DENSITY_NAME);
153244305Sbrian
153344305Sbrian	printf("%*sCurrent density: %s\n", indent, "",
153444305Sbrian	    denstostring(entry->value_unsigned));
153544305Sbrian
153644305Sbrian	/*
153744305Sbrian	 * It isn't an error if we don't have any density reports.  Tape
153844305Sbrian	 * drives that don't support the REPORT DENSITY SUPPORT command
153944305Sbrian	 * won't have any; they will only have the current density entry
154044305Sbrian	 * above.
154144305Sbrian	 */
154244305Sbrian	STAILQ_FOREACH(entry, &density_root->child_entries, links) {
154344305Sbrian		if (strcmp(entry->entry_name, MT_DENSITY_REPORT_NAME) != 0)
154444305Sbrian			continue;
154544305Sbrian
154644305Sbrian		retval = mt_print_density_report(entry, indent);
154744305Sbrian	}
154844305Sbrian
154944305Sbrian	return (retval);
155031343Sbrian}
15516059Samurai
155237210Sbrianint
155379119Sbrianmt_getdensity(int argc, char **argv, char *xml_str,
155478410Sbrian    struct mt_status_data *status_data)
155531343Sbrian{
155636285Sbrian	int retval = 0;
155736285Sbrian	int verbose = 0, xml_dump = 0;
155881634Sbrian	struct mt_status_entry *density_root = NULL;
155981634Sbrian	int c;
15606059Samurai
156136285Sbrian	while ((c = getopt(argc, argv, "vx")) != -1) {
156236285Sbrian		switch (c) {
156326551Sbrian		case 'v':
156431343Sbrian			verbose = 1;
156526551Sbrian			break;
156679119Sbrian		case 'x':
156779119Sbrian			xml_dump = 1;
156836285Sbrian			break;
156936285Sbrian		}
157036285Sbrian	}
157136285Sbrian
157236285Sbrian	if (xml_dump != 0) {
157336285Sbrian		printf("%s", xml_str);
157436285Sbrian		return (0);
157536285Sbrian	}
157636285Sbrian
157736285Sbrian	density_root = mt_status_entry_find(status_data,
157826551Sbrian	    __DECONST(char *, MT_DENSITY_ROOT_NAME));
157928679Sbrian	if (density_root == NULL)
158050139Sbrian		errx(1, "Cannot find density root node %s",
158150139Sbrian		    MT_DENSITY_ROOT_NAME);
158250139Sbrian
158328679Sbrian	retval = mt_print_density(density_root, 0);
158437210Sbrian
158528679Sbrian	return (retval);
158640622Sbrian}
158758880Sbrian
158858880Sbrianstatic void
158958880Sbrianwarn_eof(void)
159058880Sbrian{
159140622Sbrian	fprintf(stderr,
159240622Sbrian		"The \"eof\" command has been disabled.\n"
159340622Sbrian		"Use \"weof\" if you really want to write end-of-file marks,\n"
159440622Sbrian		"or \"eom\" if you rather want to skip to the end of "
159540622Sbrian		"recorded medium.\n");
159640622Sbrian	exit(1);
159736285Sbrian}
159828679Sbrian