aboutsummaryrefslogtreecommitdiff
path: root/archive
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2007-06-27 03:09:47 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2007-06-27 03:09:47 +0000
commitd0771aa6e58b5592a554f27b46f5a3ddee4866e0 (patch)
treee4b8a15319bfa55c263a41e5a0b2bb1bcb393c56 /archive
parente459cae20b2c98bf198995f8af9245f517225a80 (diff)
downloadsg3_utils-d0771aa6e58b5592a554f27b46f5a3ddee4866e0.tar.gz
Load sg3_utils-1.14 into trunk/.
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@53 6180dd3e-e324-4e3e-922d-17de1ae2f315
Diffstat (limited to 'archive')
-rw-r--r--archive/llseek.h6
-rw-r--r--archive/sdparm.8255
-rw-r--r--archive/sdparm.c1501
-rw-r--r--archive/sg3_utils.spec113234
4 files changed, 1995 insertions, 1 deletions
diff --git a/archive/llseek.h b/archive/llseek.h
index cdf6fcea..2f6aef0f 100644
--- a/archive/llseek.h
+++ b/archive/llseek.h
@@ -1,3 +1,5 @@
+#ifndef LLSEEK_H
+#define LLSEEK_H
#if defined(__GNUC__) || defined(HAS_LONG_LONG)
typedef long long llse_loff_t;
@@ -7,4 +9,6 @@ typedef long llse_loff_t;
extern llse_loff_t llse_llseek(unsigned int fd,
llse_loff_t offset,
- unsigned int origin);
+ unsigned int origin);
+
+#endif
diff --git a/archive/sdparm.8 b/archive/sdparm.8
new file mode 100644
index 00000000..49639fe5
--- /dev/null
+++ b/archive/sdparm.8
@@ -0,0 +1,255 @@
+.TH SDPARM "8" "April 2005" "sg3_utils-1.14" SG3_UTILS
+.SH NAME
+sdparm \- fetch and potentially change SCSI disk parameters
+.SH SYNOPSIS
+.B sdparm
+[\fI--all\fR] [\fI--clear=<str>\fR] [\fI--defaults\fR] [\fI--dummy\fR]
+[\fI--enumerate\fR] [\fI--get=<str>\fR] [\fI--help\fR] [\fI--hex\fR]
+[\fI--inquiry\fR] [\fI--long\fR] [\fI--page=<pg>[,<spg>]\fR]
+[\fI--save\fR] [\fI--set=<str>\fR] [\fI--six\fR] [\fI--verbose\fR]
+[\fI--version\fR] \fI<scsi_disk>\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This utility primarily fetches and potentially changes SCSI disk
+mode pages. Inquiry data including Vital Product Data (VPD) pages
+can also be displayed.
+.PP
+If no options (other than <scsi_disk>) are given then a selection of
+common mode page parameters for that device are listed. If the '--long'
+option is also given then the description of the parameters is placed
+on the right of each line. If the '--all' option is given then all
+known mode page parameters for that device are listed.
+.PP
+Although this utility is primarily designed for SCSI disks (or storage
+devices that appear to the OS as SCSI disks) many of the mode pages
+are in common with other SCSI devices. These include CD/DVD players
+that use the ATAPI transport, SCSI tapes drives and SCSI enclosures.
+.TP
+--all | -a
+by default when mode pages are being displayed only a small number of
+parameters are shown. When this option is given, all recognised parameters
+are output. The default action is to output a relatively small number
+of parameters from different pages. When a specific (mode) page number
+is given with the "--page=" option the all the parameters of that
+page are output (irrespective of the setting of this option).
+.TP
+--clear=<str> | -c <str>
+The <str> contains a comma separated list of parameter acronyms whose
+values are to be set to zero. See the parameters section below.
+.TP
+--defaults | -D
+sets the given mode page (via '--page=' option) to its default values.
+To make the default mode page values also the saved mode page values
+use the '--save' option as well.
+.TP
+--dummy | -d
+when set inhibits changes being written back to a mode page. Instead
+the mode data that would have been sent to a MODE SELECT command,
+is output in ASCII hex to the console.
+.TP
+--enumerate | -e
+lists out the mode pages and parameters known by this utility. Ignores
+the <scsi_device> argument and most other options apart from the '--page='
+and '-all' options. If '--enumerate' is given without other options
+then the known mode pages are listed. If it is given with a page
+number then all known parameters (fields) in that mode page are
+listed. If '--enumerate' is given with the '-all' option then all
+mode pages followed by all parameters within those mode pages are
+listed. See listings section below.
+.TP
+--get=<str> | -g <str>
+The <str> contains a comma separated list of parameter acronyms whose
+values are to be fetched to zero. See the parameters section below.
+The '--long' and '--hex' options effect the output format. Also if
+a value of "1" is given (e.g. '--get=WCE=1') only the current value
+is output.
+.TP
+--help | -h
+output the usage message then exit.
+.TP
+--hex | -H
+rather than trying to decode mode (or VPD) pages, print them out in
+hex. When used with the '-get=' option the corresponding current,
+changeable, default and saveable values are output in hex, prefixed
+by "0x" and space separated. If a value of "1" is given with
+the '--get=' option (e.g. '--get=WCE=1') then only the current value
+is output in hex, prefixed by "0x".
+.TP
+--inquiry | -i
+output INQUIRY VPD pages. The default is to output mode pages.
+.TP
+--long | -l
+output extra information. In the case of mode page parameters a
+description (with units if applicable) is output to the right if
+it is known.
+.TP
+--page=<pg>[,<spg>] | -p <pg>[,<spg>]
+supply the page number and optionally the sub page number of the mode
+page to output. These numbers re interpreted as decimal unless
+prefixed with "0x". Alternatively a two letter abbreviation for
+a (mode) page can be given (e.g. "--page=ca" for the caching control
+mode page). See the '--enumerate' option for available abbreviations.
+.TP
+--page=<str> | -p <str>
+alternatively a two letter acronym for a mode page can be given.
+The '--enumerate' option can be used to list the mode page
+acronyms.
+.TP
+--save | -S
+when a mode page is being modified (by using the '--clear=' and/or '--set="
+options) then the default action is to only modify the current mode page.
+When this option is given the corresponding value(s) in the saved mode
+page is also changed. The next time the device is power cycled (or reset)
+the saved mode page values become (i.e. are copied to) the current mode
+page. See notes section below.
+.TP
+--set=<str> | -s <str>
+The <str> contains a comma separated list of parameter acronyms whose
+values are to be set to (all) ones. Alternatively each acronym may be
+followed by "=<n>" where <n> is the value to set that parameter to.
+See the parameters section below.
+.TP
+--six | -6
+The default action of this utility is to issue MODE SENSE and MODE
+SELECT SCSI commands with 10 byte cdbs. When this option is given the
+6 byte cdb variants are used.
+.TP
+--verbose | -v
+increase the level of verbosity, (i.e. debug output).
+.TP
+--version | -V
+print the version string and then exit.
+.SH PARAMETERS
+The '--clear=', '--get=' and '--set=" options can take a string argument
+which is a comma separated list of parameters. Each parameter can
+be either an acronym name or a <start_byte>:<start_bit>:<num_bits> triple
+Either form can optionally be followed by "=<val>". Acronyms (e.g.
+WCE for "write cache enable") that this utility supports can be viewed
+with the '--enumerate' option. Alternatively the mode page parameters
+to be changed can be described in terms of a <start_byte> (origin 0)
+within the mode page, a <start_bit> (0 to 7 inclusive) and <num_bits> (1
+to 32 inclusive). The low level representation of the WCE bit (in
+the caching control mode page) is 2:2:1 . The <start_byte> and
+the <val> can optionally be given in hex (prefixed with "0x").
+.PP
+Both '--clear=' and '--set=" parameter lists can be given. The only
+difference is the value set in the absence of "=<val>". For '--clear='
+the default value is zero while for '--set' the default value is all
+ones (as many as <num_bits> permits).
+.PP
+When an acronym is given the mode page is imputed from that acronym (e.g.
+WCE is in the cache mode page). When only the start_byte:start_bit:num_bits
+representation is used then the '--page=' option must be given to establish
+which mode page is to be used. When multiple parameters are given, they
+must all be in the same mode page (except for '--get=').
+.SH LISTINGS
+When known mode pages are listed (via the '--enumerate' option) each
+line starts with a two letter acronym. This is followed by the page
+number (in hex prefixed by "0x") optionally followed by a comma and
+the subpage number. Finally the descriptive name of the mode
+page (i.e. as found in spc-3 or sbc-2) is output.
+.PP
+When known parameters (fields) of a mode page are listed each line
+starts with an acronym. This will match (or be an acronym for) the
+description for that field found in the draft standards. Next are
+three numbers, separated by colons, surrounded by brackets. These
+are the byte offset (in hex, prefixed by "0x") of the start of the
+field within the mode page; the starting bit (0 through 7 inclusive)
+and then the number of bits. The descriptive name of the
+parameter (field) is then given, with units where
+applicable (e.g. "(ms)" means the units are milliseconds).
+.PP
+Mode parameters for which the num_bits is greater than 1 can be
+viewed as unsigned integers. Often 16 and 32 bit fields are set
+to 0xffff and 0xffffffff respectively (all ones) which usually
+has a special meaning (see drafts). In listings such values are
+represented by "-1" to save space (rather than their unsigned
+integer equivalents).
+.SH NOTES
+The SPC-3 draft (rev 22a) says that devices that implement no
+distinction between current and saved pages can return an
+error (ILLEGAL REQUEST, invalid field in cdb) if the SP bit (which
+corresponds to the '--save' option) is _not_ set. In such cases
+the '--save' option needs to be given.
+.PP
+If the '--save' option is given but the existing mode page indicates (via
+its PS bit) that the page is not savable, then this utility generates
+an error message. That message suggests to try again without the '--save'
+option.
+.PP
+In the 2.4 series of Linux kernels the given device must be
+a SCSI generic (sg) device. In the 2.6 series block devices (e.g. disks
+and SCSI DVDs) can also be specified. For example "sdparm /dev/sda"
+will work in the 2.6 series kernels. From lk 2.6.6 other SCSI "char"
+device names may be used as well (e.g. "/dev/st0m").
+.SH EXAMPLES
+These examples assume a 2.6 series linux kernel. For the 2.4 series
+scsi generic (i.e. sg) device names would need to be used.
+To list the common (mode) parameters of a disk:
+.PP
+ sdparm /dev/sda
+.PP
+To see all parameters for the caching control mode page:
+.PP
+ sdparm --page=ca /dev/sda
+.PP
+To get the WCE values (current changeable default and saved) in hex:
+.PP
+ sdparm -g WCE -H /dev/sda
+.br
+0x01 0x00 0x01 0x01
+.PP
+To get the WCE current value in hex:
+.PP
+ sdparm -g WCE=1 -H /dev/sda
+.br
+0x01
+.PP
+To set the "write cache enable" bit in the current page:
+.PP
+ sdparm --set=WCE /dev/sda
+.PP
+To set the "write cache enable" bit in the current and saved page:
+.PP
+ sdparm --set=WCE --save /dev/sda
+.PP
+To set the "write cache enable" and clear "read cache disable":
+.PP
+ sdparm --set=WCE --clear=RCD --save /dev/sda
+.PP
+The previous example can also by written as:
+.PP
+ sdparm -s WCE=1,RCD=0 -S /dev/sda
+.PP
+To re-establish the manufacturer's defaults in the current and saved
+caching control mode page:
+.PP
+ sdparm --page=ca --defaults -save /dev/sda
+.PP
+If an ATAPI cd/dvd player is at /dev/hdc then its common (mode) parameters
+could be listed with:
+.PP
+ sdparm /dev/hdc
+.PP
+One disk vendor has a "performance mode" bit (PM) in the vendor specific
+unit attention mode page [0x0,0x0]. PM=0 is server mode (the default)
+while PM=1 is desktop mode. Desktop mode can be set (both current and
+saved) with:
+.PP
+ sdparm --page=0 --set=2:7:1=1 --save /dev/sda
+.PP
+The resultant change can be viewed in hex with the '--hex' option as
+there are no acronyms for vendor extensions yet.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2005 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_modes(sg3_utils), sg_wr_mode(sg3_utils), sginfo(sg3_utils)
+.B sg_senddiag(sg3_utils), smartmontools(internet, sourceforge)
diff --git a/archive/sdparm.c b/archive/sdparm.c
new file mode 100644
index 00000000..6fa7f291
--- /dev/null
+++ b/archive/sdparm.c
@@ -0,0 +1,1501 @@
+/*
+ * Copyright (c) 2005 Douglas Gilbert.
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE.
+ *
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include "sg_include.h"
+#include "sg_lib.h"
+#include "sg_cmds.h"
+
+/* A utility program for the Linux OS SCSI subsystem.
+ *
+ * This utility fetches various parameters associated with a given
+ * SCSI disk (or a disk that uses, or translates the SCSI command
+ * set). In some cases these parameters can be changed.
+ */
+
+static char * version_str = "0.90 20050411";
+
+#define ME "sdparm: "
+
+#define DEF_MODE_RESP_LEN 252
+#define RW_ERR_RECOVERY_MP 1
+#define DISCONNECT_MP 2
+#define V_ERR_RECOVERY_MP 7
+#define CACHING_MP 8
+#define CONTROL_MP 0xa
+#define POWER_MP 0x1a
+#define IEC_MP 0x1c
+#define PROT_SPEC_LU_MP 0x18
+#define PROT_SPEC_PORT_MP 0x19
+
+#define MODE_DATA_OVERHEAD 128
+#define EBUFF_SZ 256
+#define MAX_MP_IT_VAL 128
+#define MAX_MODE_DATA_LEN 2048
+
+
+static struct option long_options[] = {
+ {"six", 0, 0, '6'},
+ {"all", 0, 0, 'a'},
+ {"clear", 1, 0, 'c'},
+ {"defaults", 1, 0, 'D'},
+ {"dummy", 1, 0, 'd'},
+ {"enumerate", 0, 0, 'e'},
+ {"get", 1, 0, 'g'},
+ {"help", 0, 0, 'h'},
+ {"hex", 0, 0, 'H'},
+ {"inquiry", 0, 0, 'i'},
+ {"long", 0, 0, 'l'},
+ {"page", 1, 0, 'p'},
+ {"set", 1, 0, 's'},
+ {"save", 0, 0, 'S'},
+ {"verbose", 0, 0, 'v'},
+ {"version", 0, 0, 'V'},
+ {0, 0, 0, 0},
+};
+
+static void usage()
+{
+ fprintf(stderr, "Usage: "
+ "sdparm [-all] [--clear=<str>] [--defaults] [-dummy] "
+ "[--enumerate]\n"
+ " [--get=<str>] [--help] [--hex] [--inquiry] "
+ "[--long]\n"
+ " [--page=<pg>] [--save] [--set=<str>] [--six] "
+ "[--verbose]\n"
+ " [--version] <scsi_disk>\n"
+ " where:\n"
+ " --all | -a list all known parameters for given "
+ "disk\n"
+ " --clear=<str> | -c <str> clear (zero) parameter value(s)\n"
+ " --defaults | -D set a mode page to its default "
+ "values\n"
+ " --dummy | -d don't write back modified mode page\n"
+ " --enumerate | -e list known pages and parameters "
+ "(ignore disk)\n"
+ " --get=<str> | -g <str> get (fetch) parameter value(s)\n"
+ " --help | -h print out usage message\n"
+ " --hex | -H output in hex rather than name/value "
+ "pairs\n"
+ " --inquiry | -i output INQUIRY VPD page(s) (def mode "
+ "page(s))\n"
+ " --long | -l add description to parameter output\n"
+ " --page=<pg> | -p <pg> page ([,subpage]) number to output "
+ "(or change)\n"
+ " --save | -S place mode changes in saved page as "
+ "well\n"
+ " --set=<str> | -s <str> set parameter value(s)\n"
+ " --six | -6 use 6 byte SCSI cdbs (def 10 byte)\n"
+ " --verbose | -v increase verbosity\n"
+ " --version | -V print version string and exit\n\n"
+ "View or change parameters of a SCSI disk\n"
+ );
+}
+
+struct values_name_t {
+ int value;
+ int subvalue;
+ const char * acron;
+ const char * name;
+};
+
+static struct values_name_t mode_nums_name[] = {
+ {CACHING_MP, 0, "ca", "Caching"},
+ {CONTROL_MP, 0, "co", "Control"},
+ {DISCONNECT_MP, 0, "dr", "Disconnect-reconnect"},
+ {IEC_MP, 0, "ie", "Informational exception control"},
+ {PROT_SPEC_LU_MP, 0, "pl", "Protocol specific logical unit"},
+ {POWER_MP, 0, "po", "Power condition"},
+ {PROT_SPEC_PORT_MP, 0, "pp", "Protocol specific port"},
+ {RW_ERR_RECOVERY_MP, 0, "rw", "Read write error recovery"},
+ {V_ERR_RECOVERY_MP, 0, "ve", "Verify error recovery"},
+};
+
+static int mode_nums_name_len =
+ (sizeof(mode_nums_name) / sizeof(mode_nums_name[0]));
+
+static void list_mps()
+{
+ int k;
+ const struct values_name_t * vnp;
+
+ for (k = 0, vnp = mode_nums_name; k < mode_nums_name_len; ++k, ++vnp) {
+ if (vnp->subvalue)
+ printf(" %-4s 0x%02x,0x%02x %s\n", vnp->acron, vnp->value,
+ vnp->subvalue, vnp->name);
+ else
+ printf(" %-4s 0x%02x %s\n", vnp->acron, vnp->value,
+ vnp->name);
+ }
+}
+
+static const char * get_mode_name(int page_num, int subpage_num)
+{
+ int k;
+ const struct values_name_t * vnp;
+
+ for (k = 0, vnp = mode_nums_name; k < mode_nums_name_len; ++k, ++vnp) {
+ if ((page_num == vnp->value) && (subpage_num == vnp->subvalue))
+ return vnp->name;
+ }
+ return NULL;
+}
+
+static const struct values_name_t * find_mp_by_acron(const char * ap)
+{
+ int k;
+ const struct values_name_t * vnp;
+
+ for (k = 0, vnp = mode_nums_name; k < mode_nums_name_len; ++k, ++vnp) {
+ if (0 == strncmp(vnp->acron, ap, 2))
+ return vnp;
+ }
+ return NULL;
+}
+
+struct mode_page_item {
+ const char * acron;
+ int page_num;
+ int subpage_num;
+ int start_byte;
+ int start_bit;
+ int num_bits;
+ int common;
+ const char * description;
+};
+
+struct mode_page_it_val {
+ struct mode_page_item mpi;
+ int val;
+};
+
+struct mode_page_settings {
+ int page_num;
+ int subpage_num;
+ struct mode_page_it_val it_vals[MAX_MP_IT_VAL];
+ int num_it_vals;
+};
+
+
+static struct mode_page_item mitem_arr[] = {
+ {"AWRE", RW_ERR_RECOVERY_MP, 0, 2, 7, 1, 1, /* [0x1] sbc2 */
+ "Automatic write reallocation enabled"},
+ {"ARRE", RW_ERR_RECOVERY_MP, 0, 2, 6, 1, 1,
+ "Automatic read reallocation enabled"},
+ {"TB", RW_ERR_RECOVERY_MP, 0, 2, 5, 1, 0,
+ "Transfer block"},
+ {"RC", RW_ERR_RECOVERY_MP, 0, 2, 4, 1, 0,
+ "Read continuous"},
+ {"EER", RW_ERR_RECOVERY_MP, 0, 2, 3, 1, 0,
+ "Enable early recover"},
+ {"PER", RW_ERR_RECOVERY_MP, 0, 2, 2, 1, 1,
+ "Post error"},
+ {"DTE", RW_ERR_RECOVERY_MP, 0, 2, 1, 1, 0,
+ "Data terminate on error"},
+ {"DCR", RW_ERR_RECOVERY_MP, 0, 2, 0, 1, 0,
+ "Disable correction"},
+ {"RRC", RW_ERR_RECOVERY_MP, 0, 3, 7, 8, 0,
+ "Read retry count"},
+ {"WRC", RW_ERR_RECOVERY_MP, 0, 8, 7, 8, 0,
+ "Write retry count"},
+ {"RTL", RW_ERR_RECOVERY_MP, 0, 10, 7, 16, 0,
+ "Recovery time limit (ms)"},
+
+ {"BITL", DISCONNECT_MP, 0, 4, 7, 16, 0, /* [0x2] spc3,sas1 */
+ "Bus inactivity time limit (sas: 100us)"},
+ {"MCTL", DISCONNECT_MP, 0, 8, 7, 16, 0,
+ "Maximum connect time limit (sas: 100us)"},
+ {"MBS", DISCONNECT_MP, 0, 10, 7, 16, 0,
+ "Maximum burst size"},
+ {"FBS", DISCONNECT_MP, 0, 14, 7, 16, 0,
+ "First burst size"},
+
+ {"V_EER", V_ERR_RECOVERY_MP, 0, 2, 3, 1, 0, /* [0x8] sbc2 */
+ "Enable early recover"},
+ {"V_PER", V_ERR_RECOVERY_MP, 0, 2, 2, 1, 0,
+ "Post error"},
+ {"V_DTE", V_ERR_RECOVERY_MP, 0, 2, 1, 1, 0,
+ "Data terminate on error"},
+ {"V_DCR", V_ERR_RECOVERY_MP, 0, 2, 0, 1, 0,
+ "Disable correction"},
+ {"V_RC", V_ERR_RECOVERY_MP, 0, 3, 7, 8, 0,
+ "Verify retry count"},
+ {"V_RTL", V_ERR_RECOVERY_MP, 0, 10, 7, 16, 0,
+ "Verify recovery time limit (ms)"},
+
+ {"IC", CACHING_MP, 0, 2, 7, 1, 0, /* [0x8] sbc2 */
+ "Initiator control"},
+ {"ABPF", CACHING_MP, 0, 2, 6, 1, 0,
+ "Abort pre-fetch"},
+ {"CAP", CACHING_MP, 0, 2, 5, 1, 0,
+ "Caching analysis permitted"},
+ {"DISC", CACHING_MP, 0, 2, 4, 1, 0,
+ "Discontinuity"},
+ {"SIZE", CACHING_MP, 0, 2, 3, 1, 0,
+ "Size"},
+ {"WCE", CACHING_MP, 0, 2, 2, 1, 1,
+ "Write cache enable"},
+ {"MF", CACHING_MP, 0, 2, 1, 1, 0,
+ "Multiplication factor"},
+ {"RCD", CACHING_MP, 0, 2, 0, 1, 1,
+ "Read cache disable"},
+ {"DRRP", CACHING_MP, 0, 3, 7, 4, 0,
+ "Demand read retension prioriry"},
+ {"WRP", CACHING_MP, 0, 3, 3, 4, 0,
+ "Write retension prioriry"},
+ {"DPTL", CACHING_MP, 0, 4, 7, 16, 0,
+ "Disable pre-fetch transfer length"},
+ {"MIPF", CACHING_MP, 0, 6, 7, 16, 0,
+ "Minimum pre-fetch"},
+ {"MAPF", CACHING_MP, 0, 8, 7, 16, 0,
+ "Maximum pre-fetch"},
+ {"MAPFC", CACHING_MP, 0, 10, 7, 16, 0,
+ "Maximum pre-fetch ceiling"},
+ {"FSW", CACHING_MP, 0, 12, 7, 1, 0,
+ "Force sequential write"},
+ {"LBCSS", CACHING_MP, 0, 12, 5, 1, 0,
+ "Logical block cache segment size"},
+ {"DRA", CACHING_MP, 0, 12, 4, 1, 0,
+ "disable read ahead"},
+ {"NV_DIS", CACHING_MP, 0, 12, 0, 1, 0,
+ "Non-volatile cache disbale"},
+ {"NCS", CACHING_MP, 0, 13, 7, 8, 0,
+ "Number of cache segments"},
+ {"CSS", CACHING_MP, 0, 14, 7, 16, 0,
+ "Cache segment size"},
+
+ {"TST", CONTROL_MP, 0, 2, 7, 3, 0, /* [0xa] spc3 */
+ "Task set type"},
+ {"TMF_ONLY", CONTROL_MP, 0, 2, 4, 1, 0,
+ "Task management functions only"},
+ {"D_SENSE", CONTROL_MP, 0, 2, 2, 1, 0,
+ "Descriptor format sense data"},
+ {"GLTSD", CONTROL_MP, 0, 2, 1, 1, 0,
+ "Global logging target save disable"},
+ {"RLEC", CONTROL_MP, 0, 2, 0, 1, 0,
+ "Report log exception condition"},
+ {"QAM", CONTROL_MP, 0, 3, 7, 4, 0,
+ "Queue algorithm modifier"},
+ {"QERR", CONTROL_MP, 0, 3, 2, 2, 0,
+ "Queue error management"},
+ {"RAC", CONTROL_MP, 0, 4, 6, 1, 0,
+ "Report a check"},
+ {"UA_INTLCK", CONTROL_MP, 0, 4, 5, 2, 0,
+ "Unit attention interlocks controls"},
+ {"SWP", CONTROL_MP, 0, 4, 3, 1, 1,
+ "Software write protect"},
+ {"ATO", CONTROL_MP, 0, 5, 7, 1, 0,
+ "Application tag owner"},
+ {"TAS", CONTROL_MP, 0, 5, 6, 1, 0,
+ "Task aborted status"},
+ {"AUTOLOAD", CONTROL_MP, 0, 5, 2, 3, 0,
+ "Autoload mode"},
+ {"BTP", CONTROL_MP, 0, 8, 7, 16, 0,
+ "Busy timeout period (100us)"},
+ {"ESTCT", CONTROL_MP, 0, 10, 7, 16, 0,
+ "Extended self test completion time (sec)"},
+
+ {"PID", PROT_SPEC_PORT_MP, 0, 2, 3, 4, 0, /* [0x19] spc3 */
+ "Protocol identifier"},
+
+ {"LUPID", PROT_SPEC_LU_MP, 0, 2, 3, 4, 0, /* [0x18] spc3 */
+ "Protocol identifier"},
+
+ {"IDLE", POWER_MP, 0, 3, 1, 1, 0, /* [0x1a] spc3 */
+ "Idle timer active"},
+ {"STANDBY", POWER_MP, 0, 3, 0, 1, 0,
+ "Standby timer active"},
+ {"ICT", POWER_MP, 0, 4, 7, 32, 0,
+ "Idle condition timer (100 ms)"},
+ {"SCT", POWER_MP, 0, 8, 7, 32, 0,
+ "Standby condition timer (100 ms)"},
+
+ {"PERF", IEC_MP, 0, 2, 7, 1, 0, /* [0x1c] spc3 */
+ "Performance"},
+ {"EBF", IEC_MP, 0, 2, 5, 1, 0,
+ "Enable background function"},
+ {"EWASC", IEC_MP, 0, 2, 4, 1, 1,
+ "Enable warning"},
+ {"DEXCPT", IEC_MP, 0, 2, 3, 1, 1,
+ "Disable exceptions"},
+ {"TEST", IEC_MP, 0, 2, 2, 1, 0,
+ "Test (simulate device failure"},
+ {"LOGERR", IEC_MP, 0, 2, 0, 1, 0,
+ "Log errors"},
+ {"MRIE", IEC_MP, 0, 3, 3, 4, 1,
+ "Method of reporting infomational exceptions"},
+ {"INTT", IEC_MP, 0, 4, 7, 32, 0,
+ "Interval timer (100 ms)"},
+ {"REPC", IEC_MP, 0, 8, 7, 32, 0,
+ "Report count"},
+};
+
+static int mitem_arr_len = (sizeof(mitem_arr) / sizeof(mitem_arr[0]));
+
+static void list_mitems(int pn, int spn)
+{
+ int k, t_pn, t_spn;
+ const struct mode_page_item * mpi;
+ const char * name;
+ int found = 0;
+
+ t_pn = -1;
+ t_spn = -1;
+ for (k = 0, mpi = mitem_arr; k < mitem_arr_len; ++k, ++mpi) {
+ if ((t_pn != mpi->page_num) || (t_spn != mpi->subpage_num)) {
+ t_pn = mpi->page_num;
+ t_spn = mpi->subpage_num;
+ if ((pn >= 0) && ((pn != t_pn) || (spn != t_spn)))
+ continue;
+ name = get_mode_name(t_pn, t_spn);
+ if (name) {
+ if (t_spn)
+ printf("%s mode page [0x%x,0x%x]:\n", name, t_pn, t_spn);
+ else
+ printf("%s mode page [0x%x]:\n", name, t_pn);
+ } else if (0 == t_spn)
+ printf("mode page 0x%x:\n", t_pn);
+ else
+ printf("mode page 0x%x,0x%x:\n", t_pn, t_spn);
+ } else {
+ if ((pn >= 0) && ((pn != t_pn) || (spn != t_spn)))
+ continue;
+ }
+ printf(" %-10s [0x%02x:%d:%-2d] %s\n", mpi->acron, mpi->start_byte,
+ mpi->start_bit, mpi->num_bits, mpi->description);
+ found = 1;
+ }
+ if ((! found) && (pn >= 0)) {
+ name = get_mode_name(pn, spn);
+ if (name) {
+ if (spn)
+ printf("%s mode page [0x%x,0x%x]: no items found\n", name,
+ pn, spn);
+ else
+ printf("%s mode page [0x%x]: no items found\n", name, pn);
+ } else if (0 == spn)
+ printf("mode page 0x%x: no items found\n", pn);
+ else
+ printf("mode page 0x%x,0x%x: no items found\n", pn, spn);
+ }
+}
+
+static const struct mode_page_item * find_mitem_by_acron(const char * ap, int * from)
+{
+ int k = 0;
+ const struct mode_page_item * mpi;
+
+ if (from) {
+ k = *from;
+ if (k < 0)
+ k = 0;
+ }
+ for (mpi = mitem_arr + k; k < mitem_arr_len; ++k, ++mpi) {
+ if (0 == strcmp(mpi->acron, ap))
+ break;
+ }
+ if (k >= mitem_arr_len) {
+ k = mitem_arr_len;
+ mpi = NULL;
+ }
+ if (from)
+ *from = k + 1;
+ return mpi;
+}
+
+static const char * scsi_ptype_strs[] = {
+ /* 0 */ "disk",
+ "tape",
+ "printer",
+ "processor",
+ "write once optical disk",
+ /* 5 */ "cd/dvd",
+ "scanner",
+ "optical memory device",
+ "medium changer",
+ "communications",
+ /* 0xa */ "graphics",
+ "graphics",
+ "storage array controller",
+ "enclosure services device",
+ "simplified direct access device",
+ "optical card reader/writer device",
+ /* 0x10 */ "bridge controller commands",
+ "object based storage",
+ "automation/driver interface",
+ "0x13", "0x14", "0x15", "0x16", "0x17", "0x18",
+ "0x19", "0x1a", "0x1b", "0x1c", "0x1d",
+ "well known logical unit",
+ "no physical device on this lu",
+};
+
+static unsigned int get_big_endian(const unsigned char * from, int start_bit,
+ int num_bits)
+{
+ unsigned int res;
+ int sbit_o1 = start_bit + 1;
+
+ res = (*from++ & ((1 << sbit_o1) - 1));
+ num_bits -= sbit_o1;
+ while (num_bits > 0) {
+ res <<= 8;
+ res |= *from++;
+ num_bits -= 8;
+ }
+ if (num_bits < 0)
+ res >>= (-num_bits);
+ return res;
+}
+
+static void set_big_endian(unsigned int val, unsigned char * to,
+ int start_bit, int num_bits)
+{
+ int sbit_o1 = start_bit + 1;
+ int mask, num, k, x;
+
+ mask = (8 != sbit_o1) ? ((1 << sbit_o1) - 1) : 0xff;
+ k = start_bit - ((num_bits - 1) % 8);
+ if (0 != k)
+ val <<= ((k > 0) ? k : (8 + k));
+ num = (num_bits + 15 - sbit_o1) / 8;
+ for (k = 0; k < num; ++k) {
+ if ((sbit_o1 - num_bits) > 0)
+ mask &= ~((1 << (sbit_o1 - num_bits)) - 1);
+ if (k < (num - 1))
+ x = (val >> ((num - k - 1) * 8)) & 0xff;
+ else
+ x = val & 0xff;
+ to[k] = (to[k] & ~mask) | (x & mask);
+ mask = 0xff;
+ num_bits -= sbit_o1;
+ sbit_o1 = 8;
+ }
+}
+
+static unsigned int mp_get_value(const struct mode_page_item *mpi,
+ const unsigned char * mp)
+{
+ return get_big_endian(mp + mpi->start_byte, mpi->start_bit,
+ mpi->num_bits);
+}
+
+static unsigned int mp_get_value_check(const struct mode_page_item *mpi,
+ const unsigned char * mp,
+ int * all_set)
+{
+ unsigned int res;
+
+ res = get_big_endian(mp + mpi->start_byte, mpi->start_bit,
+ mpi->num_bits);
+ if (all_set) {
+ if ((16 == mpi->num_bits) && (0xffff == res))
+ *all_set = 1;
+ else if ((32 == mpi->num_bits) && (0xffffffff == res))
+ *all_set = 1;
+ else
+ *all_set = 0;
+ }
+ return res;
+}
+
+static void mp_set_value(unsigned int val, struct mode_page_item *mpi,
+ unsigned char * mp)
+{
+ set_big_endian(val, mp + mpi->start_byte, mpi->start_bit, mpi->num_bits);
+}
+
+static void print_mp_entry(const char * pre, int smask,
+ const struct mode_page_item *mpi,
+ const unsigned char * cur_mp,
+ const unsigned char * cha_mp,
+ const unsigned char * def_mp,
+ const unsigned char * sav_mp,
+ int long_out)
+{
+ int sep = 0;
+ int all_set;
+ unsigned int u;
+ const char * acron;
+
+ all_set = 0;
+ acron = (mpi->acron ? mpi->acron : "");
+ u = mp_get_value_check(mpi, cur_mp, &all_set);
+ if (all_set)
+ printf("%s%-10s -1", pre, acron);
+ else
+ printf("%s%-10s %u", pre, acron, u);
+ if (smask & 0xe) {
+ printf(" [");
+ if (smask & 2) {
+ printf("Changeable: %s",
+ (mp_get_value(mpi, cha_mp) ? "y" : "n"));
+ sep = 1;
+ }
+ if (smask & 4) {
+ all_set = 0;
+ u = mp_get_value_check(mpi, def_mp, &all_set);
+ if (all_set)
+ printf("%sdef: -1", (sep ? ", " : " "));
+ else
+ printf("%sdef: %u", (sep ? ", " : " "), u);
+ sep = 1;
+ }
+ if (smask & 8) {
+ all_set = 0;
+ u = mp_get_value_check(mpi, sav_mp, &all_set);
+ if (all_set)
+ printf("%ssaved: -1", (sep ? ", " : " "));
+ else
+ printf("%ssaved: %u", (sep ? ", " : " "), u);
+ }
+ printf("]");
+ }
+ if (long_out && mpi->description)
+ printf(" %s", mpi->description);
+ printf("\n");
+}
+
+static void print_mode_info(int sg_fd, int mode6, int pn, int spn, int all,
+ int long_out, int hex, int verbose)
+{
+ int k, res, len, verb, smask, single, fetch;
+ const struct mode_page_item * mpi;
+ unsigned char cur_mp[DEF_MODE_RESP_LEN];
+ unsigned char cha_mp[DEF_MODE_RESP_LEN];
+ unsigned char def_mp[DEF_MODE_RESP_LEN];
+ unsigned char sav_mp[DEF_MODE_RESP_LEN];
+ const char * name;
+
+ verb = (verbose > 0) ? verbose - 1 : 0;
+ if (pn >= 0) {
+ single = 1;
+ fetch = 1;
+ for (k = 0, mpi = mitem_arr; k < mitem_arr_len; ++k, ++mpi) {
+ if ((pn == mpi->page_num) && (spn == mpi->subpage_num))
+ break;
+ }
+ if (k >= mitem_arr_len) {
+ if (verbose) {
+ if (0 == spn)
+ printf("mode page 0x%x, attributes not found\n", pn);
+ else
+ printf("mode page 0x%x,0x%x, attributes not found\n",
+ pn, spn);
+ }
+ if (hex) {
+ k = 0;
+ mpi = mitem_arr; /* trick to enter main loop once */
+ }
+ }
+ } else {
+ single = 0;
+ fetch = 0;
+ mpi = mitem_arr;
+ k = 0;
+ }
+ name = "";
+ smask = 0;
+ for (; k < mitem_arr_len; ++k, ++mpi, fetch = 0) {
+ if (0 == fetch) {
+ if (! (all || mpi->common))
+ continue;
+ if ((pn != mpi->page_num) || (spn != mpi->subpage_num)) {
+ if (single)
+ break;
+ fetch = 1;
+ pn = mpi->page_num;
+ spn = mpi->subpage_num;
+ }
+ }
+ if (fetch) {
+ smask = 0;
+ res = sg_get_mode_page_types(sg_fd, mode6, pn, spn,
+ DEF_MODE_RESP_LEN, &smask, cur_mp,
+ cha_mp, def_mp, sav_mp, verb);
+ if (SG_LIB_CAT_INVALID_OP == res) {
+ if (mode6)
+ fprintf(stderr, "6 byte MODE SENSE cdb not supported, "
+ "try again without '-6' option\n");
+ else
+ fprintf(stderr, "10 byte MODE SENSE cdb not supported, "
+ "try again with '-6' option\n");
+ return;
+ }
+ if ((smask & 1)) {
+ name = get_mode_name(pn, spn);
+ if (name) {
+ if (0 == spn)
+ printf("%s mode page [0x%x]:\n", name, pn);
+ else
+ printf("%s mode page [0x%x,0x%x]:\n", name, pn, spn);
+ } else if (0 == spn)
+ printf("mode page 0x%x:\n", pn);
+ else
+ printf("mode page 0x%x,0x%x:\n", pn, spn);
+ if (hex) {
+ if (cur_mp[0] & 0x40)
+ len = (cur_mp[2] << 8) + cur_mp[3] + 4;
+ else
+ len = cur_mp[1] + 2;
+ printf(" Current:\n");
+ dStrHex((const char *)cur_mp, len, 1);
+ if (smask & 2) {
+ printf(" Changeable:\n");
+ dStrHex((const char *)cha_mp, len, 1);
+ }
+ if (smask & 4) {
+ printf(" Default:\n");
+ dStrHex((const char *)def_mp, len, 1);
+ }
+ if (smask & 8) {
+ printf(" Saved:\n");
+ dStrHex((const char *)sav_mp, len, 1);
+ }
+ }
+ } else {
+ if (verbose || single) {
+ name = get_mode_name(pn, spn);
+ if (name)
+ printf(">> %s mode page not supported\n", name);
+ else if (0 == spn)
+ printf(">> mode page 0x%x not supported\n", pn);
+ else
+ printf(">> mode page 0x%x,0x%x not supported\n",
+ pn, spn);
+ }
+ }
+ }
+ if (smask && (! hex))
+ print_mp_entry(" ", smask, mpi, cur_mp, cha_mp,
+ def_mp, sav_mp, long_out);
+ }
+}
+
+static void get_mode_info(int sg_fd, int mode6,
+ struct mode_page_settings * mps, int long_out,
+ int hex, int verbose)
+{
+ int k, res, verb, smask, pn, spn;
+ unsigned int u, val;
+ const struct mode_page_item * mpi;
+ unsigned char cur_mp[DEF_MODE_RESP_LEN];
+ unsigned char cha_mp[DEF_MODE_RESP_LEN];
+ unsigned char def_mp[DEF_MODE_RESP_LEN];
+ unsigned char sav_mp[DEF_MODE_RESP_LEN];
+ const struct mode_page_it_val * ivp;
+
+ verb = (verbose > 0) ? verbose - 1 : 0;
+ for (k = 0, pn = 0, spn = 0; k < mps->num_it_vals; ++k) {
+ ivp = &mps->it_vals[k];
+ val = ivp->val;
+ mpi = &ivp->mpi;
+ if ((0 == k) || (pn != mpi->page_num) || (spn != mpi->subpage_num)) {
+ pn = mpi->page_num;
+ spn = mpi->subpage_num;
+ smask = 0;
+ switch (val) {
+ case 0:
+ res = sg_get_mode_page_types(sg_fd, mode6, pn, spn,
+ DEF_MODE_RESP_LEN, &smask,
+ cur_mp, cha_mp, def_mp, sav_mp,
+ verb);
+ break;
+ case 1:
+ res = sg_get_mode_page_types(sg_fd, mode6, pn, spn,
+ DEF_MODE_RESP_LEN, &smask,
+ cur_mp, NULL, NULL, NULL, verb);
+ break;
+ default:
+ if (mpi->acron)
+ fprintf(stderr, "bad format 'val' given to %s\n",
+ mpi->acron);
+ else
+ fprintf(stderr, "bad format 'val' given to 0x%x:%d:%d\n",
+ mpi->start_byte, mpi->start_bit, mpi->num_bits);
+ return;
+ }
+ if (SG_LIB_CAT_INVALID_OP == res) {
+ if (mode6)
+ fprintf(stderr, "6 byte MODE SENSE cdb not supported, "
+ "try again without '-6' option\n");
+ else
+ fprintf(stderr, "10 byte MODE SENSE cdb not supported, "
+ "try again with '-6' option\n");
+ return;
+ }
+ }
+ if (0 == val) {
+ if (hex) {
+ if (smask & 1) {
+ u = mp_get_value(mpi, cur_mp);
+ printf("0x%02x ", u);
+ } else
+ printf("- ");
+ if (smask & 2) {
+ u = mp_get_value(mpi, cha_mp);
+ printf("0x%02x ", u);
+ } else
+ printf("- ");
+ if (smask & 4) {
+ u = mp_get_value(mpi, def_mp);
+ printf("0x%02x ", u);
+ } else
+ printf("- ");
+ if (smask & 8) {
+ u = mp_get_value(mpi, sav_mp);
+ printf("0x%02x ", u);
+ } else
+ printf("- ");
+ printf("\n");
+ } else
+ print_mp_entry("", smask, mpi, cur_mp, cha_mp,
+ def_mp, sav_mp, long_out);
+ } else if (1 == val) {
+ if (hex) {
+ if (smask & 1) {
+ u = mp_get_value(mpi, cur_mp);
+ printf("0x%02x ", u);
+ } else
+ printf("- ");
+ printf("\n");
+ } else
+ print_mp_entry("", smask, mpi, cur_mp, NULL,
+ NULL, NULL, long_out);
+ }
+ }
+}
+
+/* Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
+ * bad field in cdb, -1 -> other failure */
+static int change_mode_page(int sg_fd, int save, int mode_6,
+ struct mode_page_settings * mps, int dummy,
+ int verbose)
+{
+ int k, len, off, md_len, res;
+ char ebuff[EBUFF_SZ];
+ unsigned char mdpg[MAX_MODE_DATA_LEN];
+ struct mode_page_it_val * ivp;
+
+ len = MAX_MODE_DATA_LEN;
+ memset(mdpg, 0, len);
+ if (mode_6)
+ res = sg_ll_mode_sense6(sg_fd, 0 /* dbd */, 0 /*current */,
+ mps->page_num, mps->subpage_num,
+ mdpg, ((len > 252) ? 252 : len), 1,
+ verbose);
+ else
+ res = sg_ll_mode_sense10(sg_fd, 0 /* llbaa */, 0 /* dbd */,
+ 0 /* current */, mps->page_num,
+ mps->subpage_num, mdpg, len, 1, verbose);
+ if (0 != res) {
+ fprintf(stderr, "change_mode_page: failed fetching page: 0x%x,0x%x\n",
+ mps->page_num, mps->subpage_num);
+ return -1;
+ }
+ off = sg_mode_page_offset(mdpg, len, mode_6, ebuff, EBUFF_SZ);
+ if (off < 0) {
+ fprintf(stderr, "change_mode_page: page offset failed: %s\n", ebuff);
+ return -1;
+ }
+ if (mode_6)
+ md_len = mdpg[0] + 1;
+ else
+ md_len = (mdpg[0] << 8) + mdpg[1] + 2;
+ mdpg[0] = 0; /* mode data length reserved for mode select */
+ if (! mode_6)
+ mdpg[1] = 0; /* mode data length reserved for mode select */
+ if (md_len > len) {
+ fprintf(stderr, "change_mode_page: mode data length=%d exceeds "
+ "allocation length=%d\n", md_len, len);
+ return -1;
+ }
+
+ for (k = 0; k < mps->num_it_vals; ++k) {
+ ivp = &mps->it_vals[k];
+ mp_set_value(ivp->val, &ivp->mpi, mdpg + off);
+ }
+
+ if ((! (mdpg[off] & 0x80)) && save) {
+ fprintf(stderr, "change_mode_page: mode page indicates it is not "
+ "savable but\n '--save' option given (try without "
+ "it)\n");
+ return -1;
+ }
+ mdpg[off] &= 0x7f; /* mask out PS bit, reserved in mode select */
+ if (dummy) {
+ printf("Mode data that would have been written:\n");
+ dStrHex((const char *)mdpg, md_len, 1);
+ return 0;
+ }
+ if (verbose) {
+ printf("Mode data about to be written:\n");
+ dStrHex((const char *)mdpg, md_len, 1);
+ }
+ if (mode_6)
+ res = sg_ll_mode_select6(sg_fd, 1, save, mdpg, md_len, 1,
+ verbose);
+ else
+ res = sg_ll_mode_select10(sg_fd, 1, save, mdpg, md_len, 1,
+ verbose);
+ if (0 != res) {
+ fprintf(stderr, "change__mode_page: failed setting page: 0x%x,0x%x\n",
+ mps->page_num, mps->subpage_num);
+ return -1;
+ }
+ return 0;
+}
+
+/* Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
+ * bad field in cdb, -1 -> other failure */
+static int set_mode_page(int sg_fd, int pn, int spn, int save, int mode_6,
+ unsigned char * mode_pg, int mode_pg_len,
+ int dummy, int verbose)
+{
+ int len, off, md_len;
+ unsigned char * mdp;
+ char ebuff[EBUFF_SZ];
+ int ret = -1;
+
+ len = mode_pg_len + MODE_DATA_OVERHEAD;
+ mdp = malloc(len);
+ if (NULL ==mdp) {
+ fprintf(stderr, "set_mode_page: malloc failed, out of memory\n");
+ return -1;
+ }
+ memset(mdp, 0, len);
+ if (mode_6)
+ ret = sg_ll_mode_sense6(sg_fd, 0 /* dbd */, 0 /*current */, pn,
+ spn, mdp, ((len > 252) ? 252 : len), 1,
+ verbose);
+ else
+ ret = sg_ll_mode_sense10(sg_fd, 0 /* llbaa */, 0 /* dbd */,
+ 0 /* current */, pn, spn, mdp, len, 1,
+ verbose);
+ if (0 != ret) {
+ fprintf(stderr, "set_mode_page: failed fetching page: 0x%x,0x%x\n",
+ pn, spn);
+ goto err_out;
+ }
+ off = sg_mode_page_offset(mdp, len, mode_6, ebuff, EBUFF_SZ);
+ if (off < 0) {
+ fprintf(stderr, "set_mode_page: page offset failed: %s\n", ebuff);
+ ret = -1;
+ goto err_out;
+ }
+ if (mode_6)
+ md_len = mdp[0] + 1;
+ else
+ md_len = (mdp[0] << 8) + mdp[1] + 2;
+ mdp[0] = 0; /* mode data length reserved for mode select */
+ if (! mode_6)
+ mdp[1] = 0; /* mode data length reserved for mode select */
+ if (md_len > len) {
+ fprintf(stderr, "set_mode_page: mode data length=%d exceeds "
+ "allocation length=%d\n", md_len, len);
+ ret = -1;
+ goto err_out;
+ }
+ if ((md_len - off) > mode_pg_len) {
+ fprintf(stderr, "set_mode_page: mode length length=%d exceeds "
+ "new contents length=%d\n", md_len - off, mode_pg_len);
+ ret = -1;
+ goto err_out;
+ }
+ memcpy(mdp + off, mode_pg, md_len - off);
+ mdp[off] &= 0x7f; /* mask out PS bit, reserved in mode select */
+ if (dummy) {
+ printf("Mode data that would have been written:\n");
+ dStrHex((const char *)mdp, md_len, 1);
+ ret = 0;
+ goto err_out;
+ }
+ if (verbose) {
+ printf("Mode data about to be written:\n");
+ dStrHex((const char *)mdp, md_len, 1);
+ }
+ if (mode_6)
+ ret = sg_ll_mode_select6(sg_fd, 1, save, mdp, md_len, 1,
+ verbose);
+ else
+ ret = sg_ll_mode_select10(sg_fd, 1, save, mdp, md_len, 1,
+ verbose);
+ if (0 != ret) {
+ fprintf(stderr, "set_mode_page: failed setting page: 0x%x,0x%x\n",
+ pn, spn);
+ goto err_out;
+ }
+
+err_out:
+ free(mdp);
+ return ret;
+}
+
+static int set_mp_defaults(int sg_fd, int pn, int spn, int saved,
+ int mode_6, int dummy, int verbose)
+{
+ int smask, res, len;
+ unsigned char cur_mp[DEF_MODE_RESP_LEN];
+ unsigned char def_mp[DEF_MODE_RESP_LEN];
+ const char * name;
+
+
+ smask = 0;
+ res = sg_get_mode_page_types(sg_fd, mode_6, pn, spn, DEF_MODE_RESP_LEN,
+ &smask, cur_mp, NULL, def_mp, NULL,
+ verbose);
+ if (SG_LIB_CAT_INVALID_OP == res) {
+ if (mode_6)
+ fprintf(stderr, "6 byte MODE SENSE cdb not supported, "
+ "try again without '-6' option\n");
+ else
+ fprintf(stderr, "10 byte MODE SENSE cdb not supported, "
+ "try again with '-6' option\n");
+ return -1;
+ }
+ if ((smask & 1)) {
+ if ((smask & 4)) {
+ if (cur_mp[0] & 0x40)
+ len = (cur_mp[2] << 8) + cur_mp[3] + 4; /* spf set */
+ else
+ len = cur_mp[1] + 2; /* spf clear (not subpage) */
+ return set_mode_page(sg_fd, pn, spn, saved, mode_6, def_mp,
+ len, dummy, verbose);
+ }
+ else {
+ name = get_mode_name(pn, spn);
+ if (name)
+ printf(">> %s mode page (default) not supported\n", name);
+ else if (0 == spn)
+ printf(">> mode page 0x%x (default) not supported\n", pn);
+ else
+ printf(">> mode page 0x%x,0x%x (default) not supported\n",
+ pn, spn);
+ return -1;
+ }
+ } else {
+ name = get_mode_name(pn, spn);
+ if (name)
+ printf(">> %s mode page not supported\n", name);
+ else if (0 == spn)
+ printf(">> mode page 0x%x not supported\n", pn);
+ else
+ printf(">> mode page 0x%x,0x%x not supported\n", pn, spn);
+ return -1;
+ }
+}
+
+/* Trying to decode multipliers as sg_get_num() [in sg_libs does] would
+ * only confuse things here, so use this local trimmed version */
+static int get_num(const char * buf)
+{
+ int res;
+ int num;
+ unsigned int unum;
+
+ if ((NULL == buf) || ('\0' == buf[0]))
+ return -1;
+ if (('0' == buf[0]) && (('x' == buf[1]) || ('X' == buf[1]))) {
+ res = sscanf(buf + 2, "%x", &unum);
+ num = unum;
+ } else
+ res = sscanf(buf, "%d", &num);
+ if (1 == res)
+ return num;
+ else
+ return -1;
+}
+
+static int build_mp_settings(const char * arg,
+ struct mode_page_settings * mps, int clear,
+ int get)
+{
+ int len, b_sz, num, from, cont;
+ unsigned int u;
+ char buff[64];
+ char acron[64];
+ char vb[64];
+ const char * cp;
+ const char * ncp;
+ const char * ecp;
+ struct mode_page_it_val * ivp;
+ const struct mode_page_item * mpi;
+ const struct mode_page_item * prev_mpi;
+
+ b_sz = sizeof(buff) - 1;
+ cp = arg;
+ while (mps->num_it_vals < MAX_MP_IT_VAL) {
+ memset(buff, 0, sizeof(buff));
+ ivp = &mps->it_vals[mps->num_it_vals];
+ if ('\0' == *cp)
+ break;
+ ncp = strchr(cp, ',');
+ if (ncp) {
+ len = ncp - cp;
+ if (len <= 0) {
+ ++cp;
+ continue;
+ }
+ strncpy(buff, cp, (len < b_sz ? len : b_sz));
+ } else
+ strncpy(buff, cp, b_sz);
+ if (isalpha(buff[0])) {
+ ecp = strchr(buff, '=');
+ if (ecp) {
+ strncpy(acron, buff, ecp - buff);
+ acron[ecp - buff] = '\0';
+ strcpy(vb, ecp + 1);
+ if (0 == strcmp("-1", vb))
+ ivp->val = -1;
+ else {
+ ivp->val = get_num(vb);
+ if (-1 == ivp->val) {
+ fprintf(stderr, "build_mp_settings: unable to "
+ "decode: %s value\n", buff);
+ fprintf(stderr, " expected: <acronym>[=<val>]\n");
+ return -1;
+ }
+ }
+ } else {
+ strcpy(acron, buff);
+ ivp->val = ((clear || get) ? 0 : -1);
+ }
+ from = 0;
+ cont = 0;
+ prev_mpi = NULL;
+ if (get) {
+ do {
+ mpi = find_mitem_by_acron(acron, &from);
+ if (NULL == mpi) {
+ if (cont) {
+ mpi = prev_mpi;
+ break;
+ } else
+ fprintf(stderr, "build_mp_settings: couldn't "
+ "find acronym: %s\n", acron);
+ return -1;
+ }
+ if (mps->page_num < 0) {
+ mps->page_num = mpi->page_num;
+ mps->subpage_num = mpi->subpage_num;
+ break;
+ }
+ cont = 1;
+ prev_mpi = mpi;
+ } while ((mps->page_num != mpi->page_num) ||
+ (mps->subpage_num != mpi->subpage_num));
+ } else {
+ do {
+ mpi = find_mitem_by_acron(acron, &from);
+ if (NULL == mpi) {
+ if (cont) {
+ fprintf(stderr, "build_mp_settings: mode page "
+ "of acronym: %s [0x%x,0x%x] doesn't "
+ "match prior\n", acron,
+ prev_mpi->page_num,
+ prev_mpi->subpage_num);
+ fprintf(stderr, " mode page: 0x%x,0x%x\n",
+ mps->page_num, mps->subpage_num);
+ } else
+ fprintf(stderr, "build_mp_settings: couldn't "
+ "find acronym: %s\n", acron);
+ return -1;
+ }
+ if (mps->page_num < 0) {
+ mps->page_num = mpi->page_num;
+ mps->subpage_num = mpi->subpage_num;
+ break;
+ }
+ cont = 1;
+ prev_mpi = mpi;
+ } while ((mps->page_num != mpi->page_num) ||
+ (mps->subpage_num != mpi->subpage_num));
+ }
+ if (mpi->num_bits < 32)
+ ivp->val &= (1 << mpi->num_bits) - 1;
+ ivp->mpi = *mpi; /* struct assignment */
+ } else { /* expect "byte_off:bit_off:num_bits[=<val>]" */
+ if ((0 == strncmp("0x", buff, 2)) ||
+ (0 == strncmp("0X", buff, 2))) {
+ num = sscanf(buff + 2, "%x:%d:%d=%s", &u,
+ &ivp->mpi.start_bit, &ivp->mpi.num_bits, vb);
+ ivp->mpi.start_byte = u;
+ } else
+ num = sscanf(buff, "%d:%d:%d=%s", &ivp->mpi.start_byte,
+ &ivp->mpi.start_bit, &ivp->mpi.num_bits, vb);
+ if (num < 3) {
+ fprintf(stderr, "build_mp_settings: unable to decode: %s\n",
+ buff);
+ fprintf(stderr, " expected: byte_off:bit_off:num_bits[="
+ "<val>]\n");
+ return -1;
+ }
+ if (3 == num)
+ ivp->val = ((clear || get) ? 0 : -1);
+ else {
+ if (0 == strcmp("-1", vb))
+ ivp->val = -1;
+ else {
+ ivp->val = get_num(vb);
+ if (-1 == ivp->val) {
+ fprintf(stderr, "build_mp_settings: unable to "
+ "decode byte_off:bit_off:num_bits value\n");
+ return -1;
+ }
+ }
+ }
+ if (ivp->mpi.start_byte < 0) {
+ fprintf(stderr, "build_mp_settings: need positive start "
+ "byte offset\n");
+ return -1;
+ }
+ if ((ivp->mpi.start_bit < 0) || (ivp->mpi.start_bit > 7)) {
+ fprintf(stderr, "build_mp_settings: need start bit in "
+ "0..7 range (inclusive)\n");
+ return -1;
+ }
+ if ((ivp->mpi.num_bits < 1) || (ivp->mpi.num_bits > 32)) {
+ fprintf(stderr, "build_mp_settings: need number of bits in "
+ "1..32 range (inclusive)\n");
+ return -1;
+ }
+ if (mps->page_num < 0) {
+ fprintf(stderr, "build_mp_settings: need '--page=' option "
+ "for mode page number\n");
+ return -1;
+ } else if (get) {
+ ivp->mpi.page_num = mps->page_num;
+ ivp->mpi.subpage_num = mps->subpage_num;
+ }
+ if (ivp->mpi.num_bits < 32)
+ ivp->val &= (1 << ivp->mpi.num_bits) - 1;
+ }
+ ++mps->num_it_vals;
+ if (ncp)
+ cp = ncp + 1;
+ else
+ break;
+ }
+ return 0;
+}
+
+int main(int argc, char * argv[])
+{
+ int sg_fd, res, c, pdt, flags;
+ int six_byte_cdb = 0;
+ int all = 0;
+ const char * clear_str = NULL;
+ const char * get_str = NULL;
+ const char * set_str = NULL;
+ int defaults = 0;
+ int dummy = 0;
+ int enumerate = 0;
+ int hex = 0;
+ int inquiry = 0;
+ int long_out = 0;
+ int saved = 0;
+ int verbose = 0;
+ char device_name[256];
+ int pn = -1;
+ int spn = -1;
+ int rw = 0;
+ struct sg_simple_inquiry_resp sir;
+ const struct values_name_t * vnp;
+ struct mode_page_settings mp_settings;
+ char * cp;
+ int ret = 1;
+
+ memset(device_name, 0, sizeof(device_name));
+ memset(&mp_settings, 0, sizeof(mp_settings));
+ while (1) {
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "6ac:Ddeg:hHilp:s:SvV", long_options,
+ &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case '6':
+ six_byte_cdb = 1;
+ break;
+ case 'a':
+ all = 1;
+ break;
+ case 'c':
+ clear_str = optarg;
+ rw = 1;
+ break;
+ case 'd':
+ dummy = 1;
+ break;
+ case 'D':
+ defaults = 1;
+ rw = 1;
+ break;
+ case 'e':
+ enumerate = 1;
+ break;
+ case 'g':
+ get_str = optarg;
+ break;
+ case 'h':
+ case '?':
+ usage();
+ return 0;
+ case 'H':
+ hex = 1;
+ break;
+ case 'i':
+ inquiry = 1;
+ break;
+ case 'l':
+ long_out = 1;
+ break;
+ case 'p':
+ if (isalpha(optarg[0])) {
+ vnp = find_mp_by_acron(optarg);
+ if (NULL == vnp) {
+ fprintf(stderr, "mode page acronym not found\n");
+ return 1;
+ }
+ pn = vnp->value;
+ spn = vnp->subvalue;
+ } else {
+ cp = strchr(optarg, ',');
+ pn = get_num(optarg);
+ if ((pn < 0) || (pn > 255)) {
+ fprintf(stderr, "Bad page code value after '-p' "
+ "switch\n");
+ return 1;
+ }
+ if (cp) {
+ spn = get_num(cp + 1);
+ if ((spn < 0) || (spn > 255)) {
+ fprintf(stderr, "Bad page code value after "
+ "'-p' switch\n");
+ return 1;
+ }
+ } else
+ spn = 0;
+ }
+ break;
+ case 's':
+ set_str = optarg;
+ rw = 1;
+ break;
+ case 'S':
+ saved = 1;
+ rw = 1;
+ break;
+ case 'v':
+ ++verbose;
+ break;
+ case 'V':
+ fprintf(stderr, ME "version: %s\n", version_str);
+ return 0;
+ default:
+ fprintf(stderr, "unrecognised switch code 0x%x ??\n", c);
+ usage();
+ return 1;
+ }
+ }
+ if (optind < argc) {
+ if ('\0' == device_name[0]) {
+ strncpy(device_name, argv[optind], sizeof(device_name) - 1);
+ device_name[sizeof(device_name) - 1] = '\0';
+ ++optind;
+ }
+ if (optind < argc) {
+ for (; optind < argc; ++optind)
+ fprintf(stderr, "Unexpected extra argument: %s\n",
+ argv[optind]);
+ usage();
+ return 1;
+ }
+ }
+/* think about --get= with --enumerate */
+ if (pn < 0) {
+ mp_settings.page_num = -1;
+ mp_settings.subpage_num = -1;
+ } else {
+ mp_settings.page_num = pn;
+ mp_settings.subpage_num = spn;
+ }
+ if (get_str) {
+ if (set_str || clear_str) {
+ fprintf(stderr, "'--get=' can't be used with '--set=' or "
+ "'--clear='\n");
+ return 1;
+ }
+ if (build_mp_settings(get_str, &mp_settings, 0, 1))
+ return 1;
+ }
+ if (enumerate) {
+ if (device_name[0] || set_str || clear_str || get_str || saved)
+ printf("Most option including <scsi_disk> are ignored when "
+ "'--enumerate' is given\n");
+ if (pn < 0) {
+ printf("Mode pages:\n");
+ list_mps();
+ }
+ if (all || (pn >= 0))
+ list_mitems(pn, spn);
+ return 0;
+ }
+ if (0 == device_name[0]) {
+ fprintf(stderr, "missing device name!\n");
+ usage();
+ return 1;
+ }
+
+ if (inquiry) {
+ fprintf(stderr, "INQUIRY VPD pages not supported yet\n");
+ return 1;
+ }
+ if (defaults && (set_str || clear_str || get_str)) {
+ fprintf(stderr, "'--get=', '--set=' or '--clear=' can't be used "
+ "with '--defaults'\n");
+ return 1;
+ }
+
+ if (set_str) {
+ if (build_mp_settings(set_str, &mp_settings, 0, 0))
+ return 1;
+ }
+ if (clear_str) {
+ if (build_mp_settings(clear_str, &mp_settings, 1, 0))
+ return 1;
+ }
+
+ if (verbose && (mp_settings.num_it_vals > 0)) {
+ struct mode_page_it_val * ivp;
+ int k;
+
+ printf("mp_settings: page,subpage=0x%x,0x%x num=%d\n",
+ mp_settings.page_num, mp_settings.subpage_num,
+ mp_settings.num_it_vals);
+ for (k = 0; k < mp_settings.num_it_vals; ++k) {
+ ivp = &mp_settings.it_vals[k];
+ if (get_str)
+ printf(" [0x%x,0x%x] byte_off=0x%x, bit_off=%d, num_bits"
+ "=%d val=%d acronym: %s\n", ivp->mpi.page_num,
+ ivp->mpi.subpage_num, ivp->mpi.start_byte,
+ ivp->mpi.start_bit, ivp->mpi.num_bits, ivp->val,
+ (ivp->mpi.acron ? ivp->mpi.acron : ""));
+ else
+ printf(" byte_off=0x%x, bit_off=%d, num_bits=%d "
+ " val=%d acronym: %s\n", ivp->mpi.start_byte,
+ ivp->mpi.start_bit, ivp->mpi.num_bits, ivp->val,
+ (ivp->mpi.acron ? ivp->mpi.acron : ""));
+ }
+ }
+
+ if (defaults && (pn < 0)) {
+ fprintf(stderr, "to set defaults, the '--page=' option must "
+ "be used\n");
+ return 1;
+ }
+
+ flags = (O_NONBLOCK | (rw ? O_RDWR : O_RDONLY));
+ sg_fd = open(device_name, flags);
+ if (sg_fd < 0) {
+ fprintf(stderr, ME "open error: %s, flags=0x%x: ", device_name,
+ flags);
+ perror("");
+ return 1;
+ }
+
+ if (sg_simple_inquiry(sg_fd, &sir, 0, verbose)) {
+ fprintf(stderr, "SCSI INQUIRY command failed on %s\n", device_name);
+ goto err_out;
+ }
+ pdt = sir.peripheral_type;
+ if (0 == hex) {
+ printf(" %s: %.8s %.16s %.4s",
+ device_name, sir.vendor, sir.product, sir.revision);
+ if (0 != pdt)
+ printf(" [pdt=%d]", pdt);
+ printf("\n");
+ if (! ((0 == pdt) || (4 == pdt) || (7 == pdt) || (0xe == pdt))) {
+ fprintf(stderr, " expected disk device type, got %s\n",
+ scsi_ptype_strs[pdt]);
+ }
+ }
+
+ if ((pn > 0x3e) || (spn > 0xfe)) {
+ fprintf(stderr, "Allowable mode page numbers are 0 to 62\n");
+ fprintf(stderr, " Allowable mode subpage numbers are 0 to 254\n");
+ goto err_out;
+ }
+ if (defaults) {
+ res = set_mp_defaults(sg_fd, pn, spn, saved, six_byte_cdb, dummy,
+ verbose);
+ if (0 != res)
+ goto err_out;
+ } else if (set_str || clear_str) {
+ if (mp_settings.num_it_vals < 1) {
+ fprintf(stderr, "no parameters found to set or clear\n");
+ goto err_out;
+ }
+ res = change_mode_page(sg_fd, saved, six_byte_cdb, &mp_settings,
+ dummy, verbose);
+ if (0 != res)
+ goto err_out;
+ } else if (get_str) {
+ if (mp_settings.num_it_vals < 1) {
+ fprintf(stderr, "no parameters found to get\n");
+ goto err_out;
+ }
+ get_mode_info(sg_fd, six_byte_cdb, &mp_settings, long_out, hex,
+ verbose);
+ } else
+ print_mode_info(sg_fd, six_byte_cdb, pn, spn, ((pn >= 0) ? 1 : all),
+ long_out, hex, verbose);
+ ret = 0;
+
+err_out:
+ res = close(sg_fd);
+ if (res < 0) {
+ perror(ME "close error");
+ return 1;
+ }
+ return ret;
+}
diff --git a/archive/sg3_utils.spec113 b/archive/sg3_utils.spec113
new file mode 100644
index 00000000..3dfc4873
--- /dev/null
+++ b/archive/sg3_utils.spec113
@@ -0,0 +1,234 @@
+Summary: Utilities for SCSI devices in Linux
+Name: sg3_utils
+Version: 1.13
+Release: 1
+Packager: Douglas Gilbert <dgilbert at interlog dot com>
+License: GPL/FreeBSD
+Group: Utilities/System
+Source: ftp://www.torque.net/sg/p/sg3_utils-%{version}.tgz
+Url: http://www.torque.net/sg/u_index.html
+Provides: sg_utils
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root/
+
+%description
+Collection of Linux utilities for devices that use the SCSI command set.
+Includes utilities to copy data based on "dd" syntax and semantics (called
+sg_dd, sgp_dd and sgm_dd); check INQUIRY data and VPD pages (sg_inq); check
+mode and log pages (sginfo, sg_modes and sg_logs); spin up and down
+disks (sg_start); do self tests (sg_senddiag); and various other functions.
+See the README, CHANGELOG and COVERAGE files. Requires the linux kernel 2.4
+series or later. In the 2.4 series SCSI generic device names (e.g. /dev/sg0)
+must be used. In the 2.6 series other device names may be used as
+well (e.g. /dev/sda).
+
+Warning: Some of these tools access the internals of your system and their
+incorrect usage may render your system inoperable.
+
+Authors:
+--------
+ Doug Gilbert <dgilbert at interlog dot com>
+ See CREDITS file
+
+%package devel
+Summary: Files needed for developing applications using the SCSI command set
+Group: Development/Libraries
+Requires: %{name} = %{version}-%{release}
+
+%description devel
+sg3_utils-devel contains a static library (libsgutils.a) and header files
+to support using the SCSI command set on devices in Linux.
+
+%prep
+%setup
+
+%build
+make LIBDIR=/usr/lib
+
+%install
+if [ "$RPM_BUILD_ROOT" != "/" ]; then
+ rm -rf $RPM_BUILD_ROOT
+fi
+make install INSTDIR=$RPM_BUILD_ROOT/usr/bin MANDIR=$RPM_BUILD_ROOT/usr/share/man LIBDIR=$RPM_BUILD_ROOT/usr/lib INCLUDEDIR=$RPM_BUILD_ROOT/usr/include
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(-,root,root)
+%attr(-,root,root) %doc CREDITS README README.sg_start
+%attr(-,root,root) %doc CHANGELOG INSTALL COVERAGE
+%attr(755,root,root) %{_bindir}/sg_dd
+%attr(755,root,root) %{_bindir}/sg_inq
+%attr(755,root,root) %{_bindir}/sg_scan
+%attr(755,root,root) %{_bindir}/sg_rbuf
+%attr(755,root,root) %{_bindir}/sginfo
+%attr(755,root,root) %{_bindir}/sg_readcap
+%attr(755,root,root) %{_bindir}/sgp_dd
+%attr(755,root,root) %{_bindir}/sg_map
+%attr(755,root,root) %{_bindir}/sg_turs
+%attr(755,root,root) %{_bindir}/sg_test_rwbuf
+%attr(755,root,root) %{_bindir}/sg_start
+%attr(755,root,root) %{_bindir}/sgm_dd
+%attr(755,root,root) %{_bindir}/sg_read
+%attr(755,root,root) %{_bindir}/sg_reset
+%attr(755,root,root) %{_bindir}/sg_modes
+%attr(755,root,root) %{_bindir}/sg_logs
+%attr(755,root,root) %{_bindir}/sg_senddiag
+%attr(755,root,root) %{_bindir}/sg_opcodes
+%attr(755,root,root) %{_bindir}/sg_persist
+%attr(755,root,root) %{_bindir}/sg_write_long
+%attr(755,root,root) %{_bindir}/sg_read_long
+%attr(755,root,root) %{_bindir}/sg_requests
+%attr(755,root,root) %{_bindir}/sg_ses
+%attr(755,root,root) %{_bindir}/sg_verify
+%attr(755,root,root) %{_bindir}/sg_emc_trespass
+%attr(755,root,root) %{_bindir}/sg_luns
+%attr(755,root,root) %{_bindir}/sg_sync
+%attr(755,root,root) %{_bindir}/sg_prevent
+%attr(755,root,root) %{_bindir}/sg_get_config
+%attr(755,root,root) %{_bindir}/sg_wr_mode
+%attr(755,root,root) %{_bindir}/sg_rtpg
+%attr(755,root,root) %{_bindir}/sg_reassign
+%attr(755,root,root) %{_bindir}/sg_format
+%attr(755,root,root) %{_libdir}/libsgutils.so
+%attr(755,root,root) %{_libdir}/libsgutils.so.1
+%attr(755,root,root) %{_libdir}/libsgutils.so.1.0.0
+# Mandrake compresses man pages with bzip2, RedHat with gzip
+%attr(-,root,root) %doc %{_mandir}/man8/sg_dd.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sgp_dd.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sgm_dd.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_read.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_map.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_scan.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_rbuf.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sginfo.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_readcap.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_turs.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_inq.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_test_rwbuf.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_start.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_reset.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_modes.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_logs.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_senddiag.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_opcodes.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_persist.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_write_long.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_read_long.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_requests.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_ses.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_verify.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_emc_trespass.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_luns.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_sync.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_prevent.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_get_config.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_wr_mode.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_rtpg.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_reassign.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_format.8*
+
+%files devel
+%defattr(-,root,root)
+%attr(644,root,root) %{_includedir}/scsi/sg_lib.h
+%attr(644,root,root) %{_includedir}/scsi/sg_cmds.h
+%attr(755,root,root) %{_libdir}/libsgutils.la
+%attr(644,root,root) %{_libdir}/libsgutils.a
+
+
+%changelog
+* Sun Mar 13 2005 - dgilbert at interlog dot com
+- add sg_format, sg_dd extensions
+ * sg3_utils-1.13
+
+* Fri Jan 21 2005 - dgilbert at interlog dot com
+- add sg_wr_mode, sg_rtpg + sg_reassign; sginfo sas tweaks
+ * sg3_utils-1.12
+
+* Fri Nov 26 2004 - dgilbert at interlog dot com
+- add sg_sync, sg_prevent and sg_get_config; fix sg_requests
+ * sg3_utils-1.11
+
+* Sat Oct 30 2004 - dgilbert at interlog dot com
+- fix read capacity (10+16), add sg_luns
+ * sg3_utils-1.10
+
+* Thu Oct 21 2004 - dgilbert at interlog dot com
+- sg_requests, sg_ses, sg_verify, libsgutils(sg_lib.c+sg_cmds.c), devel rpm
+ * sg3_utils-1.09
+
+* Tue Aug 31 2004 - dgilbert at interlog dot com
+- 'register+move' in sg_persist, sg_opcodes sorts, sg_write_long
+ * sg3_utils-1.08
+
+* Thu Jul 08 2004 - dgilbert at interlog dot com
+- add '-fHead' to sginfo, '-i' for sg_inq, new sg_opcodes + sg_persist
+ * sg3_utils-1.07
+
+* Mon Apr 26 2004 - dgilbert at interlog dot com
+- sg3_utils.spec for mandrake; more sginfo work, sg_scan, sg_logs
+ * sg3_utils-1.06
+
+* Wed Nov 12 2003 - dgilbert at interlog dot com
+- sg_readcap: sizes; sg_logs: double fetch; sg_map 256 sg devices; sginfo
+ * sg3_utils-1.05
+
+* Tue May 13 2003 - dgilbert at interlog dot com
+- default sg_turs '-n=' to 1, sg_logs gets '-t' for temperature, CREDITS
+ * sg3_utils-1.04
+
+* Wed Apr 02 2003 - dgilbert at interlog dot com
+- 6 byte CDBs for sg_modes, sg_start on block devs, sg_senddiag, man pages
+ * sg3_utils-1.03
+
+* Wed Jan 01 2003 - dgilbert at interlog dot com
+- interwork with block SG_IO, fix in sginfo, '-t' for sg_turs
+ * sg3_utils-1.02
+
+* Wed Aug 14 2002 - dgilbert at interlog dot com
+- raw switch in sg_inq
+ * sg3_utils-1.01
+
+* Sun Jul 28 2002 - dgilbert at interlog dot com
+- decode sg_logs pages, add dio to sgm_dd, drop "gen=1" arg, "of=/dev/null"
+ * sg3_utils-1.00
+
+* Sun Mar 17 2002 - dgilbert at interlog dot com
+- add sg_modes+sg_logs for sense pages, expand sg_inq, add fua+sync to sg_dd++
+ * sg3_utils-0.99
+
+* Sat Feb 16 2002 - dgilbert at interlog dot com
+- resurrect sg_reset; snprintf cleanup, time,gen+cdbsz args to sg_dd++
+ * sg3_utils-0.98
+
+* Sun Dec 23 2001 - dgilbert at interlog dot com
+- move isosize to archive directory; now found in util-linux-2.10s and later
+ * sg3_utils-0.97
+
+* Fri Dec 21 2001 - dgilbert at interlog dot com
+- add sgm_dd, sg_read, sg_simple4 and sg_simple16 [add mmap-ed IO support]
+ * sg3_utils-0.96
+
+* Sun Sep 15 2001 - dgilbert at interlog dot com
+- sg_map can do inquiry; sg_dd, sgp_dd + sgq_dd dio help
+ * sg3_utils-0.95
+
+* Sun Apr 19 2001 - dgilbert at interlog dot com
+- add sg_start, improve sginfo and sg_map [Kurt Garloff]
+ * sg3_utils-0.94
+
+* Sun Mar 5 2001 - dgilbert at interlog dot com
+- add scsi_devfs_scan, add sg_include.h, 'coe' more general in sgp_dd
+ * sg3_utils-0.93
+
+* Tue Jan 16 2001 - dgilbert at interlog dot com
+- clean sg_err.h include dependencies, bug fixes, Makefile in archive directory
+ * sg3_utils-0.92
+
+* Mon Dec 21 2000 - dgilbert at interlog dot com
+- signals for sg_dd, man pages and additions for sg_rbuf and isosize
+ * sg3_utils-0.91
+
+* Mon Dec 11 2000 - dgilbert at interlog dot com
+- Initial creation of package, containing
+ * sg3_utils-0.90