add sg_safte utility

git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@108 6180dd3e-e324-4e3e-922d-17de1ae2f315
diff --git a/COVERAGE b/COVERAGE
index e38aa2a..e472ef9 100644
--- a/COVERAGE
+++ b/COVERAGE
@@ -34,7 +34,7 @@
 READ(10)            sg_dd, sgm_dd, sgp_dd, sg_read
 READ(12)            sg_dd, sgm_dd, sgp_dd, sg_read
 READ(16)            sg_dd, sgm_dd, sgp_dd, sg_read
-READ BUFFER         sg_rbuf, sg_test_rwbuf, sg_read_buffer
+READ BUFFER         sg_rbuf, sg_test_rwbuf, sg_read_buffer, sg_safte, ++
 READ CAPACITY(10)   sg_readcap, sg_dd, sgm_dd, sgp_dd, sg_format, ++
 READ CAPACITY(16)   sg_readcap, sg_dd, sgm_dd, sgp_dd, sg_format, ++
 READ DEFECT(10)     sginfo, sg_reassign('-g'), ++
@@ -61,7 +61,7 @@
 WRITE(10)           sg_dd, sgm_dd, sgp_dd
 WRITE(12)           sg_dd, sgm_dd, sgp_dd
 WRITE(16)           sg_dd, sgm_dd, sgp_dd
-WRITE BUFFER        sg_test_rwbuf, sg_write_buffer
+WRITE BUFFER        sg_test_rwbuf, sg_write_buffer, ++
 WRITE LONG (10)     sg_write_long, ++
 WRITE LONG (16)     sg_write_long, ++
 
@@ -87,4 +87,4 @@
 
 
 Doug Gilbert
-31st August 2007
+29th September 2007
diff --git a/CREDITS b/CREDITS
index f401635..b2ccffb 100644
--- a/CREDITS
+++ b/CREDITS
@@ -29,6 +29,10 @@
 Grant Grundler <grundler at parisc-linux dot org> co-author of blk512-linux
         that has become sg_format [20050201]
 
+Hannes Reinecke <hare at suse dot de> 
+        contributed sg_rdac, (and the corresponding VPD entry to
+        sg_vpd_vendor), sg_stpg and sg_safte [20070927]
+
 Hayashi Naoyuki <titan at culzean dot org>
         port to Tru64 [20060127]
 
@@ -36,7 +40,7 @@
         programs for the original sg driver
 
 Ingo van Lil <inguin at gmx dot de>
-	contributed sg_raw [20070331]
+        contributed sg_raw [20070331]
 
 James Bottomley <jejb at parisc-linux dot org> co-author of blk512-linux
         that has become sg_format [20050201]
@@ -69,7 +73,7 @@
 Saeed Bishara contributed sg_write_long
 
 Thomas Kolbe <tkolbe at partnersdata dot com>
-	Solaris port help and testing [20070503]
+        Solaris port help and testing [20070503]
 
 Tim Hunt <tim at timhunt dot net> increased number of (sd and sg) devices
         that sginfo could detect.
@@ -82,4 +86,4 @@
 
 
 Doug Gilbert
-16th July 2007
+27th September 2007
diff --git a/ChangeLog b/ChangeLog
index ea91a7f..072d4a5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,8 +2,9 @@
 some description at the top of its ".c" file. All utilities in the main
 directory have their own "man" pages. There is also a sg3_utils man page.
 
-Changelog for sg3_utils-1.25 [20070926]
+Changelog for sg3_utils-1.25 [20070929]
   - sg_stpg: new utility to Set Target Port Groups
+  - sg_safte: new utility to query SAF-TE processor (SES like)
   - sg_cmds_extra: add sg_ll_set_tgt_prt_grp()
   - sg_sat_set_features: new utility (actually copied from examples
     directory); renamed examples version: sg__sat_set_features
diff --git a/README b/README
index 97e4b9c..06286c7 100644
--- a/README
+++ b/README
@@ -152,7 +152,7 @@
     sg_format, sg_ident, sg_inq, sg_logs, sg_luns, sg_map, sg_map26,
     sg_modes, sg_opcodes, sg_persist, sg_prevent, sg_raw, sg_rbuf, sg_rdac,
     sg_read, sg_readcap, sg_read_buffer, sg_read_long, sg_reassign,
-    sg_request, sg_reset, sg_rmsn, sg_rtpg, sg_sat_identify,
+    sg_request, sg_reset, sg_rmsn, sg_rtpg, sg_safte, sg_sat_identify,
     sg_sat_set_features, sg_scan, sg_senddiag, sg_ses, sg_start, sg_stpg,
     sg_sync, sg_test_rwbuff, sg_turs, sg_verify, sg_vpd, sg_write_buffer,
     sg_write_long, sg_wr_mode
@@ -286,7 +286,7 @@
 The more recent utilities that use "getopt_long" only are:
   - sg_format sg_get_config sg_ident sg_luns sg_map26 sg_persist
     sg_prevent sg_raw sg_read_buffer sg_read_long sg_reassign sg_requests
-    sg_rmsn sg_rtpg sg_sat_identify sg_sat_set_features
+    sg_rmsn sg_rtpg sg_sat_identify sg_safte sg_sat_set_features
     sg_sat_set_features sg_scan(w) sg_ses sg_stpg sg_sync sg_test_rwbuf
     sg_verify sg_vpd sg_write_buffer sg_write_long sg_wr_mode
 
@@ -327,4 +327,4 @@
 
 
 Doug Gilbert
-26th September 2007
+29th September 2007
diff --git a/README.freebsd b/README.freebsd
index b880ae0..dbe9ba0 100644
--- a/README.freebsd
+++ b/README.freebsd
@@ -27,7 +27,9 @@
     sg_requests
     sg_rmsn
     sg_rtpg
+    sg_safte
     sg_sat_identify
+    sg_sat_set_features
     sg_senddiag
     sg_ses
     sg_start
@@ -82,4 +84,4 @@
 
 
 Doug Gilbert
-31st August 2007
+29th September 2007
diff --git a/README.solaris b/README.solaris
index d229653..b94eee4 100644
--- a/README.solaris
+++ b/README.solaris
@@ -27,7 +27,9 @@
     sg_requests
     sg_rmsn
     sg_rtpg
+    sg_safte
     sg_sat_identify
+    sg_sat_set_features
     sg_senddiag
     sg_ses
     sg_start
@@ -76,4 +78,4 @@
 
 
 Doug Gilbert
-31st August 2007
+29th September 2007
diff --git a/README.tru64 b/README.tru64
index fb7023f..3c6db4b 100644
--- a/README.tru64
+++ b/README.tru64
@@ -27,7 +27,9 @@
     sg_requests
     sg_rmsn
     sg_rtpg
+    sg_safte
     sg_sat_identify
+    sg_sat_set_features
     sg_senddiag
     sg_ses
     sg_start
@@ -81,4 +83,4 @@
 
 
 Doug Gilbert
-31st August 2007
+29th September 2007
diff --git a/README.win32 b/README.win32
index 4a9b489..f806bc6 100644
--- a/README.win32
+++ b/README.win32
@@ -38,7 +38,9 @@
     sg_requests
     sg_rmsn
     sg_rtpg
+    sg_safte
     sg_sat_identify
+    sg_sat_set_features
     sg_scan         [this is Windows specific]
     sg_senddiag
     sg_ses
@@ -130,4 +132,4 @@
 
 
 Doug Gilbert
-31st August 2007
+29th September 2007
diff --git a/debian/changelog b/debian/changelog
index d9c258f..f6dbed9 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,7 +2,7 @@
 
   * New upstream version
 
- -- Doug Gilbert <[email protected]>  Wed, 26 Sep 2007 23:00:00 -0400
+ -- Doug Gilbert <[email protected]>  Sat, 29 Sep 2007 01:00:00 -0400
 
 sg3-utils (1.24-1) unstable; urgency=low
 
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 91f67d4..284ec50 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -14,7 +14,7 @@
 	sgm_dd.8 sg_modes.8 sg_opcodes.8 sgp_dd.8 sg_persist.8 \
 	sg_prevent.8 sg_raw.8 sg_rbuf.8 sg_rdac.8 sg_read.8 sg_readcap.8 \
 	sg_read_buffer.8 sg_read_long.8 sg_reassign.8 sg_requests.8 \
-	sg_reset.8 sg_rmsn.8 sg_rtpg.8 sg_sat_identify.8 \
+	sg_reset.8 sg_rmsn.8 sg_rtpg.8 sg_safte.8 sg_sat_identify.8 \
 	sg_sat_set_features.8 sg_scan.8 sg_senddiag.8 sg_ses.8 sg_start.8 \
 	sg_stpg.8 sg_sync.8 sg_test_rwbuf.8 sg_turs.8 sg_verify.8 sg_vpd.8 \
 	sg_write_buffer.8 sg_write_long.8 sg_wr_mode.8
@@ -31,8 +31,8 @@
 	sg_modes.8 sg_opcodes.8 sg_persist.8 \
 	sg_prevent.8 sg_raw.8 sg_rdac.8 sg_readcap.8 sg_read_buffer.8 \
 	sg_read_long.8 sg_reassign.8 sg_requests.8 sg_rmsn.8 \
-	sg_rtpg.8 sg_sat_identify.8 sg_sat_set_features.8 sg_scan.8 \
-	sg_senddiag.8 sg_ses.8 sg_start.8 sg_stpg.8 sg_sync.8 \
+	sg_rtpg.8 sg_safte.8 sg_sat_identify.8 sg_sat_set_features.8 \
+	sg_scan.8 sg_senddiag.8 sg_ses.8 sg_start.8 sg_stpg.8 sg_sync.8 \
 	sg_turs.8 sg_verify.8 sg_vpd.8 sg_write_buffer.8 sg_write_long.8 \
 	sg_wr_mode.8
 
@@ -48,8 +48,8 @@
 	sg_modes.8 sg_opcodes.8 sg_persist.8 \
 	sg_prevent.8 sg_raw.8 sg_rdac.8 sg_readcap.8 sg_read_buffer.8 \
 	sg_read_long.8 sg_reassign.8 sg_requests.8 sg_rmsn.8 \
-	sg_rtpg.8 sg_sat_identify.8 sg_sat_set_features.8 sg_scan.8 \
-	sg_senddiag.8 sg_ses.8 sg_start.8 sg_stpg.8 sg_sync.8 \
+	sg_rtpg.8 sg_safte.8 sg_sat_identify.8 sg_sat_set_features.8 \
+	sg_scan.8 sg_senddiag.8 sg_ses.8 sg_start.8 sg_stpg.8 sg_sync.8 \
 	sg_turs.8 sg_verify.8 sg_vpd.8 sg_write_buffer.8 sg_write_long.8 \
 	sg_wr_mode.8
 
@@ -65,7 +65,7 @@
 	sg_modes.8 sg_opcodes.8 sg_persist.8 \
 	sg_prevent.8 sg_raw.8 sg_rdac.8 sg_readcap.8 sg_read_buffer.8 \
 	sg_read_long.8 sg_reassign.8 sg_requests.8 sg_rmsn.8 \
-	sg_rtpg.8 sg_sat_identify.8 sg_sat_set_features.8 \
+	sg_rtpg.8 sg_safte.8 sg_sat_identify.8 sg_sat_set_features.8 \
 	sg_senddiag.8 sg_ses.8 sg_start.8 sg_stpg.8 sg_sync.8 \
 	sg_turs.8 sg_verify.8 sg_vpd.8 sg_write_buffer.8 sg_write_long.8 \
 	sg_wr_mode.8
@@ -82,7 +82,7 @@
 	sg_modes.8 sg_opcodes.8 sg_persist.8 \
 	sg_prevent.8 sg_raw.8 sg_rdac.8 sg_readcap.8 sg_read_buffer.8 \
 	sg_read_long.8 sg_reassign.8 sg_requests.8 sg_rmsn.8 \
-	sg_rtpg.8 sg_sat_identify.8 sg_sat_set_features.8 \
+	sg_rtpg.8 sg_safte.8 sg_sat_identify.8 sg_sat_set_features.8 \
 	sg_senddiag.8 sg_ses.8 sg_start.8 sg_stpg.8 sg_sync.8 \
 	sg_turs.8 sg_verify.8 sg_vpd.8 sg_write_buffer.8 sg_write_long.8 \
 	sg_wr_mode.8
@@ -99,7 +99,7 @@
 	sg_modes.8 sg_opcodes.8 sg_persist.8 \
 	sg_prevent.8 sg_raw.8 sg_rdac.8 sg_readcap.8 sg_read_buffer.8 \
 	sg_read_long.8 sg_reassign.8 sg_requests.8 sg_rmsn.8 \
-	sg_rtpg.8 sg_sat_identify.8 sg_sat_set_features.8 \
+	sg_rtpg.8 sg_safte.8 sg_sat_identify.8 sg_sat_set_features.8 \
 	sg_senddiag.8 sg_ses.8 sg_start.8 sg_stpg.8 sg_sync.8 \
 	sg_turs.8 sg_verify.8 sg_vpd.8 sg_write_buffer.8 sg_write_long.8 \
 	sg_wr_mode.8
diff --git a/doc/Makefile.in b/doc/Makefile.in
index 75786cb..3b424b6 100644
--- a/doc/Makefile.in
+++ b/doc/Makefile.in
@@ -162,7 +162,7 @@
 @OS_FREEBSD_TRUE@	sg_modes.8 sg_opcodes.8 sg_persist.8 \
 @OS_FREEBSD_TRUE@	sg_prevent.8 sg_raw.8 sg_rdac.8 sg_readcap.8 sg_read_buffer.8 \
 @OS_FREEBSD_TRUE@	sg_read_long.8 sg_reassign.8 sg_requests.8 sg_rmsn.8 \
-@OS_FREEBSD_TRUE@	sg_rtpg.8 sg_sat_identify.8 sg_sat_set_features.8 \
+@OS_FREEBSD_TRUE@	sg_rtpg.8 sg_safte.8 sg_sat_identify.8 sg_sat_set_features.8 \
 @OS_FREEBSD_TRUE@	sg_senddiag.8 sg_ses.8 sg_start.8 sg_stpg.8 sg_sync.8 \
 @OS_FREEBSD_TRUE@	sg_turs.8 sg_verify.8 sg_vpd.8 sg_write_buffer.8 sg_write_long.8 \
 @OS_FREEBSD_TRUE@	sg_wr_mode.8
@@ -180,7 +180,7 @@
 @OS_LINUX_TRUE@	sgm_dd.8 sg_modes.8 sg_opcodes.8 sgp_dd.8 sg_persist.8 \
 @OS_LINUX_TRUE@	sg_prevent.8 sg_raw.8 sg_rbuf.8 sg_rdac.8 sg_read.8 sg_readcap.8 \
 @OS_LINUX_TRUE@	sg_read_buffer.8 sg_read_long.8 sg_reassign.8 sg_requests.8 \
-@OS_LINUX_TRUE@	sg_reset.8 sg_rmsn.8 sg_rtpg.8 sg_sat_identify.8 \
+@OS_LINUX_TRUE@	sg_reset.8 sg_rmsn.8 sg_rtpg.8 sg_safte.8 sg_sat_identify.8 \
 @OS_LINUX_TRUE@	sg_sat_set_features.8 sg_scan.8 sg_senddiag.8 sg_ses.8 sg_start.8 \
 @OS_LINUX_TRUE@	sg_stpg.8 sg_sync.8 sg_test_rwbuf.8 sg_turs.8 sg_verify.8 sg_vpd.8 \
 @OS_LINUX_TRUE@	sg_write_buffer.8 sg_write_long.8 sg_wr_mode.8
@@ -192,7 +192,7 @@
 @OS_OSF_TRUE@	sg_modes.8 sg_opcodes.8 sg_persist.8 \
 @OS_OSF_TRUE@	sg_prevent.8 sg_raw.8 sg_rdac.8 sg_readcap.8 sg_read_buffer.8 \
 @OS_OSF_TRUE@	sg_read_long.8 sg_reassign.8 sg_requests.8 sg_rmsn.8 \
-@OS_OSF_TRUE@	sg_rtpg.8 sg_sat_identify.8 sg_sat_set_features.8 \
+@OS_OSF_TRUE@	sg_rtpg.8 sg_safte.8 sg_sat_identify.8 sg_sat_set_features.8 \
 @OS_OSF_TRUE@	sg_senddiag.8 sg_ses.8 sg_start.8 sg_stpg.8 sg_sync.8 \
 @OS_OSF_TRUE@	sg_turs.8 sg_verify.8 sg_vpd.8 sg_write_buffer.8 sg_write_long.8 \
 @OS_OSF_TRUE@	sg_wr_mode.8
@@ -204,7 +204,7 @@
 @OS_SOLARIS_TRUE@	sg_modes.8 sg_opcodes.8 sg_persist.8 \
 @OS_SOLARIS_TRUE@	sg_prevent.8 sg_raw.8 sg_rdac.8 sg_readcap.8 sg_read_buffer.8 \
 @OS_SOLARIS_TRUE@	sg_read_long.8 sg_reassign.8 sg_requests.8 sg_rmsn.8 \
-@OS_SOLARIS_TRUE@	sg_rtpg.8 sg_sat_identify.8 sg_sat_set_features.8 \
+@OS_SOLARIS_TRUE@	sg_rtpg.8 sg_safte.8 sg_sat_identify.8 sg_sat_set_features.8 \
 @OS_SOLARIS_TRUE@	sg_senddiag.8 sg_ses.8 sg_start.8 sg_stpg.8 sg_sync.8 \
 @OS_SOLARIS_TRUE@	sg_turs.8 sg_verify.8 sg_vpd.8 sg_write_buffer.8 sg_write_long.8 \
 @OS_SOLARIS_TRUE@	sg_wr_mode.8
@@ -216,8 +216,8 @@
 @OS_WIN32_CYGWIN_TRUE@	sg_modes.8 sg_opcodes.8 sg_persist.8 \
 @OS_WIN32_CYGWIN_TRUE@	sg_prevent.8 sg_raw.8 sg_rdac.8 sg_readcap.8 sg_read_buffer.8 \
 @OS_WIN32_CYGWIN_TRUE@	sg_read_long.8 sg_reassign.8 sg_requests.8 sg_rmsn.8 \
-@OS_WIN32_CYGWIN_TRUE@	sg_rtpg.8 sg_sat_identify.8 sg_sat_set_features.8 sg_scan.8 \
-@OS_WIN32_CYGWIN_TRUE@	sg_senddiag.8 sg_ses.8 sg_start.8 sg_stpg.8 sg_sync.8 \
+@OS_WIN32_CYGWIN_TRUE@	sg_rtpg.8 sg_safte.8 sg_sat_identify.8 sg_sat_set_features.8 \
+@OS_WIN32_CYGWIN_TRUE@	sg_scan.8 sg_senddiag.8 sg_ses.8 sg_start.8 sg_stpg.8 sg_sync.8 \
 @OS_WIN32_CYGWIN_TRUE@	sg_turs.8 sg_verify.8 sg_vpd.8 sg_write_buffer.8 sg_write_long.8 \
 @OS_WIN32_CYGWIN_TRUE@	sg_wr_mode.8
 
@@ -228,8 +228,8 @@
 @OS_WIN32_MINGW_TRUE@	sg_modes.8 sg_opcodes.8 sg_persist.8 \
 @OS_WIN32_MINGW_TRUE@	sg_prevent.8 sg_raw.8 sg_rdac.8 sg_readcap.8 sg_read_buffer.8 \
 @OS_WIN32_MINGW_TRUE@	sg_read_long.8 sg_reassign.8 sg_requests.8 sg_rmsn.8 \
-@OS_WIN32_MINGW_TRUE@	sg_rtpg.8 sg_sat_identify.8 sg_sat_set_features.8 sg_scan.8 \
-@OS_WIN32_MINGW_TRUE@	sg_senddiag.8 sg_ses.8 sg_start.8 sg_stpg.8 sg_sync.8 \
+@OS_WIN32_MINGW_TRUE@	sg_rtpg.8 sg_safte.8 sg_sat_identify.8 sg_sat_set_features.8 \
+@OS_WIN32_MINGW_TRUE@	sg_scan.8 sg_senddiag.8 sg_ses.8 sg_start.8 sg_stpg.8 sg_sync.8 \
 @OS_WIN32_MINGW_TRUE@	sg_turs.8 sg_verify.8 sg_vpd.8 sg_write_buffer.8 sg_write_long.8 \
 @OS_WIN32_MINGW_TRUE@	sg_wr_mode.8
 
diff --git a/doc/sg_safte.8 b/doc/sg_safte.8
new file mode 100644
index 0000000..f71f0e0
--- /dev/null
+++ b/doc/sg_safte.8
@@ -0,0 +1,118 @@
+.TH SG_SAFTE "8" "September 2007" "sg3_utils\-1.25" SG3_UTILS
+.SH NAME
+sg_safte \- Fetch status from a SCSI Accessed Fault\-Tolerant Enclosure
+(SAF\-TE) device
+.SH SYNOPSIS
+.B sg_safte
+[\fI\-\-config\fR] [\fI\-\-devstatus\fR] [\fI\-\-encstatus\fR]
+[\fI\-\-flags\fR] [\fI\-\-help\fR] [\fI\-\-insertions\fR]
+[\fI\-\-usage\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Fetches enclosure status (via a SCSI READ BUFFER command).
+The \fIDEVICE\fR should be a SAF\-TE device which may be a storage
+array controller (INQUIRY peripheral device type 0xc) or a generic
+processor device (INQUIRY peripheral device type 0x3).
+.PP
+If no options are given (only the \fIDEVICE\fR argument) then the
+overall enclosure status as reported by the option
+.I
+\-\-config
+.R
+is reported.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-c\fR, \fB\-\-config\fR
+will issues a 
+.I
+Read Enclosure Configuration
+.R
+(READ BUFFER ID 0) cdb to the device, which returns a list of the
+enclosure hardware resources.
+.TP
+\fB\-d\fR, \fB\-\-devstatus\fR
+will issue a
+.I
+Read Device Slot Status
+.R
+(READ BUFFER ID 4) cdb to the device, which returns information about
+the current state of each drive or slot.
+.TP
+\fB\-s\fR, \fB\-\-encstatus\fR
+will issue a
+.I
+Read Enclosure Status
+.R
+(READ BUFFER ID 1) cdb to the device, which returns the operational
+state of the components.
+.TP
+\fB\-f\fR, \fB\-\-flags\fR
+will issue a
+.I
+Read Global Flags
+.R
+(READ BUFFER ID 5) cdb to the device, which read the most recent state
+of the global flags of the RAID processor device.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-i\fR, \fB\-\-insertions\fR
+will issue a
+.I
+Read Device Insertions
+.R
+(READ BUFFER ID 3) cdb to the device, which returns information about
+the number of times devices have been inserted whilst the RAID system
+was powered on.
+.TP
+\fB\-u\fR, \fB\-\-usage\fR
+will issue a
+.I
+Read Usage Statistics
+.R
+(READ BUFFER ID 2) cdb to the device, which returns the information on
+total usage time and number of power\-on cycles of the RAID device.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH NOTES
+The implementation is based on the intermediate review document eg as
+found at
+.PP
+http://www.intel.com/design/servers/ipmi/saf\-te.htm
+.PP
+As the specification was never finalized this document serves as the
+de\-facto standard.
+.PP
+A similar functionality is provided by the SPC\-4 SCSI Enclosure
+Services devices (Peripheral device type 0xd), which can be queried
+with the sg_ses utility.
+.SH EXAMPLES
+To view the configuration:
+.PP
+   sg_safte /dev/sg1
+.PP
+To view the device slot status:
+.PP
+   sg_safte \-\-devstatus /dev/sg1
+.PP
+.SH EXIT STATUS
+The exit status of sg_ses is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Hannes Reinecke and Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2004\-2007 Hannes Reinecke and 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_inq, sg_ses (in sg3_utils package)
diff --git a/sg3_utils.spec b/sg3_utils.spec
index 184ccc8..c3e72a9 100644
--- a/sg3_utils.spec
+++ b/sg3_utils.spec
@@ -79,8 +79,8 @@
 %{_libdir}/*.la
 
 %changelog
-* Wed Sep 26 2007 - dgilbert at interlog dot com
-- add sg_sat_set_features, sg_stpg; sg_dd oflag=sparse,null
+* Sat Sep 29 2007 - dgilbert at interlog dot com
+- add sg_sat_set_features, sg_stpg, sg_safte; sg_dd oflag=sparse,null
   * sg3_utils-1.25
 
 * Mon May 07 2007 - dgilbert at interlog dot com
diff --git a/src/Makefile.am b/src/Makefile.am
index 4edf7c2..0726b16 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -13,10 +13,10 @@
 	sgm_dd sg_modes sg_opcodes sgp_dd sg_persist \
 	sg_prevent sg_raw sg_rbuf sg_rdac sg_read sg_readcap \
 	sg_read_buffer sg_read_long sg_reassign sg_requests \
-	sg_reset sg_rmsn sg_rtpg sg_sat_identify sg_sat_set_features \
-	sg_scan sg_senddiag sg_ses sg_start sg_stpg sg_sync sg_test_rwbuf \
-	sg_turs sg_verify sg_vpd sg_write_buffer sg_write_long \
-	sg_wr_mode
+	sg_reset sg_rmsn sg_rtpg sg_safte sg_sat_identify \
+	sg_sat_set_features sg_scan sg_senddiag sg_ses sg_start sg_stpg \
+	sg_sync sg_test_rwbuf sg_turs sg_verify sg_vpd sg_write_buffer \
+	sg_write_long sg_wr_mode
 
 endif
 
@@ -29,7 +29,7 @@
 	sg_modes sg_opcodes sg_persist \
 	sg_prevent sg_raw sg_rdac sg_readcap \
 	sg_read_buffer sg_read_long sg_reassign sg_requests sg_rmsn \
-	sg_rtpg sg_sat_identify sg_sat_set_features sg_scan \
+	sg_rtpg sg_safte sg_sat_identify sg_sat_set_features sg_scan \
 	sg_senddiag sg_ses sg_start sg_stpg sg_sync \
 	sg_turs sg_verify sg_vpd sg_write_buffer sg_write_long \
 	sg_wr_mode
@@ -45,7 +45,7 @@
 	sg_modes sg_opcodes sg_persist \
 	sg_prevent sg_raw sg_rdac sg_readcap \
 	sg_read_buffer sg_read_long sg_reassign sg_requests sg_rmsn \
-	sg_rtpg sg_sat_identify sg_sat_set_features sg_scan \
+	sg_rtpg sg_safte sg_sat_identify sg_sat_set_features sg_scan \
 	sg_senddiag sg_ses sg_start sg_stpg sg_sync \
 	sg_turs sg_verify sg_vpd sg_write_buffer sg_write_long \
 	sg_wr_mode
@@ -61,7 +61,7 @@
 	sg_modes sg_opcodes sg_persist \
 	sg_prevent sg_raw sg_rdac sg_readcap \
 	sg_read_buffer sg_read_long sg_reassign sg_requests sg_rmsn \
-	sg_rtpg sg_sat_identify sg_sat_set_features \
+	sg_rtpg sg_safte sg_sat_identify sg_sat_set_features \
 	sg_senddiag sg_ses sg_start sg_stpg sg_sync \
 	sg_turs sg_verify sg_vpd sg_write_buffer sg_write_long \
 	sg_wr_mode
@@ -77,7 +77,7 @@
 	sg_modes sg_opcodes sg_persist \
 	sg_prevent sg_raw sg_rdac sg_readcap \
 	sg_read_buffer sg_read_long sg_reassign sg_requests sg_rmsn \
-	sg_rtpg sg_sat_identify sg_sat_set_features \
+	sg_rtpg sg_safte sg_sat_identify sg_sat_set_features \
 	sg_senddiag sg_ses sg_start sg_stpg sg_sync \
 	sg_turs sg_verify sg_vpd sg_write_buffer sg_write_long \
 	sg_wr_mode
@@ -93,7 +93,7 @@
 	sg_modes sg_opcodes sg_persist \
 	sg_prevent sg_raw sg_rdac sg_readcap \
 	sg_read_buffer sg_read_long sg_reassign sg_requests sg_rmsn \
-	sg_rtpg sg_sat_identify sg_sat_set_features \
+	sg_rtpg sg_safte sg_sat_identify sg_sat_set_features \
 	sg_senddiag sg_ses sg_start sg_stpg sg_sync \
 	sg_turs sg_verify sg_vpd sg_write_buffer sg_write_long \
 	sg_wr_mode
@@ -191,6 +191,9 @@
 sg_rtpg_SOURCES = sg_rtpg.c
 sg_rtpg_LDADD = ../lib/libsgutils.la @os_libs@
 
+sg_safte_SOURCES = sg_safte.c
+sg_safte_LDADD = ../lib/libsgutils.la @os_libs@
+
 sg_sat_identify_SOURCES = sg_sat_identify.c
 sg_sat_identify_LDADD = ../lib/libsgutils.la @os_libs@
 
diff --git a/src/Makefile.in b/src/Makefile.in
index 9a4d091..dfadda5 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -51,6 +51,7 @@
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@	sg_requests$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@	sg_rmsn$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@	sg_rtpg$(EXEEXT) \
+@OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@	sg_safte$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@	sg_sat_identify$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@	sg_sat_set_features$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@	sg_scan$(EXEEXT) \
@@ -84,6 +85,7 @@
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@	sg_requests$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@	sg_rmsn$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@	sg_rtpg$(EXEEXT) \
+@OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@	sg_safte$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@	sg_sat_identify$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@	sg_sat_set_features$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@	sg_scan$(EXEEXT) \
@@ -117,6 +119,7 @@
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@	sg_requests$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@	sg_rmsn$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@	sg_rtpg$(EXEEXT) \
+@OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@	sg_safte$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@	sg_sat_identify$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@	sg_sat_set_features$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@	sg_senddiag$(EXEEXT) \
@@ -149,6 +152,7 @@
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@	sg_requests$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@	sg_rmsn$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@	sg_rtpg$(EXEEXT) \
+@OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@	sg_safte$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@	sg_sat_identify$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@	sg_sat_set_features$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@	sg_senddiag$(EXEEXT) \
@@ -191,6 +195,7 @@
 @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@	sg_reset$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@	sg_rmsn$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@	sg_rtpg$(EXEEXT) \
+@OS_FREEBSD_FALSE@@OS_LINUX_TRUE@	sg_safte$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@	sg_sat_identify$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@	sg_sat_set_features$(EXEEXT) \
 @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@	sg_scan$(EXEEXT) \
@@ -216,7 +221,7 @@
 @OS_FREEBSD_TRUE@	sg_read_buffer$(EXEEXT) sg_read_long$(EXEEXT) \
 @OS_FREEBSD_TRUE@	sg_reassign$(EXEEXT) sg_requests$(EXEEXT) \
 @OS_FREEBSD_TRUE@	sg_rmsn$(EXEEXT) sg_rtpg$(EXEEXT) \
-@OS_FREEBSD_TRUE@	sg_sat_identify$(EXEEXT) \
+@OS_FREEBSD_TRUE@	sg_safte$(EXEEXT) sg_sat_identify$(EXEEXT) \
 @OS_FREEBSD_TRUE@	sg_sat_set_features$(EXEEXT) \
 @OS_FREEBSD_TRUE@	sg_senddiag$(EXEEXT) sg_ses$(EXEEXT) \
 @OS_FREEBSD_TRUE@	sg_start$(EXEEXT) sg_stpg$(EXEEXT) \
@@ -314,6 +319,9 @@
 am_sg_rtpg_OBJECTS = sg_rtpg.$(OBJEXT)
 sg_rtpg_OBJECTS = $(am_sg_rtpg_OBJECTS)
 sg_rtpg_DEPENDENCIES = ../lib/libsgutils.la
+am_sg_safte_OBJECTS = sg_safte.$(OBJEXT)
+sg_safte_OBJECTS = $(am_sg_safte_OBJECTS)
+sg_safte_DEPENDENCIES = ../lib/libsgutils.la
 am_sg_sat_identify_OBJECTS = sg_sat_identify.$(OBJEXT)
 sg_sat_identify_OBJECTS = $(am_sg_sat_identify_OBJECTS)
 sg_sat_identify_DEPENDENCIES = ../lib/libsgutils.la
@@ -391,14 +399,14 @@
 	$(sg_read_buffer_SOURCES) $(sg_read_long_SOURCES) \
 	$(sg_readcap_SOURCES) $(sg_reassign_SOURCES) \
 	$(sg_requests_SOURCES) $(sg_reset_SOURCES) $(sg_rmsn_SOURCES) \
-	$(sg_rtpg_SOURCES) $(sg_sat_identify_SOURCES) \
-	$(sg_sat_set_features_SOURCES) $(sg_scan_SOURCES) \
-	$(sg_senddiag_SOURCES) $(sg_ses_SOURCES) $(sg_start_SOURCES) \
-	$(sg_stpg_SOURCES) $(sg_sync_SOURCES) $(sg_test_rwbuf_SOURCES) \
-	$(sg_turs_SOURCES) $(sg_verify_SOURCES) $(sg_vpd_SOURCES) \
-	$(sg_wr_mode_SOURCES) $(sg_write_buffer_SOURCES) \
-	$(sg_write_long_SOURCES) $(sginfo_SOURCES) $(sgm_dd_SOURCES) \
-	$(sgp_dd_SOURCES)
+	$(sg_rtpg_SOURCES) $(sg_safte_SOURCES) \
+	$(sg_sat_identify_SOURCES) $(sg_sat_set_features_SOURCES) \
+	$(sg_scan_SOURCES) $(sg_senddiag_SOURCES) $(sg_ses_SOURCES) \
+	$(sg_start_SOURCES) $(sg_stpg_SOURCES) $(sg_sync_SOURCES) \
+	$(sg_test_rwbuf_SOURCES) $(sg_turs_SOURCES) \
+	$(sg_verify_SOURCES) $(sg_vpd_SOURCES) $(sg_wr_mode_SOURCES) \
+	$(sg_write_buffer_SOURCES) $(sg_write_long_SOURCES) \
+	$(sginfo_SOURCES) $(sgm_dd_SOURCES) $(sgp_dd_SOURCES)
 DIST_SOURCES = $(sg_dd_SOURCES) $(sg_emc_trespass_SOURCES) \
 	$(sg_format_SOURCES) $(sg_get_config_SOURCES) \
 	$(sg_ident_SOURCES) $(sg_inq_SOURCES) $(sg_logs_SOURCES) \
@@ -409,14 +417,14 @@
 	$(sg_read_buffer_SOURCES) $(sg_read_long_SOURCES) \
 	$(sg_readcap_SOURCES) $(sg_reassign_SOURCES) \
 	$(sg_requests_SOURCES) $(sg_reset_SOURCES) $(sg_rmsn_SOURCES) \
-	$(sg_rtpg_SOURCES) $(sg_sat_identify_SOURCES) \
-	$(sg_sat_set_features_SOURCES) $(sg_scan_SOURCES) \
-	$(sg_senddiag_SOURCES) $(sg_ses_SOURCES) $(sg_start_SOURCES) \
-	$(sg_stpg_SOURCES) $(sg_sync_SOURCES) $(sg_test_rwbuf_SOURCES) \
-	$(sg_turs_SOURCES) $(sg_verify_SOURCES) $(sg_vpd_SOURCES) \
-	$(sg_wr_mode_SOURCES) $(sg_write_buffer_SOURCES) \
-	$(sg_write_long_SOURCES) $(sginfo_SOURCES) $(sgm_dd_SOURCES) \
-	$(sgp_dd_SOURCES)
+	$(sg_rtpg_SOURCES) $(sg_safte_SOURCES) \
+	$(sg_sat_identify_SOURCES) $(sg_sat_set_features_SOURCES) \
+	$(sg_scan_SOURCES) $(sg_senddiag_SOURCES) $(sg_ses_SOURCES) \
+	$(sg_start_SOURCES) $(sg_stpg_SOURCES) $(sg_sync_SOURCES) \
+	$(sg_test_rwbuf_SOURCES) $(sg_turs_SOURCES) \
+	$(sg_verify_SOURCES) $(sg_vpd_SOURCES) $(sg_wr_mode_SOURCES) \
+	$(sg_write_buffer_SOURCES) $(sg_write_long_SOURCES) \
+	$(sginfo_SOURCES) $(sgm_dd_SOURCES) $(sgp_dd_SOURCES)
 ETAGS = etags
 CTAGS = ctags
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
@@ -588,6 +596,8 @@
 sg_rmsn_LDADD = ../lib/libsgutils.la @os_libs@
 sg_rtpg_SOURCES = sg_rtpg.c
 sg_rtpg_LDADD = ../lib/libsgutils.la @os_libs@
+sg_safte_SOURCES = sg_safte.c
+sg_safte_LDADD = ../lib/libsgutils.la @os_libs@
 sg_sat_identify_SOURCES = sg_sat_identify.c
 sg_sat_identify_LDADD = ../lib/libsgutils.la @os_libs@
 sg_sat_set_features_SOURCES = sg_sat_set_features.c
@@ -757,6 +767,9 @@
 sg_rtpg$(EXEEXT): $(sg_rtpg_OBJECTS) $(sg_rtpg_DEPENDENCIES) 
 	@rm -f sg_rtpg$(EXEEXT)
 	$(LINK) $(sg_rtpg_OBJECTS) $(sg_rtpg_LDADD) $(LIBS)
+sg_safte$(EXEEXT): $(sg_safte_OBJECTS) $(sg_safte_DEPENDENCIES) 
+	@rm -f sg_safte$(EXEEXT)
+	$(LINK) $(sg_safte_OBJECTS) $(sg_safte_LDADD) $(LIBS)
 sg_sat_identify$(EXEEXT): $(sg_sat_identify_OBJECTS) $(sg_sat_identify_DEPENDENCIES) 
 	@rm -f sg_sat_identify$(EXEEXT)
 	$(LINK) $(sg_sat_identify_OBJECTS) $(sg_sat_identify_LDADD) $(LIBS)
@@ -845,6 +858,7 @@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_reset.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_rmsn.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_rtpg.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_safte.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_sat_identify.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_sat_set_features.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_scan.Po@am__quote@
diff --git a/src/sg_safte.c b/src/sg_safte.c
new file mode 100644
index 0000000..941ed4f
--- /dev/null
+++ b/src/sg_safte.c
@@ -0,0 +1,654 @@
+/*
+ * Copyright (c) 2004-2007 Hannes Reinecke and 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 <ctype.h>
+#include <getopt.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+
+/* A utility program for the Linux OS SCSI subsystem.
+ *
+ *  This program accesses a processor device which operates according
+ *  to the 'SCSI Accessed Fault-Tolerant Enclosures' (SAF-TE) spec.
+ */
+
+static char * version_str = "0.21 20070929";
+
+
+#define SENSE_BUFF_LEN 32       /* Arbitrary, could be larger */
+#define DEF_TIMEOUT 60000       /* 60,000 millisecs == 60 seconds */
+#define EBUFF_SZ 256
+
+#define RB_MODE_DESC 3
+#define RWB_MODE_DATA 2 
+#define RWB_MODE_VENDOR 1 
+#define RB_DESC_LEN 4
+
+#define SAFTE_CFG_FLAG_DOORLOCK 1
+#define SAFTE_CFG_FLAG_ALARM 2
+#define SAFTE_CFG_FLAG_CELSIUS 3
+
+struct safte_cfg_t {
+    int fans;
+    int psupplies;
+    int slots;
+    int temps;
+    int thermostats;
+    int vendor_specific;
+    int flags;
+};
+
+struct safte_cfg_t safte_cfg;
+
+static int peri_type = 0; /* ugly but not easy to pass to alpha compare */
+static unsigned int buf_capacity = 64;
+
+/* Buffer ID 0x0: Read Enclosure Configuration (mandatory) */
+static int read_safte_configuration (int sg_fd, unsigned char *rb_buff,
+                                   unsigned int rb_len, int verbose)
+{
+    int res;
+
+    if (rb_len < buf_capacity) {
+        fprintf(stderr,"SCSI BUFFER size too small (%d/%d bytes)\n",
+                rb_len, buf_capacity);
+        return SG_LIB_CAT_ILLEGAL_REQ;
+    }
+
+    res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 0, 0,
+                            rb_buff, rb_len, 1, verbose);
+    if (res && res != SG_LIB_CAT_RECOVERED)
+        return res;
+
+    safte_cfg.fans = rb_buff[0];
+    safte_cfg.psupplies = rb_buff[1];
+    safte_cfg.slots = rb_buff[2];
+    safte_cfg.temps = rb_buff[4];
+    if (rb_buff[3]) 
+        safte_cfg.flags |= SAFTE_CFG_FLAG_DOORLOCK;
+    if (rb_buff[5])
+        safte_cfg.flags |= SAFTE_CFG_FLAG_ALARM;
+    if (rb_buff[6] & 0x80)
+        safte_cfg.flags |= SAFTE_CFG_FLAG_CELSIUS;
+
+    safte_cfg.thermostats = rb_buff[6] & 0x0f;
+    safte_cfg.vendor_specific = rb_buff[63];
+
+    return 0;
+}
+
+static int print_safte_configuration (void)
+{
+    printf("Enclosure Configuration:\n");
+    printf("\tNumber of Fans: %d\n", safte_cfg.fans);
+    printf("\tNumber of Power Supplies: %d\n", safte_cfg.psupplies);
+    printf("\tNumber of Device Slots: %d\n", safte_cfg.slots);
+    printf("\tNumber of Temperature Sensors: %d\n", safte_cfg.temps);
+    printf("\tNumber of Thermostats: %d\n", safte_cfg.thermostats);
+    printf("\tVendor unique bytes: %d\n", safte_cfg.vendor_specific);
+
+    return 0;
+}
+
+/* Buffer ID 0x01: Read Enclosure Status (mandatory) */
+static int do_safte_encl_status (int sg_fd, int verbose)
+{
+    int res, i, offset;
+    unsigned int rb_len;
+    unsigned char *rb_buff;
+
+    rb_len = safte_cfg.fans + safte_cfg.psupplies + safte_cfg.slots +
+        safte_cfg.temps + 5 + safte_cfg.vendor_specific;
+    rb_buff = (unsigned char *)malloc(rb_len);
+
+
+    res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 1, 0,
+                            rb_buff, rb_len, 0, verbose);
+    if (res && res != SG_LIB_CAT_RECOVERED)
+        return res;
+
+    printf("Enclosure Status:\n");
+    offset = 0;
+    for (i = 0; i < safte_cfg.fans; i++) {
+        printf("\tFan %d status: ", i);
+        switch(rb_buff[i]) {
+            case 0:
+                printf("operational\n");
+                break;
+            case 1:
+                printf("malfunctioning\n");
+                break;
+            case 2:
+                printf("not installed\n");
+                break;
+            case 80:
+                printf("not reportable\n");
+                break;
+            default:
+                printf("unknown\n");
+                break;
+        }
+    }
+
+    offset += safte_cfg.fans;
+    for (i = 0; i < safte_cfg.psupplies; i++) {
+        printf("\tPower supply %d status: ", i);
+        switch(rb_buff[i + offset]) {
+            case 0:
+                printf("operational / on\n");
+                break;
+            case 1:
+                printf("operational / off\n");
+                break;
+            case 0x10:
+                printf("malfunctioning / on\n");
+                break;
+            case 0x11:
+                printf("malfunctioning / off\n");
+                break;
+            case 0x20:
+                printf("not present\n");
+                break;
+            case 0x21:
+                printf("present\n");
+                break;
+            case 0x80:
+                printf("not reportable\n");
+                break;
+            default:
+                printf("unknown\n");
+                break;
+        }
+    }
+
+    offset += safte_cfg.psupplies;
+    for (i = 0; i < safte_cfg.slots; i++) {
+        printf("\tDevice Slot %d: SCSI ID %d\n", i, rb_buff[i + offset]);
+    }
+
+    offset += safte_cfg.slots;
+    if (safte_cfg.flags & SAFTE_CFG_FLAG_DOORLOCK) {
+        switch(rb_buff[offset]) {
+            case 0x0:
+                printf("\tDoor lock status: locked\n");
+                break;
+            case 0x01:
+                printf("\tDoor lock status: unlocked\n");
+                break;
+            case 0x80:
+                printf("\tDoor lock status: not reportable\n");
+                break;
+        }
+    } else {
+        printf("\tDoor lock status: not installed\n");
+    }
+
+    offset++;
+    if (!(safte_cfg.flags & SAFTE_CFG_FLAG_ALARM)) {
+        printf("\tSpeaker status: not installed\n");
+    } else {
+        switch(rb_buff[offset]) {
+            case 0x0:
+                printf("\tSpeaker status: off\n");
+                break;
+            case 0x01:
+                printf("\tSpeaker status: on\n");
+                break;
+        }
+    }
+
+    offset++;
+    for (i = 0; i < safte_cfg.temps; i++) {
+        int temp = 0;
+
+        if (!(safte_cfg.flags & SAFTE_CFG_FLAG_CELSIUS))
+            temp -= 10;
+
+        printf("\tTemperature sensor %d: %d deg %c\n", i, rb_buff[i + offset],
+               safte_cfg.flags & SAFTE_CFG_FLAG_CELSIUS?'C':'F');
+    }
+
+    offset += safte_cfg.temps;
+    if (safte_cfg.thermostats) {
+        if (rb_buff[offset] & 0x80) {
+            printf("\tEnclosure Temperature alert status: abnormal\n");
+        } else {
+            printf("\tEnclosure Temperature alert status: normal\n");
+        }
+    }
+    return 0;
+}
+
+/* Buffer ID 0x02: Read Usage Statistics (optional) */
+static int do_safte_usage_statistics (int sg_fd, int verbose)
+{
+    int res;
+    unsigned int rb_len;
+    unsigned char *rb_buff;
+    unsigned long minutes;
+
+    rb_len = 16 + safte_cfg.vendor_specific;
+    rb_buff = (unsigned char *)malloc(rb_len);
+
+    res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 2, 0,
+                            rb_buff, rb_len, 0, verbose);
+    if (res) {
+        if (res == SG_LIB_CAT_ILLEGAL_REQ) {
+            printf("Usage Statistics:\n\tNot implemented\n");
+            return 0;
+        }
+        if (res != SG_LIB_CAT_RECOVERED) {
+            free(rb_buff);
+            return res;
+        }
+    }
+
+    printf("Usage Statistics:\n");
+    minutes = (rb_buff[0] << 24) + (rb_buff[1] << 16) +
+        (rb_buff[2] <<  8) + rb_buff[3];
+    printf("\tPower on Minutes: %ld\n", minutes);
+    minutes = (rb_buff[4] << 24) + (rb_buff[5] << 16) +
+        (rb_buff[6] <<  8) + rb_buff[7];
+    printf("\tPower on Cycles: %ld\n", minutes);
+
+    free(rb_buff);
+    return 0;
+}
+
+/* Buffer ID 0x03: Read Device Insertions (optional) */
+static int do_safte_slot_insertions (int sg_fd, int verbose)
+{
+    int res, i;
+    unsigned int rb_len;
+    unsigned char *rb_buff, slot_status;
+
+    rb_len = safte_cfg.slots * 2;
+    rb_buff = (unsigned char *)malloc(rb_len);
+
+    res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 3, 0,
+                            rb_buff, rb_len, 0, verbose);
+    if (res ) {
+        if (res == SG_LIB_CAT_ILLEGAL_REQ) {
+                printf("Slot insertions:\n\tNot implemented\n");
+                return 0;
+        }
+        if (res != SG_LIB_CAT_RECOVERED) {
+                free(rb_buff);
+                return res;
+        }
+    }
+
+    printf("Slot insertions:\n");
+    for (i = 0; i < safte_cfg.slots; i++) {
+        slot_status = (rb_buff[i * 2] << 8) + rb_buff[i * 2];
+        printf("\tSlot %d: %d insertions", i, slot_status);
+    }
+    free(rb_buff);
+    return 0;
+}
+
+/* Buffer ID 0x04: Read Device Slot Status (mandatory) */
+static int do_safte_slot_status (int sg_fd, int verbose)
+{
+    int res, i;
+    unsigned int rb_len;
+    unsigned char *rb_buff, slot_status;
+
+    rb_len = safte_cfg.slots * 4;
+    rb_buff = (unsigned char *)malloc(rb_len);
+
+    res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 4, 0,
+                            rb_buff, rb_len, 0, verbose);
+    if (res && res != SG_LIB_CAT_RECOVERED) {
+        free(rb_buff);
+        return res;
+    }
+
+    printf("Slot status:\n");
+    for (i = 0; i < safte_cfg.slots; i++) {
+        slot_status = rb_buff[i * 4 + 3];
+        printf("\tSlot %d: ", i);
+        if (slot_status & 0x7) {
+            if (slot_status & 0x1)
+                printf("inserted ");
+            if (slot_status & 0x2)
+                printf("ready ");
+            if (slot_status & 0x4)
+                printf("activated ");
+            printf("\n");
+        } else {
+            printf("empty\n");
+        }
+    }
+    free(rb_buff);
+    return 0;
+}
+
+/* Buffer ID 0x05: Read Global Flags (optional) */
+static int do_safte_global_flags (int sg_fd, int verbose)
+{
+    int res;
+    unsigned int rb_len;
+    unsigned char *rb_buff;
+
+    rb_len = 16;
+    rb_buff = (unsigned char *)malloc(rb_len);
+
+    res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 5, 0,
+                            rb_buff, rb_len, 0, verbose);
+    if (res ) {
+        if (res == SG_LIB_CAT_ILLEGAL_REQ) {
+                printf("Global Flags:\n\tNot implemented\n");
+                return 0;
+        }
+        if (res != SG_LIB_CAT_RECOVERED) {
+                free(rb_buff);
+                return res;
+        }
+    }
+
+    printf("Global Flags:\n");
+    printf("\tAudible Alarm Control: %s\n",
+           rb_buff[0] & 0x1?"on":"off");
+    printf("\tGlobal Failure Indicator: %s\n",
+           rb_buff[0] & 0x2?"on":"off");
+    printf("\tGlobal Warning Indicator: %s\n",
+           rb_buff[0] & 0x4?"on":"off");
+    printf("\tEnclosure Power: %s\n",
+           rb_buff[0] & 0x8?"on":"off");
+    printf("\tCooling Failure: %s\n",
+           rb_buff[0] & 0x10?"yes":"no");
+    printf("\tPower Failure: %s\n",
+           rb_buff[0] & 0x20?"yes":"no");
+    printf("\tDrive Failure: %s\n",
+           rb_buff[0] & 0x40?"yes":"no");
+    printf("\tDrive Warning: %s\n",
+           rb_buff[0] & 0x80?"yes":"no");
+    printf("\tArray Failure: %s\n",
+           rb_buff[1] & 0x1?"yes":"no");
+    printf("\tArray Warning: %s\n",
+           rb_buff[0] & 0x2?"yes":"no");
+    printf("\tEnclosre Lock: %s\n",
+           rb_buff[0] & 0x4?"on":"off");
+    printf("\tEnclosre Identify: %s\n",
+           rb_buff[0] & 0x8?"on":"off");
+
+    free(rb_buff);
+    return 0;
+}
+
+static void usage()
+{
+    fprintf(stderr,
+            "Usage:  sg_safte [--config] [--devstatus] [--encstatus] "
+            "[--flags] [--help]\n"
+            "                 [--insertions] [--usage] [--verbose] "
+            "[--version] DEVICE\n"
+            "  where:\n"
+            "    --config|-c         output enclosure configuration\n"
+            "    --devstatus|-d      output device slot status\n"
+            "    --encstatus|-s      output enclosure status\n"
+            "    --flags|-f          output global flags\n"
+            "    --help|-h           output command usage message then "
+            "exit\n"
+            "    --insertions|-i     output insertion statistics\n"
+            "    --usage|-u          output usage statistics\n"
+            "    --verbose|-v        increase verbosity\n"
+            "    --version|-v        output version then exit\n\n"
+            "Queries a SAF-TE processor device\n");
+}
+
+static struct option long_options[] = {
+    {"config", 0, 0, 'c'},
+    {"devstatus", 0, 0, 'd'},
+    {"encstatus", 0, 0, 's'},
+    {"flags", 0, 0, 'f'},
+    {"help", 0, 0, 'h'},
+    {"insertions", 0, 0, 'i'},
+    {"usage", 0, 0, 'u'},
+    {"verbose", 0, 0, 'v'},
+    {"version", 0, 0, 'V'},
+    {0, 0, 0, 0},
+};
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, c, res = SG_LIB_CAT_OTHER;
+    const char * device_name = NULL;
+    char ebuff[EBUFF_SZ];
+    unsigned char *rb_buff;
+    int do_config = 0;
+    int do_status = 0;
+    int do_slots = 0;
+    int do_flags = 0;
+    int do_usage = 0;
+    int verbose = 0;
+    int do_insertions = 0;
+    const char * cp;
+    char buff[48];
+    struct sg_simple_inquiry_resp inq_resp;
+    const char op_name[] = "READ BUFFER";
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "cdfhisuvV?", long_options,
+                        &option_index);
+
+        if (c == -1)
+            break;
+
+        switch (c) {
+            case 'c':
+                do_config = 1;
+                break;
+            case 'd':
+                do_slots = 1;
+                break;
+            case 'f':
+                do_flags = 1;
+                break;
+            case 'h':
+            case '?':
+                usage();
+                return 0;
+            case 'i':
+                do_insertions = 1;
+                break;
+            case 's':
+                do_status = 1;
+                break;
+            case 'u':
+                do_usage = 1;
+                break;
+            case 'v':
+                ++verbose;
+                break;
+            case 'V':
+                fprintf(stderr, "Version string: %s\n", version_str);
+                exit(0);
+            default:
+                fprintf(stderr, "unrecognised option code 0x%x ??\n", c);
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                fprintf(stderr, "Unexpected extra argument: %s\n",
+                        argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (NULL == device_name) {
+        fprintf(stderr, "missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if ((sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose)) < 0) {
+        snprintf(ebuff, EBUFF_SZ, "sg_safte: error opening file: %s (ro)",
+                 device_name);
+        perror(ebuff);
+        return SG_LIB_FILE_ERROR;
+    }
+
+    if (0 == sg_simple_inquiry(sg_fd, &inq_resp, 1, verbose)) {
+        printf("  %.8s  %.16s  %.4s\n", inq_resp.vendor, inq_resp.product,
+               inq_resp.revision);
+        peri_type = inq_resp.peripheral_type;
+        cp = sg_get_pdt_str(peri_type, sizeof(buff), buff);
+        if (strlen(cp) > 0)
+            printf("  Peripheral device type: %s\n", cp);
+        else
+            printf("  Peripheral device type: 0x%x\n", peri_type);
+    } else {
+        printf("sg_safte: %s doesn't respond to a SCSI INQUIRY\n", device_name);
+        return SG_LIB_CAT_OTHER;
+    }
+
+    rb_buff = (unsigned char *)malloc(buf_capacity);
+    if (!rb_buff)
+        goto err_out;
+
+    memset(rb_buff, 0, buf_capacity);
+
+    res = read_safte_configuration(sg_fd, rb_buff, buf_capacity, verbose);
+    switch (res) {
+    case 0:
+    case SG_LIB_CAT_RECOVERED:
+        break;
+    default:
+        goto err_out;
+    }
+
+    if (do_config)
+        print_safte_configuration();
+
+    if (do_status) {
+        res = do_safte_encl_status(sg_fd, verbose);
+        switch (res) {
+            case 0:
+            case SG_LIB_CAT_RECOVERED:
+                break;
+            default:
+                goto err_out;
+        }
+    }
+
+    if (do_usage) {
+        res = do_safte_usage_statistics(sg_fd, verbose);
+        switch (res) {
+            case 0:
+            case SG_LIB_CAT_RECOVERED:
+                break;
+            default:
+                goto err_out;
+        }
+    }
+
+    if (do_insertions) {
+        res = do_safte_slot_insertions(sg_fd, verbose);
+        switch (res) {
+            case 0:
+            case SG_LIB_CAT_RECOVERED:
+                break;
+            default:
+                goto err_out;
+        }
+    }
+
+    if (do_slots) {
+        res = do_safte_slot_status(sg_fd, verbose);
+        switch (res) {
+            case 0:
+            case SG_LIB_CAT_RECOVERED:
+                break;
+            default:
+                goto err_out;
+        }
+    }
+
+    if (do_flags) {
+        res = do_safte_global_flags(sg_fd, verbose);
+        switch (res) {
+            case 0:
+            case SG_LIB_CAT_RECOVERED:
+                break;
+            default:
+                goto err_out;
+        }
+    }
+    res = 0;
+
+err_out:
+    switch (res) {
+    case 0:
+    case SG_LIB_CAT_RECOVERED:
+        break;
+    case SG_LIB_CAT_ABORTED_COMMAND:
+        fprintf(stderr, "%s: aborted command\n", op_name);
+        break;
+    case SG_LIB_CAT_NOT_READY:
+        fprintf(stderr, "%s: device not ready\n", op_name);
+        break;
+    case SG_LIB_CAT_UNIT_ATTENTION:
+        fprintf(stderr, "%s: unit attention\n", op_name);
+        break;
+    case SG_LIB_CAT_INVALID_OP:
+        fprintf(stderr, "%s: operation not supported\n", op_name);
+        break;
+    case SG_LIB_CAT_ILLEGAL_REQ:
+        fprintf(stderr, "%s: bad field in cdb\n", op_name);
+        break;
+    default:
+        fprintf(stderr, "%s failed\n", op_name);
+        break;
+    }
+
+    res = sg_cmds_close_device(sg_fd);
+    return res;
+}