mt.c revision 280230
11590Srgrimes/*
21590Srgrimes * Copyright (c) 1980, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 4. Neither the name of the University nor the names of its contributors
141590Srgrimes *    may be used to endorse or promote products derived from this software
151590Srgrimes *    without specific prior written permission.
161590Srgrimes *
171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271590Srgrimes * SUCH DAMAGE.
281590Srgrimes */
29279219Sken/*-
30279219Sken * Copyright (c) 2013, 2014, 2015 Spectra Logic Corporation
31279219Sken * All rights reserved.
32279219Sken *
33279219Sken * Redistribution and use in source and binary forms, with or without
34279219Sken * modification, are permitted provided that the following conditions
35279219Sken * are met:
36279219Sken * 1. Redistributions of source code must retain the above copyright
37279219Sken *    notice, this list of conditions, and the following disclaimer,
38279219Sken *    without modification.
39279219Sken * 2. Redistributions in binary form must reproduce at minimum a disclaimer
40279219Sken *    substantially similar to the "NO WARRANTY" disclaimer below
41279219Sken *    ("Disclaimer") and any redistribution must be conditioned upon
42279219Sken *    including a substantially similar Disclaimer requirement for further
43279219Sken *    binary redistribution.
44279219Sken *
45279219Sken * NO WARRANTY
46279219Sken * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
47279219Sken * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
48279219Sken * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
49279219Sken * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
50279219Sken * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51279219Sken * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52279219Sken * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53279219Sken * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
54279219Sken * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
55279219Sken * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
56279219Sken * POSSIBILITY OF SUCH DAMAGES.
57279219Sken *
58279219Sken * Authors: Ken Merry           (Spectra Logic Corporation)
59279219Sken */
601590Srgrimes
611590Srgrimes#ifndef lint
6227752Scharnierstatic const char copyright[] =
631590Srgrimes"@(#) Copyright (c) 1980, 1993\n\
641590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
651590Srgrimes#endif /* not lint */
661590Srgrimes
671590Srgrimes#ifndef lint
6827752Scharnier#if 0
6923693Speterstatic char sccsid[] = "@(#)mt.c	8.2 (Berkeley) 5/4/95";
7027752Scharnier#endif
711590Srgrimes#endif /* not lint */
721590Srgrimes
7394505Scharnier#include <sys/cdefs.h>
7494505Scharnier__FBSDID("$FreeBSD: head/usr.bin/mt/mt.c 280230 2015-03-18 20:52:34Z ken $");
7594505Scharnier
761590Srgrimes/*
771590Srgrimes * mt --
781590Srgrimes *   magnetic tape manipulation program
791590Srgrimes */
801590Srgrimes#include <sys/types.h>
811590Srgrimes#include <sys/ioctl.h>
821590Srgrimes#include <sys/mtio.h>
83279219Sken#include <sys/queue.h>
84279219Sken#include <sys/sbuf.h>
8523693Speter
8623693Speter#include <ctype.h>
8727752Scharnier#include <err.h>
881590Srgrimes#include <fcntl.h>
8923693Speter#include <stdio.h>
901590Srgrimes#include <stdlib.h>
911590Srgrimes#include <string.h>
9223693Speter#include <unistd.h>
93279219Sken#include <stdint.h>
94279219Sken#include <errno.h>
95279219Sken#include <bsdxml.h>
96279219Sken#include <mtlib.h>
971590Srgrimes
98279219Sken#include <cam/cam.h>
99279219Sken#include <cam/cam_ccb.h>
100279219Sken#include <cam/cam_periph.h>
101279219Sken#include <cam/scsi/scsi_all.h>
102279219Sken#include <cam/scsi/scsi_sa.h>
103279219Sken
1047913Sjoerg/* the appropriate sections of <sys/mtio.h> are also #ifdef'd for FreeBSD */
1057913Sjoerg/* c_flags */
1067913Sjoerg#define NEED_2ARGS	0x01
1077929Sjoerg#define ZERO_ALLOWED	0x02
1087929Sjoerg#define IS_DENSITY	0x04
1099541Sjoerg#define DISABLE_THIS	0x08
11039260Sgibbs#define IS_COMP		0x10
111279219Sken#define	USE_GETOPT	0x20
1127913Sjoerg
11339260Sgibbs#ifndef TRUE
11439260Sgibbs#define TRUE 1
11539260Sgibbs#endif
11639260Sgibbs#ifndef FALSE
11739260Sgibbs#define FALSE 0
11839260Sgibbs#endif
119279219Sken#ifndef MAX
120279219Sken#define	MAX(a, b) (a > b) ? a : b
121279219Sken#endif
12239260Sgibbs
123279219Skentypedef enum {
124279219Sken	MT_CMD_NONE	= MTLOAD + 1,
125279219Sken	MT_CMD_PROTECT,
126279219Sken	MT_CMD_GETDENSITY
127279219Sken} mt_commands;
128279219Sken
129227174Sedstatic const struct commands {
130152396Sdwmalone	const char *c_name;
131228619Sdim	unsigned long c_code;
1321590Srgrimes	int c_ronly;
1337913Sjoerg	int c_flags;
1341590Srgrimes} com[] = {
13594505Scharnier	{ "bsf",	MTBSF,	1, 0 },
13694505Scharnier	{ "bsr",	MTBSR,	1, 0 },
1379541Sjoerg	/* XXX FreeBSD considered "eof" dangerous, since it's being
1389541Sjoerg	   confused with "eom" (and is an alias for "weof" anyway) */
1399541Sjoerg	{ "eof",	MTWEOF, 0, DISABLE_THIS },
14094505Scharnier	{ "fsf",	MTFSF,	1, 0 },
14194505Scharnier	{ "fsr",	MTFSR,	1, 0 },
14294505Scharnier	{ "offline",	MTOFFL,	1, 0 },
143279219Sken	{ "load",	MTLOAD, 1, 0 },
14494505Scharnier	{ "rewind",	MTREW,	1, 0 },
14594505Scharnier	{ "rewoffl",	MTOFFL,	1, 0 },
146279219Sken	{ "ostatus",	MTNOP,	1, 0 },
14741913Smjacob	{ "weof",	MTWEOF,	0, ZERO_ALLOWED },
148279219Sken	{ "weofi",	MTWEOFI, 0, ZERO_ALLOWED },
14939260Sgibbs	{ "erase",	MTERASE, 0, ZERO_ALLOWED},
1507913Sjoerg	{ "blocksize",	MTSETBSIZ, 0, NEED_2ARGS|ZERO_ALLOWED },
1517929Sjoerg	{ "density",	MTSETDNSTY, 0, NEED_2ARGS|ZERO_ALLOWED|IS_DENSITY },
15294505Scharnier	{ "eom",	MTEOD, 1, 0 },
15394505Scharnier	{ "eod",	MTEOD, 1, 0 },
15494505Scharnier	{ "smk",	MTWSS, 0, 0 },
15594505Scharnier	{ "wss",	MTWSS, 0, 0 },
15694505Scharnier	{ "fss",	MTFSS, 1, 0 },
15794505Scharnier	{ "bss",	MTBSS, 1, 0 },
15839260Sgibbs	{ "comp",	MTCOMP, 0, NEED_2ARGS|ZERO_ALLOWED|IS_COMP },
15994505Scharnier	{ "retension",	MTRETENS, 1, 0 },
16094505Scharnier	{ "rdhpos",     MTIOCRDHPOS,  0, 0 },
16194505Scharnier	{ "rdspos",     MTIOCRDSPOS,  0, 0 },
16241913Smjacob	{ "sethpos",    MTIOCHLOCATE, 0, NEED_2ARGS|ZERO_ALLOWED },
16341913Smjacob	{ "setspos",    MTIOCSLOCATE, 0, NEED_2ARGS|ZERO_ALLOWED },
16494505Scharnier	{ "errstat",	MTIOCERRSTAT, 0, 0 },
16546928Smjacob	{ "setmodel",	MTIOCSETEOTMODEL, 0, NEED_2ARGS|ZERO_ALLOWED },
16646928Smjacob	{ "seteotmodel",	MTIOCSETEOTMODEL, 0, NEED_2ARGS|ZERO_ALLOWED },
16794505Scharnier	{ "getmodel",	MTIOCGETEOTMODEL, 0, 0 },
16894505Scharnier	{ "geteotmodel",	MTIOCGETEOTMODEL, 0, 0 },
169279219Sken	{ "rblim", 	MTIOCRBLIM, 0, 0},
170279219Sken	{ "getdensity",	MT_CMD_GETDENSITY, 0, USE_GETOPT},
171279219Sken	{ "status",	MTIOCEXTGET, 0, USE_GETOPT },
172279219Sken	{ "locate",	MTIOCEXTLOCATE, 0, USE_GETOPT },
173279219Sken	{ "param",	MTIOCPARAMGET, 0, USE_GETOPT },
174279219Sken	{ "protect", 	MT_CMD_PROTECT, 0, USE_GETOPT },
17594505Scharnier	{ NULL, 0, 0, 0 }
1761590Srgrimes};
1771590Srgrimes
178279219Sken
179227174Sedstatic const char *getblksiz(int);
180227174Sedstatic void printreg(const char *, u_int, const char *);
181227174Sedstatic void status(struct mtget *);
182227174Sedstatic void usage(void);
183279219Skenconst char *get_driver_state_str(int dsreg);
184279219Skenstatic void st_status (struct mtget *);
185279219Skenstatic int mt_locate(int argc, char **argv, int mtfd, const char *tape);
186279219Skenstatic int nstatus_print(int argc, char **argv, char *xml_str,
187279219Sken			 struct mt_status_data *status_data);
188279219Skenstatic int mt_xml_cmd(unsigned long cmd, int argc, char **argv, int mtfd,
189279219Sken		      const char *tape);
190279219Skenstatic int mt_print_density_entry(struct mt_status_entry *density_root, int indent);
191279219Skenstatic int mt_print_density_report(struct mt_status_entry *report_root, int indent);
192279219Skenstatic int mt_print_density(struct mt_status_entry *density_root, int indent);
193279219Skenstatic int mt_getdensity(int argc, char **argv, char *xml_str,
194279219Sken			 struct mt_status_data *status_data);
195279219Skenstatic int mt_set_param(int mtfd, struct mt_status_data *status_data,
196279219Sken			char *param_name, char *param_value);
197279219Skenstatic int mt_protect(int argc, char **argv, int mtfd,
198279219Sken		      struct mt_status_data *status_data);
199279219Skenstatic int mt_param(int argc, char **argv, int mtfd, char *xml_str,
200279219Sken		    struct mt_status_data *status_data);
201279219Skenstatic const char *denstostring (int d);
202227174Sedstatic u_int32_t stringtocomp(const char *s);
203227174Sedstatic const char *comptostring(u_int32_t comp);
204227174Sedstatic void warn_eof(void);
2051590Srgrimes
2061590Srgrimesint
207152396Sdwmalonemain(int argc, char *argv[])
2081590Srgrimes{
209227174Sed	const struct commands *comp;
2101590Srgrimes	struct mtget mt_status;
2111590Srgrimes	struct mtop mt_com;
2121590Srgrimes	int ch, len, mtfd;
213152396Sdwmalone	const char *p, *tape;
2141590Srgrimes
215279261Sken	bzero(&mt_com, sizeof(mt_com));
216279261Sken
2171590Srgrimes	if ((tape = getenv("TAPE")) == NULL)
2181590Srgrimes		tape = DEFTAPE;
2191590Srgrimes
22024360Simp	while ((ch = getopt(argc, argv, "f:t:")) != -1)
2211590Srgrimes		switch(ch) {
2221590Srgrimes		case 'f':
2231590Srgrimes		case 't':
2241590Srgrimes			tape = optarg;
2251590Srgrimes			break;
2261590Srgrimes		case '?':
227279219Sken			usage();
228279219Sken			break;
2291590Srgrimes		default:
230279219Sken			break;
2311590Srgrimes		}
2321590Srgrimes	argc -= optind;
2331590Srgrimes	argv += optind;
2341590Srgrimes
235279219Sken	if (argc < 1)
2361590Srgrimes		usage();
2371590Srgrimes
2381590Srgrimes	len = strlen(p = *argv++);
2391590Srgrimes	for (comp = com;; comp++) {
2401590Srgrimes		if (comp->c_name == NULL)
24127752Scharnier			errx(1, "%s: unknown command", p);
2421590Srgrimes		if (strncmp(p, comp->c_name, len) == 0)
2431590Srgrimes			break;
2441590Srgrimes	}
2457913Sjoerg	if((comp->c_flags & NEED_2ARGS) && argc != 2)
2467913Sjoerg		usage();
2479541Sjoerg	if(comp->c_flags & DISABLE_THIS) {
2489541Sjoerg		warn_eof();
2499541Sjoerg	}
250279219Sken	if (comp->c_flags & USE_GETOPT) {
251279219Sken		argc--;
252279219Sken		optind = 0;
253279219Sken	}
254279219Sken
2551590Srgrimes	if ((mtfd = open(tape, comp->c_ronly ? O_RDONLY : O_RDWR)) < 0)
25627752Scharnier		err(1, "%s", tape);
2571590Srgrimes	if (comp->c_code != MTNOP) {
2581590Srgrimes		mt_com.mt_op = comp->c_code;
2591590Srgrimes		if (*argv) {
2607929Sjoerg			if (!isdigit(**argv) &&
26141925Smjacob			    (comp->c_flags & IS_DENSITY)) {
2627929Sjoerg				const char *dcanon;
263279219Sken				mt_com.mt_count = mt_density_num(*argv);
2647929Sjoerg				if (mt_com.mt_count == 0)
26527752Scharnier					errx(1, "%s: unknown density", *argv);
2667929Sjoerg				dcanon = denstostring(mt_com.mt_count);
2677929Sjoerg				if (strcmp(dcanon, *argv) != 0)
2687929Sjoerg					printf(
2697929Sjoerg					"Using \"%s\" as an alias for %s\n",
2707929Sjoerg					       *argv, dcanon);
2717929Sjoerg				p = "";
27239260Sgibbs			} else if (!isdigit(**argv) &&
27341925Smjacob				   (comp->c_flags & IS_COMP)) {
27439260Sgibbs
27539260Sgibbs				mt_com.mt_count = stringtocomp(*argv);
27639260Sgibbs				if ((u_int32_t)mt_com.mt_count == 0xf0f0f0f0)
27739260Sgibbs					errx(1, "%s: unknown compression",
27839260Sgibbs					     *argv);
27939260Sgibbs				p = "";
280279219Sken			} else if ((comp->c_flags & USE_GETOPT) == 0) {
281152396Sdwmalone				char *q;
2827929Sjoerg				/* allow for hex numbers; useful for density */
283152396Sdwmalone				mt_com.mt_count = strtol(*argv, &q, 0);
284152396Sdwmalone				p = q;
285152396Sdwmalone			}
286279219Sken			if (((comp->c_flags & USE_GETOPT) == 0)
287279219Sken			 && (((mt_com.mt_count <=
288279219Sken			     ((comp->c_flags & ZERO_ALLOWED)? -1: 0))
289279219Sken			   && ((comp->c_flags & IS_COMP) == 0))
290279219Sken			  || *p))
29127752Scharnier				errx(1, "%s: illegal count", *argv);
2921590Srgrimes		}
2931590Srgrimes		else
2941590Srgrimes			mt_com.mt_count = 1;
29541913Smjacob		switch (comp->c_code) {
29641945Smjacob		case MTIOCERRSTAT:
29741945Smjacob		{
29894505Scharnier			unsigned int i;
29941945Smjacob			union mterrstat umn;
30041945Smjacob			struct scsi_tape_errors *s = &umn.scsi_errstat;
30141945Smjacob
30241945Smjacob			if (ioctl(mtfd, comp->c_code, (caddr_t)&umn) < 0)
30341945Smjacob				err(2, "%s", tape);
30441945Smjacob			(void)printf("Last I/O Residual: %u\n", s->io_resid);
30542010Smjacob			(void)printf(" Last I/O Command:");
30642010Smjacob			for (i = 0; i < sizeof (s->io_cdb); i++)
30742010Smjacob				(void)printf(" %02X", s->io_cdb[i]);
30842010Smjacob			(void)printf("\n");
30941945Smjacob			(void)printf("   Last I/O Sense:\n\n\t");
31041945Smjacob			for (i = 0; i < sizeof (s->io_sense); i++) {
31141945Smjacob				(void)printf(" %02X", s->io_sense[i]);
31241945Smjacob				if (((i + 1) & 0xf) == 0) {
31341945Smjacob					(void)printf("\n\t");
31441945Smjacob				}
31541945Smjacob			}
31641945Smjacob			(void)printf("\n");
31741945Smjacob			(void)printf("Last Control Residual: %u\n",
31841945Smjacob			    s->ctl_resid);
31942010Smjacob			(void)printf(" Last Control Command:");
32042010Smjacob			for (i = 0; i < sizeof (s->ctl_cdb); i++)
32142010Smjacob				(void)printf(" %02X", s->ctl_cdb[i]);
32242010Smjacob			(void)printf("\n");
32341945Smjacob			(void)printf("   Last Control Sense:\n\n\t");
32441945Smjacob			for (i = 0; i < sizeof (s->ctl_sense); i++) {
32541945Smjacob				(void)printf(" %02X", s->ctl_sense[i]);
32641945Smjacob				if (((i + 1) & 0xf) == 0) {
32741945Smjacob					(void)printf("\n\t");
32841945Smjacob				}
32941945Smjacob			}
33041945Smjacob			(void)printf("\n\n");
33141945Smjacob			exit(0);
33241945Smjacob			/* NOTREACHED */
33341945Smjacob		}
33441913Smjacob		case MTIOCRDHPOS:
33541913Smjacob		case MTIOCRDSPOS:
33641925Smjacob		{
33741925Smjacob			u_int32_t block;
33841925Smjacob			if (ioctl(mtfd, comp->c_code, (caddr_t)&block) < 0)
33941913Smjacob				err(2, "%s", tape);
34041945Smjacob			(void)printf("%s: %s block location %u\n", tape,
34141913Smjacob			    (comp->c_code == MTIOCRDHPOS)? "hardware" :
34241925Smjacob			    "logical", block);
34341925Smjacob			exit(0);
34441913Smjacob			/* NOTREACHED */
34541925Smjacob		}
34641913Smjacob		case MTIOCSLOCATE:
34741913Smjacob		case MTIOCHLOCATE:
34841925Smjacob		{
34941925Smjacob			u_int32_t block = (u_int32_t)mt_com.mt_count;
35041925Smjacob			if (ioctl(mtfd, comp->c_code, (caddr_t)&block) < 0)
35141913Smjacob				err(2, "%s", tape);
35241925Smjacob			exit(0);
35341913Smjacob			/* NOTREACHED */
35441925Smjacob		}
35546928Smjacob		case MTIOCGETEOTMODEL:
35646928Smjacob		{
35746928Smjacob			u_int32_t om;
35846928Smjacob			if (ioctl(mtfd, MTIOCGETEOTMODEL, (caddr_t)&om) < 0)
35946928Smjacob				err(2, "%s", tape);
36046928Smjacob			(void)printf("%s: the model is %u filemar%s at EOT\n",
36146928Smjacob			    tape, om, (om > 1)? "ks" : "k");
36246928Smjacob			exit(0);
36346928Smjacob			/* NOTREACHED */
36446928Smjacob		}
36546928Smjacob		case MTIOCSETEOTMODEL:
36646928Smjacob		{
36746928Smjacob			u_int32_t om, nm = (u_int32_t)mt_com.mt_count;
36846928Smjacob			if (ioctl(mtfd, MTIOCGETEOTMODEL, (caddr_t)&om) < 0)
36946928Smjacob				err(2, "%s", tape);
37046928Smjacob			if (ioctl(mtfd, comp->c_code, (caddr_t)&nm) < 0)
37146928Smjacob				err(2, "%s", tape);
37246928Smjacob			(void)printf("%s: old model was %u filemar%s at EOT\n",
37346928Smjacob			    tape, om, (om > 1)? "ks" : "k");
37446928Smjacob			(void)printf("%s: new model  is %u filemar%s at EOT\n",
37546928Smjacob			    tape, nm, (nm > 1)? "ks" : "k");
37646928Smjacob			exit(0);
37746928Smjacob			/* NOTREACHED */
37846928Smjacob		}
379279219Sken		case MTIOCRBLIM:
380279219Sken		{
381279219Sken			struct mtrblim rblim;
382279219Sken
383279219Sken			bzero(&rblim, sizeof(rblim));
384279219Sken
385279219Sken			if (ioctl(mtfd, MTIOCRBLIM, (caddr_t)&rblim) < 0)
386279219Sken				err(2, "%s", tape);
387279219Sken			(void)printf("%s: min blocksize %u bytes, "
388279219Sken			    "max blocksize %u bytes, granularity %u bytes\n",
389279219Sken			    tape, rblim.min_block_length,
390279219Sken			    rblim.max_block_length, rblim.granularity);
391279219Sken			exit(0);
392279219Sken			/* NOTREACHED */
393279219Sken		}
394279219Sken		case MTIOCPARAMGET:
395279219Sken		case MTIOCEXTGET:
396279219Sken		case MT_CMD_PROTECT:
397279219Sken		case MT_CMD_GETDENSITY:
398279219Sken		{
399279219Sken			int retval = 0;
400279219Sken
401279219Sken			retval = mt_xml_cmd(comp->c_code, argc, argv, mtfd,
402279219Sken			    tape);
403279219Sken
404279219Sken			exit(retval);
405279219Sken		}
406279219Sken		case MTIOCEXTLOCATE:
407279219Sken		{
408279219Sken			int retval = 0;
409279219Sken
410279219Sken			retval = mt_locate(argc, argv, mtfd, tape);
411279219Sken
412279219Sken			exit(retval);
413279219Sken		}
41441913Smjacob		default:
41541913Smjacob			break;
41641913Smjacob		}
4171590Srgrimes		if (ioctl(mtfd, MTIOCTOP, &mt_com) < 0)
41827752Scharnier			err(1, "%s: %s", tape, comp->c_name);
4191590Srgrimes	} else {
4201590Srgrimes		if (ioctl(mtfd, MTIOCGET, &mt_status) < 0)
42127752Scharnier			err(1, NULL);
4221590Srgrimes		status(&mt_status);
4231590Srgrimes	}
42441925Smjacob	exit(0);
4251590Srgrimes	/* NOTREACHED */
4261590Srgrimes}
4271590Srgrimes
428227174Sedstatic const struct tape_desc {
4291590Srgrimes	short	t_type;		/* type of magtape device */
430152396Sdwmalone	const char *t_name;	/* printing name */
431152396Sdwmalone	const char *t_dsbits;	/* "drive status" register */
432152396Sdwmalone	const char *t_erbits;	/* "error" register */
4331590Srgrimes} tapes[] = {
4347913Sjoerg	{ MT_ISAR,	"SCSI tape drive", 0,		0 },
43594505Scharnier	{ 0, NULL, 0, 0 }
4361590Srgrimes};
4371590Srgrimes
4381590Srgrimes/*
4391590Srgrimes * Interpret the status buffer returned
4401590Srgrimes */
441227174Sedstatic void
442152396Sdwmalonestatus(struct mtget *bp)
4431590Srgrimes{
444227174Sed	const struct tape_desc *mt;
4451590Srgrimes
4461590Srgrimes	for (mt = tapes;; mt++) {
4471590Srgrimes		if (mt->t_type == 0) {
4481590Srgrimes			(void)printf("%d: unknown tape drive type\n",
4491590Srgrimes			    bp->mt_type);
4501590Srgrimes			return;
4511590Srgrimes		}
4521590Srgrimes		if (mt->t_type == bp->mt_type)
4531590Srgrimes			break;
4541590Srgrimes	}
4557913Sjoerg	if(mt->t_type == MT_ISAR)
4567913Sjoerg		st_status(bp);
4577913Sjoerg	else {
45892764Sphk		(void)printf("%s tape drive, residual=%d\n",
45992764Sphk		    mt->t_name, bp->mt_resid);
46092764Sphk		printreg("ds", (unsigned short)bp->mt_dsreg, mt->t_dsbits);
46192764Sphk		printreg("\ner", (unsigned short)bp->mt_erreg, mt->t_erbits);
46292764Sphk		(void)putchar('\n');
4637913Sjoerg	}
4641590Srgrimes}
4651590Srgrimes
4661590Srgrimes/*
4671590Srgrimes * Print a register a la the %b format of the kernel's printf.
4681590Srgrimes */
469227174Sedstatic void
470152396Sdwmaloneprintreg(const char *s, u_int v, const char *bits)
4711590Srgrimes{
472152396Sdwmalone	int i, any = 0;
473152396Sdwmalone	char c;
4741590Srgrimes
4751590Srgrimes	if (bits && *bits == 8)
4761590Srgrimes		printf("%s=%o", s, v);
4771590Srgrimes	else
4781590Srgrimes		printf("%s=%x", s, v);
47911608Sbde	if (!bits)
48011608Sbde		return;
4811590Srgrimes	bits++;
4821590Srgrimes	if (v && bits) {
4831590Srgrimes		putchar('<');
48427752Scharnier		while ((i = *bits++)) {
4851590Srgrimes			if (v & (1 << (i-1))) {
4861590Srgrimes				if (any)
4871590Srgrimes					putchar(',');
4881590Srgrimes				any = 1;
4891590Srgrimes				for (; (c = *bits) > 32; bits++)
4901590Srgrimes					putchar(c);
4911590Srgrimes			} else
4921590Srgrimes				for (; *bits > 32; bits++)
4931590Srgrimes					;
4941590Srgrimes		}
4951590Srgrimes		putchar('>');
4961590Srgrimes	}
4971590Srgrimes}
4981590Srgrimes
499227174Sedstatic void
500152396Sdwmaloneusage(void)
5011590Srgrimes{
50294505Scharnier	(void)fprintf(stderr, "usage: mt [-f device] command [count]\n");
5031590Srgrimes	exit(1);
5041590Srgrimes}
5051590Srgrimes
506227174Sedstatic const struct compression_types {
50739260Sgibbs	u_int32_t	comp_number;
50839260Sgibbs	const char 	*name;
50939260Sgibbs} comp_types[] = {
51039260Sgibbs	{ 0x00, "none" },
51139260Sgibbs	{ 0x00, "off" },
51239260Sgibbs	{ 0x10, "IDRC" },
51339260Sgibbs	{ 0x20, "DCLZ" },
51439260Sgibbs	{ 0xffffffff, "enable" },
51539260Sgibbs	{ 0xffffffff, "on" },
51639260Sgibbs	{ 0xf0f0f0f0, NULL}
51739260Sgibbs};
51839260Sgibbs
519227174Sedstatic const char *
5207929Sjoergdenstostring(int d)
5217913Sjoerg{
5227913Sjoerg	static char buf[20];
523279219Sken	const char *name = mt_density_name(d);
5247913Sjoerg
525279219Sken	if (name == NULL)
5267929Sjoerg		sprintf(buf, "0x%02x", d);
52744397Smjacob	else
528279219Sken		sprintf(buf, "0x%02x:%s", d, name);
52944397Smjacob	return buf;
5307913Sjoerg}
5317913Sjoerg
532227174Sedstatic const char *
5337913Sjoerggetblksiz(int bs)
5347913Sjoerg{
5357913Sjoerg	static char buf[25];
5367913Sjoerg	if (bs == 0)
5377913Sjoerg		return "variable";
5387913Sjoerg	else {
53939260Sgibbs		sprintf(buf, "%d bytes", bs);
5407913Sjoerg		return buf;
5417913Sjoerg	}
5427913Sjoerg}
5437913Sjoerg
544227174Sedstatic const char *
54539260Sgibbscomptostring(u_int32_t comp)
54639260Sgibbs{
54739260Sgibbs	static char buf[20];
548227174Sed	const struct compression_types *ct;
5497913Sjoerg
55039260Sgibbs	if (comp == MT_COMP_DISABLED)
55139260Sgibbs		return "disabled";
55239260Sgibbs	else if (comp == MT_COMP_UNSUPP)
55339260Sgibbs		return "unsupported";
55439260Sgibbs
55539260Sgibbs	for (ct = comp_types; ct->name; ct++)
55639260Sgibbs		if (ct->comp_number == comp)
55739260Sgibbs			break;
55839260Sgibbs
55939260Sgibbs	if (ct->comp_number == 0xf0f0f0f0) {
56044618Smjacob		sprintf(buf, "0x%x", comp);
56139260Sgibbs		return(buf);
56239260Sgibbs	} else
56339260Sgibbs		return(ct->name);
56439260Sgibbs}
56539260Sgibbs
566227174Sedstatic u_int32_t
56739260Sgibbsstringtocomp(const char *s)
56839260Sgibbs{
569227174Sed	const struct compression_types *ct;
57039260Sgibbs	size_t l = strlen(s);
57139260Sgibbs
57239260Sgibbs	for (ct = comp_types; ct->name; ct++)
57339260Sgibbs		if (strncasecmp(ct->name, s, l) == 0)
57439260Sgibbs			break;
57539260Sgibbs
57639260Sgibbs	return(ct->comp_number);
57739260Sgibbs}
57839260Sgibbs
579279219Skenstatic struct driver_state {
580279219Sken	int dsreg;
581279219Sken	const char *desc;
582279219Sken} driver_states[] = {
583279219Sken	{ MTIO_DSREG_REST, "at rest" },
584279219Sken	{ MTIO_DSREG_RBSY, "Communicating with drive" },
585279219Sken	{ MTIO_DSREG_WR, "Writing" },
586279219Sken	{ MTIO_DSREG_FMK, "Writing Filemarks" },
587279219Sken	{ MTIO_DSREG_ZER, "Erasing" },
588279219Sken	{ MTIO_DSREG_RD, "Reading" },
589279219Sken	{ MTIO_DSREG_FWD, "Spacing Forward" },
590279219Sken	{ MTIO_DSREG_REV, "Spacing Reverse" },
591279219Sken	{ MTIO_DSREG_POS, "Hardware Positioning (direction unknown)" },
592279219Sken	{ MTIO_DSREG_REW, "Rewinding" },
593279219Sken	{ MTIO_DSREG_TEN, "Retensioning" },
594279219Sken	{ MTIO_DSREG_UNL, "Unloading" },
595279219Sken	{ MTIO_DSREG_LD, "Loading" },
596279219Sken};
597279219Sken
598279219Skenconst char *
599279219Skenget_driver_state_str(int dsreg)
600279219Sken{
601279219Sken	unsigned int i;
602279219Sken
603279219Sken	for (i = 0; i < (sizeof(driver_states)/sizeof(driver_states[0])); i++) {
604279219Sken		if (driver_states[i].dsreg == dsreg)
605279219Sken			return (driver_states[i].desc);
606279219Sken	}
607279219Sken
608279219Sken	return (NULL);
609279219Sken}
610279219Sken
611227174Sedstatic void
6127913Sjoergst_status(struct mtget *bp)
6137913Sjoerg{
61444644Smjacob	printf("Mode      Density              Blocksize      bpi      "
61539260Sgibbs	       "Compression\n"
61644644Smjacob	       "Current:  %-17s    %-12s   %-7d  %s\n"
61739260Sgibbs	       "---------available modes---------\n"
61844644Smjacob	       "0:        %-17s    %-12s   %-7d  %s\n"
61944644Smjacob	       "1:        %-17s    %-12s   %-7d  %s\n"
62044644Smjacob	       "2:        %-17s    %-12s   %-7d  %s\n"
62144644Smjacob	       "3:        %-17s    %-12s   %-7d  %s\n",
62239260Sgibbs	       denstostring(bp->mt_density), getblksiz(bp->mt_blksiz),
623279219Sken	       mt_density_bp(bp->mt_density, TRUE), comptostring(bp->mt_comp),
62439260Sgibbs	       denstostring(bp->mt_density0), getblksiz(bp->mt_blksiz0),
625279219Sken	       mt_density_bp(bp->mt_density0, TRUE), comptostring(bp->mt_comp0),
62639260Sgibbs	       denstostring(bp->mt_density1), getblksiz(bp->mt_blksiz1),
627279219Sken	       mt_density_bp(bp->mt_density1, TRUE), comptostring(bp->mt_comp1),
62839260Sgibbs	       denstostring(bp->mt_density2), getblksiz(bp->mt_blksiz2),
629279219Sken	       mt_density_bp(bp->mt_density2, TRUE), comptostring(bp->mt_comp2),
63039260Sgibbs	       denstostring(bp->mt_density3), getblksiz(bp->mt_blksiz3),
631279219Sken	       mt_density_bp(bp->mt_density3, TRUE), comptostring(bp->mt_comp3));
63243629Smjacob
63343629Smjacob	if (bp->mt_dsreg != MTIO_DSREG_NIL) {
63469248Skris		const char sfmt[] = "Current Driver State: %s.\n";
63543629Smjacob		printf("---------------------------------\n");
636279219Sken		const char *state_str;
637279219Sken
638279219Sken		state_str = get_driver_state_str(bp->mt_dsreg);
639279219Sken		if (state_str == NULL) {
640279219Sken			char foo[32];
641279219Sken			(void) sprintf(foo, "Unknown state 0x%x", bp->mt_dsreg);
642279219Sken			printf(sfmt, foo);
643279219Sken		} else {
644279219Sken			printf(sfmt, state_str);
645279219Sken		}
646279219Sken	}
647279219Sken	if (bp->mt_resid == 0 && bp->mt_fileno == (daddr_t) -1 &&
648279219Sken	    bp->mt_blkno == (daddr_t) -1)
649279219Sken		return;
650279219Sken	printf("---------------------------------\n");
651279219Sken	printf("File Number: %d\tRecord Number: %d\tResidual Count %d\n",
652279219Sken	    bp->mt_fileno, bp->mt_blkno, bp->mt_resid);
653279219Sken}
654279219Sken
655279219Skenstatic int
656279219Skenmt_locate(int argc, char **argv, int mtfd, const char *tape)
657279219Sken{
658279219Sken	struct mtlocate mtl;
659279219Sken	uint64_t logical_id = 0;
660279219Sken	mt_locate_dest_type dest_type = MT_LOCATE_DEST_FILE;
661279219Sken	int eod = 0, explicit = 0, immediate = 0;
662279219Sken	int64_t partition = 0;
663279219Sken	int block_addr_set = 0, partition_set = 0, file_set = 0, set_set = 0;
664279219Sken	int c, retval;
665279219Sken
666279219Sken	retval = 0;
667279219Sken	bzero(&mtl, sizeof(mtl));
668279219Sken
669279219Sken	while ((c = getopt(argc, argv, "b:eEf:ip:s:")) != -1) {
670279219Sken		switch (c) {
671279219Sken		case 'b':
672279219Sken			/* Block address */
673279219Sken			logical_id = strtoull(optarg, NULL, 0);
674279219Sken			dest_type = MT_LOCATE_DEST_OBJECT;
675279219Sken			block_addr_set = 1;
67643629Smjacob			break;
677279219Sken		case 'e':
678279219Sken			/* end of data */
679279219Sken			eod = 1;
680279219Sken			dest_type = MT_LOCATE_DEST_EOD;
68143629Smjacob			break;
682279219Sken		case 'E':
683279219Sken			/*
684279219Sken			 * XXX KDM explicit address mode.  Should we even
685279219Sken			 * allow this, since the driver doesn't operate in
686279219Sken			 * explicit address mode?
687279219Sken			 */
688279219Sken			explicit = 1;
68943629Smjacob			break;
690279219Sken		case 'f':
691279219Sken			/* file number */
692279219Sken			logical_id = strtoull(optarg, NULL, 0);
693279219Sken			dest_type = MT_LOCATE_DEST_FILE;
694279219Sken			file_set = 1;
69543629Smjacob			break;
696279219Sken		case 'i':
697279219Sken			/*
698279219Sken			 * Immediate address mode.  XXX KDM do we want to
699279219Sken			 * implement this?  The other commands in the
700279219Sken			 * tape driver will need to be able to handle this.
701279219Sken			 */
702279219Sken			immediate = 1;
70343629Smjacob			break;
704279219Sken		case 'p':
705279219Sken			/*
706279219Sken			 * Change partition to the given partition.
707279219Sken			 */
708279219Sken			partition = strtol(optarg, NULL, 0);
709279219Sken			partition_set = 1;
71043629Smjacob			break;
711279219Sken		case 's':
712279219Sken			/* Go to the given set mark */
713279219Sken			logical_id = strtoull(optarg, NULL, 0);
714279219Sken			dest_type = MT_LOCATE_DEST_SET;
715279219Sken			set_set = 1;
71643629Smjacob			break;
717279219Sken		default:
71843629Smjacob			break;
719279219Sken		}
720279219Sken	}
721279219Sken
722279219Sken	/*
723279219Sken	 * These options are mutually exclusive.  The user may only specify
724279219Sken	 * one.
725279219Sken	 */
726279219Sken	if ((block_addr_set + file_set + eod + set_set) != 1)
727279219Sken		errx(1, "You must specify only one of -b, -f, -e, or -s");
728279219Sken
729279219Sken	mtl.dest_type = dest_type;
730279219Sken	switch (dest_type) {
731279219Sken	case MT_LOCATE_DEST_OBJECT:
732279219Sken	case MT_LOCATE_DEST_FILE:
733279219Sken	case MT_LOCATE_DEST_SET:
734279219Sken		mtl.logical_id = logical_id;
735279219Sken		break;
736279219Sken	case MT_LOCATE_DEST_EOD:
737279219Sken		break;
738279219Sken	}
739279219Sken
740279219Sken	if (immediate != 0)
741279219Sken		mtl.flags |= MT_LOCATE_FLAG_IMMED;
742279219Sken
743279219Sken	if (partition_set != 0) {
744279219Sken		mtl.flags |= MT_LOCATE_FLAG_CHANGE_PART;
745279219Sken		mtl.partition = partition;
746279219Sken	}
747279219Sken
748279219Sken	if (explicit != 0)
749279219Sken		mtl.block_address_mode = MT_LOCATE_BAM_EXPLICIT;
750279219Sken	else
751279219Sken		mtl.block_address_mode = MT_LOCATE_BAM_IMPLICIT;
752279219Sken
753279219Sken	if (ioctl(mtfd, MTIOCEXTLOCATE, &mtl) == -1)
754279219Sken		err(1, "MTIOCEXTLOCATE ioctl failed on %s", tape);
755279219Sken
756279219Sken	return (retval);
757279219Sken}
758279219Sken
759279219Skentypedef enum {
760279219Sken	MT_PERIPH_NAME			= 0,
761279219Sken	MT_UNIT_NUMBER 			= 1,
762279219Sken	MT_VENDOR			= 2,
763279219Sken	MT_PRODUCT			= 3,
764279219Sken	MT_REVISION			= 4,
765279219Sken	MT_COMPRESSION_SUPPORTED	= 5,
766279219Sken	MT_COMPRESSION_ENABLED		= 6,
767279219Sken	MT_COMPRESSION_ALGORITHM	= 7,
768279219Sken	MT_MEDIA_DENSITY		= 8,
769279219Sken	MT_MEDIA_BLOCKSIZE		= 9,
770279219Sken	MT_CALCULATED_FILENO		= 10,
771279219Sken	MT_CALCULATED_REL_BLKNO		= 11,
772279219Sken	MT_REPORTED_FILENO		= 12,
773279219Sken	MT_REPORTED_BLKNO		= 13,
774279219Sken	MT_PARTITION			= 14,
775279219Sken	MT_BOP				= 15,
776279219Sken	MT_EOP				= 16,
777279219Sken	MT_BPEW				= 17,
778279219Sken	MT_DSREG			= 18,
779279219Sken	MT_RESID			= 19,
780279219Sken	MT_FIXED_MODE			= 20,
781279219Sken	MT_SERIAL_NUM			= 21,
782279219Sken	MT_MAXIO			= 22,
783279219Sken	MT_CPI_MAXIO			= 23,
784279219Sken	MT_MAX_BLK			= 24,
785279219Sken	MT_MIN_BLK			= 25,
786279219Sken	MT_BLK_GRAN			= 26,
787279219Sken	MT_MAX_EFF_IOSIZE		= 27
788279219Sken} status_item_index;
789279219Sken
790279219Skenstatic struct mt_status_items {
791279219Sken	const char *name;
792279219Sken	struct mt_status_entry *entry;
793279219Sken} req_status_items[] = {
794279219Sken	{ "periph_name", NULL },
795279219Sken	{ "unit_number", NULL },
796279219Sken	{ "vendor", NULL },
797279219Sken	{ "product", NULL },
798279219Sken	{ "revision", NULL },
799279219Sken	{ "compression_supported", NULL },
800279219Sken	{ "compression_enabled", NULL },
801279219Sken	{ "compression_algorithm", NULL },
802279219Sken	{ "media_density", NULL },
803279219Sken	{ "media_blocksize", NULL },
804279219Sken	{ "calculated_fileno", NULL },
805279219Sken	{ "calculated_rel_blkno", NULL },
806279219Sken	{ "reported_fileno", NULL },
807279219Sken	{ "reported_blkno", NULL },
808279219Sken	{ "partition", NULL },
809279219Sken	{ "bop", NULL },
810279219Sken	{ "eop", NULL },
811279219Sken	{ "bpew", NULL },
812279219Sken	{ "dsreg", NULL },
813279219Sken	{ "residual", NULL },
814279219Sken	{ "fixed_mode", NULL },
815279219Sken	{ "serial_num", NULL },
816279219Sken	{ "maxio", NULL },
817279219Sken	{ "cpi_maxio", NULL },
818279219Sken	{ "max_blk", NULL },
819279219Sken	{ "min_blk", NULL },
820279219Sken	{ "blk_gran", NULL },
821279219Sken	{ "max_effective_iosize", NULL }
822279219Sken};
823279219Sken
824279219Skenint
825279219Skennstatus_print(int argc, char **argv, char *xml_str,
826279219Sken	      struct mt_status_data *status_data)
827279219Sken{
828279219Sken	unsigned int i;
829279219Sken	int64_t calculated_fileno, calculated_rel_blkno;
830279219Sken	int64_t rep_fileno, rep_blkno, partition, resid;
831279219Sken	char block_str[32];
832279219Sken	const char *dens_str;
833279219Sken	int dsreg, bop, eop, bpew;
834279219Sken	int xml_dump = 0;
835279219Sken	size_t dens_len;
836279219Sken	unsigned int field_width;
837279219Sken	int verbose = 0;
838279219Sken	int c;
839279219Sken
840279219Sken	while ((c = getopt(argc, argv, "xv")) != -1) {
841279219Sken		switch (c) {
842279219Sken		case 'x':
843279219Sken			xml_dump = 1;
84443629Smjacob			break;
845279219Sken		case 'v':
846279219Sken			verbose = 1;
84743629Smjacob			break;
848279219Sken		default:
84943629Smjacob			break;
850279219Sken		}
851279219Sken	}
852279219Sken
853279219Sken	if (xml_dump != 0) {
854279219Sken		printf("%s", xml_str);
855279219Sken		return (0);
856279219Sken	}
857279219Sken
858279219Sken	for (i = 0; i < (sizeof(req_status_items)/sizeof(req_status_items[0]));
859279219Sken	     i++) {
860279219Sken		char *name;
861279219Sken
862279219Sken		name = __DECONST(char *, req_status_items[i].name);
863279219Sken		req_status_items[i].entry = mt_status_entry_find(status_data,
864279219Sken		    name);
865279219Sken		if (req_status_items[i].entry == NULL) {
866279219Sken			errx(1, "Cannot find status entry %s",
867279219Sken			    req_status_items[i].name);
868279219Sken		}
869279219Sken	}
870279219Sken
871279219Sken	printf("Drive: %s%ju: <%s %s %s> Serial Number: %s\n",
872279219Sken	       req_status_items[MT_PERIPH_NAME].entry->value,
873279219Sken	       (uintmax_t)req_status_items[MT_UNIT_NUMBER].entry->value_unsigned,
874279219Sken	       req_status_items[MT_VENDOR].entry->value,
875279219Sken	       req_status_items[MT_PRODUCT].entry->value,
876279219Sken	       req_status_items[MT_REVISION].entry->value,
877279219Sken	       (req_status_items[MT_SERIAL_NUM].entry->value) ?
878279219Sken	       req_status_items[MT_SERIAL_NUM].entry->value : "none");
879279219Sken	printf("---------------------------------\n");
880279219Sken
881279219Sken	/*
882279219Sken	 * We check to see whether we're in fixed mode or not, and don't
883279219Sken	 * just believe the blocksize.  If the SILI bit is turned on, the
884279219Sken	 * blocksize will be set to 4, even though we're doing variable
885279219Sken	 * length (well, multiples of 4) blocks.
886279219Sken	 */
887279219Sken	if (req_status_items[MT_FIXED_MODE].entry->value_signed == 0)
888279219Sken		snprintf(block_str, sizeof(block_str), "variable");
889279219Sken	else
890279219Sken		snprintf(block_str, sizeof(block_str), "%s",
891279219Sken		    getblksiz(req_status_items[
892279219Sken			      MT_MEDIA_BLOCKSIZE].entry->value_unsigned));
893279219Sken
894279219Sken	dens_str = denstostring(req_status_items[
895279219Sken	    MT_MEDIA_DENSITY].entry->value_unsigned);
896279219Sken	if (dens_str == NULL)
897279219Sken		dens_len = 0;
898279219Sken	else
899279219Sken		dens_len = strlen(dens_str);
900279219Sken	field_width = MAX(dens_len, 17);
901279219Sken	printf("Mode      %-*s    Blocksize      bpi      Compression\n"
902279219Sken	       "Current:  %-*s    %-12s   %-7d  ",
903279219Sken	       field_width, "Density", field_width, dens_str, block_str,
904279219Sken	       mt_density_bp(req_status_items[
905279219Sken	       MT_MEDIA_DENSITY].entry->value_unsigned, TRUE));
906279219Sken
907279219Sken	if (req_status_items[MT_COMPRESSION_SUPPORTED].entry->value_signed == 0)
908279219Sken		printf("unsupported\n");
909279219Sken	else if (req_status_items[
910279219Sken		 MT_COMPRESSION_ENABLED].entry->value_signed == 0)
911279219Sken		printf("disabled\n");
912279219Sken	else {
913279219Sken		printf("enabled (%s)\n",
914279219Sken		       comptostring(req_status_items[
915279219Sken		       MT_COMPRESSION_ALGORITHM].entry->value_unsigned));
916279219Sken	}
917279219Sken
918279219Sken	dsreg = req_status_items[MT_DSREG].entry->value_signed;
919279219Sken	if (dsreg != MTIO_DSREG_NIL) {
920279219Sken		const char sfmt[] = "Current Driver State: %s.\n";
921279219Sken		printf("---------------------------------\n");
922279219Sken		const char *state_str;
923279219Sken
924279219Sken		state_str = get_driver_state_str(dsreg);
925279219Sken		if (state_str == NULL) {
926279219Sken			char foo[32];
927279219Sken			(void) sprintf(foo, "Unknown state 0x%x", dsreg);
928279219Sken			printf(sfmt, foo);
929279219Sken		} else {
930279219Sken			printf(sfmt, state_str);
931279219Sken		}
932279219Sken	}
933279219Sken	resid = req_status_items[MT_RESID].entry->value_signed;
934279219Sken	calculated_fileno = req_status_items[
935279219Sken	    MT_CALCULATED_FILENO].entry->value_signed;
936279219Sken	calculated_rel_blkno = req_status_items[
937279219Sken	    MT_CALCULATED_REL_BLKNO].entry->value_signed;
938279219Sken	rep_fileno = req_status_items[
939279219Sken	    MT_REPORTED_FILENO].entry->value_signed;
940279219Sken	rep_blkno = req_status_items[
941279219Sken	    MT_REPORTED_BLKNO].entry->value_signed;
942279219Sken	bop = req_status_items[MT_BOP].entry->value_signed;
943279219Sken	eop = req_status_items[MT_EOP].entry->value_signed;
944279219Sken	bpew = req_status_items[MT_BPEW].entry->value_signed;
945279219Sken	partition = req_status_items[MT_PARTITION].entry->value_signed;
946279219Sken
947279219Sken	printf("---------------------------------\n");
948279219Sken	printf("Partition: %3jd      Calc File Number: %3jd "
949279219Sken	       "    Calc Record Number: %jd\n"
950279219Sken	       "Residual:  %3jd  Reported File Number: %3jd "
951279219Sken	       "Reported Record Number: %jd\n", partition, calculated_fileno,
952279219Sken	       calculated_rel_blkno, resid, rep_fileno, rep_blkno);
953279219Sken
954279219Sken	printf("Flags: ");
955279219Sken	if (bop > 0 || eop > 0 || bpew > 0) {
956279219Sken		int need_comma = 0;
957279219Sken
958279219Sken		if (bop > 0) {
959279219Sken			printf("BOP");
960279219Sken			need_comma = 1;
961279219Sken		}
962279219Sken		if (eop > 0) {
963279219Sken			if (need_comma != 0)
964279219Sken				printf(",");
965279219Sken			printf("EOP");
966279219Sken			need_comma = 1;
967279219Sken		}
968279219Sken		if (bpew > 0) {
969279219Sken			if (need_comma != 0)
970279219Sken				printf(",");
971279219Sken			printf("BPEW");
972279219Sken			need_comma = 1;
973279219Sken		}
974279219Sken	} else {
975279219Sken		printf("None");
976279219Sken	}
977279219Sken	printf("\n");
978279219Sken	if (verbose != 0) {
979279219Sken		printf("---------------------------------\n");
980279219Sken		printf("Tape I/O parameters:\n");
981279219Sken		for (i = MT_MAXIO; i <= MT_MAX_EFF_IOSIZE; i++) {
982279219Sken			printf("  %s (%s): %ju bytes\n",
983279219Sken			    req_status_items[i].entry->desc,
984279219Sken			    req_status_items[i].name,
985279219Sken			    req_status_items[i].entry->value_unsigned);
986279219Sken		}
987279219Sken	}
988279219Sken
989279219Sken	return (0);
990279219Sken}
991279219Sken
992279219Skenint
993279219Skenmt_xml_cmd(unsigned long cmd, int argc, char **argv, int mtfd, const char *tape)
994279219Sken{
995279219Sken	struct mt_status_data status_data;
996279219Sken#if 0
997279219Sken	struct mt_status_entry *entry;
998279219Sken#endif
999279219Sken	char *xml_str;
1000279219Sken	int retval;
1001279219Sken	unsigned long ioctl_cmd;
1002279219Sken
1003279219Sken	switch (cmd) {
1004279219Sken	case MT_CMD_PROTECT:
1005279219Sken	case MTIOCPARAMGET:
1006279219Sken		ioctl_cmd = MTIOCPARAMGET;
1007279219Sken		break;
1008279219Sken	default:
1009279219Sken		ioctl_cmd = MTIOCEXTGET;
1010279219Sken		break;
1011279219Sken	}
1012279219Sken
1013279219Sken	retval = mt_get_xml_str(mtfd, ioctl_cmd, &xml_str);
1014279219Sken	if (retval != 0)
1015279219Sken		err(1, "Couldn't get mt XML string");
1016279219Sken
1017279219Sken	retval = mt_get_status(xml_str, &status_data);
1018279219Sken	if (retval != XML_STATUS_OK) {
1019279219Sken		warn("Couldn't get mt status for %s", tape);
1020279219Sken		goto bailout;
1021279219Sken	}
1022279219Sken
1023279219Sken	/*
1024279219Sken	 * This gets set if there are memory allocation or other errors in
1025279219Sken	 * our parsing of the XML.
1026279219Sken	 */
1027279219Sken	if (status_data.error != 0) {
1028279219Sken		warnx("%s", status_data.error_str);
1029279219Sken		retval = 1;
1030279219Sken		goto bailout;
1031279219Sken	}
1032279219Sken#if 0
1033279219Sken	STAILQ_FOREACH(entry, &status_data.entries, links)
1034279219Sken		mt_status_tree_print(entry, 0, NULL);
1035279219Sken#endif
1036279219Sken
1037279219Sken	switch (cmd) {
1038279219Sken	case MTIOCEXTGET:
1039279219Sken		retval = nstatus_print(argc, argv, xml_str, &status_data);
1040279219Sken		break;
1041279219Sken	case MTIOCPARAMGET:
1042279219Sken		retval = mt_param(argc, argv, mtfd, xml_str, &status_data);
1043279219Sken		break;
1044279219Sken	case MT_CMD_PROTECT:
1045279219Sken		retval = mt_protect(argc, argv, mtfd, &status_data);
1046279219Sken		break;
1047279219Sken	case MT_CMD_GETDENSITY:
1048279219Sken		retval = mt_getdensity(argc, argv, xml_str, &status_data);
1049279219Sken		break;
1050279219Sken	}
1051279219Sken
1052279219Skenbailout:
1053279219Sken	if (xml_str != NULL)
1054279219Sken		free(xml_str);
1055279219Sken
1056279219Sken	mt_status_free(&status_data);
1057279219Sken
1058279219Sken	return (retval);
1059279219Sken}
1060279219Sken
1061279219Skenstatic int
1062279219Skenmt_set_param(int mtfd, struct mt_status_data *status_data, char *param_name,
1063279219Sken    char *param_value)
1064279219Sken{
1065279219Sken	struct mt_status_entry *entry;
1066279219Sken	struct mtparamset param_set;
1067279219Sken
1068279219Sken	entry = mt_status_entry_find(status_data,
1069279219Sken	    __DECONST(char *, "mtparamget"));
1070279219Sken	if (entry == NULL)
1071279219Sken		errx(1, "Cannot find parameter root node");
1072279219Sken
1073279219Sken	bzero(&param_set, sizeof(param_set));
1074279219Sken	entry = mt_entry_find(entry, param_name);
1075279219Sken	if (entry == NULL)
1076279219Sken		errx(1, "Unknown parameter name \"%s\"", param_name);
1077279219Sken
1078279219Sken	strlcpy(param_set.value_name, param_name, sizeof(param_set.value_name));
1079279219Sken
1080279219Sken	switch (entry->var_type) {
1081279219Sken	case MT_TYPE_INT:
1082279219Sken		param_set.value.value_signed = strtoll(param_value, NULL, 0);
1083279219Sken		param_set.value_type = MT_PARAM_SET_SIGNED;
1084279219Sken		param_set.value_len = entry->size;
1085279219Sken		break;
1086279219Sken	case MT_TYPE_UINT:
1087279219Sken		param_set.value.value_unsigned = strtoull(param_value, NULL, 0);
1088279219Sken		param_set.value_type = MT_PARAM_SET_UNSIGNED;
1089279219Sken		param_set.value_len = entry->size;
1090279219Sken		break;
1091279219Sken	case MT_TYPE_STRING: {
1092279219Sken		size_t param_len;
1093279219Sken
1094279219Sken		param_len = strlen(param_value) + 1;
1095279219Sken		if (param_len > sizeof(param_set.value.value_fixed_str)) {
1096279219Sken			param_set.value_type = MT_PARAM_SET_VAR_STR;
1097279219Sken			param_set.value.value_var_str = param_value;
1098279219Sken		} else {
1099279219Sken			param_set.value_type = MT_PARAM_SET_FIXED_STR;
1100279219Sken			strlcpy(param_set.value.value_fixed_str, param_value,
1101279219Sken			    sizeof(param_set.value.value_fixed_str));
1102279219Sken		}
1103279219Sken		param_set.value_len = param_len;
1104279219Sken		break;
1105279219Sken	}
1106279219Sken	default:
1107279219Sken		errx(1, "Unknown parameter type %d for %s", entry->var_type,
1108279219Sken		    param_name);
1109279219Sken		break;
1110279219Sken	}
1111279219Sken
1112279219Sken	if (ioctl(mtfd, MTIOCPARAMSET, &param_set) == -1)
1113279219Sken		err(1, "MTIOCPARAMSET");
1114279219Sken
1115279219Sken	if (param_set.status != MT_PARAM_STATUS_OK)
1116279219Sken		errx(1, "Failed to set %s: %s", param_name,
1117279219Sken		    param_set.error_str);
1118279219Sken
1119279219Sken	return (0);
1120279219Sken}
1121279219Sken
1122279219Sken
1123279219Skentypedef enum {
1124279219Sken	MT_PP_LBP_R,
1125279219Sken	MT_PP_LBP_W,
1126279219Sken	MT_PP_RBDP,
1127279219Sken	MT_PP_PI_LENGTH,
1128279219Sken	MT_PP_PROT_METHOD
1129279219Sken} mt_protect_param;
1130279219Sken
1131279219Skenstatic struct mt_protect_info {
1132279219Sken	const char *name;
1133279219Sken	struct mt_status_entry *entry;
1134279219Sken	uint32_t value;
1135279219Sken} mt_protect_list[] = {
1136279219Sken	{ "lbp_r", NULL, 0 },
1137279219Sken	{ "lbp_w", NULL, 0 },
1138279219Sken	{ "rbdp", NULL, 0 },
1139279219Sken	{ "pi_length", NULL, 0 },
1140279219Sken	{ "prot_method", NULL, 0 }
1141279219Sken};
1142279219Sken
1143279219Sken#define	MT_NUM_PROTECT_PARAMS	(sizeof(mt_protect_list)/sizeof(mt_protect_list[0]))
1144279219Sken
1145279219Sken#define	MT_PROT_NAME	"protection"
1146279219Sken
1147279219Skenstatic int
1148279219Skenmt_protect(int argc, char **argv, int mtfd, struct mt_status_data *status_data)
1149279219Sken{
1150279219Sken	int retval = 0;
1151279219Sken	int do_enable = 0, do_disable = 0, do_list = 0;
1152279219Sken	int rbdp_set = 0, lbp_w_set = 0, lbp_r_set = 0;
1153279219Sken	int prot_method_set = 0, pi_length_set = 0;
1154279219Sken	int verbose = 0;
1155279219Sken	uint32_t rbdp = 0, lbp_w = 0, lbp_r = 0;
1156279219Sken	uint32_t prot_method = 0, pi_length = 0;
1157279219Sken	struct mt_status_entry *prot_entry, *supported_entry;
1158279219Sken	struct mt_status_entry *entry;
1159279219Sken	struct mtparamset params[MT_NUM_PROTECT_PARAMS];
1160279219Sken	struct mtsetlist param_list;
1161279219Sken	unsigned int i;
1162279219Sken	int c;
1163279219Sken
1164279219Sken	while ((c = getopt(argc, argv, "b:delL:m:r:vw:")) != -1) {
1165279219Sken		switch (c) {
1166279219Sken		case 'b':
1167279219Sken			rbdp_set = 1;
1168279219Sken			rbdp = strtoul(optarg, NULL, 0);
1169279219Sken			if ((rbdp != 0) && (rbdp != 1))
1170279219Sken				errx(1, "valid values for -b are 0 and 1");
117143629Smjacob			break;
1172279219Sken		case 'd':
1173279219Sken			do_disable = 1;
117443629Smjacob			break;
1175279219Sken		case 'e':
1176279219Sken			do_enable = 1;
1177279219Sken			break;
1178279219Sken		case 'l':
1179279219Sken			do_list = 1;
1180279219Sken			break;
1181279219Sken		case 'L':
1182279219Sken			pi_length_set = 1;
1183279219Sken			pi_length = strtoul(optarg, NULL, 0);
1184279219Sken			if (pi_length > SA_CTRL_DP_PI_LENGTH_MASK)
1185279219Sken				errx(1, "PI length %u > maximum %u",
1186279219Sken				    pi_length, SA_CTRL_DP_PI_LENGTH_MASK);
1187279219Sken			break;
1188279219Sken		case 'm':
1189279219Sken			prot_method_set = 1;
1190279219Sken			prot_method = strtoul(optarg, NULL, 0);
1191279219Sken			if (prot_method > SA_CTRL_DP_METHOD_MAX)
1192279219Sken				errx(1, "Method %u > maximum %u",
1193279219Sken				    prot_method, SA_CTRL_DP_METHOD_MAX);
1194279219Sken			break;
1195279219Sken		case 'r':
1196279219Sken			lbp_r_set = 1;
1197279219Sken			lbp_r = strtoul(optarg, NULL, 0);
1198279219Sken			if ((lbp_r != 0) && (lbp_r != 1))
1199279219Sken				errx(1, "valid values for -r are 0 and 1");
1200279219Sken			break;
1201279219Sken		case 'v':
1202279219Sken			verbose = 1;
1203279219Sken			break;
1204279219Sken		case 'w':
1205279219Sken			lbp_w_set = 1;
1206279219Sken			lbp_w = strtoul(optarg, NULL, 0);
1207279219Sken			if ((lbp_w != 0) && (lbp_r != 1))
1208279219Sken				errx(1, "valid values for -r are 0 and 1");
1209279219Sken			break;
121043629Smjacob		default:
121143629Smjacob			break;
121243629Smjacob		}
121343629Smjacob	}
1214279219Sken
1215279219Sken	if ((rbdp_set + do_disable + do_enable + do_list + pi_length_set +
1216279219Sken	    prot_method_set + lbp_r_set + lbp_w_set) == 0)
1217279219Sken		errx(1, "Need an argument for protect");
1218279219Sken
1219279219Sken	if ((do_disable + do_enable + do_list) != 1)
1220279219Sken		errx(1, "You must specify only one of -e, -d or -l");
1221279219Sken
1222279219Sken	if (do_list != 0) {
1223279219Sken		retval = mt_protect_print(status_data, verbose);
1224279219Sken		goto bailout;
1225279219Sken	}
1226279219Sken	if (do_enable != 0) {
1227279219Sken		/*
1228279219Sken		 * Enable protection, but allow the user to override
1229279219Sken		 * settings if he doesn't want everything turned on.
1230279219Sken		 */
1231279219Sken		if (rbdp_set == 0)
1232279219Sken			rbdp = 1;
1233279219Sken		if (lbp_w_set == 0)
1234279219Sken			lbp_w = 1;
1235279219Sken		if (lbp_r_set == 0)
1236279219Sken			lbp_r = 1;
1237279219Sken		/*
1238279219Sken		 * If the user doesn't override it, we default to enabling
1239279219Sken		 * Reed-Solomon checkums.
1240279219Sken		 */
1241279219Sken		if (prot_method_set == 0)
1242279219Sken			prot_method = SA_CTRL_DP_REED_SOLOMON;
1243279219Sken		if (pi_length_set == 0)
1244279219Sken			pi_length = SA_CTRL_DP_RS_LENGTH;
1245279219Sken	} else if (do_disable != 0) {
1246279219Sken		/*
1247279219Sken		 * If the user wants to disable protection, we ignore any
1248279219Sken		 * other parameters he has set.  Everything gets set to 0.
1249279219Sken		 */
1250279219Sken		rbdp = lbp_w = lbp_r = 0;
1251279219Sken		prot_method = pi_length = 0;
1252279219Sken	}
1253279219Sken
1254279219Sken	prot_entry = mt_status_entry_find(status_data,
1255279219Sken	    __DECONST(char *, MT_PROT_NAME));
1256279219Sken	if (prot_entry == NULL)
1257279219Sken		errx(1, "Unable to find protection information status");
1258279219Sken
1259279219Sken	supported_entry = mt_entry_find(prot_entry,
1260279219Sken	    __DECONST(char *, "protection_supported"));
1261279219Sken	if (supported_entry == NULL)
1262279219Sken		errx(1, "Unable to find protection support information");
1263279219Sken
1264279219Sken	if (((supported_entry->var_type == MT_TYPE_INT)
1265279219Sken	  && (supported_entry->value_signed == 0))
1266279219Sken	 || ((supported_entry->var_type == MT_TYPE_UINT)
1267279219Sken	  && (supported_entry->value_unsigned == 0)))
1268279219Sken		errx(1, "This device does not support protection information");
1269279219Sken
1270279219Sken	mt_protect_list[MT_PP_LBP_R].value = lbp_r;
1271279219Sken	mt_protect_list[MT_PP_LBP_W].value = lbp_w;
1272279219Sken	mt_protect_list[MT_PP_RBDP].value = rbdp;
1273279219Sken	mt_protect_list[MT_PP_PI_LENGTH].value = pi_length;
1274279219Sken	mt_protect_list[MT_PP_PROT_METHOD].value = prot_method;
1275279219Sken
1276279219Sken	bzero(&params, sizeof(params));
1277279219Sken	bzero(&param_list, sizeof(param_list));
1278279219Sken
1279279219Sken	/*
1280279219Sken	 * Go through the list and make sure that we have this parameter,
1281279219Sken	 * and that it is still an unsigned integer.  If not, we've got a
1282279219Sken	 * problem.
1283279219Sken	 */
1284279219Sken	for (i = 0; i < MT_NUM_PROTECT_PARAMS; i++) {
1285279219Sken		entry = mt_entry_find(prot_entry,
1286279219Sken		    __DECONST(char *, mt_protect_list[i].name));
1287279219Sken		if (entry == NULL) {
1288279219Sken			errx(1, "Unable to find parameter %s",
1289279219Sken			    mt_protect_list[i].name);
1290279219Sken		}
1291279219Sken		mt_protect_list[i].entry = entry;
1292279219Sken
1293279219Sken		if (entry->var_type != MT_TYPE_UINT)
1294279219Sken			errx(1, "Parameter %s is type %d, not unsigned, "
1295279219Sken			    "cannot proceed", mt_protect_list[i].name,
1296279219Sken			    entry->var_type);
1297279219Sken		snprintf(params[i].value_name, sizeof(params[i].value_name),
1298279219Sken		    "%s.%s", MT_PROT_NAME, mt_protect_list[i].name);
1299279219Sken		/* XXX KDM unify types here */
1300279219Sken		params[i].value_type = MT_PARAM_SET_UNSIGNED;
1301279219Sken		params[i].value_len = sizeof(mt_protect_list[i].value);
1302279219Sken		params[i].value.value_unsigned = mt_protect_list[i].value;
1303279219Sken
1304279219Sken	}
1305279219Sken	param_list.num_params = MT_NUM_PROTECT_PARAMS;
1306279219Sken	param_list.param_len = sizeof(params);
1307279219Sken	param_list.params = params;
1308279219Sken
1309279219Sken	if (ioctl(mtfd, MTIOCSETLIST, &param_list) == -1)
1310279219Sken		err(1, "error issuing MTIOCSETLIST ioctl");
1311279219Sken
1312279219Sken	for (i = 0; i < MT_NUM_PROTECT_PARAMS; i++) {
1313279219Sken		if (params[i].status != MT_PARAM_STATUS_OK) {
1314279219Sken			warnx("%s", params[i].error_str);
1315279219Sken			retval = 1;
1316279219Sken		}
1317279219Sken	}
1318279219Skenbailout:
1319279219Sken
1320279219Sken	return (retval);
13217913Sjoerg}
13227913Sjoerg
1323279219Skenstatic int
1324279219Skenmt_param(int argc, char **argv, int mtfd, char *xml_str,
1325279219Sken	 struct mt_status_data *status_data)
1326279219Sken{
1327279219Sken	int list = 0, do_set = 0, xml_dump = 0;
1328279219Sken	char *param_name = NULL, *param_value = NULL;
1329279219Sken	int retval = 0, quiet = 0;
1330279219Sken	int c;
1331279219Sken
1332279219Sken	while ((c = getopt(argc, argv, "lp:qs:x")) != -1) {
1333279219Sken		switch (c) {
1334279219Sken		case 'l':
1335279219Sken			list = 1;
1336279219Sken			break;
1337279219Sken		case 'p':
1338279261Sken			if (param_name != NULL) {
1339279261Sken				warnx("Only one paramter name may be "
1340279261Sken				    "specified");
1341279261Sken				retval = 1;
1342279261Sken				goto bailout;
1343279261Sken			}
1344279219Sken			param_name = strdup(optarg);
1345279219Sken			break;
1346279219Sken		case 'q':
1347279219Sken			quiet = 1;
1348279219Sken			break;
1349279219Sken		case 's':
1350279261Sken			if (param_value != NULL) {
1351279261Sken				warnx("Only one paramter value may be "
1352279261Sken				    "specified");
1353279261Sken				retval = 1;
1354279261Sken				goto bailout;
1355279261Sken			}
1356279219Sken			param_value = strdup(optarg);
1357279219Sken			do_set = 1;
1358279219Sken			break;
1359279219Sken		case 'x':
1360279219Sken			xml_dump = 1;
1361279219Sken			break;
1362279219Sken		default:
1363279219Sken			break;
1364279219Sken		}
1365279219Sken	}
1366279219Sken
1367279261Sken	if ((list + do_set + xml_dump) != 1) {
1368279261Sken		warnx("You must specify only one of -s, -l or -x");
1369279261Sken		retval = 1;
1370279261Sken		goto bailout;
1371279261Sken	}
1372279219Sken
1373279219Sken	if (xml_dump != 0) {
1374279219Sken		printf("%s", xml_str);
1375279261Sken		retval = 0;
1376279261Sken		goto bailout;
1377279219Sken	}
1378279219Sken
1379279219Sken	if (do_set != 0) {
1380279219Sken		if (param_name == NULL)
1381279219Sken			errx(1, "You must specify -p with -s");
1382279219Sken
1383279219Sken		retval = mt_set_param(mtfd, status_data, param_name,
1384279219Sken		    param_value);
1385279219Sken	} else if (list != 0)
1386279219Sken		retval = mt_param_list(status_data, param_name, quiet);
1387279219Sken
1388279261Skenbailout:
1389279261Sken	free(param_name);
1390279261Sken	free(param_value);
1391279219Sken	return (retval);
1392279219Sken}
1393279219Sken
1394279219Skenint
1395279219Skenmt_print_density_entry(struct mt_status_entry *density_root, int indent)
1396279219Sken{
1397279219Sken	struct mt_status_entry *entry;
1398279219Sken	int retval = 0;
1399279219Sken
1400279219Sken	STAILQ_FOREACH(entry, &density_root->child_entries, links) {
1401279219Sken		if (entry->var_type == MT_TYPE_NODE) {
1402279219Sken			retval = mt_print_density_entry(entry, indent + 2);
1403279219Sken			if (retval != 0)
1404279219Sken				break;
1405279219Sken			else
1406279219Sken				continue;
1407279219Sken		}
1408279219Sken		if ((strcmp(entry->entry_name, "primary_density_code") == 0)
1409280230Sken		 || (strcmp(entry->entry_name, "secondary_density_code") == 0)
1410280230Sken		 || (strcmp(entry->entry_name, "density_code") == 0)) {
1411279219Sken
1412279219Sken			printf("%*s%s (%s): %s\n", indent, "", entry->desc ?
1413279219Sken			    entry->desc : "", entry->entry_name,
1414279219Sken			    denstostring(entry->value_unsigned));
1415279219Sken		} else if (strcmp(entry->entry_name, "density_flags") == 0) {
1416279219Sken			printf("%*sMedium Access: ", indent, "");
1417279219Sken			if (entry->value_unsigned & MT_DENS_WRITE_OK) {
1418279219Sken				printf("Read and Write\n");
1419279219Sken			} else {
1420279219Sken				printf("Read Only\n");
1421279219Sken			}
1422279219Sken			printf("%*sDefault Density: %s\n", indent, "",
1423279219Sken			    (entry->value_unsigned & MT_DENS_DEFLT) ? "Yes" :
1424279219Sken			    "No");
1425279219Sken			printf("%*sDuplicate Density: %s\n", indent, "",
1426279219Sken			    (entry->value_unsigned & MT_DENS_DUP) ? "Yes" :
1427279219Sken			    "No");
1428279219Sken		} else if (strcmp(entry->entry_name, "media_width") == 0) {
1429279219Sken			printf("%*s%s (%s): %.1f mm\n", indent, "",
1430279219Sken			    entry->desc ?  entry->desc : "", entry->entry_name,
1431279219Sken			    (double)((double)entry->value_unsigned / 10));
1432279219Sken		} else if (strcmp(entry->entry_name, "medium_length") == 0) {
1433279219Sken			printf("%*s%s (%s): %ju m\n", indent, "",
1434279219Sken			    entry->desc ?  entry->desc : "", entry->entry_name,
1435279219Sken			    (uintmax_t)entry->value_unsigned);
1436279219Sken		} else if (strcmp(entry->entry_name, "capacity") == 0) {
1437279219Sken			printf("%*s%s (%s): %ju MB\n", indent, "", entry->desc ?
1438279219Sken			    entry->desc : "", entry->entry_name,
1439279219Sken			    (uintmax_t)entry->value_unsigned);
1440279219Sken		} else {
1441279219Sken			printf("%*s%s (%s): %s\n", indent, "", entry->desc ?
1442279219Sken			    entry->desc : "", entry->entry_name, entry->value);
1443279219Sken		}
1444279219Sken	}
1445279219Sken
1446279219Sken	return (retval);
1447279219Sken}
1448279219Sken
1449279219Skenint
1450279219Skenmt_print_density_report(struct mt_status_entry *report_root, int indent)
1451279219Sken{
1452279219Sken	struct mt_status_entry *mt_report, *media_report;
1453279219Sken	struct mt_status_entry *entry;
1454279219Sken	int retval = 0;
1455279219Sken
1456279219Sken	mt_report = mt_entry_find(report_root,
1457279219Sken	    __DECONST(char *, MT_MEDIUM_TYPE_REPORT_NAME));
1458279219Sken	if (mt_report == NULL)
1459279219Sken		return (1);
1460279219Sken
1461279219Sken	media_report = mt_entry_find(report_root,
1462279219Sken	    __DECONST(char *, MT_MEDIA_REPORT_NAME));
1463279219Sken	if (media_report == NULL)
1464279219Sken		return (1);
1465279219Sken
1466279219Sken	if ((mt_report->value_signed == 0)
1467279219Sken	 && (media_report->value_signed == 0)) {
1468279219Sken		printf("%*sThis tape drive supports the following "
1469279219Sken		    "media densities:\n", indent, "");
1470279219Sken	} else if ((mt_report->value_signed == 0)
1471279219Sken		&& (media_report->value_signed != 0)) {
1472279219Sken		printf("%*sThe tape currently in this drive supports "
1473279219Sken		    "the following media densities:\n", indent, "");
1474279219Sken	} else if ((mt_report->value_signed != 0)
1475279219Sken		&& (media_report->value_signed == 0)) {
1476279219Sken		printf("%*sThis tape drive supports the following "
1477279219Sken		    "media types:\n", indent, "");
1478279219Sken	} else {
1479279219Sken		printf("%*sThis tape currently in this drive supports "
1480279219Sken		    "the following media types:\n", indent, "");
1481279219Sken	}
1482279219Sken
1483279219Sken	STAILQ_FOREACH(entry, &report_root->child_entries, links) {
1484279219Sken		struct mt_status_nv *nv;
1485279219Sken
1486279219Sken		if (strcmp(entry->entry_name, MT_DENSITY_ENTRY_NAME) != 0)
1487279219Sken			continue;
1488279219Sken
1489279219Sken		STAILQ_FOREACH(nv, &entry->nv_list, links) {
1490279219Sken			if (strcmp(nv->name, "num") != 0)
1491279219Sken				continue;
1492279219Sken
1493279219Sken			break;
1494279219Sken		}
1495279219Sken
1496279219Sken		indent += 2;
1497279219Sken
1498279219Sken		printf("%*sDensity Entry", indent, "");
1499279219Sken		if (nv != NULL)
1500279219Sken			printf(" %s", nv->value);
1501279219Sken		printf(":\n");
1502279219Sken
1503279219Sken		retval = mt_print_density_entry(entry, indent + 2);
1504279219Sken
1505279219Sken		indent -= 2;
1506279219Sken	}
1507279219Sken
1508279219Sken	return (retval);
1509279219Sken}
1510279219Sken
1511279219Skenint
1512279219Skenmt_print_density(struct mt_status_entry *density_root, int indent)
1513279219Sken{
1514279219Sken	struct mt_status_entry *entry;
1515279219Sken	int retval = 0;
1516279219Sken
1517279219Sken	/*
1518279219Sken	 * We should have this entry for every tape drive.  This particular
1519279219Sken	 * value is reported via the mode page block header, not the
1520279219Sken	 * SCSI REPORT DENSITY SUPPORT command.
1521279219Sken	 */
1522279219Sken	entry = mt_entry_find(density_root,
1523279219Sken	    __DECONST(char *, MT_MEDIA_DENSITY_NAME));
1524279219Sken	if (entry == NULL)
1525279219Sken		errx(1, "Unable to find node %s", MT_MEDIA_DENSITY_NAME);
1526279219Sken
1527279219Sken	printf("%*sCurrent density: %s\n", indent, "",
1528279219Sken	    denstostring(entry->value_unsigned));
1529279219Sken
1530279219Sken	/*
1531279219Sken	 * It isn't an error if we don't have any density reports.  Tape
1532279219Sken	 * drives that don't support the REPORT DENSITY SUPPORT command
1533279219Sken	 * won't have any; they will only have the current density entry
1534279219Sken	 * above.
1535279219Sken	 */
1536279219Sken	STAILQ_FOREACH(entry, &density_root->child_entries, links) {
1537279219Sken		if (strcmp(entry->entry_name, MT_DENSITY_REPORT_NAME) != 0)
1538279219Sken			continue;
1539279219Sken
1540279219Sken		retval = mt_print_density_report(entry, indent);
1541279219Sken	}
1542279219Sken
1543279219Sken	return (retval);
1544279219Sken}
1545279219Sken
1546279219Skenint
1547279219Skenmt_getdensity(int argc, char **argv, char *xml_str,
1548279219Sken    struct mt_status_data *status_data)
1549279219Sken{
1550279219Sken	int retval = 0;
1551279219Sken	int verbose = 0, xml_dump = 0;
1552279219Sken	struct mt_status_entry *density_root = NULL;
1553279219Sken	int c;
1554279219Sken
1555279219Sken	while ((c = getopt(argc, argv, "vx")) != -1) {
1556279219Sken		switch (c) {
1557279219Sken		case 'v':
1558279219Sken			verbose = 1;
1559279219Sken			break;
1560279219Sken		case 'x':
1561279219Sken			xml_dump = 1;
1562279219Sken			break;
1563279219Sken		}
1564279219Sken	}
1565279219Sken
1566279219Sken	if (xml_dump != 0) {
1567279219Sken		printf("%s", xml_str);
1568279219Sken		return (0);
1569279219Sken	}
1570279219Sken
1571279219Sken	density_root = mt_status_entry_find(status_data,
1572279219Sken	    __DECONST(char *, MT_DENSITY_ROOT_NAME));
1573279219Sken	if (density_root == NULL)
1574279219Sken		errx(1, "Cannot find density root node %s",
1575279219Sken		    MT_DENSITY_ROOT_NAME);
1576279219Sken
1577279219Sken	retval = mt_print_density(density_root, 0);
1578279219Sken
1579279219Sken	return (retval);
1580279219Sken}
1581279219Sken
1582227174Sedstatic void
15839541Sjoergwarn_eof(void)
15849541Sjoerg{
15859541Sjoerg	fprintf(stderr,
15869541Sjoerg		"The \"eof\" command has been disabled.\n"
15879541Sjoerg		"Use \"weof\" if you really want to write end-of-file marks,\n"
15889541Sjoerg		"or \"eom\" if you rather want to skip to the end of "
15899541Sjoerg		"recorded medium.\n");
15909541Sjoerg	exit(1);
15919541Sjoerg}
1592