[Midnightbsd-cvs] src [11470] trunk/usr.bin/mt/mt.c: sync mt

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Sat Jul 7 12:56:26 EDT 2018


Revision: 11470
          http://svnweb.midnightbsd.org/src/?rev=11470
Author:   laffer1
Date:     2018-07-07 12:56:25 -0400 (Sat, 07 Jul 2018)
Log Message:
-----------
sync mt

Modified Paths:
--------------
    trunk/usr.bin/mt/Makefile
    trunk/usr.bin/mt/mt.1
    trunk/usr.bin/mt/mt.c

Property Changed:
----------------
    trunk/usr.bin/mt/mt.1

Modified: trunk/usr.bin/mt/Makefile
===================================================================
--- trunk/usr.bin/mt/Makefile	2018-07-07 16:56:02 UTC (rev 11469)
+++ trunk/usr.bin/mt/Makefile	2018-07-07 16:56:25 UTC (rev 11470)
@@ -1,6 +1,9 @@
+# $MidnightBSD$
 #	@(#)Makefile	8.1 (Berkeley) 6/6/93
-# $MidnightBSD$
+# $FreeBSD: stable/10/usr.bin/mt/Makefile 280438 2015-03-24 14:36:10Z ken $
 
 PROG=	mt
+DPADD=	${LIBMT} ${LIBSBUF} ${LIBBSDXML}
+LDADD=	-lmt -lsbuf -lbsdxml
 
 .include <bsd.prog.mk>

Modified: trunk/usr.bin/mt/mt.1
===================================================================
--- trunk/usr.bin/mt/mt.1	2018-07-07 16:56:02 UTC (rev 11469)
+++ trunk/usr.bin/mt/mt.1	2018-07-07 16:56:25 UTC (rev 11470)
@@ -1,3 +1,4 @@
+.\" $MidnightBSD$
 .\" Copyright (c) 1981, 1990, 1993
 .\"	The Regents of the University of California.  All rights reserved.
 .\"
@@ -27,9 +28,9 @@
 .\" SUCH DAMAGE.
 .\"
 .\"	@(#)mt.1	8.1 (Berkeley) 6/6/93
-.\" $MidnightBSD$
+.\" $FreeBSD: stable/10/usr.bin/mt/mt.1 325496 2017-11-06 20:08:02Z ken $
 .\"
-.Dd January 20, 2008
+.Dd November 3, 2017
 .Dt MT 1
 .Os
 .Sh NAME
@@ -71,6 +72,12 @@
 Write
 .Ar count
 end-of-file (EOF) marks at the current position.
+This returns when the file mark has been written to the media.
+.It Cm weofi
+Write
+.Ar count
+end-of-file (EOF) marks at the current position.
+This returns as soon as the command has been validated by the tape drive.
 .It Cm smk
 Write
 .Ar count
@@ -129,6 +136,8 @@
 .It Cm offline , rewoffl
 Rewind the tape and place the drive off line.
 Some drives are never off line.
+.It Cm load
+Load the tape into the drive.
 .It Cm retension
 Re-tension the tape.
 This winds the tape from the current position to the end
@@ -136,7 +145,7 @@
 This sometimes improves subsequent reading and writing,
 particularly for streaming drives.
 Some drives do not support this.
-.It Cm status
+.It Cm ostatus
 Output status information about the drive.
 For SCSI magnetic tape devices,
 the current operating modes of density, blocksize, and whether compression
@@ -149,6 +158,9 @@
 that this information is not definitive (only BOT, End of Recorded Media, and
 hardware or SCSI logical block position (if the drive supports such) are
 considered definitive tape positions).
+.Pp
+Also note that this is the old status command, and will be eliminated in
+favor of the new status command (see below) in a future release.
 .It Cm errstat
 Output (and clear) error status information about this device.
 For every normal
@@ -165,9 +177,12 @@
 .It Cm eod , eom
 Wind the tape to the end of the recorded data,
 typically after an EOF mark where another file may be written.
+.It Cm rblim
+Report the block limits of the tape drive, including the minimum and
+maximum block size, and the block granularity if any.
 .El
 .Pp
-The following commands require an
+The following commands may require an
 .Ar argument .
 .Bl -tag -width ".Cm seteotmodel"
 .It Cm sethpos
@@ -199,6 +214,183 @@
 .Ar 1
 or
 .Ar 2 .
+.It Cm status
+Output status information about the drive.
+For SCSI magnetic tape devices,
+the current operating modes of density, blocksize, and whether compression
+is enabled is reported.
+The current state of the driver (what it thinks that
+it is doing with the device) is reported.
+.Pp
+If the driver knows the relative
+position from BOT (in terms of filemarks and records), it outputs that.
+If the tape drive supports the long form report of the
+.Tn SCSI
+READ POSITION command, the Reported File Number and Reported Record Number
+will be numbers other than -1, and there may be Flags reported as well.
+.Pp
+The BOP flag means that the logical position of the drive is at the
+beginning of the partition.
+.Pp
+The EOP flag means that the logical position of the drive is between Early
+Warning and End of Partition.
+.Pp
+The BPEW flag means that the logical position of the drive is in a
+Programmable Early Warning Zone or on the EOP side of Early Warning.
+.Pp
+Note that the Reported Record Number is the tape block or object number
+relative to the beginning of the partition.
+The Calculated Record Number is the tape block or object number relative
+to the previous file mark.
+.Pp
+Note
+that the Calculated File and Record Numbers are not definitive.
+The Reported File and Record Numbers are definitive, if they are numbers
+other than -1.
+.Bl -tag -width 6n
+.It Fl v
+Print additional status information, such as the maximum supported I/O
+size.
+.It Fl x
+Print all available status data to stdout in XML format.
+.El
+.It Cm getdensity
+Report density support information for the tape drive and any media that is
+loaded.
+Most drives will report at least basic density information similar to that
+reported by
+.Nm status
+command.
+Newer tape drives that conform to the T-10 SSC and newer tape
+specifications may report more detailed information about the types of
+tapes they support and the tape currently in the drive.
+.Bl -tag -width 6n
+.It Fl x
+Print all available density data to stdout in XML format.
+Because density information is currently included in the general status XML
+report used for
+.Nm
+status command, this will be the same XML output via
+.Do
+.Nm
+status
+.Fl x 
+.Dc
+.El
+.It Cm param
+Display or set parameters.
+One of 
+.Fl l ,
+.Fl s ,
+or
+.Fl x
+must be specified to indicate which operation to perform.
+See
+.Xr sa 4
+for more detailed information on the parameters.
+.Bl -tag -width 8n
+.It Fl l
+List parameters, values and descriptions.
+By default all parameters will be displayed.
+To display a specific parameter, specify the parameter with
+.Fl p .
+.It Fl p Ar name
+Specify the parameter name to list (with
+.Fl l )
+or set (with 
+.Fl s ) .
+.It Fl q
+Enable quiet mode for parameter listing.
+This will suppress printing of parameter descriptions.
+.It Fl s Ar value
+Specify the parameter value to set.
+The general type of this argument (integer, unsigned integer, string) is
+determined by the type of the variable indicated by the
+.Xr sa 4
+driver.
+More detailed argument checking is done by the
+.Xr sa 4
+driver.
+.It Fl x
+Print out all parameter information in XML format.
+.El
+.It Cm protect
+Display or set drive protection parameters.
+This is used to control checking and reporting a per-block checksum for
+tape drives that support it.
+Some drives may only support some parameters.
+.Bl -tag -width 8n
+.It Fl b Ar 0|1
+Set the Recover Buffered Data Protected bit.
+If set, this indicates that checksums are transferred with the logical
+blocks transferred by the RECOVERED BUFFERED DATA
+.Tn SCSI
+command.
+.It Fl d
+Disable all protection information settings.
+.It Fl e
+Enable all protection information settings.
+The default protection method used is Reed-Solomon CRC (protection method
+1), as specified in ECMA-319.
+The default protection information length used with Reed-Solomon CRC is
+4 bytes.
+To enable all settings except one more setting, specify the
+.Fl e
+argument and then explicitly disable settings that you do not wish to
+enable.
+For example, specifying
+.Fl e
+.Fl w Ar 0
+will enable all settings except for LBP_W.
+.It Fl l
+List available protection parmeters and their current settings.
+.It Fl L Ar len
+Set the length of the protection information in bytes.
+For Reed-Solomon CRC, the protection information length should be 4 bytes.
+.It Fl m Ar num
+Specify the numeric value for the protection method.
+The numeric value for Reed-Solomon CRC is 1.
+.It Fl r Ar 0|1
+Set the LBP_R parameter.
+When set, this indicates that each block read from the tape drive will
+have a checksum at the end.
+.It Fl v
+Enable verbose mode for parameter listing.
+This will include descriptions of each parameter.
+.It Fl w Ar 0|1
+Set the LBP_W parameter.
+When set, this indicates that each block written to the tape drive will have
+a checksum at the end.
+The drive will verify the checksum before writing the block to tape.
+.El
+.It Cm locate
+Set the tape drive's logical position.
+One of 
+.Fl b ,
+.Fl e ,
+.Fl f ,
+or 
+.Fl s
+must be specified to indicate the type of position.
+If the partition number is specified, the drive will first relocate to the
+given partition (if it exists) and then to the position indicated within
+that partition.
+If the partition number is not specified, the drive will relocate to the
+given position within the current partition.
+.Bl -tag -width 14n
+.It Fl b Ar block_addr
+Relocate to the given tape block or logical object identifier.
+Note that the block number is the Reported Record Number that is relative
+to the beginning of the partition (or beginning of tape).
+.It Fl e
+Relocate to the end of data.
+.It Fl f Ar fileno
+Relocate to the given file number.
+.It Fl p Ar partition
+Specify the partition to change to.
+.It Fl s Ar setmark
+Relocate to the given set mark.
+.El
 .It Cm comp
 Set the drive's compression mode.
 The non-numeric values of
@@ -234,6 +426,13 @@
 display to see which compression algorithm is currently in use), the user
 can manually specify one of the supported compression keywords (above), or
 supply a numeric compression value from the drive's specifications.
+.Pp
+Note that for some older tape drives (for example the Exabyte 8200 and 8500
+series drives) it is necessary to switch to a different density to tell the
+drive to record data in its compressed format.
+If the user attempts to turn compression on while the uncompressed density
+is selected, the drive will return an error.
+This is generally not an issue for modern tape drives.
 .It Cm density
 Set the density for the drive.
 For the density codes, see below.
@@ -249,13 +448,14 @@
 string has been taken for.
 .El
 .Pp
-The following density table was taken from the
+The initial version of the density table below was taken from the
 .Sq Historical sequential access density codes
 table (A-1) in Revision 11 of the SCSI-3 Stream Device Commands (SSC)
 working draft, dated November 11, 1997.
+Subsequent additions have come from a number of sources.
 .Pp
 The density codes are:
-.Bd -literal -offset 3n
+.Bd -literal -offset 2n
 0x0    default for device
 0xE    reserved for ECMA
 
@@ -264,7 +464,7 @@
 0x01   12.7  (0.5)    9         32     (800)  NRZI  R   X3.22-1983   2
 0x02   12.7  (0.5)    9         63   (1,600)  PE    R   X3.39-1986   2
 0x03   12.7  (0.5)    9        246   (6,250)  GCR   R   X3.54-1986   2
-0x05    6.3  (0.25)  4/9       315   (8,000)  GCR   C   X3.136-1986  1
+0x05    6.3  (0.25)  4/9       315   (8,000)  GCR   C   X3.136-1986  1,3
 0x06   12.7  (0.5)    9        126   (3,200)  PE    R   X3.157-1987  2
 0x07    6.3  (0.25)   4        252   (6,400)  IMFM  C   X3.116-1986  1
 0x08    3.81 (0.15)   4        315   (8,000)  GCR   CS  X3.158-1987  1
@@ -278,8 +478,8 @@
 0x11    6.3  (0.25)  26        630  (16,000)  GCR   C   QIC-320      1,6
 0x12    6.3  (0.25)  30      2,034  (51,667)  RLL   C   QIC-1350     1,6
 0x13    3.81 (0.15)   1      2,400  (61,000)  DDS   CS  X3B5/88-185A 5
-0x14    8.0  (0.315)  1      1,703  (43,245)  RLL   CS  X3.202-1991  5
-0x15    8.0  (0.315)  1      1,789  (45,434)  RLL   CS  ECMA TC17    5
+0x14    8.0  (0.315)  1      1,703  (43,245)  RLL   CS  X3.202-1991  5,11
+0x15    8.0  (0.315)  1      1,789  (45,434)  RLL   CS  ECMA TC17    5,12
 0x16   12.7  (0.5)   48        394  (10,000)  MFM   C   X3.193-1990  1
 0x17   12.7  (0.5)   48      1,673  (42,500)  MFM   C   X3B5/91-174  1
 0x18   12.7  (0.5)  112      1,673  (42,500)  MFM   C   X3B5/92-50   1
@@ -302,11 +502,39 @@
 0x29   12.7  (0.5)
 0x2A
 0x2B   12.7  (0.5)    3          ?        ?     ?   C   X3.267       5
+0x40   12.7  (0.5)  384      4,800  (123,952)       C   LTO-1
 0x41   12.7  (0.5)  208      3,868  (98,250)  RLL   C   DLTapeIV(40) 6,7
-0x48   12.7  (0.5)  448      5,236  (133,000) PRML  C   SDLTapeI(110) 6,8
+0x42   12.7  (0.5)  512      7,398  (187,909)       C   LTO-2
+0x44   12.7  (0.5)  704      9,638  (244,805)       C   LTO-3
+0x46   12.7  (0.5)  896      12,725 (323,215)       C   LTO-4
+0x47    3.81 (0.25)   ?      6,417  (163,000)       CS  DAT-72
+0x48   12.7  (0.5)  448      5,236  (133,000) PRML  C   SDLTapeI(110) 6,8,13
 0x49   12.7  (0.5)  448      7,598  (193,000) PRML  C   SDLTapeI(160) 6,8
+0x4A   12.7  (0.5)  768          ?            PRML  C   T10000A      10
+0x4B   12.7  (0.5) 1152          ?            PRML  C   T10000B      10
+0x4C   12.7  (0.5) 3584          ?            PRML  C   T10000C      10
+0x4D   12.7  (0.5) 4608          ?            PRML  C   T10000D      10
+0x51   12.7  (0.5)  512      11,800 (299,720)       C   3592A1 (unencrypted)
+0x52   12.7  (0.5)  896      11,800 (299,720)       C   3592A2 (unencrypted)
+0x53   12.7  (0.5) 1152      13,452 (341,681)       C   3592A3 (unencrypted)
+0x54   12.7  (0.5) 2560      19,686 (500,024)       C   3592A4 (unencrypted)
+0x55   12.7  (0.5) 5120      20,670 (525,018)       C   3592A5 (unencrypted)
+0x56   12.7  (0.5) 7680      20,670 (525,018)       C   3592B5 (unencrypted)
+0x58   12.7  (0.5) 1280      15,142 (384,607)       C   LTO-5
+0x5A   12.7  (0.5) 2176      15,142 (384,607)       C   LTO-6
+0x5C   12.7  (0.5) 3584      19,107 (485,318)       C   LTO-7
+0x5D   12.7  (0.5) 5376      19,107 (485,318)       C   LTO-M8       14
+0x5E   12.7  (0.5) 6656      20,669 (524,993)       C   LTO-8
+0x71   12.7  (0.5)  512      11,800 (299,720)       C   3592A1 (encrypted)
+0x72   12.7  (0.5)  896      11,800 (299,720)       C   3592A2 (encrypted)
+0x73   12.7  (0.5) 1152      13,452 (341,681)       C   3592A3 (encrypted)
+0x74   12.7  (0.5) 2560      19,686 (500,024)       C   3592A4 (encrypted)
+0x75   12.7  (0.5) 5120      20,670 (525,018)       C   3592A5 (encrypted)
+0x76   12.7  (0.5) 7680      20,670 (525,018)       C   3592B5 (encrypted)
+0x8c    8.0  (0.315)  1      1,789  (45,434)  RLL   CS  EXB-8500c    5,9
+0x90    8.0  (0.315)  1      1,703  (43,245)  RLL   CS  EXB-8200c    5,9
 .Ed
-.Bd -literal -offset 3n
+.Bd -literal -offset 2n
 Code    Description                                Type Description
 ----    --------------------------------------     ---- -----------
 NRZI    Non return to zero, change on ones         R    Reel-to-reel
@@ -318,19 +546,86 @@
 RLL     Run length limited
 PRML    Partial Response Maximum Likelihood
 .Ed
-.Bd -literal -offset 3n
+.Bd -literal -offset 2n
 NOTES
-1. Serial recorded.
-2. Parallel recorded.
-3. Old format known as QIC-11.
-5. Helical scan.
-6. This is not an American National Standard.  The reference is based on
-   an industry standard definition of the media format.
-7. DLT recording: serially recorded track pairs (DLTapeIII and
-   DLTapeIV(20)), or track quads (DLTapeIV(35) and DLTapeIV(40)).
-8. Super DLT (SDLT) recording: 56 serially recorded logical tracks with
-   8 physical tracks each.
+1.  Serial recorded.
+2.  Parallel recorded.
+3.  Old format known as QIC-11.
+5.  Helical scan.
+6.  This is not an American National Standard.  The reference is based
+    on an industry standard definition of the media format.
+7.  DLT recording: serially recorded track pairs (DLTapeIII and
+    DLTapeIV(20)), or track quads (DLTapeIV(35) and DLTapeIV(40)).
+8.  Super DLT (SDLT) recording: 56 serially recorded logical tracks
+    with 8 physical tracks each.
+9.  Vendor-specific Exabyte density code for compressed format.
+10. bpi/bpmm values for the Oracle/StorageTek T10000 tape drives are
+    not listed in the manual.  Someone with access to a drive can
+    supply the necessary values by running 'mt getdensity'.
+11. This is Exabyte 8200 uncompressed format.  The compressed format
+    density code is 0x90.
+12. This is Exabyte 8500 uncompressed format.  The compressed format
+    density code is 0x8c.
+13. This density code (0x48) was also used for DAT-160.
+14. Officially known as LTO-8 Type M, abbreviated M8.  This is a pristine
+    LTO-7 cartridge initialized with a higher density format by an LTO-8
+    drive.  It cannot be read by an LTO-7 drive.  Uncompressed capacity
+    is 9TB, compared to 6TB for LTO-7 and 12TB for LTO-8.
 .Ed
+.Bd -literal -offset 2n
+NOTE ON QIC STREAMERS
+
+The following is a table of Data Cartridge types as used in the 1/4 inch
+tape drives such as the Archive Viper 150, Wangtek 5525ES, and Tandberg
+TDC4220 tape drives:
+
+Value Reference     Format    Cartridge Type  Capacity   Tracks  Length
+----- ---------     ------    --------------  --------   ------  ------
+
+0x05                QIC-11    DC300           15MB       4        300ft
+0x05                QIC-11    DC300XL/P       20MB       4        450ft
+0x05                QIC-11    DC600           27MB       4        600ft
+0x05  X3.136-1986   QIC-24    DC615A          15MB       9        150ft
+0x05  X3.136-1986   QIC-24    DC300XL/P       45MB       9        450ft
+0x05  X3.136-1986   QIC-24    DC600A          60MB       9        600ft
+0x0F  QIC-120       QIC-120   DC600A/DC6150   120MB      15       620ft
+0x10  QIC-150       QIC-150   DC600XTD/DC6150 150MB      18       620ft
+0x10  QIC-150       QIC-150   DC6250          250MB      18     1,020ft
+0x11  QIC-320       QIC-525   DC6320          320MB      26       620ft
+0x11  QIC-320       QIC-525   DC6525          525MB      26     1,020ft
+0x1E  QIC-1000C     QIC-1000  DC9100/DL9135   1.0GB      30       760ft
+0x1E  QIC-1000C     QIC-1000  DC9150          1.2GB      30       950ft
+0x22  QIC-2GB(C)    QIC-2GB   DC9200          2.0GB      42       950ft
+0x22  QIC-2GB(C)    QIC-2GB   DC9250          2.5GB      42     1,200ft
+.Ed
+.Pp
+Notes:
+.Pp
+QIC-24, QIC-120, QIC-150 use fixed blocksize of 512 bytes, QIC-525, QIC-1000
+and QIC-2GB can use blocksize of 1,024 bytes.
+DDS (DAT) drives generally use variable blocks.
+.Pp
+QIC-02 and QIC-36 are interface standards for tape drives.
+The QIC-02 and QIC-36 streamers such as the Wangtek 5250EQ are otherwise
+identical to their SCSI versions (i.e.: Wangtek 5250ES).
+.Pp
+It seems that the 150MB and larger streamers cannot write QIC-24 9 track
+formats, only read them.
+.Pp
+DC600A cartridges marked "10,000ftpi" can only be used as QIC-11, QIC-24,
+and QIC-120 format.
+DC600A cartridges marked 12,500ftpi can be used as both QIC-120 and QIC-150
+format.
+.Pp
+Some manufacturers do not use "DC" on their cartridges.
+Verbatim uses DL, Maxell uses MC, Sony uses QD, Quill uses DQ.
+.Pp
+3M/Imation & Fuji use DC.
+Thus a DL6250, MC-6250, QD6250, DQ6250 are all identical media to a DC6250.
+.Pp
+QIC tape media is not "connected" to the take up reels and will de-spool
+if the tape drive has dust covering the light sensor that looks for the end
+of tape holes in the media.
 .Sh ENVIRONMENT
 .Bl -tag -width ".Ev TAPE"
 .It Ev TAPE
@@ -343,8 +638,6 @@
 .El
 .Sh FILES
 .Bl -tag -width ".Pa /dev/*sa[0-9]*" -compact
-.It Pa /dev/*wt*
-QIC-02/QIC-36 magnetic tape interface
 .It Pa /dev/*sa[0-9]*
 SCSI magnetic tape interface
 .El
@@ -357,7 +650,6 @@
 .Sh SEE ALSO
 .Xr dd 1 ,
 .Xr ioctl 2 ,
-.Xr ast 4 ,
 .Xr mtio 4 ,
 .Xr sa 4 ,
 .Xr environ 7


Property changes on: trunk/usr.bin/mt/mt.1
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Modified: trunk/usr.bin/mt/mt.c
===================================================================
--- trunk/usr.bin/mt/mt.c	2018-07-07 16:56:02 UTC (rev 11469)
+++ trunk/usr.bin/mt/mt.c	2018-07-07 16:56:25 UTC (rev 11470)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 1980, 1993
  *	The Regents of the University of California.  All rights reserved.
@@ -26,6 +27,37 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+/*-
+ * Copyright (c) 2013, 2014, 2015 Spectra Logic Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * Authors: Ken Merry           (Spectra Logic Corporation)
+ */
 
 #ifndef lint
 static const char copyright[] =
@@ -40,7 +72,7 @@
 #endif /* not lint */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/usr.bin/mt/mt.c 293290 2016-01-07 00:40:51Z bdrewery $");
 
 /*
  * mt --
@@ -49,6 +81,8 @@
 #include <sys/types.h>
 #include <sys/ioctl.h>
 #include <sys/mtio.h>
+#include <sys/queue.h>
+#include <sys/sbuf.h>
 
 #include <ctype.h>
 #include <err.h>
@@ -57,7 +91,17 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <bsdxml.h>
+#include <mtlib.h>
 
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_periph.h>
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_sa.h>
+
 /* the appropriate sections of <sys/mtio.h> are also #ifdef'd for FreeBSD */
 /* c_flags */
 #define NEED_2ARGS	0x01
@@ -65,6 +109,7 @@
 #define IS_DENSITY	0x04
 #define DISABLE_THIS	0x08
 #define IS_COMP		0x10
+#define	USE_GETOPT	0x20
 
 #ifndef TRUE
 #define TRUE 1
@@ -72,8 +117,18 @@
 #ifndef FALSE
 #define FALSE 0
 #endif
+#ifndef MAX
+#define	MAX(a, b) (a > b) ? a : b
+#endif
+#define MT_PLURAL(a) (a == 1) ? "" : "s"
 
-struct commands {
+typedef enum {
+	MT_CMD_NONE	= MTLOAD + 1,
+	MT_CMD_PROTECT,
+	MT_CMD_GETDENSITY
+} mt_commands;
+
+static const struct commands {
 	const char *c_name;
 	unsigned long c_code;
 	int c_ronly;
@@ -87,10 +142,12 @@
 	{ "fsf",	MTFSF,	1, 0 },
 	{ "fsr",	MTFSR,	1, 0 },
 	{ "offline",	MTOFFL,	1, 0 },
+	{ "load",	MTLOAD, 1, 0 },
 	{ "rewind",	MTREW,	1, 0 },
 	{ "rewoffl",	MTOFFL,	1, 0 },
-	{ "status",	MTNOP,	1, 0 },
+	{ "ostatus",	MTNOP,	1, 0 },
 	{ "weof",	MTWEOF,	0, ZERO_ALLOWED },
+	{ "weofi",	MTWEOFI, 0, ZERO_ALLOWED },
 	{ "erase",	MTERASE, 0, ZERO_ALLOWED},
 	{ "blocksize",	MTSETBSIZ, 0, NEED_2ARGS|ZERO_ALLOWED },
 	{ "density",	MTSETDNSTY, 0, NEED_2ARGS|ZERO_ALLOWED|IS_DENSITY },
@@ -111,30 +168,54 @@
 	{ "seteotmodel",	MTIOCSETEOTMODEL, 0, NEED_2ARGS|ZERO_ALLOWED },
 	{ "getmodel",	MTIOCGETEOTMODEL, 0, 0 },
 	{ "geteotmodel",	MTIOCGETEOTMODEL, 0, 0 },
+	{ "rblim", 	MTIOCRBLIM, 0, 0},
+	{ "getdensity",	MT_CMD_GETDENSITY, 0, USE_GETOPT},
+	{ "status",	MTIOCEXTGET, 0, USE_GETOPT },
+	{ "locate",	MTIOCEXTLOCATE, 0, USE_GETOPT },
+	{ "param",	MTIOCPARAMGET, 0, USE_GETOPT },
+	{ "protect", 	MT_CMD_PROTECT, 0, USE_GETOPT },
 	{ NULL, 0, 0, 0 }
 };
 
-const char *getblksiz(int);
-void printreg(const char *, u_int, const char *);
-void status(struct mtget *);
-void usage(void);
-void st_status (struct mtget *);
-int stringtodens (const char *s);
-const char *denstostring (int d);
-int denstobp(int d, int bpi);
-u_int32_t stringtocomp(const char *s);
-const char *comptostring(u_int32_t comp);
-void warn_eof(void);
 
+static const char *getblksiz(int);
+static void printreg(const char *, u_int, const char *);
+static void status(struct mtget *);
+static void usage(void);
+const char *get_driver_state_str(int dsreg);
+static void st_status (struct mtget *);
+static int mt_locate(int argc, char **argv, int mtfd, const char *tape);
+static int nstatus_print(int argc, char **argv, char *xml_str,
+			 struct mt_status_data *status_data);
+static int mt_xml_cmd(unsigned long cmd, int argc, char **argv, int mtfd,
+		      const char *tape);
+static int mt_print_density_entry(struct mt_status_entry *density_root, int indent);
+static int mt_print_density_report(struct mt_status_entry *report_root, int indent);
+static int mt_print_density(struct mt_status_entry *density_root, int indent);
+static int mt_getdensity(int argc, char **argv, char *xml_str,
+			 struct mt_status_data *status_data);
+static int mt_set_param(int mtfd, struct mt_status_data *status_data,
+			char *param_name, char *param_value);
+static int mt_protect(int argc, char **argv, int mtfd,
+		      struct mt_status_data *status_data);
+static int mt_param(int argc, char **argv, int mtfd, char *xml_str,
+		    struct mt_status_data *status_data);
+static const char *denstostring (int d);
+static u_int32_t stringtocomp(const char *s);
+static const char *comptostring(u_int32_t comp);
+static void warn_eof(void);
+
 int
 main(int argc, char *argv[])
 {
-	struct commands *comp;
+	const struct commands *comp;
 	struct mtget mt_status;
 	struct mtop mt_com;
 	int ch, len, mtfd;
 	const char *p, *tape;
 
+	bzero(&mt_com, sizeof(mt_com));
+	
 	if ((tape = getenv("TAPE")) == NULL)
 		tape = DEFTAPE;
 
@@ -145,13 +226,15 @@
 			tape = optarg;
 			break;
 		case '?':
+			usage();
+			break;
 		default:
-			usage();
+			break;
 		}
 	argc -= optind;
 	argv += optind;
 
-	if (argc < 1 || argc > 2)
+	if (argc < 1)
 		usage();
 
 	len = strlen(p = *argv++);
@@ -166,6 +249,11 @@
 	if(comp->c_flags & DISABLE_THIS) {
 		warn_eof();
 	}
+	if (comp->c_flags & USE_GETOPT) {
+		argc--;
+		optind = 0;
+	}
+
 	if ((mtfd = open(tape, comp->c_ronly ? O_RDONLY : O_RDWR)) < 0)
 		err(1, "%s", tape);
 	if (comp->c_code != MTNOP) {
@@ -174,7 +262,7 @@
 			if (!isdigit(**argv) &&
 			    (comp->c_flags & IS_DENSITY)) {
 				const char *dcanon;
-				mt_com.mt_count = stringtodens(*argv);
+				mt_com.mt_count = mt_density_num(*argv);
 				if (mt_com.mt_count == 0)
 					errx(1, "%s: unknown density", *argv);
 				dcanon = denstostring(mt_com.mt_count);
@@ -191,16 +279,17 @@
 					errx(1, "%s: unknown compression",
 					     *argv);
 				p = "";
-			} else {
+			} else if ((comp->c_flags & USE_GETOPT) == 0) {
 				char *q;
 				/* allow for hex numbers; useful for density */
 				mt_com.mt_count = strtol(*argv, &q, 0);
 				p = q;
 			}
-			if ((mt_com.mt_count <=
-			    ((comp->c_flags & ZERO_ALLOWED)? -1: 0)
-			    && ((comp->c_flags & IS_COMP) == 0)
-			    ) || *p)
+			if (((comp->c_flags & USE_GETOPT) == 0)
+			 && (((mt_com.mt_count <=
+			     ((comp->c_flags & ZERO_ALLOWED)? -1: 0))
+			   && ((comp->c_flags & IS_COMP) == 0))
+			  || *p))
 				errx(1, "%s: illegal count", *argv);
 		}
 		else
@@ -289,6 +378,47 @@
 			exit(0);
 			/* NOTREACHED */
 		}
+		case MTIOCRBLIM:
+		{
+			struct mtrblim rblim;
+
+			bzero(&rblim, sizeof(rblim));
+
+			if (ioctl(mtfd, MTIOCRBLIM, (caddr_t)&rblim) < 0)
+				err(2, "%s", tape);
+			(void)printf("%s:\n"
+			    "    min blocksize %u byte%s\n"
+			    "    max blocksize %u byte%s\n"
+			    "    granularity %u byte%s\n",
+			    tape, rblim.min_block_length,
+			    MT_PLURAL(rblim.min_block_length),
+			    rblim.max_block_length,
+			    MT_PLURAL(rblim.max_block_length),
+			    (1 << rblim.granularity),
+			    MT_PLURAL((1 << rblim.granularity)));
+			exit(0);
+			/* NOTREACHED */
+		}
+		case MTIOCPARAMGET:
+		case MTIOCEXTGET:
+		case MT_CMD_PROTECT:
+		case MT_CMD_GETDENSITY:
+		{
+			int retval = 0;
+
+			retval = mt_xml_cmd(comp->c_code, argc, argv, mtfd,
+			    tape);
+
+			exit(retval);
+		}
+		case MTIOCEXTLOCATE:
+		{
+			int retval = 0;
+
+			retval = mt_locate(argc, argv, mtfd, tape);
+
+			exit(retval);
+		}
 		default:
 			break;
 		}
@@ -303,7 +433,7 @@
 	/* NOTREACHED */
 }
 
-struct tape_desc {
+static const struct tape_desc {
 	short	t_type;		/* type of magtape device */
 	const char *t_name;	/* printing name */
 	const char *t_dsbits;	/* "drive status" register */
@@ -316,10 +446,10 @@
 /*
  * Interpret the status buffer returned
  */
-void
+static void
 status(struct mtget *bp)
 {
-	struct tape_desc *mt;
+	const struct tape_desc *mt;
 
 	for (mt = tapes;; mt++) {
 		if (mt->t_type == 0) {
@@ -344,7 +474,7 @@
 /*
  * Print a register a la the %b format of the kernel's printf.
  */
-void
+static void
 printreg(const char *s, u_int v, const char *bits)
 {
 	int i, any = 0;
@@ -374,7 +504,7 @@
 	}
 }
 
-void
+static void
 usage(void)
 {
 	(void)fprintf(stderr, "usage: mt [-f device] command [count]\n");
@@ -381,63 +511,7 @@
 	exit(1);
 }
 
-struct densities {
-	int dens;
-	int bpmm;
-	int bpi;
-	const char *name;
-} dens[] = {
-	/*
-	 * Taken from T10 Project 997D 
-	 * SCSI-3 Stream Device Commands (SSC)
-	 * Revision 11, 4-Nov-97
-	 */
-	/*Num.  bpmm    bpi     Reference     */
-	{ 0x1,	32,	800,	"X3.22-1983" },
-	{ 0x2,	63,	1600,	"X3.39-1986" },
-	{ 0x3,	246,	6250,	"X3.54-1986" },
-	{ 0x5,	315,	8000,	"X3.136-1986" },
-	{ 0x6,	126,	3200,	"X3.157-1987" },
-	{ 0x7,	252,	6400,	"X3.116-1986" },
-	{ 0x8,	315,	8000,	"X3.158-1987" },
-	{ 0x9,	491,	37871,	"X3.180" },
-	{ 0xA,	262,	6667,	"X3B5/86-199" },
-	{ 0xB,	63,	1600,	"X3.56-1986" },
-	{ 0xC,	500,	12690,	"HI-TC1" },
-	{ 0xD,	999,	25380,	"HI-TC2" },
-	{ 0xF,	394,	10000,	"QIC-120" },
-	{ 0x10,	394,	10000,	"QIC-150" },
-	{ 0x11,	630,	16000,	"QIC-320" },
-	{ 0x12,	2034,	51667,	"QIC-1350" },
-	{ 0x13,	2400,	61000,	"X3B5/88-185A" },
-	{ 0x14,	1703,	43245,	"X3.202-1991" },
-	{ 0x15,	1789,	45434,	"ECMA TC17" },
-	{ 0x16,	394,	10000,	"X3.193-1990" },
-	{ 0x17,	1673,	42500,	"X3B5/91-174" },
-	{ 0x18,	1673,	42500,	"X3B5/92-50" },
-	{ 0x19, 2460,   62500,  "DLTapeIII" },
-	{ 0x1A, 3214,   81633,  "DLTapeIV(20GB)" },
-	{ 0x1B, 3383,   85937,  "DLTapeIV(35GB)" },
-	{ 0x1C, 1654,	42000,	"QIC-385M" },
-	{ 0x1D,	1512,	38400,	"QIC-410M" },
-	{ 0x1E, 1385,	36000,	"QIC-1000C" },
-	{ 0x1F,	2666,	67733,	"QIC-2100C" },
-	{ 0x20, 2666,	67733,	"QIC-6GB(M)" },
-	{ 0x21,	2666,	67733,	"QIC-20GB(C)" },
-	{ 0x22,	1600,	40640,	"QIC-2GB(C)" },
-	{ 0x23, 2666,	67733,	"QIC-875M" },
-	{ 0x24,	2400,	61000,	"DDS-2" },
-	{ 0x25,	3816,	97000,	"DDS-3" },
-	{ 0x26,	3816,	97000,	"DDS-4" },
-	{ 0x27,	3056,	77611,	"Mammoth" },
-	{ 0x28,	1491,	37871,	"X3.224" },
-	{ 0x41, 3868,   98250,  "DLTapeIV(40GB)" },
-	{ 0x48, 5236,   133000, "SDLTapeI(110)" },
-	{ 0x49, 7598,   193000, "SDLTapeI(160)" },
-	{ 0, 0, 0, NULL }
-};
-
-struct compression_types {
+static const struct compression_types {
 	u_int32_t	comp_number;
 	const char 	*name;
 } comp_types[] = {
@@ -450,63 +524,20 @@
 	{ 0xf0f0f0f0, NULL}
 };
 
-const char *
+static const char *
 denstostring(int d)
 {
 	static char buf[20];
-	struct densities *sd;
+	const char *name = mt_density_name(d);
 
-	/* densities 0 and 0x7f are handled as special cases */
-	if (d == 0)
-		return "default";
-	if (d == 0x7f)
-		return "same";
-	for (sd = dens; sd->dens; sd++)
-		if (sd->dens == d)
-			break;
-	if (sd->dens == 0)
+	if (name == NULL)
 		sprintf(buf, "0x%02x", d);
 	else 
-		sprintf(buf, "0x%02x:%s", d, sd->name);
+		sprintf(buf, "0x%02x:%s", d, name);
 	return buf;
 }
 
-/*
- * Given a specific density number, return either the bits per inch or bits
- * per millimeter for the given density.
- */
-int
-denstobp(int d, int bpi)
-{
-	struct densities *sd;
-
-	for (sd = dens; sd->dens; sd++)
-		if (sd->dens == d)
-			break;
-	if (sd->dens == 0)
-		return(0);
-	else {
-		if (bpi)
-			return(sd->bpi);
-		else
-			return(sd->bpmm);
-	}
-}
-
-int
-stringtodens(const char *s)
-{
-	struct densities *sd;
-	size_t l = strlen(s);
-
-	for (sd = dens; sd->dens; sd++)
-		if (strncasecmp(sd->name, s, l) == 0)
-			break;
-	return sd->dens;
-}
-
-
-const char *
+static const char *
 getblksiz(int bs)
 {
 	static char buf[25];
@@ -518,11 +549,11 @@
 	}
 }
 
-const char *
+static const char *
 comptostring(u_int32_t comp)
 {
 	static char buf[20];
-	struct compression_types *ct;
+	const struct compression_types *ct;
 
 	if (comp == MT_COMP_DISABLED)
 		return "disabled";
@@ -540,10 +571,10 @@
 		return(ct->name);
 }
 
-u_int32_t
+static u_int32_t
 stringtocomp(const char *s)
 {
-	struct compression_types *ct;
+	const struct compression_types *ct;
 	size_t l = strlen(s);
 
 	for (ct = comp_types; ct->name; ct++)
@@ -553,7 +584,39 @@
 	return(ct->comp_number);
 }
 
-void
+static struct driver_state {
+	int dsreg;
+	const char *desc;
+} driver_states[] = {
+	{ MTIO_DSREG_REST, "at rest" },
+	{ MTIO_DSREG_RBSY, "Communicating with drive" },
+	{ MTIO_DSREG_WR, "Writing" },
+	{ MTIO_DSREG_FMK, "Writing Filemarks" },
+	{ MTIO_DSREG_ZER, "Erasing" },
+	{ MTIO_DSREG_RD, "Reading" },
+	{ MTIO_DSREG_FWD, "Spacing Forward" },
+	{ MTIO_DSREG_REV, "Spacing Reverse" },
+	{ MTIO_DSREG_POS, "Hardware Positioning (direction unknown)" },
+	{ MTIO_DSREG_REW, "Rewinding" },
+	{ MTIO_DSREG_TEN, "Retensioning" },
+	{ MTIO_DSREG_UNL, "Unloading" },
+	{ MTIO_DSREG_LD, "Loading" },
+};
+
+const char *
+get_driver_state_str(int dsreg)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(driver_states)/sizeof(driver_states[0])); i++) {
+		if (driver_states[i].dsreg == dsreg)
+			return (driver_states[i].desc);
+	}
+
+	return (NULL);
+}
+
+static void
 st_status(struct mtget *bp)
 {
 	printf("Mode      Density              Blocksize      bpi      "
@@ -565,76 +628,966 @@
 	       "2:        %-17s    %-12s   %-7d  %s\n"
 	       "3:        %-17s    %-12s   %-7d  %s\n",
 	       denstostring(bp->mt_density), getblksiz(bp->mt_blksiz),
-	       denstobp(bp->mt_density, TRUE), comptostring(bp->mt_comp),
+	       mt_density_bp(bp->mt_density, TRUE), comptostring(bp->mt_comp),
 	       denstostring(bp->mt_density0), getblksiz(bp->mt_blksiz0),
-	       denstobp(bp->mt_density0, TRUE), comptostring(bp->mt_comp0),
+	       mt_density_bp(bp->mt_density0, TRUE), comptostring(bp->mt_comp0),
 	       denstostring(bp->mt_density1), getblksiz(bp->mt_blksiz1),
-	       denstobp(bp->mt_density1, TRUE), comptostring(bp->mt_comp1),
+	       mt_density_bp(bp->mt_density1, TRUE), comptostring(bp->mt_comp1),
 	       denstostring(bp->mt_density2), getblksiz(bp->mt_blksiz2),
-	       denstobp(bp->mt_density2, TRUE), comptostring(bp->mt_comp2),
+	       mt_density_bp(bp->mt_density2, TRUE), comptostring(bp->mt_comp2),
 	       denstostring(bp->mt_density3), getblksiz(bp->mt_blksiz3),
-	       denstobp(bp->mt_density3, TRUE), comptostring(bp->mt_comp3));
+	       mt_density_bp(bp->mt_density3, TRUE), comptostring(bp->mt_comp3));
 
 	if (bp->mt_dsreg != MTIO_DSREG_NIL) {
-		auto char foo[32];
 		const char sfmt[] = "Current Driver State: %s.\n";
 		printf("---------------------------------\n");
-		switch (bp->mt_dsreg) {
-		case MTIO_DSREG_REST:
-			printf(sfmt, "at rest");      
+		const char *state_str;
+
+		state_str = get_driver_state_str(bp->mt_dsreg);
+		if (state_str == NULL) {
+			char foo[32];
+			(void) sprintf(foo, "Unknown state 0x%x", bp->mt_dsreg);
+			printf(sfmt, foo);
+		} else {
+			printf(sfmt, state_str);
+		}
+	}
+	if (bp->mt_resid == 0 && bp->mt_fileno == (daddr_t) -1 &&
+	    bp->mt_blkno == (daddr_t) -1)
+		return;
+	printf("---------------------------------\n");
+	printf("File Number: %d\tRecord Number: %d\tResidual Count %d\n",
+	    bp->mt_fileno, bp->mt_blkno, bp->mt_resid);
+}
+
+static int
+mt_locate(int argc, char **argv, int mtfd, const char *tape)
+{
+	struct mtlocate mtl;
+	uint64_t logical_id = 0;
+	mt_locate_dest_type dest_type = MT_LOCATE_DEST_FILE;
+	int eod = 0, explicit = 0, immediate = 0;
+	int64_t partition = 0;
+	int block_addr_set = 0, partition_set = 0, file_set = 0, set_set = 0;
+	int c, retval;
+
+	retval = 0;
+	bzero(&mtl, sizeof(mtl));
+
+	while ((c = getopt(argc, argv, "b:eEf:ip:s:")) != -1) {
+		switch (c) {
+		case 'b':
+			/* Block address */
+			logical_id = strtoull(optarg, NULL, 0);
+			dest_type = MT_LOCATE_DEST_OBJECT;
+			block_addr_set = 1;
 			break;
-		case MTIO_DSREG_RBSY:    
-			printf(sfmt, "Communicating with drive");
+		case 'e':
+			/* end of data */
+			eod = 1;
+			dest_type = MT_LOCATE_DEST_EOD;
 			break;
-		case MTIO_DSREG_WR:
-			printf(sfmt, "Writing");
+		case 'E':
+			/*
+			 * XXX KDM explicit address mode.  Should we even
+			 * allow this, since the driver doesn't operate in
+			 * explicit address mode?
+			 */
+			explicit = 1;
 			break;
-		case MTIO_DSREG_FMK:
-			printf(sfmt, "Writing Filemarks");
+		case 'f':
+			/* file number */
+			logical_id = strtoull(optarg, NULL, 0);
+			dest_type = MT_LOCATE_DEST_FILE;
+			file_set = 1;
 			break;
-		case MTIO_DSREG_ZER:
-			printf(sfmt, "Erasing");
+		case 'i':
+			/*
+			 * Immediate address mode.  XXX KDM do we want to
+			 * implement this?  The other commands in the
+			 * tape driver will need to be able to handle this.
+			 */
+			immediate = 1;
 			break;
-		case MTIO_DSREG_RD:
-			printf(sfmt, "Reading");
+		case 'p':
+			/*
+			 * Change partition to the given partition.
+			 */
+			partition = strtol(optarg, NULL, 0);
+			partition_set = 1;
 			break;
-		case MTIO_DSREG_FWD:
-			printf(sfmt, "Spacing Forward");
+		case 's':
+			/* Go to the given set mark */
+			logical_id = strtoull(optarg, NULL, 0);
+			dest_type = MT_LOCATE_DEST_SET;
+			set_set = 1;
 			break;
-		case MTIO_DSREG_REV:     
-			printf(sfmt, "Spacing Reverse");
+		default:
 			break;
-		case MTIO_DSREG_POS:
-			printf(sfmt,
-			    "Hardware Positioning (direction unknown)");
+		}
+	}
+
+	/*
+	 * These options are mutually exclusive.  The user may only specify
+	 * one.
+	 */
+	if ((block_addr_set + file_set + eod + set_set) != 1)
+		errx(1, "You must specify only one of -b, -f, -e, or -s");
+
+	mtl.dest_type = dest_type;
+	switch (dest_type) {
+	case MT_LOCATE_DEST_OBJECT:
+	case MT_LOCATE_DEST_FILE:
+	case MT_LOCATE_DEST_SET:
+		mtl.logical_id = logical_id;
+		break;
+	case MT_LOCATE_DEST_EOD:
+		break;
+	}
+
+	if (immediate != 0)
+		mtl.flags |= MT_LOCATE_FLAG_IMMED;
+
+	if (partition_set != 0) {
+		mtl.flags |= MT_LOCATE_FLAG_CHANGE_PART;
+		mtl.partition = partition;
+	}
+
+	if (explicit != 0)
+		mtl.block_address_mode = MT_LOCATE_BAM_EXPLICIT;
+	else
+		mtl.block_address_mode = MT_LOCATE_BAM_IMPLICIT;
+
+	if (ioctl(mtfd, MTIOCEXTLOCATE, &mtl) == -1)
+		err(1, "MTIOCEXTLOCATE ioctl failed on %s", tape);
+
+	return (retval);
+}
+
+typedef enum {
+	MT_PERIPH_NAME			= 0,
+	MT_UNIT_NUMBER 			= 1,
+	MT_VENDOR			= 2,
+	MT_PRODUCT			= 3,
+	MT_REVISION			= 4,
+	MT_COMPRESSION_SUPPORTED	= 5,
+	MT_COMPRESSION_ENABLED		= 6,
+	MT_COMPRESSION_ALGORITHM	= 7,
+	MT_MEDIA_DENSITY		= 8,
+	MT_MEDIA_BLOCKSIZE		= 9,
+	MT_CALCULATED_FILENO		= 10,
+	MT_CALCULATED_REL_BLKNO		= 11,
+	MT_REPORTED_FILENO		= 12,
+	MT_REPORTED_BLKNO		= 13,
+	MT_PARTITION			= 14,
+	MT_BOP				= 15,
+	MT_EOP				= 16,
+	MT_BPEW				= 17,
+	MT_DSREG			= 18,
+	MT_RESID			= 19,
+	MT_FIXED_MODE			= 20,
+	MT_SERIAL_NUM			= 21,
+	MT_MAXIO			= 22,
+	MT_CPI_MAXIO			= 23,
+	MT_MAX_BLK			= 24,
+	MT_MIN_BLK			= 25,
+	MT_BLK_GRAN			= 26,
+	MT_MAX_EFF_IOSIZE		= 27
+} status_item_index;
+
+static struct mt_status_items {
+	const char *name;
+	struct mt_status_entry *entry;
+} req_status_items[] = {
+	{ "periph_name", NULL },
+	{ "unit_number", NULL },
+	{ "vendor", NULL },
+	{ "product", NULL },
+	{ "revision", NULL },
+	{ "compression_supported", NULL },
+	{ "compression_enabled", NULL },
+	{ "compression_algorithm", NULL },
+	{ "media_density", NULL },
+	{ "media_blocksize", NULL },
+	{ "calculated_fileno", NULL },
+	{ "calculated_rel_blkno", NULL },
+	{ "reported_fileno", NULL },
+	{ "reported_blkno", NULL },
+	{ "partition", NULL },
+	{ "bop", NULL },
+	{ "eop", NULL },
+	{ "bpew", NULL },
+	{ "dsreg", NULL },
+	{ "residual", NULL },
+	{ "fixed_mode", NULL },
+	{ "serial_num", NULL },
+	{ "maxio", NULL },
+	{ "cpi_maxio", NULL },
+	{ "max_blk", NULL },
+	{ "min_blk", NULL },
+	{ "blk_gran", NULL },
+	{ "max_effective_iosize", NULL }
+};
+
+int
+nstatus_print(int argc, char **argv, char *xml_str,
+	      struct mt_status_data *status_data)
+{
+	unsigned int i;
+	int64_t calculated_fileno, calculated_rel_blkno;
+	int64_t rep_fileno, rep_blkno, partition, resid;
+	char block_str[32];
+	const char *dens_str;
+	int dsreg, bop, eop, bpew;
+	int xml_dump = 0;
+	size_t dens_len;
+	unsigned int field_width;
+	int verbose = 0;
+	int c;
+
+	while ((c = getopt(argc, argv, "xv")) != -1) {
+		switch (c) {
+		case 'x':
+			xml_dump = 1;
 			break;
-		case MTIO_DSREG_REW:
-			printf(sfmt, "Rewinding");
+		case 'v':
+			verbose = 1;
 			break;
-		case MTIO_DSREG_TEN:
-			printf(sfmt, "Retensioning");
+		default:
 			break;
-		case MTIO_DSREG_UNL:
-			printf(sfmt, "Unloading");
+		}
+	}
+
+	if (xml_dump != 0) {
+		printf("%s", xml_str);
+		return (0);
+	}
+
+	for (i = 0; i < (sizeof(req_status_items)/sizeof(req_status_items[0]));
+	     i++) {
+		char *name;
+
+		name = __DECONST(char *, req_status_items[i].name);
+		req_status_items[i].entry = mt_status_entry_find(status_data,
+		    name);
+		if (req_status_items[i].entry == NULL) {
+			errx(1, "Cannot find status entry %s",
+			    req_status_items[i].name);
+		}
+	}
+
+	printf("Drive: %s%ju: <%s %s %s> Serial Number: %s\n",
+	       req_status_items[MT_PERIPH_NAME].entry->value,
+	       (uintmax_t)req_status_items[MT_UNIT_NUMBER].entry->value_unsigned,
+	       req_status_items[MT_VENDOR].entry->value,
+	       req_status_items[MT_PRODUCT].entry->value,
+	       req_status_items[MT_REVISION].entry->value,
+	       (req_status_items[MT_SERIAL_NUM].entry->value) ? 
+	       req_status_items[MT_SERIAL_NUM].entry->value : "none");
+	printf("---------------------------------\n");
+
+	/*
+	 * We check to see whether we're in fixed mode or not, and don't
+	 * just believe the blocksize.  If the SILI bit is turned on, the
+	 * blocksize will be set to 4, even though we're doing variable
+	 * length (well, multiples of 4) blocks.
+	 */
+	if (req_status_items[MT_FIXED_MODE].entry->value_signed == 0)
+		snprintf(block_str, sizeof(block_str), "variable");
+	else
+		snprintf(block_str, sizeof(block_str), "%s",
+		    getblksiz(req_status_items[
+			      MT_MEDIA_BLOCKSIZE].entry->value_unsigned));
+
+	dens_str = denstostring(req_status_items[
+	    MT_MEDIA_DENSITY].entry->value_unsigned);
+	if (dens_str == NULL)
+		dens_len = 0;
+	else
+		dens_len = strlen(dens_str);
+	field_width = MAX(dens_len, 17);
+	printf("Mode      %-*s    Blocksize      bpi      Compression\n"
+	       "Current:  %-*s    %-12s   %-7d  ",
+	       field_width, "Density", field_width, dens_str, block_str,
+	       mt_density_bp(req_status_items[
+	       MT_MEDIA_DENSITY].entry->value_unsigned, TRUE));
+
+	if (req_status_items[MT_COMPRESSION_SUPPORTED].entry->value_signed == 0)
+		printf("unsupported\n");
+	else if (req_status_items[
+		 MT_COMPRESSION_ENABLED].entry->value_signed == 0)
+		printf("disabled\n");
+	else {
+		printf("enabled (%s)\n",
+		       comptostring(req_status_items[
+		       MT_COMPRESSION_ALGORITHM].entry->value_unsigned));
+	}
+
+	dsreg = req_status_items[MT_DSREG].entry->value_signed;
+	if (dsreg != MTIO_DSREG_NIL) {
+		const char sfmt[] = "Current Driver State: %s.\n";
+		printf("---------------------------------\n");
+		const char *state_str;
+
+		state_str = get_driver_state_str(dsreg);
+		if (state_str == NULL) {
+			char foo[32];
+			(void) sprintf(foo, "Unknown state 0x%x", dsreg);
+			printf(sfmt, foo);
+		} else {
+			printf(sfmt, state_str);
+		}
+	}
+	resid = req_status_items[MT_RESID].entry->value_signed;
+	calculated_fileno = req_status_items[
+	    MT_CALCULATED_FILENO].entry->value_signed;
+	calculated_rel_blkno = req_status_items[
+	    MT_CALCULATED_REL_BLKNO].entry->value_signed;
+	rep_fileno = req_status_items[
+	    MT_REPORTED_FILENO].entry->value_signed;
+	rep_blkno = req_status_items[
+	    MT_REPORTED_BLKNO].entry->value_signed;
+	bop = req_status_items[MT_BOP].entry->value_signed;
+	eop = req_status_items[MT_EOP].entry->value_signed;
+	bpew = req_status_items[MT_BPEW].entry->value_signed;
+	partition = req_status_items[MT_PARTITION].entry->value_signed;
+
+	printf("---------------------------------\n");
+	printf("Partition: %3jd      Calc File Number: %3jd "
+	       "    Calc Record Number: %jd\n"
+	       "Residual:  %3jd  Reported File Number: %3jd "
+	       "Reported Record Number: %jd\n", partition, calculated_fileno,
+	       calculated_rel_blkno, resid, rep_fileno, rep_blkno);
+
+	printf("Flags: ");
+	if (bop > 0 || eop > 0 || bpew > 0) {
+		int need_comma = 0;
+
+		if (bop > 0) {
+			printf("BOP");
+			need_comma = 1;
+		}
+		if (eop > 0) {
+			if (need_comma != 0)
+				printf(",");
+			printf("EOP");
+			need_comma = 1;
+		}
+		if (bpew > 0) {
+			if (need_comma != 0)
+				printf(",");
+			printf("BPEW");
+			need_comma = 1;
+		}
+	} else {
+		printf("None");
+	}
+	printf("\n");
+	if (verbose != 0) {
+		printf("---------------------------------\n");
+		printf("Tape I/O parameters:\n");
+		for (i = MT_MAXIO; i <= MT_MAX_EFF_IOSIZE; i++) {
+			printf("  %s (%s): %ju bytes\n",
+			    req_status_items[i].entry->desc,
+			    req_status_items[i].name,
+			    req_status_items[i].entry->value_unsigned);
+		}
+	}
+
+	return (0);
+}
+
+int
+mt_xml_cmd(unsigned long cmd, int argc, char **argv, int mtfd, const char *tape)
+{
+	struct mt_status_data status_data;
+#if 0
+	struct mt_status_entry *entry;
+#endif
+	char *xml_str;
+	int retval;
+	unsigned long ioctl_cmd;
+
+	switch (cmd) {
+	case MT_CMD_PROTECT:
+	case MTIOCPARAMGET:
+		ioctl_cmd = MTIOCPARAMGET;
+		break;
+	default:
+		ioctl_cmd = MTIOCEXTGET;
+		break;
+	}
+
+	retval = mt_get_xml_str(mtfd, ioctl_cmd, &xml_str);
+	if (retval != 0)
+		err(1, "Couldn't get mt XML string");
+
+	retval = mt_get_status(xml_str, &status_data);
+	if (retval != XML_STATUS_OK) {
+		warn("Couldn't get mt status for %s", tape);
+		goto bailout;
+	}
+
+	/*
+	 * This gets set if there are memory allocation or other errors in
+	 * our parsing of the XML. 
+	 */
+	if (status_data.error != 0) {
+		warnx("%s", status_data.error_str);
+		retval = 1;
+		goto bailout;
+	}
+#if 0
+	STAILQ_FOREACH(entry, &status_data.entries, links)
+		mt_status_tree_print(entry, 0, NULL);
+#endif
+
+	switch (cmd) {
+	case MTIOCEXTGET:
+		retval = nstatus_print(argc, argv, xml_str, &status_data);
+		break;
+	case MTIOCPARAMGET:
+		retval = mt_param(argc, argv, mtfd, xml_str, &status_data);
+		break;
+	case MT_CMD_PROTECT:
+		retval = mt_protect(argc, argv, mtfd, &status_data);
+		break;
+	case MT_CMD_GETDENSITY:
+		retval = mt_getdensity(argc, argv, xml_str, &status_data);
+		break;
+	}
+
+bailout:
+	if (xml_str != NULL)
+		free(xml_str);
+
+	mt_status_free(&status_data);
+
+	return (retval);
+}
+
+static int
+mt_set_param(int mtfd, struct mt_status_data *status_data, char *param_name,
+    char *param_value)
+{
+	struct mt_status_entry *entry;
+	struct mtparamset param_set;
+
+	entry = mt_status_entry_find(status_data,
+	    __DECONST(char *, "mtparamget"));
+	if (entry == NULL)
+		errx(1, "Cannot find parameter root node");
+
+	bzero(&param_set, sizeof(param_set));
+	entry = mt_entry_find(entry, param_name);
+	if (entry == NULL)
+		errx(1, "Unknown parameter name \"%s\"", param_name);
+
+	strlcpy(param_set.value_name, param_name, sizeof(param_set.value_name));
+
+	switch (entry->var_type) {
+	case MT_TYPE_INT:
+		param_set.value.value_signed = strtoll(param_value, NULL, 0);
+		param_set.value_type = MT_PARAM_SET_SIGNED;
+		param_set.value_len = entry->size;
+		break;
+	case MT_TYPE_UINT:
+		param_set.value.value_unsigned = strtoull(param_value, NULL, 0);
+		param_set.value_type = MT_PARAM_SET_UNSIGNED;
+		param_set.value_len = entry->size;
+		break;
+	case MT_TYPE_STRING: {
+		size_t param_len;
+
+		param_len = strlen(param_value) + 1;
+		if (param_len > sizeof(param_set.value.value_fixed_str)) {
+			param_set.value_type = MT_PARAM_SET_VAR_STR;
+			param_set.value.value_var_str = param_value;
+		} else {
+			param_set.value_type = MT_PARAM_SET_FIXED_STR;
+			strlcpy(param_set.value.value_fixed_str, param_value,
+			    sizeof(param_set.value.value_fixed_str));
+		}
+		param_set.value_len = param_len;
+		break;
+	}
+	default:
+		errx(1, "Unknown parameter type %d for %s", entry->var_type,
+		    param_name);
+		break;
+	}
+
+	if (ioctl(mtfd, MTIOCPARAMSET, &param_set) == -1)
+		err(1, "MTIOCPARAMSET");
+
+	if (param_set.status != MT_PARAM_STATUS_OK)
+		errx(1, "Failed to set %s: %s", param_name,
+		    param_set.error_str);
+
+	return (0);
+}
+
+
+typedef enum {
+	MT_PP_LBP_R,
+	MT_PP_LBP_W,
+	MT_PP_RBDP,
+	MT_PP_PI_LENGTH,
+	MT_PP_PROT_METHOD
+} mt_protect_param;
+
+static struct mt_protect_info {
+	const char *name;
+	struct mt_status_entry *entry;
+	uint32_t value;
+} mt_protect_list[] = {
+	{ "lbp_r", NULL, 0 },
+	{ "lbp_w", NULL, 0 },
+	{ "rbdp", NULL, 0 },
+	{ "pi_length", NULL, 0 },
+	{ "prot_method", NULL, 0 }
+};
+
+#define	MT_NUM_PROTECT_PARAMS	(sizeof(mt_protect_list)/sizeof(mt_protect_list[0]))
+
+#define	MT_PROT_NAME	"protection"
+
+static int
+mt_protect(int argc, char **argv, int mtfd, struct mt_status_data *status_data)
+{
+	int retval = 0;
+	int do_enable = 0, do_disable = 0, do_list = 0;
+	int rbdp_set = 0, lbp_w_set = 0, lbp_r_set = 0;
+	int prot_method_set = 0, pi_length_set = 0;
+	int verbose = 0;
+	uint32_t rbdp = 0, lbp_w = 0, lbp_r = 0;
+	uint32_t prot_method = 0, pi_length = 0;
+	struct mt_status_entry *prot_entry, *supported_entry;
+	struct mt_status_entry *entry;
+	struct mtparamset params[MT_NUM_PROTECT_PARAMS];
+	struct mtsetlist param_list;
+	unsigned int i;
+	int c;
+
+	while ((c = getopt(argc, argv, "b:delL:m:r:vw:")) != -1) {
+		switch (c) {
+		case 'b':
+			rbdp_set = 1;
+			rbdp = strtoul(optarg, NULL, 0);
+			if ((rbdp != 0) && (rbdp != 1))
+				errx(1, "valid values for -b are 0 and 1");
 			break;
-		case MTIO_DSREG_LD:
-			printf(sfmt, "Loading");
+		case 'd':
+			do_disable = 1;
 			break;
+		case 'e':
+			do_enable = 1;
+			break;
+		case 'l':
+			do_list = 1;
+			break;
+		case 'L':
+			pi_length_set = 1;
+			pi_length = strtoul(optarg, NULL, 0);
+			if (pi_length > SA_CTRL_DP_PI_LENGTH_MASK)
+				errx(1, "PI length %u > maximum %u",
+				    pi_length, SA_CTRL_DP_PI_LENGTH_MASK);
+			break;
+		case 'm':
+			prot_method_set = 1;
+			prot_method = strtoul(optarg, NULL, 0);
+			if (prot_method > SA_CTRL_DP_METHOD_MAX)
+				errx(1, "Method %u > maximum %u",
+				    prot_method, SA_CTRL_DP_METHOD_MAX);
+			break;
+		case 'r':
+			lbp_r_set = 1;
+			lbp_r = strtoul(optarg, NULL, 0);
+			if ((lbp_r != 0) && (lbp_r != 1))
+				errx(1, "valid values for -r are 0 and 1");
+			break;
+		case 'v':
+			verbose = 1;
+			break;
+		case 'w':
+			lbp_w_set = 1;
+			lbp_w = strtoul(optarg, NULL, 0);
+			if ((lbp_w != 0) && (lbp_r != 1))
+				errx(1, "valid values for -r are 0 and 1");
+			break;
 		default:
-			(void) sprintf(foo, "Unknown state 0x%x", bp->mt_dsreg);
-			printf(sfmt, foo);
 			break;
 		}
 	}
-	if (bp->mt_resid == 0 && bp->mt_fileno == (daddr_t) -1 &&
-	    bp->mt_blkno == (daddr_t) -1)
-		return;
-	printf("---------------------------------\n");
-	printf("File Number: %d\tRecord Number: %d\tResidual Count %d\n",
-	    bp->mt_fileno, bp->mt_blkno, bp->mt_resid);
+
+	if ((rbdp_set + do_disable + do_enable + do_list + pi_length_set +
+	    prot_method_set + lbp_r_set + lbp_w_set) == 0)
+		errx(1, "Need an argument for protect");
+
+	if ((do_disable + do_enable + do_list) != 1)
+		errx(1, "You must specify only one of -e, -d or -l");
+
+	if (do_list != 0) {
+		retval = mt_protect_print(status_data, verbose);
+		goto bailout;
+	}
+	if (do_enable != 0) {
+		/*
+		 * Enable protection, but allow the user to override
+		 * settings if he doesn't want everything turned on.
+		 */
+		if (rbdp_set == 0)
+			rbdp = 1;
+		if (lbp_w_set == 0)
+			lbp_w = 1;
+		if (lbp_r_set == 0)
+			lbp_r = 1;
+		/*
+		 * If the user doesn't override it, we default to enabling
+		 * Reed-Solomon checkums.
+		 */
+		if (prot_method_set == 0)
+			prot_method = SA_CTRL_DP_REED_SOLOMON;
+		if (pi_length_set == 0)
+			pi_length = SA_CTRL_DP_RS_LENGTH;
+	} else if (do_disable != 0) {
+		/*
+		 * If the user wants to disable protection, we ignore any
+		 * other parameters he has set.  Everything gets set to 0.
+		 */
+		rbdp = lbp_w = lbp_r = 0;
+		prot_method = pi_length = 0;
+	}
+
+	prot_entry = mt_status_entry_find(status_data,
+	    __DECONST(char *, MT_PROT_NAME));
+	if (prot_entry == NULL)
+		errx(1, "Unable to find protection information status");
+
+	supported_entry = mt_entry_find(prot_entry,
+	    __DECONST(char *, "protection_supported"));
+	if (supported_entry == NULL)
+		errx(1, "Unable to find protection support information");
+
+	if (((supported_entry->var_type == MT_TYPE_INT)
+	  && (supported_entry->value_signed == 0))
+	 || ((supported_entry->var_type == MT_TYPE_UINT)
+	  && (supported_entry->value_unsigned == 0)))
+		errx(1, "This device does not support protection information");
+
+	mt_protect_list[MT_PP_LBP_R].value = lbp_r;
+	mt_protect_list[MT_PP_LBP_W].value = lbp_w;
+	mt_protect_list[MT_PP_RBDP].value = rbdp;
+	mt_protect_list[MT_PP_PI_LENGTH].value = pi_length;
+	mt_protect_list[MT_PP_PROT_METHOD].value = prot_method;
+
+	bzero(&params, sizeof(params));
+	bzero(&param_list, sizeof(param_list));
+
+	/*
+	 * Go through the list and make sure that we have this parameter,
+	 * and that it is still an unsigned integer.  If not, we've got a
+	 * problem.
+	 */
+	for (i = 0; i < MT_NUM_PROTECT_PARAMS; i++) {
+		entry = mt_entry_find(prot_entry,
+		    __DECONST(char *, mt_protect_list[i].name));
+		if (entry == NULL) {
+			errx(1, "Unable to find parameter %s",
+			    mt_protect_list[i].name);
+		}
+		mt_protect_list[i].entry = entry;
+
+		if (entry->var_type != MT_TYPE_UINT)
+			errx(1, "Parameter %s is type %d, not unsigned, "
+			    "cannot proceed", mt_protect_list[i].name,
+			    entry->var_type);
+		snprintf(params[i].value_name, sizeof(params[i].value_name),
+		    "%s.%s", MT_PROT_NAME, mt_protect_list[i].name);
+		/* XXX KDM unify types here */
+		params[i].value_type = MT_PARAM_SET_UNSIGNED;
+		params[i].value_len = sizeof(mt_protect_list[i].value);
+		params[i].value.value_unsigned = mt_protect_list[i].value;
+		
+	}
+	param_list.num_params = MT_NUM_PROTECT_PARAMS;
+	param_list.param_len = sizeof(params);
+	param_list.params = params;
+
+	if (ioctl(mtfd, MTIOCSETLIST, &param_list) == -1)
+		err(1, "error issuing MTIOCSETLIST ioctl");
+
+	for (i = 0; i < MT_NUM_PROTECT_PARAMS; i++) {
+		if (params[i].status != MT_PARAM_STATUS_OK) {
+			warnx("%s", params[i].error_str);
+			retval = 1;
+		}
+	}
+bailout:
+
+	return (retval);
 }
 
-void
+static int
+mt_param(int argc, char **argv, int mtfd, char *xml_str,
+	 struct mt_status_data *status_data)
+{
+	int list = 0, do_set = 0, xml_dump = 0;
+	char *param_name = NULL, *param_value = NULL;
+	int retval = 0, quiet = 0;
+	int c;
+
+	while ((c = getopt(argc, argv, "lp:qs:x")) != -1) {
+		switch (c) {
+		case 'l':
+			list = 1;
+			break;
+		case 'p':
+			if (param_name != NULL) {
+				warnx("Only one parameter name may be "
+				    "specified");
+				retval = 1;
+				goto bailout;
+			}
+			param_name = strdup(optarg);
+			break;
+		case 'q':
+			quiet = 1;
+			break;
+		case 's':
+			if (param_value != NULL) {
+				warnx("Only one parameter value may be "
+				    "specified");
+				retval = 1;
+				goto bailout;
+			}
+			param_value = strdup(optarg);
+			do_set = 1;
+			break;
+		case 'x':
+			xml_dump = 1;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if ((list + do_set + xml_dump) != 1) {
+		warnx("You must specify only one of -s, -l or -x");
+		retval = 1;
+		goto bailout;
+	}
+
+	if (xml_dump != 0) {
+		printf("%s", xml_str);
+		retval = 0;
+		goto bailout;
+	}
+
+	if (do_set != 0) {
+		if (param_name == NULL)
+			errx(1, "You must specify -p with -s");
+
+		retval = mt_set_param(mtfd, status_data, param_name,
+		    param_value);
+	} else if (list != 0)
+		retval = mt_param_list(status_data, param_name, quiet);
+
+bailout:
+	free(param_name);
+	free(param_value);
+	return (retval);
+}
+
+int
+mt_print_density_entry(struct mt_status_entry *density_root, int indent)
+{
+	struct mt_status_entry *entry;
+	int retval = 0;
+
+	STAILQ_FOREACH(entry, &density_root->child_entries, links) {
+		if (entry->var_type == MT_TYPE_NODE) {
+			retval = mt_print_density_entry(entry, indent + 2);
+			if (retval != 0)
+				break;
+			else
+				continue;
+		}
+		if ((strcmp(entry->entry_name, "primary_density_code") == 0)
+		 || (strcmp(entry->entry_name, "secondary_density_code") == 0)
+		 || (strcmp(entry->entry_name, "density_code") == 0)) {
+
+			printf("%*s%s (%s): %s\n", indent, "", entry->desc ?
+			    entry->desc : "", entry->entry_name,
+			    denstostring(entry->value_unsigned));
+		} else if (strcmp(entry->entry_name, "density_flags") == 0) {
+			printf("%*sMedium Access: ", indent, "");
+			if (entry->value_unsigned & MT_DENS_WRITE_OK) {
+				printf("Read and Write\n");
+			} else {
+				printf("Read Only\n");
+			}
+			printf("%*sDefault Density: %s\n", indent, "",
+			    (entry->value_unsigned & MT_DENS_DEFLT) ? "Yes" :
+			    "No");
+			printf("%*sDuplicate Density: %s\n", indent, "",
+			    (entry->value_unsigned & MT_DENS_DUP) ? "Yes" :
+			    "No");
+		} else if (strcmp(entry->entry_name, "media_width") == 0) {
+			printf("%*s%s (%s): %.1f mm\n", indent, "",
+			    entry->desc ?  entry->desc : "", entry->entry_name,
+			    (double)((double)entry->value_unsigned / 10));
+		} else if (strcmp(entry->entry_name, "medium_length") == 0) {
+			printf("%*s%s (%s): %ju m\n", indent, "",
+			    entry->desc ?  entry->desc : "", entry->entry_name,
+			    (uintmax_t)entry->value_unsigned);
+		} else if (strcmp(entry->entry_name, "capacity") == 0) {
+			printf("%*s%s (%s): %ju MB\n", indent, "", entry->desc ?
+			    entry->desc : "", entry->entry_name,
+			    (uintmax_t)entry->value_unsigned);
+		} else {
+			printf("%*s%s (%s): %s\n", indent, "", entry->desc ?
+			    entry->desc : "", entry->entry_name, entry->value);
+		}
+	}
+
+	return (retval);
+}
+
+int
+mt_print_density_report(struct mt_status_entry *report_root, int indent)
+{
+	struct mt_status_entry *mt_report, *media_report;
+	struct mt_status_entry *entry;
+	int retval = 0;
+
+	mt_report = mt_entry_find(report_root,
+	    __DECONST(char *, MT_MEDIUM_TYPE_REPORT_NAME));
+	if (mt_report == NULL)
+		return (1);
+
+	media_report = mt_entry_find(report_root,
+	    __DECONST(char *, MT_MEDIA_REPORT_NAME));
+	if (media_report == NULL)
+		return (1);
+
+	if ((mt_report->value_signed == 0)
+	 && (media_report->value_signed == 0)) {
+		printf("%*sThis tape drive supports the following "
+		    "media densities:\n", indent, "");
+	} else if ((mt_report->value_signed == 0)
+		&& (media_report->value_signed != 0)) {
+		printf("%*sThe tape currently in this drive supports "
+		    "the following media densities:\n", indent, "");
+	} else if ((mt_report->value_signed != 0)
+		&& (media_report->value_signed == 0)) {
+		printf("%*sThis tape drive supports the following "
+		    "media types:\n", indent, "");
+	} else {
+		printf("%*sThis tape currently in this drive supports "
+		    "the following media types:\n", indent, "");
+	}
+
+	STAILQ_FOREACH(entry, &report_root->child_entries, links) {
+		struct mt_status_nv *nv;
+
+		if (strcmp(entry->entry_name, MT_DENSITY_ENTRY_NAME) != 0)
+			continue;
+
+		STAILQ_FOREACH(nv, &entry->nv_list, links) {
+			if (strcmp(nv->name, "num") != 0)
+				continue;
+
+			break;
+		}
+
+		indent += 2;
+
+		printf("%*sDensity Entry", indent, "");
+		if (nv != NULL)
+			printf(" %s", nv->value);
+		printf(":\n");
+
+		retval = mt_print_density_entry(entry, indent + 2);
+
+		indent -= 2;
+	}
+
+	return (retval);
+}
+
+int
+mt_print_density(struct mt_status_entry *density_root, int indent)
+{
+	struct mt_status_entry *entry;
+	int retval = 0;
+
+	/*
+	 * We should have this entry for every tape drive.  This particular
+	 * value is reported via the mode page block header, not the
+	 * SCSI REPORT DENSITY SUPPORT command.
+	 */
+	entry = mt_entry_find(density_root,
+	    __DECONST(char *, MT_MEDIA_DENSITY_NAME));
+	if (entry == NULL)
+		errx(1, "Unable to find node %s", MT_MEDIA_DENSITY_NAME);
+	
+	printf("%*sCurrent density: %s\n", indent, "",
+	    denstostring(entry->value_unsigned));
+
+	/*
+	 * It isn't an error if we don't have any density reports.  Tape
+	 * drives that don't support the REPORT DENSITY SUPPORT command
+	 * won't have any; they will only have the current density entry
+	 * above.
+	 */
+	STAILQ_FOREACH(entry, &density_root->child_entries, links) {
+		if (strcmp(entry->entry_name, MT_DENSITY_REPORT_NAME) != 0)
+			continue;
+
+		retval = mt_print_density_report(entry, indent);
+	}
+
+	return (retval);
+}
+
+int
+mt_getdensity(int argc, char **argv, char *xml_str,
+    struct mt_status_data *status_data)
+{
+	int retval = 0;
+	int verbose = 0, xml_dump = 0;
+	struct mt_status_entry *density_root = NULL;
+	int c;
+
+	while ((c = getopt(argc, argv, "vx")) != -1) {
+		switch (c) {
+		case 'v':
+			verbose = 1;
+			break;
+		case 'x':
+			xml_dump = 1;
+			break;
+		}
+	}
+
+	if (xml_dump != 0) {
+		printf("%s", xml_str);
+		return (0);
+	}
+
+	density_root = mt_status_entry_find(status_data,
+	    __DECONST(char *, MT_DENSITY_ROOT_NAME));
+	if (density_root == NULL)
+		errx(1, "Cannot find density root node %s",
+		    MT_DENSITY_ROOT_NAME);
+
+	retval = mt_print_density(density_root, 0);
+
+	return (retval);
+}
+
+static void
 warn_eof(void)
 {
 	fprintf(stderr,



More information about the Midnightbsd-cvs mailing list