#!/usr/bin/perl -w
#
# (c) 2001-2005  Rudolf Cejka <cejkar@fit.vutbr.cz>
#
# $Id: lto3,v 0.30 2005/07/15 10:39:17 root Exp root $
#

use strict;
use Math::BigInt;
use Getopt::Std;

sub DEFAULTDEV () { "sa0.ctl" }

sub CAMCONTROL () { "/sbin/camcontrol" }

sub TRUE () { 1 }
sub FALSE () { 0 }

sub LOG_SENSE_10 () { 1 }
sub MODE_SENSE_6 () { 2 }

my $prog;
($prog = $0) =~ s/.*\///;

sub usage()
{
	printf STDERR <<"END", $prog, DEFAULTDEV;
Usage: %s [-d device] [-p pages|-r|-R]
    -d device   Queried device (default "%s")
    -p pages    Print Supported Pages (p), Write Error Counters (w), Read
                Error Counters (r), Sequential Access Device Log (q),
                Temperature (t), DTD Status (d), Protocol Specific (x), Tape
                Alert Log (a), Tape Usage Log (u), Tape Capacity Log (z),
                Data Compression Log (c), Performance Log (f), Device Status
                Log (s) (default: pwrqtdxauzcfs - all)
    -R          Reset logs on the drive
END
}

sub error($)
{
	die "$prog: $_[0]\n";
}

sub encode_shell($)
{
	my $str = $_[0];
	if (defined($str)) {
		$str =~ s/([`"\$\\])/\\$1/g;
	} else {
		$str = "";
	}
	return "\"" . $str . "\"";
}

sub cdb_mode_sense_6($$$)
{
	my ($pagecode, $pc, $size) = @_;
	return sprintf "1a 8 %x 0 %x 0",
	    (($pc & 0x03) << 6) | ($pagecode & 0x3F),
	    $size & 0xFF;
}

sub cdb_log_sense_10($$$)
{
	my ($pagecode, $pc, $size) = @_;
	return sprintf "4d 0 %x 0 0 0 0 %x %x 0",
	    (($pc & 0x03) << 6) | ($pagecode & 0x3F),
	    ($size >> 8) & 0xFF, $size & 0xFF;
}

sub cdb_parameter_reset10()
{
	return "4c 2 c0 0 0 0 0 0 0 0";
}

sub WD () { 32 }	# Item width: C => 8, n => 16, N => 32
sub BL () { 512 }	# Block read size

sub call($)
{
	my $r = "";
	open(FH, $_[0] . " |")
	    or error("open(): Execute error");
	while (read(FH, $r, BL, length($r))) { }
	close(FH)
	    or error("close(): Execute error");
	return unpack("N*", $r);
}

sub query($$$$)
{
	my ($device, $type, $pagecode, $pc) = @_;
	my ($cdb, $size);
	if ($type eq LOG_SENSE_10) {
		$size = 511;
		$cdb = cdb_log_sense_10($pagecode, $pc, $size);
	} elsif ($type eq MODE_SENSE_6) {
		$size = 255;
		$cdb = cdb_mode_sense_6($pagecode, $pc, $size);
	} else {
		return ();
	}
	return call(CAMCONTROL . " cmd " . encode_shell($device)
	    . " -v -c \"" . $cdb . "\" -i " . $size . " -");
}

sub parameter_reset($)
{
	my ($device) = @_;
	call(CAMCONTROL . " cmd " . encode_shell($device)
	    . " -v -c \"" . cdb_parameter_reset10() . "\"");
}

sub get_mask($)
{
	return (($_[0] < WD) ? 1 << $_[0] : 0) - 1;
}

sub get_bits($$$$)
{
	my ($data, $ofs, $len, $signed) = @_;
	my ($i, $j, $f, $l, $r, $tmp);
	
	if ($ofs < 0 || $len < 0 || $len > 64
	    || $ofs + $len > scalar(@$data) * WD) {
		return undef;
	}
	$f = TRUE;
	while ($len > 0) {
		$l = WD - ($ofs % WD);
		if ($l > $len) {
			$r = $l - $len;
			$l = $len;
		} else {
			$r = 0;
		}
		if ($f) {
			$i = new Math::BigInt(($signed && ((@$data[$ofs / WD]
			    >> ($r + $l - 1)) & 1) != 0) ? -1 : 0);
			$f = FALSE;
		}
		$i *= 2 ** $l;
		$tmp = (@$data[$ofs / WD] >> $r) & get_mask($l);
		$i += $tmp;
		$len -= $l;
		$ofs += $l;
	}
	($i = "$i") =~ s/^\+//;
	return $i;
}

sub init_value($)
{
	my ($pos) = @_;
	$$pos = 4;
}

sub get_value($$$$@)
{
	my ($data, $pos, $code, $signed, @items) = @_;
	my ($p, $c, $l, $i, $s, @r);
	$p = $$pos * 8;
	$c = get_bits($data, $p, 16, FALSE);
	$l = get_bits($data, $p + 24, 8, FALSE);
	$p += 32;
	@r = ();
	$s = 0;
	if (scalar(@items) < 1) {
		@items = ($l);
	}
	while (defined($i = shift(@items))) {
		if ($code == -1) {
			push(@r, $c, $i, get_bits($data, $p, $i * 8, $signed));
		} elsif ($code == $c) {
			push(@r, $i, get_bits($data, $p, $i * 8, $signed));
		} else {
			push(@r, $i, "Unexpected code $c instead of $code");
		}
		$p += $i * 8;
		$s += $i;
	}
	if ($s != $l) {
		$i = ($code == -1) ? 2 : 1;
		while ($i < scalar(@r)) {
			$r[$i] = "Unexpected length $s instead of $l";
			$i += ($code == -1) ? 3 : 2;
		}
	}
	$$pos += $l + 4;
	return @r;
}

sub get_log_sense_page_code($)
{
	my ($data) = @_;
	return get_bits($data, 2, 6, FALSE);
}

sub get_mode_sense_page_code($)
{
	my ($data) = @_;
	return get_bits($data, 34, 6, FALSE);
}

sub get_log_sense_length($)
{
	my ($data) = @_;
	my $l_data = get_bits($data, 16, 16, FALSE);
	if (defined($l_data)) {
		return 4 + $l_data;
	} else {
		return 0;
	}
}

sub get_mode_sense_length($)
{
	my ($data) = @_;
	my $l_data = get_bits($data, 0, 8, FALSE);
	if (defined($l_data)) {
		return 1 + $l_data;
	} else {
		return 0;
	}
}

sub print_log_sense_binary(@)
{
	my ($l_data, $n, $c, $b);
	printf "\nBinary dump (%02Xh):\n",
	    get_log_sense_page_code(\@_);
	print "=" x 40 . "\n\n";
	$l_data = get_log_sense_length(\@_);
	$c = 0;
	for ($n = 0; $n < $l_data; $n++) {
		$b = get_bits(\@_, $n * 8, 8, FALSE);
		printf "%s%02x",
		    ($c == 0) ? "" : (($c % 8 == 0) ? "\n" : " "), $b;
		$c++;
	}
	if ($c > 0) {
		print "\n";
	}
}

sub print_mode_sense_binary(@)
{
	my ($l_data, $n, $c, $b);
	printf "\nBinary dump (%02Xh):\n",
	    get_mode_sense_page_code(\@_);
	print "=" x 40 . "\n\n";
	$l_data = get_mode_sense_length(\@_);
	$c = 0;
	for ($n = 0; $n < $l_data; $n++) {
		$b = get_bits(\@_, $n * 8, 8, FALSE);
		printf "%s%02x",
		    ($c == 0) ? "" : (($c % 8 == 0) ? "\n" : " "), $b;
		$c++;
	}
	if ($c > 0) {
		print "\n";
	}
}

sub get_pages(@)
{
	my ($l_data, $n, @result);
	$l_data = get_log_sense_length(\@_);
	for ($n = 4; $n < $l_data; $n++) {
		push(@result, get_bits(\@_, $n * 8, 8, FALSE));
	}
	return @result;
}

sub TABALIGN () { 6 };

sub printa($$@)
{
	my ($fmt1, $fmt2, @args) = @_;
	my ($s, $t);
	$s = length(sprintf($fmt1, @args));
	$t = ($s >= 8 * TABALIGN) ? " " : "\t" x ((8 * TABALIGN - $s + 7) / 8);
	printf $fmt1 . $t . $fmt2 . "\n", @args;
}

sub print_log_sense_supported_pages(@)
{
	my ($c, $s);
	printf "\nSupported Pages Page (%02Xh)\n",
	    get_log_sense_page_code(\@_);
	print "=" x 40 . "\n\n";
	$s = "";
	for ($c = 0; $c < scalar(@_); $c++) {
		$s .= sprintf "%s%02Xh", ($c == 0) ? ""
		    : (($c % 8 == 0) ? "\n" . "\t" x TABALIGN : " "), $_[$c];
	}
	printa("Supported pages (%d):", "%s", scalar(@_), $s);
}

sub print_log_sense_write_error_counters(@)
{
	my ($p);
	printf "\nWrite Error Counters Page (%02Xh)\n",
	    get_log_sense_page_code(\@_);
	print "=" x 40 . "\n\n";
	init_value(\$p);
	printa("Errors Corrected Without Substantial Delay (%d):", "%s",
	    get_value(\@_, \$p, 0, FALSE));
	printa("Errors Corrected With Possible Delays (%d):", "%s",
	    get_value(\@_, \$p, 1, FALSE));
	printa("Total Corrected And Uncorrected Errors (%d):", "%s",
	    get_value(\@_, \$p, 2, FALSE));
	printa("Total Corrected Errors (%d):", "%s",
	    get_value(\@_, \$p, 3, FALSE));
	printa("Total Times Error Correction Processed (%d):", "%s",
	    get_value(\@_, \$p, 4, FALSE));
	printa("Total Data Sets Processed (%d):", "%s",
	    get_value(\@_, \$p, 5, FALSE));
	printa("Total Uncorrected Errors (%d):", "%s",
	    get_value(\@_, \$p, 6, FALSE));
}

sub print_log_sense_read_error_counters(@)
{
	my ($p);
	printf "\nRead Error Counters Page (%02Xh)\n",
	    get_log_sense_page_code(\@_);
	print "=" x 40 . "\n\n";
	init_value(\$p);
	printa("Errors Corrected Without Substantial Delay (%d):", "%s",
	    get_value(\@_, \$p, 0, FALSE));
	printa("Errors Corrected With Possible Delays (%d):", "%s",
	    get_value(\@_, \$p, 1, FALSE));
	printa("Total Corrected And Uncorrected Errors (%d):", "%s",
	    get_value(\@_, \$p, 2, FALSE));
	printa("Total Corrected Errors (%d):", "%s",
	    get_value(\@_, \$p, 3, FALSE));
	printa("Total Times Error Correction Processed (%d):", "%s",
	    get_value(\@_, \$p, 4, FALSE));
	printa("Total Data Sets Processed (%d):", "%s",
	    get_value(\@_, \$p, 5, FALSE));
	printa("Total Uncorrected Errors (%d):", "%s",
	    get_value(\@_, \$p, 6, FALSE));
}

sub print_log_sense_sequential_access_device_log(@)
{
	my ($p);
	printf "\nSequential Access Device Log Page (%02Xh)\n",
	    get_log_sense_page_code(\@_);
	print "=" x 40 . "\n\n";
	init_value(\$p);
	# SCSI Reference Bug: There are 0/1/2/3/256 values instead of
	# 1/2/3/4/256.
	printa("Written Over SCSI Before Compression (%d):", "%s bytes",
	    get_value(\@_, \$p, 0, FALSE));
	printa("Written To Media After Compression (%d):", "%s bytes",
	    get_value(\@_, \$p, 1, FALSE));
	printa("Readed From Media Before Decompression (%d):", "%s bytes",
	    get_value(\@_, \$p, 2, FALSE));
	printa("Readed Over SCSI After Decompression (%d):", "%s bytes",
	    get_value(\@_, \$p, 3, FALSE));
	printa("Cleaning Required (%d):", "%s",
	    get_value(\@_, \$p, 256, FALSE));
}

sub print_log_sense_temperature(@)
{
	my ($p, @x);
	printf "\nTemperature Page (%02Xh)\n",
	    get_log_sense_page_code(\@_);
	print "=" x 40 . "\n\n";
	init_value(\$p);
	printa("Current Temperature (%d):", "%s *C",
	    get_value(\@_, \$p, 0, FALSE));
	if ((@x = get_value(\@_, \$p, 1, FALSE))[1] ne "255") {
		printa("Maximum Temperature (%d):", "%s *C", @x);
	}
}

my %tms = (
    "0" => "No Tape Motion",
    "1" => "Cleaning Operation In Progress",
    "2" => "Tape Being Loaded",
    "3" => "Tape Being Unloaded",
    "4" => "Tape In Motion",
    "5" => "Reading",
    "6" => "Writing",
    "7" => "Locating",
    "8" => "Rewinding"
);

sub print_log_sense_dtd_status(@)
{
	my ($p, @x, @y);
	printf "\nDTD Status Page (%02Xh)\n",
	    get_log_sense_page_code(\@_);
	print "=" x 40 . "\n\n";
	init_value(\$p);
	printa("Very High Frequency Data (%d):", "0x%08X",
	    @x = get_value(\@_, \$p, 0, FALSE));
	@y = $x[1];
	printa("    Host Initiated Unload (1b):", "%s",
	    get_bits(\@y, 1, 1, FALSE));
	printa("    MAM Accessible (1b):", "%s",
	    get_bits(\@y, 2, 1, FALSE));
	printa("    Compression Enabled (1b):", "%s",
	    get_bits(\@y, 3, 1, FALSE));
	printa("    Media Is Write-Protected (1b):", "%s",
	    get_bits(\@y, 4, 1, FALSE));
	printa("    Clean Requsted (1b):", "%s",
	    get_bits(\@y, 5, 1, FALSE));
	printa("    Cleaning Required (1b):", "%s",
	    get_bits(\@y, 6, 1, FALSE));
	printa("    DTD Initialized (1b):", "%s",
	    get_bits(\@y, 7, 1, FALSE));
	printa("    In Transition (1b):", "%s",
	    get_bits(\@y, 8, 1, FALSE));
	printa("    Robotic Access Allowed (1b):", "%s",
	    get_bits(\@y, 10, 1, FALSE));
	printa("    Media Present (1b):", "%s",
	    get_bits(\@y, 11, 1, FALSE));
	printa("    Media Seated (1b):", "%s",
	    get_bits(\@y, 13, 1, FALSE));
	printa("    Media Threaded (1b):", "%s",
	    get_bits(\@y, 14, 1, FALSE));
	printa("    Data Accessible (1b):", "%s",
	    get_bits(\@y, 15, 1, FALSE));
	printa("    Tape Motion Status (8b):", "%s",
	    defined($tms{get_bits(\@y, 16, 8, FALSE)})
	    ? $tms{get_bits(\@y, 16, 8, FALSE)} : "Unknown");
	printa("    Recovery Requested (1b):", "%s",
	    get_bits(\@y, 29, 1, FALSE));
	printa("    Interface Changed (1b):", "%s",
	    get_bits(\@y, 30, 1, FALSE));
	printa("    Tape Alert Flag Changed (1b):", "%s",
	    get_bits(\@y, 31, 1, FALSE));
	printa("Very High Frequency Polling Delay (%d):", "%s ms",
	    get_value(\@_, \$p, 1, FALSE));
	printa("Parallel SCSI (%d):", "0x%08X",
	    @x = get_value(\@_, \$p, 257, FALSE));
	@y = $x[1];
	printa("    Current Bus Mode (2b):", "%s",
	    get_bits(\@y, 5, 2, FALSE));
	printa("    Most Recent Transfer Period Factor (8b):", "%s",
	    get_bits(\@y, 16, 8, FALSE));
	printa("    Current SCSI Address (8b):", "%s",
	    get_bits(\@y, 24, 8, FALSE));
}

sub print_log_sense_protocol_specific(@)
{
	my ($p);
	printf "\nProtocol Specific Page (%02Xh)\n",
	    get_log_sense_page_code(\@_);
	print "=" x 40 . "\n\n";
	init_value(\$p);
}

sub print_log_sense_tape_alert_log(@)
{
	my ($p, $i, @x);
	printf "\nTape Alert Log Page (%02Xh)\n",
	    get_log_sense_page_code(\@_);
	print "=" x 40 . "\n\n";
	init_value(\$p);
	printa("Warning - Read Warning (%d):", "%s",
	    get_value(\@_, \$p, 1, FALSE));
	printa("Warning - Write Warning (%d):", "%s",
	    get_value(\@_, \$p, 2, FALSE));
	printa("Warning - Hard Error (%d):", "%s",
	    get_value(\@_, \$p, 3, FALSE));
	printa("Critical - Media (%d):", "%s",
	    get_value(\@_, \$p, 4, FALSE));
	printa("Critical - Read Failure (%d):", "%s",
	    get_value(\@_, \$p, 5, FALSE));
	printa("Critical - Write Failure (%d):", "%s",
	    get_value(\@_, \$p, 6, FALSE));
	printa("Warning - Media Life (%d):", "%s",
	    get_value(\@_, \$p, 7, FALSE));
	printa("Warning - Not Data Grade (%d):", "%s",
	    get_value(\@_, \$p, 8, FALSE));
	printa("Critical - Write Protect (%d):", "%s",
	    get_value(\@_, \$p, 9, FALSE));
	printa("Information - No Removal (%d):", "%s",
	    get_value(\@_, \$p, 10, FALSE));
	printa("Information - Cleaning Media(%d):", "%s",
	    get_value(\@_, \$p, 11, FALSE));
	printa("Information - Unsupported Format (%d):", "%s",
	    get_value(\@_, \$p, 12, FALSE));
	printa("Critical - Recoverable Snapped Tape (%d):", "%s",
	    get_value(\@_, \$p, 13, FALSE));
	printa("Critical - Unrecoverable Snapped Tape (%d):", "%s",
	    get_value(\@_, \$p, 14, FALSE));
	printa("Warning - Memory in Cartridge Failure (%d):", "%s",
	    get_value(\@_, \$p, 15, FALSE));
	printa("Critical - Forced Eject (%d):", "%s",
	    get_value(\@_, \$p, 16, FALSE));
	printa("Warning - Read-Only Format (%d):", "%s",
	    get_value(\@_, \$p, 17, FALSE));
	printa("Warning - Tape Directory Corrupted (%d):", "%s",
	    get_value(\@_, \$p, 18, FALSE));
	printa("Information - Nearing Media Life (%d):", "%s",
	    get_value(\@_, \$p, 19, FALSE));
	printa("Critical - Clean Now (%d):", "%s",
	    get_value(\@_, \$p, 20, FALSE));
	printa("Warning - Clean Periodic (%d):", "%s",
	    get_value(\@_, \$p, 21, FALSE));
	printa("Critical - Expired Cleaning Media (%d):", "%s",
	    get_value(\@_, \$p, 22, FALSE));
	printa("Critical - Invalid Cleaning Cartridge (%d):", "%s",
	    get_value(\@_, \$p, 23, FALSE));
	printa("Warning - Retension Requested (%d):", "%s",
	    get_value(\@_, \$p, 24, FALSE));
	printa("Warning - Dual-port Interface Error (%d):", "%s",
	    get_value(\@_, \$p, 25, FALSE));
	printa("Warning - Cooling Fan Failure (%d):", "%s",
	    get_value(\@_, \$p, 26, FALSE));
	printa("Warning - Power Supply Failure (%d):", "%s",
	    get_value(\@_, \$p, 27, FALSE));
	printa("Warning - Power Consumption (%d):", "%s",
	    get_value(\@_, \$p, 28, FALSE));
	printa("Warning - Drive Maintenance (%d):", "%s",
	    get_value(\@_, \$p, 29, FALSE));
	printa("Critical - Hardware A (%d):", "%s",
	    get_value(\@_, \$p, 30, FALSE));
	printa("Critical - Hardware B (%d):", "%s",
	    get_value(\@_, \$p, 31, FALSE));
	printa("Warning - Interface (%d):", "%s",
	    get_value(\@_, \$p, 32, FALSE));
	printa("Critical - Eject Media (%d):", "%s",
	    get_value(\@_, \$p, 33, FALSE));
	printa("Warning - Download Fault (%d):", "%s",
	    get_value(\@_, \$p, 34, FALSE));
	printa("Warning - Drive Humidity (%d):", "%s",
	    get_value(\@_, \$p, 35, FALSE));
	printa("Warning - Drive Temperature (%d):", "%s",
	    get_value(\@_, \$p, 36, FALSE));
	printa("Warning - Drive Voltage (%d):", "%s",
	    get_value(\@_, \$p, 37, FALSE));
	printa("Critical - Predictive Failure (%d):", "%s",
	    get_value(\@_, \$p, 38, FALSE));
	printa("Warning - Diagnostics Required (%d):", "%s",
	    get_value(\@_, \$p, 39, FALSE));
	foreach $i (40 .. 49) {
		if ((@x = get_value(\@_, \$p, $i, FALSE))[1] ne "0") {
			printa("Reserved (%d):", "%s", @x);
		}
	}
	printa("Warning - Lost Statistics (%d):", "%s",
	    get_value(\@_, \$p, 50, FALSE));
	printa("Warning - Tape Directory Invalid at Unload (%d):", "%s",
	    get_value(\@_, \$p, 51, FALSE));
	printa("Critical - Tape System Area Write Failure (%d):", "%s",
	    get_value(\@_, \$p, 52, FALSE));
	printa("Critical - Tape System Area Read Failure (%d):", "%s",
	    get_value(\@_, \$p, 53, FALSE));
	printa("Critical - No Start of Data (%d):", "%s",
	    get_value(\@_, \$p, 54, FALSE));
	printa("Critical - Loading Failure (%d):", "%s",
	    get_value(\@_, \$p, 55, FALSE));
	printa("Critical - Unrecoverable Load Failure (%d):", "%s",
	    get_value(\@_, \$p, 56, FALSE));
	printa("Critical - Automation Interface Failure (%d):", "%s",
	    get_value(\@_, \$p, 57, FALSE));
	printa("Warning - Firmware Failure (%d):", "%s",
	    get_value(\@_, \$p, 58, FALSE));
	printa("Warning - WORM - Integrity Check Failed (%d):", "%s",
	    get_value(\@_, \$p, 59, FALSE));
	printa("Warning - WORM - Overwrite Attempted (%d):", "%s",
	    get_value(\@_, \$p, 60, FALSE));
	foreach $i (61 .. 64) {
		if ((@x = get_value(\@_, \$p, $i, FALSE))[1] ne "0") {
			printa("Reserved (%d):", "%s", @x);
		}
	}
}

sub print_log_sense_tape_usage_log(@)
{
	my ($p);
	printf "\nTape Usage Log Page (%02Xh)\n",
	    get_log_sense_page_code(\@_);
	print "=" x 40 . "\n\n";
	init_value(\$p);
	printa("Thread Count (%d):", "%s",
	    get_value(\@_, \$p, 1, FALSE));
	printa("Total Data Sets Written (%d):", "%s",
	    get_value(\@_, \$p, 2, FALSE));
	printa("Total Write Retries (%d):", "%s",
	    get_value(\@_, \$p, 3, FALSE));
	printa("Total Unrecovered Write Errors (%d):", "%s",
	    get_value(\@_, \$p, 4, FALSE));
	printa("Total Suspended Writes (%d):", "%s",
	    get_value(\@_, \$p, 5, FALSE));
	printa("Total Fatal Suspended Writes (%d):", "%s",
	    get_value(\@_, \$p, 6, FALSE));
	printa("Total Data Sets Read (%d):", "%s",
	    get_value(\@_, \$p, 7, FALSE));
	printa("Total Read Retries (%d):", "%s",
	    get_value(\@_, \$p, 8, FALSE));
	printa("Total Unrecovered Read Errors (%d):", "%s",
	    get_value(\@_, \$p, 9, FALSE));
}

sub print_log_sense_tape_capacity_log(@)
{
	my ($p);
	printf "\nTape Capacity Log Page (%02Xh)\n",
	    get_log_sense_page_code(\@_);
	print "=" x 40 . "\n\n";
	init_value(\$p);
	printa("Main Partition Remaining Capacity (%d):", "%s MB",
	    get_value(\@_, \$p, 1, FALSE));
	printa("Alt. Partition Remaining Capacity (%d):", "%s MB",
	    get_value(\@_, \$p, 2, FALSE));
	printa("Main Partition Maximum Capacity (%d):", "%s MB",
	    get_value(\@_, \$p, 3, FALSE));
	printa("Alt. Partition Maximum Capacity (%d):", "%s MB",
	    get_value(\@_, \$p, 4, FALSE));
}

sub print_log_sense_data_compression_log(@)
{
	my ($p);
	printf "\nData Compression Log Page (%02Xh)\n",
	    get_log_sense_page_code(\@_);
	print "=" x 40 . "\n\n";
	init_value(\$p);
	printa("Read Compression Ratio x 100 (%d):", "%s",
	    get_value(\@_, \$p, 0, FALSE));
	printa("Write Compression Ratio x 100 (%d):", "%s",
	    get_value(\@_, \$p, 1, FALSE));
	printa("Megabytes Transferred To Host (%d):", "%s",
	    get_value(\@_, \$p, 2, FALSE));
	printa("Bytes Transferred To Host (%d):", "%s",
	    get_value(\@_, \$p, 3, TRUE));
	printa("Megabytes Read From Tape (%d):", "%s",
	    get_value(\@_, \$p, 4, FALSE));
	printa("Bytes Read From Tape (%d):", "%s",
	    get_value(\@_, \$p, 5, TRUE));
	printa("Megabytes Transferred From Host (%d):", "%s",
	    get_value(\@_, \$p, 6, FALSE));
	printa("Bytes Transferred From Host (%d):", "%s",
	    get_value(\@_, \$p, 7, TRUE));
	printa("Megabytes Written To Tape (%d):", "%s",
	    get_value(\@_, \$p, 8, FALSE));
	printa("Bytes Written To Tape (%d):", "%s",
	    get_value(\@_, \$p, 9, TRUE));
}

sub print_log_sense_performance_log(@)
{
	my ($p);
	printf "\nPerformance Log Page (%02Xh)\n",
	    get_log_sense_page_code(\@_);
	print "=" x 40 . "\n\n";
	init_value(\$p);
	printa("Repositions Per 100 MB (%d):", "%s",
	    get_value(\@_, \$p, 0, FALSE));
	printa("Data Rate Into Buffer (%d):", "%s * 100 KB/s",
	    get_value(\@_, \$p, 1, FALSE));
	printa("Maximum Data Rate (%d):", "%s * 100 KB/s",
	    get_value(\@_, \$p, 2, FALSE));
	printa("Current Data Rate (%d):", "%s * 100 KB/s",
	    get_value(\@_, \$p, 3, FALSE));
	printa("Native Data Rate (%d):", "%s * 100 KB/s",
	    get_value(\@_, \$p, 4, FALSE));
}

my %ds = (
    "1" => "OK",
    "2" => "Degraded",
    "3" => "Failed"
);

sub print_log_sense_device_status_log(@)
{
	my ($p, @x, @y);
	printf "\nDevice Status Log Page (%02Xh)\n",
	    get_log_sense_page_code(\@_);
	print "=" x 40 . "\n\n";
	init_value(\$p);
	if ((@x = get_value(\@_, \$p, 0, FALSE))[1] ne "0") {
		printa("Device Type (%d):", "%s", @x);
	}
	printa("Device Status Bits (%d):", "%s",
	    @x = get_value(\@_, \$p, 1, FALSE));
	@y = $x[1];
	printa("    Cleaning Required Flag (1b):", "%s",
	    get_bits(\@y, 5, 1, FALSE));
	printa("    Cleaning Requested Flag (1b):", "%s",
	    get_bits(\@y, 6, 1, FALSE));
	printa("    Exhausted Cleaning Tape Flag (1b):", "%s",
	    get_bits(\@y, 7, 1, FALSE));
	printa("    Temperature (2b):", "%s",
	    defined($ds{get_bits(\@y, 12, 2, FALSE)})
	    ? $ds{get_bits(\@y, 12, 2, FALSE)} : "Unknown");
	printa("    Device Status (2b):", "%s",
	    defined($ds{get_bits(\@y, 14, 2, FALSE)})
	    ? $ds{get_bits(\@y, 14, 2, FALSE)} : "Unknown");
	printa("    Medium Status (2b):", "%s",
	    defined($ds{get_bits(\@y, 22, 2, FALSE)})
	    ? $ds{get_bits(\@y, 22, 2, FALSE)} : "Unknown");
	printa("Total Number Of Loads (%d):", "%s",
	    get_value(\@_, \$p, 2, FALSE));
	printa("Cleaning Cartridge Status (%d):", "%s",
	    get_value(\@_, \$p, 3, FALSE));
	printa("Product Number (%d):", "%s",
	    get_value(\@_, \$p, 4, FALSE));
}

sub is($$)
{
	my ($optlist, $opt) = @_;
	return !defined($optlist) || index($optlist, $opt) >= 0;
}

# main()

my (@pages, $dev, %o);

if (!getopts("?hd:p:R", \%o)) {
	usage();
	exit;
}

$dev = (defined($o{"d"})) ? $o{"d"} : DEFAULTDEV;

if (defined($o{"?"}) || defined($o{"h"})) {
	usage();
	exit;
} elsif (defined($o{"R"})) {
	parameter_reset($dev);
	exit;
}

@pages = get_pages(query($dev, LOG_SENSE_10, 0, 1));
if (is($o{"p"}, "p")) {
	print_log_sense_supported_pages(@pages);
}
if (grep($_ == 2, @pages) > 0 && is($o{"p"}, "w")) {
	print_log_sense_write_error_counters(query($dev, LOG_SENSE_10, 2, 1));
}
if (grep($_ == 3, @pages) > 0 && is($o{"p"}, "r")) {
	print_log_sense_read_error_counters(query($dev, LOG_SENSE_10, 3, 1));
}
if (grep($_ == 12, @pages) > 0 && is($o{"p"}, "q")) {
	print_log_sense_sequential_access_device_log(query($dev,
	    LOG_SENSE_10, 12, 1));
}
if (grep($_ == 13, @pages) > 0 && is($o{"p"}, "t")) {
	print_log_sense_temperature(query($dev, LOG_SENSE_10, 13, 1));
}
if (grep($_ == 17, @pages) > 0 && is($o{"p"}, "d")) {
	print_log_sense_dtd_status(query($dev, LOG_SENSE_10, 17, 1));
}
if (grep($_ == 24, @pages) > 0 && is($o{"p"}, "x")) {
	print_log_sense_protocol_specific(query($dev, LOG_SENSE_10, 24, 1));
}
if (grep($_ == 46, @pages) > 0 && is($o{"p"}, "a")) {
	print_log_sense_tape_alert_log(query($dev, LOG_SENSE_10, 46, 1));
}
if (grep($_ == 48, @pages) > 0 && is($o{"p"}, "u")) {
	print_log_sense_tape_usage_log(query($dev, LOG_SENSE_10, 48, 1));
}
if (grep($_ == 49, @pages) > 0 && is($o{"p"}, "z")) {
	print_log_sense_tape_capacity_log(query($dev, LOG_SENSE_10, 49, 1));
}
if (grep($_ == 50, @pages) > 0 && is($o{"p"}, "c")) {
	print_log_sense_data_compression_log(query($dev, LOG_SENSE_10, 50, 1));
}
if (grep($_ == 52, @pages) > 0 && is($o{"p"}, "f")) {
	print_log_sense_performance_log(query($dev, LOG_SENSE_10, 52, 1));
}
if (grep($_ == 62, @pages) > 0 && is($o{"p"}, "s")) {
	print_log_sense_device_status_log(query($dev, LOG_SENSE_10, 62, 1));
}
print "\n";

