aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2007-06-27 03:24:23 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2007-06-27 03:24:23 +0000
commit71e6e2d40657e63b9c20dc68f5e307639ef19c21 (patch)
tree18cab096bd20dcd67e51d1d741039fc85d3bad34
parent88d420308cbb9cd9e58873ef726913690d5c91a4 (diff)
downloadsg3_utils-71e6e2d40657e63b9c20dc68f5e307639ef19c21.tar.gz
Load sg3_utils-1.21 into trunk/.
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@69 6180dd3e-e324-4e3e-922d-17de1ae2f315
-rw-r--r--CHANGELOG49
-rw-r--r--COVERAGE25
-rw-r--r--INSTALL8
-rw-r--r--Makefile12
-rw-r--r--Makefile.asroot39
-rw-r--r--Makefile.freebsd11
-rw-r--r--Makefile.osf111
-rw-r--r--README46
-rw-r--r--README.freebsd8
-rw-r--r--README.tru648
-rw-r--r--archive/sg_dd2048.c6
-rw-r--r--archive/sg_dd512.c6
-rw-r--r--archive/sg_dd_old.c6
-rw-r--r--archive/sgq_dd.c4
-rw-r--r--archive/sgq_old_dd.c4
-rw-r--r--archive/sgs_dd.c4
-rwxr-xr-xbuild_debian.sh12
-rw-r--r--debian/changelog6
-rw-r--r--debian/control2
-rw-r--r--debian/docs2
-rwxr-xr-x[-rw-r--r--]debian/rules0
-rw-r--r--debian/sg3-utils.examples1
-rw-r--r--doc/sg3_utils.html (renamed from doc/u_index.html)385
-rw-r--r--doc/sg_dd.html6
-rw-r--r--doc/tools.html312
-rw-r--r--examples/Makefile12
-rw-r--r--examples/sg_iovec_tst.c6
-rw-r--r--examples/sg_sat_chk_power.c211
-rw-r--r--examples/sg_sat_identify.c215
-rw-r--r--examples/sg_sat_smart_rd_data.c184
-rw-r--r--examples/sg_sense_test.c8
-rw-r--r--no_lib/Makefile.freebsd11
-rw-r--r--no_lib/Makefile.linux14
-rw-r--r--no_lib/Makefile.linux_static10
-rw-r--r--no_lib/Makefile.osf111
-rw-r--r--no_lib/sg3_utils.spec13
-rw-r--r--sg3_utils.8160
-rw-r--r--sg3_utils.spec74
-rw-r--r--sg_cmds.c404
-rw-r--r--sg_cmds.h130
-rw-r--r--sg_dd.829
-rw-r--r--sg_dd.c483
-rw-r--r--sg_emc_trespass.85
-rw-r--r--sg_emc_trespass.c20
-rw-r--r--sg_format.823
-rw-r--r--sg_format.c116
-rw-r--r--sg_get_config.85
-rw-r--r--sg_get_config.c32
-rw-r--r--sg_ident.810
-rw-r--r--sg_ident.c72
-rw-r--r--sg_inq.811
-rw-r--r--sg_inq.c571
-rw-r--r--sg_io_linux.c8
-rw-r--r--sg_io_linux.h5
-rw-r--r--sg_lib.c116
-rw-r--r--sg_lib.h63
-rw-r--r--sg_logs.879
-rw-r--r--sg_logs.c411
-rw-r--r--sg_luns.87
-rw-r--r--sg_luns.c23
-rw-r--r--sg_map.85
-rw-r--r--sg_map.c12
-rw-r--r--sg_map26.88
-rw-r--r--sg_map26.c38
-rw-r--r--sg_modes.88
-rw-r--r--sg_modes.c55
-rw-r--r--sg_opcodes.87
-rw-r--r--sg_opcodes.c51
-rw-r--r--sg_persist.832
-rw-r--r--sg_persist.c74
-rw-r--r--sg_prevent.87
-rw-r--r--sg_prevent.c30
-rw-r--r--sg_rbuf.85
-rw-r--r--sg_rbuf.c50
-rw-r--r--sg_rdac.838
-rw-r--r--sg_rdac.c392
-rw-r--r--sg_read.818
-rw-r--r--sg_read.c108
-rw-r--r--sg_read_long.820
-rw-r--r--sg_read_long.c105
-rw-r--r--sg_readcap.810
-rw-r--r--sg_readcap.c36
-rw-r--r--sg_reassign.87
-rw-r--r--sg_reassign.c53
-rw-r--r--sg_requests.85
-rw-r--r--sg_requests.c29
-rw-r--r--sg_rmsn.87
-rw-r--r--sg_rmsn.c27
-rw-r--r--sg_rtpg.87
-rw-r--r--sg_rtpg.c24
-rw-r--r--sg_scan.85
-rw-r--r--sg_scan.c12
-rw-r--r--sg_senddiag.89
-rw-r--r--sg_senddiag.c97
-rw-r--r--sg_ses.87
-rw-r--r--sg_ses.c94
-rw-r--r--sg_start.88
-rw-r--r--sg_start.c36
-rw-r--r--sg_sync.87
-rw-r--r--sg_sync.c32
-rw-r--r--sg_test_rwbuf.85
-rw-r--r--sg_test_rwbuf.c85
-rw-r--r--sg_turs.85
-rw-r--r--sg_turs.c45
-rw-r--r--sg_verify.810
-rw-r--r--sg_verify.c38
-rw-r--r--sg_vpd.897
-rw-r--r--sg_vpd.c1720
-rw-r--r--sg_vpd_vendor.c488
-rw-r--r--sg_wr_mode.87
-rw-r--r--sg_wr_mode.c54
-rw-r--r--sg_write_long.85
-rw-r--r--sg_write_long.c37
-rw-r--r--sgm_dd.88
-rw-r--r--sgm_dd.c178
-rw-r--r--sgp_dd.88
-rw-r--r--sgp_dd.c181
-rw-r--r--utils/Makefile5
-rw-r--r--utils/sg_chk_asc.c217
l---------utils/sg_lib.h1
120 files changed, 7362 insertions, 1932 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 83220f73..6d12a8f6 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,53 @@
Each utility has its own version number, date of last change and
some description at the top of its ".c" file. All utilities in the main
-directory have their own "man" pages.
+directory have their own "man" pages. There is also a sg3_utils man page.
+
+Changelog for sg3_utils-1.21 [20060706]
+ - sg_vpd: new utility for decoding VPD pages. sg_inq's cli is
+ cluttered; also borrows from sdparm's VPD handling
+ - sg_rdac: new utility for vendor specific work
+ - sg_lib: add sg_vpd_dev_id_iter() to iterate over di VPD page
+ - add sg_ata_get_chars() to fetch chars from ATA words
+ - sync additional sense code strings with spc4r05a
+ - add SG_LIB_CAT_NOT_READY category when sense_key is NOT READY
+ - add SG_LIB_FILE_ERROR category for open problems
+ - add SG_LIB_SYNTAX_ERROR category for command line problems
+ - broaden SG_LIB_CAT_MEDIA_CHANGED to SG_LIB_CAT_UNIT_ATTENTION
+ - add SG_LIB_CAT_MALFORMED for bad responses
+ - BEWARE: these changes cause confusion if an executable from this
+ version is run with a libsgutils library from 1.20 or earlier
+ - sg_cmds: add SG_LIB_CAT_NOT_READY return to most "ll" functions
+ - alter many utilities to report SG_LIB_CAT_NOT_READY
+ - sg_dd: add retries=<n> option for sg_io
+ - sg_logs: add '-T' option to output protocol specific port log page
+ - add support for log subpages (new in spc4r05)
+ - more sanity checks in Start Stop Cycle Counter page
+ - sg_cmds: add sg_ll_read_long16()
+ - add page_code and subpage_code to sg_ll_log_select()
+ - add subpage_code to sg_ll_log_sense()
+ - sg_read_long: do READ LONG(16) when '--16' given
+ - sg_read: accept and ignore 'of=' arguments
+ - sg_dd: expand medium/hardware error "coe' processing to include
+ the "blank check" sense key (for optical devices)
+ - sg_ses: expand display element (per 05-011r2)
+ - sg_format: clear 'cmplist' bit (for MO disks)
+ - add '--six' ('-6') option for mode sense/select(6)
+ - sg_format + sg_test_rwbuf: fix for when char is unsigned
+ - sg_inq: VPD page 0x89: output ATA IDENTIFY DEVICE strings
+ - for IDENTIFY (PACKET) DEVICE response use sg_ata_get_chars()
+ - sg3_utils.html : new name, was previously u_index.html. Copy
+ placed in doc subdirectory
+ - tools.html : SCSI and storage tools reference, copy placed in
+ doc subdirectory
+ - sg3_utils.8 : add a new man page containing general information
+ especially common exit status values
+ - sg_sat_identify: added to examples directory (SAT passthrough test)
+ - extend to pass through IDENTIFY PACKET DEVICE with '-p' option
+ - sg_sat_chk_power: added to examples directory
+ - sg_sat_smart_rd_data: added to examples directory
+ - sg_chk_asc: added to utils directory to check asc_ascq codes
+ - debian: stop placing archive directory under examples
+ - add build_debian.sh script
Changelog for sg3_utils-1.20 [20060418]
- sg_logs: decode phy event descriptors in SAS port specific
diff --git a/COVERAGE b/COVERAGE
index a60cd081..74deeaaa 100644
--- a/COVERAGE
+++ b/COVERAGE
@@ -6,21 +6,24 @@ on the right. The second table lists supported ATA commands.
SCSI command sg3_utils utilities that use this SCSI command
------------ -------------------------------------------------
+ATA COMMAND PASS THROUGH(16) examples/sg_sat_chk_power,
+ examples/sg_sat_identify,
+ examples/sg_sat_smart_rd_data
GET CONFIGURATION sg_get_config, ++
INQUIRY sg_dd, sg_format, sg_inq, sginfo,
sg_logs, sg_map('-i'), sg_modes, sg_opcodes,
- sg_persist, sg_scan, sg_ses, ++
+ sg_persist, sg_scan, sg_ses, sg_vpd ++
FORMAT UNIT sg_format, ++
LOG SELECT sg_logs('-r', '-scum' or '-sthr'), ++
LOG SENSE sg_logs, ++
MODE SELECT(6) sg_wr_mode, sginfo, sg_format,
- sg_emc_trespass, ++
+ sg_emc_trespass, sg_rdac, ++
MODE SELECT(10) sg_wr_mode, sginfo, sg_format,
- sg_emc_trespass, ++
+ sg_emc_trespass, sg_rdac, ++
MODE SENSE(6) sg_modes, sg_wr_mode, sginfo, sg_format,
- sg_senddiag('-e'), ++
+ sg_senddiag('-e'), sg_rdac, ++
MODE SENSE(10) sg_modes, sg_wr_mode, sginfo, sg_format,
- sg_senddiag('-e'), ++
+ sg_senddiag('-e'), sg_rdac, ++
PERSISTENT RESERVE IN sg_persist ++
PERSISTENT RESERVE OUT sg_persist ++
PREVENT ALLOW MEDIUM REMOVAL sg_prevent, ++
@@ -31,9 +34,10 @@ READ(16) sg_dd, sgm_dd, sgp_dd, sg_read
READ BUFFER sg_rbuf, sg_test_rwbuf
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, ++
+READ DEFECT(10) sginfo, sg_reassign('-g'), ++
READ DEFECT(12) sginfo
READ LONG (10) sg_read_long, sg_dd
+READ LONG (16) sg_read_long
READ MEDIA SERIAL NUMBER sg_rmsn, ++
REASSIGN BLOCKS sg_reassign, ++
RECEIVE DIAGNOSTIC sg_senddiag, sg_ses, ++
@@ -57,13 +61,16 @@ WRITE BUFFER sg_test_rwbuf
WRITE LONG (10) sg_write_long
+
ATA command sg3_utils utilities that use this SCSI command
----------- ----------------------------------------------
-IDENTIFY DEVICE sg_inq, sg_scan
-IDENTIFY PACKET DEVICE sg_inq
+CHECK POWER MODE examples/sg_sat_chk_power
+IDENTIFY DEVICE sg_inq, sg_scan, examples/sg_sat_identify
+IDENTIFY PACKET DEVICE sg_inq, examples/sg_sat_identify
+SMART READ DATA examples/sg_sat_smart_rd_data
++ command wrapper found in sg_cmds.c for this command
Doug Gilbert
-22nd March 2006
+2nd July 2006
diff --git a/INSTALL b/INSTALL
index ee6d1ac1..18fd80f4 100644
--- a/INSTALL
+++ b/INSTALL
@@ -5,7 +5,7 @@ By default, the Makefile will build for Linux with a shared library.
The invocation sequence is:
make
-make install [may need root permission]
+make install [may need root permission]
Unless overridden or edited in the Makefile, INSTDIR places the executables
in the /usr/local/bin directory, LIBDIR places libraries in the
@@ -64,7 +64,9 @@ change directory to the top level within the sg3_utils source. Then:
# chmod +x debian/rules
# dpkg-buildpackage -b -rfakeroot
The binary deb packages will be placed in the parent directory (of
-the sg3_utils source directory) if all goes well.
+the sg3_utils source directory) if all goes well. There is now
+a script called build_debian.sh in the main directory which invokes
+those two commands.
If the shared object (library) is troublesome or unwanted then
a "no_lib" version of the Makefile and the sg3_utils.spec file
@@ -91,4 +93,4 @@ Utilities that are linux specific (e.g. sg_map) are not built for
Tru64.
-24th January 2006
+29th June 2006
diff --git a/Makefile b/Makefile
index e210e595..6403759f 100644
--- a/Makefile
+++ b/Makefile
@@ -15,7 +15,7 @@ EXECS = sg_dd sgp_dd sgm_dd sg_read sg_map sg_scan sg_rbuf \
sg_persist sg_write_long sg_read_long sg_requests sg_ses \
sg_verify sg_emc_trespass sg_luns sg_sync sg_prevent \
sg_get_config sg_wr_mode sg_rtpg sg_reassign sg_format \
- sg_rmsn sg_ident sg_map26
+ sg_rmsn sg_ident sg_map26 sg_rdac sg_vpd
MAN_PGS = sg_dd.8 sgp_dd.8 sgm_dd.8 sg_read.8 sg_map.8 sg_scan.8 sg_rbuf.8 \
sginfo.8 sg_readcap.8 sg_turs.8 sg_inq.8 sg_test_rwbuf.8 \
@@ -24,14 +24,13 @@ MAN_PGS = sg_dd.8 sgp_dd.8 sgm_dd.8 sg_read.8 sg_map.8 sg_scan.8 sg_rbuf.8 \
sg_requests.8 sg_ses.8 sg_verify.8 sg_emc_trespass.8 \
sg_luns.8 sg_sync.8 sg_prevent.8 sg_get_config.8 sg_wr_mode.8 \
sg_rtpg.8 sg_reassign.8 sg_format.8 sg_rmsn.8 sg_ident.8 \
- sg_map26.8
+ sg_map26.8 sg_rdac.8 sg_vpd.8 sg3_utils.8
MAN_PREF = man8
HEADERS = sg_lib.h sg_cmds.h sg_pt.h sg_io_linux.h sg_linux_inc.h
OS_FLAGS = -DSG3_UTILS_LINUX
LARGE_FILE_FLAGS = -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
-# S_CFLAGS = $(OS_FLAGS) $(LARGE_FILE_FLAGS) -DSG_KERNEL_INCLUDES
S_CFLAGS = $(OS_FLAGS) $(LARGE_FILE_FLAGS)
# CFLAGS = -O2 -Wall -W
@@ -190,6 +189,13 @@ sg_ident: sg_ident.o libsgutils.la
sg_map26: sg_map26.o
libtool --mode=link $(LD) -o $@ $(LDFLAGS) $^
+sg_rdac: sg_rdac.o libsgutils.la
+ libtool --mode=link $(LD) -o $@ $(LDFLAGS) $^
+
+sg_vpd: sg_vpd.o sg_vpd_vendor.o libsgutils.la
+ libtool --mode=link $(LD) -o $@ $(LDFLAGS) $^
+
+
install: $(EXECS)
install -d $(INSTDIR)
install -d $(LIBDIR)
diff --git a/Makefile.asroot b/Makefile.asroot
index e9fc9ba0..dff817e7 100644
--- a/Makefile.asroot
+++ b/Makefile.asroot
@@ -15,7 +15,7 @@ EXECS = sg_dd sgp_dd sgm_dd sg_read sg_map sg_scan sg_rbuf \
sg_persist sg_write_long sg_read_long sg_requests sg_ses \
sg_verify sg_emc_trespass sg_luns sg_sync sg_prevent \
sg_get_config sg_wr_mode sg_rtpg sg_reassign sg_format \
- sg_rmsn sg_ident sg_map26
+ sg_rmsn sg_ident sg_map26 sg_rdac sg_vpd
MAN_PGS = sg_dd.8 sgp_dd.8 sgm_dd.8 sg_read.8 sg_map.8 sg_scan.8 sg_rbuf.8 \
sginfo.8 sg_readcap.8 sg_turs.8 sg_inq.8 sg_test_rwbuf.8 \
@@ -24,20 +24,18 @@ MAN_PGS = sg_dd.8 sgp_dd.8 sgm_dd.8 sg_read.8 sg_map.8 sg_scan.8 sg_rbuf.8 \
sg_requests.8 sg_ses.8 sg_verify.8 sg_emc_trespass.8 \
sg_luns.8 sg_sync.8 sg_prevent.8 sg_get_config.8 sg_wr_mode.8 \
sg_rtpg.8 sg_reassign.8 sg_format.8 sg_rmsn.8 sg_ident.8 \
- sg_map26.8
+ sg_map26.8 sg_rdac.8 sg_vpd.8 sg3_utils.8
MAN_PREF = man8
-HEADERS = sg_lib.h sg_cmds.h sg_pt.h
-
+HEADERS = sg_lib.h sg_cmds.h sg_pt.h sg_io_linux.h sg_linux_inc.h
OS_FLAGS = -DSG3_UTILS_LINUX
LARGE_FILE_FLAGS = -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
-EXTRA_FLAGS = $(OS_FLAGS) $(LARGE_FILE_FLAGS)
+S_CFLAGS = $(OS_FLAGS) $(LARGE_FILE_FLAGS)
-# CFLAGS = -O2 -Wall -W $(EXTRA_FLAGS)
-CFLAGS = -g -O2 -Wall -W $(EXTRA_FLAGS)
-# CFLAGS = -g -O2 -W -DSG_KERNEL_INCLUDES $(EXTRA_FLAGS)
-# CFLAGS = -g -O2 -Wall -W -pedantic -std=c99 $(EXTRA_FLAGS)
+# CFLAGS = -O2 -Wall -W
+CFLAGS = -g -O2 -Wall -W
+# CFLAGS = -g -O2 -Wall -W -pedantic -std=c99
CFLAGS_PTHREADS = -D_REENTRANT
@@ -51,7 +49,7 @@ LIB_VINFO = 1:0:0
all: $(EXECS)
depend dep:
- for i in *.c; do $(CC) $(INCLUDES) $(CFLAGS) -M $$i; \
+ for i in *.c; do $(CC) $(INCLUDES) $(CFLAGS) $(S_CFLAGS) -M $$i; \
done > .depend
clean:
@@ -59,6 +57,9 @@ clean:
/bin/rm -rf .libs
+.c.o:
+ $(CC) $(INCLUDES) $(CFLAGS) $(S_CFLAGS) -c -o $@ $<
+
sg_lib.lo: sg_lib.o
libtool --mode=compile $(CC) -c sg_lib.c
@@ -98,7 +99,8 @@ sg_readcap: sg_readcap.o libsgutils.la
libtool --mode=link $(LD) -o $@ $(LDFLAGS) $^
sgp_dd.o: sgp_dd.c
- $(CC) $(INCLUDES) $(CFLAGS) $(CFLAGS_PTHREADS) -c $<
+ $(CC) $(INCLUDES) $(CFLAGS) $(S_CFLAGS) $(CFLAGS_PTHREADS) \
+ -c -o sgp_dd.o $<
sgp_dd: sgp_dd.o llseek.o sg_io_linux.o libsgutils.la
libtool --mode=link $(LD) -o $@ $(LDFLAGS) $^ -lpthread
@@ -133,13 +135,13 @@ sg_senddiag: sg_senddiag.o libsgutils.la
sg_opcodes: sg_opcodes.o sg_io_linux.o libsgutils.la
libtool --mode=link $(LD) -o $@ $(LDFLAGS) $^
-sg_persist: sg_persist.o sg_io_linux.o libsgutils.la
+sg_persist: sg_persist.o libsgutils.la
libtool --mode=link $(LD) -o $@ $(LDFLAGS) $^
-sg_write_long: sg_write_long.o sg_io_linux.o libsgutils.la
+sg_write_long: sg_write_long.o libsgutils.la
libtool --mode=link $(LD) -o $@ $(LDFLAGS) $^
-sg_read_long: sg_read_long.o sg_io_linux.o libsgutils.la
+sg_read_long: sg_read_long.o libsgutils.la
libtool --mode=link $(LD) -o $@ $(LDFLAGS) $^
sg_requests: sg_requests.o libsgutils.la
@@ -148,7 +150,7 @@ sg_requests: sg_requests.o libsgutils.la
sg_ses: sg_ses.o libsgutils.la
libtool --mode=link $(LD) -o $@ $(LDFLAGS) $^
-sg_verify: sg_verify.o sg_io_linux.o libsgutils.la
+sg_verify: sg_verify.o libsgutils.la
libtool --mode=link $(LD) -o $@ $(LDFLAGS) $^
sg_emc_trespass: sg_emc_trespass.o libsgutils.la
@@ -187,6 +189,13 @@ sg_ident: sg_ident.o libsgutils.la
sg_map26: sg_map26.o
libtool --mode=link $(LD) -o $@ $(LDFLAGS) $^
+sg_rdac: sg_rdac.o libsgutils.la
+ libtool --mode=link $(LD) -o $@ $(LDFLAGS) $^
+
+sg_vpd: sg_vpd.o sg_vpd_vendor.o libsgutils.la
+ libtool --mode=link $(LD) -o $@ $(LDFLAGS) $^
+
+
install: $(EXECS)
install -d $(INSTDIR)
install -d $(LIBDIR)
diff --git a/Makefile.freebsd b/Makefile.freebsd
index 4ebad4f9..d40b7fa9 100644
--- a/Makefile.freebsd
+++ b/Makefile.freebsd
@@ -16,13 +16,14 @@ EXECS = sg_readcap sg_turs sg_inq sg_start sg_modes sg_logs \
sg_senddiag sg_persist sg_requests sg_ses sg_luns \
sg_sync sg_prevent sg_get_config sg_wr_mode \
sg_rtpg sg_reassign sg_format sg_rmsn sg_ident \
- sg_read_long sg_write_long sg_verify
+ sg_read_long sg_write_long sg_verify sg_rdac sg_vpd
MAN_PGS = sg_readcap.8 sg_turs.8 sg_inq.8 sg_start.8 sg_modes.8 sg_logs.8 \
sg_senddiag.8 sg_persist.8 sg_requests.8 sg_ses.8 sg_luns.8 \
sg_sync.8 sg_prevent.8 sg_get_config.8 sg_wr_mode.8 \
sg_rtpg.8 sg_reassign.8 sg_format.8 sg_rmsn.8 sg_ident.8 \
- sg_read_long.8 sg_write_long.8 sg_verify.8
+ sg_read_long.8 sg_write_long.8 sg_verify.8 sg_rdac.8 sg_vpd.8 \
+ sg3_utils.8
MAN_PREF = man8
@@ -145,6 +146,12 @@ sg_write_long: sg_write_long.o libsgutils.la
sg_verify: sg_verify.o libsgutils.la
$(LIBTOOL) $(LT_EXTRA) --mode=link $(LD) -o $@ $(LDFLAGS) $@.o libsgutils.la
+sg_rdac: sg_rdac.o libsgutils.la
+ $(LIBTOOL) $(LT_EXTRA) --mode=link $(LD) -o $@ $(LDFLAGS) $@.o libsgutils.la
+
+sg_vpd: sg_vpd.o sg_vpd_vendor.o libsgutils.la
+ $(LIBTOOL) $(LT_EXTRA) --mode=link $(LD) -o $@ $(LDFLAGS) $@.o libsgutils.la
+
install: $(EXECS)
install -d $(INSTDIR)
install -d $(LIBDIR)
diff --git a/Makefile.osf1 b/Makefile.osf1
index 129b5f8b..cdd34d44 100644
--- a/Makefile.osf1
+++ b/Makefile.osf1
@@ -15,13 +15,14 @@ EXECS = sg_readcap sg_turs sg_inq sg_start sg_modes sg_logs \
sg_senddiag sg_persist sg_requests sg_ses sg_luns \
sg_sync sg_prevent sg_get_config sg_wr_mode \
sg_rtpg sg_reassign sg_format sg_rmsn sg_ident \
- sg_read_long sg_write_long sg_verify
+ sg_read_long sg_write_long sg_verify sg_rdac sg_vpd
MAN_PGS = sg_readcap.8 sg_turs.8 sg_inq.8 sg_start.8 sg_modes.8 sg_logs.8 \
sg_senddiag.8 sg_persist.8 sg_requests.8 sg_ses.8 sg_luns.8 \
sg_sync.8 sg_prevent.8 sg_get_config.8 sg_wr_mode.8 \
sg_rtpg.8 sg_reassign.8 sg_format.8 sg_rmsn.8 sg_ident.8 \
- sg_read_long.8 sg_write_long.8 sg_verify.8
+ sg_read_long.8 sg_write_long.8 sg_verify.8 sg_rdac.8 sg_vpd.8 \
+ sg3_utils.8
MAN_PREF = man8
@@ -145,6 +146,12 @@ sg_write_long: sg_write_long.o libsgutils.la getopt_long/getopt_long.o
sg_verify: sg_verify.o libsgutils.la getopt_long/getopt_long.o
$(LIBTOOL) $(LT_EXTRA) --mode=link $(LD) -o $@ $(LDFLAGS) $@.o libsgutils.la getopt_long/getopt_long.o
+sg_rdac: sg_rdac.o libsgutils.la
+ $(LIBTOOL) $(LT_EXTRA) --mode=link $(LD) -o $@ $(LDFLAGS) $@.o libsgutils.la getopt_long/getopt_long.o
+
+sg_vpd: sg_vpd.o sg_vpd_vendor.o libsgutils.la getopt_long/getopt_long.o
+ $(LIBTOOL) $(LT_EXTRA) --mode=link $(LD) -o $@ $(LDFLAGS) $@.o libsgutils.la getopt_long/getopt_long.o
+
install: $(EXECS)
test -d $(INSTDIR) || mkdir -p $(INSTDIR)
test -d $(LIBDIR) || mkdir -p $(LIBDIR)
diff --git a/README b/README
index d184f97e..a18621f1 100644
--- a/README
+++ b/README
@@ -2,8 +2,8 @@
====================
Introduction
============
-This package contains low level utilities for devices that use
-a SCSI command set. Apart from SCSI parallel devices, the SCSI
+This package contains low level utilities for devices that use a SCSI
+command set. Apart from SCSI parallel interface (SPI) devices, the SCSI
command set is used by ATAPI devices (CD/DVDs and tapes), USB
mass storage devices, Fibre Channel disks and IEEE 1394 storage
devices (that use the "SBP" protocol).
@@ -31,7 +31,7 @@ and the Linux SCSI generic driver (and the scsi_debug driver) can be
found at http://www.torque.net/sg . The most recent release version
of sg3_utils and the most recent beta is on that page. There is also
a page describing the utilities in the sg3_utils and sg_utils packages:
-http://www.torque.net/sg/u_index.html . A copy of the "u_index.html"
+http://www.torque.net/sg/sg3_utils.html . A copy of the "sg3_utils.html"
file is in the "doc" subdirectory.
In the Linux 2.4 kernel series these utilities need to use the SCSI generic
@@ -90,9 +90,11 @@ utilities without depending on libsgutils.so . For example
does not depend on libsgutils.so . See the INSTALL file for more
information.
-All the utilities in the main directory have "man" pages. Additional
-information (including a version number) can be found towards the top
-of each ".c" file corresponding to each utility.
+All the utilities in the main directory have "man" pages. There is
+also a sg3_utils (8) man page that summarizes common facilities
+including exit statuses. Additional information (including a version
+number) can be found towards the top of each ".c" file corresponding
+to each utility.
The sg driver in Linux can be seen as having 3 distinct versions:
@@ -151,10 +153,10 @@ Here is list in alphabetical order of utilities found in the main directory
of the sg3_utils directory:
- sginfo, sgm_dd, sgp_dd, sg_dd, sg_emc_trespass, sg_get_config,
sg_format, sg_ident, sg_inq, sg_logs, sg_luns, sg_map, sg_map26,
- sg_modes, sg_opcodes, sg_persist, sg_prevent, sg_rbuf, sg_read,
+ sg_modes, sg_opcodes, sg_persist, sg_prevent, sg_rbuf, sg_rdac, sg_read,
sg_readcap, sg_read_long, sg_reassign, sg_request, sg_reset, sg_rmsn,
sg_rtpg, sg_scan, sg_senddiag, sg_ses, sg_start, sg_sync,
- sg_test_rwbuff, sg_turs, sg_verify, sg_write_long, sg_wr_mode
+ sg_test_rwbuff, sg_turs, sg_verify, sg_vpd, sg_write_long, sg_wr_mode
These utilities and the libsgutils.so library which they depend on are built
by the Makefile in the main directory. This Makefile does not invoke the
@@ -192,7 +194,10 @@ phys into a compliant jitter tolerance pattern (CJTPAT).
The "utils" subdirectory contains source and a Makefile to build
"hxascdmp" which accepts binary data from stdin (or a file on the
command line) and outputs an ASCII-HEX and ASCII representation of
-it. It is similar to the Unix od command.
+it. It is similar to the Unix od command. There is also code to
+sg_chk_asc.c which checks a given text file (typically a copy of
+http://www.t10.org/lists/asc-num.txt ) and checks it against the
+asc/ascq test strings held in sg_lib.c .
The "doc" subdirectory contains copies of web pages relevant to
the sg3_utils package. Currently it contains:
@@ -201,7 +206,8 @@ the sg3_utils package. Currently it contains:
- sg_io.html : describes the SG_IO ioctl used by sg3_utils. Looks
at differences between the lk 2.4 and 2.6 series;
including its use with block devices
- - u_index.html : a description of the sg3_utils package contents
+ - sg3_utils.html : a description of the sg3_utils package contents
+ - tools.html : a list of SCSI and storage tools with a summary and url
Notes for utilities without man pages
@@ -240,6 +246,22 @@ scsi device. This is only supported for lk >= 2.4.15 and for adapter
drivers that indicate that they have 16 byte CDB capability (otherwise
DID_ABORT will appear in the host_status).
+"sg_sat_identify" attempts to push an ATA IDENTIFY DEVICE or
+IDENTIFY pACKET DEVICE command through the SAT-defined ATA PASS
+THROUGH (16) SCSI command. If successful, the 256 word (512 byte)
+response is output.
+
+"sg_sat_chk_power" attempts to push an ATA CHECK POWER MODE command
+through the SAT-defined ATA PASS THROUGH (16) SCSI command. That
+ATA command needs to read the "FIS" registers after the command is
+completed which involves using the ATA Status Return (sense data)
+descriptor (as defined in SAT).
+
+"sg_sat_smart_rd_data" attempts to push an ATA SMART/READ DATA command
+through the SAT-defined ATA PASS THROUGH (16) SCSI command. If
+successful, the 256 word (512 byte) response is output.
+
+
Command line processing
=======================
These utilities can be divided into 3 groups when their handling of
@@ -255,7 +277,7 @@ The recent utilities that use "getopt_long" are, in alphabetical
order:
- sg_format sg_get_config sg_ident sg_luns sg_map26 sg_persist
sg_prevent sg_read_long sg_reassign sg_requests sg_rmsn sg_rtpg
- sg_ses sg_sync sg_test_rwbuf sg_verify sg_write_long sg_wr_mode
+ sg_ses sg_sync sg_test_rwbuf sg_verify sg_vpd sg_write_long sg_wr_mode
Linux header file problems
@@ -303,4 +325,4 @@ user). It is available for Linux and other operating systems.
See http://members.aol.com/plscsi .
Doug Gilbert
-18th April 2006
+6th July 2006
diff --git a/README.freebsd b/README.freebsd
index d539b5ce..51efd16a 100644
--- a/README.freebsd
+++ b/README.freebsd
@@ -17,6 +17,7 @@ Here is a list of utilities that have been ported:
sg_modes
sg_persist
sg_prevent
+ sg_rdac
sg_read_long
sg_readcap
sg_reassign
@@ -29,6 +30,7 @@ Here is a list of utilities that have been ported:
sg_sync
sg_turs
sg_verify
+ sg_vpd
sg_wr_mode
sg_write_long
@@ -37,8 +39,8 @@ that they execute. Some utilities are slightly higher level, for
example sg_ses fetches SCSI Enclosure Services (SES) status pages and
can send control pages. Each utility has a man page (placed in
section 8). An overview of sg3_utils can be found at:
-http://www.torque.net/sg/u_index.html .
-A copy of the "u_index.html" file is in the "doc" subdirectory.
+http://www.torque.net/sg/sg3_utils.html .
+A copy of the "sg3_utils.html" file is in the "doc" subdirectory.
See the INSTALL file (at the end) for instructions on how to build
@@ -70,4 +72,4 @@ drive and an ATAPI CD drive.
Doug Gilbert
-24th January 2006
+29th May 2006
diff --git a/README.tru64 b/README.tru64
index a8ad32e3..60f6d210 100644
--- a/README.tru64
+++ b/README.tru64
@@ -17,6 +17,7 @@ Here is a list of utilities that have been ported:
sg_modes
sg_persist
sg_prevent
+ sg_rdac
sg_read_long
sg_readcap
sg_reassign
@@ -29,6 +30,7 @@ Here is a list of utilities that have been ported:
sg_sync
sg_turs
sg_verify
+ sg_vpd
sg_wr_mode
sg_write_long
@@ -37,8 +39,8 @@ that they execute. Some utilities are slightly higher level, for
example sg_ses fetches SCSI Enclosure Services (SES) status pages and
can send control pages. Each utility has a man page (placed in
section 8). An overview of sg3_utils can be found at:
-http://www.torque.net/sg/u_index.html .
-A copy of the "u_index.html" file is in the "doc" subdirectory.
+http://www.torque.net/sg/sg3_utils.html .
+A copy of the "sg3_utils.html" file is in the "doc" subdirectory.
See the INSTALL file (at the end) for instructions on how to build
@@ -71,4 +73,4 @@ subdirectory. Currently only the Tru64 port uses it.
Doug Gilbert
-24th January 2006
+29th May 2006
diff --git a/archive/sg_dd2048.c b/archive/sg_dd2048.c
index 9e88683e..ea9bf415 100644
--- a/archive/sg_dd2048.c
+++ b/archive/sg_dd2048.c
@@ -90,7 +90,7 @@ int read_capacity(int sg_fd, int * num_sect, int * sect_sz)
return -1;
}
res = sg_err_category3(&io_hdr);
- if (SG_LIB_CAT_MEDIA_CHANGED == res)
+ if (SG_LIB_CAT_UNIT_ATTENTION == res)
return 2; /* probably have another go ... */
else if (SG_LIB_CAT_CLEAN != res) {
sg_chk_n_print3("read capacity", &io_hdr);
@@ -156,7 +156,7 @@ int sg_read(int sg_fd, unsigned char * buff, int blocks, int from_block)
printf("Recovered error while reading block=%d, num=%d\n",
from_block, blocks);
break;
- case SG_LIB_CAT_MEDIA_CHANGED:
+ case SG_LIB_CAT_UNIT_ATTENTION:
return 2;
default:
sg_chk_n_print3("reading", &io_hdr);
@@ -217,7 +217,7 @@ int sg_write(int sg_fd, unsigned char * buff, int blocks, int to_block)
printf("Recovered error while writing block=%d, num=%d\n",
to_block, blocks);
break;
- case SG_LIB_CAT_MEDIA_CHANGED:
+ case SG_LIB_CAT_UNIT_ATTENTION:
return 2;
default:
sg_chk_n_print3("writing", &io_hdr);
diff --git a/archive/sg_dd512.c b/archive/sg_dd512.c
index ebb92bc6..6291ef43 100644
--- a/archive/sg_dd512.c
+++ b/archive/sg_dd512.c
@@ -90,7 +90,7 @@ int read_capacity(int sg_fd, int * num_sect, int * sect_sz)
return -1;
}
res = sg_err_category3(&io_hdr);
- if (SG_LIB_CAT_MEDIA_CHANGED == res)
+ if (SG_LIB_CAT_UNIT_ATTENTION == res)
return 2; /* probably have another go ... */
else if (SG_LIB_CAT_CLEAN != res) {
sg_chk_n_print3("read capacity", &io_hdr);
@@ -156,7 +156,7 @@ int sg_read(int sg_fd, unsigned char * buff, int blocks, int from_block)
printf("Recovered error while reading block=%d, num=%d\n",
from_block, blocks);
break;
- case SG_LIB_CAT_MEDIA_CHANGED:
+ case SG_LIB_CAT_UNIT_ATTENTION:
return 2;
default:
sg_chk_n_print3("reading", &io_hdr);
@@ -217,7 +217,7 @@ int sg_write(int sg_fd, unsigned char * buff, int blocks, int to_block)
printf("Recovered error while writing block=%d, num=%d\n",
to_block, blocks);
break;
- case SG_LIB_CAT_MEDIA_CHANGED:
+ case SG_LIB_CAT_UNIT_ATTENTION:
return 2;
default:
sg_chk_n_print3("writing", &io_hdr);
diff --git a/archive/sg_dd_old.c b/archive/sg_dd_old.c
index fbdb19b7..83dc6451 100644
--- a/archive/sg_dd_old.c
+++ b/archive/sg_dd_old.c
@@ -86,7 +86,7 @@ int read_capacity(int sg_fd, int * num_sect, int * sect_sz)
return -1;
}
res = sg_err_category3(&io_hdr);
- if (SG_LIB_CAT_MEDIA_CHANGED == res)
+ if (SG_LIB_CAT_UNIT_ATTENTION == res)
return 2; /* probably have another go ... */
else if (SG_LIB_CAT_CLEAN != res) {
sg_chk_n_print3("read capacity", &io_hdr);
@@ -157,7 +157,7 @@ int sg_read(int sg_fd, unsigned char * buff, int blocks, int from_block,
printf("Recovered error while reading block=%d, num=%d\n",
from_block, blocks);
break;
- case SG_LIB_CAT_MEDIA_CHANGED:
+ case SG_LIB_CAT_UNIT_ATTENTION:
return 2;
default:
sg_chk_n_print3("reading", &io_hdr);
@@ -228,7 +228,7 @@ int sg_write(int sg_fd, unsigned char * buff, int blocks, int to_block,
printf("Recovered error while writing block=%d, num=%d\n",
to_block, blocks);
break;
- case SG_LIB_CAT_MEDIA_CHANGED:
+ case SG_LIB_CAT_UNIT_ATTENTION:
return 2;
default:
sg_chk_n_print3("writing", &io_hdr);
diff --git a/archive/sgq_dd.c b/archive/sgq_dd.c
index 8bc4aeef..52de5184 100644
--- a/archive/sgq_dd.c
+++ b/archive/sgq_dd.c
@@ -301,7 +301,7 @@ int read_capacity(int sg_fd, int * num_sect, int * sect_sz)
return -1;
}
res = sg_err_category3(&io_hdr);
- if (SG_LIB_CAT_MEDIA_CHANGED == res)
+ if (SG_LIB_CAT_UNIT_ATTENTION == res)
return 2; /* probably have another go ... */
else if (SG_LIB_CAT_CLEAN != res) {
sg_chk_n_print3("read capacity", &io_hdr, 1);
@@ -528,7 +528,7 @@ int sg_finish_io(int wr, Rq_elem * rep)
fprintf(stderr, "Recovered error on block=%d, num=%d\n",
rep->blk, rep->num_blks);
break;
- case SG_LIB_CAT_MEDIA_CHANGED:
+ case SG_LIB_CAT_UNIT_ATTENTION:
return 1;
default:
{
diff --git a/archive/sgq_old_dd.c b/archive/sgq_old_dd.c
index 854e44fe..66c149b3 100644
--- a/archive/sgq_old_dd.c
+++ b/archive/sgq_old_dd.c
@@ -143,7 +143,7 @@ int read_capacity(int sg_fd, int * num_sect, int * sect_sz)
return -1;
}
res = sg_err_category3(&io_hdr);
- if (SG_LIB_CAT_MEDIA_CHANGED == res)
+ if (SG_LIB_CAT_UNIT_ATTENTION == res)
return 2; /* probably have another go ... */
else if (SG_LIB_CAT_CLEAN != res) {
sg_chk_n_print3("read capacity", &io_hdr);
@@ -259,7 +259,7 @@ int sg_finish_io(Rq_coll * clp, int wr, Rq_elem ** repp)
printf("Recovered error on block=%d, num=%d\n",
rep->blk, rep->num_blks);
break;
- case SG_LIB_CAT_MEDIA_CHANGED:
+ case SG_LIB_CAT_UNIT_ATTENTION:
return 1;
default:
sg_chk_n_print3(rep->wr ? "writing": "reading", hp);
diff --git a/archive/sgs_dd.c b/archive/sgs_dd.c
index 4c68d90c..fa34d7b1 100644
--- a/archive/sgs_dd.c
+++ b/archive/sgs_dd.c
@@ -166,7 +166,7 @@ int read_capacity(int sg_fd, int * num_sect, int * sect_sz)
return -1;
}
res = sg_err_category3(&io_hdr);
- if (SG_LIB_CAT_MEDIA_CHANGED == res)
+ if (SG_LIB_CAT_UNIT_ATTENTION == res)
return 2; /* probably have another go ... */
else if (SG_LIB_CAT_CLEAN != res) {
sg_chk_n_print3("read capacity", &io_hdr);
@@ -272,7 +272,7 @@ int sg_finish_io(Rq_coll * clp, int wr, Rq_elem ** repp)
printf("Recovered error on block=%d, num=%d\n",
rep->blk, rep->num_blks);
break;
- case SG_LIB_CAT_MEDIA_CHANGED:
+ case SG_LIB_CAT_UNIT_ATTENTION:
return 1;
default:
sg_chk_n_print3(rep->wr ? "writing": "reading", hp);
diff --git a/build_debian.sh b/build_debian.sh
new file mode 100755
index 00000000..7e815175
--- /dev/null
+++ b/build_debian.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+echo "chmod +x debian/rules"
+chmod +x debian/rules
+
+# in some environments the '-rfakeroot' can cause a failure (e.g. when
+# building as root). If so, remove that argument from the following:
+echo "dpkg-buildpackage -b -rfakeroot"
+dpkg-buildpackage -b -rfakeroot
+
+# If the above succeeds then the ".deb" binary package is placed in the
+# parent directory.
diff --git a/debian/changelog b/debian/changelog
index 6fe2088c..94dfbe14 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+sg3-utils (1.21-0.1) unstable; urgency=low
+
+ * New upstream version
+
+ -- Doug Gilbert <dgilbert@interlog.com> Thu, 6 Jul 2006 10:00:00 -0400
+
sg3-utils (1.20-0.1) unstable; urgency=low
* New upstream version
diff --git a/debian/control b/debian/control
index 875da8eb..d0c4bec5 100644
--- a/debian/control
+++ b/debian/control
@@ -36,6 +36,7 @@ Description: Collection of Linux utilities for devices that use the
* sg_persist - accesses the Persistent Reservation In and Out commands
* sg_prevent - issues PREVENT ALLOW MEDIUM REMOVAL command (MMC + SSC)
* sg_rbuf - tests SCSI bus speed
+ * sg_rdac - vendor specific for RDAC hardware
* sg_read - reads multiple blocks, useful for timing
* sg_read_long - read a "long" block from the given device
* sg_readcap - prints the output of a READ CAPACITY command
@@ -52,6 +53,7 @@ Description: Collection of Linux utilities for devices that use the
* sg_test_rwbuf - tests the SCSI host adapter
* sg_turs - execute a TEST UNIT READY command on the given device
* sg_verify - issues VERIFY command on SCSI disks
+ * sg_vpd - decode VPD pages
* sg_write_long - write a "long" block to the given device
* sg_wr_mode - low level utility to change mode pages
.
diff --git a/debian/docs b/debian/docs
index e9c2decf..0d16aea8 100644
--- a/debian/docs
+++ b/debian/docs
@@ -4,5 +4,5 @@ CREDITS
INSTALL
README
README.sg_start
-doc/u_index.html
+doc/sg3_utils.html
doc/sg_dd.html
diff --git a/debian/rules b/debian/rules
index 52d66a97..52d66a97 100644..100755
--- a/debian/rules
+++ b/debian/rules
diff --git a/debian/sg3-utils.examples b/debian/sg3-utils.examples
index a9da2053..e39721e2 100644
--- a/debian/sg3-utils.examples
+++ b/debian/sg3-utils.examples
@@ -1,2 +1 @@
examples/*
-archive
diff --git a/doc/u_index.html b/doc/sg3_utils.html
index 9701c3e8..bc9eb18a 100644
--- a/doc/u_index.html
+++ b/doc/sg3_utils.html
@@ -17,33 +17,16 @@ sg3_utils package</h1>
<a href="#intro"></a>
<ol id="mozToc">
<!--mozToc h1 1 h2 2 h3 3 h4 4 h5 5 h6 6--><li><a href="#mozTocId697451">
-The&nbsp; Linux sg3_utils package</a>
+The&nbsp; Linux
+sg3_utils package</a>
<ol>
<li><a href="#mozTocId746876"> Introduction</a></li>
- <li><a href="#mozTocId764860"> Contents of sg3_utils</a></li>
+ <li><a href="#mozTocId832900"> Contents of sg3_utils</a></li>
<li><a href="#mozTocId143590">Changing mode page
settings</a></li>
<li><a href="#mozTocId301018">Examples</a></li>
<li><a href="#mozTocId585558"> Notes</a></li>
<li><a href="#mozTocId479511"> Download</a></li>
- <li><a href="#mozTocId185822"> Programs from other sources</a>
- <ol>
- <li><a href="#mozTocId348388"> scu</a></li>
- <li><a href="#mozTocId668788"> dt</a></li>
- <li><a href="#mozTocId673853">scsiinfo</a></li>
- <li><a href="#mozTocId550034"> scsidev</a></li>
- <li><a href="#mozTocId212850"> ddrescue</a></li>
- <li><a href="#mozTocId380766"> mapscsi</a></li>
- <li><a href="#mozTocId108175"> scsimap</a></li>
- <li><a href="#mozTocId143073"> smartsuite</a></li>
- <li><a href="#mozTocId535090">smartmontools</a></li>
- <li><a href="#mozTocId974127">scsirastools</a></li>
- <li><a href="#mozTocId783871">SeaTools</a></li>
- <li><a href="#mozTocId264328"> devlabel</a></li>
- <li><a href="#mozTocId295637">plscsi</a></li>
- <li><a href="#mozTocId325127">safte-monitor</a></li>
- </ol>
- </li>
</ol>
</li>
</ol>
@@ -59,9 +42,9 @@ examples of devices that use SCSI command sets.<br>
<br>
SCSI command sets are divided into a common set and several device
class specific sets. The common set of commands is referred to as the
-SCSI Primary Commands (SPC) with SPC-3 being the most recent standard
-(or soon will be). The SCSI INQUIRY command whose support is mandatory
-for all SCSI devices is defined in SPC-3. The SCSI Block Commands (SBC)
+SCSI Primary Commands (SPC) with SPC-3 being the most recent standard.
+The mandatory SCSI INQUIRY command is defined in SPC-3. The SCSI Block
+Commands (SBC)
cover direct access devices such as disks. The MultiMedia Commands
(MMC) cover CD and DVD drives and the media within them. SCSI
command sets and transport
@@ -77,16 +60,16 @@ targeted the linux kernel 2.2 series with some support for the
2.0 series. See an earlier version of this web <a href="uu_index.html">page</a>
for further information about sg_utils.&nbsp; This
document describes
-<span style="font-weight: bold;">version 1.20</span> of <span
+<span style="font-weight: bold;">version 1.21</span> of <span
style="font-weight: bold;">sg3_utils</span> .&nbsp; A subset of these
utilities have been ported to the FreeBSD and Tru64 operating systems.<br>
<br>
-In the linux kernel 2.4 series most these utilities must be
+In the linux kernel 2.4 series most of these utilities must be
used with a SCSI generic (sg) driver device name (e.g. <span
style="font-family: monospace;">/dev/sg0</span>). In
the lk 2.6 series almost all of these utilities can be used with the
primary device names as well (e.g. <span
- style="font-family: monospace;">/dev/sda, /dev/sdc0, /dev/st0</span>
+ style="font-family: monospace;">/dev/sda, /dev/scd0, /dev/st0</span>
and
<span style="font-family: monospace;">/dev/hdd</span> (if it is an
ATAPI device)). The SCSI generic (sg) interface
@@ -95,10 +78,11 @@ since the drivers behind primary device names have their
own policies, may interfere with error processing and, in some cases,
run their own
state machines.<br>
-<p>SCSI utility programs from other sources are listed and
-briefly described in
-the final <a href="#mozTocId185822">section</a>. </p>
-<h2><a class="mozTocH2" name="mozTocId764860"></a> Contents of sg3_utils</h2>
+<p>A list of SCSI and storage utility programs that was at the bottom
+of this page has been moved to the <a href="tools.html">tools</a> page.<span
+ style="font-weight: bold;"><br>
+</span></p>
+<h2><a class="mozTocH2" name="mozTocId832900"></a> Contents of sg3_utils</h2>
Here is a listing in alphabetical order of the utilities in the
sg3_utils package:<br>
<br>
@@ -201,6 +185,19 @@ commands.<br>
</td>
</tr>
<tr>
+ <td style="vertical-align: top;">sg_chk_asc<br>
+ </td>
+ <td style="vertical-align: top;">-<br>
+ </td>
+ <td style="vertical-align: top;">utils<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">check asc/ascq codes from
+www.t10.org page against sg3_utils table<br>
+ </td>
+ </tr>
+ <tr>
<td style="vertical-align: top;">sginfo<br>
</td>
<td style="vertical-align: top;">MODE SENSE/SELECT, READ DEFECT<br>
@@ -329,7 +326,8 @@ to the device.<br>
<td style="vertical-align: top;">fb,t64 </td>
<td style="vertical-align: top;">fetch standard response, VPD
pages (including device identification) or version descriptors. Also
-can perform IDENTIFY (PACKET) DEVICE ATA command.<br>
+can perform IDENTIFY (PACKET) DEVICE ATA command. VPD page decoding now
+also performed by sg_vpd .<br>
</td>
</tr>
<tr>
@@ -448,6 +446,18 @@ Typically for testing the SCSI transport (for throughput or errors)<br>
</td>
</tr>
<tr>
+ <td style="vertical-align: top;">sg_rdac<br>
+ </td>
+ <td style="vertical-align: top;">MODE SENSE/SELECT<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">fb,t64</td>
+ <td style="vertical-align: top;">display or modify RDAC redundant
+controller mode page<br>
+ </td>
+ </tr>
+ <tr>
<td style="vertical-align: top;">sg_read<br>
</td>
<td style="vertical-align: top;">READ<br>
@@ -551,6 +561,20 @@ over another (or others).<br>
</td>
</tr>
<tr>
+ <td style="vertical-align: top;">sg_sat_identify<br>
+ </td>
+ <td style="vertical-align: top;">ATA PASS THROUGH<br>
+ </td>
+ <td style="vertical-align: top;">examples<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">Sends IDENTIFY (PACKET) DEVICE
+ATA command via the SAT ATA PASS THROUGH SCSI command. Also
+sg_sat_chk_power and sg_sat_smart_rd_data .<br>
+ </td>
+ </tr>
+ <tr>
<td style="vertical-align: top;">sg_scan<br>
</td>
<td style="vertical-align: top;">[INQUIRY]<br>
@@ -679,6 +703,19 @@ disks, stops on the first error found. Does not yield any data. Useful
for media scans.</td>
</tr>
<tr>
+ <td style="vertical-align: top;">sg_vpd<br>
+ </td>
+ <td style="vertical-align: top;">INQUIRY<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">fb,t64<br>
+ </td>
+ <td style="vertical-align: top;">Decodes standard and some vendor
+Vital Product Data (VPD) pages.<br>
+ </td>
+ </tr>
+ <tr>
<td style="vertical-align: top;">sg_write_long<br>
</td>
<td style="vertical-align: top;">WRITE LONG<br>
@@ -714,9 +751,11 @@ commands" column. For example many utilities issue a SCSI INQUIRY
command to find out the peripheral device type of the given SCSI
device. Some SCSI commands listed above are only relevant to a specific
device type (e.g. FORMAT UNIT) and should not be sent to a device
-belonging to other peripheral device types (and in some cases the logic
-blocks inappropriate attempts). See the COVERAGE file in the main
-directory of the tarball for a more exhaustive list of SCSI commands
+belonging to other peripheral device types (and in some cases these
+utilities will
+block inappropriate attempts). See the COVERAGE file in the main
+directory of the tarball for a more exhaustive list of SCSI (and ATA)
+commands
issued by the sg3_utils utilities.<br>
<br>
If the "subdirectory"
@@ -727,14 +766,14 @@ directory have "man" pages and some in the subdirectories have "man"
pages. <br>
<br>
If the "Ported" column is empty then the utility is only found in
-linux. At this time the other ports are to FreeBSD, indicated by "fb"
-in the "ported" column, and to Tru64, indicated by "t64" in the
-"ported" column. See the README.freebsd and the README.tru64 files
+Linux. At this time the other ports are to FreeBSD, indicated by "fb"
+in the "Ported" column, and to Tru64, indicated by "t64" in the
+"Ported" column. See the README.freebsd and the README.tru64 files
for more information.<br>
<br>
In the above table all utilities in the main directory except sgp_dd
issue SG_IO ioctls when they want to send SCSI commands to the given
-device. The sgp_dd utility will only issue SCSI commands using the sg
+device. The sgp_dd utility issues SCSI commands using the sg
driver's asynchronous ( write()/read() ) interface to device nodes that
have the sg driver's major device number (i.e. "char" major 21). This
means that all utilities in the main directory are "safe" with any
@@ -752,9 +791,68 @@ For example invoking a "sg_format -F" utility on a disk with mounted
file
systems on it is going to cause damage.<br>
<br>
-These command line utilities exit with process status 0 when
-successful. In most cases if an error is detected the process exits
-with a status value of 1 .<br>
+Each process containing one of these command line utilities completes
+with an exit status 0 when
+successful. Prior to version 1.21 all errors yielded an exit status of
+1. From version 1.21 the exit status is one of:<br>
+<ul>
+ <li><span style="font-weight: bold;">1</span>&nbsp;&nbsp; syntax
+error in command line options or their arguments, or an illegal
+combination of options.</li>
+ <li><span style="font-weight: bold;">2</span>&nbsp;&nbsp; the device
+reports that it is not ready for the operation requested. The device
+may be in the process of becoming ready (e.g. spinning up but not at
+speed) so the utility may work a little while later.</li>
+ <li><span style="font-weight: bold;">3</span>&nbsp;&nbsp; the device
+reports a medium or hardware error (or a blank check). For example an
+attempt to read a corrupted block on a disk will yield this value. <br>
+ </li>
+ <li><span style="font-weight: bold;">5</span>&nbsp;&nbsp; the device
+reports an "illegal request" with an additional sense code other than
+"invalid operation code". This is often a supported command with a
+field set requesting an unsupported capability. For commands that
+require a "service action" field (e.g. READ CAPACITY(16) ) this value
+can indicate that the
+command is not supported.</li>
+ <li><span style="font-weight: bold;">6</span>&nbsp;&nbsp; the device
+reports a "unit attention" condition. This usually indicates that
+something unrelated to the requested command has occurred (e.g. a
+device reset) potentially before the current SCSI command was sent. The
+requested command has not been executed by the device. Note that unit
+attention conditions are usually only reported once by a device.</li>
+ <li><span style="font-weight: bold;">9</span>&nbsp;&nbsp; the device
+reports an illegal request with an additional sense code of "invalid
+operation code" which means that it doesn't support the requested
+command.</li>
+ <li><span style="font-weight: bold;">10</span>&nbsp;&nbsp; the
+&lt;scsi_device&gt; reports it has a check condition but "no sense".
+Some polling commands (e.g. REQUEST SENSE) can react this way. It is
+unlikely that this value will occur as an exit status.</li>
+ <li><span style="font-weight: bold;">11</span>&nbsp;&nbsp; the device
+reports a "recovered error". The requested command was successful. Most
+likely a utility will report a recovered error to stderr and continue,
+probably leaving the utility with an exit status of 0 .</li>
+ <li><span style="font-weight: bold;">15</span> &nbsp; the utility is
+unable to open, close or use the given device. The given file name
+could be incorrect or there may be permission problems. Adding the '-v'
+option may give more information.</li>
+ <li><span style="font-weight: bold;">33</span>&nbsp;&nbsp; the
+command sent to the device has timed out.</li>
+ <li><span style="font-weight: bold;">97</span>&nbsp;&nbsp; the
+response to a SCSI command failed sanity checks.</li>
+ <li><span style="font-weight: bold;">98</span>&nbsp;&nbsp; the device
+reports it has a check condition but the error doesn't fit into any of
+the above categories.</li>
+ <li><span style="font-weight: bold;">99</span>&nbsp;&nbsp; any errors
+that can't be categorized into values 1 to 98 may yield this value.
+This includes transport and operating system errors.<br>
+ </li>
+</ul>
+Many of the above exit statuses will be repeatable so executing the
+utility again with one or more '-v' options may yield more information.
+Unit attentions (exit status 6) are only reported once per condition.
+Notice that some of the lower exit status values correspond to the SCSI
+sense key values that they correspond to.<br>
<h2><a class="mozTocH2" name="mozTocId143590"></a>Changing mode page
settings</h2>
SCSI devices store settings (metadata) that could possibly be changed
@@ -923,6 +1021,47 @@ Peripheral device type: disk]</span><br style="font-family: monospace;">
<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp;&nbsp;
0x83&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Device identification</span><br>
<br>
+For displaying VPD pages, sg_vpd (or sdparm) may be a better choice
+than sg_inq as sg_vpd has a simpler, less cluttered command line
+interface and additional support for vendor specific VPD pages.<br>
+<br>
+<span style="font-family: monospace;"># sg_vpd /dev/sdh</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">Supported VPD pages VPD page:</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp; Supported VPD pages [sv]</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp; Unit serial number [sn]</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp; Implemented operating
+definition (obs) [iod]</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp; Device identification [di]</span><br>
+<br>
+The following displays a subset of the device identification VPD page,
+namely the designators for the target port:<br>
+<br>
+<span style="font-family: monospace;"># sg_vpd --page=di_port /dev/sdh</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">Device Identification VPD page:</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp; Target port:</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp; desig_type:
+NAA,&nbsp; code_set: Binary</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp;&nbsp;
+transport: Serial Attached SCSI (SAS)</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+0x5000c500005208ee</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp; desig_type:
+Relative target port,&nbsp; code_set: Binary</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp;&nbsp;
+transport: Serial Attached SCSI (SAS)</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+Relative target port: 0x2</span><br>
+<br>
The sg_scan and sg_map utilities show the relationships between sg
devices and their &lt;bus, channel, target, lun&gt; tuples and their
primary device node names:<br>
@@ -1023,167 +1162,19 @@ devices and support the SG_IO ioctl from lk 2.6.6 onwards.<br>
each package. </p>
<h2><a class="mozTocH2" name="mozTocId479511"></a> Download</h2>
Various tarballs that include the source, a README, CHANGELOG, INSTALL
-and a Makefile can be found in the <span
- style="text-decoration: underline;">table 2</span> on the main
-page (link at the bottom of this page). There are also source,
+and a Makefile can be found in the <a
+ href="http://www.torque.net/sg/index.html#mozTocId566194"><span
+ style="text-decoration: underline;">table 2</span></a> on the <a
+ href="index.html">main</a>
+page. There are also source,
i386 binary rpm and deb packages available. Both sg3_utils and the
older sg_utils packages can
-be found on http://freshmeat.net .
-<h2><a class="mozTocH2" name="mozTocId185822"></a> Programs from other
-sources</h2>
-<h3><a class="mozTocH3" name="mozTocId348388"></a> scu</h3>
-The SCSI Command Utility (SCU) implements various SCSI commands
-necessary for normal maintenance and diagnostics of SCSI
-peripherals. Some of its features include: formatting, scanning for
-(and reassigning) bad blocks, downloading new firmware, executing
-diagnostics and obtaining performance information. It is available on
-several Unix platforms (and NT), however it is only currently available
-in binary form. See&nbsp;<a class="moz-txt-link-freetext"
- href="http://www.scsifaq.org/RMiller_Tools/index.html"><span
- style="font-family: monospace;"> </span>www.scsifaq.org/RMiller_Tools/index.html</a>
-for more details.
-<h3><a class="mozTocH3" name="mozTocId668788"></a> dt</h3>
-The Data Test (DT) program is modelled on dd's syntax but <b>dt </b>can
-do a lot more than sequential copies. It is a comprehensive data test
-program for SCSI devices such as disks, tapes and cdrom/dvds. It is
-available on several Unix platforms (and NT), and its source is
-available (unlike its stable mate <b>scu</b> discussed earlier).
-See&nbsp;<a class="moz-txt-link-freetext"
- href="http://www.scsifaq.org/RMiller_Tools/index.html"><span
- style="font-family: monospace;"></span>www.scsifaq.org/RMiller_Tools/index.html</a>&nbsp;for
-more details. Both <b>scu</b> and <b>dt </b>are written by Robin
-T. Miller &lt;Robin.Miller at netapp dot com&gt;<br>
-<h3><a class="mozTocH3" name="mozTocId673853"></a>scsiinfo</h3>
-Older package that includes the <span style="font-weight: bold;">scsiinfo</span>
-and <span style="font-weight: bold;">scsiformat</span> utilities plus
-tcl/tk GUI interfaces for those utilities. The last update of the <span
- style="font-weight: bold;">scsiinfo</span> package was in 1997. The
-function and syntax of the <span style="font-weight: bold;">scsiinfo</span>
-utility have inspired <span style="font-weight: bold;">sginfo</span>
-which now can be considered as a "drop in" replacement for <span
- style="font-weight: bold;">scsiinfo</span> . Recent changes to SCSI
-standards (e.g. extra and extended mode pages) are reflected in <span
- style="font-weight: bold;">sginfo</span> . In a similar way the <span
- style="font-weight: bold;">sg_format</span> utility can be thought of
-as a modern replacement for the <span style="font-weight: bold;">scsiformat</span>
-utility.<br>
-<h3><a class="mozTocH3" name="mozTocId550034"></a> scsidev</h3>
-Kurt Garloff &lt;garloff@suse.de&gt; describes this utility thus: "This
-program scans the SCSI bus and creates device nodes in /dev/scsi/,
-which have a naming corresponding to their SCSI IDs and LUNs, just
-like with devfs. (The devfs has no notion of host adapter IDs,
-scsidev is better here.) Furthermore, the devices are inquired to
-tell their names and serial numbers. Those can be compared with the
-entries in a database /etc/scsi.alias and device nodes corresponding
-to these entries are being built. So,this will even work if you change
-the SCSI IDs of a device, where the devfs approach would fail". For
-more information see: <a
- href="http://www.garloff.de/kurt/linux/scsidev">www.garloff.de/kurt/linux/scsidev</a>
-. He also has the useful <b>rescan-scsi-bus.sh</b> script at the same
-location.
-<h3><a class="mozTocH3" name="mozTocId212850"></a> ddrescue</h3>
-This is another utility from&nbsp; Kurt Garloff &lt;garloff@suse.de&gt;
-for rescuing damaged media. It is a variant of dd that will continue
-passed errors on the input file. It is applicable to any device that
-can be read by dd (e.g. IDE and SCSI disks, cds and tapes). For more
-information see: <a href="http://www.garloff.de/kurt/linux/ddrescue">www.garloff.de/kurt/linux/ddrescue</a><br>
-<br>
-There is also a GNU program of the same name that has a similar
-function. See <a href="http://ftp.gnu.org/gnu/ddrescue/">
-http://ftp.gnu.org/gnu/ddrescue/</a> . John Gilmour has some
-information on disk recovery utilities at&nbsp; <a
- href="http://www.toad.com/gnu/sysadmin/">http://www.toad.com/gnu/sysadmin/</a>
-.<br>
-<h3><a class="mozTocH3" name="mozTocId380766"></a> mapscsi</h3>
-Michael Clark &lt;michael@metaparadigm.com&gt; describes his utility
-thus: "mapscsi is a small utility that creates a consistent mapping
-to Linux scsi devices. mapscsi achieves this by creating symbolic
-links to linux scsi disk devices after scanning all scsi disk devices,
-finding out their host, channel, id, lun, pci location (if available),
-Fibre Channel world wide node and port names, loop and port ids (with
-qla2x00 v4.46.5 driver) vendor, product and serial number details and
-using this information plus a mapping rules file containing device
-templates to dynamically create link names". For more information see: <a
- href="http://gort.metaparadigm.com/mapscsi">http://gort.metaparadigm.com/mapscsi</a>
-.
-<h3><a class="mozTocH3" name="mozTocId108175"></a> scsimap</h3>
-Steve Cameron &lt;smcameron@yahoo.com&gt; has the following description
-at his site:
-<p>This is a utility to create and maintain symbolic links mapping a
-predictable set of names to the rather unpredictable names used by
-linux for disk devices. For example, you might map: <br>
-<tt>/dev/mydisk1 -&gt; /dev/sda1</tt> <br>
-<tt>/dev/mydisk2 -&gt; /dev/sdb1</tt> <br>
-<tt>/dev/mydisk3 -&gt; /dev/sdc1</tt> <br>
-If you removed the disk corresponding to /dev/sdb1, then on reboot,
-/dev/sdc1 will become /dev/sdb1, and /dev/sdc1 will be gone. and your
-fstab will be wrong, etc. (Especially problematic on a SAN). <b>scsimap</b>
-will maintain the mapping so that after the reboot, /dev/mydisk3 -&gt;
-will point to /dev/sdb1 and /dev/mydisk2 will be gone. scsimap also
-handles later generation Compaq array controllers (those which use
-the cciss driver.) </p>
-<p>See: <a href="http://www.geocities.com/smcameron">www.geocities.com/smcameron</a>
-. </p>
-<h3><a class="mozTocH3" name="mozTocId143073"></a> smartsuite</h3>
-This is a package that supports S.M.A.R.T. capabilities built into
-modern IDE and SCSI-3 disks. Self-Monitoring, Analysis and Reporting
-Technology (S.M.A.R.T.) is described at <a
- href="http://www.pc.ibm.com/us/infobrf/ibsmart.html">www.pc.ibm.com/us/infobrf/ibsmart.html</a>.
-For smartsuite see <a href="http://sourceforge.net/projects/smartsuite">sourceforge.net/projects/smartsuite</a>
-. <br>
-<br>
-<h3><a class="mozTocH3" name="mozTocId535090"></a>smartmontools</h3>
-This project has taken over from the aforementioned
-smartsuite which is currently not actively maintained. See <a
- href="http://smartmontools.sourceforge.net">http://smartmontools.sourceforge.net</a>
-. The author is a maintainer of the SCSI component of this project.
-[The lead maintainer is Bruce Allen.]<br>
-<br>
-<h3><a class="mozTocH3" name="mozTocId974127"></a>scsirastools</h3>
-"This project includes changes that enhance the Reliability,
-Availability and Serviceability (RAS) of the drivers that are
-commonly used in a Linux software RAID-1 configuration. Other
-efforts have been made to enable various common hardware RAID
-adapters and their drivers on Linux." See <a
- href="http://scsirastools.sourceforge.net">http://scsirastools.sourceforge.net</a>
-. The package contains some low level scsi utilities including <b>sgdskfl</b>
-to load disk firmware, <b>sgmode</b> to get and set mode pages, <b>sgdefects</b>
-to read primary and grown defect lists and <b>sgdiag</b> to perform
-format and other test functions.<br>
-&nbsp;<br>
-<h3><a class="mozTocH3" name="mozTocId783871"></a>SeaTools</h3>
-SeaTools is a freely available (binary, not source) utility for disk
-diagnostics from Seagate which is a disk manufacturer. It can be found
-at <a href="http://www.seagate.com">http://www.seagate.com</a> under
-the support tab. They have both a command line and a graphical utility.
-&nbsp;Some of the facilities will work on any SCSI disks while others
-are Seagate specific. Self tests, mode page settings and formats (to
-different block sizes) are amongst the facilities available.<br>
-<h3><a class="mozTocH3" name="mozTocId264328"></a> devlabel</h3>
-Devlabel is "a small userspace app which maps symlinks to underlying
-disk names. It uses [INQUIRY VPD] Page83/Page80 data to track the
-true locations of disks even if their hd/sd name changes and simply
-updates the symlink to point to the right place." Sysfs support for the
-lk 2.6 series and support for multi-path configurations is on the
-author's "to do" list. See <a href="http://www.lerhaupt.com/linux.html">http://www.lerhaupt.com/linux.html</a>
-.<br>
-<br>
-<h3><a class="mozTocH3" name="mozTocId295637"></a>plscsi</h3>
-This utility allows arbitrary SCSI commands to be sent to a device. See
-<a href="http://members.aol.com/plscsi">http://members.aol.com/plscsi</a>
-. It is similar to FreeBSD's camcontrol command.<br>
-<h3><a class="mozTocH3" name="mozTocId325127"></a>safte-monitor</h3>
-SAF-TE (SCSI Attached Fault-Tolerant Enclosure) is a SCSI command set
-for monitoring and controlling enclosures and RAIDs. SAF-TE devices
-report "processor" peripheral device type (0x3) in their INQUIRY
-responses. More recent products tend to use SES (drafts at
-http://www.t10.org ) which covers similar functionality. For a SAF-TE
-monitoring tool for linux see: <a
- href="http://oss.metaparadigm.com/safte-monitor">http://oss.metaparadigm.com/safte-monitor</a><br>
+be found on http://freshmeat.net .<a
+ href="http://oss.metaparadigm.com/safte-monitor"></a><br>
<br>
<p>Return to <a href="index.html">main</a> page. </p>
<center>
-<p>Last updated: 11th April 2006<br>
+<p>Last updated: 6th July 2006<br>
<br>
</p>
</center>
diff --git a/doc/sg_dd.html b/doc/sg_dd.html
index f898ab15..83da1357 100644
--- a/doc/sg_dd.html
+++ b/doc/sg_dd.html
@@ -39,7 +39,7 @@ of the standard Unix command <span style="font-weight: bold;">dd</span>
which
copies files. The sg_dd utility is specialized for devices that use the
SCSI command set in the Linux operating system. The sg_dd utility is
-found in the <a href="u_index.html">sg3_utils</a> package which
+found in the <a href="sg3_utils.html">sg3_utils</a> package which
targets the linux kernel 2.4 and
2.6 series.<br>
<br>
@@ -70,7 +70,7 @@ the
sg_dd utility; they use SCSI command sets.<br>
<br>
This page outlines the features of the sg_dd utility version 5.48 found
-in the <a href="u_index.html">sg3_utils</a> version 1.20 package.<br>
+in the <a href="sg3_utils.html">sg3_utils</a> version 1.20 package.<br>
<h2><a class="mozTocH2" name="mozTocId352668"></a>&nbsp;dd like features</h2>
The basic syntax of the sg_dd utility is the same as the dd command in
Unix. That said, the syntax of the dd command in Unix is different from
@@ -1041,7 +1041,7 @@ MEDIUM errors, while recovering as much data as possible.<br>
<br>
<p>Return to <a href="index.html">main</a> page. </p>
<center>
-<p>Last updated: 4th April 2006<br>
+<p>Last updated: 29th May 2006<br>
<br>
</p>
</center>
diff --git a/doc/tools.html b/doc/tools.html
new file mode 100644
index 00000000..de38f74a
--- /dev/null
+++ b/doc/tools.html
@@ -0,0 +1,312 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <title>Storage and SCSI tools</title>
+ <meta http-equiv="Content-Type"
+ content="text/html; charset=iso-8859-1">
+ <meta name="keywords" content="tool, storgae, SCSI, linux">
+ <meta name="GENERATOR"
+ content="Mozilla/4.79 [en] (X11; U; Linux 2.5.31 i686) [Netscape]">
+</head>
+<body alink="#ff0000" background="paper.jpg" bgcolor="#ffffff"
+ link="#0000ff" text="#000000" vlink="#000080">
+<center>
+<h1><a class="mozTocH1" name="mozTocId697451"></a> Tools for storage
+and SCSI<br>
+</h1>
+</center>
+<a href="#intro"></a>
+<ol id="mozToc">
+<!--mozToc h1 1 h2 2 h3 3 h4 4 h5 5 h6 6--><li><a href="#mozTocId697451">
+Tools for storage
+and SCSI </a>
+ <ol>
+ <li><a href="#mozTocId746876"> Introduction</a></li>
+ <li><a href="#mozTocId520511">blktool</a></li>
+ <li><a href="#mozTocId212850"> ddrescue</a></li>
+ <li><a href="#mozTocId264328"> devlabel</a></li>
+ <li><a href="#mozTocId668788"> dt</a></li>
+ <li><a href="#mozTocId231383">fio</a></li>
+ <li><a href="#mozTocId851510">hdparm</a></li>
+ <li><a href="#mozTocId777351">lsscsi</a></li>
+ <li><a href="#mozTocId380766"> mapscsi</a></li>
+ <li><a href="#mozTocId295637">plscsi</a></li>
+ <li><a href="#mozTocId325127">safte-monitor</a></li>
+ <li><a href="#mozTocId222172">scsiadd</a></li>
+ <li><a href="#mozTocId550034"> scsidev</a></li>
+ <li><a href="#mozTocId520455">scsi_id</a></li>
+ <li><a href="#mozTocId673853">scsiinfo</a></li>
+ <li><a href="#mozTocId108175"> scsimap</a></li>
+ <li><a href="#mozTocId974127">scsirastools</a></li>
+ <li><a href="#mozTocId348388"> scu</a></li>
+ <li><a href="#mozTocId382975">sdparm</a></li>
+ <li><a href="#mozTocId783871">SeaTools</a></li>
+ <li><a href="#mozTocId847247">sg3_utils</a></li>
+ <li><a href="#mozTocId705237">sg_utils</a></li>
+ <li><a href="#mozTocId535090">smartmontools</a></li>
+ <li><a href="#mozTocId143073"> smartsuite</a></li>
+ <li><a href="#mozTocId53551">smp_utils</a></li>
+ <li><a href="#mozTocId440267">spew</a></li>
+ <li><a href="#mozTocId708282">udev</a></li>
+ <li><a href="#mozTocId728109">Conclusion</a></li>
+ </ol>
+ </li>
+</ol>
+<h2><a class="mozTocH2" name="mozTocId746876"></a> Introduction</h2>
+The page is a resource for those looking for tools for storage devices
+with an emphasis on SCSI devices. These tools are user space prorams
+rather than kernel drivers. The entries are brief abstracts with links
+to pages that have more information. The entries are in alphabetical
+order.<br>
+<h2><a class="mozTocH2" name="mozTocId520511"></a>blktool</h2>
+This is a utility for fetching and changing parameters in the linux
+block susbsystem. It supports ATA and SCSI disks with some support for
+cd/dvd drives. See this <a
+ href="http://sourceforge.net/projects/gkernel/">site</a> . For finer
+grain control of SCSI devices (and SATA(PI) devices connected via a
+SCSI
+transport) see the <a href="#mozTocId382975">sdparm</a> utility below.<br>
+<h2><a class="mozTocH3" name="mozTocId212850"></a> ddrescue</h2>
+This is another utility from&nbsp; Kurt Garloff &lt;garloff@suse.de&gt;
+for rescuing damaged media. It is a variant of dd that will continue
+passed errors on the input file. It is applicable to any device that
+can be read by dd (e.g. IDE and SCSI disks, cds and tapes). For more
+information see: <a href="http://www.garloff.de/kurt/linux/ddrescue">www.garloff.de/kurt/linux/ddrescue</a><br>
+<br>
+There is also a GNU program of the same name that has a similar
+function. See <a href="http://ftp.gnu.org/gnu/ddrescue/">
+http://ftp.gnu.org/gnu/ddrescue/</a> . John Gilmour has some
+information on disk recovery utilities at&nbsp; <a
+ href="http://www.toad.com/gnu/sysadmin/">http://www.toad.com/gnu/sysadmin/</a>
+.<br>
+<h2><a class="mozTocH3" name="mozTocId264328"></a> devlabel</h2>
+Devlabel is "a small userspace app which maps symlinks to underlying
+disk names. It uses [INQUIRY VPD] Page83/Page80 data to track the
+true locations of disks even if their hd/sd name changes and simply
+updates the symlink to point to the right place." Sysfs support for the
+lk 2.6 series and support for multi-path configurations is on the
+author's "to do" list. See <a href="http://www.lerhaupt.com/linux.html">http://www.lerhaupt.com/linux.html</a>
+. Probably better to use scsi_id/udev in the lk 2.6 series kernels<br>
+<h2><a class="mozTocH3" name="mozTocId668788"></a> dt</h2>
+The Data Test (DT) program is modelled on dd's syntax but <b>dt </b>can
+do a lot more than sequential copies. It is a comprehensive data test
+program for SCSI devices such as disks, tapes and cdrom/dvds. It is
+available on several Unix platforms (and NT), and its source is
+available (unlike its stable mate <b>scu</b> discussed earlier).
+See&nbsp;<a class="moz-txt-link-freetext"
+ href="http://www.scsifaq.org/RMiller_Tools/index.html"><span
+ style="font-family: monospace;"></span>www.scsifaq.org/RMiller_Tools/index.html</a>&nbsp;for
+more details. dt is written by Robin
+T. Miller &lt;Robin.Miller at netapp dot com&gt;<br>
+<h2><a class="mozTocH2" name="mozTocId231383"></a>fio</h2>
+This utility tests the performance of Linux/Unix block devices or file<span
+ style="font-family: monospace;"> </span>systems. <a
+ href="http://brick.kernel.dk/snaps/">fio</a> is a tool that will spawn
+a number of threads doing a<span style="font-family: monospace;"> </span>particular
+type of io action as specified by the user in a defined job<span
+ style="font-family: monospace;"> </span>file. <a
+ href="http://brick.kernel.dk/snaps/">fio</a> supports various types of
+io backends, such as regular sync<span style="font-family: monospace;">
+</span>io, linux aio, posix aio, and sg v3 io (SG_IO and queued
+read/write).<span style="font-family: monospace;"> </span>fio can be
+used for both performance testing, data/media verification,<span
+ style="font-family: monospace;"> </span>etc.<br>
+<h2><a class="mozTocH2" name="mozTocId851510"></a>hdparm</h2>
+This utility gets and sets ATA drive parameters under Linux. Can also
+get transport parameters for ATAPI devices. There is also limited
+support for SCSI devices. See this <a
+ href="http://sourceforge.net/projects/hdparm/">site</a> . Overlaps in
+functionality with blktool (see <a href="#mozTocId520511">above</a> ).<br>
+<h2><a class="mozTocH2" name="mozTocId777351"></a>lsscsi</h2>
+This utility lists SCSI devices (or hosts) that are present in the
+linux kernel 2.6 series. It is a passive tools in the sense that it
+"data mines" the linux sysfs file system rather than attempting to
+query devices. See <a href="../scsi/lsscsi.html">lsscsi</a>&nbsp; .<br>
+<h2><a class="mozTocH3" name="mozTocId380766"></a> mapscsi</h2>
+Michael Clark &lt;michael@metaparadigm.com&gt; describes his utility
+thus: "mapscsi is a small utility that creates a consistent mapping
+to Linux scsi devices. mapscsi achieves this by creating symbolic
+links to linux scsi disk devices after scanning all scsi disk devices,
+finding out their host, channel, id, lun, pci location (if available),
+Fibre Channel world wide node and port names, loop and port ids (with
+qla2x00 v4.46.5 driver) vendor, product and serial number details and
+using this information plus a mapping rules file containing device
+templates to dynamically create link names". For more information see: <a
+ href="http://gort.metaparadigm.com/mapscsi">http://gort.metaparadigm.com/mapscsi</a>
+. In the linux 2.6 series the scsi_id/udev pair is probably appropriate.<br>
+<h2><a class="mozTocH3" name="mozTocId295637"></a>plscsi</h2>
+This utility allows arbitrary SCSI commands to be sent to a device. See
+<a href="http://members.aol.com/plscsi">http://members.aol.com/plscsi</a>
+. It is similar to FreeBSD's camcontrol command.<br>
+<h2><a class="mozTocH3" name="mozTocId325127"></a>safte-monitor</h2>
+SAF-TE (SCSI Attached Fault-Tolerant Enclosure) is a SCSI command set
+for monitoring and controlling enclosures and RAIDs. SAF-TE devices
+report "processor" peripheral device type (0x3) in their INQUIRY
+responses. More recent products tend to use SES (drafts at
+http://www.t10.org ) which covers similar functionality. For a SAF-TE
+monitoring tool for linux see: <a
+ href="http://oss.metaparadigm.com/safte-monitor">http://oss.metaparadigm.com/safte-monitor</a>
+.<br>
+<h2><a class="mozTocH2" name="mozTocId222172"></a>scsiadd</h2>
+This utility permits a user to add and remove scsi devices from the
+Linux scsi subsystem on the
+fly. See scsiadd on this <a href="http://llg.cubic.org/tools/">page</a>
+.
+<h2><a class="mozTocH3" name="mozTocId550034"></a> scsidev</h2>
+Kurt Garloff &lt;garloff@suse.de&gt; describes this utility thus: "This
+program scans the SCSI bus and creates device nodes in /dev/scsi/,
+which have a naming corresponding to their SCSI IDs and LUNs, just
+like with devfs. (The devfs has no notion of host adapter IDs,
+scsidev is better here.) Furthermore, the devices are inquired to
+tell their names and serial numbers. Those can be compared with the
+entries in a database /etc/scsi.alias and device nodes corresponding
+to these entries are being built. So,this will even work if you change
+the SCSI IDs of a device, where the devfs approach would fail". For
+more information see: <a
+ href="http://www.garloff.de/kurt/linux/scsidev">www.garloff.de/kurt/linux/scsidev</a>
+. He also has the useful <b>rescan-scsi-bus.sh</b> script at the same
+location.<br>
+<h2><a class="mozTocH2" name="mozTocId520455"></a>scsi_id</h2>
+This utility is used by various linux 2.6 series distributions together
+udev to dynamically add and remove scsi device nodes. See <a
+ href="http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html">udev</a>
+'s page.<br>
+<h2><a class="mozTocH3" name="mozTocId673853"></a>scsiinfo</h2>
+Older package that includes the <span style="font-weight: bold;">scsiinfo</span>
+and <span style="font-weight: bold;">scsiformat</span> utilities plus
+tcl/tk GUI interfaces for those utilities. The last update of the <span
+ style="font-weight: bold;">scsiinfo</span> package was in 1997. The
+function and syntax of the <span style="font-weight: bold;">scsiinfo</span>
+utility have inspired <span style="font-weight: bold;">sginfo</span>
+which now can be considered as a "drop in" replacement for <span
+ style="font-weight: bold;">scsiinfo</span> . Recent changes to SCSI
+standards (e.g. extra and extended mode pages) are reflected in <span
+ style="font-weight: bold;">sginfo</span> . In a similar way the <span
+ style="font-weight: bold;">sg_format</span> utility can be thought of
+as a modern replacement for the <span style="font-weight: bold;">scsiformat</span>
+utility. <span style="font-weight: bold;">sdparm</span> can do most of
+the things that scsiinfo and sginfo can.<br>
+<h2><a class="mozTocH3" name="mozTocId108175"></a> scsimap</h2>
+Steve Cameron &lt;smcameron@yahoo.com&gt; has the following description
+at his site:
+<p>This is a utility to create and maintain symbolic links mapping a
+predictable set of names to the rather unpredictable names used by
+linux for disk devices. For example, you might map: <br>
+<tt>/dev/mydisk1 -&gt; /dev/sda1</tt> <br>
+<tt>/dev/mydisk2 -&gt; /dev/sdb1</tt> <br>
+<tt>/dev/mydisk3 -&gt; /dev/sdc1</tt> <br>
+If you removed the disk corresponding to /dev/sdb1, then on reboot,
+/dev/sdc1 will become /dev/sdb1, and /dev/sdc1 will be gone. and your
+fstab will be wrong, etc. (Especially problematic on a SAN). <b>scsimap</b>
+will maintain the mapping so that after the reboot, /dev/mydisk3 -&gt;
+will point to /dev/sdb1 and /dev/mydisk2 will be gone. scsimap also
+handles later generation Compaq array controllers (those which use
+the cciss driver.) </p>
+<p>See: <a href="http://www.geocities.com/smcameron">www.geocities.com/smcameron</a>
+.<br>
+</p>
+<h2><a class="mozTocH3" name="mozTocId974127"></a>scsirastools</h2>
+"This project includes changes that enhance the Reliability,
+Availability and Serviceability (RAS) of the drivers that are
+commonly used in a Linux software RAID-1 configuration. Other
+efforts have been made to enable various common hardware RAID
+adapters and their drivers on Linux." See <a
+ href="http://scsirastools.sourceforge.net/">http://scsirastools.sourceforge.net</a>
+. The package contains some low level scsi utilities including <b>sgdskfl</b>
+to load disk firmware, <b>sgmode</b> to get and set mode pages, <b>sgdefects</b>
+to read primary and grown defect lists and <b>sgdiag</b> to perform
+format and other test functions.
+<h2><a class="mozTocH3" name="mozTocId348388"></a> scu</h2>
+The SCSI Command Utility (SCU) implements various SCSI commands
+necessary for normal maintenance and diagnostics of SCSI
+peripherals. Some of its features include: formatting, scanning for
+(and reassigning) bad blocks, downloading new firmware, executing
+diagnostics and obtaining performance information. It is available on
+several Unix platforms (and NT), however it is only currently available
+in binary form. See&nbsp;<a class="moz-txt-link-freetext"
+ href="http://www.scsifaq.org/RMiller_Tools/index.html"><span
+ style="font-family: monospace;"> </span>www.scsifaq.org/RMiller_Tools/index.html</a>
+for more details. scu is written by Robin
+T. Miller &lt;Robin.Miller at netapp dot com&gt;
+<h2><a class="mozTocH2" name="mozTocId382975"></a>sdparm</h2>
+This utility allows information from SCSI mode pages to be fetched and
+potentially modified. <a href="sdparm.html">sdparm</a> also decodes
+Vital Product Data pages. It was written for the Linux 2.4 and 2.6
+series and has been ported to FreeBSD and Tru64.<br>
+<h2><a class="mozTocH3" name="mozTocId783871"></a>SeaTools</h2>
+SeaTools is a freely available (binary, not source) utility for disk
+diagnostics from Seagate which is a disk manufacturer. It can be found
+at <a href="http://www.seagate.com/">http://www.seagate.com</a> under
+the support tab. They have both a command line and a graphical utility.
+&nbsp;Some of the facilities will work on any SCSI disks while others
+are Seagate specific. Self tests, mode page settings and formats (to
+different block sizes) are amongst the facilities available. Other disk
+vendors have similar tools.<br>
+<h2><a class="mozTocH2" name="mozTocId847247"></a>sg3_utils</h2>
+This is a package of utilities that send SCSI commands and decodes the
+response. It also includes slightly higher level utilities such as
+sg_dd which permits a finer level of control over SCSI devices involved
+in copies compared to the standard Unix dd command. <a
+ href="sg3_utils.html">sg3_utils</a> is written for the Linux 2.4 and
+2.6
+series and a large subset of its utilities have been ported to FreeBSD
+and Tru64 .<br>
+<h2><a class="mozTocH2" name="mozTocId705237"></a>sg_utils</h2>
+This package is the precursor of sg3_utils. <a href="uu_index.html">sg_utils</a>
+was written for the Linux 2.2 series with some support for the linux
+2.0 series.<br>
+<br>
+<h2><a class="mozTocH3" name="mozTocId535090"></a>smartmontools</h2>
+This project has taken over from the aforementioned
+smartsuite which is currently not actively maintained. See <a
+ href="http://smartmontools.sourceforge.net/">http://smartmontools.sourceforge.net</a>
+. The author is a maintainer of the SCSI component of this project.
+[The lead maintainer is Bruce Allen.]<br>
+<p> </p>
+<h2><a class="mozTocH3" name="mozTocId143073"></a> smartsuite</h2>
+This is a package that supports S.M.A.R.T. capabilities built into
+modern IDE and SCSI-3 disks. Self-Monitoring, Analysis and Reporting
+Technology (S.M.A.R.T.) is described at <a
+ href="http://www.pc.ibm.com/us/infobrf/ibsmart.html">www.pc.ibm.com/us/infobrf/ibsmart.html</a>.
+For smartsuite see <a href="http://sourceforge.net/projects/smartsuite">sourceforge.net/projects/smartsuite</a>
+.<br>
+<h2><a class="mozTocH2" name="mozTocId53551"></a>smp_utils</h2>
+This is a package of utilities that sends Serial Attached SCSI (SAS)
+Management Protocol (SMP) requests to a device (typically a SAS
+expander) and decodes the response. See the <a href="smp_utils.html">smp_utils</a>
+page.<br>
+<h2><a class="mozTocH2" name="mozTocId440267"></a>spew<span
+ style="font-weight: bold;"></span></h2>
+This utility is used to measure I/O performance of character devices,
+block devices, and regular files. It can also be used to generate high
+I/O loads to stress systems while verifying data integrity. It is easy
+to use and is flexible. No configuration files or complicated
+client/server configurations are needed. Spew also generates its own
+data patterns that are designed to make it easy to find and debug data
+integrity problems. See <a href="http://spew.berlios.de">spew.berlios.de</a>
+.
+<h2><a class="mozTocH2" name="mozTocId708282"></a>udev</h2>
+<a href="http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html">udev</a>
+provides a dynamic device directory containing only the files for
+actually present devices in linux 2.6 series kernels. It creates or
+removes device node files usually located in the /dev directory,
+or it renames network interfaces.<br>
+<h2><a class="mozTocH2" name="mozTocId728109"></a>Conclusion</h2>
+If readers have corrections or suggested additions to this list of
+tools, please contact the author.<br>
+<br>
+<a href="http://oss.metaparadigm.com/safte-monitor"></a><br>
+<p>Return to <a href="index.html">main</a> page.<br>
+</p>
+<div style="text-align: center;"><a
+ href="mailto:dgilbert%20at%20interlog%20dot%20com">Douglas
+Gilbert</a> can
+be emailed at this address (also at dougg at torque dot net). <br>
+</div>
+<center>
+<p>Last updated: 11th June 2006<br>
+<br>
+</p>
+</center>
+</body>
+</html>
diff --git a/examples/Makefile b/examples/Makefile
index bf1434f4..4a71efb0 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -8,7 +8,8 @@ CC = gcc
LD = gcc
EXECS = sg_simple1 sg_simple2 sg_simple3 sg_simple4 sg_simple16 \
- sg_iovec_tst scsi_inquiry sg_excl sg_sense_test sg_simple5
+ sg_iovec_tst scsi_inquiry sg_excl sg_sense_test sg_simple5 \
+ sg_sat_identify sg_sat_chk_power sg_sat_smart_rd_data
# EXECS = sg_simple1 sg_simple2 sg_simple3 sg_simple4 sg_simple16 \
# sg_simple_aio sg_iovec_tst scsi_inquiry sg_excl
@@ -66,6 +67,15 @@ sg_sense_test: sg_sense_test.o ../sg_lib.o ../sg_io_linux.o
sg_simple5: sg_simple5.o ../sg_lib.o ../sg_pt_linux.o
$(LD) -o $@ $(LDFLAGS) $^
+sg_sat_identify: sg_sat_identify.o ../sg_lib.o ../sg_io_linux.o
+ $(LD) -o $@ $(LDFLAGS) $^
+
+sg_sat_chk_power: sg_sat_chk_power.o ../sg_lib.o ../sg_io_linux.o
+ $(LD) -o $@ $(LDFLAGS) $^
+
+sg_sat_smart_rd_data: sg_sat_smart_rd_data.o ../sg_lib.o ../sg_io_linux.o
+ $(LD) -o $@ $(LDFLAGS) $^
+
install: $(EXECS)
install -d $(INSTDIR)
for name in $^; \
diff --git a/examples/sg_iovec_tst.c b/examples/sg_iovec_tst.c
index f4f62382..8732f1bc 100644
--- a/examples/sg_iovec_tst.c
+++ b/examples/sg_iovec_tst.c
@@ -24,7 +24,7 @@
normal file. The purpose is to test the sg_iovec mechanism within the
sg_io_hdr structure.
- Version 0.10 (20030529)
+ Version 0.11 (20060622)
*/
@@ -91,8 +91,8 @@ int sg_read(int sg_fd, unsigned char * buff, int num_blocks, int from_block,
fprintf(stderr, "Recovered error while reading block=%d, num=%d\n",
from_block, num_blocks);
break;
- case SG_LIB_CAT_MEDIA_CHANGED:
- fprintf(stderr, "Media changed\n");
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ fprintf(stderr, "Unit attention\n");
return -1;
default:
sg_chk_n_print3("reading", &io_hdr, 1);
diff --git a/examples/sg_sat_chk_power.c b/examples/sg_sat_chk_power.c
new file mode 100644
index 00000000..aefbdab9
--- /dev/null
+++ b/examples/sg_sat_chk_power.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2006 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 <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+
+/* This program performs a ATA PASS THROUGH (16) SCSI command in order
+ to perform an ATA CHECK POWER MODE command. See http://www.t10.org
+ SAT draft at time of writing: sat-r08.pdf
+
+ Invocation: sg_sat_chk_power [-v] [-V] <device>
+
+*/
+
+#define SAT_ATA_PASS_THROUGH16 0x85
+#define SAT_ATA_PASS_THROUGH16_LEN 16
+#define SAT_STATUS_RETURN_DESC 9 /* ATA status return (sense) descriptor */
+
+#define ATA_CHECK_POWER_MODE 0xe5
+
+#define EBUFF_SZ 256
+
+static char * version_str = "1.02 20060629";
+
+int main(int argc, char * argv[])
+{
+ int sg_fd, k;
+ unsigned char aptCmdBlk[SAT_ATA_PASS_THROUGH16_LEN] =
+ {SAT_ATA_PASS_THROUGH16, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0};
+ sg_io_hdr_t io_hdr;
+ char * file_name = 0;
+ char ebuff[EBUFF_SZ];
+ unsigned char sense_buffer[64];
+ int verbose = 0;
+ int extend = 0;
+ int chk_cond = 1; /* set to 1 to read register(s) back */
+ int protocol = 3; /* non-dat data-in */
+ int t_dir = 1; /* 0 -> to device, 1 -> from device */
+ int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */
+ int t_length = 0; /* 0 -> no data transferred, 2 -> sector count */
+ const unsigned char * ucp = NULL;
+
+ for (k = 1; k < argc; ++k) {
+ if (0 == strcmp(argv[k], "-v"))
+ ++verbose;
+ else if (0 == strcmp(argv[k], "-vv"))
+ verbose += 2;
+ else if (0 == strcmp(argv[k], "-V")) {
+ fprintf(stderr, "version: %s\n", version_str);
+ exit(0);
+ } else if (*argv[k] == '-') {
+ printf("Unrecognized switch: %s\n", argv[k]);
+ file_name = 0;
+ break;
+ }
+ else if (0 == file_name)
+ file_name = argv[k];
+ else {
+ printf("too many arguments\n");
+ file_name = 0;
+ break;
+ }
+ }
+ if (0 == file_name) {
+ printf("Usage: 'sg_sat_chk_power [-v] [-V] <device>'\n");
+ return 1;
+ }
+
+ if ((sg_fd = open(file_name, O_RDWR)) < 0) {
+ snprintf(ebuff, EBUFF_SZ,
+ "sg_sat_chk_power: error opening file: %s", file_name);
+ perror(ebuff);
+ return 1;
+ }
+
+ /* Prepare ATA PASS THROUGH COMMAND (16) command */
+ aptCmdBlk[14] = ATA_CHECK_POWER_MODE;
+ aptCmdBlk[1] = (protocol << 1) | extend;
+ aptCmdBlk[2] = (chk_cond << 5) | (t_dir << 3) |
+ (byte_block << 2) | t_length;
+ if (verbose) {
+ fprintf(stderr, " ata pass through(16) cdb: ");
+ for (k = 0; k < SAT_ATA_PASS_THROUGH16_LEN; ++k)
+ fprintf(stderr, "%02x ", aptCmdBlk[k]);
+ fprintf(stderr, "\n");
+ }
+
+ memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+ io_hdr.interface_id = 'S';
+ io_hdr.cmd_len = sizeof(aptCmdBlk);
+ /* io_hdr.iovec_count = 0; */ /* memset takes care of this */
+ io_hdr.mx_sb_len = sizeof(sense_buffer);
+ io_hdr.dxfer_direction = SG_DXFER_NONE;
+ io_hdr.dxfer_len = 0;
+ io_hdr.dxferp = NULL;
+ io_hdr.cmdp = aptCmdBlk;
+ io_hdr.sbp = sense_buffer;
+ io_hdr.timeout = 20000; /* 20000 millisecs == 20 seconds */
+ /* io_hdr.flags = 0; */ /* take defaults: indirect IO, etc */
+ /* io_hdr.pack_id = 0; */
+ /* io_hdr.usr_ptr = NULL; */
+
+ if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+ perror("sg_sat_chk_power: SG_IO ioctl error");
+ close(sg_fd);
+ return 1;
+ }
+
+ /* error processing: N.B. expect check condition, no sense ... !! */
+ switch (sg_err_category3(&io_hdr)) {
+ case SG_LIB_CAT_CLEAN:
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ case SG_LIB_CAT_NO_SENSE:
+ /* XXX: Until the spec decides which one to go with. 20060607 */
+ ucp = sg_scsi_sense_desc_find(sense_buffer, sizeof(sense_buffer),
+ SAT_STATUS_RETURN_DESC);
+ if (NULL == ucp)
+ sg_chk_n_print3("ATA_16 command error", &io_hdr, 1);
+ else if (verbose)
+ sg_chk_n_print3("status return descriptor, as expected",
+ &io_hdr, 1);
+ if (ucp && ucp[3]) {
+ if (ucp[3] & 0x4)
+ printf("error in returned FIS: aborted command\n");
+ else
+ printf("error=0x%x, status=0x%x\n", ucp[3], ucp[13]);
+ }
+ break;
+ default:
+ fprintf(stderr, "unexpected SCSI sense category\n");
+ ucp = sg_scsi_sense_desc_find(sense_buffer, sizeof(sense_buffer),
+ SAT_STATUS_RETURN_DESC);
+ if (NULL == ucp)
+ sg_chk_n_print3("ATA_16 command error", &io_hdr, 1);
+ else if (verbose)
+ sg_chk_n_print3("status return descriptor, as expected",
+ &io_hdr, 1);
+ if (ucp && ucp[3]) {
+ if (ucp[3] & 0x4)
+ printf("error in returned FIS: aborted command\n");
+ else
+ printf("error=0x%x, status=0x%x\n", ucp[3], ucp[13]);
+ }
+ break;
+ }
+
+ if (ucp) {
+ switch (ucp[5]) { /* sector_count (7:0) */
+ case 0xff:
+ printf("In active mode or idle mode\n");
+ break;
+ case 0x80:
+ printf("In idle mode\n");
+ break;
+ case 0x41:
+ printf("In NV power mode and spindle is spun or spinning up\n");
+ break;
+ case 0x40:
+ printf("In NV power mode and spindle is spun or spinning down\n");
+ break;
+ case 0x0:
+ printf("In standby mode\n");
+ break;
+ default:
+ printf("unknown power mode (sector count) value=0x%x\n", ucp[5]);
+ break;
+ }
+ } else
+ fprintf(stderr, "Expecting a special error return and didn't "
+ "receive it\n");
+
+ close(sg_fd);
+ return 0;
+}
diff --git a/examples/sg_sat_identify.c b/examples/sg_sat_identify.c
new file mode 100644
index 00000000..c3fa5528
--- /dev/null
+++ b/examples/sg_sat_identify.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2006 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 <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+
+/* This program uses a ATA PASS THROUGH (16) SCSI command to package
+ an ATA IDENTIFY DEVICE (A1h) command. If the '-p' option is given,
+ it will package an ATA IDENTIFY PACKET DEVICE (Ech) instead (for
+ ATAPI device like cd/dvd drives) See http://www.t10.org
+ SAT draft at time of writing: sat-r08a.pdf
+
+ Invocation: sg_sat_identify [-p] [-v] [-V] <device>
+
+ With SAT, the user can find out whether a device is an ATA disk or
+ an ATAPI device. The ATA Information VPD page contains a "command
+ code" field in byte 56. Its values are either ECh for a (s/p)ATA
+ disk, A1h for a (s/p)ATAPI device, or 0 for unknown.
+
+*/
+
+#define SAT_ATA_PASS_THROUGH16 0x85
+#define SAT_ATA_PASS_THROUGH16_LEN 16
+#define SAT_STATUS_RETURN_DESC 9 /* ATA status return (sense) descriptor */
+
+#define ATA_IDENTIFY_DEVICE 0xec
+#define ATA_IDENTIFY_PACKET_DEVICE 0xa1
+#define ID_RESPONSE_LEN 512
+
+#define EBUFF_SZ 256
+
+static char * version_str = "1.01 20060629";
+
+static void usage()
+{
+ fprintf(stderr, "Usage: "
+ "sg_sat_identify [-p] [-v] [-V] <device>\n"
+ " where: -p do IDENTIFY PACKET DEVICE (def: IDENTIFY "
+ "DEVICE) command\n"
+ " -v increase verbosity\n"
+ " -V print version string and exit\n\n"
+ "Performs a IDENTIFY (PACKET) DEVICE ATA command via a SAT "
+ "pass through\n");
+}
+
+int main(int argc, char * argv[])
+{
+ int sg_fd, k, ok;
+ unsigned char aptCmdBlk[SAT_ATA_PASS_THROUGH16_LEN] =
+ {SAT_ATA_PASS_THROUGH16, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0};
+ sg_io_hdr_t io_hdr;
+ char * file_name = 0;
+ char ebuff[EBUFF_SZ];
+ unsigned char inBuff[ID_RESPONSE_LEN];
+ unsigned char sense_buffer[32];
+ int do_packet = 0;
+ int verbose = 0;
+ int extend = 0;
+ int chk_cond = 0; /* set to 1 to read register(s) back */
+ int protocol = 4; /* PIO data-in */
+ int t_dir = 1; /* 0 -> to device, 1 -> from device */
+ int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */
+ int t_length = 2; /* 0 -> no data transferred, 2 -> sector count */
+ const unsigned char * cucp;
+
+ memset(inBuff, 0, sizeof(inBuff));
+ for (k = 1; k < argc; ++k) {
+ if (0 == strcmp(argv[k], "-p"))
+ ++do_packet;
+ else if (0 == strcmp(argv[k], "-v"))
+ ++verbose;
+ else if (0 == strcmp(argv[k], "-vv"))
+ verbose += 2;
+ else if (0 == strcmp(argv[k], "-vvv"))
+ verbose += 3;
+ else if (0 == strcmp(argv[k], "-V")) {
+ fprintf(stderr, "version: %s\n", version_str);
+ exit(0);
+ } else if (*argv[k] == '-') {
+ printf("Unrecognized switch: %s\n", argv[k]);
+ file_name = 0;
+ break;
+ }
+ else if (0 == file_name)
+ file_name = argv[k];
+ else {
+ printf("too many arguments\n");
+ file_name = 0;
+ break;
+ }
+ }
+ if (0 == file_name) {
+ usage();
+ return 1;
+ }
+
+ if ((sg_fd = open(file_name, O_RDWR)) < 0) {
+ snprintf(ebuff, EBUFF_SZ,
+ "sg_sat_identify: error opening file: %s", file_name);
+ perror(ebuff);
+ return 1;
+ }
+
+ /* Prepare ATA PASS THROUGH COMMAND (16) command */
+ aptCmdBlk[6] = 1; /* sector count */
+ aptCmdBlk[14] = (do_packet ? ATA_IDENTIFY_PACKET_DEVICE :
+ ATA_IDENTIFY_DEVICE);
+ aptCmdBlk[1] = (protocol << 1) | extend;
+ aptCmdBlk[2] = (chk_cond << 5) | (t_dir << 3) |
+ (byte_block << 2) | t_length;
+ if (verbose) {
+ fprintf(stderr, " ata pass through(16) cdb: ");
+ for (k = 0; k < SAT_ATA_PASS_THROUGH16_LEN; ++k)
+ fprintf(stderr, "%02x ", aptCmdBlk[k]);
+ fprintf(stderr, "\n");
+ }
+
+ memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+ io_hdr.interface_id = 'S';
+ io_hdr.cmd_len = sizeof(aptCmdBlk);
+ /* io_hdr.iovec_count = 0; */ /* memset takes care of this */
+ io_hdr.mx_sb_len = sizeof(sense_buffer);
+ io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+ io_hdr.dxfer_len = ID_RESPONSE_LEN;
+ io_hdr.dxferp = inBuff;
+ io_hdr.cmdp = aptCmdBlk;
+ io_hdr.sbp = sense_buffer;
+ io_hdr.timeout = 20000; /* 20000 millisecs == 20 seconds */
+ /* io_hdr.flags = 0; */ /* take defaults: indirect IO, etc */
+ /* io_hdr.pack_id = 0; */
+ /* io_hdr.usr_ptr = NULL; */
+
+ if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+ perror("sg_sat_identify: SG_IO ioctl error");
+ close(sg_fd);
+ return 1;
+ }
+
+ /* now for the error processing */
+ ok = 0;
+ switch (sg_err_category3(&io_hdr)) {
+ case SG_LIB_CAT_CLEAN:
+ ok = 1;
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ /* check for ATA status return descriptor */
+ if (verbose)
+ sg_chk_n_print3(">>> ATA_16 command", &io_hdr, 1);
+ cucp = sg_scsi_sense_desc_find(io_hdr.sbp, io_hdr.sb_len_wr,
+ SAT_STATUS_RETURN_DESC);
+ if (cucp && (cucp[3])) {
+ if (cucp[3] & 0x4) {
+ printf("error in returned FIS: aborted command\n");
+ printf(" try again with%s '-p' option\n",
+ (do_packet ? "out" : ""));
+ break;
+ }
+ }
+ ok = 1; /* not sure what is happening so output response */
+ if (0 == verbose) {
+ printf(">>> Recovered error on ATA_16, may have failed\n");
+ printf(" Add '-v' for more information\n");
+ }
+ break;
+ default: /* won't bother decoding other categories */
+ sg_chk_n_print3("ATA_16 command error", &io_hdr, 1);
+ break;
+ }
+
+ if (ok) { /* output result if it is available */
+ printf("Response for IDENTIFY %sDEVICE ATA command:\n",
+ (do_packet ? "PACKET " : ""));
+ dWordHex((const unsigned short *)inBuff, 256, 0,
+ sg_is_big_endian());
+ }
+
+ close(sg_fd);
+ return 0;
+}
diff --git a/examples/sg_sat_smart_rd_data.c b/examples/sg_sat_smart_rd_data.c
new file mode 100644
index 00000000..5ebf1212
--- /dev/null
+++ b/examples/sg_sat_smart_rd_data.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2006 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 <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+
+/* This program performs a ATA PASS THROUGH (16) SCSI command in order
+ to perform an ATA SMART/READ DATA command. See http://www.t10.org
+ SAT draft at time of writing: sat-r08.pdf
+
+ Invocation: sg_sat_smart_rd_data [-v] [-V] <device>
+
+*/
+
+#define SAT_ATA_PASS_THROUGH16 0x85
+#define SAT_ATA_PASS_THROUGH16_LEN 16
+#define SAT_STATUS_RETURN_DESC 9 /* ATA status return (sense) descriptor */
+
+#define ATA_SMART 0xb0
+#define ATA_SMART_READ_DATA 0xd0
+#define SMART_READ_DATA_RESPONSE_LEN 512
+
+#define EBUFF_SZ 256
+
+static char * version_str = "1.01 20060629";
+
+int main(int argc, char * argv[])
+{
+ int sg_fd, k, ok;
+ unsigned char aptCmdBlk[SAT_ATA_PASS_THROUGH16_LEN] =
+ {SAT_ATA_PASS_THROUGH16, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0};
+ sg_io_hdr_t io_hdr;
+ char * file_name = 0;
+ char ebuff[EBUFF_SZ];
+ unsigned char inBuff[SMART_READ_DATA_RESPONSE_LEN];
+ unsigned char sense_buffer[32];
+ int verbose = 0;
+ int extend = 0;
+ int chk_cond = 0; /* set to 1 to read register(s) back */
+ int protocol = 4; /* PIO data-in */
+ int t_dir = 1; /* 0 -> to device, 1 -> from device */
+ int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */
+ int t_length = 2; /* 0 -> no data transferred, 2 -> sector count */
+ const unsigned char * ucp = NULL;
+
+ for (k = 1; k < argc; ++k) {
+ if (0 == strcmp(argv[k], "-v"))
+ ++verbose;
+ else if (0 == strcmp(argv[k], "-vv"))
+ verbose += 2;
+ else if (0 == strcmp(argv[k], "-V")) {
+ fprintf(stderr, "version: %s\n", version_str);
+ exit(0);
+ } else if (*argv[k] == '-') {
+ printf("Unrecognized switch: %s\n", argv[k]);
+ file_name = 0;
+ break;
+ }
+ else if (0 == file_name)
+ file_name = argv[k];
+ else {
+ printf("too many arguments\n");
+ file_name = 0;
+ break;
+ }
+ }
+ if (0 == file_name) {
+ printf("Usage: 'sg_sat_smart_rd_data [-v] [-V] <device>'\n");
+ return 1;
+ }
+
+ if ((sg_fd = open(file_name, O_RDWR)) < 0) {
+ snprintf(ebuff, EBUFF_SZ,
+ "sg_sat_smart_rd_data: error opening file: %s", file_name);
+ perror(ebuff);
+ return 1;
+ }
+
+ /* Prepare ATA PASS THROUGH COMMAND (16) command */
+ aptCmdBlk[4] = ATA_SMART_READ_DATA; /* feature (7:0) */
+ aptCmdBlk[6] = 1; /* number of block (sector count) */
+ aptCmdBlk[10] = 0x4f; /* lba_mid (7:0) */
+ aptCmdBlk[12] = 0xc2; /* lba_high (7:0) */
+ aptCmdBlk[14] = ATA_SMART;
+ aptCmdBlk[1] = (protocol << 1) | extend;
+ aptCmdBlk[2] = (chk_cond << 5) | (t_dir << 3) |
+ (byte_block << 2) | t_length;
+ if (verbose) {
+ fprintf(stderr, " ata pass through(16) cdb: ");
+ for (k = 0; k < SAT_ATA_PASS_THROUGH16_LEN; ++k)
+ fprintf(stderr, "%02x ", aptCmdBlk[k]);
+ fprintf(stderr, "\n");
+ }
+
+ memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+ io_hdr.interface_id = 'S';
+ io_hdr.cmd_len = sizeof(aptCmdBlk);
+ /* io_hdr.iovec_count = 0; */ /* memset takes care of this */
+ io_hdr.mx_sb_len = sizeof(sense_buffer);
+ io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+ io_hdr.dxfer_len = SMART_READ_DATA_RESPONSE_LEN;
+ io_hdr.dxferp = inBuff;
+ io_hdr.cmdp = aptCmdBlk;
+ io_hdr.sbp = sense_buffer;
+ io_hdr.timeout = 20000; /* 20000 millisecs == 20 seconds */
+ /* io_hdr.flags = 0; */ /* take defaults: indirect IO, etc */
+ /* io_hdr.pack_id = 0; */
+ /* io_hdr.usr_ptr = NULL; */
+
+ if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+ perror("sg_sat_smart_rd_data: SG_IO ioctl error");
+ close(sg_fd);
+ return 1;
+ }
+
+ /* now for the error processing */
+ ok = 0;
+ switch (sg_err_category3(&io_hdr)) {
+ case SG_LIB_CAT_CLEAN:
+ ok = 1;
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ ucp = sg_scsi_sense_desc_find(sense_buffer, sizeof(sense_buffer),
+ SAT_STATUS_RETURN_DESC);
+ if (NULL == ucp)
+ sg_chk_n_print3("ATA_16 command error", &io_hdr, 1);
+ else if (verbose)
+ sg_chk_n_print3("ATA status return descriptor",
+ &io_hdr, 1);
+ if (ucp && ucp[3])
+ printf("error=0x%x, status=0x%x\n", ucp[3], ucp[13]);
+ else
+ ok = 1;
+ break;
+ default: /* won't bother decoding other categories */
+ sg_chk_n_print3("ATA_16 command error", &io_hdr, 1);
+ break;
+ }
+
+ if (ok) { /* output result if it is available */
+ printf("Response:\n");
+ dWordHex((const unsigned short *)inBuff, 256, 0,
+ sg_is_big_endian());
+ }
+
+ close(sg_fd);
+ return 0;
+}
diff --git a/examples/sg_sense_test.c b/examples/sg_sense_test.c
index 5cef47b6..d91157cf 100644
--- a/examples/sg_sense_test.c
+++ b/examples/sg_sense_test.c
@@ -12,7 +12,7 @@
/* This is a simple program that tests the sense data descriptor format
printout function in sg_lib.c
-* Copyright (C) 2004-2005 D. Gilbert
+* Copyright (C) 2004-2006 D. Gilbert
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
@@ -24,7 +24,7 @@
#define ME "sg_sense_test: "
-int main(int argc, char * argv[])
+int main(/* int argc, char * argv[] */)
{
unsigned char err1[] = {0x72, 0x5, 0x4, 0x1, 0, 0, 0, 32,
0x2, 0x6, 0, 0, 0xc8, 0x0, 0x3, 0,
@@ -49,8 +49,8 @@ int main(int argc, char * argv[])
0x33, 0x44, 0xa,
0x0, 0x0, 0, 0, 0x4, 0x1, 0, 0xcf, 0, 5,};
unsigned char err6[] = {0x72, SPC_SK_NO_SENSE, 0x4, 0x1, 0, 0, 0, 14,
- 0x9, 0xc, 1, 0, 0x11, 0x22, 0x33, 0x44,
- 0x55, 0x66, 0x77, 0x88, 0x1, 0x2};
+ 0x9, 0xc, 1, 0, 0x11, 0x22, 0x66, 0x33,
+ 0x77, 0x44, 0x88, 0x55, 0x1, 0x2};
unsigned char err7[] = {0xf1, 0, 0xe5, 0x11, 0x22, 0x33, 0x44, 0xa,
0x0, 0x0, 0x0, 0x0, 0x24, 0x1, 0xbb,
0xc9, 0x0, 0x2};
diff --git a/no_lib/Makefile.freebsd b/no_lib/Makefile.freebsd
index cc47e612..b51851ce 100644
--- a/no_lib/Makefile.freebsd
+++ b/no_lib/Makefile.freebsd
@@ -11,13 +11,14 @@ EXECS = sg_readcap sg_turs sg_inq sg_start sg_modes sg_logs sg_senddiag \
sg_persist sg_requests sg_ses sg_luns \
sg_sync sg_prevent sg_get_config sg_wr_mode \
sg_rtpg sg_reassign sg_format sg_rmsn sg_ident \
- sg_read_long sg_write_long sg_verify
+ sg_read_long sg_write_long sg_verify sg_rdac sg_vpd
MAN_PGS = sg_readcap.8 sg_turs.8 sg_inq.8 sg_start.8 sg_modes.8 sg_logs.8 \
sg_senddiag.8 sg_persist.8 sg_requests.8 sg_ses.8 sg_luns.8 \
sg_sync.8 sg_prevent.8 sg_get_config.8 sg_wr_mode.8 \
sg_rtpg.8 sg_reassign.8 sg_format.8 sg_rmsn.8 sg_ident.8 \
- sg_read_long.8 sg_write_long.8 sg_verify.8
+ sg_read_long.8 sg_write_long.8 sg_verify.8 sg_rdac.8 sg_vpd.8 \
+ sg3_utils.8
MAN_PREF = man8
OS_FLAGS = -DSG3_UTILS_FREEBSD
@@ -112,6 +113,12 @@ sg_write_long: sg_write_long.o $(O_FILES)
sg_verify: sg_verify.o $(O_FILES)
$(LD) -o $@ $(LDFLAGS) $@.o $(O_FILES)
+sg_rdac: sg_rdac.o $(O_FILES)
+ $(LD) -o $@ $(LDFLAGS) $@.o $(O_FILES)
+
+sg_vpd: sg_vpd.o sg_vpd_vendor.o $(O_FILES)
+ $(LD) -o $@ $(LDFLAGS) $@.o $(O_FILES)
+
install: $(EXECS)
install -d $(INSTDIR)
for name in $(EXECS); \
diff --git a/no_lib/Makefile.linux b/no_lib/Makefile.linux
index 1263e041..2dfb1ec1 100644
--- a/no_lib/Makefile.linux
+++ b/no_lib/Makefile.linux
@@ -15,7 +15,7 @@ EXECS = sg_dd sgp_dd sgm_dd sg_read sg_map sg_scan sg_rbuf \
sg_persist sg_write_long sg_read_long sg_requests sg_ses \
sg_verify sg_emc_trespass sg_luns sg_sync sg_prevent \
sg_get_config sg_wr_mode sg_rtpg sg_reassign sg_format \
- sg_rmsn sg_ident sg_map26
+ sg_rmsn sg_ident sg_map26 sg_rdac sg_vpd
MAN_PGS = sg_dd.8 sgp_dd.8 sgm_dd.8 sg_read.8 sg_map.8 sg_scan.8 sg_rbuf.8 \
sginfo.8 sg_readcap.8 sg_turs.8 sg_inq.8 sg_test_rwbuf.8 \
@@ -24,7 +24,7 @@ MAN_PGS = sg_dd.8 sgp_dd.8 sgm_dd.8 sg_read.8 sg_map.8 sg_scan.8 sg_rbuf.8 \
sg_requests.8 sg_ses.8 sg_verify.8 sg_emc_trespass.8 \
sg_luns.8 sg_sync.8 sg_prevent.8 sg_get_config.8 sg_wr_mode.8 \
sg_rtpg.8 sg_reassign.8 sg_format.8 sg_rmsn.8 sg_ident.8 \
- sg_map26.8
+ sg_map26.8 sg_rdac.8 sg_vpd.8 sg3_utils.8
MAN_PREF = man8
OS_FLAGS = -DSG3_UTILS_LINUX
@@ -32,9 +32,9 @@ LARGE_FILE_FLAGS = -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
EXTRA_FLAGS = $(OS_FLAGS) $(LARGE_FILE_FLAGS)
# CFLAGS = -O2 -Wall -W $(EXTRA_FLAGS)
-CFLAGS = -g -O2 -Wall -W $(EXTRA_FLAGS)
+# CFLAGS = -g -O2 -Wall -W $(EXTRA_FLAGS)
# CFLAGS = -g -O2 -W -DSG_KERNEL_INCLUDES $(EXTRA_FLAGS)
-# CFLAGS = -g -O2 -Wall -W -pedantic -std=c99 $(EXTRA_FLAGS)
+CFLAGS = -g -O2 -Wall -W -pedantic -std=c99 $(EXTRA_FLAGS)
CFLAGS_PTHREADS = -D_REENTRANT
@@ -162,6 +162,12 @@ sg_ident: sg_ident.o sg_lib.o sg_cmds.o sg_pt_linux.o
sg_map26: sg_map26.o
$(LD) -o $@ $(LDFLAGS) $^
+sg_rdac: sg_rdac.o sg_lib.o sg_cmds.o sg_pt_linux.o
+ $(LD) -o $@ $(LDFLAGS) $^
+
+sg_vpd: sg_vpd.o sg_vpd_vendor.o sg_lib.o sg_cmds.o sg_pt_linux.o
+ $(LD) -o $@ $(LDFLAGS) $^
+
install: $(EXECS)
install -d $(INSTDIR)
for name in $^; \
diff --git a/no_lib/Makefile.linux_static b/no_lib/Makefile.linux_static
index d244bc3c..16166399 100644
--- a/no_lib/Makefile.linux_static
+++ b/no_lib/Makefile.linux_static
@@ -15,7 +15,7 @@ EXECS = sg_dd sgp_dd sgm_dd sg_read sg_map sg_scan sg_rbuf \
sg_persist sg_write_long sg_read_long sg_requests sg_ses \
sg_verify sg_emc_trespass sg_luns sg_sync sg_prevent \
sg_get_config sg_wr_mode sg_rtpg sg_reassign sg_format \
- sg_rmsn sg_ident sg_map26
+ sg_rmsn sg_ident sg_map26 sg_rdac sg_vpd
MAN_PGS = sg_dd.8 sgp_dd.8 sgm_dd.8 sg_read.8 sg_map.8 sg_scan.8 sg_rbuf.8 \
sginfo.8 sg_readcap.8 sg_turs.8 sg_inq.8 sg_test_rwbuf.8 \
@@ -24,7 +24,7 @@ MAN_PGS = sg_dd.8 sgp_dd.8 sgm_dd.8 sg_read.8 sg_map.8 sg_scan.8 sg_rbuf.8 \
sg_requests.8 sg_ses.8 sg_verify.8 sg_emc_trespass.8 \
sg_luns.8 sg_sync.8 sg_prevent.8 sg_get_config.8 sg_wr_mode.8 \
sg_rtpg.8 sg_reassign.8 sg_format.8 sg_rmsn.8 sg_ident.8 \
- sg_map26.8
+ sg_map26.8 sg_rdac.8 sg_vpd.8 sg3_utils.8
MAN_PREF = man8
OS_FLAGS = -DSG3_UTILS_LINUX
@@ -162,6 +162,12 @@ sg_ident: sg_ident.o sg_lib.o sg_cmds.o sg_pt_linux.o
sg_map26: sg_map26.o
$(LD) -o $@ $(LDFLAGS) $^
+sg_rdac: sg_rdac.o sg_lib.o sg_cmds.o sg_pt_linux.o
+ $(LD) -o $@ $(LDFLAGS) $^
+
+sg_vpd: sg_vpd.o sg_vpd_vendor.o sg_lib.o sg_cmds.o sg_pt_linux.o
+ $(LD) -o $@ $(LDFLAGS) $^
+
install: $(EXECS)
install -d $(INSTDIR)
for name in $^; \
diff --git a/no_lib/Makefile.osf1 b/no_lib/Makefile.osf1
index 1de8ed86..0ad35e84 100644
--- a/no_lib/Makefile.osf1
+++ b/no_lib/Makefile.osf1
@@ -11,13 +11,14 @@ EXECS = sg_readcap sg_turs sg_inq sg_start sg_modes sg_logs sg_senddiag \
sg_persist sg_requests sg_ses sg_luns \
sg_sync sg_prevent sg_get_config sg_wr_mode \
sg_rtpg sg_reassign sg_format sg_rmsn sg_ident \
- sg_read_long sg_write_long sg_verify
+ sg_read_long sg_write_long sg_verify sg_rdac sg_vpd
MAN_PGS = sg_readcap.8 sg_turs.8 sg_inq.8 sg_start.8 sg_modes.8 sg_logs.8 \
sg_senddiag.8 sg_persist.8 sg_requests.8 sg_ses.8 sg_luns.8 \
sg_sync.8 sg_prevent.8 sg_get_config.8 sg_wr_mode.8 \
sg_rtpg.8 sg_reassign.8 sg_format.8 sg_rmsn.8 sg_ident.8 \
- sg_read_long.8 sg_write_long.8 sg_verify.8
+ sg_read_long.8 sg_write_long.8 sg_verify.8 sg_rdac.8 sg_vpd.8 \
+ sg3_utils.8
MAN_PREF = man8
@@ -118,6 +119,12 @@ sg_write_long: sg_write_long.o $(O_FILES) $(O_GETOPT_LONG)
sg_verify: sg_verify.o $(O_FILES) $(O_GETOPT_LONG)
$(LD) -o $@ $(LDFLAGS) $@.o $(O_FILES) $(O_GETOPT_LONG)
+sg_rdac: sg_rdac.o $(O_FILES) $(O_GETOPT_LONG)
+ $(LD) -o $@ $(LDFLAGS) $@.o $(O_FILES) $(O_GETOPT_LONG)
+
+sg_vpd: sg_vpd.o sg_vpd_vendor.o $(O_FILES) $(O_GETOPT_LONG)
+ $(LD) -o $@ $(LDFLAGS) $@.o $(O_FILES) $(O_GETOPT_LONG)
+
install: $(EXECS)
test -d $(INSTDIR) || mkdir -p $(INSTDIR)
for name in $(EXECS); \
diff --git a/no_lib/sg3_utils.spec b/no_lib/sg3_utils.spec
index 467e7354..f2477c6d 100644
--- a/no_lib/sg3_utils.spec
+++ b/no_lib/sg3_utils.spec
@@ -1,12 +1,12 @@
Summary: Utilities for SCSI devices in Linux
Name: sg3_utils
-Version: 1.20
+Version: 1.21
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
+Url: http://www.torque.net/sg/sg3_utils.html
Provides: sg_utils
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root/
@@ -84,6 +84,8 @@ rm -rf $RPM_BUILD_ROOT
%attr(755,root,root) %{_bindir}/sg_rmsn
%attr(755,root,root) %{_bindir}/sg_ident
%attr(755,root,root) %{_bindir}/sg_map26
+%attr(755,root,root) %{_bindir}/sg_vpd
+%attr(755,root,root) %{_bindir}/sg_rdac
# 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*
@@ -121,9 +123,16 @@ rm -rf $RPM_BUILD_ROOT
%attr(-,root,root) %doc %{_mandir}/man8/sg_rmsn.8*
%attr(-,root,root) %doc %{_mandir}/man8/sg_ident.8*
%attr(-,root,root) %doc %{_mandir}/man8/sg_map26.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_vpd.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_rdac.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg3_utils.8*
%changelog
+* Thu Jul 06 2006 - dgilbert at interlog dot com
+- add sg_vpd and sg_rdac, uniform exit statuses
+ * sg3_utils-1.21
+
* Tue Apr 18 2006 - dgilbert at interlog dot com
- sg_logs: sas port specific page decoding, sg*_dd updates
* sg3_utils-1.20
diff --git a/sg3_utils.8 b/sg3_utils.8
new file mode 100644
index 00000000..e9a4a2df
--- /dev/null
+++ b/sg3_utils.8
@@ -0,0 +1,160 @@
+.TH SG3_UTILS "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
+.SH NAME
+sg3_utils \- a package of utilities for sending SCSI commands
+.SH SYNOPSIS
+.B sg_*
+[\fI--help\fR] [\fI--verbose\fR] [\fI--version\fR]
+\fI<scsi_device>\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+sg3_utils is a package of utilities that send SCSI commands to
+the given <scsi_device> via a SCSI pass through interface
+provided by the host operating system.
+.PP
+The names of all utilities start with "sg" and most start with "sg_"
+often followed by the abbreviation of the name of the SCSI command
+that they send. For example the "sg_verify" utility sends the SCSI
+VERIFY command. A mapping between SCSI commands and the sg3_utils
+utilities that issue them is given in the COVERAGE file.
+.PP
+SCSI draft standards can be found at http://www.t10.org . The
+standards themselves can be purchased from ANSI and other
+standards organistions. A good overview of various SCSI
+standards can be seen at http://www.t10.org/scsi-3.htm with the
+SCSI command sets in the upper part of the diagram. SCSI commands
+in common with all device types can be found in SPC of which SPC-4
+is the latest major version. Block device specific commands (e.g.
+as used by disks) can be in SBC, those for tape drives in SSC
+and those for CD/DVD drives in MMC.
+.PP
+There are two generations of command line option usage. The older utilities
+such as sg_inq have individual command line processing code (often
+found at the top of the main() function) based on a single "-" followed
+by one or more characters. If an argument is needed then it follows
+a "=" (e.g. '-p=1f' in sg_modes). Various options can be elided as long
+as it is not ambiguous (e.g. '-vv' to increase the verbosity). The
+<scsi_device> is any device that can receive SCSI commands and is shown
+after the options in the synopsis. It may appear between other options.
+.PP
+The second generation of newer utilties uses the getopt_long() function
+to parse command line options. With that function, each option has two
+representations: a short form (e.g. '-v') and a longer
+form (e.g. '--verbose'). If an argument is required then it follows a
+space in the short form and a "=" in the longer form (e.g. in the
+sg_verify utility '-l 2a6h' and '--lba=2a6h' are equivalent). Again
+the <scsi_device> argument may appear between or prior to other options.
+.PP
+Several sg3_utils utilities are based on the Unix dd command (e.g. sg_dd)
+and share its rather quirky command line interface.
+.SH EXIT STATUS
+To aid scripts that call these utilities, the exit status is set to
+indicate success (0) or failure (1 or more). Note that some of the
+lower values correspond to the SCSI sense key values to which they
+correspond. The exit status values are:
+.TP
+.B 0
+success
+.TP
+.B 1
+syntax error. Either illegal coomand line options, options with bad
+arguments or a combination of options that is not permitted.
+.TP
+.B 2
+the <scsi_device> reports that it is not ready for the operation
+requested. The device may be in the process of becoming ready (e.g.
+spinning up but not at speed) so the utility may work after a wait.
+.TP
+.B 3
+the <scsi_device> reports a medium or hardware error (or a blank
+check). For example an attempt to read a corrupted block on a disk
+will yield this value.
+.TP
+.B 5
+the <scsi_device> reports an "illegal request" with an additional
+sense code other than "invalid operation code". This is often a
+supported command with a field set requesting an unsupported
+capability. For commands that require a "service action" field
+this value can indicate that the command is not supported.
+.TP
+.B 6
+the <scsi_device> reports a "unit attention" condition. This usually
+indicates that something unrelated to the requested command has
+occurred (e.g. a device reset) potentially before the current SCSI
+command was sent. The requested command has not been executed by the
+device. Note that unit attention conditions are usually only reported
+once by a device.
+.TP
+.B 9
+the <scsi_device> reports an illegal request with an additional
+sense code of "invalid operation code" which means that it doesn't
+support the requested command.
+.TP
+.B 10
+the <scsi_device> reports it has a check condition but "no sense".
+Some polling commands (e.g. REQUEST SENSE) can react this way.
+It is unlikely that this value will occur as an exit status.
+.TP
+.B 11
+the <scsi_device> reports a "recovered error". The requested command
+was successful. Most likely a utility will report a recovered error
+to stderr and continue, probably leaving the utility with an exit
+status of 0 .
+.TP
+.B 15
+the utility is unable to open, close or use the given <scsi_device>.
+The given file name could be incorrect or there may be permission
+problems. Adding the '-v' option may give more information.
+.TP
+.B 33
+the command sent to <scsi_device> has timed out.
+.TP
+.B 97
+the response to a SCSI command failed sanity checks.
+.TP
+.B 98
+the <scsi_device> reports it has a check condition but the error
+doesn't fit into any of the above categories.
+.TP
+.B 99
+any errors that can't be categorized into values 1 to 98 may yield
+this value. This includes transport and operating system errors
+after the command has been sent to the device.
+.PP
+Most of the error conditions reported above will be repeatable (an
+example of one that is not is "unit attention") so the utility can
+be run again with the '-v' option (or several) to obtain more
+information.
+.SH COMMON OPTIONS
+.TP
+--help | -h | -?
+output the usage message then exit. In a few older utilities the '-h'
+option request hexadecimal output. In these cases the '-?' option will
+output the usage message then exit.
+.TP
+--verbose | -v
+increase the level of verbosity, (i.e. debug output). Such extra output
+is usually sent to stderr.
+.TP
+--version | -V
+print the version string and then exit. Each utility has its own version
+number and date of last code change.
+.PP
+Notice that the '--verbose' option can be used multiple times for more
+verbose output. Obviously the short form ('-vv') is more convenient than
+the longer form ('--verbose --verbose').
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 1999-2006 Douglas Gilbert
+.br
+Some utilities are distributed under a GPL version 2 license while
+others, usually more recent ones, are under a FreeBSD license. The files
+that are common to almost all utilities and thus have the most reusable
+code, namely sg_lib.[hc] and sg_cmds.[hc] are under a FreeBSD license.
+There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sdparm(sdparm)
diff --git a/sg3_utils.spec b/sg3_utils.spec
index 678b4aa0..94215e93 100644
--- a/sg3_utils.spec
+++ b/sg3_utils.spec
@@ -1,21 +1,21 @@
-%define name sg3_utils
-%define version 1.20
-%define release 1
-
-%define major 1
-%define minor 0
-%define libname %{_lib}sgutils-%{major}_%{minor}
-
-Summary: Utilities for SCSI devices in Linux
-Name: %{name}
-Version: %{version}
-Release: %{release}
-License: GPL/FreeBSD
-Group: Utilities/System
-URL: http://www.torque.net/sg/u_index.html
-Source0: http://www.torque.net/sg/p/%{name}-%{version}.tgz
-BuildRoot: %{_tmppath}/%{name}-%{version}-root
-Packager: Douglas Gilbert <dgilbert at interlog dot com>
+%define name sg3_utils
+%define version 1.21
+%define release 1
+
+%define major 1
+%define minor 0
+%define libname %{_lib}sgutils-%{major}_%{minor}
+
+Summary: Utilities for SCSI devices in Linux
+Name: %{name}
+Version: %{version}
+Release: %{release}
+License: GPL/FreeBSD
+Group: Utilities/System
+URL: http://www.torque.net/sg/sg3_utils.html
+Source0: http://www.torque.net/sg/p/%{name}-%{version}.tgz
+BuildRoot: %{_tmppath}/%{name}-%{version}-root
+Packager: Douglas Gilbert <dgilbert at interlog dot com>
%description
Collection of Linux utilities for devices that use the SCSI command set.
@@ -31,22 +31,22 @@ well (e.g. /dev/sda).
Warning: Some of these tools access the internals of your system
and the incorrect usage of them may render your system inoperable.
-%package -n %{libname}
-Summary: Shared library for %{name}
+%package -n %{libname}
+Summary: Shared library for %{name}
Group: System/Libraries
-%description -n %{libname}
+%description -n %{libname}
This package contains the shared library for %{name}.
-%package -n %{libname}-devel
-Summary: Static library and header files for the sgutils library
-Group: Development/C
-Obsoletes: %{name}-devel
-Provides: %{name}-devel
-Provides: libsgutils-devel
-Requires: %{libname} = %{version}-%{release}
+%package -n %{libname}-devel
+Summary: Static library and header files for the sgutils library
+Group: Development/C
+Obsoletes: %{name}-devel
+Provides: %{name}-devel
+Provides: libsgutils-devel
+Requires: %{libname} = %{version}-%{release}
-%description -n %{libname}-devel
+%description -n %{libname}-devel
This package contains the static sgutils library and its header
files.
@@ -64,12 +64,12 @@ make \
[ "%{buildroot}" != "/" ] && rm -rf %{buildroot}
make install \
- PREFIX=%{_prefix} \
- LIBDIR=%{buildroot}/%{_libdir} \
- INSTDIR=%{buildroot}/%{_bindir} \
- MANDIR=%{buildroot}/%{_mandir} \
- INCLUDEDIR=%{buildroot}/%{_includedir} \
- LIB_VINFO=1:0:0
+ PREFIX=%{_prefix} \
+ LIBDIR=%{buildroot}/%{_libdir} \
+ INSTDIR=%{buildroot}/%{_bindir} \
+ MANDIR=%{buildroot}/%{_mandir} \
+ INCLUDEDIR=%{buildroot}/%{_includedir} \
+ LIB_VINFO=1:0:0
%post -n %{libname} -p /sbin/ldconfig
@@ -96,6 +96,10 @@ make install \
%{_libdir}/*.la
%changelog
+* Thu Jul 06 2006 - dgilbert at interlog dot com
+- add sg_vpd and sg_rdac, uniform exit statuses
+ * sg3_utils-1.21
+
* Tue Apr 18 2006 - dgilbert at interlog dot com
- sg_logs: sas port specific page decoding, sg*_dd updates
* sg3_utils-1.20
diff --git a/sg_cmds.c b/sg_cmds.c
index 21b1337f..0f63d9e5 100644
--- a/sg_cmds.c
+++ b/sg_cmds.c
@@ -55,7 +55,7 @@
#include "sg_pt.h"
-static char * version_str = "1.26 20060413";
+static char * version_str = "1.31 20060623";
#define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */
@@ -72,6 +72,7 @@ static char * version_str = "1.26 20060413";
#define SERVICE_ACTION_IN_16_CMD 0x9e
#define SERVICE_ACTION_IN_16_CMDLEN 16
#define READ_CAPACITY_16_SA 0x10
+#define READ_LONG_16_SA 0x11
#define READ_CAPACITY_10_CMD 0x25
#define READ_CAPACITY_10_CMDLEN 10
#define MODE_SENSE6_CMD 0x1a
@@ -207,7 +208,8 @@ static int process_resp(void * ptvp, const char * leadin, int res,
slen = get_scsi_pt_sense_len(ptvp);
scat = sg_err_category_sense(sense_b, slen);
switch (scat) {
- case SG_LIB_CAT_MEDIA_CHANGED:
+ case SG_LIB_CAT_NOT_READY:
+ case SG_LIB_CAT_UNIT_ATTENTION:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
case SG_LIB_CAT_NO_SENSE:
@@ -252,25 +254,14 @@ static int process_resp(void * ptvp, const char * leadin, int res,
}
}
-static int is_recovered_or_no_sense(void * ptvp, unsigned char * sense_b)
-{
- struct sg_scsi_sense_hdr ssh;
- int slen = get_scsi_pt_sense_len(ptvp);
-
- if (sg_scsi_normalize_sense(sense_b, slen, &ssh)) {
- if ((SPC_SK_NO_SENSE == ssh.sense_key) ||
- (SPC_SK_RECOVERED_ERROR == ssh.sense_key))
- return 1;
- }
- return 0;
-}
-
-/* Invokes a SCSI INQUIRY command and yields the response */
-/* Returns 0 when successful, -1 -> pass through failed, -2 -> bad response */
+/* Invokes a SCSI INQUIRY command and yields the response
+ * Returns 0 when successful, SG_LIB_CAT_INVALID_OP -> not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
+ * SG_LIB_CAT_MALFORMED -> bad response, -1 -> other errors */
int sg_ll_inquiry(int sg_fd, int cmddt, int evpd, int pg_op,
void * resp, int mx_resp_len, int noisy, int verbose)
{
- int res, ret, k;
+ int res, ret, k, sense_cat;
unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0};
unsigned char sense_b[SENSE_BUFF_LEN];
unsigned char * up;
@@ -308,29 +299,43 @@ int sg_ll_inquiry(int sg_fd, int cmddt, int evpd, int pg_op,
set_scsi_pt_data_in(ptvp, resp, mx_resp_len);
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
ret = process_resp(ptvp, "inquiry", res, mx_resp_len, sense_b,
- noisy, verbose, NULL);
+ noisy, verbose, &sense_cat);
+ destruct_scsi_pt_obj(ptvp);
if (-1 == ret)
;
- else if (-2 == ret)
- ret = is_recovered_or_no_sense(ptvp, sense_b) ? 0 : -2;
- else if (ret < 4) {
+ else if (-2 == ret) {
+ switch (sense_cat) {
+ case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_ILLEGAL_REQ:
+ ret = sense_cat;
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ case SG_LIB_CAT_NO_SENSE:
+ ret = 0;
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+ } else if (ret < 4) {
if (verbose)
fprintf(sg_warnings_strm, "inquiry: got too few "
"bytes (%d)\n", ret);
- ret = -2;
+ ret = SG_LIB_CAT_MALFORMED;
} else
ret = 0;
- destruct_scsi_pt_obj(ptvp);
return ret;
}
-/* Yields most of first 36 bytes of a standard INQUIRY (evpd==0) response. */
-/* Returns 0 when successful, -1 -> pass through failed, -2 -> bad response */
+/* Yields most of first 36 bytes of a standard INQUIRY (evpd==0) response.
+ * Returns 0 when successful, SG_LIB_CAT_INVALID_OP -> not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
+ * SG_LIB_CAT_MALFORMED -> bad response, -1 -> other errors */
int sg_simple_inquiry(int sg_fd, struct sg_simple_inquiry_resp * inq_data,
int noisy, int verbose)
{
- int res, ret, k;
+ int res, ret, k, sense_cat;
unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0};
unsigned char sense_b[SENSE_BUFF_LEN];
unsigned char inq_resp[INQUIRY_RESP_INITIAL_LEN];
@@ -362,16 +367,28 @@ int sg_simple_inquiry(int sg_fd, struct sg_simple_inquiry_resp * inq_data,
set_scsi_pt_data_in(ptvp, inq_resp, sizeof(inq_resp));
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
ret = process_resp(ptvp, "inquiry", res, sizeof(inq_resp),
- sense_b, noisy, verbose, NULL);
+ sense_b, noisy, verbose, &sense_cat);
if (-1 == ret)
;
- else if (-2 == ret)
- ret = is_recovered_or_no_sense(ptvp, sense_b) ? 0 : -2;
- else if (ret < 4) {
+ else if (-2 == ret) {
+ switch (sense_cat) {
+ case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_ILLEGAL_REQ:
+ ret = sense_cat;
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ case SG_LIB_CAT_NO_SENSE:
+ ret = 0;
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+ } else if (ret < 4) {
if (verbose)
fprintf(sg_warnings_strm, "inquiry: got too few "
"bytes (%d)\n", ret);
- ret = -2;
+ ret = SG_LIB_CAT_MALFORMED;
} else
ret = 0;
@@ -396,11 +413,12 @@ int sg_simple_inquiry(int sg_fd, struct sg_simple_inquiry_resp * inq_data,
* 'pack_id' is just for diagnostics, safe to set to 0.
* Looks for progress indicator if 'progress' non-NULL;
* if found writes value [0..65535] else write -1.
- * Return of 0 -> success, -1 -> failure */
+ * Return of 0 -> success, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
int sg_ll_test_unit_ready_progress(int sg_fd, int pack_id, int * progress,
int noisy, int verbose)
{
- int res, ret, k;
+ int res, ret, k, sense_cat;
unsigned char turCmdBlk[TUR_CMDLEN] = {TUR_CMD, 0, 0, 0, 0, 0};
unsigned char sense_b[SENSE_BUFF_LEN];
void * ptvp;
@@ -424,7 +442,7 @@ int sg_ll_test_unit_ready_progress(int sg_fd, int pack_id, int * progress,
set_scsi_pt_packet_id(ptvp, pack_id);
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
ret = process_resp(ptvp, "test unit ready", res, 0, sense_b,
- noisy, verbose, NULL);
+ noisy, verbose, &sense_cat);
if (-1 == ret)
;
else if (-2 == ret) {
@@ -434,7 +452,21 @@ int sg_ll_test_unit_ready_progress(int sg_fd, int pack_id, int * progress,
if (! sg_get_sense_progress_fld(sense_b, slen, progress))
*progress = -1;
}
- ret = is_recovered_or_no_sense(ptvp, sense_b) ? 0 : -1;
+ switch (sense_cat) {
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ case SG_LIB_CAT_NOT_READY:
+ ret = sense_cat;
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ case SG_LIB_CAT_NO_SENSE:
+ ret = 0;
+ break;
+ case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_ILLEGAL_REQ:
+ default:
+ ret = -1;
+ break;
+ }
} else
ret = 0;
@@ -444,7 +476,8 @@ int sg_ll_test_unit_ready_progress(int sg_fd, int pack_id, int * progress,
/* Invokes a SCSI TEST UNIT READY command.
* 'pack_id' is just for diagnostics, safe to set to 0.
- * Return of 0 -> success, -1 -> failure */
+ * Return of 0 -> success, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
int sg_ll_test_unit_ready(int sg_fd, int pack_id, int noisy, int verbose)
{
return sg_ll_test_unit_ready_progress(sg_fd, pack_id, NULL, noisy,
@@ -452,8 +485,10 @@ int sg_ll_test_unit_ready(int sg_fd, int pack_id, int noisy, int verbose)
}
/* Invokes a SCSI SYNCHRONIZE CACHE (10) command. Return of 0 -> success,
- * -1 -> failure, SG_LIB_CAT_MEDIA_CHANGED -> repeat, SG_LIB_CAT_INVALID_OP
- * -> cdb not supported, SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb */
+ * SG_LIB_CAT_UNIT_ATTENTION -> repeat,
+ * SG_LIB_CAT_INVALID_OP -> cdb not supported,
+ * SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
int sg_ll_sync_cache_10(int sg_fd, int sync_nv, int immed, int group,
unsigned int lba, unsigned int count, int noisy,
int verbose)
@@ -502,7 +537,8 @@ int sg_ll_sync_cache_10(int sg_fd, int sync_nv, int immed, int group,
;
else if (-2 == ret) {
switch (sense_cat) {
- case SG_LIB_CAT_MEDIA_CHANGED:
+ case SG_LIB_CAT_NOT_READY:
+ case SG_LIB_CAT_UNIT_ATTENTION:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
ret = sense_cat;
@@ -522,10 +558,10 @@ int sg_ll_sync_cache_10(int sg_fd, int sync_nv, int immed, int group,
return ret;
}
-
/* Invokes a SCSI READ CAPACITY (16) command. Returns 0 -> success,
- * -1 -> failure, SG_LIB_CAT_MEDIA_CHANGED -> repeat, SG_LIB_CAT_INVALID_OP
- * -> cdb not supported, SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb */
+ * SG_LIB_CAT_UNIT_ATTENTION -> media changed??, SG_LIB_CAT_INVALID_OP
+ * -> cdb not supported, SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
int sg_ll_readcap_16(int sg_fd, int pmi, unsigned long long llba,
void * resp, int mx_resp_len, int noisy, int verbose)
{
@@ -575,7 +611,8 @@ int sg_ll_readcap_16(int sg_fd, int pmi, unsigned long long llba,
;
else if (-2 == ret) {
switch (sense_cat) {
- case SG_LIB_CAT_MEDIA_CHANGED:
+ case SG_LIB_CAT_NOT_READY:
+ case SG_LIB_CAT_UNIT_ATTENTION:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
ret = sense_cat;
@@ -595,10 +632,10 @@ int sg_ll_readcap_16(int sg_fd, int pmi, unsigned long long llba,
return ret;
}
-/* Invokes a SCSI READ CAPACITY (10) command. Return of 0 -> success,
- * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_MEDIA_CHANGED
- * -> media changed, SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
- * -1 -> other failure */
+/* Invokes a SCSI READ CAPACITY (10) command. Returns 0 -> success,
+ * SG_LIB_CAT_UNIT_ATTENTION -> media changed??, SG_LIB_CAT_INVALID_OP
+ * -> cdb not supported, SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
int sg_ll_readcap_10(int sg_fd, int pmi, unsigned int lba,
void * resp, int mx_resp_len, int noisy, int verbose)
{
@@ -638,7 +675,8 @@ int sg_ll_readcap_10(int sg_fd, int pmi, unsigned int lba,
;
else if (-2 == ret) {
switch (sense_cat) {
- case SG_LIB_CAT_MEDIA_CHANGED:
+ case SG_LIB_CAT_NOT_READY:
+ case SG_LIB_CAT_UNIT_ATTENTION:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
ret = sense_cat;
@@ -660,7 +698,8 @@ int sg_ll_readcap_10(int sg_fd, int pmi, unsigned int lba,
/* Invokes a SCSI MODE SENSE (6) command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
- * bad field in cdb, -1 -> other failure */
+ * bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
+ * SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
int sg_ll_mode_sense6(int sg_fd, int dbd, int pc, int pg_code, int sub_pg_code,
void * resp, int mx_resp_len, int noisy, int verbose)
{
@@ -701,8 +740,10 @@ int sg_ll_mode_sense6(int sg_fd, int dbd, int pc, int pg_code, int sub_pg_code,
;
else if (-2 == ret) {
switch (sense_cat) {
+ case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
@@ -727,7 +768,8 @@ int sg_ll_mode_sense6(int sg_fd, int dbd, int pc, int pg_code, int sub_pg_code,
/* Invokes a SCSI MODE SENSE (10) command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
- * bad field in cdb, -1 -> other failure */
+ * bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
+ * SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
int sg_ll_mode_sense10(int sg_fd, int llbaa, int dbd, int pc, int pg_code,
int sub_pg_code, void * resp, int mx_resp_len,
int noisy, int verbose)
@@ -770,8 +812,10 @@ int sg_ll_mode_sense10(int sg_fd, int llbaa, int dbd, int pc, int pg_code,
;
else if (-2 == ret) {
switch (sense_cat) {
+ case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
@@ -796,7 +840,8 @@ int sg_ll_mode_sense10(int sg_fd, int llbaa, int dbd, int pc, int pg_code,
/* Invokes a SCSI MODE SELECT (6) command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
- * bad field in cdb, -1 -> other failure */
+ * bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
+ * SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
int sg_ll_mode_select6(int sg_fd, int pf, int sp, void * paramp,
int param_len, int noisy, int verbose)
{
@@ -840,8 +885,10 @@ int sg_ll_mode_select6(int sg_fd, int pf, int sp, void * paramp,
;
else if (-2 == ret) {
switch (sense_cat) {
+ case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
@@ -861,7 +908,8 @@ int sg_ll_mode_select6(int sg_fd, int pf, int sp, void * paramp,
/* Invokes a SCSI MODE SELECT (10) command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
- * bad field in cdb, -1 -> other failure */
+ * bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
+ * SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
int sg_ll_mode_select10(int sg_fd, int pf, int sp, void * paramp,
int param_len, int noisy, int verbose)
{
@@ -906,8 +954,10 @@ int sg_ll_mode_select10(int sg_fd, int pf, int sp, void * paramp,
;
else if (-2 == ret) {
switch (sense_cat) {
+ case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
@@ -971,13 +1021,15 @@ int sg_mode_page_offset(const unsigned char * resp, int resp_len,
/* Fetches current, changeable, default and/or saveable modes pages as
* indicated by pcontrol_arr for given pg_code and sub_pg_code. If
* mode6==0 then use MODE SENSE (10) else use MODE SENSE (6). If
- * flexible set and mode data length seems wrong then try and
+ * flexible set and mode data length seems wrong then try and
* fix (compensating hack for bad device or driver). pcontrol_arr
* should have 4 elements for output of current, changeable, default
* and saved values respectively. Each element should be NULL or
* at least mx_mpage_len bytes long.
* Return of 0 -> overall success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure.
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready,
+ * SG_LIB_CAT_MALFORMED -> bad response, -1 -> other failure.
* If success_mask pointer is not NULL then zeroes it then sets bit 0, 1,
* 2 and/or 3 if the current, changeable, default and saved values
* respectively have been fetched. If error on current page
@@ -1051,7 +1103,7 @@ int sg_get_mode_page_controls(int sg_fd, int mode6, int pg_code,
if (('\0' != ebuff[0]) && (verbose > 0))
fprintf(sg_warnings_strm, "sg_get_mode_page_types: "
"current values: %s\n", ebuff);
- return offset;
+ return SG_LIB_CAT_MALFORMED;
}
xfer_len = calc_len - offset;
if (xfer_len > mx_mpage_len)
@@ -1137,6 +1189,7 @@ int sg_ll_request_sense(int sg_fd, int desc, void * resp, int mx_resp_len,
case SG_LIB_CAT_NO_SENSE:
ret = 0;
break;
+ case SG_LIB_CAT_NOT_READY: /* shouldn't happen ?? */
default:
ret = -1;
break;
@@ -1203,6 +1256,7 @@ int sg_ll_report_luns(int sg_fd, int select_report, void * resp,
case SG_LIB_CAT_NO_SENSE:
ret = 0;
break;
+ case SG_LIB_CAT_NOT_READY: /* shouldn't happen ?? */
default:
ret = -1;
break;
@@ -1215,10 +1269,11 @@ int sg_ll_report_luns(int sg_fd, int select_report, void * resp,
/* Invokes a SCSI LOG SENSE command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Log Sense not supported,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
int sg_ll_log_sense(int sg_fd, int ppc, int sp, int pc, int pg_code,
- int paramp, unsigned char * resp, int mx_resp_len,
- int noisy, int verbose)
+ int subpg_code, int paramp, unsigned char * resp,
+ int mx_resp_len, int noisy, int verbose)
{
int res, ret, k, sense_cat;
unsigned char logsCmdBlk[LOG_SENSE_CMDLEN] =
@@ -1234,6 +1289,7 @@ int sg_ll_log_sense(int sg_fd, int ppc, int sp, int pc, int pg_code,
}
logsCmdBlk[1] = (unsigned char)((ppc ? 2 : 0) | (sp ? 1 : 0));
logsCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f));
+ logsCmdBlk[3] = (unsigned char)(subpg_code & 0xff);
logsCmdBlk[5] = (unsigned char)((paramp >> 8) & 0xff);
logsCmdBlk[6] = (unsigned char)(paramp & 0xff);
logsCmdBlk[7] = (unsigned char)((mx_resp_len >> 8) & 0xff);
@@ -1260,8 +1316,10 @@ int sg_ll_log_sense(int sg_fd, int ppc, int sp, int pc, int pg_code,
;
else if (-2 == ret) {
switch (sense_cat) {
+ case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
@@ -1278,11 +1336,12 @@ int sg_ll_log_sense(int sg_fd, int ppc, int sp, int pc, int pg_code,
return ret;
}
-
/* Invokes a SCSI LOG SELECT command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Log Select not supported,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
int sg_ll_log_select(int sg_fd, int pcr, int sp, int pc,
+ int pg_code, int subpg_code,
unsigned char * paramp, int param_len,
int noisy, int verbose)
{
@@ -1299,7 +1358,8 @@ int sg_ll_log_select(int sg_fd, int pcr, int sp, int pc,
return -1;
}
logsCmdBlk[1] = (unsigned char)((pcr ? 2 : 0) | (sp ? 1 : 0));
- logsCmdBlk[2] = (unsigned char)((pc << 6) & 0xc0);
+ logsCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f));
+ logsCmdBlk[3] = (unsigned char)(subpg_code & 0xff);
logsCmdBlk[7] = (unsigned char)((param_len >> 8) & 0xff);
logsCmdBlk[8] = (unsigned char)(param_len & 0xff);
if (verbose) {
@@ -1328,8 +1388,10 @@ int sg_ll_log_select(int sg_fd, int pcr, int sp, int pc,
;
else if (-2 == ret) {
switch (sense_cat) {
+ case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
@@ -1349,7 +1411,8 @@ int sg_ll_log_select(int sg_fd, int pcr, int sp, int pc,
/* Invokes a SCSI REPORT TARGET PORT GROUPS command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Report Target Port Groups not supported,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
+ * SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
int sg_ll_report_tgt_prt_grp(int sg_fd, void * resp,
int mx_resp_len, int noisy, int verbose)
{
@@ -1391,6 +1454,7 @@ int sg_ll_report_tgt_prt_grp(int sg_fd, void * resp,
switch (sense_cat) {
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
@@ -1410,7 +1474,8 @@ int sg_ll_report_tgt_prt_grp(int sg_fd, void * resp,
/* Invokes a SCSI SEND DIAGNOSTIC command. Foreground, extended self tests can
* take a long time, if so set long_duration flag. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Send diagnostic not supported,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
int sg_ll_send_diag(int sg_fd, int sf_code, int pf_bit, int sf_bit,
int devofl_bit, int unitofl_bit, int long_duration,
void * paramp, int param_len, int noisy,
@@ -1458,8 +1523,10 @@ int sg_ll_send_diag(int sg_fd, int sf_code, int pf_bit, int sf_bit,
;
else if (-2 == ret) {
switch (sense_cat) {
+ case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
@@ -1479,7 +1546,8 @@ int sg_ll_send_diag(int sg_fd, int sf_code, int pf_bit, int sf_bit,
/* Invokes a SCSI RECEIVE DIAGNOSTIC RESULTS command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Receive diagnostic results not supported,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
int sg_ll_receive_diag(int sg_fd, int pcv, int pg_code, void * resp,
int mx_resp_len, int noisy, int verbose)
{
@@ -1519,8 +1587,10 @@ int sg_ll_receive_diag(int sg_fd, int pcv, int pg_code, void * resp,
;
else if (-2 == ret) {
switch (sense_cat) {
+ case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
@@ -1539,7 +1609,8 @@ int sg_ll_receive_diag(int sg_fd, int pcv, int pg_code, void * resp,
/* Invokes a SCSI READ DEFECT DATA (10) command (SBC). Return of 0 ->
* success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
int sg_ll_read_defect10(int sg_fd, int req_plist, int req_glist,
int dl_format, void * resp, int mx_resp_len,
int noisy, int verbose)
@@ -1582,8 +1653,10 @@ int sg_ll_read_defect10(int sg_fd, int req_plist, int req_glist,
;
else if (-2 == ret) {
switch (sense_cat) {
+ case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
@@ -1608,7 +1681,8 @@ int sg_ll_read_defect10(int sg_fd, int req_plist, int req_glist,
/* Invokes a SCSI READ MEDIA SERIAL NUMBER command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Read media serial number not supported,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
int sg_ll_read_media_serial_num(int sg_fd, void * resp, int mx_resp_len,
int noisy, int verbose)
{
@@ -1648,8 +1722,10 @@ int sg_ll_read_media_serial_num(int sg_fd, void * resp, int mx_resp_len,
;
else if (-2 == ret) {
switch (sense_cat) {
+ case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
@@ -1675,7 +1751,8 @@ int sg_ll_read_media_serial_num(int sg_fd, void * resp, int mx_resp_len,
/* Invokes a SCSI START STOP UNIT command (MMC + SBC).
* Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Start stop unit not supported,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
int sg_ll_start_stop_unit(int sg_fd, int immed, int fl_num, int power_cond,
int fl, int loej, int start, int noisy, int verbose)
{
@@ -1711,8 +1788,10 @@ int sg_ll_start_stop_unit(int sg_fd, int immed, int fl_num, int power_cond,
;
else if (-2 == ret) {
switch (sense_cat) {
+ case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
@@ -1729,11 +1808,12 @@ int sg_ll_start_stop_unit(int sg_fd, int immed, int fl_num, int power_cond,
return ret;
}
-/* Invokes a SCSI PREVENT ALLOW MEDIUM REMOVAL command (SPC-3)
+/* Invokes a SCSI PREVENT ALLOW MEDIUM REMOVAL command (SPC-3)
* prevent==0 allows removal, prevent==1 prevents removal ...
* Return of 0 -> success,
- * SG_LIB_CAT_INVALID_OP -> command not supported
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_INVALID_OP -> command not supported
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
int sg_ll_prevent_allow(int sg_fd, int prevent, int noisy, int verbose)
{
int k, res, ret, sense_cat;
@@ -1771,8 +1851,10 @@ int sg_ll_prevent_allow(int sg_fd, int prevent, int noisy, int verbose)
;
else if (-2 == ret) {
switch (sense_cat) {
+ case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
@@ -1790,8 +1872,9 @@ int sg_ll_prevent_allow(int sg_fd, int prevent, int noisy, int verbose)
}
/* Invokes a SCSI REPORT DEVICE IDENTIFIER command. Return of 0 -> success,
- * SG_LIB_CAT_INVALID_OP -> Report device identifier not supported,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_INVALID_OP -> Report media serial number not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
int sg_ll_report_dev_id(int sg_fd, void * resp, int mx_resp_len,
int noisy, int verbose)
{
@@ -1831,8 +1914,10 @@ int sg_ll_report_dev_id(int sg_fd, void * resp, int mx_resp_len,
;
else if (-2 == ret) {
switch (sense_cat) {
+ case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
@@ -1856,8 +1941,9 @@ int sg_ll_report_dev_id(int sg_fd, void * resp, int mx_resp_len,
}
/* Invokes a SCSI SET DEVICE IDENTIFIER command. Return of 0 -> success,
- * SG_LIB_CAT_INVALID_OP -> Set device identifier not supported,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_INVALID_OP -> Read media serial number not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
int sg_ll_set_dev_id(int sg_fd, void * paramp, int param_len,
int noisy, int verbose)
{
@@ -1901,8 +1987,10 @@ int sg_ll_set_dev_id(int sg_fd, void * paramp, int param_len,
;
else if (-2 == ret) {
switch (sense_cat) {
+ case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
@@ -1922,7 +2010,8 @@ int sg_ll_set_dev_id(int sg_fd, void * paramp, int param_len,
/* Invokes a FORMAT UNIT (SBC-3) command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Format unit not supported,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
int sg_ll_format_unit(int sg_fd, int fmtpinfo, int rto_req, int longlist,
int fmtdata, int cmplist, int dlist_format,
int timeout_secs, void * paramp, int param_len,
@@ -1975,8 +2064,10 @@ int sg_ll_format_unit(int sg_fd, int fmtpinfo, int rto_req, int longlist,
;
else if (-2 == ret) {
switch (sense_cat) {
+ case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
@@ -1995,8 +2086,9 @@ int sg_ll_format_unit(int sg_fd, int fmtpinfo, int rto_req, int longlist,
}
/* Invokes a SCSI REASSIGN BLOCKS command. Return of 0 -> success,
- * SG_LIB_CAT_INVALID_OP -> invalid opcode,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
int sg_ll_reassign_blocks(int sg_fd, int longlba, int longlist,
void * paramp, int param_len, int noisy,
int verbose)
@@ -2036,8 +2128,10 @@ int sg_ll_reassign_blocks(int sg_fd, int longlba, int longlist,
;
else if (-2 == ret) {
switch (sense_cat) {
+ case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
@@ -2058,7 +2152,7 @@ int sg_ll_reassign_blocks(int sg_fd, int longlba, int longlist,
/* Invokes a SCSI GET CONFIGURATION command (MMC-3,4,5).
* Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not
* supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
- * else -1 */
+ * SG_LIB_CAT_UNIT_ATTENTION, else -1 */
int sg_ll_get_config(int sg_fd, int rt, int starting, void * resp,
int mx_resp_len, int noisy, int verbose)
{
@@ -2113,6 +2207,7 @@ int sg_ll_get_config(int sg_fd, int rt, int starting, void * resp,
switch (sense_cat) {
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
@@ -2137,7 +2232,8 @@ int sg_ll_get_config(int sg_fd, int rt, int starting, void * resp,
/* Invokes a SCSI PERSISTENT RESERVE IN command (SPC). Returns 0
* when successful, SG_LIB_CAT_INVALID_OP if command not supported,
- * SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported, else -1 */
+ * SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
+ * SG_LIB_CAT_UNIT_ATTENTION, else -1 */
int sg_ll_persistent_reserve_in(int sg_fd, int rq_servact, void * resp,
int mx_resp_len, int noisy, int verbose)
{
@@ -2179,6 +2275,7 @@ int sg_ll_persistent_reserve_in(int sg_fd, int rq_servact, void * resp,
switch (sense_cat) {
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
@@ -2203,7 +2300,8 @@ int sg_ll_persistent_reserve_in(int sg_fd, int rq_servact, void * resp,
/* Invokes a SCSI PERSISTENT RESERVE OUT command (SPC). Returns 0
* when successful, SG_LIB_CAT_INVALID_OP if command not supported,
- * SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported, else -1 */
+ * SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
+ * SG_LIB_CAT_UNIT_ATTENTION, else -1 */
int sg_ll_persistent_reserve_out(int sg_fd, int rq_servact, int rq_scope,
unsigned int rq_type, void * paramp,
int param_len, int noisy, int verbose)
@@ -2250,6 +2348,7 @@ int sg_ll_persistent_reserve_out(int sg_fd, int rq_servact, int rq_scope,
switch (sense_cat) {
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
@@ -2284,12 +2383,13 @@ static int has_blk_ili(unsigned char * sensep, int sb_len)
return 0;
}
-/* Invokes a SCSI READ LONG (10) SBC command. Note that 'xfer_len'
+/* Invokes a SCSI READ LONG (10) command (SBC). Note that 'xfer_len'
* is in bytes. Returns 0 -> success,
* SG_LIB_CAT_INVALID_OP -> READ LONG(10) not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
* SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info
- * field written to 'offsetp', -1 -> other failure */
+ * field written to 'offsetp',
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
int sg_ll_read_long10(int sg_fd, int correct, unsigned long lba,
void * resp, int xfer_len, int * offsetp,
int noisy, int verbose)
@@ -2304,15 +2404,12 @@ int sg_ll_read_long10(int sg_fd, int correct, unsigned long lba,
if (correct)
readLongCmdBlk[1] |= 0x2;
- /*lba*/
- readLongCmdBlk[2] = (lba & 0xff000000) >> 24;
- readLongCmdBlk[3] = (lba & 0x00ff0000) >> 16;
- readLongCmdBlk[4] = (lba & 0x0000ff00) >> 8;
- readLongCmdBlk[5] = (lba & 0x000000ff);
- /*size*/
- readLongCmdBlk[7] = (xfer_len & 0x0000ff00) >> 8;
- readLongCmdBlk[8] = (xfer_len & 0x000000ff);
-
+ readLongCmdBlk[2] = (lba >> 24) & 0xff;
+ readLongCmdBlk[3] = (lba >> 16) & 0xff;
+ readLongCmdBlk[4] = (lba >> 8) & 0xff;
+ readLongCmdBlk[5] = lba & 0xff;
+ readLongCmdBlk[7] = (xfer_len >> 8) & 0xff;
+ readLongCmdBlk[8] = xfer_len & 0xff;
if (NULL == sg_warnings_strm)
sg_warnings_strm = stderr;
if (verbose) {
@@ -2337,7 +2434,103 @@ int sg_ll_read_long10(int sg_fd, int correct, unsigned long lba,
;
else if (-2 == ret) {
switch (sense_cat) {
+ case SG_LIB_CAT_NOT_READY:
+ case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ ret = sense_cat;
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ case SG_LIB_CAT_NO_SENSE:
+ ret = 0;
+ break;
+ case SG_LIB_CAT_ILLEGAL_REQ:
+ {
+ int valid, slen;
+ unsigned long long ull = 0;
+
+ slen = get_scsi_pt_sense_len(ptvp);
+ valid = sg_get_sense_info_fld(sense_b, slen, &ull);
+ if (valid && has_blk_ili(sense_b, slen)) {
+ if (offsetp)
+ *offsetp = (int)(long long)ull;
+ ret = SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO;
+ } else {
+ if (verbose || noisy)
+ fprintf(sg_warnings_strm, " info field [%d], but "
+ "ILI clear ??\n", (int)(long long)ull);
+ ret = SG_LIB_CAT_ILLEGAL_REQ;
+ }
+ }
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+ } else
+ ret = 0;
+ destruct_scsi_pt_obj(ptvp);
+ return ret;
+}
+
+/* Invokes a SCSI READ LONG (16) command (SBC). Note that 'xfer_len'
+ * is in bytes. Returns 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> READ LONG(16) not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
+ * SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info
+ * field written to 'offsetp',
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
+int sg_ll_read_long16(int sg_fd, int correct, unsigned long long llba,
+ void * resp, int xfer_len, int * offsetp,
+ int noisy, int verbose)
+{
+ int k, res, sense_cat, ret;
+ unsigned char readLongCmdBlk[SERVICE_ACTION_IN_16_CMDLEN];
+ unsigned char sense_b[SENSE_BUFF_LEN];
+ void * ptvp;
+
+ memset(readLongCmdBlk, 0, sizeof(readLongCmdBlk));
+ readLongCmdBlk[0] = SERVICE_ACTION_IN_16_CMD;
+ readLongCmdBlk[1] = READ_LONG_16_SA;
+ if (correct)
+ readLongCmdBlk[14] |= 0x1;
+
+ readLongCmdBlk[2] = (llba >> 56) & 0xff;
+ readLongCmdBlk[3] = (llba >> 48) & 0xff;
+ readLongCmdBlk[4] = (llba >> 40) & 0xff;
+ readLongCmdBlk[5] = (llba >> 32) & 0xff;
+ readLongCmdBlk[6] = (llba >> 24) & 0xff;
+ readLongCmdBlk[7] = (llba >> 16) & 0xff;
+ readLongCmdBlk[8] = (llba >> 8) & 0xff;
+ readLongCmdBlk[9] = llba & 0xff;
+ readLongCmdBlk[12] = (xfer_len >> 8) & 0xff;
+ readLongCmdBlk[13] = xfer_len & 0xff;
+ if (NULL == sg_warnings_strm)
+ sg_warnings_strm = stderr;
+ if (verbose) {
+ fprintf(sg_warnings_strm, " Read Long (16) cmd: ");
+ for (k = 0; k < SERVICE_ACTION_IN_16_CMDLEN; ++k)
+ fprintf(sg_warnings_strm, "%02x ", readLongCmdBlk[k]);
+ fprintf(sg_warnings_strm, "\n");
+ }
+
+ ptvp = construct_scsi_pt_obj();
+ if (NULL == ptvp) {
+ fprintf(sg_warnings_strm, "read long (16): out of memory\n");
+ return -1;
+ }
+ set_scsi_pt_cdb(ptvp, readLongCmdBlk, sizeof(readLongCmdBlk));
+ set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ set_scsi_pt_data_in(ptvp, resp, xfer_len);
+ res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+ ret = process_resp(ptvp, "read long (16)", res, xfer_len,
+ sense_b, noisy, verbose, &sense_cat);
+ if (-1 == ret)
+ ;
+ else if (-2 == ret) {
+ switch (sense_cat) {
+ case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_UNIT_ATTENTION:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
@@ -2378,7 +2571,8 @@ int sg_ll_read_long10(int sg_fd, int correct, unsigned long lba,
* SG_LIB_CAT_INVALID_OP -> WRITE LONG(10) not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
* SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info
- * field written to 'offsetp', -1 -> other failure */
+ * field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
int sg_ll_write_long10(int sg_fd, int cor_dis, unsigned long lba,
void * data_out, int xfer_len, int * offsetp,
int noisy, int verbose)
@@ -2390,19 +2584,15 @@ int sg_ll_write_long10(int sg_fd, int cor_dis, unsigned long lba,
memset(writeLongCmdBlk, 0, WRITE_LONG10_CMDLEN);
writeLongCmdBlk[0] = WRITE_LONG10_CMD;
-
- /*lba*/
- writeLongCmdBlk[2] = (lba & 0xff000000) >> 24;
- writeLongCmdBlk[3] = (lba & 0x00ff0000) >> 16;
- writeLongCmdBlk[4] = (lba & 0x0000ff00) >> 8;
- writeLongCmdBlk[5] = (lba & 0x000000ff);
- /*size*/
- writeLongCmdBlk[7] = (xfer_len & 0x0000ff00) >> 8;
- writeLongCmdBlk[8] = (xfer_len & 0x000000ff);
-
if (cor_dis)
writeLongCmdBlk[1] |= 0x80;
+ writeLongCmdBlk[2] = (lba >> 24) & 0xff;
+ writeLongCmdBlk[3] = (lba >> 16) & 0xff;
+ writeLongCmdBlk[4] = (lba >> 8) & 0xff;
+ writeLongCmdBlk[5] = lba & 0xff;
+ writeLongCmdBlk[7] = (xfer_len >> 8) & 0xff;
+ writeLongCmdBlk[8] = xfer_len & 0xff;
if (NULL == sg_warnings_strm)
sg_warnings_strm = stderr;
if (verbose) {
@@ -2427,6 +2617,8 @@ int sg_ll_write_long10(int sg_fd, int cor_dis, unsigned long lba,
;
else if (-2 == ret) {
switch (sense_cat) {
+ case SG_LIB_CAT_NOT_READY:
+ case SG_LIB_CAT_UNIT_ATTENTION:
case SG_LIB_CAT_INVALID_OP:
ret = sense_cat;
break;
@@ -2468,10 +2660,10 @@ int sg_ll_write_long10(int sg_fd, int cor_dis, unsigned long lba,
* Note that 'veri_len' is in blocks while 'data_out_len' is in bytes.
* Returns of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Verify(10) not supported,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_MEDIUM_HARD -> medium or hardware error, no valid info,
* SG_LIB_CAT_MEDIUM_HARD_WITH_INFO -> as previous, with valid info,
- * -1 -> other failure */
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
int sg_ll_verify10(int sg_fd, int dpo, int bytechk, unsigned long lba,
int veri_len, void * data_out, int data_out_len,
unsigned long * infop, int noisy, int verbose)
@@ -2516,8 +2708,10 @@ int sg_ll_verify10(int sg_fd, int dpo, int bytechk, unsigned long lba,
;
else if (-2 == ret) {
switch (sense_cat) {
+ case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
diff --git a/sg_cmds.h b/sg_cmds.h
index f2b059f2..a1990a88 100644
--- a/sg_cmds.h
+++ b/sg_cmds.h
@@ -35,17 +35,18 @@
* Additional sense data categories (to those defined in sg_lib.h).
*/
-#define SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO 9 /* Illegal request (other than */
+#define SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO 17 /* Illegal request (other than */
/* invalid opcode) plus 'info' field: */
/* [sk,asc,ascq: 0x5,*,*] */
-#define SG_LIB_CAT_MEDIUM_HARD_WITH_INFO 10 /* medium or hardware error */
+#define SG_LIB_CAT_MEDIUM_HARD_WITH_INFO 18 /* medium or hardware error */
/* sense key plus 'info' field: */
/* [sk,asc,ascq: 0x3/0x4,*,*] */
/* Invokes a FORMAT UNIT (SBC-3) command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Format unit not supported,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
extern int sg_ll_format_unit(int sg_fd, int fmtpinfo, int rto_req,
int longlist, int fmtdata, int cmplist,
int dlist_format, int timeout_secs,
@@ -55,66 +56,76 @@ extern int sg_ll_format_unit(int sg_fd, int fmtpinfo, int rto_req,
/* Invokes a SCSI GET CONFIGURATION command (MMC-3,4,5).
* Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not
* supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
- * else -1 */
+ * SG_LIB_CAT_UNIT_ATTENTION, else -1 */
extern int sg_ll_get_config(int sg_fd, int rt, int starting, void * resp,
int mx_resp_len, int noisy, int verbose);
-/* Invokes a SCSI INQUIRY command and yields the response */
-/* Returns 0 when successful, -1 -> pass through failed, -2 -> bad response */
+/* Invokes a SCSI INQUIRY command and yields the response
+ * Returns 0 when successful, SG_LIB_CAT_INVALID_OP -> not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other errors */
extern int sg_ll_inquiry(int sg_fd, int cmddt, int evpd, int pg_op,
void * resp, int mx_resp_len, int noisy,
int verbose);
/* Invokes a SCSI LOG SELECT command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Log Select not supported,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
extern int sg_ll_log_select(int sg_fd, int pcr, int sp, int pc,
+ int pg_code, int subpg_code,
unsigned char * paramp, int param_len,
int noisy, int verbose);
/* Invokes a SCSI LOG SENSE command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Log Sense not supported,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
extern int sg_ll_log_sense(int sg_fd, int ppc, int sp, int pc, int pg_code,
- int paramp, unsigned char * resp, int mx_resp_len,
- int noisy, int verbose);
+ int subpg_code, int paramp, unsigned char * resp,
+ int mx_resp_len, int noisy, int verbose);
/* Invokes a SCSI MODE SELECT (6) command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
- * bad field in cdb, -1 -> other failure */
+ * bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
+ * SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
extern int sg_ll_mode_select6(int sg_fd, int pf, int sp, void * paramp,
int param_len, int noisy, int verbose);
/* Invokes a SCSI MODE SELECT (10) command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
- * bad field in cdb, -1 -> other failure */
+ * bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
+ * SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
extern int sg_ll_mode_select10(int sg_fd, int pf, int sp, void * paramp,
int param_len, int noisy, int verbose);
/* Invokes a SCSI MODE SENSE (6) command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
- * bad field in cdb, -1 -> other failure */
+ * bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
+ * SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
extern int sg_ll_mode_sense6(int sg_fd, int dbd, int pc, int pg_code,
int sub_pg_code, void * resp, int mx_resp_len,
int noisy, int verbose);
/* Invokes a SCSI MODE SENSE (10) command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
- * bad field in cdb, -1 -> other failure */
+ * bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
+ * SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
extern int sg_ll_mode_sense10(int sg_fd, int llbaa, int dbd, int pc,
int pg_code, int sub_pg_code, void * resp,
int mx_resp_len, int noisy, int verbose);
/* Invokes a SCSI PERSISTENT RESERVE IN command (SPC). Returns 0
* when successful, SG_LIB_CAT_INVALID_OP if command not supported,
- * SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported, else -1 */
+ * SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
+ * SG_LIB_CAT_UNIT_ATTENTION, else -1 */
extern int sg_ll_persistent_reserve_in(int sg_fd, int rq_servact,
void * resp, int mx_resp_len,
int noisy, int verbose);
/* Invokes a SCSI PERSISTENT RESERVE OUT command (SPC). Returns 0
* when successful, SG_LIB_CAT_INVALID_OP if command not supported,
- * SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported, else -1 */
+ * SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
+ * SG_LIB_CAT_UNIT_ATTENTION, else -1 */
extern int sg_ll_persistent_reserve_out(int sg_fd, int rq_servact,
int rq_scope, unsigned int rq_type,
void * paramp, int param_len,
@@ -124,28 +135,31 @@ extern int sg_ll_persistent_reserve_out(int sg_fd, int rq_servact,
* prevent==0 allows removal, prevent==1 prevents removal ...
* Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> command not supported
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
extern int sg_ll_prevent_allow(int sg_fd, int prevent, int noisy,
int verbose);
/* Invokes a SCSI READ CAPACITY (10) command. Return of 0 -> success,
- * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_MEDIA_CHANGED
- * -> media changed, SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
- * -1 -> other failure */
+ * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_UNIT_ATTENTION
+ * -> perhaps media changed, SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
extern int sg_ll_readcap_10(int sg_fd, int pmi, unsigned int lba,
void * resp, int mx_resp_len, int noisy,
int verbose);
/* Invokes a SCSI READ CAPACITY (16) command. Returns 0 -> success,
- * -1 -> failure, SG_LIB_CAT_MEDIA_CHANGED -> repeat, SG_LIB_CAT_INVALID_OP
- * -> cdb not supported, SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb */
+ * SG_LIB_CAT_UNIT_ATTENTION -> media changed??, SG_LIB_CAT_INVALID_OP
+ * -> cdb not supported, SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
extern int sg_ll_readcap_16(int sg_fd, int pmi, unsigned long long llba,
void * resp, int mx_resp_len, int noisy,
int verbose);
/* Invokes a SCSI READ DEFECT DATA (10) command (SBC). Return of 0 ->
* success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
extern int sg_ll_read_defect10(int sg_fd, int req_plist, int req_glist,
int dl_format, void * resp, int mx_resp_len,
int noisy, int verbose);
@@ -155,34 +169,50 @@ extern int sg_ll_read_defect10(int sg_fd, int req_plist, int req_glist,
* SG_LIB_CAT_INVALID_OP -> READ LONG(10) not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
* SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info
- * field written to 'offsetp', -1 -> other failure */
+ * field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
extern int sg_ll_read_long10(int sg_fd, int correct, unsigned long lba,
void * resp, int xfer_len, int * offsetp,
int noisy, int verbose);
+/* Invokes a SCSI READ LONG (16) command (SBC). Note that 'xfer_len'
+ * is in bytes. Returns 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> READ LONG(16) not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
+ * SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info
+ * field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
+extern int sg_ll_read_long16(int sg_fd, int correct, unsigned long long llba,
+ void * resp, int xfer_len, int * offsetp,
+ int noisy, int verbose);
+
/* Invokes a SCSI READ MEDIA SERIAL NUMBER command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Read media serial number not supported,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
extern int sg_ll_read_media_serial_num(int sg_fd, void * resp,
int mx_resp_len, int noisy,
int verbose);
/* Invokes a SCSI REASSIGN BLOCKS command. Return of 0 -> success,
- * SG_LIB_CAT_INVALID_OP -> invalid opcode,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
extern int sg_ll_reassign_blocks(int sg_fd, int longlba, int longlist,
void * paramp, int param_len, int noisy,
int verbose);
/* Invokes a SCSI RECEIVE DIAGNOSTIC RESULTS command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Receive diagnostic results not supported,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
extern int sg_ll_receive_diag(int sg_fd, int pcv, int pg_code, void * resp,
int mx_resp_len, int noisy, int verbose);
/* Invokes a SCSI REPORT DEVICE IDENTIFIER command. Return of 0 -> success,
- * SG_LIB_CAT_INVALID_OP -> Read media serial number not supported,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_INVALID_OP -> Report media serial number not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
extern int sg_ll_report_dev_id(int sg_fd, void * resp, int max_resp_len,
int noisy, int verbose);
@@ -194,7 +224,8 @@ extern int sg_ll_report_luns(int sg_fd, int select_report, void * resp,
/* Invokes a SCSI REPORT TARGET PORT GROUPS command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Report Target Port Groups not supported,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
+ * SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
extern int sg_ll_report_tgt_prt_grp(int sg_fd, void * resp,
int mx_resp_len, int noisy, int verbose);
@@ -207,7 +238,8 @@ extern int sg_ll_request_sense(int sg_fd, int desc, void * resp,
/* Invokes a SCSI SEND DIAGNOSTIC command. Foreground, extended self tests can
* take a long time, if so set long_duration flag. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Send diagnostic not supported,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
extern int sg_ll_send_diag(int sg_fd, int sf_code, int pf_bit, int sf_bit,
int devofl_bit, int unitofl_bit, int long_duration,
void * paramp, int param_len, int noisy,
@@ -215,28 +247,33 @@ extern int sg_ll_send_diag(int sg_fd, int sf_code, int pf_bit, int sf_bit,
/* Invokes a SCSI SET DEVICE IDENTIFIER command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Read media serial number not supported,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
extern int sg_ll_set_dev_id(int sg_fd, void * paramp, int param_len,
int noisy, int verbose);
/* Invokes a SCSI START STOP UNIT command (MMC + SBC).
* Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Start stop unit not supported,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
extern int sg_ll_start_stop_unit(int sg_fd, int immed, int fl_num,
int power_cond, int fl, int loej,
int start, int noisy, int verbose);
/* Invokes a SCSI SYNCHRONIZE CACHE (10) command. Return of 0 -> success,
- * -1 -> failure, SG_LIB_CAT_MEDIA_CHANGED -> repeat, SG_LIB_CAT_INVALID_OP
- * -> cdb not supported, SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb */
+ * SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_INVALID_OP -> cdb not supported,
+ * SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
extern int sg_ll_sync_cache_10(int sg_fd, int sync_nv, int immed, int group,
unsigned int lba, unsigned int count,
int noisy, int verbose);
/* Invokes a SCSI TEST UNIT READY command.
* 'pack_id' is just for diagnostics, safe to set to 0.
- * Return of 0 -> success, -1 -> failure */
+ * Return of 0 -> success, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
extern int sg_ll_test_unit_ready(int sg_fd, int pack_id, int noisy,
int verbose);
@@ -244,7 +281,8 @@ extern int sg_ll_test_unit_ready(int sg_fd, int pack_id, int noisy,
* 'pack_id' is just for diagnostics, safe to set to 0.
* Looks for progress indicator if 'progress' non-NULL;
* if found writes value [0..65535] else write -1.
- * Return of 0 -> success, -1 -> failure */
+ * Return of 0 -> success, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
extern int sg_ll_test_unit_ready_progress(int sg_fd, int pack_id,
int * progress, int noisy,
int verbose);
@@ -253,10 +291,10 @@ extern int sg_ll_test_unit_ready_progress(int sg_fd, int pack_id,
* Note that 'veri_len' is in blocks while 'data_out_len' is in bytes.
* Returns of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Verify(10) not supported,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_MEDIUM_HARD -> medium or hardware error, no valid info,
* SG_LIB_CAT_MEDIUM_HARD_WITH_INFO -> as previous, with valid info,
- * -1 -> other failure */
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
extern int sg_ll_verify10(int sg_fd, int dpo, int bytechk, unsigned long lba,
int veri_len, void * data_out, int data_out_len,
unsigned long * infop, int noisy, int verbose);
@@ -266,7 +304,8 @@ extern int sg_ll_verify10(int sg_fd, int dpo, int bytechk, unsigned long lba,
* SG_LIB_CAT_INVALID_OP -> WRITE LONG(10) not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
* SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info
- * field written to 'offsetp', -1 -> other failure */
+ * field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
extern int sg_ll_write_long10(int sg_fd, int cor_dis, unsigned long lba,
void * data_out, int xfer_len, int * offsetp,
int noisy, int verbose);
@@ -286,8 +325,9 @@ struct sg_simple_inquiry_resp {
char revision[5];
};
-/* Yields most of first 36 bytes of a standard INQUIRY (evpd==0) response. */
-/* Returns 0 when successful, -1 -> pass through failed, -2 -> bad response */
+/* Yields most of first 36 bytes of a standard INQUIRY (evpd==0) response.
+ * Returns 0 when successful, SG_LIB_CAT_INVALID_OP -> not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other errors */
extern int sg_simple_inquiry(int sg_fd,
struct sg_simple_inquiry_resp * inq_data,
int noisy, int verbose);
@@ -311,7 +351,9 @@ extern int sg_mode_page_offset(const unsigned char * resp, int resp_len,
* and saved values respectively. Each element should be NULL or
* at least mx_mpage_len bytes long.
* Return of 0 -> overall success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
- * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure.
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready,
+ * SG_LIB_CAT_MALFORMED -> bad response, -1 -> other failure.
* If success_mask pointer is not NULL then zeroes it then sets bit 0, 1,
* 2 and/or 3 if the current, changeable, default and saved values
* respectively have been fetched. If error on current page
diff --git a/sg_dd.8 b/sg_dd.8
index 3fcfab19..3df5748a 100644
--- a/sg_dd.8
+++ b/sg_dd.8
@@ -1,4 +1,4 @@
-.TH SG_DD "8" "April 2006" "sg3_utils-1.20" SG3_UTILS
+.TH SG_DD "8" "July 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_dd \- copies data to and from files and devices. Specialised for
devices that understand the SCSI command set.
@@ -10,8 +10,8 @@ devices that understand the SCSI command set.
[\fI--help\fR] [\fI--version\fR]
.PP
[\fIblk_sgio=0|1\fR] [\fIbpt=<n>\fR] [\fIcdbsz=6|10|12|16\fR]
-[\fIcoe=0|1|2|3\fR] [\fIdio=0|1\fR] [\fIodir=0|1\fR] [\fIsync=0|1\fR]
-[\fItime=0|1\fR] [\fIverbose=<n>\fR]
+[\fIcoe=0|1|2|3\fR] [\fIdio=0|1\fR] [\fIodir=0|1\fR] [\fIretries=<n>\fR]
+[\fIsync=0|1\fR] [\fItime=0|1\fR] [\fIverbose=<n>\fR]
.SH DESCRIPTION
.\" Add any additional description here
.PP
@@ -131,6 +131,11 @@ where FLAGS is a comma separated list of one or more flags outlined below.
These flags are associated with <ofile> and are ignored when <ofile>
is /dev/null, . (period), or stdout.
.TP
+retries=<n>
+sometimes retries at the host are useful, for example when there is a
+transport error. When <n> is greater than zero then SCSI READs and WRITEs
+are retried on error <n> times. Default value is zero.
+.TP
seek=BLOCKS
start writing BLOCKS bs-sized blocks from the start of the output file.
Default is block 0 (i.e. start of file).
@@ -177,7 +182,7 @@ block <n>.
.TP
coe
continue on error. Only active for sg devices and block devices that
-have the 'sgio' flag set. To have the same action as 'coe=2', give
+have the 'sgio' flag set. To have the same action as 'coe=2', use
twice (e.g. 'iflag=coe,coe'). A medium or hardware error while reading
will re-read blocks prior to the bad block, then try to recover the bad
block, supplying zeroes if that fails, and finally reread
@@ -189,7 +194,7 @@ SCSI disks may automatically try and remap faulty sectors (see the
AWRE and ARRE in the read write error recovery mode page (perhaps
with the sdparm utility)). Errors occurring on other files types will
stop sg_dd. Error messages are sent to stderr.
-Similar to 'conv=noerror' in the
+This flag is similar to 'conv=noerror' in the
.B dd(1)
utility. See note about READ LONG below.
.TP
@@ -219,7 +224,7 @@ causes the O_EXCL flag to be added to the open of <ifile> and/or <ofile>.
.TP
fua
causes the FUA (force unit access) bit to be set in READ and/or WRITE
-SCSI commands. This only has effect with sg devices or block devices
+SCSI commands. This only has an effect with sg devices or block devices
that have the 'sgio' flag set. The 6 byte variants of the READ and
WRITE SCSI commands do not support the FUA bit.
.TP
@@ -242,10 +247,6 @@ force unit access bit. When 3, fua is set on both 'if' and 'of', when 2, fua
is set on 'if', when 1, fua is set on 'of', when 0 (default), fua is cleared
on both. See the 'fua' flag.
.SH NOTES
-The exit status of the sg_dd utility is 0 when successful, 1 for
-total failure (i.e. failed before copying data) and 2 when some
-data has been copied (prior to some problem).
-.PP
BYTES and BLOCKS may be followed by one of these multiplicative suffixes:
c C *1; w W *2; b B *512; k K KiB *1,024; KB *1,000; m M MiB *1,048,576;
MB *1,000,000 . This pattern continues for "G", "T" and "P". The latter two
@@ -350,6 +351,12 @@ SIGPIPE output the number of remaining blocks to be transferred and
the records in + out counts; then they have their default action.
SIGUSR1 causes the same information to be output yet the copy continues.
All output caused by signals is sent to stderr.
+.SH EXIT STATUS
+The exit status of sg_dd is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page. Since this utility works at a higher level
+than individual commands, and there are 'coe' and 'retries' flags,
+individual SCSI command failures do not necessary cause the process
+to exit.
.SH AUTHORS
Written by Doug Gilbert and Peter Allworth.
.SH "REPORTING BUGS"
@@ -376,7 +383,7 @@ which is found at http://www.scsifaq.org/RMiller_Tools/index.html
.PP
To change mode parameters that effect a SCSI device's caching and error
recovery see
-.B sdparm
+.B sdparm(sdparm)
.PP
See also
.B raw(8), dd(1), ddrescue(GNU)
diff --git a/sg_dd.c b/sg_dd.c
index 8f42824f..446dac0d 100644
--- a/sg_dd.c
+++ b/sg_dd.c
@@ -49,7 +49,7 @@
This version is designed for the linux kernel 2.4 and 2.6 series.
*/
-static char * version_str = "5.49 20060405";
+static char * version_str = "5.53 20060704";
#define ME "sg_dd: "
@@ -101,6 +101,8 @@ static char * version_str = "5.49 20060405";
#define MIN_RESERVED_SIZE 8192
+#define MAX_UNIT_ATTENTIONS 10
+
static int sum_of_resids = 0;
static long long dd_count = -1;
@@ -112,12 +114,14 @@ static int out_partial = 0;
static int recovered_errs = 0;
static int unrecovered_errs = 0;
static int read_longs = 0;
+static int num_retries = 0;
static int do_time = 0;
static int verbose = 0;
static int start_tm_valid = 0;
struct timeval start_tm;
static int blk_sz = 0;
+static int max_uas = MAX_UNIT_ATTENTIONS;
static const char * proc_allow_dio = "/proc/scsi/sg/allow_dio";
@@ -130,6 +134,9 @@ struct flags_t {
int excl;
int fua;
int sgio;
+ int pdt;
+ int cdbsz;
+ int retries;
};
static struct flags_t iflag;
@@ -160,12 +167,14 @@ static void print_stats(const char * str)
out_partial);
if (recovered_errs > 0)
fprintf(stderr, "%s%d recovered errors\n", str, recovered_errs);
+ if (num_retries > 0)
+ fprintf(stderr, "%s%d retries attempted\n", str, num_retries);
if (iflag.coe || oflag.coe) {
fprintf(stderr, "%s%d unrecovered errors\n", str, unrecovered_errs);
fprintf(stderr, "%s%d read_longs fetched part of unrecovered "
"read errors\n", str, read_longs);
} else if (unrecovered_errs)
- fprintf(stderr, "%s%d unrecovered read error(s)\n", str,
+ fprintf(stderr, "%s%d unrecovered error(s)\n", str,
unrecovered_errs);
}
@@ -278,6 +287,7 @@ static void usage()
" oflag comma separated list from: [append,coe,direct,dpo,"
"dsync,excl,\n"
" fua,sgio]\n"
+ " retries number of times to retry sgio errors (def: 0)\n"
" seek block position to start writing to 'of'\n"
" skip block position to start reading from 'if'\n"
" sync 0->no sync(def), 1->SYNCHRONIZE CACHE after "
@@ -288,9 +298,7 @@ static void usage()
" --version print version information then exit\n");
}
-/* Return of 0 -> success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
- * SG_LIB_CAT_MEDIA_CHANGED -> media changed, SG_LIB_CAT_ILLEGAL_REQ
- * -> bad field in cdb, -1 -> other failure */
+/* Return of 0 -> success, see sg_ll_read_capacity*() otherwise */
static int scsi_read_capacity(int sg_fd, long long * num_sect, int * sect_sz)
{
int k, res;
@@ -469,12 +477,16 @@ static int sg_build_scsi_cdb(unsigned char * cdbp, int cdb_sz,
return 0;
}
-/* 0 -> successful, 1 -> recoverable (ENOMEM), 2 -> try again (ua),
- 3 -> unrecoverable error with io_addr, -2 -> ioctl or request error,
- -1 -> other SCSI error */
+/* 0 -> successful, SG_LIB_SYNTAX_ERROR -> unable to build cdb,
+ SG_LIB_CAT_UNIT_ATTENTION -> try again,
+ SG_LIB_CAT_MEDIUM_HARD_WITH_INFO -> 'io_addrp' written to,
+ SG_LIB_CAT_MEDIUM_HARD -> no info field,
+ SG_LIB_CAT_NOT_READY,
+ -2 -> ENOMEM
+ -1 other errors */
static int sg_read_low(int sg_fd, unsigned char * buff, int blocks,
- long long from_block, int bs, int cdbsz, int fua,
- int dpo, int pdt, int * diop,
+ long long from_block, int bs,
+ const struct flags_t * ifp, int * diop,
unsigned long long * io_addrp)
{
unsigned char rdCmd[MAX_SCSI_CDBSZ];
@@ -482,15 +494,16 @@ static int sg_read_low(int sg_fd, unsigned char * buff, int blocks,
struct sg_io_hdr io_hdr;
int res, k, info_valid;
- if (sg_build_scsi_cdb(rdCmd, cdbsz, blocks, from_block, 0, fua, dpo)) {
+ if (sg_build_scsi_cdb(rdCmd, ifp->cdbsz, blocks, from_block, 0,
+ ifp->fua, ifp->dpo)) {
fprintf(stderr, ME "bad rd cdb build, from_block=%lld, blocks=%d\n",
from_block, blocks);
- return -2;
+ return SG_LIB_SYNTAX_ERROR;
}
memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
io_hdr.interface_id = 'S';
- io_hdr.cmd_len = cdbsz;
+ io_hdr.cmd_len = ifp->cdbsz;
io_hdr.cmdp = rdCmd;
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
io_hdr.dxfer_len = bs * blocks;
@@ -504,7 +517,7 @@ static int sg_read_low(int sg_fd, unsigned char * buff, int blocks,
if (verbose > 2) {
fprintf(stderr, " read cdb: ");
- for (k = 0; k < cdbsz; ++k)
+ for (k = 0; k < ifp->cdbsz; ++k)
fprintf(stderr, "%02x ", rdCmd[k]);
fprintf(stderr, "\n");
}
@@ -512,13 +525,14 @@ static int sg_read_low(int sg_fd, unsigned char * buff, int blocks,
;
if (res < 0) {
if (ENOMEM == errno)
- return 1;
+ return -2;
perror("reading (SG_IO) on sg device, error");
- return -2;
+ return -1;
}
if (verbose > 2)
fprintf(stderr, " duration=%u ms\n", io_hdr.duration);
- switch (sg_err_category3(&io_hdr)) {
+ res = sg_err_category3(&io_hdr);
+ switch (res) {
case SG_LIB_CAT_CLEAN:
break;
case SG_LIB_CAT_RECOVERED:
@@ -536,28 +550,33 @@ static int sg_read_low(int sg_fd, unsigned char * buff, int blocks,
sg_chk_n_print3("reading", &io_hdr, verbose > 1);
}
break;
- case SG_LIB_CAT_MEDIA_CHANGED:
- if (verbose > 1)
- sg_chk_n_print3("reading", &io_hdr, 1);
- return 2;
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ sg_chk_n_print3("reading", &io_hdr, verbose > 1);
+ return res;
case SG_LIB_CAT_MEDIUM_HARD:
if (verbose > 1)
- sg_chk_n_print3("reading", &io_hdr, 1);
+ sg_chk_n_print3("reading", &io_hdr, verbose > 1);
++unrecovered_errs;
info_valid = sg_get_sense_info_fld(io_hdr.sbp, io_hdr.sb_len_wr,
io_addrp);
- if ((info_valid) || ((5 == pdt) && (*io_addrp > 0)))
- return 3; /* MMC devices don't necessarily set VALID bit */
+ /* MMC devices don't necessarily set VALID bit */
+ if ((info_valid) || ((5 == ifp->pdt) && (*io_addrp > 0)))
+ return SG_LIB_CAT_MEDIUM_HARD_WITH_INFO;
else {
fprintf(stderr, "Medium or hardware error but no lba of failure"
" given\n");
- return -1;
+ return res;
}
break;
+ case SG_LIB_CAT_NOT_READY:
+ ++unrecovered_errs;
+ if (verbose > 0)
+ sg_chk_n_print3("reading", &io_hdr, verbose > 1);
+ return res;
default:
++unrecovered_errs;
sg_chk_n_print3("reading", &io_hdr, verbose > 1);
- return -1;
+ return res;
}
if (diop && *diop &&
((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
@@ -566,46 +585,80 @@ static int sg_read_low(int sg_fd, unsigned char * buff, int blocks,
return 0;
}
-/* Returns >= 0 -> number of blocks read, -1 -> unrecoverable error,
- -2 -> recoverable (ENOMEM) */
+/* 0 -> successful, SG_LIB_SYNTAX_ERROR -> unable to build cdb,
+ SG_LIB_CAT_UNIT_ATTENTION -> try again, SG_LIB_CAT_NOT_READY,
+ SG_LIB_CAT_MEDIUM_HARD, -2 -> ENOMEM, -1 other errors */
static int sg_read(int sg_fd, unsigned char * buff, int blocks,
- long long from_block, int bs, int cdbsz, int fua,
- int dpo, int * diop, int pdt)
+ long long from_block, int bs, struct flags_t * ifp,
+ int * diop, int * blks_readp)
{
unsigned long long io_addr;
long long lba;
- int res, blks, cont, xferred;
+ int res, blks, repeat, xferred;
unsigned char * bp;
+ int retries_tmp;
+ int ret = 0;
+ retries_tmp = ifp->retries;
for (xferred = 0, blks = blocks, lba = from_block, bp = buff;
blks > 0; blks = blocks - xferred) {
io_addr = 0;
- cont = 0;
- res = sg_read_low(sg_fd, bp, blks, lba, bs, cdbsz, fua, dpo, pdt,
- diop, &io_addr);
+ repeat = 0;
+ res = sg_read_low(sg_fd, bp, blks, lba, bs, ifp, diop, &io_addr);
switch (res) {
case 0:
- return xferred + blks;
- case 1:
- return -2;
- case 2:
- fprintf(stderr,
- "Unit attention, media changed, continuing (r)\n");
- cont = 1;
+ if (blks_readp)
+ *blks_readp = xferred + blks;
+ return 0;
+ case -2: /* ENOMEM */
+ return res;
+ case SG_LIB_CAT_NOT_READY:
+ fprintf(stderr, "Device (r) not ready\n");
+ return res;
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ if (--max_uas > 0) {
+ fprintf(stderr, "Unit attention, continuing (r)\n");
+ repeat = 1;
+ } else {
+ fprintf(stderr, "Unit attention, too many (r)\n");
+ return res;
+ }
break;
- case -1:
+ case SG_LIB_CAT_MEDIUM_HARD_WITH_INFO:
+ if (retries_tmp > 0) {
+ fprintf(stderr, ">>> retrying a sgio read, lba=0x%llx\n",
+ (unsigned long long)lba);
+ --retries_tmp;
+ ++num_retries;
+ if (unrecovered_errs > 0)
+ --unrecovered_errs;
+ repeat = 1;
+ }
+ ret = SG_LIB_CAT_MEDIUM_HARD;
+ break; /* unrecovered read error at lba=io_addr */
+ case SG_LIB_SYNTAX_ERROR:
+ ifp->coe = 0;
+ ret = res;
goto err_out;
- case -2:
- iflag.coe = 0;
+ case -1:
+ ret = res;
goto err_out;
- case 3:
- break; /* unrecovered read error at lba=io_addr */
+ case SG_LIB_CAT_MEDIUM_HARD:
default:
- fprintf(stderr, ">> unexpected result=%d from sg_read_low()\n",
- res);
- return -1;
+ if (retries_tmp > 0) {
+ fprintf(stderr, ">>> retrying a sgio read, lba=0x%llx\n",
+ (unsigned long long)lba);
+ --retries_tmp;
+ ++num_retries;
+ if (unrecovered_errs > 0)
+ --unrecovered_errs;
+ repeat = 1;
+ break;
+ }
+ ret = res;
+ goto err_out;
}
- if (cont)
+ if (repeat)
continue;
if ((io_addr < (unsigned long long)lba) ||
(io_addr >= (unsigned long long)(lba + blks))) {
@@ -617,41 +670,55 @@ static int sg_read(int sg_fd, unsigned char * buff, int blocks,
}
blks = (int)(io_addr - (unsigned long long)lba);
if (blks > 0) {
- res = sg_read_low(sg_fd, bp, blks, lba, bs, cdbsz, fua, dpo,
- pdt, diop, &io_addr);
+ if (verbose)
+ fprintf(stderr, " partial read of %d blocks prior to "
+ "medium error\n", blks);
+ res = sg_read_low(sg_fd, bp, blks, lba, bs, ifp, diop, &io_addr);
switch (res) {
case 0:
break;
- case 1:
+ case -1:
+ ifp->coe = 0;
+ ret = res;
+ goto err_out;
+ case -2:
fprintf(stderr, "ENOMEM again, unexpected (r)\n");
return -1;
- case 2:
+ case SG_LIB_CAT_NOT_READY:
+ fprintf(stderr, "device (r) not ready\n");
+ return res;
+ case SG_LIB_CAT_UNIT_ATTENTION:
fprintf(stderr,
- "Unit attention, media changed, unexpected (r)\n");
- return -1;
- case -2:
- iflag.coe = 0;
- goto err_out;
- case -1: case 3:
+ "Unit attention, unexpected (r)\n");
+ return res;
+ case SG_LIB_CAT_MEDIUM_HARD_WITH_INFO:
+ case SG_LIB_CAT_MEDIUM_HARD:
+ ret = SG_LIB_CAT_MEDIUM_HARD;
goto err_out;
+ case SG_LIB_SYNTAX_ERROR:
default:
fprintf(stderr, ">> unexpected result=%d from "
"sg_read_low() 2\n", res);
- return -1;
+ ret = res;
+ goto err_out;
}
}
xferred += blks;
- if (! iflag.coe)
- return xferred; /* give up at block before problem unless 'coe' */
+ if (0 == ifp->coe) {
+ /* give up at block before problem unless 'coe' */
+ if (blks_readp)
+ *blks_readp = xferred + blks;
+ return ret;
+ }
if (bs < 32) {
fprintf(stderr, ">> bs=%d too small for read_long\n", bs);
return -1; /* nah, block size can't be that small */
}
bp += (blks * bs);
lba += blks;
- if ((0 != pdt) || (iflag.coe < 2)) {
+ if ((0 != ifp->pdt) || (ifp->coe < 2)) {
fprintf(stderr, ">> unrecovered read error at blk=%lld, "
- "pdt=%d, use zeros\n", lba, pdt);
+ "pdt=%d, use zeros\n", lba, ifp->pdt);
memset(bp, 0, bs);
} else if (io_addr < UINT_MAX) {
unsigned char * buffp;
@@ -662,7 +729,7 @@ static int sg_read(int sg_fd, unsigned char * buff, int blocks,
fprintf(stderr, ">> heap problems\n");
return -1;
}
- corrct = (iflag.coe > 2) ? 1 : 0;
+ corrct = (ifp->coe > 2) ? 1 : 0;
res = sg_ll_read_long10(sg_fd, corrct, lba, buffp, bs + 8,
&offset, 1, verbose);
ok = 0;
@@ -691,13 +758,19 @@ static int sg_read(int sg_fd, unsigned char * buff, int blocks,
"read_long(10)\n", r);
break;
case SG_LIB_CAT_INVALID_OP:
- fprintf(stderr, ">> read_long(10) not supported\n");
+ fprintf(stderr, ">> read_long(10); not supported\n");
break;
case SG_LIB_CAT_ILLEGAL_REQ:
- fprintf(stderr, ">> read_long(10) bad cdb field\n");
+ fprintf(stderr, ">> read_long(10): bad cdb field\n");
+ break;
+ case SG_LIB_CAT_NOT_READY:
+ fprintf(stderr, ">> read_long(10): device not ready\n");
+ break;
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ fprintf(stderr, ">> read_long(10): unit attention\n");
break;
default:
- fprintf(stderr, ">> read_long(10) problem\n");
+ fprintf(stderr, ">> read_long(10): problem (%d)\n", res);
break;
}
if (ok)
@@ -715,22 +788,28 @@ static int sg_read(int sg_fd, unsigned char * buff, int blocks,
bp += bs;
++lba;
}
- return xferred;
+ if (blks_readp)
+ *blks_readp = xferred;
+ return 0;
err_out:
- if (iflag.coe) {
+ if (ifp->coe) {
memset(bp, 0, bs * blks);
fprintf(stderr, ">> unable to read at blk=%lld for "
"%d bytes, use zeros\n", lba, bs * blks);
- return xferred + blks; /* fudge success */
+ /* fudge success */
+ if (blks_readp)
+ *blks_readp = xferred + blks;
+ return ret;
} else
- return -1;
+ return ret ? ret : -1;
}
-/* 0 -> successful, -1 -> unrecoverable error, -2 -> recoverable (ENOMEM),
- -3 -> try again (media changed unit attention) */
+/* 0 -> successful, SG_LIB_SYNTAX_ERROR -> unable to build cdb,
+ SG_LIB_CAT_NOT_READY, SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_MEDIUM_HARD,
+ -2 -> recoverable (ENOMEM), -1 -> unrecoverable error + others */
static int sg_write(int sg_fd, unsigned char * buff, int blocks,
- long long to_block, int bs, int cdbsz, int fua, int dpo,
+ long long to_block, int bs, const struct flags_t * ofp,
int * diop)
{
unsigned char wrCmd[MAX_SCSI_CDBSZ];
@@ -739,15 +818,16 @@ static int sg_write(int sg_fd, unsigned char * buff, int blocks,
int res, k, info_valid;
unsigned long long io_addr = 0;
- if (sg_build_scsi_cdb(wrCmd, cdbsz, blocks, to_block, 1, fua, dpo)) {
+ if (sg_build_scsi_cdb(wrCmd, ofp->cdbsz, blocks, to_block, 1, ofp->fua,
+ ofp->dpo)) {
fprintf(stderr, ME "bad wr cdb build, to_block=%lld, blocks=%d\n",
to_block, blocks);
- return -1;
+ return SG_LIB_SYNTAX_ERROR;
}
memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
io_hdr.interface_id = 'S';
- io_hdr.cmd_len = cdbsz;
+ io_hdr.cmd_len = ofp->cdbsz;
io_hdr.cmdp = wrCmd;
io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
io_hdr.dxfer_len = bs * blocks;
@@ -761,7 +841,7 @@ static int sg_write(int sg_fd, unsigned char * buff, int blocks,
if (verbose > 2) {
fprintf(stderr, " write cdb: ");
- for (k = 0; k < cdbsz; ++k)
+ for (k = 0; k < ofp->cdbsz; ++k)
fprintf(stderr, "%02x ", wrCmd[k]);
fprintf(stderr, "\n");
}
@@ -776,7 +856,8 @@ static int sg_write(int sg_fd, unsigned char * buff, int blocks,
if (verbose > 2)
fprintf(stderr, " duration=%u ms\n", io_hdr.duration);
- switch (sg_err_category3(&io_hdr)) {
+ res = sg_err_category3(&io_hdr);
+ switch (res) {
case SG_LIB_CAT_CLEAN:
break;
case SG_LIB_CAT_RECOVERED:
@@ -794,19 +875,24 @@ static int sg_write(int sg_fd, unsigned char * buff, int blocks,
sg_chk_n_print3("writing", &io_hdr, verbose > 1);
}
break;
- case SG_LIB_CAT_MEDIA_CHANGED:
- if (verbose > 1)
- sg_chk_n_print3("writing", &io_hdr, 1);
- return -3;
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ sg_chk_n_print3("writing", &io_hdr, verbose > 1);
+ return res;
+ case SG_LIB_CAT_NOT_READY:
+ ++unrecovered_errs;
+ fprintf(stderr, "device not ready (w)\n");
+ return res;
+ case SG_LIB_CAT_MEDIUM_HARD:
default:
sg_chk_n_print3("writing", &io_hdr, verbose > 1);
- if (oflag.coe) {
+ ++unrecovered_errs;
+ if (ofp->coe) {
fprintf(stderr, ">> ignored errors for out blk=%lld for "
"%d bytes\n", to_block, bs * blocks);
return 0; /* fudge success */
}
else
- return -1;
+ return res;
}
if (diop && *diop &&
((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
@@ -900,13 +986,11 @@ int main(int argc, char * argv[])
int out_type = FT_OTHER;
int dio = 0;
int dio_incomplete = 0;
- int scsi_cdbsz_in = DEF_SCSI_CDBSZ;
- int scsi_cdbsz_out = DEF_SCSI_CDBSZ;
int cdbsz_given = 0;
int do_sync = 0;
int verb = 0;
- int res, k, t, buf_sz, dio_tmp, flags, fl;
- int infd, outfd, blocks, in_pdt, out_pdt;
+ int res, k, t, buf_sz, dio_tmp, flags, fl, first;
+ int infd, outfd, blocks, retries_tmp, blks_read;
unsigned char * wrkBuff;
unsigned char * wrkPos;
long long in_num_sect = -1;
@@ -915,14 +999,17 @@ int main(int argc, char * argv[])
char ebuff[EBUFF_SZ];
int blocks_per;
struct sg_simple_inquiry_resp sir;
+ int ret = 0;
inf[0] = '\0';
outf[0] = '\0';
+ iflag.cdbsz = DEF_SCSI_CDBSZ;
+ oflag.cdbsz = DEF_SCSI_CDBSZ;
if (argc < 2) {
fprintf(stderr,
"Can't have both 'if' as stdin _and_ 'of' as stdout\n");
fprintf(stderr, "For more information use '--help'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
for (k = 1; k < argc; k++) {
@@ -946,18 +1033,18 @@ int main(int argc, char * argv[])
bpt = sg_get_num(buf);
if (-1 == bpt) {
fprintf(stderr, ME "bad argument to 'bpt'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
bpt_given = 1;
} else if (0 == strcmp(key, "bs")) {
blk_sz = sg_get_num(buf);
if (-1 == blk_sz) {
fprintf(stderr, ME "bad argument to 'bs'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strcmp(key, "cdbsz")) {
- scsi_cdbsz_in = sg_get_num(buf);
- scsi_cdbsz_out = scsi_cdbsz_in;
+ iflag.cdbsz = sg_get_num(buf);
+ oflag.cdbsz = iflag.cdbsz;
cdbsz_given = 1;
} else if (0 == strcmp(key, "coe")) {
iflag.coe = sg_get_num(buf);
@@ -966,7 +1053,7 @@ int main(int argc, char * argv[])
dd_count = sg_get_llnum(buf);
if (-1LL == dd_count) {
fprintf(stderr, ME "bad argument to 'count'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strcmp(key, "dio"))
dio = sg_get_num(buf);
@@ -979,13 +1066,13 @@ int main(int argc, char * argv[])
else if (strcmp(key, "if") == 0) {
if ('\0' != inf[0]) {
fprintf(stderr, "Second 'if=' argument??\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
} else
strncpy(inf, buf, INOUTF_SZ);
} else if (0 == strcmp(key, "iflag")) {
if (process_flags(buf, &iflag)) {
fprintf(stderr, ME "bad argument to 'iflag'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strcmp(key, "obs"))
obs = sg_get_num(buf);
@@ -995,25 +1082,32 @@ int main(int argc, char * argv[])
} else if (strcmp(key, "of") == 0) {
if ('\0' != outf[0]) {
fprintf(stderr, "Second 'of=' argument??\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
} else
strncpy(outf, buf, INOUTF_SZ);
} else if (0 == strcmp(key, "oflag")) {
if (process_flags(buf, &oflag)) {
fprintf(stderr, ME "bad argument to 'oflag'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ } else if (0 == strcmp(key, "retries")) {
+ iflag.retries = sg_get_num(buf);
+ oflag.retries = iflag.retries;
+ if (-1 == iflag.retries) {
+ fprintf(stderr, ME "bad argument to 'retries'\n");
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strcmp(key, "seek")) {
seek = sg_get_llnum(buf);
if (-1LL == seek) {
fprintf(stderr, ME "bad argument to 'seek'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strcmp(key, "skip")) {
skip = sg_get_llnum(buf);
if (-1LL == skip) {
fprintf(stderr, ME "bad argument to 'skip'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strcmp(key, "sync"))
do_sync = sg_get_num(buf);
@@ -1032,7 +1126,7 @@ int main(int argc, char * argv[])
} else {
fprintf(stderr, "Unrecognized option '%s'\n", key);
fprintf(stderr, "For more information use '--help'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (blk_sz <= 0) {
@@ -1043,19 +1137,19 @@ int main(int argc, char * argv[])
if ((ibs && (ibs != blk_sz)) || (obs && (obs != blk_sz))) {
fprintf(stderr, "If 'ibs' or 'obs' given must be same as 'bs'\n");
fprintf(stderr, "For more information use '--help'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if ((skip < 0) || (seek < 0)) {
fprintf(stderr, "skip and seek cannot be negative\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if ((oflag.append > 0) && (seek > 0)) {
fprintf(stderr, "Can't use both append and seek switches\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (bpt < 1) {
fprintf(stderr, "bpt must be greater than 0\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
/* defaulting transfer size to 128*2048 for CD/DVDs is too large
for the block layer in lk 2.6 and results in an EIO on the
@@ -1073,8 +1167,8 @@ int main(int argc, char * argv[])
infd = STDIN_FILENO;
outfd = STDOUT_FILENO;
- in_pdt = -1;
- out_pdt = -1;
+ iflag.pdt = -1;
+ oflag.pdt = -1;
if (inf[0] && ('-' != inf[0])) {
in_type = dd_filetype(inf);
if (verbose)
@@ -1082,13 +1176,13 @@ int main(int argc, char * argv[])
dd_filetype_str(in_type, ebuff));
if (FT_ERROR & in_type) {
fprintf(stderr, ME "unable access %s\n", inf);
- return 1;
+ return SG_LIB_FILE_ERROR;
} else if ((FT_BLOCK & in_type) && iflag.sgio)
in_type |= FT_SG;
if (FT_ST & in_type) {
fprintf(stderr, ME "unable to use scsi tape device %s\n", inf);
- return 1;
+ return SG_LIB_FILE_ERROR;
} else if (FT_SG & in_type) {
flags = O_NONBLOCK;
if (iflag.direct)
@@ -1104,7 +1198,7 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ,
ME "could not open %s for sg reading", inf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
}
if (verbose)
@@ -1112,12 +1206,12 @@ int main(int argc, char * argv[])
fl | flags);
if (sg_simple_inquiry(infd, &sir, 0, verb)) {
fprintf(stderr, "INQUIRY failed on %s\n", inf);
- return -1;
+ return SG_LIB_CAT_OTHER;
}
- in_pdt = sir.peripheral_type;
+ iflag.pdt = sir.peripheral_type;
if (verbose)
fprintf(stderr, " %s: %.8s %.16s %.4s [pdt=%d]\n",
- inf, sir.vendor, sir.product, sir.revision, in_pdt);
+ inf, sir.vendor, sir.product, sir.revision, iflag.pdt);
if (! (FT_BLOCK & in_type)) {
t = blk_sz * bpt;
res = ioctl(infd, SG_SET_RESERVED_SIZE, &t);
@@ -1130,7 +1224,7 @@ int main(int argc, char * argv[])
" device\n");
else
fprintf(stderr, ME "sg driver prior to 3.x.y\n");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
}
}
@@ -1147,7 +1241,7 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ,
ME "could not open %s for reading", inf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
else {
if (verbose)
@@ -1161,7 +1255,7 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ, ME "couldn't skip to "
"required position on %s", inf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (verbose)
fprintf(stderr, " >> skip: llseek SEEK_SET, "
@@ -1182,7 +1276,7 @@ int main(int argc, char * argv[])
if (FT_ST & out_type) {
fprintf(stderr, ME "unable to use scsi tape device %s\n", outf);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
else if (FT_SG & out_type) {
flags = O_RDWR | O_NONBLOCK;
@@ -1196,19 +1290,19 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ,
ME "could not open %s for sg writing", outf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (verbose)
fprintf(stderr, " open output(sg_io), flags=0x%x\n",
flags);
if (sg_simple_inquiry(outfd, &sir, 0, verb)) {
fprintf(stderr, "INQUIRY failed on %s\n", outf);
- return -1;
+ return SG_LIB_CAT_OTHER;
}
- out_pdt = sir.peripheral_type;
+ oflag.pdt = sir.peripheral_type;
if (verbose)
fprintf(stderr, " %s: %.8s %.16s %.4s [pdt=%d]\n",
- outf, sir.vendor, sir.product, sir.revision, out_pdt);
+ outf, sir.vendor, sir.product, sir.revision, oflag.pdt);
if (! (FT_BLOCK & out_type)) {
t = blk_sz * bpt;
res = ioctl(outfd, SG_SET_RESERVED_SIZE, &t);
@@ -1217,7 +1311,7 @@ int main(int argc, char * argv[])
res = ioctl(outfd, SG_GET_VERSION_NUM, &t);
if ((res < 0) || (t < 30000)) {
fprintf(stderr, ME "sg driver prior to 3.x.y\n");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
}
}
@@ -1238,7 +1332,7 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ,
ME "could not open %s for writing", outf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
}
else {
@@ -1253,7 +1347,7 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ,
ME "could not open %s for raw writing", outf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
}
if (verbose)
@@ -1266,7 +1360,7 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ,
ME "couldn't seek to required position on %s", outf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (verbose)
fprintf(stderr, " >> seek: llseek SEEK_SET, "
@@ -1278,22 +1372,25 @@ int main(int argc, char * argv[])
fprintf(stderr,
"Can't have both 'if' as stdin _and_ 'of' as stdout\n");
fprintf(stderr, "For more information use '--help'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if ((dd_count < 0) || ((verbose > 0) && (0 == dd_count))) {
in_num_sect = -1;
if (FT_SG & in_type) {
res = scsi_read_capacity(infd, &in_num_sect, &in_sect_sz);
- if (SG_LIB_CAT_MEDIA_CHANGED == res) {
+ if (SG_LIB_CAT_UNIT_ATTENTION == res) {
fprintf(stderr,
- "Unit attention, media changed(in), continuing\n");
+ "Unit attention (readcap in), continuing\n");
res = scsi_read_capacity(infd, &in_num_sect, &in_sect_sz);
}
if (0 != res) {
if (res == SG_LIB_CAT_INVALID_OP)
fprintf(stderr, "read capacity not supported on %s\n",
inf);
+ else if (res == SG_LIB_CAT_NOT_READY)
+ fprintf(stderr, "read capacity failed on %s - not "
+ "ready\n", inf);
else
fprintf(stderr, "Unable to read capacity on %s\n", inf);
in_num_sect = -1;
@@ -1318,9 +1415,9 @@ int main(int argc, char * argv[])
out_num_sect = -1;
if (FT_SG & out_type) {
res = scsi_read_capacity(outfd, &out_num_sect, &out_sect_sz);
- if (SG_LIB_CAT_MEDIA_CHANGED == res) {
+ if (SG_LIB_CAT_UNIT_ATTENTION == res) {
fprintf(stderr,
- "Unit attention, media changed(out), continuing\n");
+ "Unit attention (readcap out), continuing\n");
res = scsi_read_capacity(outfd, &out_num_sect, &out_sect_sz);
}
if (0 != res) {
@@ -1370,20 +1467,20 @@ int main(int argc, char * argv[])
if (dd_count < 0) {
fprintf(stderr, "Couldn't calculate count, please give one\n");
- return 1;
+ return SG_LIB_CAT_OTHER;
}
if (! cdbsz_given) {
- if ((FT_SG & in_type) && (MAX_SCSI_CDBSZ != scsi_cdbsz_in) &&
+ if ((FT_SG & in_type) && (MAX_SCSI_CDBSZ != iflag.cdbsz) &&
(((dd_count + skip) > UINT_MAX) || (bpt > USHRT_MAX))) {
fprintf(stderr, "Note: SCSI command size increased to 16 bytes "
"(for 'if')\n");
- scsi_cdbsz_in = MAX_SCSI_CDBSZ;
+ iflag.cdbsz = MAX_SCSI_CDBSZ;
}
- if ((FT_SG & out_type) && (MAX_SCSI_CDBSZ != scsi_cdbsz_out) &&
+ if ((FT_SG & out_type) && (MAX_SCSI_CDBSZ != oflag.cdbsz) &&
(((dd_count + seek) > UINT_MAX) || (bpt > USHRT_MAX))) {
fprintf(stderr, "Note: SCSI command size increased to 16 bytes "
"(for 'of')\n");
- scsi_cdbsz_out = MAX_SCSI_CDBSZ;
+ oflag.cdbsz = MAX_SCSI_CDBSZ;
}
}
@@ -1393,7 +1490,7 @@ int main(int argc, char * argv[])
wrkBuff = malloc(blk_sz * bpt + psz);
if (0 == wrkBuff) {
fprintf(stderr, "Not enough user memory for raw\n");
- return 1;
+ return SG_LIB_CAT_OTHER;
}
wrkPos = (unsigned char *)(((unsigned long)wrkBuff + psz - 1) &
(~(psz - 1)));
@@ -1402,7 +1499,7 @@ int main(int argc, char * argv[])
wrkBuff = malloc(blk_sz * bpt);
if (0 == wrkBuff) {
fprintf(stderr, "Not enough user memory\n");
- return 1;
+ return SG_LIB_CAT_OTHER;
}
wrkPos = wrkBuff;
}
@@ -1420,16 +1517,17 @@ int main(int argc, char * argv[])
}
req_count = dd_count;
- /* main loop that does the copy ... */
+ /* <<< main loop that does the copy >>> */
while (dd_count > 0) {
blocks = (dd_count > blocks_per) ? blocks_per : dd_count;
if (FT_SG & in_type) {
dio_tmp = dio;
- res = sg_read(infd, wrkPos, blocks, skip, blk_sz, scsi_cdbsz_in,
- iflag.fua, iflag.dpo, &dio_tmp, in_pdt);
+ res = sg_read(infd, wrkPos, blocks, skip, blk_sz, &iflag,
+ &dio_tmp, &blks_read);
if (-2 == res) { /* ENOMEM, find what's available+try that */
if (ioctl(infd, SG_GET_RESERVED_SIZE, &buf_sz) < 0) {
perror("RESERVED_SIZE ioctls failed");
+ ret = res;
break;
}
if (buf_sz < MIN_RESERVED_SIZE)
@@ -1440,19 +1538,19 @@ int main(int argc, char * argv[])
fprintf(stderr, "Reducing read to %d blocks per "
"loop\n", blocks_per);
res = sg_read(infd, wrkPos, blocks, skip, blk_sz,
- scsi_cdbsz_in, iflag.fua, iflag.dpo,
- &dio_tmp, in_pdt);
+ &iflag, &dio_tmp, &blks_read);
}
}
- if (res < 0) {
+ if (res) {
fprintf(stderr, "sg_read failed,%s at or after lba=%lld "
"[0x%llx]\n",
((-2 == res) ? " try reducing bpt," : ""), skip, skip);
+ ret = res;
break;
} else {
- if (res < blocks) {
+ if (blks_read < blocks) {
dd_count = 0; /* force exit after write */
- blocks = res;
+ blocks = blks_read;
}
in_full += blocks;
if (dio && (0 == dio_tmp))
@@ -1469,6 +1567,7 @@ int main(int argc, char * argv[])
if (res < 0) {
snprintf(ebuff, EBUFF_SZ, ME "reading, skip=%lld ", skip);
perror(ebuff);
+ ret = -1;
break;
}
else if (res < blocks * blk_sz) {
@@ -1487,35 +1586,54 @@ int main(int argc, char * argv[])
if (FT_SG & out_type) {
dio_tmp = dio;
- res = sg_write(outfd, wrkPos, blocks, seek, blk_sz,
- scsi_cdbsz_out, oflag.fua, oflag.dpo, &dio_tmp);
- if (-2 == res) { /* ENOMEM, find what's available+try that */
- if (ioctl(outfd, SG_GET_RESERVED_SIZE, &buf_sz) < 0) {
- perror("RESERVED_SIZE ioctls failed");
+ retries_tmp = oflag.retries;
+ first = 1;
+ while (1) {
+ ret = sg_write(outfd, wrkPos, blocks, seek, blk_sz,
+ &oflag, &dio_tmp);
+ if (0 == ret)
break;
- }
- if (buf_sz < MIN_RESERVED_SIZE)
- buf_sz = MIN_RESERVED_SIZE;
- blocks_per = (buf_sz + blk_sz - 1) / blk_sz;
- if (blocks_per < blocks) {
- blocks = blocks_per;
- fprintf(stderr,
- "Reducing write to %d blocks per loop\n", blocks);
- res = sg_write(outfd, wrkPos, blocks, seek, blk_sz,
- scsi_cdbsz_out, oflag.fua, oflag.dpo,
- &dio_tmp);
- }
- }
- else if (-3 == res) {
- fprintf(stderr,
- "Unit attention, media changed, continuing (w)\n");
- res = sg_write(outfd, wrkPos, blocks, seek, blk_sz,
- scsi_cdbsz_out, oflag.fua, oflag.dpo,
- &dio_tmp);
+ if ((SG_LIB_CAT_NOT_READY == ret) ||
+ (SG_LIB_SYNTAX_ERROR == ret))
+ break;
+ else if ((-2 == ret) && first) {
+ /* ENOMEM: find what's available and try that */
+ if (ioctl(outfd, SG_GET_RESERVED_SIZE, &buf_sz) < 0) {
+ perror("RESERVED_SIZE ioctls failed");
+ break;
+ }
+ if (buf_sz < MIN_RESERVED_SIZE)
+ buf_sz = MIN_RESERVED_SIZE;
+ blocks_per = (buf_sz + blk_sz - 1) / blk_sz;
+ if (blocks_per < blocks) {
+ blocks = blocks_per;
+ fprintf(stderr, "Reducing write to %d blocks per "
+ "loop\n", blocks);
+ } else
+ break;
+ } else if ((SG_LIB_CAT_UNIT_ATTENTION == ret) && first) {
+ if (--max_uas > 0)
+ fprintf(stderr, "Unit attention, continuing (w)\n");
+ else {
+ fprintf(stderr, "Unit attention, too many (w)\n");
+ break;
+ }
+ } else if (ret < 0)
+ break;
+ else if (retries_tmp > 0) {
+ fprintf(stderr, ">>> retrying a sgio write, "
+ "lba=0x%llx\n", (unsigned long long)seek);
+ --retries_tmp;
+ ++num_retries;
+ if (unrecovered_errs > 0)
+ --unrecovered_errs;
+ } else
+ break;
+ first = 0;
}
- if (res < 0) {
+ if (0 != ret) {
fprintf(stderr, "sg_write failed,%s seek=%lld\n",
- ((-2 == res) ? " try reducing bpt," : ""), seek);
+ ((-2 == ret) ? " try reducing bpt," : ""), seek);
break;
}
else {
@@ -1523,8 +1641,7 @@ int main(int argc, char * argv[])
if (dio && (0 == dio_tmp))
dio_incomplete++;
}
- }
- else if (FT_DEV_NULL & out_type)
+ } else if (FT_DEV_NULL & out_type)
out_full += blocks; /* act as if written out without error */
else {
while (((res = write(outfd, wrkPos, blocks * blk_sz)) < 0)
@@ -1536,6 +1653,7 @@ int main(int argc, char * argv[])
if (res < 0) {
snprintf(ebuff, EBUFF_SZ, ME "writing, seek=%lld ", seek);
perror(ebuff);
+ ret = -1;
break;
}
else if (res < blocks * blk_sz) {
@@ -1544,6 +1662,7 @@ int main(int argc, char * argv[])
out_full += blocks;
if ((res % blk_sz) > 0)
out_partial++;
+ ret = -1;
break;
}
else
@@ -1562,9 +1681,9 @@ int main(int argc, char * argv[])
if (FT_SG & out_type) {
fprintf(stderr, ">> Synchronizing cache on %s\n", outf);
res = sg_ll_sync_cache_10(outfd, 0, 0, 0, 0, 0, 0, 0);
- if (2 == res) {
- fprintf(stderr,
- "Unit attention, media changed(in), continuing\n");
+ if (SG_LIB_CAT_UNIT_ATTENTION == res) {
+ fprintf(stderr, "Unit attention (out, sync cache), "
+ "continuing\n");
res = sg_ll_sync_cache_10(outfd, 0, 0, 0, 0, 0, 0, 0);
}
if (0 != res)
@@ -1576,10 +1695,10 @@ int main(int argc, char * argv[])
close(infd);
if (! ((STDOUT_FILENO == outfd) || (FT_DEV_NULL & out_type)))
close(outfd);
- res = 0;
if (0 != dd_count) {
fprintf(stderr, "Some error occurred,");
- res = 2;
+ if (0 == ret)
+ ret = SG_LIB_CAT_OTHER;
}
print_stats("");
if (dio_incomplete) {
@@ -1600,5 +1719,5 @@ int main(int argc, char * argv[])
if (sum_of_resids)
fprintf(stderr, ">> Non-zero sum of residual counts=%d\n",
sum_of_resids);
- return res;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_emc_trespass.8 b/sg_emc_trespass.8
index 487ab1dd..065bab94 100644
--- a/sg_emc_trespass.8
+++ b/sg_emc_trespass.8
@@ -1,4 +1,4 @@
-.TH SG_EMC_TRESPASS "8" "Oct 2004" "sg3_utils-1.09" SG3_UTILS
+.TH SG_EMC_TRESPASS "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_emc_trespass \- Changes ownership of a LUN from another
Service-Processor to this one.
@@ -36,6 +36,9 @@ 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 "sg_start 0 /dev/sda"
will work in the 2.6 series kernels.
+.SH EXIT STATUS
+The exit status of sg_emc_trespass is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHOR
Written by Lars Marowsky-Bree, based on sg_start.
.SH "REPORTING BUGS"
diff --git a/sg_emc_trespass.c b/sg_emc_trespass.c
index fc1caeb7..de3e0074 100644
--- a/sg_emc_trespass.c
+++ b/sg_emc_trespass.c
@@ -25,13 +25,13 @@
* any later version.
*/
-static char * version_str = "0.12 20051220";
+static char * version_str = "0.13 20060623";
static int debug = 0;
#define TRESPASS_PAGE 0x22
-static void do_trespass(int fd, int hr, int short_cmd)
+static int do_trespass(int fd, int hr, int short_cmd)
{
unsigned char long_trespass_pg[] =
{ 0, 0, 0, 0, 0, 0, 0, 0x00,
@@ -75,12 +75,19 @@ static void do_trespass(int fd, int hr, int short_cmd)
"'-s' option\n", short_cmd ? "short" : "long",
short_cmd ? "without" : "with");
break;
+ case SG_LIB_CAT_NOT_READY:
+ fprintf(stderr, "device not ready\n");
+ break;
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ fprintf(stderr, "unit attention\n");
+ break;
default:
if (debug)
fprintf(stderr, "%s trespass failed\n",
short_cmd ? "short" : "long");
break;
}
+ return res;
}
void usage ()
@@ -106,6 +113,7 @@ int main(int argc, char * argv[])
int k, fd;
int hr = 0;
int short_cmd = 0;
+ int ret = 0;
if (argc < 2)
usage ();
@@ -137,7 +145,7 @@ int main(int argc, char * argv[])
}
if (0 == file_name) {
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
fd = open(file_name, O_RDWR | O_NONBLOCK);
@@ -145,11 +153,11 @@ int main(int argc, char * argv[])
fprintf(stderr, "Error trying to open %s\n", file_name);
perror("");
usage();
- return 2;
+ return SG_LIB_FILE_ERROR;
}
- do_trespass(fd, hr, short_cmd);
+ ret = do_trespass(fd, hr, short_cmd);
close (fd);
- return 0;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_format.8 b/sg_format.8
index ea4f0e72..a27bf6a4 100644
--- a/sg_format.8
+++ b/sg_format.8
@@ -1,11 +1,11 @@
-.TH SG_FORMAT "8" "December 2005" "sg3_utils-1.19" SG3_UTILS
+.TH SG_FORMAT "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_format \- format or resize a SCSI disk (perhaps change its block size)
.SH SYNOPSIS
.B sg_format
[\fI--count=<n>\fR] [\fI--early\fR] [\fI--format\fR] [\fI--help\fR]
-[\fI--long\fR] [\fI--resize\fR] [\fI--size=<n>\fR] [\fI--verbose\fR]
-[\fI--version\fR] [\fI--wait\fR]
+[\fI--long\fR] [\fI--resize\fR] [\fI--six\fR] [\fI--size=<n>\fR]
+[\fI--verbose\fR] [\fI--version\fR] [\fI--wait\fR]
\fI<scsi_device>\fR
.SH DESCRIPTION
.\" Add any additional description here
@@ -132,6 +132,11 @@ the "logical block reference tag" field. The default action is to
disable application client ownership of that field. Has no action
unless both '--format' and '--pinfo' are given.
.TP
+--six | -6
+Use 6 byte variants of MODE SENSE and MODE SELECT. The default action
+is to use the 10 byte variants. Some MO drives need this option set
+when doing a format.
+.TP
--size=<n> | -s <n>
block size (i.e. number of bytes in each block) to format the device to.
The default value is whatever is currently reported by the block descriptor
@@ -186,9 +191,10 @@ TEST UNIT READY commands until it reports an "all clear" (i.e. the
format operation has completed). Normally these polling commands will
result in a progress indicator (expressed as a percentage) being output
to the screen. If the user gets bored watching the progress report then
-sg_format can be terminated (e.g. with control-C) without effecting the
-format operation which continues. However a bus or device reset (or a
-power cycle) may well cause the device to become "format corrupt".
+sg_format process can be terminated (e.g. with control-C) without
+affecting the format operation which continues. However a bus or device
+reset (or a power cycle) may well cause the device to become "format
+corrupt".
.PP
When the "--format" and "--wait" options are both given then this utility
may take a long time to return. In this case care should be taken not to
@@ -279,12 +285,15 @@ Now resize the disk back to its normal (maximum) block count:
.PP
sg_format --resize --count=-1 /dev/sdm
.PP
+.SH EXIT STATUS
+The exit status of sg_format is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHORS
Written by Grant Grundler, James Bottomley and Douglas Gilbert.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2005 Grant Grundler, James Bottomley and Douglas Gilbert
+Copyright \(co 2005-2006 Grant Grundler, James Bottomley and Douglas Gilbert
.br
This software is distributed under the GPL version 2. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/sg_format.c b/sg_format.c
index eb8105a6..13341b08 100644
--- a/sg_format.c
+++ b/sg_format.c
@@ -53,7 +53,7 @@
#define MAX_BUFF_SZ 252
static unsigned char dbuff[MAX_BUFF_SZ];
-static char * version_str = "1.07 20060106";
+static char * version_str = "1.09 20060623";
static struct option long_options[] = {
{"count", 1, 0, 'c'},
@@ -64,6 +64,7 @@ static struct option long_options[] = {
{"pinfo", 0, 0, 'p'},
{"resize", 0, 0, 'r'},
{"rto_req", 0, 0, 'R'},
+ {"six", 0, 0, '6'},
{"size", 1, 0, 's'},
{"verbose", 0, 0, 'v'},
{"version", 0, 0, 'V'},
@@ -71,7 +72,7 @@ static struct option long_options[] = {
{0, 0, 0, 0},
};
-/* Return 0 on success, else -1 */
+/* Return 0 on success, else see sg_ll_format_unit() */
static int
scsi_format(int fd, int pinfo, int rto_req, int immed, int early, int verbose)
{
@@ -86,7 +87,7 @@ scsi_format(int fd, int pinfo, int rto_req, int immed, int early, int verbose)
fmt_hdr[3] = 0; /* defect list length LSB */
res = sg_ll_format_unit(fd, pinfo, rto_req, 0 /* longlist */,
- (!! immed) /* fmtdata */, 8 /* cmplist */,
+ (!! immed) /* fmtdata */, 0 /* cmplist */,
0 /* dlist_format */,
(immed ? SHORT_TIMEOUT : FORMAT_TIMEOUT),
fmt_hdr, (immed ? sizeof(fmt_hdr) : 0),
@@ -94,17 +95,25 @@ scsi_format(int fd, int pinfo, int rto_req, int immed, int early, int verbose)
switch (res) {
case 0:
- break;
+ break;
+ case SG_LIB_CAT_NOT_READY:
+ fprintf(stderr, "Format command, device not ready\n");
+ break;
case SG_LIB_CAT_INVALID_OP:
fprintf(stderr, "Format command not supported\n");
- return -1;
+ break;
case SG_LIB_CAT_ILLEGAL_REQ:
- fprintf(stderr, "Format command illegal parameter\n");
- return -1;
+ fprintf(stderr, "Format command, illegal parameter\n");
+ break;
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ fprintf(stderr, "Format command, unit attention\n");
+ break;
default:
fprintf(stderr, "Format command failed\n");
- return -1;
+ break;
}
+ if (res)
+ return res;
if (! immed)
return 0;
@@ -112,8 +121,8 @@ scsi_format(int fd, int pinfo, int rto_req, int immed, int early, int verbose)
printf("\nFormat has started\n");
if (early) {
if (immed)
- printf("Format continuing, use request sense or "
- "test unit ready to monitor progress\n");
+ printf("Format continuing, request sense or test "
+ "unit ready can be used to monitor progress\n");
return 0;
}
@@ -184,13 +193,16 @@ print_read_cap(int fd, int do_16, int verbose)
return (int)block_size;
}
}
- if (SG_LIB_CAT_INVALID_OP == res)
+ if (SG_LIB_CAT_NOT_READY == res)
+ fprintf(stderr, "READ CAPACITY (%d): device not ready\n",
+ (do_16 ? 16 : 10));
+ else if (SG_LIB_CAT_INVALID_OP == res)
fprintf(stderr, "READ CAPACITY (%d) not supported\n",
(do_16 ? 16 : 10));
- if (SG_LIB_CAT_ILLEGAL_REQ == res)
+ else if (SG_LIB_CAT_ILLEGAL_REQ == res)
fprintf(stderr, "bad field in READ CAPACITY (%d) "
"cdb\n", (do_16 ? 16 : 10));
- if (verbose)
+ else if (verbose)
fprintf(stderr, "READ CAPACITY (%d) failed "
"[res=%d]\n", (do_16 ? 16 : 10), res);
return -1;
@@ -200,7 +212,8 @@ static void usage()
{
printf("usage: sg_format [--count=<block count>] [--early] [--format]"
" [--help]\n"
- " [--long] [--pinfo] [--resize] [--rto_req]\n"
+ " [--long] [--pinfo] [--resize] [--rto_req] "
+ "[--six]\n"
" [--size=<block size>] [--verbose]"
" [--version] [--wait]\n"
" <scsi_disk>\n"
@@ -223,6 +236,7 @@ static void usage()
"value\n"
" --rto_req | -R set the RTO_REQ bit in format (only valid "
"with '--pinfo')\n"
+ " --six | -6 use 6 byte MODE SENSE/SELECT\n"
" --size=<block size> | -s <block size>\n"
" only needed to change block size"
" (default to\n"
@@ -262,14 +276,14 @@ int main(int argc, char **argv)
char device_name[256];
char pdt_name[64];
struct sg_simple_inquiry_resp inq_out;
- int ret = 1;
+ int ret = 0;
device_name[0] = '\0';
while (1) {
int option_index = 0;
- char c;
+ int c;
- c = getopt_long(argc, argv, "c:eFhlprRs:vVw",
+ c = getopt_long(argc, argv, "c:eFhlprRs:vVw6",
long_options, &option_index);
if (c == -1)
break;
@@ -283,7 +297,7 @@ int main(int argc, char **argv)
if (-1 == blk_count) {
fprintf(stderr, "bad argument to "
"'--count'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
break;
@@ -314,7 +328,7 @@ int main(int argc, char **argv)
if (blk_size <= 0) {
fprintf(stderr, "bad argument to '--size', "
"want arg > 0)\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 'v':
@@ -327,9 +341,12 @@ int main(int argc, char **argv)
case 'w':
fwait = 1;
break;
+ case '6':
+ mode6 = 1;
+ break;
default:
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (optind < argc) {
@@ -345,29 +362,29 @@ int main(int argc, char **argv)
fprintf(stderr, "Unexpected extra argument: %s\n",
argv[optind]);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if ('\0' == device_name[0]) {
fprintf(stderr, "no device name given\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (resize) {
if (format) {
fprintf(stderr, "both '--format' and '--resize'"
"not permitted\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
} else if (0 == blk_count) {
fprintf(stderr, "'--resize' needs a '--count' (other"
" than 0)\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
} else if (0 != blk_size) {
fprintf(stderr, "'--resize' not compatible with "
"'--size')\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
@@ -379,12 +396,13 @@ int main(int argc, char **argv)
if ((fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose)) < 0) {
fprintf(stderr, "error opening device file: %s: %s\n",
device_name, safe_strerror(-fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (sg_simple_inquiry(fd, &inq_out, 1, verbose)) {
fprintf(stderr, "%s doesn't respond to a SCSI INQUIRY\n",
device_name);
+ ret = SG_LIB_CAT_OTHER;
goto out;
}
printf(" %.8s %.16s %.4s peripheral_type: %s [0x%x]\n",
@@ -401,6 +419,7 @@ int main(int argc, char **argv)
(0xe != inq_out.peripheral_type)) {
fprintf(stderr, "This format is only defined for disks "
"(using SBC-2 or RBC)\n");
+ ret = SG_LIB_CAT_MALFORMED;
goto out;
}
@@ -414,11 +433,21 @@ int main(int argc, char **argv)
0 /* current */, mode_page,
0 /* subpage */, dbuff,
MAX_BUFF_SZ, 1, verbose);
+ ret = res;
if (res) {
- if (SG_LIB_CAT_INVALID_OP == res)
+ if (SG_LIB_CAT_NOT_READY == res)
+ fprintf(stderr, "MODE SENSE (%d) command, device "
+ "not ready\n", (mode6 ? 6 : 10));
+ else if (SG_LIB_CAT_UNIT_ATTENTION == res)
+ fprintf(stderr, "MODE SENSE (%d) command, unit "
+ "attention\n", (mode6 ? 6 : 10));
+ else if (SG_LIB_CAT_INVALID_OP == res) {
fprintf(stderr, "MODE SENSE (%d) command is not "
"supported\n", (mode6 ? 6 : 10));
- else if (SG_LIB_CAT_ILLEGAL_REQ == res) {
+ fprintf(stderr, " try again %s the '--six' "
+ "option\n", (mode6 ? "without" : "with"));
+
+ } else if (SG_LIB_CAT_ILLEGAL_REQ == res) {
if (long_lba && (! mode6))
fprintf(stderr, "bad field in MODE SENSE "
"(%d) [longlba flag not supported?]"
@@ -535,6 +564,7 @@ int main(int argc, char **argv)
"change number or blocks or block length)\n");
fprintf(stderr, "but (single) block descriptor not "
"found in earlier MODE SENSE\n");
+ ret = SG_LIB_CAT_MALFORMED;
goto out;
}
if (blk_count != 0) {
@@ -565,8 +595,15 @@ int main(int argc, char **argv)
else
res = sg_ll_mode_select10(fd, 1 /* PF */, 1 /* SP */,
dbuff, calc_len, 1, verbose);
+ ret = res;
if (res) {
- if (SG_LIB_CAT_INVALID_OP == res)
+ if (SG_LIB_CAT_NOT_READY == res)
+ fprintf(stderr, "MODE SELECT command, "
+ "device not ready\n");
+ else if (SG_LIB_CAT_UNIT_ATTENTION == res)
+ fprintf(stderr, "MODE SELECT command, "
+ "unit attention\n");
+ else if (SG_LIB_CAT_INVALID_OP == res)
fprintf(stderr, "MODE SELECT (%d) command is "
"not supported\n", (mode6 ? 6 : 10));
else if (SG_LIB_CAT_ILLEGAL_REQ == res)
@@ -575,19 +612,20 @@ int main(int argc, char **argv)
else
fprintf(stderr, "MODE SELECT (%d) command "
"failed\n", (mode6 ? 6 : 10));
- if (0 == verbose)
- fprintf(stderr, " try '-v' for "
- "more information\n");
+ if (0 == verbose)
+ fprintf(stderr, " try '-v' for "
+ "more information\n");
goto out;
}
}
if (resize) {
- ret = 0;
printf("Resize operation seems to have been successful\n");
goto out;
}
else if (! format) {
res = print_read_cap(fd, do_rcap16, verbose);
+ if (res < 0)
+ ret = -1;
if ((res > 0) && (bd_blk_len > 0) &&
(res != (int)bd_blk_len)) {
printf(" Warning: mode sense and read capacity "
@@ -597,7 +635,6 @@ int main(int argc, char **argv)
}
printf("No changes made. To format use '--format'. To "
"resize use '--resize'\n");
- ret = 0;
goto out;
}
@@ -613,6 +650,7 @@ int main(int argc, char **argv)
sleep(5);
res = scsi_format(fd, pinfo, rto_req, ! fwait, early,
verbose);
+ ret = res;
if (res) {
fprintf(stderr, "FORMAT failed\n");
if (0 == verbose)
@@ -622,9 +660,13 @@ int main(int argc, char **argv)
#else
fprintf(stderr, "FORMAT ignored, testing\n");
#endif
- ret = 0;
out:
- sg_cmds_close_device(fd);
- return ret;
+ res = sg_cmds_close_device(fd);
+ if (res < 0) {
+ fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ return SG_LIB_FILE_ERROR;
+ }
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_get_config.8 b/sg_get_config.8
index 559fbba4..702303d6 100644
--- a/sg_get_config.8
+++ b/sg_get_config.8
@@ -1,4 +1,4 @@
-.TH SG_GET_CONFIG "8" "August 2005" "sg3_utils-1.16" SG3_UTILS
+.TH SG_GET_CONFIG "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_get_config \- invoke SCSI GET CONFIGURATION command on a (cd/dvd) device
.SH SYNOPSIS
@@ -101,6 +101,9 @@ can also be specified. For example "sg_get_config /dev/hdc"
will work in the 2.6 series kernels as long as /dev/hdc is
an ATAPI device. In the 2.6 series external DVD writers attached
via USB could be queried with "sg_get_config /dev/scd1" for example.
+.SH EXIT STATUS
+The exit status of sg_get_config is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHORS
Written by Douglas Gilbert.
.SH "REPORTING BUGS"
diff --git a/sg_get_config.c b/sg_get_config.c
index 51dbe3ca..2e3e8144 100644
--- a/sg_get_config.c
+++ b/sg_get_config.c
@@ -45,7 +45,7 @@
*/
-static char * version_str = "0.25 20060315";
+static char * version_str = "0.26 20060623";
#define MX_ALLOC_LEN 8192
#define NAME_BUFF_SZ 64
@@ -884,7 +884,7 @@ int main(int argc, char * argv[])
char buff[64];
const char * cp;
struct sg_simple_inquiry_resp inq_resp;
- int ret = 1;
+ int ret = 0;
memset(device_name, 0, sizeof device_name);
while (1) {
@@ -919,14 +919,14 @@ int main(int argc, char * argv[])
rt = sg_get_num(optarg);
if ((rt < 0) || (rt > 3)) {
fprintf(stderr, "bad argument to '--rt'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 's':
starting = sg_get_num(optarg);
if ((starting < 0) || (starting > 0xffff)) {
fprintf(stderr, "bad argument to '--starting'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 'v':
@@ -938,7 +938,7 @@ int main(int argc, char * argv[])
default:
fprintf(stderr, "unrecognised switch code 0x%x ??\n", c);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (optind < argc) {
@@ -952,7 +952,7 @@ int main(int argc, char * argv[])
fprintf(stderr, "Unexpected extra argument: %s\n",
argv[optind]);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
@@ -963,12 +963,12 @@ int main(int argc, char * argv[])
if (0 == device_name[0]) {
fprintf(stderr, "missing device name!\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if ((sg_fd = sg_cmds_open_device(device_name, 1 /* ro */, verbose)) < 0) {
fprintf(stderr, ME "error opening file: %s (ro): %s\n",
device_name, safe_strerror(-sg_fd));
- return 1;
+ 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,
@@ -982,20 +982,20 @@ int main(int argc, char * argv[])
} else {
fprintf(stderr, ME "%s doesn't respond to a SCSI INQUIRY\n",
device_name);
- return 1;
+ return SG_LIB_CAT_OTHER;
}
sg_cmds_close_device(sg_fd);
sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
if (sg_fd < 0) {
fprintf(stderr, ME "open error (rw): %s\n", safe_strerror(-sg_fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
res = sg_ll_get_config(sg_fd, rt, starting, resp_buffer,
sizeof(resp_buffer), 1, verbose);
+ ret = res;
if (0 == res) {
- ret = 0;
len = (resp_buffer[0] << 24) + (resp_buffer[1] << 16) +
(resp_buffer[2] << 8) + resp_buffer[3] + 4;
if (hex) {
@@ -1009,6 +1009,8 @@ int main(int argc, char * argv[])
fprintf(stderr, "Get Configuration command not supported\n");
else if (SG_LIB_CAT_ILLEGAL_REQ == res)
fprintf(stderr, "field in Get Configuration command illegal\n");
+ else if (SG_LIB_CAT_UNIT_ATTENTION == res)
+ fprintf(stderr, "Get Configuration received unit attention\n");
else {
fprintf(stderr, "Get Configuration command failed\n");
if (0 == verbose)
@@ -1017,9 +1019,9 @@ int main(int argc, char * argv[])
res = sg_cmds_close_device(sg_fd);
if (res < 0) {
- fprintf(stderr, ME "close error: %s\n", safe_strerror(-res));
- return 1;
+ fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ return SG_LIB_FILE_ERROR;
}
- return ret;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
-
diff --git a/sg_ident.8 b/sg_ident.8
index f29c3739..f71451a5 100644
--- a/sg_ident.8
+++ b/sg_ident.8
@@ -1,4 +1,4 @@
-.TH SG_IDENT "8" "August 2005" "sg3_utils-1.16" SG3_UTILS
+.TH SG_IDENT "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_ident \- report or set device identifier
.SH SYNOPSIS
@@ -69,9 +69,6 @@ There is also the READ MEDIA SERIAL NUMBER command (see sg_rmsn).
The MMC-4 command set for CDs and DVDs has a "media serial number"
feature (0x109) [and a "logical unit serial number" feature]. These
can be viewed with sg_get_config.
-.PP
-When this utility succeeds its process exits with a status of 0;
-if problems are encountered the process status is 1 .
.SH EXAMPLES
First, to see if there is an existing device identifier whose format
is unknown, use no options:
@@ -102,12 +99,15 @@ re-asserted with this sequence:
# sg_ident --clear /dev/sdb
.br
# cat t | sg_ident --set /dev/sdb
+.SH EXIT STATUS
+The exit status of sg_ident is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHORS
Written by Douglas Gilbert.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2005 Douglas Gilbert
+Copyright \(co 2005-2006 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.
diff --git a/sg_ident.c b/sg_ident.c
index c5eb2a51..2de032a4 100644
--- a/sg_ident.c
+++ b/sg_ident.c
@@ -44,7 +44,7 @@
* SET DEVICE IDENTIFIER and/or INQUIRY (VPD=0x83 [device identifier]).
*/
-static char * version_str = "1.01 20060106";
+static char * version_str = "1.02 20060623";
#define ME "sg_ident: "
@@ -94,7 +94,7 @@ int main(int argc, char * argv[])
int do_set = 0;
int verbose = 0;
char device_name[512];
- int ret = 1;
+ int ret = 0;
memset(device_name, 0, sizeof device_name);
while (1) {
@@ -131,7 +131,7 @@ int main(int argc, char * argv[])
default:
fprintf(stderr, "unrecognised switch code 0x%x ??\n", c);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (optind < argc) {
@@ -145,36 +145,36 @@ int main(int argc, char * argv[])
fprintf(stderr, "Unexpected extra argument: %s\n",
argv[optind]);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (0 == device_name[0]) {
fprintf(stderr, "missing device name!\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (do_set && do_clear) {
fprintf(stderr, "only one of '--clear' and '--set' can be given\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (ascii && raw) {
fprintf(stderr, "only one of '--ascii' and '--raw' can be given\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if ((do_set || do_clear) && (raw || ascii)) {
fprintf(stderr, "'--set' cannot be used with either '--ascii' or "
"'--raw'\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
if (sg_fd < 0) {
fprintf(stderr, ME "open error: %s: %s\n", device_name,
safe_strerror(-sg_fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
memset(rdi_buff, 0x0, sizeof(rdi_buff));
@@ -194,12 +194,16 @@ int main(int argc, char * argv[])
res = sg_ll_set_dev_id(sg_fd, rdi_buff, di_len, 1, verbose);
} else /* do_clear */
res = sg_ll_set_dev_id(sg_fd, rdi_buff, 0, 1, verbose);
- if (0 == res)
- ret = 0;
- else {
- if (SG_LIB_CAT_INVALID_OP == res)
+ if (res) {
+ ret = res;
+ if (SG_LIB_CAT_NOT_READY == res)
+ fprintf(stderr, "Set Device Identifier command, device "
+ "not ready\n");
+ else if (SG_LIB_CAT_INVALID_OP == res)
fprintf(stderr, "Set Device Identifier command not "
"supported\n");
+ else if (SG_LIB_CAT_UNIT_ATTENTION == res)
+ fprintf(stderr, "Set Device Identifier, unit attention\n");
else if (SG_LIB_CAT_ILLEGAL_REQ == res)
fprintf(stderr, "bad field in Set Device Identifier "
"cdb\n");
@@ -243,20 +247,27 @@ int main(int argc, char * argv[])
dStrHex((const char *)ucp + 4, di_len, 0);
}
}
- ret = 0;
- }
- }
- if (0 != res) {
- if (SG_LIB_CAT_INVALID_OP == res)
- fprintf(stderr, "Report Device Identifier command not "
- "supported\n");
- else if (SG_LIB_CAT_ILLEGAL_REQ == res)
- fprintf(stderr, "bad field in Report Device Identifier "
- "cdb\n");
- else {
- fprintf(stderr, "Report Device Identifier command failed\n");
- if (0 == verbose)
- fprintf(stderr, " try '-v' for more information\n");
+ } else {
+ ret = res;
+ if (SG_LIB_CAT_NOT_READY == res)
+ fprintf(stderr, "Report Device Identifier command, "
+ "device not ready\n");
+ else if (SG_LIB_CAT_UNIT_ATTENTION == res)
+ fprintf(stderr, "Report Device Identifier, unit "
+ "attention\n");
+ else if (SG_LIB_CAT_INVALID_OP == res)
+ fprintf(stderr, "Report Device Identifier command not "
+ "supported\n");
+ else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+ fprintf(stderr, "bad field in Report Device Identifier "
+ "cdb\n");
+ else {
+ fprintf(stderr, "Report Device Identifier command "
+ "failed\n");
+ if (0 == verbose)
+ fprintf(stderr, " try '-v' for more "
+ "information\n");
+ }
}
}
}
@@ -264,8 +275,9 @@ int main(int argc, char * argv[])
err_out:
res = sg_cmds_close_device(sg_fd);
if (res < 0) {
- fprintf(stderr, ME "close error: %s\n", safe_strerror(-res));
- return 1;
+ fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ return SG_LIB_FILE_ERROR;
}
- return ret;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_inq.8 b/sg_inq.8
index 50f80308..a4b120c6 100644
--- a/sg_inq.8
+++ b/sg_inq.8
@@ -1,4 +1,4 @@
-.TH SG_INQ "8" "December 2005" "sg3_utils-1.19" SG3_UTILS
+.TH SG_INQ "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_inq \- outputs data retrieved from the SCSI INQUIRY or
ATA IDENTIFY (PACKET) DEVICE command
@@ -35,7 +35,7 @@ transport information to be found for ATAPI cd/dvd drives; since
without this option such drives respond to a SCSI INQUIRY command.
.PP
The reference document used for interpreting an INQUIRY is T10/1713-D
-Revision 2 (SPC-4, 15 September 2005) found at http://www.t10.org .
+Revision 4 (SPC-4, 9 March 2006) found at http://www.t10.org .
Obsolete items in the standard INQUIRY response are displayed in
brackets. The reference document for the ATA IDENTIFY (PACKET) DEVICE
command is ATA8-ACS found at http://www.t13.org .
@@ -245,15 +245,18 @@ This utility doesn't decode the response to an ATA IDENTIFY (PACKET)
DEVICE command, hdparm does a good job at that. The '-rr' option has
been added to use with either the '-A' or '-a' option to produce a
format acceptable to "hdparm --Istdin". See hdparm.
+.SH EXIT STATUS
+The exit status of sg_inq is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHOR
Written by Doug Gilbert
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2001-2005 Douglas Gilbert
+Copyright \(co 2001-2006 Douglas Gilbert
.br
This software is distributed under the GPL version 2. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
.SH "SEE ALSO"
.B sgdiag(scsirastools), sg_opcodes(sg3_utils), sg_modes(sg3_utils),
-.B sg_logs(sg3_utils), hdparm(hdparm)
+.B sg_logs(sg3_utils), hdparm(hdparm), sg_vpd(sg_vpd)
diff --git a/sg_inq.c b/sg_inq.c
index db493a42..4bf935c8 100644
--- a/sg_inq.c
+++ b/sg_inq.c
@@ -59,15 +59,9 @@
* information [MAINTENANCE IN, service action = 0xc]; see sg_opcodes.
*/
-static char * version_str = "0.59 20050320"; /* spc-4 rev 04 */
+static char * version_str = "0.61 20050622"; /* spc-4 rev 05 */
-#define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */
-#define DEF_TIMEOUT 60000 /* 60,000 millisecs == 60 seconds */
-
-#define INQUIRY_CMD 0x12
-#define INQUIRY_CMDLEN 6
-
#define SUPPORTED_VPDS_VPD 0x0
#define UNIT_SERIAL_NUM_VPD 0x80
#define DEV_ID_VPD 0x83
@@ -79,6 +73,8 @@ static char * version_str = "0.59 20050320"; /* spc-4 rev 04 */
#define ATA_INFO_VPD 0x89
#define BLOCK_LIMITS_VPD 0xb0
#define UPR_EMC_VPD 0xc0
+#define RDAC_VERS_VPD 0xc2
+#define RDAC_VAC_VPD 0xc9
#define DEF_ALLOC_LEN 252
#define SAFE_STD_INQ_RESP_LEN 36
@@ -181,8 +177,9 @@ static struct vpd_name vpd_name_arr[] = {
{0xb1, 0x11, "Security token (osd)"},
{0xc0, 0, "vendor: Firmware numbers (seagate); Unit path report (EMC)"},
{0xc1, 0, "vendor: Date code (seagate)"},
- {0xc2, 0, "vendor: Jumper settings (seagate)"},
+ {0xc2, 0, "vendor: Jumper settings (seagate); Software version (RDAC)"},
{0xc3, 0, "vendor: Device behavior (seagate)"},
+ {0xc9, 0, "Volume Access Control (RDAC)"},
};
const char * get_vpd_page_str(int vpd_page_num, int scsi_ptype)
@@ -418,27 +415,33 @@ static const char * id_type_arr[] =
"Reserved [0xc]", "Reserved [0xd]", "Reserved [0xe]", "Reserved [0xf]",
};
+extern int sg_vpd_dev_id_iter(const unsigned char * initial_desig_desc,
+ int page_len, int * off, int m_assoc,
+ int m_desig_type, int m_code_set);
+
/* These are target port, device server (i.e. target) and lu identifiers */
static void decode_dev_ids(const char * leadin, unsigned char * buff,
int len, int do_hex)
{
- int k, j, m, id_len, p_id, c_set, piv, assoc, id_type, i_len;
- int ci_off, c_id, d_id, naa, vsi;
+ int u, j, m, id_len, p_id, c_set, piv, assoc, id_type, i_len;
+ int off, ci_off, c_id, d_id, naa, vsi;
unsigned long long vsei;
unsigned long long id_ext;
const unsigned char * ucp;
const unsigned char * ip;
- ucp = buff;
- for (k = 0, j = 1; k < len; k += id_len, ucp += id_len, ++j) {
+ for (j = 1, off = -1;
+ (u = sg_vpd_dev_id_iter(buff, len, &off, -1, -1, -1)) == 0;
+ ++j) {
+ ucp = buff + off;
i_len = ucp[3];
id_len = i_len + 4;
printf(" Designation descriptor number %d, "
"descriptor length: %d\n", j, id_len);
- if ((k + id_len) > len) {
+ if ((off + id_len) > len) {
fprintf(stderr, "%s VPD page error: designator length longer "
"than\n remaining response length=%d\n", leadin,
- (len - k));
+ (len - off));
return;
}
ip = ucp + 4;
@@ -652,10 +655,12 @@ static void decode_dev_ids(const char * leadin, unsigned char * buff,
break;
}
}
+ if (-2 == u)
+ fprintf(stderr, "%s VPD page error: around offset=%d\n", leadin, off);
}
/* Transport IDs are initiator port identifiers, typically other than the
- initiator port issuing a SCSI command. Borrowed from sg_persist.c */
+ initiator port issuing a SCSI command. Code borrowed from sg_persist.c */
static void decode_transport_id(const char * leadin, unsigned char * ucp,
int len)
{
@@ -801,7 +806,8 @@ static void decode_softw_inf_id(unsigned char * buff, int len, int do_hex)
static void decode_ata_info_vpd(unsigned char * buff, int len, int do_hex)
{
- char b[32];
+ char b[80];
+ int is_be, num;
if (len < 36) {
fprintf(stderr, "ATA information VPD page length too "
@@ -823,16 +829,28 @@ static void decode_ata_info_vpd(unsigned char * buff, int len, int do_hex)
printf(" SAT Product revision level: %s\n", b);
if (len < 56)
return;
- printf(" Signature (20 bytes):\n");
- dStrHex((const char *)buff + 36, 20, 0);
+ printf(" Signature (Device to host FIS):\n");
+ dStrHex((const char *)buff + 36, 20, 1);
if (len < 60)
return;
- if (0xec == buff[56])
- printf(" ATA command IDENTIFY DEVICE got following response:\n");
- else if (0xa1 == buff[56])
- printf(" ATA command IDENTIFY PACKET DEVICE got following "
- "response:\n");
- else
+ is_be = sg_is_big_endian();
+ if ((0xec == buff[56]) || (0xa1 == buff[56])) {
+ printf(" ATA command IDENTIFY %sDEVICE response summary:\n",
+ ((0xa1 == buff[56]) ? "PACKET " : ""));
+ num = sg_ata_get_chars((const unsigned short *)(buff + 60), 27, 20,
+ is_be, b);
+ b[num] = '\0';
+ printf(" model: %s\n", b);
+ num = sg_ata_get_chars((const unsigned short *)(buff + 60), 10, 10,
+ is_be, b);
+ b[num] = '\0';
+ printf(" serial number: %s\n", b);
+ num = sg_ata_get_chars((const unsigned short *)(buff + 60), 23, 4,
+ is_be, b);
+ b[num] = '\0';
+ printf(" firmware revision: %s\n", b);
+ printf(" response in hex:\n");
+ } else
printf(" ATA command 0x%x got following response:\n",
(unsigned int)buff[56]);
if (len < 572)
@@ -982,37 +1000,122 @@ static void decode_upr_vpd_c0_emc(unsigned char * buff, int len)
return;
}
-/* Returns 0 if Unit Serial Number VPD page contents found, else -1 */
+static void decode_rdac_vpd_c2(unsigned char * buff, int len)
+{
+ if (len < 3) {
+ fprintf(stderr, "Software Version VPD page length too "
+ "short=%d\n", len);
+ return;
+ }
+ if (buff[4] != 's' && buff[5] != 'w' && buff[6] != 'r') {
+ fprintf(stderr, "Invalid page identifier %c%c%c%c, decoding "
+ "not possible.\n" , buff[4], buff[5], buff[6], buff[7]);
+ return;
+ }
+ printf(" Software Version: %d.%d.%d\n", buff[8], buff[9], buff[10]);
+ printf(" Software Date: %02x/%02x/%02x\n", buff[11], buff[12], buff[13]);
+ printf(" Features:");
+ if (buff[14] & 0x01)
+ printf(" Dual Active,");
+ if (buff[14] & 0x02)
+ printf(" Series 3,");
+ if (buff[14] & 0x04)
+ printf(" Multiple Sub-enclosures,");
+ if (buff[14] & 0x08)
+ printf(" DCE/DRM,");
+ if (buff[14] & 0x10)
+ printf(" AVT,");
+ printf("\n");
+ printf(" Max. #of LUNS: %d\n", buff[15]);
+ return;
+}
+
+static void decode_rdac_vpd_c9(unsigned char * buff, int len)
+{
+ if (len < 3) {
+ fprintf(stderr, "Volume Access Control VPD page length too "
+ "short=%d\n", len);
+ return;
+ }
+ if (buff[4] != 'v' && buff[5] != 'a' && buff[6] != 'c') {
+ fprintf(stderr, "Invalid page identifier %c%c%c%c, decoding "
+ "not possible.\n" , buff[4], buff[5], buff[6], buff[7]);
+ return;
+ }
+ if (buff[7] != '1') {
+ fprintf(stderr, "Invalid page version '%c' (should be 1)\n",
+ buff[7]);
+ }
+ printf(" AVT:");
+ if (buff[8] & 0x80) {
+ printf(" Enabled");
+ if (buff[8] & 0x40)
+ printf(" (Allow reads on sector 0)");
+ printf("\n");
+ } else {
+ printf(" Disabled\n");
+ }
+ printf(" Volume Access via: ");
+ if (buff[8] & 0x01)
+ printf("primary controller\n");
+ else
+ printf("alternate controller\n");
+
+ printf(" Path priority: %d ", buff[9] & 0xf);
+ switch(buff[9] & 0xf) {
+ case 0x1:
+ printf("(preferred path)\n");
+ break;
+ case 0x2:
+ printf("(secondary path)\n");
+ break;
+ default:
+ printf("(unknown)\n");
+ break;
+ }
+
+ return;
+}
+
+/* Returns 0 if Unit Serial Number VPD page contents found, else see
+ sg_ll_inquiry() */
static int fetch_unit_serial_num(int sg_fd, char * obuff, int obuff_len,
int verbose)
{
- int sz, len, k;
+ int sz, len, k, res;
unsigned char b[DEF_ALLOC_LEN];
+ res = 0;
sz = sizeof(b);
memset(b, 0xff, 4); /* guard against empty response */
/* first check if unit serial number VPD page is supported */
- if ((0 == sg_ll_inquiry(sg_fd, 0, 1, SUPPORTED_VPDS_VPD, b, sz, 0,
- verbose)) &&
- (SUPPORTED_VPDS_VPD == b[1]) && (0x0 == b[2])) {
+ res = sg_ll_inquiry(sg_fd, 0, 1, SUPPORTED_VPDS_VPD, b, sz, 0,
+ verbose);
+ if (0 == res) {
+ if ((SUPPORTED_VPDS_VPD != b[1]) || (0x0 != b[2]))
+ return SG_LIB_CAT_MALFORMED;
len = b[3];
for (k = 0; k < len; ++k) {
if (UNIT_SERIAL_NUM_VPD == b[k + 4])
break;
}
- if ((k < len) &&
- (0 == sg_ll_inquiry(sg_fd, 0, 1, UNIT_SERIAL_NUM_VPD,
- b, sz, 0, verbose))) {
- len = b[3];
- len = (len < (obuff_len - 1)) ? len : (obuff_len - 1);
- if ((UNIT_SERIAL_NUM_VPD == b[1]) && (len > 0)) {
- memcpy(obuff, b + 4, len);
- obuff[len] = '\0';
- return 0;
+ if (k < len) {
+ res = sg_ll_inquiry(sg_fd, 0, 1, UNIT_SERIAL_NUM_VPD,
+ b, sz, 0, verbose);
+ if (0 == res) {
+ len = b[3];
+ len = (len < (obuff_len - 1)) ? len : (obuff_len - 1);
+ if ((UNIT_SERIAL_NUM_VPD == b[1]) && (len > 0)) {
+ memcpy(obuff, b + 4, len);
+ obuff[len] = '\0';
+ return 0;
+ } else
+ return SG_LIB_CAT_MALFORMED;
}
- }
+ } else
+ return SG_LIB_CAT_MALFORMED;
}
- return -1;
+ return res;
}
static const char * ansi_version_arr[] =
@@ -1042,7 +1145,7 @@ static int process_std_inq(int sg_fd, const char * file_name, int do_36,
int do_vdescriptors, int do_hex, int do_raw,
int do_verbose)
{
- int res, len, act_len, pqual, peri_type, ansi_version, ret, k, j;
+ int res, len, act_len, pqual, peri_type, ansi_version, k, j;
const char * cp;
int vdesc_arr[8];
char buff[48];
@@ -1071,20 +1174,19 @@ static int process_std_inq(int sg_fd, const char * file_name, int do_36,
if ((len > SAFE_STD_INQ_RESP_LEN) && (len < 256) && (! do_36)) {
if (sg_ll_inquiry(sg_fd, 0, 0, 0, rsp_buff, len, 1, do_verbose)) {
fprintf(stderr, "second INQUIRY (%d byte) failed\n", len);
- return 1;
+ return SG_LIB_CAT_OTHER;
}
if (len != (rsp_buff[4] + 5)) {
fprintf(stderr,
"strange, twin INQUIRYs yield different "
"'additional length'\n");
- ret = 2;
+ res = SG_LIB_CAT_MALFORMED;
}
}
if (do_36) {
act_len = len;
len = SAFE_STD_INQ_RESP_LEN;
- }
- else
+ } else
act_len = len;
if (do_hex)
dStrHex((const char *)rsp_buff, len, 0);
@@ -1185,23 +1287,22 @@ static int process_std_inq(int sg_fd, const char * file_name, int do_36,
}
}
}
- }
- else if (-1 == res) { /* could be an ATA device */
+ } else if (res < 0) { /* could be an ATA device */
#ifdef SG3_UTILS_LINUX
/* Try an ATA Identify Device command */
res = try_ata_identify(sg_fd, do_hex, do_raw, do_verbose);
if (0 != res) {
fprintf(stderr, "Both SCSI INQUIRY and fetching ATA information "
"failed on %s\n", file_name);
- return 1;
+ return SG_LIB_CAT_OTHER;
}
#else
fprintf(stderr, "SCSI INQUIRY failed on %s\n", file_name);
- return 1;
+ return res;
#endif
} else { /* SCSI device not supporting 36 byte INQUIRY?? */
printf("36 byte INQUIRY failed\n");
- return 1;
+ return res;
}
return 0;
}
@@ -1210,15 +1311,16 @@ static int process_std_inq(int sg_fd, const char * file_name, int do_36,
static int process_cmddt(int sg_fd, int do_cmdlst, int num_opcode,
int do_hex, int do_raw, int do_verbose)
{
- int k, j, num, len, peri_type, reserved_cmddt, support_num;
+ int k, j, num, len, peri_type, reserved_cmddt, support_num, res;
char op_name[128];
memset(rsp_buff, 0, DEF_ALLOC_LEN);
if (do_cmdlst) {
printf("Supported command list:\n");
for (k = 0; k < 256; ++k) {
- if (0 == sg_ll_inquiry(sg_fd, 1, 0, k, rsp_buff, DEF_ALLOC_LEN,
- 1, do_verbose)) {
+ res = sg_ll_inquiry(sg_fd, 1, 0, k, rsp_buff, DEF_ALLOC_LEN,
+ 1, do_verbose);
+ if (0 == res) {
peri_type = rsp_buff[0] & 0x1f;
support_num = rsp_buff[1] & 7;
reserved_cmddt = rsp_buff[4];
@@ -1249,8 +1351,9 @@ static int process_cmddt(int sg_fd, int do_cmdlst, int num_opcode,
}
}
else {
- if (0 == sg_ll_inquiry(sg_fd, 1, 0, num_opcode, rsp_buff,
- DEF_ALLOC_LEN, 1, do_verbose)) {
+ res = sg_ll_inquiry(sg_fd, 1, 0, num_opcode, rsp_buff,
+ DEF_ALLOC_LEN, 1, do_verbose);
+ if (0 == res) {
peri_type = rsp_buff[0] & 0x1f;
if (! do_raw) {
printf("CmdDt INQUIRY, opcode=0x%.2x: [", num_opcode);
@@ -1309,45 +1412,42 @@ static int process_cmddt(int sg_fd, int do_cmdlst, int num_opcode,
op_name[sizeof(op_name) - 1] = '\0';
printf("%s]\n", op_name);
}
- fprintf(stderr,
- "CmdDt INQUIRY on opcode=0x%.2x: failed\n",
+ fprintf(stderr, "CmdDt INQUIRY on opcode=0x%.2x: failed\n",
num_opcode);
- return 1;
}
}
- return 0;
+ return res;
}
/* Returns 0 if successful */
static int process_evpd(int sg_fd, int num_opcode, int do_hex,
int do_raw, int verbose)
{
- int ret, len, num, k, peri_type, vpd;
+ int res, len, num, k, peri_type, vpd;
const char * cp;
char buff[48];
memset(rsp_buff, 0, DEF_ALLOC_LEN);
if (!do_raw)
printf("VPD INQUIRY, page code=0x%.2x:\n", num_opcode);
- if (0 == sg_ll_inquiry(sg_fd, 0, 1, num_opcode, rsp_buff, DEF_ALLOC_LEN,
- 1, verbose)) {
+ res = sg_ll_inquiry(sg_fd, 0, 1, num_opcode, rsp_buff, DEF_ALLOC_LEN,
+ 1, verbose);
+ if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
- ret = 3;
if (num_opcode != rsp_buff[1]) {
fprintf(stderr, "invalid VPD response; probably a STANDARD "
"INQUIRY response\n");
- return 1;
+ return SG_LIB_CAT_MALFORMED;
}
if (len > MX_ALLOC_LEN) {
fprintf(stderr, "response length too long: %d > %d\n", len,
MX_ALLOC_LEN);
- return 1;
+ return SG_LIB_CAT_MALFORMED;
} else if (len > DEF_ALLOC_LEN) {
if (sg_ll_inquiry(sg_fd, 0, 1, num_opcode, rsp_buff, len, 1,
verbose))
- return 1;
+ return SG_LIB_CAT_OTHER;
}
- ret = 0;
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else {
@@ -1371,13 +1471,11 @@ static int process_evpd(int sg_fd, int num_opcode, int do_hex,
} else
dStrHex((const char *)rsp_buff, len, 0);
}
- }
- else {
+ } else {
fprintf(stderr,
"VPD INQUIRY, page code=0x%.2x: failed\n", num_opcode);
- return 1;
}
- return 0;
+ return res;
}
/* Returns 0 if successful */
@@ -1385,18 +1483,20 @@ static int decode_vpd(int sg_fd, int num_opcode, int do_hex,
int do_raw, int verbose)
{
int len, pdt;
+ int res = 0;
switch(num_opcode) {
case UNIT_SERIAL_NUM_VPD:
if (! do_raw)
printf("VPD INQUIRY: Unit serial number page\n");
- if (0 == sg_ll_inquiry(sg_fd, 0, 1, UNIT_SERIAL_NUM_VPD, rsp_buff,
- DEF_ALLOC_LEN, 1, verbose)) {
+ res = sg_ll_inquiry(sg_fd, 0, 1, UNIT_SERIAL_NUM_VPD, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
len = rsp_buff[3] + 4;
if (UNIT_SERIAL_NUM_VPD != rsp_buff[1]) {
fprintf(stderr, "invalid VPD response; probably a STANDARD "
"INQUIRY response\n");
- return 1;
+ return SG_LIB_CAT_MALFORMED;
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
@@ -1410,154 +1510,154 @@ static int decode_vpd(int sg_fd, int num_opcode, int do_hex,
memcpy(obuff, rsp_buff + 4, len);
printf(" Unit serial number: %s\n", obuff);
}
- return 0;
}
break;
case DEV_ID_VPD:
if (! do_raw)
printf("VPD INQUIRY: Device Identification page\n");
- if (0 == sg_ll_inquiry(sg_fd, 0, 1, DEV_ID_VPD, rsp_buff,
- DEF_ALLOC_LEN, 1, verbose)) {
+ res = sg_ll_inquiry(sg_fd, 0, 1, DEV_ID_VPD, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
if (DEV_ID_VPD != rsp_buff[1]) {
fprintf(stderr, "invalid VPD response; probably a STANDARD "
"INQUIRY response\n");
- return 1;
+ return SG_LIB_CAT_MALFORMED;
}
if (len > MX_ALLOC_LEN) {
fprintf(stderr, "response length too long: %d > %d\n", len,
MX_ALLOC_LEN);
- return 1;
+ return SG_LIB_CAT_MALFORMED;
} else if (len > DEF_ALLOC_LEN) {
if (sg_ll_inquiry(sg_fd, 0, 1, DEV_ID_VPD, rsp_buff, len,
1, verbose))
- return 1;
+ return SG_LIB_CAT_OTHER;
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else
decode_id_vpd(rsp_buff, len, do_hex);
- return 0;
}
break;
case SOFTW_INF_ID_VPD:
if (! do_raw)
printf("VPD INQUIRY: Software interface identification page\n");
- if (0 == sg_ll_inquiry(sg_fd, 0, 1, SOFTW_INF_ID_VPD, rsp_buff,
- DEF_ALLOC_LEN, 1, verbose)) {
+ res = sg_ll_inquiry(sg_fd, 0, 1, SOFTW_INF_ID_VPD, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
len = rsp_buff[3] + 4;
if (SOFTW_INF_ID_VPD != rsp_buff[1]) {
fprintf(stderr, "invalid VPD response; probably a STANDARD "
"INQUIRY response\n");
- return 1;
+ return SG_LIB_CAT_MALFORMED;
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else
decode_softw_inf_id(rsp_buff, len, do_hex);
- return 0;
}
break;
case MAN_NET_ADDR_VPD:
if (!do_raw)
printf("VPD INQUIRY: Management network addresses page\n");
- if (0 == sg_ll_inquiry(sg_fd, 0, 1, MAN_NET_ADDR_VPD, rsp_buff,
- DEF_ALLOC_LEN, 1, verbose)) {
+ res = sg_ll_inquiry(sg_fd, 0, 1, MAN_NET_ADDR_VPD, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
if (MAN_NET_ADDR_VPD != rsp_buff[1]) {
fprintf(stderr, "invalid VPD response; probably a STANDARD "
"INQUIRY response\n");
- return 1;
+ return SG_LIB_CAT_MALFORMED;
}
if (len > MX_ALLOC_LEN) {
fprintf(stderr, "response length too long: %d > %d\n", len,
MX_ALLOC_LEN);
- return 1;
+ return SG_LIB_CAT_MALFORMED;
} else if (len > DEF_ALLOC_LEN) {
if (sg_ll_inquiry(sg_fd, 0, 1, MAN_NET_ADDR_VPD, rsp_buff,
len, 1, verbose))
- return 1;
+ return SG_LIB_CAT_OTHER;
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else
decode_net_man_vpd(rsp_buff, len, do_hex);
- return 0;
}
break;
case MODE_PG_POLICY_VPD:
if (!do_raw)
printf("VPD INQUIRY: Mode page policy\n");
- if (0 == sg_ll_inquiry(sg_fd, 0, 1, MODE_PG_POLICY_VPD, rsp_buff,
- DEF_ALLOC_LEN, 1, verbose)) {
+ res = sg_ll_inquiry(sg_fd, 0, 1, MODE_PG_POLICY_VPD, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
if (MODE_PG_POLICY_VPD != rsp_buff[1]) {
fprintf(stderr, "invalid VPD response; probably a STANDARD "
"INQUIRY response\n");
- return 1;
+ return SG_LIB_CAT_MALFORMED;
}
if (len > MX_ALLOC_LEN) {
fprintf(stderr, "response length too long: %d > %d\n", len,
MX_ALLOC_LEN);
- return 1;
+ return SG_LIB_CAT_MALFORMED;
} else if (len > DEF_ALLOC_LEN) {
if (sg_ll_inquiry(sg_fd, 0, 1, MODE_PG_POLICY_VPD, rsp_buff,
len, 1, verbose))
- return 1;
+ return SG_LIB_CAT_OTHER;
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else
decode_mode_policy_vpd(rsp_buff, len, do_hex);
- return 0;
}
break;
case X_INQ_VPD:
if (!do_raw)
printf("VPD INQUIRY: extended INQUIRY data page\n");
- if (0 == sg_ll_inquiry(sg_fd, 0, 1, X_INQ_VPD, rsp_buff,
- DEF_ALLOC_LEN, 1, verbose)) {
+ res = sg_ll_inquiry(sg_fd, 0, 1, X_INQ_VPD, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
if (X_INQ_VPD != rsp_buff[1]) {
fprintf(stderr, "invalid VPD response; probably a STANDARD "
"INQUIRY response\n");
- return 1;
+ return SG_LIB_CAT_MALFORMED;
}
if (len > MX_ALLOC_LEN) {
fprintf(stderr, "response length too long: %d > %d\n", len,
MX_ALLOC_LEN);
- return 1;
+ return SG_LIB_CAT_MALFORMED;
} else if (len > DEF_ALLOC_LEN) {
if (sg_ll_inquiry(sg_fd, 0, 1, X_INQ_VPD, rsp_buff, len,
1, verbose))
- return 1;
+ return SG_LIB_CAT_OTHER;
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else
decode_x_inq_vpd(rsp_buff, len, do_hex);
- return 0;
}
break;
case ATA_INFO_VPD:
if (!do_raw)
printf("VPD INQUIRY: ATA information page\n");
- if (0 == sg_ll_inquiry(sg_fd, 0, 1, ATA_INFO_VPD, rsp_buff,
- ATA_INFO_VPD_LEN, 1, verbose)) {
+ res = sg_ll_inquiry(sg_fd, 0, 1, ATA_INFO_VPD, rsp_buff,
+ ATA_INFO_VPD_LEN, 1, verbose);
+ if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
if (ATA_INFO_VPD != rsp_buff[1]) {
fprintf(stderr, "invalid VPD response; probably a STANDARD "
"INQUIRY response\n");
- return 1;
+ return SG_LIB_CAT_MALFORMED;
}
if (len > MX_ALLOC_LEN) {
fprintf(stderr, "response length too long: %d > %d\n", len,
MX_ALLOC_LEN);
- return 1;
+ return SG_LIB_CAT_MALFORMED;
} else if (len > ATA_INFO_VPD_LEN) {
if (sg_ll_inquiry(sg_fd, 0, 1, ATA_INFO_VPD, rsp_buff, len,
1, verbose))
- return 1;
+ return SG_LIB_CAT_OTHER;
}
if (do_raw) {
if (2 == do_raw)
@@ -1567,12 +1667,12 @@ static int decode_vpd(int sg_fd, int num_opcode, int do_hex,
dStrRaw((const char *)rsp_buff, len);
} else
decode_ata_info_vpd(rsp_buff, len, do_hex);
- return 0;
}
break;
case 0xb0: /* could be BLOCK LIMITS but need to know pdt to find out */
- if (0 == sg_ll_inquiry(sg_fd, 0, 1, 0xb0, rsp_buff,
- DEF_ALLOC_LEN, 1, verbose)) {
+ res = sg_ll_inquiry(sg_fd, 0, 1, 0xb0, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
pdt = rsp_buff[0] & 0x1f;
if (! do_raw) {
switch (pdt) {
@@ -1595,44 +1695,44 @@ static int decode_vpd(int sg_fd, int num_opcode, int do_hex,
if (0xb0 != rsp_buff[1]) {
fprintf(stderr, "invalid VPD response; probably a STANDARD "
"INQUIRY response\n");
- return 1;
+ return SG_LIB_CAT_MALFORMED;
}
if (len > MX_ALLOC_LEN) {
fprintf(stderr, "response length too long: %d > %d\n", len,
MX_ALLOC_LEN);
- return 1;
+ return SG_LIB_CAT_MALFORMED;
} else if (len > DEF_ALLOC_LEN) {
if (sg_ll_inquiry(sg_fd, 0, 1, 0xb0, rsp_buff,
len, 1, verbose))
- return 1;
+ return SG_LIB_CAT_OTHER;
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else
decode_b0_vpd(rsp_buff, len, do_hex, pdt);
- return 0;
} else if (! do_raw)
printf("VPD INQUIRY: page=0xb0\n");
break;
- case UPR_EMC_VPD:
+ case UPR_EMC_VPD: /* 0xc0 */
if (!do_raw)
printf("VPD INQUIRY: Unit Path Report Page (EMC)\n");
- if (0 == sg_ll_inquiry(sg_fd, 0, 1, UPR_EMC_VPD, rsp_buff,
- DEF_ALLOC_LEN, 1, verbose)) {
- len = rsp_buff[3] + 3;
+ res = sg_ll_inquiry(sg_fd, 0, 1, UPR_EMC_VPD, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
+ len = rsp_buff[3] + 4;
if (UPR_EMC_VPD != rsp_buff[1]) {
fprintf(stderr, "invalid VPD response; probably not "
"supported\n");
- return 1;
+ return SG_LIB_CAT_MALFORMED;
}
if (len > MX_ALLOC_LEN) {
fprintf(stderr, "response length too long: %d > %d\n", len,
MX_ALLOC_LEN);
- return 1;
+ return SG_LIB_CAT_MALFORMED;
} else if (len > DEF_ALLOC_LEN) {
if (sg_ll_inquiry(sg_fd, 0, 1, UPR_EMC_VPD, rsp_buff, len, 1,
verbose))
- return 1;
+ return SG_LIB_CAT_OTHER;
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
@@ -1640,47 +1740,104 @@ static int decode_vpd(int sg_fd, int num_opcode, int do_hex,
dStrHex((const char *)rsp_buff, len, 1);
else
decode_upr_vpd_c0_emc(rsp_buff, len);
- return 0;
+ }
+ break;
+ case RDAC_VERS_VPD: /* 0xc2 */
+ if (!do_raw)
+ printf("VPD INQUIRY: Software Version (RDAC)\n");
+ res = sg_ll_inquiry(sg_fd, 0, 1, RDAC_VERS_VPD, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
+ len = rsp_buff[3] + 4;
+ if (RDAC_VERS_VPD != rsp_buff[1]) {
+ fprintf(stderr, "invalid VPD response; probably not "
+ "supported\n");
+ return SG_LIB_CAT_MALFORMED;
+ }
+ if (len > MX_ALLOC_LEN) {
+ fprintf(stderr, "response length too long: %d > %d\n", len,
+ MX_ALLOC_LEN);
+ return SG_LIB_CAT_MALFORMED;
+ } else if (len > DEF_ALLOC_LEN) {
+ if (sg_ll_inquiry(sg_fd, 0, 1, RDAC_VERS_VPD, rsp_buff, len, 1,
+ verbose))
+ return SG_LIB_CAT_OTHER;
+ }
+ if (do_raw)
+ dStrRaw((const char *)rsp_buff, len);
+ else if (do_hex)
+ dStrHex((const char *)rsp_buff, len, 1);
+ else
+ decode_rdac_vpd_c2(rsp_buff, len);
+ }
+ break;
+ case RDAC_VAC_VPD: /* 0xc9 */
+ if (!do_raw)
+ printf("VPD INQUIRY: Volume Access Control (RDAC)\n");
+ res = sg_ll_inquiry(sg_fd, 0, 1, RDAC_VAC_VPD, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
+ len = rsp_buff[3] + 4;
+ if (RDAC_VAC_VPD != rsp_buff[1]) {
+ fprintf(stderr, "invalid VPD response; probably not "
+ "supported\n");
+ return SG_LIB_CAT_MALFORMED;
+ }
+ if (len > MX_ALLOC_LEN) {
+ fprintf(stderr, "response length too long: %d > %d\n", len,
+ MX_ALLOC_LEN);
+ return SG_LIB_CAT_MALFORMED;
+ } else if (len > DEF_ALLOC_LEN) {
+ if (sg_ll_inquiry(sg_fd, 0, 1, RDAC_VAC_VPD, rsp_buff, len, 1,
+ verbose))
+ return SG_LIB_CAT_OTHER;
+ }
+ if (do_raw)
+ dStrRaw((const char *)rsp_buff, len);
+ else if (do_hex)
+ dStrHex((const char *)rsp_buff, len, 1);
+ else
+ decode_rdac_vpd_c9(rsp_buff, len);
}
break;
case SCSI_PORTS_VPD:
if (!do_raw)
printf("VPD INQUIRY: SCSI Ports page\n");
- if (0 == sg_ll_inquiry(sg_fd, 0, 1, SCSI_PORTS_VPD, rsp_buff,
- DEF_ALLOC_LEN, 1, verbose)) {
+ res = sg_ll_inquiry(sg_fd, 0, 1, SCSI_PORTS_VPD, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
if (SCSI_PORTS_VPD != rsp_buff[1]) {
fprintf(stderr, "invalid VPD response; probably a STANDARD "
"INQUIRY response\n");
- return 1;
+ return SG_LIB_CAT_MALFORMED;
}
if (len > MX_ALLOC_LEN) {
fprintf(stderr, "response length too long: %d > %d\n", len,
MX_ALLOC_LEN);
- return 1;
+ return SG_LIB_CAT_MALFORMED;
} else if (len > DEF_ALLOC_LEN) {
if (sg_ll_inquiry(sg_fd, 0, 1, SCSI_PORTS_VPD, rsp_buff, len,
1, verbose))
- return 1;
+ return SG_LIB_CAT_OTHER;
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else
decode_scsi_ports_vpd(rsp_buff, len, do_hex);
- return 0;
}
break;
default:
printf(" Only hex output supported\n");
return process_evpd(sg_fd, num_opcode, do_hex, do_raw, verbose);
}
- return 1;
+ return res;
}
int main(int argc, char * argv[])
{
- int sg_fd, k, num, plen, jmp_out;
+ int sg_fd, k, num, plen, jmp_out, res;
const char * file_name = 0;
const char * cp;
unsigned int num_opcode = 0; /* SUPPORTED_VPDS_VPD == 0 */
@@ -1790,7 +1947,7 @@ int main(int argc, char * argv[])
break;
case '?':
usage();
- return 1;
+ return 0;
default:
jmp_out = 1;
break;
@@ -1805,7 +1962,7 @@ int main(int argc, char * argv[])
if ((1 != num) || (num_opcode > 255)) {
fprintf(stderr, "Bad number after 'o=' option\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
num_opcode_given = 1;
++num_pages;
@@ -1814,7 +1971,7 @@ int main(int argc, char * argv[])
if ((1 != num) || (num_opcode > 255)) {
fprintf(stderr, "Bad number after '-p' switch\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
num_opcode_given = 1;
p_switch_given = 1;
@@ -1822,7 +1979,7 @@ int main(int argc, char * argv[])
} else if (jmp_out) {
fprintf(stderr, "Unrecognized option: %s\n", cp);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == file_name)
file_name = cp;
@@ -1830,24 +1987,24 @@ int main(int argc, char * argv[])
fprintf(stderr, "too many arguments, got: %s, not expecting: "
"%s\n", file_name, cp);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (do_raw && do_hex) {
fprintf(stderr, "Can't do hex and raw at the same time\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (do_evpd && do_cmddt) {
fprintf(stderr, "Can't have both '-e' and '-c' (or '-cl')\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (num_pages > 1) {
fprintf(stderr, "Can only fetch one page (VPD or Cmd) at a time\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (do_decode) {
if (num_pages)
@@ -1858,13 +2015,13 @@ int main(int argc, char * argv[])
fprintf(stderr, "version descriptors need > 36 byte "
"INQUIRY\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (do_cmddt || do_evpd) {
fprintf(stderr, "version descriptors require standard"
"INQUIRY\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
}
@@ -1872,12 +2029,12 @@ int main(int argc, char * argv[])
fprintf(stderr, "Can't use '-A' with an explicit decode VPD "
"page option\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (0 == file_name) {
fprintf(stderr, "No <device> argument given\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (num_pages && (! do_cmddt) && (! do_evpd)) {
@@ -1889,49 +2046,54 @@ int main(int argc, char * argv[])
if ((sg_fd = sg_cmds_open_device(file_name, 1 /* ro */, do_verbose)) < 0) {
fprintf(stderr, "sg_inq: error opening file: %s: %s\n",
file_name, safe_strerror(-sg_fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
memset(rsp_buff, 0, MX_ALLOC_LEN + 1);
#ifdef SG3_UTILS_LINUX
if (do_ata_device) {
- int res;
-
res = try_ata_identify(sg_fd, do_hex, do_raw, do_verbose);
if (0 != res) {
fprintf(stderr, "fetching ATA information failed on %s\n",
file_name);
- ret = 1;
+ ret = SG_LIB_CAT_OTHER;
} else
ret = 0;
goto err_out;
}
#endif
- ret = 1;
if ((! do_cmddt) && (! do_evpd)) {
/* So it's a Standard INQUIRY, try ATA IDENTIFY if that fails */
- if (process_std_inq(sg_fd, file_name, do_36, do_vdescriptors,
- do_hex, do_raw, do_verbose))
+ ret = process_std_inq(sg_fd, file_name, do_36, do_vdescriptors,
+ do_hex, do_raw, do_verbose);
+ if (ret)
goto err_out;
} else if (do_cmddt) {
- if (process_cmddt(sg_fd, do_cmdlst, num_opcode, do_hex, do_raw,
- do_verbose))
+ ret = process_cmddt(sg_fd, do_cmdlst, num_opcode, do_hex, do_raw,
+ do_verbose);
+ if (ret)
goto err_out;
} else if (do_evpd) {
if (num_opcode_given) {
- if (process_evpd(sg_fd, num_opcode, do_hex, do_raw, do_verbose))
+ ret = process_evpd(sg_fd, num_opcode, do_hex, do_raw, do_verbose);
+ if (ret)
goto err_out;
} else {
- if (decode_vpd(sg_fd, num_opcode, do_hex, do_raw, do_verbose))
+ ret = decode_vpd(sg_fd, num_opcode, do_hex, do_raw, do_verbose);
+ if (ret)
goto err_out;
}
}
- ret = 0;
err_out:
- sg_cmds_close_device(sg_fd);
- return ret;
+ res = sg_cmds_close_device(sg_fd);
+ if (res < 0) {
+ fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ return SG_LIB_FILE_ERROR;
+ }
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
@@ -1971,73 +2133,6 @@ struct ata_identify_device {
unsigned short words088_255[168];
};
-/* Copies n bytes (or n-1 if n is odd) from in to out, but swaps adjacents
- * bytes.
- */
-static void swapbytes(char *out, const char *in, size_t n)
-{
- size_t k;
-
- if (n > 1) {
- for (k = 0; k < (n - 1); k += 2) {
- out[k] = in[k + 1];
- out[k + 1] = in[k];
- }
- }
-}
-
-/* Copies in to out, but removes leading and trailing whitespace. */
-static void trim(char *out, const char *in)
-{
- int k, first, last;
-
- /* Find the first non-space character (maybe none). */
- first = -1;
- for (k = 0; in[k]; k++) {
- if (! isspace((int)in[k])) {
- first = k;
- break;
- }
- }
-
- if (first == -1) {
- /* There are no non-space characters. */
- out[0] = '\0';
- return;
- }
-
- /* Find the last non-space character. */
- for (k = strlen(in) - 1; k >= first && isspace((int)in[k]); k--)
- ;
- last = k;
- strncpy(out, in + first, last - first + 1);
- out[last - first + 1] = '\0';
-}
-
-/* Convenience function for formatting strings from ata_identify_device */
-static void formatdriveidstring(char *out, const char *in, int n)
-{
- char tmp[65];
-
- n = n > 64 ? 64 : n;
- swapbytes(tmp, in, n);
- tmp[n] = '\0';
- trim(out, tmp);
-}
-
-/* Function for printing ASCII byte-swapped strings, skipping white
- * space. Please note that this is needed on both big- and
- * little-endian hardware.
- */
-static void printswap(char *output, char *in, unsigned int n)
-{
- formatdriveidstring(output, in, n);
- if (*output)
- printf("%.*s ", (int)n, output);
- else
- printf("%.*s ", (int)n, "[Nothing Found]");
-}
-
#define ATA_IDENTIFY_BUFF_SZ sizeof(struct ata_identify_device)
#define HDIO_DRIVE_CMD_OFFSET 4
@@ -2096,7 +2191,7 @@ static int ata_command_interface(int device, char *data, int * atapi_flag,
}
} else if (atapi_flag)
*atapi_flag = 1;
- } else { /* assume non-packey device */
+ } else { /* assume non-packet device */
buff[0] = ATA_IDENTIFY_DEVICE;
buff[3] = 1;
if (ioctl(device, HDIO_DRIVE_CMD, buff) < 0) {
@@ -2147,15 +2242,18 @@ static int try_ata_identify(int ata_fd, int do_hex, int do_raw,
sg_is_big_endian());
}
} else {
- if (atapi)
- printf("ATAPI device:\n");
- else
- printf("ATA device (probably a disk):\n");
- printf(" ");
- printswap(model, (char *)ata_ident.model, 40);
- printswap(serial, (char *)ata_ident.serial_no, 20);
- printswap(firm, (char *)ata_ident.fw_rev, 8);
- printf("\n");
+ printf("%s device: model, serial number and firmware revision:\n",
+ (atapi ? "ATAPI" : "ATA"));
+ res = sg_ata_get_chars((const unsigned short *)ata_ident.model,
+ 0, 20, sg_is_big_endian(), model);
+ model[res] = '\0';
+ res = sg_ata_get_chars((const unsigned short *)ata_ident.serial_no,
+ 0, 10, sg_is_big_endian(), serial);
+ serial[res] = '\0';
+ res = sg_ata_get_chars((const unsigned short *)ata_ident.fw_rev,
+ 0, 4, sg_is_big_endian(), firm);
+ firm[res] = '\0';
+ printf(" %s %s %s\n", model, serial, firm);
if (verbose) {
if (atapi)
printf("ATA IDENTIFY PACKET DEVICE response "
@@ -2286,6 +2384,7 @@ static struct version_descriptor version_descriptor_arr[] = {
{0x480, "SMC-3 (no version claimed)"},
{0x4a0, "ADC-2 (no version claimed)"},
{0x4c0, "SBC-3 (no version claimed)"},
+ {0x4e0, "MMC-6 (no version claimed)"},
{0x820, "SSA-TL2 (no version claimed)"},
{0x83b, "SSA-TL2 T10/1147-D revision 05b"},
{0x83c, "SSA-TL2 ANSI INCITS 308-1998"},
@@ -2336,6 +2435,7 @@ static struct version_descriptor version_descriptor_arr[] = {
{0xa07, "FCP-3 T10/1560-D revision 3f"},
{0xa0f, "FCP-3 T10/1560-D revision 4"},
{0xa20, "ADT-2 (no version claimed)"},
+ {0xa40, "FCP-4 (no version claimed)"},
{0xaa0, "SPI (no version claimed)"},
{0xab9, "SPI T10/0855-D revision 15a"},
{0xaba, "SPI ANSI INCITS 253-1995"},
@@ -2376,6 +2476,7 @@ static struct version_descriptor version_descriptor_arr[] = {
{0xc00, "SAS-1.1 (no version claimed)"},
{0xc07, "SAS-1.1 T10/1602-D revision 9"},
{0xc0f, "SAS-1.1 T10/1602-D revision 10"},
+ {0xc11, "SAS-1.1 ANSI INCITS 417-2006"},
{0xc20, "SAS-2 (no version claimed)"},
{0xd20, "FC-PH (no version claimed)"},
{0xd3b, "FC-PH ANSI INCITS 230-1994"},
@@ -2431,7 +2532,7 @@ static struct version_descriptor version_descriptor_arr[] = {
{0x1620, "ATA/ATAPI-8 ATA-AAM Architecture model (no version claimed)"},
{0x1621, "ATA/ATAPI-8 ATA-PT Parallel transport (no version claimed)"},
{0x1622, "ATA/ATAPI-8 ATA-AST Serial transport (no version claimed)"},
- {0x1623, "ATA/ATAPI-8 ATA-ACS ATA?ATAPI command set (no version "
+ {0x1623, "ATA/ATAPI-8 ATA-ACS ATA/ATAPI command set (no version "
"claimed)"},
{0x1728, "Universal Serial Bus Specification, Revision 1.1"},
{0x1729, "Universal Serial Bus Specification, Revision 2.0"},
diff --git a/sg_io_linux.c b/sg_io_linux.c
index 4b9760b9..df135522 100644
--- a/sg_io_linux.c
+++ b/sg_io_linux.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999-2005 Douglas Gilbert.
+ * Copyright (c) 1999-2006 Douglas Gilbert.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -34,6 +34,9 @@
#include "sg_io_linux.h"
+/* Version 1.01 20060626 */
+
+
void sg_print_masked_status(int masked_status)
{
int scsi_status = (masked_status << 1) & 0x7e;
@@ -139,6 +142,9 @@ static int sg_linux_sense_print(const char * leadin, int scsi_status,
fprintf(sg_warnings_strm, "\n");
}
if (0 != driver_status) {
+ if (done_sense &&
+ (SG_LIB_DRIVER_SENSE == (SG_LIB_DRIVER_MASK & driver_status)))
+ return 0;
if (leadin && (! done_leadin))
fprintf(sg_warnings_strm, "%s: ", leadin);
if (done_leadin)
diff --git a/sg_io_linux.h b/sg_io_linux.h
index 99cea452..8f97c951 100644
--- a/sg_io_linux.h
+++ b/sg_io_linux.h
@@ -31,7 +31,7 @@
*/
/*
- * Version 1.00 [20051221]
+ * Version 1.01 [20060621]
*/
/*
@@ -161,8 +161,7 @@ extern int sg_normalize_sense(const struct sg_io_hdr * hp,
/* Some extra "category" function returns (beyond those in sg_lib.h) */
-#define SG_LIB_CAT_TIMEOUT 3
-#define SG_LIB_CAT_OTHER 99 /* Some other error/warning has occurred */
+#define SG_LIB_CAT_TIMEOUT 33
extern int sg_err_category(int masked_status, int host_status,
int driver_status, const unsigned char * sense_buffer,
diff --git a/sg_lib.c b/sg_lib.c
index f445bc0e..df9d75ea 100644
--- a/sg_lib.c
+++ b/sg_lib.c
@@ -68,7 +68,7 @@
#include "sg_lib.h"
-static char * version_str = "1.20 20060418"; /* spc-4 rev 04 */
+static char * version_str = "1.24 20060630"; /* spc-4 rev 5a */
FILE * sg_warnings_strm = NULL; /* would like to default to stderr */
@@ -546,8 +546,8 @@ static struct error_info additional[] =
{0x0B,0x01,"Warning - specified temperature exceeded"},
{0x0B,0x02,"Warning - enclosure degraded"},
{0x0B,0x03,"Warning - background self-test failed"},
- {0x0B,0x04,"Warning - background pre-scan failed"},
- {0x0B,0x05,"Warning - background medium scan failed"},
+ {0x0B,0x04,"Warning - background pre-scan detected medium error"},
+ {0x0B,0x05,"Warning - background medium scan detected medium error"},
{0x0C,0x00,"Write error"},
{0x0C,0x01,"Write error - recovered with auto reallocation"},
{0x0C,0x02,"Write error - auto reallocation failed"},
@@ -566,9 +566,9 @@ static struct error_info additional[] =
{0x0D,0x00,"Error detected by third party temporary initiator"},
{0x0D,0x01,"Third party device failure"},
{0x0D,0x02,"Copy target device not reachable"},
- {0x0D,0x03,"Incorrect copy target device"},
- {0x0D,0x04,"Copy target device underrun"},
- {0x0D,0x05,"Copy target device overrun"},
+ {0x0D,0x03,"Incorrect copy target device type"},
+ {0x0D,0x04,"Copy target device data underrun"},
+ {0x0D,0x05,"Copy target device data overrun"},
{0x0E,0x00,"Invalid information unit"},
{0x0E,0x01,"Information unit too short"},
{0x0E,0x02,"Information unit too long"},
@@ -650,9 +650,9 @@ static struct error_info additional[] =
{0x20,0x00,"Invalid command operation code"},
{0x20,0x01,"Access denied - initiator pending-enrolled"},
{0x20,0x02,"Access denied - no access rights"},
- {0x20,0x03,"Access denied - no mgmt id key"},
+ {0x20,0x03,"Access denied - invalid mgmt id key"},
{0x20,0x04,"Illegal command while in write capable state"},
- {0x20,0x05,"Obsolete"},
+ {0x20,0x05,"Write type operation while in read capable state (obs)"},
{0x20,0x06,"Illegal command while in explicit address mode"},
{0x20,0x07,"Illegal command while in implicit address mode"},
{0x20,0x08,"Access denied - enrollment conflict"},
@@ -666,6 +666,8 @@ static struct error_info additional[] =
{0x22,0x00,"Illegal function (use 20 00, 24 00, or 26 00)"},
{0x24,0x00,"Invalid field in cdb"},
{0x24,0x01,"CDB decryption error"},
+ {0x24,0x02,"Invalid cdb field while in explicit block model (obs)"},
+ {0x24,0x03,"Invalid cdb field while in implicit block model (obs)"},
{0x24,0x04,"Security audit value frozen"},
{0x24,0x05,"Security working key frozen"},
{0x24,0x06,"Nonce not unique"},
@@ -686,7 +688,10 @@ static struct error_info additional[] =
{0x26,0x0C,"Invalid operation for copy source or destination"},
{0x26,0x0D,"Copy segment granularity violation"},
{0x26,0x0E,"Invalid parameter while port is enabled"},
- {0x26,0x0F,"Invalid data-out buffer integrity"},
+ {0x26,0x0F,"Invalid data-out buffer integrity check value"},
+ {0x26,0x10,"Data decryption key fail limit reached"},
+ {0x26,0x11,"Incomplete key-associated data set"},
+ {0x26,0x12,"Vendor specific key reference not found"},
{0x27,0x00,"Write protected"},
{0x27,0x01,"Hardware write protected"},
{0x27,0x02,"Logical unit software write protected"},
@@ -716,6 +721,9 @@ static struct error_info additional[] =
{0x2A,0x08,"Priority changed"},
{0x2A,0x09,"Capacity data has changed"},
{0x2A,0x10,"Timestamp changed"},
+ {0x2A,0x11,"Data encryption parameters changed by another i_t nexus"},
+ {0x2A,0x12,"Data encryption parameters changed by vendor specific event"},
+ {0x2A,0x13,"Data encryption key instance counter has changed"},
{0x2B,0x00,"Copy cannot execute since host cannot disconnect"},
{0x2C,0x00,"Command sequence error"},
{0x2C,0x01,"Too many windows specified"},
@@ -745,8 +753,8 @@ static struct error_info additional[] =
{0x30,0x09,"Current session not fixated for append"},
{0x30,0x0A,"Cleaning request rejected"},
{0x30,0x0B,"Cleaning tape expired"},
- {0x30,0x0C,"WORM medium, overwrite attempted"},
- {0x30,0x0D,"Cleaning completed"},
+ {0x30,0x0C,"WORM medium - overwrite attempted"},
+ {0x30,0x0D,"WORM medium - integrity check"},
{0x30,0x10,"Medium not formatted"},
{0x31,0x00,"Medium format corrupted"},
{0x31,0x01,"Format command failed"},
@@ -761,7 +769,7 @@ static struct error_info additional[] =
{0x35,0x03,"Enclosure services transfer failure"},
{0x35,0x04,"Enclosure services transfer refused"},
{0x35,0x05,"Enclosure services checksum error"},
- {0x36,0x00,"Ribbon,ink,or toner failure"},
+ {0x36,0x00,"Ribbon, ink, or toner failure"},
{0x37,0x00,"Rounded parameter"},
{0x38,0x00,"Event status notification"},
{0x38,0x02,"Esn - power management class event"},
@@ -776,7 +784,7 @@ static struct error_info additional[] =
{0x3B,0x00,"Sequential positioning error"},
{0x3B,0x01,"Tape position error at beginning-of-medium"},
{0x3B,0x02,"Tape position error at end-of-medium"},
- {0x3B,0x03,"Tape or electronic vertical forms unit not ready, "},
+ {0x3B,0x03,"Tape or electronic vertical forms unit not ready"},
{0x3B,0x04,"Slew failure"},
{0x3B,0x05,"Paper jam"},
{0x3B,0x06,"Failed to sense top-of-form"},
@@ -841,7 +849,7 @@ static struct error_info additional[] =
{0x47,0x00,"SCSI parity error"},
{0x47,0x01,"Data phase CRC error detected"},
{0x47,0x02,"SCSI parity error detected during st data phase"},
- {0x47,0x03,"Information unit CRC error detected"},
+ {0x47,0x03,"Information unit iuCRC error detected"},
{0x47,0x04,"Asynchronous information protection error detected"},
{0x47,0x05,"Protocol service CRC error"},
{0x47,0x06,"Phy test function in progress"},
@@ -884,6 +892,7 @@ static struct error_info additional[] =
{0x55,0x05,"Insufficient access control resources"},
{0x55,0x06,"Auxiliary memory out of space"},
{0x55,0x07,"Quota error"},
+ {0x55,0x08,"Maximum number of supplemental decryption keys exceeded"},
{0x57,0x00,"Unable to recover table-of-contents"},
{0x58,0x00,"Generation does not exist"},
{0x59,0x00,"Updated block read"},
@@ -1016,6 +1025,7 @@ static struct error_info additional[] =
{0x67,0x08,"Assign failure occurred"},
{0x67,0x09,"Multiply assigned logical unit"},
{0x67,0x0A,"Set target port groups command failed"},
+ {0x67,0x0B,"ATA device feature not enabled"},
{0x68,0x00,"Logical unit not configured"},
{0x69,0x00,"Data loss on logical unit"},
{0x69,0x01,"Multiple logical unit failures"},
@@ -1057,10 +1067,17 @@ static struct error_info additional[] =
{0x73,0x03,"Power calibration area error"},
{0x73,0x04,"Program memory area update failure"},
{0x73,0x05,"Program memory area is full"},
- {0x73,0x06,"RMA/PMA is full"},
+ {0x73,0x06,"RMA/PMA is almost full"},
{0x73,0x10,"Current power calibration area almost full"},
{0x73,0x11,"Current power calibration area is full"},
{0x73,0x17,"RDZ is full"},
+ {0x74,0x00,"Security error"},
+ {0x74,0x01,"Unable to decrypt data"},
+ {0x74,0x02,"Unencrypted data encountered while decrypting"},
+ {0x74,0x03,"Incorrect data encryption key"},
+ {0x74,0x04,"Cryptographic integrity validation failed"},
+ {0x74,0x05,"Error decrypting data"},
+ {0x74,0x71,"Logical unit access not authorized"},
{0, 0, NULL}
};
@@ -1428,7 +1445,7 @@ static void sg_get_sense_descriptors_str(const unsigned char * sense_buffer,
processed = 0;
break;
case 9:
- n += sprintf(b + n, "ATA return\n");
+ n += sprintf(b + n, "ATA Status Return\n");
if (add_len >= 12) {
int extended, sector_count;
@@ -1673,7 +1690,7 @@ void sg_get_sense_str(const char * leadin,
r += sprintf(b + r, "Non-extended sense class %d code 0x%0x ",
(sense_buffer[0] >> 4) & 0x07, sense_buffer[0] & 0xf);
- n += snprintf(buff + n, buff_len - n, "%s", b);
+ n += snprintf(buff + n, buff_len - n, "%s\n", b);
if (n >= buff_len)
return;
len = 4;
@@ -1743,13 +1760,15 @@ int sg_err_category_sense(const unsigned char * sense_buffer, int sb_len)
return SG_LIB_CAT_NO_SENSE;
case SPC_SK_RECOVERED_ERROR:
return SG_LIB_CAT_RECOVERED;
+ case SPC_SK_NOT_READY:
+ return SG_LIB_CAT_NOT_READY;
case SPC_SK_MEDIUM_ERROR:
case SPC_SK_HARDWARE_ERROR:
+ case SPC_SK_BLANK_CHECK:
return SG_LIB_CAT_MEDIUM_HARD;
case SPC_SK_UNIT_ATTENTION:
- if (0x28 == ssh.asc)
- return SG_LIB_CAT_MEDIA_CHANGED;
- break;
+ return SG_LIB_CAT_UNIT_ATTENTION;
+ /* used to return SG_LIB_CAT_MEDIA_CHANGED when ssh.asc==0x28 */
case SPC_SK_ILLEGAL_REQUEST:
if ((0x20 == ssh.asc) && (0x0 == ssh.ascq))
return SG_LIB_CAT_INVALID_OP;
@@ -1912,6 +1931,32 @@ void sg_get_opcode_name(unsigned char cmd_byte0, int peri_type,
}
}
+int sg_vpd_dev_id_iter(const unsigned char * initial_desig_desc,
+ int page_len, int * off, int m_assoc,
+ int m_desig_type, int m_code_set)
+{
+ const unsigned char * ucp;
+ int k, c_set, assoc, desig_type;
+
+ for (k = *off, ucp = initial_desig_desc ; (k + 3) < page_len; ) {
+ k = (k < 0) ? 0 : (k + ucp[k + 3] + 4);
+ if ((k + 4) > page_len)
+ break;
+ c_set = (ucp[k] & 0xf);
+ if ((m_code_set >= 0) && (m_code_set != c_set))
+ continue;
+ assoc = ((ucp[k + 1] >> 4) & 0x3);
+ if ((m_assoc >= 0) && (m_assoc != assoc))
+ continue;
+ desig_type = (ucp[k + 1] & 0xf);
+ if ((m_desig_type >= 0) && (m_desig_type != desig_type))
+ continue;
+ *off = k;
+ return 0;
+ }
+ return (k == page_len) ? -1 : -2;
+}
+
/* safe_strerror() contributed by Clayton Weaver <cgweav at email dot com>
Allows for situation in which strerror() is given a wild value (or the
@@ -2345,6 +2390,37 @@ long long sg_get_llnum(const char * buf)
}
}
+/* Extract character sequence from ATA words as in the model string
+ in a IDENTIFY DEVICE response. Returns number of characters
+ written to 'ochars' before 0 character is found or 'num' words
+ are processed. */
+int sg_ata_get_chars(const unsigned short * word_arr, int start_word,
+ int num_words, int is_big_endian, char * ochars)
+{
+ int k;
+ unsigned short s;
+ char a, b;
+ char * op = ochars;
+
+ for (k = start_word; k < (start_word + num_words); ++k) {
+ s = word_arr[k];
+ if (is_big_endian) {
+ a = s & 0xff;
+ b = (s >> 8) & 0xff;
+ } else {
+ a = (s >> 8) & 0xff;
+ b = s & 0xff;
+ }
+ if (a == 0)
+ break;
+ *op++ = a;
+ if (b == 0)
+ break;
+ *op++ = b;
+ }
+ return op - ochars;
+}
+
const char * sg_lib_version()
{
return version_str;
diff --git a/sg_lib.h b/sg_lib.h
index 5964659e..253ad8b4 100644
--- a/sg_lib.h
+++ b/sg_lib.h
@@ -30,7 +30,7 @@
*
*/
-/* Version 1.20 [20050418]
+/* Version 1.24 [20050630]
*
* On 5th October 2004 a FreeBSD license was added to this file.
* The intention is to keep this file and the related sg_lib.c file
@@ -162,10 +162,11 @@ extern int sg_get_sense_progress_fld(const unsigned char * sensep,
int sb_len, int * progress_outp);
/* Closely related to sg_print_sense(). Puts decode sense data in 'buff'.
- Usually multiline with multiple '\n' including one trailing. */
+ Usually multiline with multiple '\n' including one trailing. If
+ 'raw_sinfo' set appends sense buffer in hex. */
extern void sg_get_sense_str(const char * leadin,
const unsigned char * sense_buffer, int sb_len,
- int raw_info, int buff_len, char * buff);
+ int raw_sinfo, int buff_len, char * buff);
/* Yield string associated with peripheral device type (pdt). Returns
'buff'. If 'pdt' out of range yields "bad pdt" string. */
@@ -183,25 +184,52 @@ extern void sg_print_sense(const char * leadin,
int raw_info);
extern void sg_print_scsi_status(int scsi_status);
-/* The following "category" function returns one of the following */
+/* Utilities can use these process status values for syntax errors and
+ file (device node) problems (e.g. not found or permissions). */
+#define SG_LIB_SYNTAX_ERROR 1
+#define SG_LIB_FILE_ERROR 15
+
+/* The sg_err_category_sense() function returns one of the following.
+ These may be used as process status values (on exit). Notice that
+ some of the lower values correspond to SCSI sense key values. */
#define SG_LIB_CAT_CLEAN 0 /* No errors or other information */
-#define SG_LIB_CAT_MEDIA_CHANGED 1 /* interpreted from sense buffer */
- /* [sk,asc,ascq: 0x6,0x28,*] */
-#define SG_LIB_CAT_RECOVERED 4 /* Successful command after recovered err */
- /* [sk,asc,ascq: 0x1,*,*] */
-#define SG_LIB_CAT_INVALID_OP 5 /* Invalid operation code: */
- /* [sk,asc,ascq: 0x5,0x20,0x0] */
-#define SG_LIB_CAT_MEDIUM_HARD 6 /* medium or hardware error sense key */
- /* [sk,asc,ascq: 0x3/0x4,*,*] */
-#define SG_LIB_CAT_ILLEGAL_REQ 7 /* Illegal request (other than invalid */
+/* Value 1 left unused for utilities to use SG_LIB_SYNTAX_ERROR */
+#define SG_LIB_CAT_NOT_READY 2 /* interpreted from sense buffer */
+ /* [sk,asc,ascq: 0x2,*,*] */
+#define SG_LIB_CAT_MEDIUM_HARD 3 /* medium or hardware error, blank check */
+ /* [sk,asc,ascq: 0x3/0x4/0x8,*,*] */
+#define SG_LIB_CAT_ILLEGAL_REQ 5 /* Illegal request (other than invalid */
/* opcode): [sk,asc,ascq: 0x5,*,*] */
-#define SG_LIB_CAT_NO_SENSE 8 /* sense data with key of "no sense" */
+#define SG_LIB_CAT_UNIT_ATTENTION 6 /* interpreted from sense buffer */
+ /* [sk,asc,ascq: 0x6,*,*] */
+ /* was SG_LIB_CAT_MEDIA_CHANGED earlier [sk,asc,ascq: 0x6,0x28,*] */
+#define SG_LIB_CAT_INVALID_OP 9 /* (Illegal request,) Invalid opcode: */
+ /* [sk,asc,ascq: 0x5,0x20,0x0] */
+#define SG_LIB_CAT_NO_SENSE 10 /* sense data with key of "no sense" */
/* [sk,asc,ascq: 0x0,*,*] */
+#define SG_LIB_CAT_RECOVERED 11 /* Successful command after recovered err */
+ /* [sk,asc,ascq: 0x1,*,*] */
+#define SG_LIB_CAT_MALFORMED 97 /* Response to SCSI command malformed */
#define SG_LIB_CAT_SENSE 98 /* Something else is in the sense buffer */
+#define SG_LIB_CAT_OTHER 99 /* Some other error/warning has occurred
+ (e.g. a transport or driver error) */
extern int sg_err_category_sense(const unsigned char * sense_buffer,
int sb_len);
+/* Iterates to next designation descriptor in the device identification
+ VPD page. The 'initial_desig_desc' should point to start of first
+ descriptor with 'page_len' being the number of valid bytes in that
+ and following descriptors. To start, 'off' should point to a negative
+ value, thereafter it should point to the value yielded by the previous
+ call. If 0 returned then 'initial_desig_desc + *off' should be a valid
+ descriptor; returns -1 if normal end condition and -2 for an abnormal
+ termination. Matches association, designator_type and/or code_set when
+ any of those values are greater than or equal to zero. */
+extern int sg_vpd_dev_id_iter(const unsigned char * initial_desig_desc,
+ int page_len, int * off, int m_assoc,
+ int m_desig_type, int m_code_set);
+
/* <<< General purpose (i.e. not SCSI specific) utility functions >>> */
@@ -226,6 +254,13 @@ extern void dStrHex(const char* str, int len, int no_ascii);
*/
extern int sg_is_big_endian();
+/* Extract character sequence from ATA words as in the model string
+ in a IDENTIFY DEVICE response. Returns number of characters
+ written to 'ochars' before 0 character is found or 'num' words
+ are processed. */
+extern int sg_ata_get_chars(const unsigned short * word_arr, int start_word,
+ int num_words, int is_big_endian, char * ochars);
+
/* Print (to stdout) 16 bit 'words' in hex, 8 words per line optionally
followed at the right hand side of the line with an ASCII interpretation
(pairs of ASCII characters in big endian order (upper first)).
diff --git a/sg_logs.8 b/sg_logs.8
index d81b0b07..6d50482f 100644
--- a/sg_logs.8
+++ b/sg_logs.8
@@ -1,26 +1,42 @@
-.TH SG_LOGS "8" "December 2005" "sg3_utils-1.19" SG3_UTILS
+.TH SG_LOGS "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_logs \- read SCSI device log pages
.SH SYNOPSIS
.B sg_logs
-[\fI-a\fR] [\fI-c=<page_control>\fR] [\fI-h\fR] [\fI-H\fR] [\fI-l\fR]
-[\fI-p=<page_code>\fR] [\fI-paramp=<parameter_pointer>\fR] [\fI-pcb\fR]
-[\fI-ppc\fR] [\fI-r\fR] [\fI-scum\fR] [\fI-sp\fR] [\fI-sthr\fR] [\fI-t\fR]
-[\fI-v\fR] [\fI-V\fR] [\fI-?\fR] \fI<scsi_device>\fR
+[\fI-a\fR] [\fI-A\fR] [\fI-c=<page_control>\fR] [\fI-h\fR] [\fI-H\fR]
+[\fI-l\fR] [\fI-L\fR] [\fI-p=<page_code>[,<subpage_code]\fR]
+[\fI-paramp=<parameter_pointer>\fR] [\fI-pcb\fR] [\fI-ppc\fR] [\fI-r\fR]
+[\fI-scum\fR] [\fI-sp\fR] [\fI-sthr\fR] [\fI-t\fR] [\fI-T\fR] [\fI-v\fR]
+[\fI-V\fR] [\fI-?\fR] \fI<scsi_device>\fR
.SH DESCRIPTION
.\" Add any additional description here
.PP
This utility sends a LOG SENSE SCSI command to the given device and then
outputs the response. This is used in most cases to fetch log pages
stored on the given device and known log pages are decoded and output
-in ASCII. In one case a LOG SELECT SCSI command is issued to reset
+in ASCII. In some cases a LOG SELECT SCSI command is issued to reset
parameters back to the device manufacturer's default values.
.PP
+In SPC-4 revision 5 a subpage code was introduced to both the LOG
+SENSE and LOG SELECT command. At the same time a page code field was
+introduced to the to the LOG SELECT command. The log subpage code
+can range from 0 to ff (255) inclusive. The subpage code value 'ff'
+can be thought of as a wildcard.
+.PP
Options with arguments or with two or more letters can have an extra '-'
prepended. For example: both '-pcb' and '--pcb' are acceptable.
.TP
-a
-outputs all the log pages supported by the device.
+outputs all the log pages supported by the device. This requires
+a two stage process: first the supported log pages log page is
+fetched, then for each entry in the response, the corresponding
+log page is fetched and displayed.
+.TP
+-A
+outputs all the log pages and subpages supported by the device. This
+requires a two stage process: first the supported log pages and subpages
+log page is fetched, then for each entry in the response, the corresponding
+log page is fetched and displayed.
.TP
-c=page_control
accepts 0, 1, 2 or 3 as an argument. 0 for current threshold values,
@@ -39,9 +55,19 @@ same action as '-h'.
lists the names of all logs sense pages supported by this device.
See the list of common log page codes below.
.TP
+-L
+lists the names of all logs sense pages and subpages supported by this
+device. See the list of common log page codes below.
+.TP
-p=<page_code>
-log page code to fetch. Should be a hexadecimal number between 0 and 3f
-inclusive.
+log page code to access. Should be a hexadecimal number between 0 and 3f
+inclusive. Commonly accessed log pages are listed below.
+.TP
+-p=<page_code>,<subpage_code>
+log page code and subpage code to access. The page code should be a
+hexadecimal number between 0 and 3f inclusive. The subpage code should be
+a hexadecimal number between 0 and ff inclusive. The subpage code of 'ff'
+can be thought of as a wildcard.
.TP
-paramp=<parameter_pointer>
parameter pointer value (in hex) to place in command. Should be a number
@@ -55,18 +81,20 @@ being output in ASCII).
sets the Parameter Pointer Control (PPC) bit. Default is 0 (i.e. cleared).
.TP
-r
-use LOG SELECT SCSI command (PCR bit) to reset all implemented parameter
-values to be the device defined (i.e. manufacturer's) default values. In
-most cases the default values are zeroes. This option seems to clear error
-counter log pages but leaves pages like self-test results, start-stop
-cycle counter and temperature log pages unaffected. This option may be
-required to clear log pages if a counter reaches its maximum value since
- the log page in which the counter is found will remain "stuck" until
-something is done. Most other options are ignored.
+use LOG SELECT SCSI command (PCR bit) to reset the given page or all pages
+to the implemented parameter values to be the device defined (i.e.
+manufacturer's) default values. In most cases the default values are zeroes.
+This option seems to clear error counter log pages but leaves pages like
+self-test results, start-stop cycle counter and temperature log pages
+unaffected. This option may be required to clear log pages if a counter
+reaches its maximum value since the log page in which the counter is found
+will remain "stuck" until something is done. Most other options are ignored.
+Prior to SPC-4 revision 5 the LOG SELECT command did not take a page
+code (or a subpage code).
.TP
-scum
-sets all cumulative parameters to their default values. Uses LOG SELECT
-SCSI command with pc=3 .
+sets all (or specified log page) cumulative parameters to their default
+values. Uses LOG SELECT SCSI command with pc=3 .
.TP
-sp
sets the Saving Parameters (SP) bit. Default is 0 (i.e. cleared). When
@@ -79,14 +107,18 @@ the device to periodically save all saveable log parameters to
non-volatile locations.
.TP
-sthr
-sets the current threshold parameters to the default threshold values.
-Uses LOG SELECT SCSI command with pc=2 .
+sets all (or specified log page) current threshold parameters to the default
+threshold values. Uses LOG SELECT SCSI command with pc=2 .
.TP
-t
outputs the temperature. First looks in the temperature log page and if
that is not available tries the Informational Exceptions page which may also
have the current temperature (especially in older disks).
.TP
+-T
+outputs the transport ('Protocol specific port') log page. Equivalent
+to setting '-p=18'.
+.TP
-v
verbose: show SCSI command blocks prior to execution. '-vv'
and '-vvv' are also accepted yielding greater verbosity.
@@ -151,12 +183,15 @@ 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 "sg_logs -a /dev/sda"
will work in the 2.6 series kernels.
+.SH EXIT STATUS
+The exit status of sg_logs is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHOR
Written by Doug Gilbert
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2002-2005 Douglas Gilbert
+Copyright \(co 2002-2006 Douglas Gilbert
.br
This software is distributed under the GPL version 2. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/sg_logs.c b/sg_logs.c
index 9a247fe9..b63ce438 100644
--- a/sg_logs.c
+++ b/sg_logs.c
@@ -19,12 +19,13 @@
*/
-static char * version_str = "0.53 20060316";
+static char * version_str = "0.58 20060705"; /* SPC-4 revision 5a */
#define ME "sg_logs: "
#define MX_ALLOC_LEN (1024 * 17)
#define PG_CODE_ALL 0x0
+#define SUBPG_CODE_ALL 0xff
#define PCB_STR_LEN 128
@@ -35,20 +36,27 @@ static char * version_str = "0.53 20060316";
second fetch is odd then it is incremented (perhaps should be made modulo 4
in the future for SAS). Returns 0 if ok, SG_LIB_CAT_INVALID_OP for
log_sense not supported, SG_LIB_CAT_ILLEGAL_REQ for bad field in log sense
- command nd -1 for other errors. */
+ command, SG_LIB_CAT_NOT_READY, SG_LIB_CAT_UNIT_ATTENTION,
+ and -1 for other errors. */
static int do_logs(int sg_fd, int ppc, int sp, int pc, int pg_code,
- int paramp, unsigned char * resp, int mx_resp_len,
- int noisy, int verbose)
+ int subpg_code, int paramp, unsigned char * resp,
+ int mx_resp_len, int noisy, int verbose)
{
int actual_len;
int res;
memset(resp, 0, mx_resp_len);
- if ((res = sg_ll_log_sense(sg_fd, ppc, sp, pc, pg_code, paramp, resp,
- 4, noisy, verbose))) {
- if ((SG_LIB_CAT_INVALID_OP == res) || (SG_LIB_CAT_ILLEGAL_REQ == res))
+ if ((res = sg_ll_log_sense(sg_fd, ppc, sp, pc, pg_code, subpg_code,
+ paramp, resp, 4, noisy, verbose))) {
+ switch (res) {
+ case SG_LIB_CAT_NOT_READY:
+ case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
return res;
- return -1;
+ default:
+ return -1;
+ }
}
actual_len = (resp[2] << 8) + resp[3] + 4;
if (verbose > 1) {
@@ -62,11 +70,17 @@ static int do_logs(int sg_fd, int ppc, int sp, int pc, int pg_code,
actual_len += 1;
if (actual_len > mx_resp_len)
actual_len = mx_resp_len;
- if ((res = sg_ll_log_sense(sg_fd, ppc, sp, pc, pg_code, paramp, resp,
- actual_len, noisy, verbose))) {
- if ((SG_LIB_CAT_INVALID_OP == res) || (SG_LIB_CAT_ILLEGAL_REQ == res))
+ if ((res = sg_ll_log_sense(sg_fd, ppc, sp, pc, pg_code, subpg_code,
+ paramp, resp, actual_len, noisy, verbose))) {
+ switch (res) {
+ case SG_LIB_CAT_NOT_READY:
+ case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
return res;
- return -1;
+ default:
+ return -1;
+ }
}
if (verbose > 1) {
fprintf(stderr, " Log sense response:\n");
@@ -77,20 +91,24 @@ static int do_logs(int sg_fd, int ppc, int sp, int pc, int pg_code,
static void usage()
{
- printf("Usage: sg_logs [-a] [-c=<page_control] [-h] [-H] [-l] "
- "[-p=<page_number>]\n"
- " [-p=<page_number>] "
- "[-paramp=<parameter_pointer>] [-ppc]\n"
- " [-scum] [-sp] [-sthr] [-t] [-v] [-V] "
- "<scsi_device>\n"
+ printf("Usage: sg_logs [-a] [-A] [-c=<page_control] [-h] [-H] [-l] "
+ "[-L]\n"
+ " [-p=<page_number>[,<subpage_code>]]\n"
+ " [-paramp=<parameter_pointer>] [-ppc] [-r] "
+ "[-scum] [-sp]\n"
+ " [-sthr] [-t] [-T] [-v] [-V] <scsi_device>\n"
" where: -a output all log pages\n"
+ " -A output all log pages and subpages\n"
" -c=<page_control> page control(PC) (default: 1)\n"
- " (0 [current threshhold], 1 [current cumulative]\n"
- " 2 [default threshhold], 3 [default cumulative])\n"
+ " 0: current threshhold, 1: current cumulative\n"
+ " 2: default threshhold, 3: default cumulative\n"
" -h output in hex\n"
" -H output in hex (same as '-h)\n"
" -l list supported log page names of given device\n"
+ " -L list supported log page and subpages names of "
+ "given device\n"
" -p=<page_code> page code (in hex)\n"
+ " -p=<page_code>,<subpage_code> both in hex, (defs: 0)\n"
" -paramp=<parameter_pointer> (in hex) (def: 0)\n"
" -pcb show parameter control bytes (ignored if -h "
"given)\n");
@@ -104,66 +122,89 @@ static void usage()
" -sthr set threshold parameters to default threshold "
"values\n"
" -t outputs temperature log page (0xd)\n"
+ " -T outputs transport (protocol specific port) log "
+ "page (0x18)\n"
" -v verbose: output cdbs prior to execution\n"
" -V output version string\n"
" -? output this usage message\n\n"
"Performs a SCSI LOG SENSE (or SELECT) command\n");
}
-static void show_page_name(int page_no,
+static void show_page_name(int pg_code, int subpg_code,
struct sg_simple_inquiry_resp * inq_dat)
{
int done;
+ char b[64];
+ memset(b, 0, sizeof(b));
/* first process log pages that do not depend on peripheral type */
+ if (0 == subpg_code)
+ snprintf(b, sizeof(b) - 1, " 0x%02x ", pg_code);
+ else
+ snprintf(b, sizeof(b) - 1, " 0x%02x,0x%02x ", pg_code,
+ subpg_code);
done = 1;
- switch (page_no) {
- case 0x0: printf(" 0x00 Supported log pages\n"); break;
- case 0x1: printf(" 0x01 Buffer over-run/under-run\n"); break;
- case 0x2: printf(" 0x02 Error counters (write)\n"); break;
- case 0x3: printf(" 0x03 Error counters (read)\n"); break;
- case 0x4: printf(" 0x04 Error counters (read reverse)\n"); break;
- case 0x5: printf(" 0x05 Error counters (verify)\n"); break;
- case 0x6: printf(" 0x06 Non-medium errors\n"); break;
- case 0x7: printf(" 0x07 Last n error events\n"); break;
- case 0xb: printf(" 0x0b Last n deferred errors or "
- "asynchronous events\n"); break;
- case 0xd: printf(" 0x0d Temperature\n"); break;
- case 0xe: printf(" 0x0e Start-stop cycle counter\n"); break;
- case 0xf: printf(" 0x0f Application client\n"); break;
- case 0x10: printf(" 0x10 Self-test results\n"); break;
- case 0x18: printf(" 0x18 Protocol specific port\n"); break;
- case 0x2f: printf(" 0x2f Informational exceptions (SMART)\n");
- break;
- default : done = 0; break;
+ if ((0 == subpg_code) || (0xff == subpg_code)) {
+ switch (pg_code) {
+ case 0x0: printf("%sSupported log pages", b); break;
+ case 0x1: printf("%sBuffer over-run/under-run", b); break;
+ case 0x2: printf("%sError counters (write)", b); break;
+ case 0x3: printf("%sError counters (read)", b); break;
+ case 0x4: printf("%sError counters (read reverse)", b); break;
+ case 0x5: printf("%sError counters (verify)", b); break;
+ case 0x6: printf("%sNon-medium errors", b); break;
+ case 0x7: printf("%sLast n error events", b); break;
+ case 0xb: printf("%sLast n deferred errors or "
+ "asynchronous events", b); break;
+ case 0xd: printf("%sTemperature", b); break;
+ case 0xe: printf("%sStart-stop cycle counter", b); break;
+ case 0xf: printf("%sApplication client", b); break;
+ case 0x10: printf("%sSelf-test results", b); break;
+ case 0x18: printf("%sProtocol specific port", b); break;
+ case 0x19: printf("%sGeneral statistics and performance", b); break;
+ case 0x2f: printf("%sInformational exceptions (SMART)", b); break;
+ default : done = 0; break;
+ }
+ if (done) {
+ if (0xff == subpg_code)
+ printf(" and subpages\n");
+ else
+ printf("\n");
+ return;
+ }
}
- if (done)
+ if ((0x19 == pg_code) && (subpg_code > 0) && (subpg_code < 32)) {
+ printf("%sGroup statistics and performance (%d)\n", b, subpg_code);
+ return;
+ }
+ if (subpg_code > 0) {
+ printf("%s??\n", b);
return;
+ }
done = 1;
switch (inq_dat->peripheral_type) {
case 0: case 4: case 7: case 0xe:
/* disk (direct access) type devices */
{
- switch (page_no) {
+ switch (pg_code) {
case 0x8:
- printf(" 0x08 Format status (sbc-2)\n");
+ printf("%sFormat status (sbc-2)\n", b);
break;
case 0x15:
- printf(" 0x15 Background scan results (sbc-3)\n");
+ printf("%sBackground scan results (sbc-3)\n", b);
break;
case 0x17:
- printf(" 0x17 Non-volatile cache (sbc-2)\n");
+ printf("%sNon-volatile cache (sbc-2)\n", b);
break;
case 0x30:
- printf(" 0x30 Performance counters (Hitachi)\n");
+ printf("%sPerformance counters (Hitachi)\n", b);
break;
case 0x37:
- printf(" 0x37 Cache (Seagate), Miscellaneous"
- " (Hitachi)\n");
+ printf("%sCache (Seagate), Miscellaneous (Hitachi)\n", b);
break;
case 0x3e:
- printf(" 0x3e Factory (Seagate/Hitachi)\n");
+ printf("%sFactory (Seagate/Hitachi)\n", b);
break;
default:
done = 0;
@@ -174,15 +215,18 @@ static void show_page_name(int page_no,
case 1: case 2: case 8:
/* tape (streaming) and medium changer type devices */
{
- switch (page_no) {
+ switch (pg_code) {
case 0xc:
- printf(" 0x0c Sequential access device (ssc-2)\n");
+ printf("%sSequential access device (ssc-2)\n", b);
break;
case 0x14:
- printf(" 0x14 Device statistics (ssc-3)\n");
+ printf("%sDevice statistics (ssc-3)\n", b);
+ break;
+ case 0x16:
+ printf("%sTape diagnostic (ssc-3)\n", b);
break;
case 0x2e:
- printf(" 0x2e TapeAlert (ssc-2)\n");
+ printf("%sTapeAlert (ssc-2)\n", b);
break;
default:
done = 0;
@@ -191,21 +235,21 @@ static void show_page_name(int page_no,
}
case 0x12: /* Automation Device interface (ADC) */
{
- switch (page_no) {
+ switch (pg_code) {
case 0x11:
- printf(" 0x11 DTD status (adc)\n");
+ printf("%sDTD status (adc)\n", b);
break;
case 0x12:
- printf(" 0x12 Tape alert response (adc)\n");
+ printf("%sTape alert response (adc)\n", b);
break;
case 0x13:
- printf(" 0x13 Requested recovery (adc)\n");
+ printf("%sRequested recovery (adc)\n", b);
break;
case 0x14:
- printf(" 0x14 Device statistics (adc)\n");
+ printf("%sDevice statistics (adc)\n", b);
break;
case 0x15:
- printf(" 0x15 Service buffers information (adc)\n");
+ printf("%sService buffers information (adc)\n", b);
break;
default:
done = 0;
@@ -218,7 +262,7 @@ static void show_page_name(int page_no,
if (done)
return;
- printf(" 0x%.2x ??\n", page_no);
+ printf("%s??\n", b);
}
static void get_pcb_str(int pcb, char * outp, int maxoutlen)
@@ -613,7 +657,8 @@ static void show_Temperature_page(unsigned char * resp, int len,
}
}
-static void show_Start_Stop_page(unsigned char * resp, int len, int show_pcb)
+static void show_Start_Stop_page(unsigned char * resp, int len, int show_pcb,
+ int verbose)
{
int k, num, extra, pc, pcb;
unsigned int n;
@@ -637,20 +682,34 @@ static void show_Start_Stop_page(unsigned char * resp, int len, int show_pcb)
pcb = ucp[2];
switch (pc) {
case 1:
- if (extra > 9)
+ if (10 == extra)
printf(" Date of manufacture, year: %.4s, week: %.2s",
&ucp[4], &ucp[8]);
+ else if (verbose) {
+ printf(" Date of manufacture parameter length "
+ "strange: %d\n", extra - 4);
+ dStrHex((const char *)ucp, extra, 1);
+ }
break;
case 2:
- if (extra > 9)
+ if (10 == extra)
printf(" Accounting date, year: %.4s, week: %.2s",
&ucp[4], &ucp[8]);
+ else if (verbose) {
+ printf(" Accounting date parameter length strange: %d\n",
+ extra - 4);
+ dStrHex((const char *)ucp, extra, 1);
+ }
break;
case 3:
if (extra > 7) {
n = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
- printf(" Specified cycle count over device lifetime = %u",
- n);
+ if (0xffffffff == n)
+ printf(" Specified cycle count over device lifetime "
+ "= -1");
+ else
+ printf(" Specified cycle count over device lifetime "
+ "= %u", n);
}
break;
case 4:
@@ -680,6 +739,7 @@ static void show_IE_page(unsigned char * resp, int len, int show_pcb, int full)
int k, num, extra, pc, pcb;
unsigned char * ucp;
char pcb_str[PCB_STR_LEN];
+ char b[256];
num = len - 4;
ucp = &resp[0] + 4;
@@ -699,8 +759,13 @@ static void show_IE_page(unsigned char * resp, int len, int show_pcb, int full)
pcb = ucp[2];
if (0 == pc) {
if (extra > 5) {
- if (full)
+ if (full) {
printf(" IE asc = 0x%x, ascq = 0x%x", ucp[4], ucp[5]);
+ if (ucp[4]) {
+ if(sg_get_asc_ascq_str(ucp[4], ucp[5], sizeof(b), b))
+ printf("\n [%s]", b);
+ }
+ }
if (extra > 6) {
if (ucp[6] < 0xff)
printf("\n Current temperature = %d C", ucp[6]);
@@ -905,15 +970,17 @@ static int show_protocol_specific_page(unsigned char * resp, int len,
break;
case 3: snprintf(s, sz, "phy enabled; SATA spinup hold state");
break;
+ case 4: snprintf(s, sz, "phy enabled; port selector");
+ break;
case 8: snprintf(s, sz, "phy enabled; 1.5 Gbps"); break;
case 9: snprintf(s, sz, "phy enabled; 3 Gbps"); break;
case 0xa: snprintf(s, sz, "phy enabled; 6 Gbps"); break;
default: snprintf(s, sz, "reserved [%d]", t); break;
}
printf(" negotiated physical link rate: %s\n", s);
- printf(" attached initiator port: ssp=%d, stp=%d smp=%d\n",
+ printf(" attached initiator port: ssp=%d stp=%d smp=%d\n",
!! (vcp[6] & 8), !! (vcp[6] & 4), !! (vcp[6] & 2));
- printf(" attached target port: ssp=%d, stp=%d smp=%d\n",
+ printf(" attached target port: ssp=%d stp=%d smp=%d\n",
!! (vcp[7] & 8), !! (vcp[7] & 4), !! (vcp[7] & 2));
ull = vcp[8]; ull <<= 8; ull |= vcp[9]; ull <<= 8; ull |= vcp[10];
ull <<= 8; ull |= vcp[11]; ull <<= 8; ull |= vcp[12];
@@ -1507,7 +1574,7 @@ static void show_ascii_page(unsigned char * resp, int len, int show_pcb,
struct sg_simple_inquiry_resp * inq_dat,
int verbose)
{
- int k, num, done, page_code, spage_code;
+ int k, num, done, pg_code, subpg_code, spf;
if (len < 0) {
printf("response has bad length\n");
@@ -1515,14 +1582,29 @@ static void show_ascii_page(unsigned char * resp, int len, int show_pcb,
}
num = len - 4;
done = 1;
- page_code = resp[0] & 0x3f;
- spage_code = (resp[0] & 0x40) ? resp[1] : 0;
-
- switch (page_code) {
+ spf = !!(resp[0] & 0x40);
+ pg_code = resp[0] & 0x3f;
+ subpg_code = spf ? resp[1] : 0;
+
+ if ((0 != pg_code ) && (0xff == subpg_code)) {
+ printf("Supported subpages for log page=0x%x\n", pg_code);
+ for (k = 0; k < num; k += 2)
+ show_page_name((int)resp[4 + k], (int)resp[4 + k + 1],
+ inq_dat);
+ return;
+ }
+ switch (pg_code) {
case 0:
- printf("Supported pages:\n");
- for (k = 0; k < num; ++k)
- show_page_name((int)resp[4 + k], inq_dat);
+ if (spf) {
+ printf("Supported log pages and subpages:\n");
+ for (k = 0; k < num; k += 2)
+ show_page_name((int)resp[4 + k], (int)resp[4 + k + 1],
+ inq_dat);
+ } else {
+ printf("Supported log pages:\n");
+ for (k = 0; k < num; ++k)
+ show_page_name((int)resp[4 + k], 0, inq_dat);
+ }
break;
case 0x1:
show_buffer_under_overrun_page(resp, len, show_pcb);
@@ -1572,7 +1654,7 @@ static void show_ascii_page(unsigned char * resp, int len, int show_pcb,
show_Temperature_page(resp, len, show_pcb, 1, 1);
break;
case 0xe:
- show_Start_Stop_page(resp, len, show_pcb);
+ show_Start_Stop_page(resp, len, show_pcb, verbose);
break;
case 0x10:
show_self_test_page(resp, len, show_pcb);
@@ -1675,17 +1757,21 @@ static int fetchTemperature(int sg_fd, unsigned char * resp, int max_len,
{
int res = 0;
- if (0 == do_logs(sg_fd, 0, 0, 1, 0xd, 0, resp, max_len, 0, verbose))
+ res = do_logs(sg_fd, 0, 0, 1, 0xd, 0, 0, resp, max_len, 0, verbose);
+ if (0 == res)
show_Temperature_page(resp, (resp[2] << 8) + resp[3] + 4, 0, 0, 0);
- else if (0 == do_logs(sg_fd, 0, 0, 1, 0x2f, 0, resp, max_len, 0, verbose))
- show_IE_page(resp, (resp[2] << 8) + resp[3] + 4, 0, 0);
+ else if (SG_LIB_CAT_NOT_READY == res)
+ fprintf(stderr, "Device not ready\n");
else {
- printf("Unable to find temperature in either log page (temperature "
- "or IE)\n");
- res = 1;
+ res = do_logs(sg_fd, 0, 0, 1, 0x2f, 0, 0, resp, max_len, 0, verbose);
+ if (0 == res)
+ show_IE_page(resp, (resp[2] << 8) + resp[3] + 4, 0, 0);
+ else
+ fprintf(stderr, "Unable to find temperature in either log page "
+ "(temperature or IE)\n");
}
sg_cmds_close_device(sg_fd);
- return res;
+ return (res >= 0) ? res : SG_LIB_CAT_OTHER;
}
@@ -1695,8 +1781,10 @@ int main(int argc, char * argv[])
const char * file_name = 0;
const char * cp;
unsigned char rsp_buff[MX_ALLOC_LEN];
- unsigned int u;
+ unsigned int u, uu;
int pg_code = 0;
+ int subpg_code = 0;
+ int subpg_code_set = 0;
int pc = 1; /* N.B. some disks only give data for current cumulative */
int paramp = 0;
int do_list = 0;
@@ -1710,6 +1798,7 @@ int main(int argc, char * argv[])
int do_temp = 0;
int do_pcreset = 0;
int do_verbose = 0;
+ int ret = 0;
struct sg_simple_inquiry_resp inq_out;
memset(rsp_buff, 0, sizeof(rsp_buff));
@@ -1724,6 +1813,9 @@ int main(int argc, char * argv[])
case 'a':
do_all = 1;
break;
+ case 'A':
+ do_all = 2;
+ break;
case 'h':
case 'H':
do_hex = 1;
@@ -1731,12 +1823,18 @@ int main(int argc, char * argv[])
case 'l':
do_list = 1;
break;
+ case 'L':
+ do_list = 2;
+ break;
case 'r':
do_pcreset = 1;
break;
case 't':
do_temp = 1;
break;
+ case 'T':
+ pg_code = 0x18;
+ break;
case 'v':
++do_verbose;
break;
@@ -1745,7 +1843,7 @@ int main(int argc, char * argv[])
exit(0);
case '?':
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
case '-':
++cp;
jmp_out = 1;
@@ -1764,23 +1862,41 @@ int main(int argc, char * argv[])
if ((1 != num) || (u > 3)) {
printf("Bad page control after 'c=' option [0..3]\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
pc = u;
} else if (0 == strncmp("p=", cp, 2)) {
- num = sscanf(cp + 2, "%x", &u);
- if ((1 != num) || (u > 63)) {
- printf("Bad page code after 'p=' option [0..63]\n");
+ if (NULL == strchr(cp + 2, ',')) {
+ num = sscanf(cp + 2, "%x", &u);
+ if ((1 != num) || (u > 63)) {
+ fprintf(stderr, "Bad page code value after 'p=' "
+ "option\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ pg_code = u;
+ } else if (2 == sscanf(cp + 2, "%x,%x", &u, &uu)) {
+ if (uu > 255) {
+ fprintf(stderr, "Bad sub page code value after 'p=' "
+ "option\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ pg_code = u;
+ subpg_code = uu;
+ subpg_code_set = 1;
+ } else {
+ fprintf(stderr, "Bad page code, subpage code sequence "
+ "after 'p=' option\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
- pg_code = u;
} else if (0 == strncmp("paramp=", cp, 7)) {
num = sscanf(cp + 7, "%x", &u);
if ((1 != num) || (u > 0xffff)) {
printf("Bad parameter pointer after 'paramp=' option\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
paramp = u;
} else if (0 == strncmp("pcb", cp, 3))
@@ -1796,7 +1912,7 @@ int main(int argc, char * argv[])
else if (jmp_out) {
fprintf(stderr, "Unrecognized option: %s\n", cp);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == file_name)
file_name = cp;
@@ -1804,7 +1920,7 @@ int main(int argc, char * argv[])
fprintf(stderr, "too many arguments, got: %s, not expecting: "
"%s\n", file_name, cp);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
@@ -1812,12 +1928,12 @@ int main(int argc, char * argv[])
if (select_cmds > 1) {
fprintf(stderr, "choose one of '-r', '-scum' or '-sthr'. Try '?' "
"for usage.\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (0 == file_name) {
fprintf(stderr, "No <scsi_device> argument given. Try '-?' for "
"usage.\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if ((sg_fd = sg_cmds_open_device(file_name, 0 /* rw */,
@@ -1826,44 +1942,51 @@ int main(int argc, char * argv[])
do_verbose)) < 0) {
fprintf(stderr, ME "error opening file: %s: %s \n", file_name,
safe_strerror(-sg_fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
}
- if (do_list || do_all)
+ if (do_list || do_all) {
pg_code = PG_CODE_ALL;
+ if ((do_list > 1) || (do_all > 1))
+ subpg_code = SUBPG_CODE_ALL;
+ }
pg_len = 0;
if (sg_simple_inquiry(sg_fd, &inq_out, 1, do_verbose)) {
fprintf(stderr, ME "%s doesn't respond to a SCSI INQUIRY\n",
file_name);
sg_cmds_close_device(sg_fd);
- return 1;
+ return SG_LIB_CAT_OTHER;
} else
printf(" %.8s %.16s %.4s\n", inq_out.vendor, inq_out.product,
inq_out.revision);
if (1 == do_temp)
return fetchTemperature(sg_fd, rsp_buff, MX_ALLOC_LEN, do_verbose);
+
if (select_cmds) {
- if (do_pcreset) {
- k = sg_ll_log_select(sg_fd, 1, do_sp, pc, NULL, 0, 1, do_verbose);
- if (SG_LIB_CAT_INVALID_OP == k)
- fprintf(stderr, "log_select: not supported\n");
- return k ? 1 : 0;
- } else if (do_scum) {
- k = sg_ll_log_select(sg_fd, 0, 0, 3 /* pc */, NULL, 0, 1, do_verbose);
- if (SG_LIB_CAT_INVALID_OP == k)
+ k = 0;
+ if (do_pcreset)
+ k = sg_ll_log_select(sg_fd, 1, do_sp, pc, pg_code, subpg_code,
+ NULL, 0, 1, do_verbose);
+ else if (do_scum)
+ k = sg_ll_log_select(sg_fd, 0, 0, 3 /* pc */, pg_code, subpg_code,
+ NULL, 0, 1, do_verbose);
+ else if (do_sthr)
+ k = sg_ll_log_select(sg_fd, 0, 0, 2 /* pc */, pg_code, subpg_code,
+ NULL, 0, 1, do_verbose);
+ if (k) {
+ if (SG_LIB_CAT_NOT_READY == k)
+ fprintf(stderr, "log_select: device not ready\n");
+ else if (SG_LIB_CAT_INVALID_OP == k)
fprintf(stderr, "log_select: not supported\n");
- return k ? 1 : 0;
- } else if (do_sthr) {
- k = sg_ll_log_select(sg_fd, 0, 0, 2 /* pc */, NULL, 0, 1, do_verbose);
- if (SG_LIB_CAT_INVALID_OP == k)
- fprintf(stderr, "log_select: not supported\n");
- return k ? 1 : 0;
+ else if (SG_LIB_CAT_UNIT_ATTENTION == k)
+ fprintf(stderr, "log_select: unit attention\n");
}
+ return (k >= 0) ? k : SG_LIB_CAT_OTHER;
}
- res = do_logs(sg_fd, do_ppc, do_sp, pc, pg_code, paramp, rsp_buff,
- MX_ALLOC_LEN, 1, do_verbose);
+ res = do_logs(sg_fd, do_ppc, do_sp, pc, pg_code, subpg_code, paramp,
+ rsp_buff, MX_ALLOC_LEN, 1, do_verbose);
if (0 == res) {
pg_len = (rsp_buff[2] << 8) + rsp_buff[3];
if ((pg_len + 4) > MX_ALLOC_LEN) {
@@ -1871,6 +1994,15 @@ int main(int argc, char * argv[])
MX_ALLOC_LEN);
pg_len = MX_ALLOC_LEN - 4;
}
+ } else if (SG_LIB_CAT_INVALID_OP == res)
+ fprintf(stderr, "log_sense: not supported\n");
+ else if (SG_LIB_CAT_NOT_READY == res)
+ fprintf(stderr, "log_sense: device not ready\n");
+ else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+ fprintf(stderr, "log_sense: field in cdb illegal\n");
+ else if (SG_LIB_CAT_UNIT_ATTENTION == res)
+ fprintf(stderr, "log_sense: unit attention\n");
+ if ((pg_len > 1) && (0 == do_all)) {
if (do_hex) {
if (rsp_buff[0] & 0x40)
printf("Log page code=0x%x,0x%x, DS=%d, SPF=1, "
@@ -1884,22 +2016,32 @@ int main(int argc, char * argv[])
else
show_ascii_page(rsp_buff, pg_len + 4, do_pcb, &inq_out,
do_verbose);
- } else if (SG_LIB_CAT_INVALID_OP == res)
- fprintf(stderr, "log_sense: not supported\n");
- else if (SG_LIB_CAT_ILLEGAL_REQ == res)
- fprintf(stderr, "log_sense: field in cdb illegal\n");
+ }
+ ret = res;
if (do_all && (pg_len > 1)) {
- int my_len = pg_len - 1;
- unsigned char parr[256];
-
- memcpy(parr, rsp_buff + 5, my_len);
+ int my_len = pg_len;
+ int spf;
+ unsigned char parr[1024];
+
+ spf = !!(rsp_buff[0] & 0x40);
+ if (my_len > (int)sizeof(parr)) {
+ fprintf(stderr, "Unexpectedly large page_len=%d, trim to %d\n",
+ my_len, (int)sizeof(parr));
+ my_len = sizeof(parr);
+ }
+ memcpy(parr, rsp_buff + 4, my_len);
for (k = 0; k < my_len; ++k) {
printf("\n");
- pg_code = parr[k];
- if (0 == do_logs(sg_fd, do_ppc, do_sp, pc, pg_code, paramp,
- rsp_buff, MX_ALLOC_LEN, 1, do_verbose))
- {
+ pg_code = parr[k] & 0x3f;
+ if (spf)
+ subpg_code = parr[++k];
+ else
+ subpg_code = 0;
+
+ res = do_logs(sg_fd, do_ppc, do_sp, pc, pg_code, subpg_code,
+ paramp, rsp_buff, MX_ALLOC_LEN, 1, do_verbose);
+ if (0 == res) {
pg_len = (rsp_buff[2] << 8) + rsp_buff[3];
if ((pg_len + 4) > MX_ALLOC_LEN) {
printf("Only fetched %d bytes of response, truncate "
@@ -1920,9 +2062,18 @@ int main(int argc, char * argv[])
else
show_ascii_page(rsp_buff, pg_len + 4, do_pcb, &inq_out,
do_verbose);
- }
+ } else if (SG_LIB_CAT_INVALID_OP == res)
+ fprintf(stderr, "log_sense: page=0x%x,0x%x not supported\n",
+ pg_code, subpg_code);
+ else if (SG_LIB_CAT_NOT_READY == res)
+ fprintf(stderr, "log_sense: device not ready\n");
+ else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+ fprintf(stderr, "log_sense: field in cdb illegal "
+ "[page=0x%x,0x%x]\n", pg_code, subpg_code);
+ else if (SG_LIB_CAT_UNIT_ATTENTION == res)
+ fprintf(stderr, "log_sense: unit attention\n");
}
}
sg_cmds_close_device(sg_fd);
- return 0;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_luns.8 b/sg_luns.8
index 8c0faf86..c014dbda 100644
--- a/sg_luns.8
+++ b/sg_luns.8
@@ -1,4 +1,4 @@
-.TH SG_LUNS "8" "October 2004" "sg3_utils-1.10" SG3_UTILS
+.TH SG_LUNS "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_luns \- send the scsi command report luns
.SH SYNOPSIS
@@ -36,12 +36,15 @@ increase the level of verbosity, (i.e. debug output).
.TP
--version | -V
print the version string and then exit.
+.SH EXIT STATUS
+The exit status of sg_luns is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHORS
Written by Douglas Gilbert.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2004 Douglas Gilbert
+Copyright \(co 2004-2006 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.
diff --git a/sg_luns.c b/sg_luns.c
index bf2c84c8..c34d3771 100644
--- a/sg_luns.c
+++ b/sg_luns.c
@@ -43,7 +43,7 @@
* This program issues the SCSI command REPORT LUNS to the given SCSI device.
*/
-static char * version_str = "1.05 20060127";
+static char * version_str = "1.06 20060623";
#define REPORT_LUNS_BUFF_LEN 1024
@@ -203,7 +203,7 @@ int main(int argc, char * argv[])
int select_rep = 0;
int verbose = 0;
char device_name[256];
- int ret = 1;
+ int ret = 0;
memset(device_name, 0, sizeof device_name);
while (1) {
@@ -226,7 +226,7 @@ int main(int argc, char * argv[])
if ((1 != sscanf(optarg, "%d", &select_rep)) ||
(select_rep < 0) || (select_rep > 255)) {
fprintf(stderr, "bad argument to '--select'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 'v':
@@ -238,7 +238,7 @@ int main(int argc, char * argv[])
default:
fprintf(stderr, "unrecognised switch code 0x%x ??\n", c);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (optind < argc) {
@@ -252,20 +252,20 @@ int main(int argc, char * argv[])
fprintf(stderr, "Unexpected extra argument: %s\n",
argv[optind]);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (0 == device_name[0]) {
fprintf(stderr, "missing device name!\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
if (sg_fd < 0) {
fprintf(stderr, ME "open error: %s: %s\n", device_name,
safe_strerror(-sg_fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
memset(reportLunsBuff, 0x0, sizeof(reportLunsBuff));
@@ -273,6 +273,7 @@ int main(int argc, char * argv[])
res = sg_ll_report_luns(sg_fd, select_rep, reportLunsBuff,
sizeof(reportLunsBuff), 1, verbose);
+ ret = res;
if (0 == res) {
list_len = (reportLunsBuff[0] << 24) + (reportLunsBuff[1] << 16) +
(reportLunsBuff[2] << 8) + reportLunsBuff[3];
@@ -300,7 +301,6 @@ int main(int argc, char * argv[])
if (decode)
decode_lun(" ", reportLunsBuff + off - 8);
}
- ret = 0;
} else if (SG_LIB_CAT_INVALID_OP == res)
fprintf(stderr, "Report Luns command not supported (support "
"mandatory in SPC-3)\n");
@@ -314,8 +314,9 @@ int main(int argc, char * argv[])
res = sg_cmds_close_device(sg_fd);
if (res < 0) {
- fprintf(stderr, ME "close error: %s\n", safe_strerror(-res));
- return 1;
+ fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ return SG_LIB_FILE_ERROR;
}
- return ret;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_map.8 b/sg_map.8
index 545569a0..f14e7cb2 100644
--- a/sg_map.8
+++ b/sg_map.8
@@ -1,4 +1,4 @@
-.TH SG_MAP "8" "March 2006" "sg3_utils-1.20" SG3_UTILS
+.TH SG_MAP "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_map \- displays mapping between sg and other SCSI devices
.SH SYNOPSIS
@@ -150,6 +150,9 @@ When a SCSI scanner is added the output becomes:
/dev/sg3
.PP
By process of elimination /dev/sg3 must be the scanner.
+.SH EXIT STATUS
+The exit status of sg_map is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHOR
Written by Doug Gilbert
.SH "REPORTING BUGS"
diff --git a/sg_map.c b/sg_map.c
index a27a122a..e51d6c85 100644
--- a/sg_map.c
+++ b/sg_map.c
@@ -36,7 +36,7 @@
*/
-static char * version_str = "1.05 20060405";
+static char * version_str = "1.06 20060623";
static const char * devfs_id = "/dev/.devfsd";
@@ -223,15 +223,15 @@ int main(int argc, char * argv[])
printf(
"Show mapping from sg devices to other scsi device names\n\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
} else if (*argv[k] == '-') {
printf("Unknown switch: %s\n", argv[k]);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
} else if (*argv[k] != '-') {
printf("Unknown argument\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
@@ -246,7 +246,7 @@ int main(int argc, char * argv[])
if (res < 0) {
snprintf(ebuff, EBUFF_SZ, "Error closing %s ", fname);
perror("sg_map: close error");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (has_sysfs_sg) {
if (0 == gen_index_arr[k]) {
@@ -305,7 +305,7 @@ int main(int argc, char * argv[])
printf("Stopping because there are too many error\n");
if (eacces_err)
printf(" root access may be required\n");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (last_sg_ind < 0) {
printf("Stopping because no sg devices found\n");
diff --git a/sg_map26.8 b/sg_map26.8
index 7f2fe3cc..8ef5c405 100644
--- a/sg_map26.8
+++ b/sg_map26.8
@@ -1,4 +1,4 @@
-.TH SG_MAP26 "8" "February 2006" "sg3_utils-1.20" SG3_UTILS
+.TH SG_MAP26 "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_map26 \- maps a special file to a SCSI generic (sg) device (or vv)
.SH SYNOPSIS
@@ -91,9 +91,6 @@ to search the device directory.
This utility only shows one relationship at a time. To get an
overview of all SCSI devices, with special file names and optionally
the "mapped" sg device name, see the lsscsi utility.
-.PP
-When this utility succeeds its process exits with a status of 0;
-if problems are encountered the process status is 1 .
.SH EXAMPLES
Assume sg2 maps to sdb while dvd, cdrom and hdc are all matching.
.PP
@@ -146,6 +143,9 @@ Now look at '/dev/hdc' and friends
/dev/dvd
.br
/dev/hdc
+.SH EXIT STATUS
+The exit status of sg_map26 is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHORS
Written by Douglas Gilbert.
.SH "REPORTING BUGS"
diff --git a/sg_map26.c b/sg_map26.c
index 6aa96615..3d84defc 100644
--- a/sg_map26.c
+++ b/sg_map26.c
@@ -54,7 +54,9 @@
#include <sys/stat.h>
#include <linux/major.h>
-static char * version_str = "1.03 20060323";
+#include "sg_lib.h"
+
+static char * version_str = "1.03 20060623";
#define ME "sg_map26: "
@@ -978,7 +980,7 @@ static int map_sg(const char * device_name, const char * device_dir, int ma,
int main(int argc, char * argv[])
{
- int c, num, res, tt, cont;
+ int c, num, tt, cont, res;
int do_dev_dir = 0;
int given_is = -1;
int result = 0;
@@ -1012,7 +1014,7 @@ int main(int argc, char * argv[])
else {
fprintf(stderr, "value for '--given_to=' "
"must be 0 or 1\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 'h':
@@ -1026,7 +1028,7 @@ int main(int argc, char * argv[])
else {
fprintf(stderr, "value for '--result=' "
"must be 0..3\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 's':
@@ -1042,7 +1044,7 @@ int main(int argc, char * argv[])
fprintf(stderr, "unrecognised switch code 0x%x ??\n",
c);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (optind < argc) {
@@ -1057,14 +1059,14 @@ int main(int argc, char * argv[])
fprintf(stderr, "Unexpected extra argument: "
"%s\n", argv[optind]);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (0 == device_name[0]) {
fprintf(stderr, "missing device name!\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
ma = 0;
@@ -1080,7 +1082,7 @@ int main(int argc, char * argv[])
"dev_dir: %s\n", device_dir);
} else {
fprintf(stderr, "dev_dir: %s invalid\n", device_dir);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
} else {
strcpy(device_dir, device_name);
@@ -1094,7 +1096,7 @@ int main(int argc, char * argv[])
if (ret < 0) {
fprintf(stderr, "stat failed on %s: %s\n", device_name,
ssafe_strerror(-ret));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (verbose)
fprintf(stderr, " %s: %s device [maj=%d, min=%d]\n",
@@ -1107,7 +1109,7 @@ int main(int argc, char * argv[])
if (given_is > 0) {
fprintf(stderr, "block special but '--given_is=' "
"suggested sysfs device\n");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
break;
case NT_ST:
@@ -1117,14 +1119,14 @@ int main(int argc, char * argv[])
if (given_is > 0) {
fprintf(stderr, "character special but '--given_is=' "
"suggested sysfs device\n");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
break;
case NT_REG:
if (0 == given_is) {
fprintf(stderr, "regular file but '--given_is=' "
"suggested block or char special\n");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
strcpy(device_dir, def_dev_dir);
break;
@@ -1132,7 +1134,7 @@ int main(int argc, char * argv[])
if (0 == given_is) {
fprintf(stderr, "directory but '--given_is=' "
"suggested block or char special\n");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
strcpy(device_dir, def_dev_dir);
break;
@@ -1159,7 +1161,7 @@ int main(int argc, char * argv[])
if (result < 2) {
fprintf(stderr, "a hd device does not map "
"to a sg device\n");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
res = map_hd(device_dir, ma, mi, result,
follow_symlink, verbose);
@@ -1185,13 +1187,13 @@ int main(int argc, char * argv[])
sizeof(value))) {
fprintf(stderr, "Couldn't fetch value "
"from: %s\n", device_name);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (verbose)
fprintf(stderr, "value: %s\n", value);
if (2 != sscanf(value, "%d:%d", &ma, &mi)) {
fprintf(stderr, "Couldn't decode value\n");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
tt = nt_typ_from_major(ma);
cont = 1;
@@ -1201,13 +1203,13 @@ int main(int argc, char * argv[])
sizeof(value))) {
fprintf(stderr, "Couldn't fetch value from: "
"%s/dev\n", device_name);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (verbose)
fprintf(stderr, "value: %s\n", value);
if (2 != sscanf(value, "%d:%d", &ma, &mi)) {
fprintf(stderr, "Couldn't decode value\n");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
tt = nt_typ_from_major(ma);
cont = 1;
diff --git a/sg_modes.8 b/sg_modes.8
index 0cd817da..dcd75a85 100644
--- a/sg_modes.8
+++ b/sg_modes.8
@@ -1,4 +1,4 @@
-.TH SG_MODES "8" "March 2006" "sg3_utils-1.20" SG3_UTILS
+.TH SG_MODES "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_modes \- reads SCSI MODE SENSE pages
.SH SYNOPSIS
@@ -133,13 +133,13 @@ type of the device (e.g. 0 -> Direct Access Device (disk)) prior to
sending a MODE SENSE command. This helps in decoding the block
descriptor and mode pages.
.PP
-When this utility succeeds its process exits with a status of 0;
-if problems are encountered the process status is 1 .
-.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 "sg_modes -a /dev/sda"
will work in the 2.6 series kernels.
+.SH EXIT STATUS
+The exit status of sg_modes is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHOR
Written by Doug Gilbert
.SH "REPORTING BUGS"
diff --git a/sg_modes.c b/sg_modes.c
index 86dffa90..5cfa92bc 100644
--- a/sg_modes.c
+++ b/sg_modes.c
@@ -21,7 +21,7 @@
*/
-static char * version_str = "1.15 20060331";
+static char * version_str = "1.16 20060623";
#define ME "sg_modes: "
@@ -408,7 +408,7 @@ static int examine_pages(int sg_fd, int do_mode6, int inq_pdt, int inq_byte6,
if (SG_LIB_CAT_INVALID_OP == res) {
fprintf(stderr, ">>>>>> try again without the '-6' "
"switch for a 10 byte MODE SENSE command\n");
- return 1;
+ return res;
}
} else {
res = sg_ll_mode_sense10(sg_fd, 0, 0, 0, k,
@@ -416,7 +416,7 @@ static int examine_pages(int sg_fd, int do_mode6, int inq_pdt, int inq_byte6,
if (SG_LIB_CAT_INVALID_OP == res) {
fprintf(stderr, ">>>>>> try again with a '-6' "
"switch for a 6 byte MODE SENSE command\n");
- return 1;
+ return res;
}
}
if (0 == res) {
@@ -429,9 +429,10 @@ static int examine_pages(int sg_fd, int do_mode6, int inq_pdt, int inq_byte6,
printf(" %s\n", cp);
else
printf(" [0x%x]\n", k);
- }
+ } else if (SG_LIB_CAT_NOT_READY == res)
+ fprintf(stderr, "MODE SENSE failed, device not ready\n");
}
- return 0;
+ return res;
}
static const char * pg_control_str_arr[] = {
@@ -501,8 +502,9 @@ int main(int argc, char * argv[])
int do_list = 0;
int do_raw = 0;
int do_verbose = 0;
+ int ret = 0;
int density_code_off, t_proto, inq_pdt, inq_byte6, resp_mode6;
- int num_ua_pages, plen, jmp_out, ret;
+ int num_ua_pages, plen, jmp_out;
unsigned char * ucp;
unsigned char uc;
struct sg_simple_inquiry_resp inq_out;
@@ -559,7 +561,7 @@ int main(int argc, char * argv[])
exit(0);
case '?':
usage();
- return 1;
+ return 0;
default:
jmp_out = 1;
break;
@@ -574,7 +576,7 @@ int main(int argc, char * argv[])
if ((1 != num) || (u > 3)) {
fprintf(stderr, "Bad page control after 'c=' option\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
pc = u;
} else if (0 == strncmp("p=", cp, 2)) {
@@ -584,7 +586,7 @@ int main(int argc, char * argv[])
fprintf(stderr, "Bad page code value after 'p=' "
"option\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
pg_code = u;
} else if (2 == sscanf(cp + 2, "%x,%x", &u, &uu)) {
@@ -592,7 +594,7 @@ int main(int argc, char * argv[])
fprintf(stderr, "Bad sub page code value after 'p=' "
"option\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
pg_code = u;
sub_pg_code = uu;
@@ -601,7 +603,7 @@ int main(int argc, char * argv[])
fprintf(stderr, "Bad page code, subpage code sequence "
"after 'p=' option\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strncmp("subp=", cp, 5)) {
num = sscanf(cp + 5, "%x", &u);
@@ -609,7 +611,7 @@ int main(int argc, char * argv[])
fprintf(stderr, "Bad sub page code after 'subp=' "
"option\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
sub_pg_code = u;
sub_pg_code_set = 1;
@@ -618,7 +620,7 @@ int main(int argc, char * argv[])
} else if (jmp_out) {
fprintf(stderr, "Unrecognized option: %s\n", cp);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == file_name)
file_name = cp;
@@ -626,7 +628,7 @@ int main(int argc, char * argv[])
fprintf(stderr, "too many arguments, got: %s, not expecting: "
"%s\n", file_name, cp);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
@@ -647,12 +649,12 @@ int main(int argc, char * argv[])
}
fprintf(stderr, "No <scsi_device> argument given\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (do_examine && (pg_code >= 0)) {
fprintf(stderr, "can't give '-e' and a page number\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
/* The 6 bytes command only allows up to 252 bytes of response data */
@@ -660,7 +662,7 @@ int main(int argc, char * argv[])
if (do_llbaa) {
fprintf(stderr, "LLBAA not defined for MODE SENSE 6, try "
"without '-L'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
rsp_buff_size = 252;
}
@@ -671,14 +673,14 @@ int main(int argc, char * argv[])
if ((sg_fd = sg_cmds_open_device(file_name, 1 /* ro */, do_verbose)) < 0) {
fprintf(stderr, ME "error opening file: %s: %s\n", file_name,
safe_strerror(-sg_fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (sg_simple_inquiry(sg_fd, &inq_out, 1, do_verbose)) {
fprintf(stderr, ME "%s doesn't respond to a SCSI INQUIRY\n",
file_name);
sg_cmds_close_device(sg_fd);
- return 1;
+ return SG_LIB_CAT_OTHER;
}
inq_pdt = inq_out.peripheral_type;
inq_byte6 = inq_out.byte_6;
@@ -708,12 +710,12 @@ int main(int argc, char * argv[])
if (do_all) {
fprintf(stderr, "'-r' requires a given (sub)page (not all)\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (do_hex) {
fprintf(stderr, "'-r' and '-h' clash");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
@@ -738,7 +740,14 @@ int main(int argc, char * argv[])
fprintf(stderr, "invalid field in cdb (perhaps subpages "
"or page control (PC) not supported)\n");
}
- ret = 1;
+ if (SG_LIB_CAT_ILLEGAL_REQ == res)
+ fprintf(stderr, "invalid field in cdb (perhaps subpages "
+ "or page control (PC) not supported)\n");
+ else if (SG_LIB_CAT_NOT_READY == res)
+ fprintf(stderr, "device not ready\n");
+ else if (SG_LIB_CAT_UNIT_ATTENTION == res)
+ fprintf(stderr, "unit attention\n");
+ ret = res;
if (0 == res) {
int medium_type, specific, headerlen;
@@ -905,5 +914,5 @@ int main(int argc, char * argv[])
}
finish:
sg_cmds_close_device(sg_fd);
- return ret;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_opcodes.8 b/sg_opcodes.8
index ac0d48e0..d55f70f4 100644
--- a/sg_opcodes.8
+++ b/sg_opcodes.8
@@ -1,4 +1,4 @@
-.TH SG_OPCODES "8" "April 2005" "sg3_utils-1.14" SG3_UTILS
+.TH SG_OPCODES "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_opcodes \- reports information on supported SCSI commands or
supported task management functions
@@ -82,12 +82,15 @@ 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 "sg_opcodes /dev/sda"
will work in the 2.6 series kernels.
+.SH EXIT STATUS
+The exit status of sg_opcodes is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHOR
Written by Doug Gilbert
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2004-2005 Douglas Gilbert
+Copyright \(co 2004-2006 Douglas Gilbert
.br
This software is distributed under the GPL version 2. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/sg_opcodes.c b/sg_opcodes.c
index a3204d7b..b5a26c25 100644
--- a/sg_opcodes.c
+++ b/sg_opcodes.c
@@ -24,7 +24,7 @@
*/
-static char * version_str = "0.23 20060315";
+static char * version_str = "0.24 20060623";
#define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */
@@ -92,7 +92,7 @@ static unsigned char dummy_rsmft_r0 = 0xff;
/* Report Supported Operation Codes */
-/* Returns 0 when successful, else -1 */
+/* Returns 0 when successful */
static int do_rsoc(int sg_fd, int rep_opts, int rq_opcode, int rq_servact,
void * resp, int mx_resp_len, int noisy, int verbose)
{
@@ -161,12 +161,12 @@ static int do_rsoc(int sg_fd, int rep_opts, int rq_opcode, int rq_servact,
"rq_sa=0x%x ", rq_opcode, rq_servact);
sg_chk_n_print3(ebuff, &io_hdr, verbose > 1);
}
- return -1;
+ return res;
}
}
/* Report Supported Task Management Function */
-/* Returns 0 when successful, else -1 */
+/* Returns 0 when successful */
static int do_rstmf(int sg_fd, void * resp, int mx_resp_len, int noisy,
int verbose)
{
@@ -218,7 +218,7 @@ static int do_rstmf(int sg_fd, void * resp, int mx_resp_len, int noisy,
snprintf(ebuff, EBUFF_SZ, "RSTMF error ");
sg_chk_n_print3(ebuff, &io_hdr, verbose > 1);
}
- return -1;
+ return res;
}
}
@@ -359,7 +359,7 @@ void list_all_codes(unsigned char * rsoc_buff, int rsoc_len, int unsorted,
int main(int argc, char * argv[])
{
- int sg_fd, k, num, cd_len, plen, jmp_out;
+ int sg_fd, k, num, cd_len, plen, jmp_out, res;
const char * file_name = 0;
char ebuff[EBUFF_SZ];
unsigned char rsoc_buff[MX_ALLOC_LEN];
@@ -372,7 +372,6 @@ int main(int argc, char * argv[])
int do_unsorted = 0;
int do_taskman = 0;
int rep_opts = 0;
- int ret = 0;
const char * cp;
char buff[48];
struct sg_simple_inquiry_resp inq_resp;
@@ -403,7 +402,7 @@ int main(int argc, char * argv[])
case 'h':
case '?':
usage();
- return 1;
+ return 0;
default:
jmp_out = 1;
break;
@@ -418,19 +417,19 @@ int main(int argc, char * argv[])
if ((1 != num) || (do_opcode > 255)) {
fprintf(stderr, "Bad number after 'o=' option\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strncmp("s=", cp, 2)) {
num = sscanf(cp + 2, "%x", (unsigned int *)&do_servact);
if (1 != num) {
fprintf(stderr, "Bad number after 's=' option\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (jmp_out) {
fprintf(stderr, "Unrecognized option: %s\n", cp);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == file_name)
file_name = cp;
@@ -438,19 +437,19 @@ int main(int argc, char * argv[])
fprintf(stderr, "too many arguments, got: %s, not expecting: "
"%s\n", file_name, cp);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (0 == file_name) {
fprintf(stderr, "No <scsi_device> argument given\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if ((-1 != do_servact) && (-1 == do_opcode)) {
fprintf(stderr, "When '-s' is chosen, so must '-o' be chosen\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (do_unsorted && do_alpha)
fprintf(stderr, "warning: unsorted ('-u') and alpha ('-a') options "
@@ -465,7 +464,7 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ, "sg_opcodes: error opening file: %s (ro)",
file_name);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (0 == sg_simple_inquiry(sg_fd, &inq_resp, 1, do_verbose)) {
printf(" %.8s %.16s %.4s\n", inq_resp.vendor, inq_resp.product,
@@ -478,7 +477,7 @@ int main(int argc, char * argv[])
printf(" Peripheral device type: 0x%x\n", peri_type);
} else {
printf("sg_opcodes: %s doesn't respond to a SCSI INQUIRY\n", file_name);
- return 1;
+ return SG_LIB_CAT_OTHER;
}
close(sg_fd);
#ifndef TEST_CODE
@@ -489,7 +488,7 @@ int main(int argc, char * argv[])
else
printf("'Report supported operation codes' command not "
"supported for CD/DVD devices\n");
- return 1;
+ return SG_LIB_CAT_OTHER;
}
#endif
@@ -497,23 +496,25 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ, "sg_opcodes: error opening file: %s (rw)",
file_name);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (do_opcode >= 0)
rep_opts = ((do_servact >= 0) ? 2 : 1);
memset(rsoc_buff, 0, sizeof(rsoc_buff));
#ifndef TEST_CODE
if (do_taskman) {
- if (0 != do_rstmf(sg_fd, rsoc_buff, sizeof(rsoc_buff), 0,
- do_verbose)) {
+ res = do_rstmf(sg_fd, rsoc_buff, sizeof(rsoc_buff), 0,
+ do_verbose);
+ if (res) {
fprintf(stderr, "Report supported task management functions failed\n");
- return 1;
+ return res;
}
} else {
- if (0 != do_rsoc(sg_fd, rep_opts, do_opcode, do_servact, rsoc_buff,
- sizeof(rsoc_buff), 0, do_verbose)) {
+ res = do_rsoc(sg_fd, rep_opts, do_opcode, do_servact, rsoc_buff,
+ sizeof(rsoc_buff), 0, do_verbose);
+ if (res) {
fprintf(stderr, "Report supported operation codes failed\n");
- return 1;
+ return res;
}
}
#else
@@ -581,5 +582,5 @@ int main(int argc, char * argv[])
}
}
close(sg_fd);
- return ret;
+ return 0;
}
diff --git a/sg_persist.8 b/sg_persist.8
index d6202f86..912d6ef1 100644
--- a/sg_persist.8
+++ b/sg_persist.8
@@ -1,4 +1,4 @@
-.TH SG_PERSIST "8" "March 2006" "sg3_utils-1.20" SG3_UTILS
+.TH SG_PERSIST "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_persist \- use the Persistent Reserve In and Out SCSI commands
to manipulate registrations and reservations
@@ -10,11 +10,18 @@ to manipulate registrations and reservations
.PP
This utility allows Persistent reservations and registrations to be
queried and changed. Persistent reservations and registrations are
-queried by sub-commands (called "service actions" in SPC-3) of the Persistent
+queried by sub-commands (called "service actions" in SPC-4) of the Persistent
Reserve In (PRIN) SCSI command. Persistent reservations and
registrations are changed by sub-commands of the Persistent Reserve
Out (PROUT) SCSI command.
.PP
+There is a two stage process to obtain a persistent reservation. First an
+application (an I_T nexus in standard's jargon) must register a reservation
+key. If that is accepted (and it should be unless some other I_T nexus has
+registered that key) then the application can try and reserve
+the device. The reserve operation must specify the reservation
+key and a "type" (see the '--prout-type=' option).
+.PP
It is relatively safe to query the state of Persistent reservations and
registrations. With no options this utility defaults to the READ KEYS
sub-command of the PRIN command. Other PRIN sub-commands are
@@ -22,15 +29,17 @@ READ RESERVATION, REPORT CAPABILITIES and READ FULL STATUS.
.PP
Before trying to change Persistent reservations and registrations users
should be aware of what they are doing! The relevant sections of the
-SCSI Primary Commands document (SPC-3 most recent draft revision 21d
-dated 14th February 2005) are sections 5.6 (general information), 6.11 (for
+SCSI Primary Commands document (SPC-4 most recent draft revision 5a
+dated 14th June 2006) are sections 5.6 (general information), 6.11 (for
PRIN) and 6.12 (for PROUT). To safeguard against accidental use,
the '--out' option must be given when a PROUT sub-command (e.g. '--register')
is used.
.PP
The older SCSI Reserve and Release commands (both 6 and 10 byte variants)
are not supported by this utility. In SPC-3, Reserve and Release are
-deprecated, replaced by Persistent Reservations. See a utility
+deprecated, replaced by Persistent Reservations. Reserve and Release
+have been removed from SPC-4 and Annex B is provided showing how to
+convert to persistent reservation commands. See a utility
called 'scsires' for support of the SCSI Reserve and Release commands.
.PP
The <scsi_device> is required by all variants of this utility apart
@@ -38,7 +47,7 @@ from '--help' ('-h'). The <scsi_device> can be given either as an
argument (typically but not necessarily the last one) or via
the '--device=<scsi_device>' (or '-d <scsi_device') option.
.PP
-SPC-3 does not use the term "sub-command". It uses the term "service action"
+SPC-4 does not use the term "sub-command". It uses the term "service action"
for this and for part of a field's name in the parameter block associated
with the PROUT command (i.e. "service action reservation key"). To lessen
the potential confusion the term "sub-command" has been introduced.
@@ -61,7 +70,7 @@ output a usage message.
--hex | -H
the response to a valid PRIN sub-command will be output in hexadecimal.
Normally if the PRIN sub-command is recognised then the response
-will be decoded as per SPC-3.
+will be decoded as per SPC-4.
.TP
--in | -i
specify that a Persistent Reserve In SCSI command is required. This
@@ -117,7 +126,9 @@ sub-commands. Valid arguments: 1-> write exclusive, 3->
exclusive access, 5-> write exclusive - registrants only, 6->
exclusive access - registrants only, 7-> write exclusive - all registrants,
8-> exclusive access - all registrants. Default value is 0 (which is
-an invalid type).
+an invalid type). Each "persistent reservation type" is explained in more
+detail in a subsection of that name in the read reservation section of
+the PRIN command (section 6.11.3.4 of SPC-4 revision 5a).
.TP
--read-full-status | -s
Read Full Status is a sub-command of the PRIN command. For each registration
@@ -234,7 +245,7 @@ For example "sg_persist --read-keys /dev/sda"
will work in the 2.6 series kernels.
.PP
The only scope for PROUT commands supported in the current draft of
-SPC-3 is "LU_SCOPE". Hence there seems to be no point in offering an
+SPC-4 is "LU_SCOPE". Hence there seems to be no point in offering an
option to set scope to another value.
.PP
Most errors with the PROUT sub-commands (e.g. missing or
@@ -297,6 +308,9 @@ unregister examples above.
An example file that is suitably formatted to pass transportIDs via
the '-transport-id=-' option can be found in the examples sub-directory
of the sg3_utils package. That file is called 'transport_ids.txt'.
+.SH EXIT STATUS
+The exit status of sg_persist is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHOR
Written by Doug Gilbert
.SH "REPORTING BUGS"
diff --git a/sg_persist.c b/sg_persist.c
index 75130225..78fae81f 100644
--- a/sg_persist.c
+++ b/sg_persist.c
@@ -20,7 +20,7 @@
*/
-static char * version_str = "0.25 20060117";
+static char * version_str = "0.26 20060623";
#define PRIN_RKEY_SA 0x0
@@ -104,7 +104,7 @@ static void usage()
{
fprintf(stderr,
"Usage: 'sg_persist [<options>] [<scsi_device>]\n"
- " where Persistent Reservation (PR) <options> include:\n"
+ " where Persistent Reserve (PR) <options> include:\n"
" --clear|-C PR Out: Clear\n"
" --device=<scsi_device> device to query or change\n"
" -d <scsi_device> device to query or change "
@@ -279,11 +279,14 @@ static int prin_work(int sg_fd, int prin_sa, int do_verbose, int do_hex)
sizeof(pr_buff), 1, do_verbose);
if (res) {
if (SG_LIB_CAT_INVALID_OP == res)
- fprintf(stderr, "Persistent reserve in command not supported\n");
+ fprintf(stderr, "Persistent reserve in, command not "
+ "supported\n");
else if (SG_LIB_CAT_ILLEGAL_REQ == res)
- fprintf(stderr, "bad field in Persistent reserve in cdb\n");
+ fprintf(stderr, "Persistent reserve in: bad field in cdb\n");
+ else if (SG_LIB_CAT_UNIT_ATTENTION == res)
+ fprintf(stderr, "Persistent reserve in: unit attention\n");
else
- fprintf(stderr, "Persistent reserve in command failed\n");
+ fprintf(stderr, "Persistent reserve in, command failed\n");
return 1;
}
if (PRIN_RCAP_SA == prin_sa) {
@@ -461,11 +464,13 @@ static int prout_work(int sg_fd, int prout_sa, unsigned int prout_type,
pr_buff, len, 1, do_verbose);
if (res) {
if (SG_LIB_CAT_INVALID_OP == res)
- fprintf(stderr, "Persistent reserve out command not supported\n");
+ fprintf(stderr, "Persistent reserve out, command not supported\n");
else if (SG_LIB_CAT_ILLEGAL_REQ == res)
- fprintf(stderr, "bad field in Persistent reserve out cdb\n");
+ fprintf(stderr, "Persistent reserve out, bad field in cdb\n");
+ else if (SG_LIB_CAT_UNIT_ATTENTION == res)
+ fprintf(stderr, "Persistent reserve out, unit attention\n");
else
- fprintf(stderr, "Persistent reserve out command failed\n");
+ fprintf(stderr, "Persistent reserve out, command failed\n");
return 1;
} else if (do_verbose) {
char buff[64];
@@ -521,7 +526,9 @@ static int prout_rmove_work(int sg_fd, unsigned int prout_type,
if (SG_LIB_CAT_INVALID_OP == res)
fprintf(stderr, "Persistent reserve out command not supported\n");
else if (SG_LIB_CAT_ILLEGAL_REQ == res)
- fprintf(stderr, "bad field in Persistent reserve out cdb\n");
+ fprintf(stderr, "Persistent reserve out, bad field in cdb\n");
+ else if (SG_LIB_CAT_UNIT_ATTENTION == res)
+ fprintf(stderr, "Persistent reserve out, unit attention\n");
else
fprintf(stderr, "Persistent reserve out command failed\n");
return 1;
@@ -665,7 +672,7 @@ static int build_transportid(const char * inp, unsigned char * tid_arr,
int main(int argc, char * argv[])
{
- int sg_fd, c;
+ int sg_fd, c, res;
unsigned int prout_type;
unsigned long long param_rk = 0;
unsigned long long param_sark = 0;
@@ -744,7 +751,7 @@ int main(int argc, char * argv[])
case 'K':
if (1 != sscanf(optarg, "%llx", &param_rk)) {
fprintf(stderr, "bad argument to '--param-rk'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
++num_prout_param;
break;
@@ -769,12 +776,12 @@ int main(int argc, char * argv[])
case 'Q':
if (1 != sscanf(optarg, "%x", &param_rtp)) {
fprintf(stderr, "bad argument to '--relative-target-port'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (param_rtp > 0xffff) {
fprintf(stderr, "argument to '--relative-target-port' 0 to "
"ffff inclusive\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
++num_prout_param;
break;
@@ -793,14 +800,14 @@ int main(int argc, char * argv[])
case 'S':
if (1 != sscanf(optarg, "%llx", &param_sark)) {
fprintf(stderr, "bad argument to '--param-sark'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
++num_prout_param;
break;
case 'T':
if (1 != sscanf(optarg, "%x", &prout_type)) {
fprintf(stderr, "bad argument to '--prout-type'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
++num_prout_param;
break;
@@ -820,7 +827,7 @@ int main(int argc, char * argv[])
&num_transportids,
sizeof(transportid_arr))) {
fprintf(stderr, "bad argument to '--transport-id'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
++num_prout_param;
break;
@@ -834,12 +841,12 @@ int main(int argc, char * argv[])
break;
case '?':
usage();
- return 1;
+ return 0;
default:
fprintf(stderr, "unrecognised switch "
"code 0x%x ??\n", c);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (optind < argc) {
@@ -853,33 +860,33 @@ int main(int argc, char * argv[])
fprintf(stderr, "Unexpected extra argument: %s\n",
argv[optind]);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if ('\0' == device_name[0]) {
fprintf(stderr, "No device name given\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if ((want_prout + want_prin) > 1) {
fprintf(stderr, "choose '--in' _or_ '--out' (not both)\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
} else if (want_prout) { /* syntax check on PROUT arguments */
prin = 0;
if ((1 != num_prout_sa) || (0 != num_prin_sa)) {
fprintf(stderr, ">> For Persistent Reserve Out one and "
"only one appropriate\n>> service action must be "
"chosen (e.g. '--register')\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else { /* syntax check on PRIN arguments */
if (num_prout_sa > 0) {
fprintf(stderr, ">> When a service action for Persistent "
"Reserve Out is chosen the\n"
">> '--out' option must be given (as a safeguard)\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (0 == num_prin_sa) {
fprintf(stderr, ">> No service action given; assume Persistent"
@@ -891,20 +898,20 @@ int main(int argc, char * argv[])
fprintf(stderr, "Too many service actions given; choose "
"one only\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if ((param_unreg || param_rtp) && (PROUT_REG_MOVE_SA != prout_sa)) {
fprintf(stderr, "--unreg or --relative-target-port"
" only useful with --register-move\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if ((PROUT_REG_MOVE_SA == prout_sa) && (1 != num_transportids)) {
fprintf(stderr, "with --register-move one (and only one) "
"--transport-id should be given\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (((PROUT_RES_SA == prout_sa) ||
(PROUT_REL_SA == prout_sa) ||
@@ -926,7 +933,7 @@ int main(int argc, char * argv[])
do_verbose)) < 0) {
fprintf(stderr, "sg_persist: error opening file (ro): %s: %s\n",
device_name, safe_strerror(-sg_fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (0 == sg_simple_inquiry(sg_fd, &inq_resp, 1, do_verbose)) {
printf(" %.8s %.16s %.4s\n", inq_resp.vendor, inq_resp.product,
@@ -940,7 +947,7 @@ int main(int argc, char * argv[])
} else {
printf("sg_persist: %s doesn't respond to a SCSI INQUIRY\n",
device_name);
- return 1;
+ return SG_LIB_CAT_OTHER;
}
sg_cmds_close_device(sg_fd);
}
@@ -949,7 +956,7 @@ int main(int argc, char * argv[])
do_verbose)) < 0) {
fprintf(stderr, "sg_persist: error opening file (rw): %s: %s\n",
device_name, safe_strerror(-sg_fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (prin)
@@ -965,6 +972,11 @@ int main(int argc, char * argv[])
param_sark, param_alltgpt, param_aptpl,
transportid_arr, transportid_arr_len, do_verbose);
- sg_cmds_close_device(sg_fd);
- return ret;
+ res = sg_cmds_close_device(sg_fd);
+ if (res < 0) {
+ fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ return SG_LIB_FILE_ERROR;
+ }
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_prevent.8 b/sg_prevent.8
index 891d4a51..3fa3db8b 100644
--- a/sg_prevent.8
+++ b/sg_prevent.8
@@ -1,4 +1,4 @@
-.TH SG_PREVENT "8" "November 2004" "sg3_utils-1.11" SG3_UTILS
+.TH SG_PREVENT "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_prevent \- invoke SCSI PREVENT ALLOW MEDIUM REMOVAL command on a device
.SH SYNOPSIS
@@ -45,12 +45,15 @@ In some cases removable media can be ejected by an application (see
sg_start and its '-loej' option). Often a user with physical access
to the drive can attempt to eject the media by pressing a button on
the drive.
+.SH EXIT STATUS
+The exit status of sg_prevent is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHORS
Written by Douglas Gilbert.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2004 Douglas Gilbert
+Copyright \(co 2004-2006 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.
diff --git a/sg_prevent.c b/sg_prevent.c
index 0dfd6efb..35c36732 100644
--- a/sg_prevent.c
+++ b/sg_prevent.c
@@ -46,7 +46,7 @@
* given SCSI device.
*/
-static char * version_str = "1.03 20060125";
+static char * version_str = "1.04 20060623";
#define ME "sg_prevent: "
@@ -87,7 +87,7 @@ int main(int argc, char * argv[])
int prevent = -1;
int verbose = 0;
char device_name[256];
- int ret = 1;
+ int ret = 0;
memset(device_name, 0, sizeof device_name);
while (1) {
@@ -110,7 +110,7 @@ int main(int argc, char * argv[])
prevent = sg_get_num(optarg);
if ((prevent < 0) || (prevent > 3)) {
fprintf(stderr, "bad argument to '--prevent'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 'v':
@@ -122,7 +122,7 @@ int main(int argc, char * argv[])
default:
fprintf(stderr, "unrecognised switch code 0x%x ??\n", c);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (optind < argc) {
@@ -136,18 +136,18 @@ int main(int argc, char * argv[])
fprintf(stderr, "Unexpected extra argument: %s\n",
argv[optind]);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (0 == device_name[0]) {
fprintf(stderr, "missing device name!\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (allow && (prevent >= 0)) {
fprintf(stderr, "can't give both '--allow' and '--prevent='\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (allow)
prevent = 0;
@@ -158,11 +158,16 @@ int main(int argc, char * argv[])
if (sg_fd < 0) {
fprintf(stderr, ME "open error: %s: %s\n", device_name,
safe_strerror(-sg_fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
res = sg_ll_prevent_allow(sg_fd, prevent, 1, verbose);
+ ret = res;
if (0 == res)
- ret = 0;
+ ;
+ else if (SG_LIB_CAT_NOT_READY == res)
+ fprintf(stderr, "Device not ready\n");
+ else if (SG_LIB_CAT_UNIT_ATTENTION == res)
+ fprintf(stderr, "Unit attention\n");
else if (SG_LIB_CAT_INVALID_OP == res)
fprintf(stderr, "Prevent allow medium removal command not "
"supported\n");
@@ -174,8 +179,9 @@ int main(int argc, char * argv[])
res = sg_cmds_close_device(sg_fd);
if (res < 0) {
- fprintf(stderr, ME "close error: %s\n", safe_strerror(-res));
- return 1;
+ fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ return SG_LIB_FILE_ERROR;
}
- return ret;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_rbuf.8 b/sg_rbuf.8
index 69d32485..af45e386 100644
--- a/sg_rbuf.8
+++ b/sg_rbuf.8
@@ -1,4 +1,4 @@
-.TH SG_RBUF "8" "May 2005" "sg3_utils-1.15" SG3_UTILS
+.TH SG_RBUF "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_rbuf \- reads data using SCSI READ BUFFER command
.SH SYNOPSIS
@@ -106,6 +106,9 @@ Read 200 MiB (actual 199 MiB, 209531584 bytes),
buffer size=3354 KiB
.br
real 0m2.784s, user 0m0.000s, sys 0m0.000s
+.SH EXIT STATUS
+The exit status of sg_rbuf is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHOR
Written by Doug Gilbert
.SH "REPORTING BUGS"
diff --git a/sg_rbuf.c b/sg_rbuf.c
index e31551bd..14da102d 100644
--- a/sg_rbuf.c
+++ b/sg_rbuf.c
@@ -45,7 +45,7 @@
#define ME "sg_rbuf: "
-static char * version_str = "4.84 20060311";
+static char * version_str = "4.85 20060623";
static void usage()
{
@@ -119,7 +119,7 @@ int main(int argc, char * argv[])
exit(0);
case '?':
usage();
- return 1;
+ return 0;
default:
jmp_out = 1;
break;
@@ -135,7 +135,7 @@ int main(int argc, char * argv[])
if ((1 != num) || (buf_size <= 0)) {
printf("Couldn't decode number after 'b=' option\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
buf_size *= 1024;
}
@@ -145,12 +145,12 @@ int main(int argc, char * argv[])
if (1 != num) {
printf("Couldn't decode number after 's=' option\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (jmp_out) {
fprintf(stderr, "Unrecognized option: %s\n", cp);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == file_name)
file_name = cp;
@@ -158,19 +158,19 @@ int main(int argc, char * argv[])
fprintf(stderr, "too many arguments, got: %s, not expecting: "
"%s\n", file_name, cp);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (0 == file_name) {
fprintf(stderr, "No <scsi_device> argument given\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
sg_fd = open(file_name, O_RDONLY | O_NONBLOCK);
if (sg_fd < 0) {
perror(ME "open error");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
/* Don't worry, being very careful not to write to a none-sg file ... */
if (do_mmap) {
@@ -179,7 +179,7 @@ int main(int argc, char * argv[])
}
if (NULL == (rawp = malloc(512))) {
printf(ME "out of memory (query)\n");
- return 1;
+ return SG_LIB_CAT_OTHER;
}
rbBuff = rawp;
@@ -208,13 +208,14 @@ int main(int argc, char * argv[])
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
perror(ME "SG_IO READ BUFFER descriptor error");
if (rawp) free(rawp);
- return 1;
+ return SG_LIB_CAT_OTHER;
}
if (verbose > 2)
fprintf(stderr, " duration=%u ms\n", io_hdr.duration);
/* now for the error processing */
- switch (sg_err_category3(&io_hdr)) {
+ res = sg_err_category3(&io_hdr);
+ switch (res) {
case SG_LIB_CAT_RECOVERED:
sg_chk_n_print3("READ BUFFER descriptor, continuing", &io_hdr,
verbose > 1);
@@ -224,7 +225,7 @@ int main(int argc, char * argv[])
default: /* won't bother decoding other categories */
sg_chk_n_print3("READ BUFFER descriptor error", &io_hdr, verbose > 1);
if (rawp) free(rawp);
- return 1;
+ return (res >= 0) ? res : SG_LIB_CAT_OTHER;
}
buf_capacity = ((rbBuff[1] << 16) | (rbBuff[2] << 8) | rbBuff[3]);
@@ -237,7 +238,7 @@ int main(int argc, char * argv[])
printf("Requested buffer size=%d exceeds reported capacity=%d\n",
buf_size, buf_capacity);
if (rawp) free(rawp);
- return 1;
+ return SG_LIB_CAT_MALFORMED;
}
if (rawp) {
free(rawp);
@@ -258,17 +259,19 @@ int main(int argc, char * argv[])
if (MAP_FAILED == rbBuff) {
if (ENOMEM == errno)
printf(ME "mmap() out of memory, try a smaller "
- "buffer size than %d KiB\n", buf_size / 1024);
+ "buffer size than %d KiB\n"
+ " [with '-b=<n>' where <n> is in KB]\n",
+ buf_size / 1024);
else
perror(ME "error using mmap()");
- return 1;
+ return SG_LIB_CAT_OTHER;
}
}
else { /* non mmap-ed IO */
rawp = malloc(buf_size + (do_dio ? psz : 0));
if (NULL == rawp) {
printf(ME "out of memory (data)\n");
- return 1;
+ return SG_LIB_CAT_OTHER;
}
if (do_dio) /* align to page boundary */
rbBuff= (unsigned char *)(((unsigned long)rawp + psz - 1) &
@@ -323,18 +326,21 @@ int main(int argc, char * argv[])
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
if (ENOMEM == errno)
printf(ME "SG_IO data; out of memory, try a smaller "
- "buffer size than %d KiB\n", buf_size / 1024);
+ "buffer size than %d KiB\n"
+ " [with '-b=<n>' where <n> is in KB]\n",
+ buf_size / 1024);
else
perror(ME "SG_IO READ BUFFER data error");
if (rawp) free(rawp);
- return 1;
+ return SG_LIB_CAT_OTHER;
}
if (verbose > 2)
fprintf(stderr, " duration=%u ms\n",
io_hdr.duration);
/* now for the error processing */
- switch (sg_err_category3(&io_hdr)) {
+ res = sg_err_category3(&io_hdr);
+ switch (res) {
case SG_LIB_CAT_CLEAN:
break;
case SG_LIB_CAT_RECOVERED:
@@ -344,7 +350,7 @@ int main(int argc, char * argv[])
default: /* won't bother decoding other categories */
sg_chk_n_print3("READ BUFFER data error", &io_hdr, verbose > 1);
if (rawp) free(rawp);
- return 1;
+ return (res >= 0) ? res : SG_LIB_CAT_OTHER;
}
if (do_dio &&
((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
@@ -392,7 +398,7 @@ int main(int argc, char * argv[])
res = close(sg_fd);
if (res < 0) {
perror(ME "close error");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
#ifdef SG_DEBUG
if (clear)
@@ -400,5 +406,5 @@ int main(int argc, char * argv[])
else
printf("read buffer non-zero\n");
#endif
- return 0;
+ return (res >= 0) ? res : SG_LIB_CAT_OTHER;
}
diff --git a/sg_rdac.8 b/sg_rdac.8
new file mode 100644
index 00000000..5c6fb215
--- /dev/null
+++ b/sg_rdac.8
@@ -0,0 +1,38 @@
+.TH SG_RDAC "4" "June 2006" "sg3_utils-1.21" SG3_UTILS
+.SH NAME
+sg_rdac \- Display or Modify RDAC Redundant Controller Page
+.SH SYNOPSIS
+.B sg_rdac
+[\fI-a\fR] [\fI-f=<lun>\fR] [\fI-v\fR] [\fI-V\fR] \fI<sg_device>\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+sg_rdac displays or modifies the RDAC controller settings via the
+Redundant Controller Page (0x2C). When modifying the settings it
+allows to transfer the ownership of individual drives to the
+controller the command was received on.
+.TP
+-a
+Transfer all (visible) devices
+.TP
+-f=<lun>
+Transfer the device with LUN <lun>. This command will only work if
+the controller supports 'Dual Active Mode' (aka active/active mode).
+.TP
+-v
+be verbose
+.TP
+-V
+print version string then exit
+.SH EXIT STATUS
+The exit status of sg_rdac is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHOR
+Written by Hannes Reinecke <hare at suse dot com>, based on sg_emc_trespass.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2006 Hannes Reinecke, Douglas Gilbert.
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/sg_rdac.c b/sg_rdac.c
new file mode 100644
index 00000000..8becf8c4
--- /dev/null
+++ b/sg_rdac.c
@@ -0,0 +1,392 @@
+/*
+ * sg_rdac
+ *
+ * Retrieve / set RDAC options.
+ *
+ * Copyright (C) 2006 Hannes Reinecke <hare@suse.de>
+ *
+ * Based on sg_modes.c and sg_emc_trespass.c; credits from there apply.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include "sg_lib.h"
+#include "sg_cmds.h"
+#include "sg_io_linux.h"
+
+
+static char * version_str = "1.02 20060623";
+
+unsigned char mode6_hdr[] = {
+ 75, /* Length */
+ 0, /* medium */
+ 0, /* params */
+ 8, /* Block descriptor length */
+};
+
+unsigned char block_descriptor[] = {
+ 0, /* Density code */
+ 0, 0, 0, /* Number of blocks */
+ 0, /* Reserved */
+ 0, 0x02, 0, /* 512 byte blocks */
+};
+
+struct rdac_legacy_page {
+ unsigned char page_code;
+ unsigned char page_length;
+ char current_serial[16];
+ char alternate_serial[16];
+ unsigned char current_mode_msb;
+ unsigned char current_mode_lsb;
+ unsigned char alternate_mode_msb;
+ unsigned char alternate_mode_lsb;
+ unsigned char quiescence;
+ unsigned char options;
+ unsigned char lun_table[32];
+ unsigned char lun_table_exp[32];
+ unsigned short reserved;
+};
+
+static int do_verbose = 0;
+
+static void dump_mode_page( unsigned char *page, int len )
+{
+ int i, k;
+
+ for (k = 0; k < len; k += 16) {
+
+ printf("%x:",k / 16);
+ for (i = 0; i < 16; i++) {
+ printf(" %02x", page[k + i]);
+ if (k + i >= len) {
+ printf("\n");
+ break;
+ }
+ }
+ printf("\n");
+ }
+
+}
+
+#define MX_ALLOC_LEN (1024 * 4)
+#define RDAC_CONTROLLER_PAGE 0x2c
+#define RDAC_CONTROLLER_PAGE_LEN 0x68
+#define LEGACY_PAGE 0x00
+#define EXPANDED_LUN_SPACE_PAGE 0x01
+#define RDAC_FAIL_ALL_PATHS 0x1
+#define RDAC_FAIL_SELECTED_PATHS 0x2
+#define RDAC_FORCE_QUIESCENCE 0x2
+#define RDAC_QUIESCENCE_TIME 10
+
+static int fail_all_paths(int fd)
+{
+ unsigned char fail_paths_pg[118];
+ struct rdac_legacy_page *rdac_page;
+ int res;
+
+ memset(fail_paths_pg, 0, 118);
+ memcpy(fail_paths_pg, mode6_hdr, 4);
+ memcpy(fail_paths_pg + 4, block_descriptor, 8);
+ rdac_page = (struct rdac_legacy_page *)(fail_paths_pg + 4 + 8);
+ rdac_page->page_code = RDAC_CONTROLLER_PAGE | 0x40;
+ rdac_page->page_length = RDAC_CONTROLLER_PAGE_LEN;
+ rdac_page->quiescence = RDAC_QUIESCENCE_TIME;
+ rdac_page->options = RDAC_FORCE_QUIESCENCE;
+ rdac_page->current_mode_lsb = RDAC_FAIL_ALL_PATHS;
+
+ res = sg_ll_mode_select6(fd, 1 /* pf */, 0 /* sp */,
+ fail_paths_pg, 118,
+ 1, (do_verbose ? 2 : 0));
+
+ switch (res) {
+ case 0:
+ if (do_verbose)
+ fprintf(stderr, "fail paths successful\n");
+ break;
+ case SG_LIB_CAT_INVALID_OP:
+ fprintf(stderr, "fail paths page failed (Invalid opcode)\n");
+ break;
+ case SG_LIB_CAT_ILLEGAL_REQ:
+ fprintf(stderr, "fail paths page failed (illegal request)\n");
+ break;
+ case SG_LIB_CAT_NOT_READY:
+ fprintf(stderr, "fail paths page failed (not ready)\n");
+ break;
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ fprintf(stderr, "fail paths page failed (unit attention)\n");
+ break;
+ default:
+ if (do_verbose)
+ fprintf(stderr, "fail paths failed\n");
+ break;
+ }
+
+ return res;
+}
+
+static int fail_this_path(int fd, int lun)
+{
+ unsigned char fail_paths_pg[118];
+ struct rdac_legacy_page *rdac_page;
+ int res;
+
+ memset(fail_paths_pg, 0, 118);
+ memcpy(fail_paths_pg, mode6_hdr, 4);
+ memcpy(fail_paths_pg + 4, block_descriptor, 8);
+ rdac_page = (struct rdac_legacy_page *)(fail_paths_pg + 4 + 8);
+ rdac_page->page_code = RDAC_CONTROLLER_PAGE | 0x40;
+ rdac_page->page_length = RDAC_CONTROLLER_PAGE_LEN;
+ rdac_page->current_mode_lsb = RDAC_FAIL_SELECTED_PATHS;
+ rdac_page->quiescence = RDAC_QUIESCENCE_TIME;
+ rdac_page->options = RDAC_FORCE_QUIESCENCE;
+ memset(rdac_page->lun_table, 0x0, 32);
+ rdac_page->lun_table[lun] = 0x81;
+
+ res = sg_ll_mode_select6(fd, 1 /* pf */, 0 /* sp */,
+ fail_paths_pg, 118,
+ 1, (do_verbose ? 2 : 0));
+
+ switch (res) {
+ case 0:
+ if (do_verbose)
+ fprintf(stderr, "fail paths successful\n");
+ break;
+ case SG_LIB_CAT_INVALID_OP:
+ fprintf(stderr, "fail paths page failed (Invalid opcode)\n");
+ break;
+ case SG_LIB_CAT_NOT_READY:
+ fprintf(stderr, "fail paths page failed (not ready)\n");
+ break;
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ fprintf(stderr, "fail paths page failed (unit attention)\n");
+ break;
+ case SG_LIB_CAT_ILLEGAL_REQ:
+ fprintf(stderr, "fail lun %d page failed (illegal request)\n",
+ lun);
+ break;
+ default:
+ if (do_verbose)
+ fprintf(stderr, "fail paths failed\n");
+ break;
+ }
+
+ return res;
+}
+
+static void print_rdac_mode( unsigned char *ptr )
+{
+ struct rdac_legacy_page *rdac_ptr;
+ int i, k, bd_len;
+
+ bd_len = ptr[3];
+
+ rdac_ptr = (struct rdac_legacy_page *)(ptr + 4 + bd_len);
+
+ printf("RDAC Legacy page\n");
+ printf(" Controller serial: %s\n",
+ rdac_ptr->current_serial);
+ printf(" Alternate controller serial: %s\n",
+ rdac_ptr->alternate_serial);
+ printf(" RDAC mode (redundant processor): ");
+ switch (rdac_ptr->current_mode_msb) {
+ case 0x00:
+ printf("alternate controller not present; ");
+ break;
+ case 0x01:
+ printf("alternate controller present; ");
+ break;
+ default:
+ printf("(Unknown controller status 0x%x); ",
+ rdac_ptr->current_mode_msb);
+ break;
+ }
+ switch (rdac_ptr->current_mode_lsb) {
+ case 0x0:
+ printf("inactive\n");
+ break;
+ case 0x1:
+ printf("active\n");
+ break;
+ case 0x2:
+ printf("Dual active mode\n");
+ break;
+ default:
+ printf("(Unknown mode 0x%x)\n",
+ rdac_ptr->current_mode_lsb);
+ }
+
+ printf(" RDAC mode (alternate processor): ");
+ switch (rdac_ptr->alternate_mode_msb) {
+ case 0x00:
+ printf("alternate controller not present; ");
+ break;
+ case 0x01:
+ printf("alternate controller present; ");
+ break;
+ case 0x02:
+ printf("active/active mode; ");
+ break;
+ default:
+ printf("(Unknown status 0x%x); ",
+ rdac_ptr->alternate_mode_msb);
+ break;
+ }
+ switch (rdac_ptr->alternate_mode_lsb) {
+ case 0x0:
+ printf("inactive\n");
+ break;
+ case 0x1:
+ printf("active\n");
+ break;
+ case 0x2:
+ printf("Dual active mode\n");
+ break;
+ case 0x04:
+ printf("held in reset\n");
+ break;
+ default:
+ printf("(Unknown mode 0x%x)\n",
+ rdac_ptr->alternate_mode_lsb);
+ }
+ printf(" Quiescence timeout: %d\n", rdac_ptr->quiescence);
+ printf(" RDAC option 0x%x\n", rdac_ptr->options);
+ printf (" LUN Table:\n");
+ for (k = 0; k < 32; k += 8) {
+ printf(" %x:",k / 8);
+ for (i = 0; i < 8; i++) {
+ switch (rdac_ptr->lun_table[k + i]) {
+ case 0x0:
+ printf(" x");
+ break;
+ case 0x1:
+ printf(" p");
+ break;
+ case 0x2:
+ printf(" a");
+ break;
+ case 0x3:
+ printf(" u");
+ break;
+ default:
+ printf(" ?");
+ break;
+ }
+ }
+ printf("\n");
+ }
+}
+
+static void usage()
+{
+ printf("Usage: sg_rdac [-a] [-f=<lun>] [-v] <scsi_device>\n"
+ " where -a transfer all devices to the controller\n"
+ " serving <scsi_device>.\n"
+ " -f=<lun> transfer the device with LUN <lun> to the\n"
+ " controller serving <scsi_device>\n"
+ " -v verbose\n"
+ " -V print version then exit\n"
+ " Display/Modify RDAC Redundant Controller Page 0x2c.\n"
+ " If [-a] or [-f] is not specified the current settings"
+ " are displayed.\n");
+}
+
+int main(int argc, char * argv[])
+{
+ unsigned char rsp_buff[MX_ALLOC_LEN];
+ char **argptr;
+ char * file_name = 0;
+ int res, fd, k, lun = -1;
+ int fail_all = 0;
+ int fail_path = 0;
+ int ret = 0;
+
+ if (argc < 2)
+ usage ();
+
+ for (k = 1; k < argc; ++k) {
+ argptr = argv + k;
+ if (!strcmp (*argptr, "-v"))
+ ++do_verbose;
+ else if (!strncmp(*argptr, "-f=",3)) {
+ ++fail_path;
+ lun = strtoul(*argptr + 3, NULL, 0);
+ }
+ else if (!strcmp(*argptr, "-a")) {
+ ++fail_all;
+ }
+ else if (!strcmp(*argptr, "-V")) {
+ fprintf(stderr, "sg_rdac version: %s\n", version_str);
+ return 0;
+ }
+ else if (*argv[k] == '-') {
+ fprintf(stderr, "Unrecognized switch: %s\n", argv[k]);
+ file_name = 0;
+ break;
+ }
+ else if (0 == file_name)
+ file_name = argv[k];
+ else {
+ fprintf(stderr, "too many arguments\n");
+ file_name = 0;
+ break;
+ }
+ }
+ if (0 == file_name) {
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+
+ fd = open(file_name, O_RDWR | O_NONBLOCK);
+ if (fd < 0) {
+ fprintf(stderr, "Error trying to open %s\n", file_name);
+ perror("");
+ usage();
+ return SG_LIB_FILE_ERROR;
+ }
+
+ if (fail_all) {
+ res = fail_all_paths(fd);
+ } else if (fail_path) {
+ res = fail_this_path(fd, lun);
+ } else {
+ res = sg_ll_mode_sense6(fd, /*DBD*/ 0, /* page control */0,
+ 0x2c, 0, rsp_buff, 252,
+ 1, do_verbose);
+
+ if (!res) {
+ if (do_verbose)
+ dump_mode_page(rsp_buff, rsp_buff[0]);
+ print_rdac_mode(rsp_buff);
+ }
+ }
+ ret = res;
+ if (SG_LIB_CAT_INVALID_OP == res)
+ fprintf(stderr, ">>>>>> try again without the '-6' "
+ "switch for a 10 byte MODE SENSE command\n");
+ else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+ fprintf(stderr, "invalid field in cdb (perhaps subpages "
+ "or page control (PC) not supported)\n");
+ else if (SG_LIB_CAT_NOT_READY == res)
+ fprintf(stderr, "mode sense failed, device not ready\n");
+ else if (res)
+ fprintf(stderr," mode sense failed\n");
+
+ res = close(fd);
+ if (res < 0) {
+ fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ return SG_LIB_FILE_ERROR;
+ }
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg_read.8 b/sg_read.8
index a7cedebc..bdefde55 100644
--- a/sg_read.8
+++ b/sg_read.8
@@ -1,4 +1,4 @@
-.TH SG_READ "8" "April 2006" "sg3_utils-1.20" SG3_UTILS
+.TH SG_READ "8" "July 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_read \- read blocks of data continually from same offset
.SH SYNOPSIS
@@ -10,14 +10,15 @@ sg_read \- read blocks of data continually from same offset
.SH DESCRIPTION
.\" Add any additional description here
.PP
-Read data from a Linux SCSI generic (sg) device, a block device, a raw
-device or a normal file with each read command issued to the same offset.
-This can be used to test (or time) disk caching, SCSI (or some other)
-transport throughput, and/or SCSI command overhead.
+Read data from a Linux SCSI generic (sg) device, a block device or
+a normal file with each read command issued to the same offset or
+logical block address (lba). This can be used to test (or time) disk
+caching, SCSI (or some other) transport throughput, and/or SCSI
+command overhead.
.PP
When the 'count' argument is positive, then up to 'bpt' blocks are read
at a time, until the 'count' is exhausted. Each read operation
-starts at the same offset which, if 'skip' is not given, is the beginning
+starts at the same lba which, if 'skip' is not given, is the beginning
of the file or device.
.PP
The 'count' argument may be negative when '<ifile>' is a sg device
@@ -38,7 +39,7 @@ device.
bpt=BLOCKS
each read operation will be made using this number of blocks (or less if
near the end of 'count'). Default is 128. Note also that each read
-operation starts at the same offset (as given by skip or 0). If 'bpt=0'
+operation starts at the same lba (as given by 'skip=<n>' or 0). If 'bpt=0'
then the 'count' is interpreted as the number of zero block READ SCSI
commands to issue.
.TP
@@ -160,6 +161,9 @@ output of third command will look like this:
Average number of READ commands per second was 1735.27
.br
1000000+0 records in, SCSI commands issued: 7813
+.SH EXIT STATUS
+The exit status of sg_read is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHORS
Written by Doug Gilbert.
.SH "REPORTING BUGS"
diff --git a/sg_read.c b/sg_read.c
index ba76e06f..32be3bfa 100644
--- a/sg_read.c
+++ b/sg_read.c
@@ -40,7 +40,7 @@
*/
-static const char * version_str = "1.10 20060409";
+static const char * version_str = "1.14 20060702";
#define DEF_BLOCK_SIZE 512
#define DEF_BLOCKS_PER_TRANSFER 128
@@ -159,6 +159,8 @@ static void usage()
"SG_IO\n"
" bpt is blocks_per_transfer (default is 128, or 64 KiB for "
"def 'bs')\n"
+ " setting 'bpt=0' will do 'count' zero block SCSI "
+ "READs\n"
" bs must match sector size if 'if' accessed via SCSI "
"commands (def=512)\n"
" cdbsz size of SCSI READ command (default is 10)\n"
@@ -278,8 +280,9 @@ static int sg_build_scsi_cdb(unsigned char * cdbp, int cdb_sz,
return 0;
}
-/* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM),
- 2 -> try again */
+/* -3 medium/hardware error, -2 -> not ready, 0 -> successful,
+ 1 -> recoverable (ENOMEM), 2 -> try again (e.g. unit attention),
+ -1 -> other unrecoverable error */
static int sg_bread(int sg_fd, unsigned char * buff, int blocks,
long long from_block, int bs, int cdbsz,
int fua, int dpo, int * diop, int do_mmap,
@@ -339,8 +342,18 @@ static int sg_bread(int sg_fd, unsigned char * buff, int blocks,
/* fall through */
case SG_LIB_CAT_CLEAN:
break;
- case SG_LIB_CAT_MEDIA_CHANGED:
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ if (verbose)
+ sg_chk_n_print3("reading", &io_hdr, verbose - 1);
return 2;
+ case SG_LIB_CAT_NOT_READY:
+ if (verbose)
+ sg_chk_n_print3("reading", &io_hdr, verbose - 1);
+ return -2;
+ case SG_LIB_CAT_MEDIUM_HARD:
+ if (verbose)
+ sg_chk_n_print3("reading", &io_hdr, verbose - 1);
+ return -3;
default:
sg_chk_n_print3("reading", &io_hdr, verbose);
return -1;
@@ -368,6 +381,7 @@ int main(int argc, char * argv[])
char * key;
char * buf;
char inf[INF_SZ];
+ char outf[INF_SZ];
int in_type = FT_OTHER;
int do_dio = 0;
int do_odir = 0;
@@ -387,8 +401,10 @@ int main(int argc, char * argv[])
char ebuff[EBUFF_SZ];
struct timeval start_tm, end_tm;
const char * read_str;
- size_t psz = getpagesize();
+ int ret = 0;
+ size_t psz;
+ psz = getpagesize();
inf[0] = '\0';
for (k = 1; k < argc; k++) {
@@ -407,13 +423,13 @@ int main(int argc, char * argv[])
bpt = sg_get_num(buf);
if (-1 == bpt) {
fprintf(stderr, ME "bad argument to 'bpt'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strcmp(key,"bs")) {
bs = sg_get_num(buf);
if (-1 == bs) {
fprintf(stderr, ME "bad argument to 'bs'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strcmp(key,"cdbsz"))
scsi_cdbsz = sg_get_num(buf);
@@ -423,14 +439,14 @@ int main(int argc, char * argv[])
dd_count = sg_get_llnum(buf + 1);
if (-1 == dd_count) {
fprintf(stderr, ME "bad argument to 'count'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
dd_count = - dd_count;
} else {
dd_count = sg_get_llnum(buf);
if (-1 == dd_count) {
fprintf(stderr, ME "bad argument to 'count'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
} else if (0 == strcmp(key,"dio"))
@@ -447,11 +463,13 @@ int main(int argc, char * argv[])
no_dxfer = sg_get_num(buf);
else if (0 == strcmp(key,"odir"))
do_odir = sg_get_num(buf);
+ else if (strcmp(key,"of") == 0)
+ strncpy(outf, buf, INF_SZ);
else if (0 == strcmp(key,"skip")) {
skip = sg_get_llnum(buf);
if (-1 == skip) {
fprintf(stderr, ME "bad argument to 'skip'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strcmp(key,"time"))
do_time = sg_get_num(buf);
@@ -466,7 +484,7 @@ int main(int argc, char * argv[])
} else {
fprintf(stderr, "Unrecognized argument '%s'\n", key);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (bs <= 0) {
@@ -478,11 +496,11 @@ int main(int argc, char * argv[])
if (! count_given) {
fprintf(stderr, "'count' must be given\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (skip < 0) {
fprintf(stderr, "skip cannot be negative\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (bpt < 1) {
if (0 == bpt) {
@@ -490,16 +508,16 @@ int main(int argc, char * argv[])
dd_count = - dd_count;
} else {
fprintf(stderr, "bpt must be greater than 0\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (do_dio && do_mmap) {
fprintf(stderr, "cannot select both dio and mmap\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (no_dxfer && (do_dio || do_mmap)) {
fprintf(stderr, "cannot select no_dxfer with dio or mmap\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
install_handler (SIGINT, interrupt_handler);
@@ -510,17 +528,17 @@ int main(int argc, char * argv[])
if (! inf[0]) {
fprintf(stderr, "must provide 'if=<filename>'\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (0 == strcmp("-", inf)) {
fprintf(stderr, "'-' (stdin) invalid as <filename>\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
in_type = dd_filetype(inf);
if (FT_ERROR == in_type) {
fprintf(stderr, "Unable to access: %s\n", inf);
- return 1;
+ return SG_LIB_FILE_ERROR;
} else if ((FT_BLOCK & in_type) && do_blk_sgio)
in_type |= FT_SG;
@@ -528,7 +546,7 @@ int main(int argc, char * argv[])
if ((dd_count < 0) && (6 == scsi_cdbsz)) {
fprintf(stderr, ME "SCSI READ (6) can't do zero block "
"reads\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
flags = O_RDWR;
if (do_odir)
@@ -541,13 +559,17 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ,
ME "could not open %s for sg reading", inf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
}
if (verbose)
fprintf(stderr, "Opened %s for SG_IO with flags=0x%x\n", inf,
flags);
if ((dd_count > 0) && (! (FT_BLOCK & in_type))) {
+ if (verbose > 2) {
+ if (ioctl(infd, SG_GET_RESERVED_SIZE, &t) >= 0)
+ fprintf(stderr, " SG_GET_RESERVED_SIZE yields: %d\n", t);
+ }
t = bs * bpt;
if ((do_mmap) && (0 != (t % psz)))
t = ((t / psz) + 1) * psz; /* round up to next pagesize */
@@ -557,24 +579,24 @@ int main(int argc, char * argv[])
res = ioctl(infd, SG_GET_VERSION_NUM, &t);
if ((res < 0) || (t < 30000)) {
fprintf(stderr, ME "sg driver prior to 3.x.y\n");
- return 1;
+ return SG_LIB_CAT_OTHER;
}
if (do_mmap && (t < 30122)) {
fprintf(stderr, ME "mmap-ed IO needs a sg driver version "
">= 3.1.22\n");
- return 1;
+ return SG_LIB_CAT_OTHER;
}
}
} else {
if (do_mmap) {
fprintf(stderr, ME "mmap-ed IO only support on sg "
"devices\n");
- return 1;
+ return SG_LIB_CAT_OTHER;
}
if (dd_count < 0) {
fprintf(stderr, ME "negative 'count' only supported with "
"SCSI READs\n");
- return 1;
+ return SG_LIB_CAT_OTHER;
}
flags = O_RDONLY;
if (do_odir)
@@ -583,7 +605,7 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ,
ME "could not open %s for reading", inf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (verbose)
fprintf(stderr, "Opened %s for Unix reads with flags=0x%x\n",
@@ -596,7 +618,7 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ,
ME "couldn't skip to required position on %s", inf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
}
}
@@ -611,7 +633,7 @@ int main(int argc, char * argv[])
if (0 == wrkBuff) {
fprintf(stderr, "Not enough user memory for aligned "
"storage\n");
- return 1;
+ return SG_LIB_CAT_OTHER;
}
wrkPos = (unsigned char *)(((unsigned long)wrkBuff + psz - 1) &
(~(psz - 1)));
@@ -620,13 +642,13 @@ int main(int argc, char * argv[])
MAP_SHARED, infd, 0);
if (MAP_FAILED == wrkPos) {
perror(ME "error from mmap()");
- return 1;
+ return SG_LIB_CAT_OTHER;
}
} else {
wrkBuff = malloc(bs * bpt);
if (0 == wrkBuff) {
fprintf(stderr, "Not enough user memory\n");
- return 1;
+ return SG_LIB_CAT_OTHER;
}
wrkPos = wrkBuff;
}
@@ -667,12 +689,29 @@ int main(int argc, char * argv[])
fua, dpo, &dio_tmp, do_mmap, no_dxfer);
} else if (2 == res) {
fprintf(stderr,
- "Unit attention, media changed, continuing (r)\n");
+ "Unit attention, try again (r)\n");
res = sg_bread(infd, wrkPos, blocks, skip, bs, scsi_cdbsz,
fua, dpo, &dio_tmp, do_mmap, no_dxfer);
}
if (0 != res) {
- fprintf(stderr, ME "SCSI READ failed\n");
+ switch (res) {
+ case -3:
+ ret = SG_LIB_CAT_MEDIUM_HARD;
+ fprintf(stderr, ME "SCSI READ medium/hardware error\n");
+ break;
+ case -2:
+ ret = SG_LIB_CAT_NOT_READY;
+ fprintf(stderr, ME "device not ready\n");
+ break;
+ case 2:
+ ret = SG_LIB_CAT_UNIT_ATTENTION;
+ fprintf(stderr, ME "SCSI READ unit attention\n");
+ break;
+ default:
+ ret = SG_LIB_CAT_OTHER;
+ fprintf(stderr, ME "SCSI READ failed\n");
+ break;
+ }
break;
} else {
in_full += blocks;
@@ -779,7 +818,8 @@ int main(int argc, char * argv[])
res = 0;
if (0 != dd_count) {
fprintf(stderr, "Some error occurred,");
- res = 2;
+ if (0 == ret)
+ ret = SG_LIB_CAT_OTHER;
}
print_stats(iters, read_str);
@@ -801,5 +841,5 @@ int main(int argc, char * argv[])
if (sum_of_resids)
fprintf(stderr, ">> Non-zero sum of residual counts=%d\n",
sum_of_resids);
- return res;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_read_long.8 b/sg_read_long.8
index 846e40c0..a5aaf2b2 100644
--- a/sg_read_long.8
+++ b/sg_read_long.8
@@ -1,11 +1,11 @@
-.TH SG_READ_LONG "8" "March 2006" "sg3_utils-1.20" SG3_UTILS
+.TH SG_READ_LONG "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
-sg_read_long \- send the scsi command read long
+sg_read_long \- calls a READ LONG command on a SCSI device
.SH SYNOPSIS
.B sg_read_long
-[\fI--correct\fR] [\fI--help\fR] [\fI--lba=<num>\fR] [\fI--out=<name>\fR]
-[\fI--verbose\fR] [\fI--version\fR] [\fI--xfer_len=<num>\fR]
-\fI<scsi_device>\fR
+[\fI--16\fR] [\fI--correct\fR] [\fI--help\fR] [\fI--lba=<num>\fR]
+[\fI--out=<name>\fR] [\fI--verbose\fR] [\fI--version\fR]
+[\fI--xfer_len=<num>\fR] \fI<scsi_device>\fR
.SH DESCRIPTION
.\" Add any additional description here
.PP
@@ -14,7 +14,12 @@ buffer is output in hex and ASCII to stdout or placed in a file.
Note that the data returned includes the logical block data (typically
512 bytes for a disk) plus ECC information (whose format is proprietary)
plus optionally other proprietary data.
-.P
+.TP
+--16 | -S
+attempts a READ LONG(16) SCSI command. The default action is to attempt
+a READ LONG(10) SCSI command. The READ LONG(10) command has a 32 bit
+field for the lba while READ LONG(16) has a 64 bit field.
+.TP
--correct | -c
sets the 'CORRCT' bit in the READ LONG SCSI command. When set the data is
corrected by the ECC before being transferred back to this utility. The
@@ -67,6 +72,9 @@ given, multipliers cannot be used.
As a data point, Fujitsu uses a 54 byte ECC (per block) which is capable
of correcting up to a single burst error or 216 bits "on the
fly". [Information obtained from MAV20xxrc product manual.]
+.SH EXIT STATUS
+The exit status of sg_read_long is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHORS
Written by Douglas Gilbert.
.SH "REPORTING BUGS"
diff --git a/sg_read_long.c b/sg_read_long.c
index 07065773..7567de52 100644
--- a/sg_read_long.c
+++ b/sg_read_long.c
@@ -23,7 +23,7 @@
the sector data and the ECC bytes.
*/
-static char * version_str = "1.07 20060106";
+static char * version_str = "1.09 20060623";
#define MAX_XFER_LEN 10000
@@ -33,6 +33,7 @@ static char * version_str = "1.07 20060106";
static struct option long_options[] = {
+ {"16", 0, 0, 'S'},
{"correct", 0, 0, 'c'},
{"help", 0, 0, 'h'},
{"lba", 1, 0, 'l'},
@@ -46,14 +47,17 @@ static struct option long_options[] = {
static void usage()
{
fprintf(stderr, "Usage: "
- "sg_read_long [--correct] [--help] [--lba=<num>] [--out=<name>]\n"
+ "sg_read_long [--16] [--correct] [--help] [--lba=<num>] "
+ "[--out=<name>]\n"
" [--verbose] [--version] [--xfer_len=<num>]"
" <scsi_device>\n"
- " where: --correct|-c use ECC to correct data "
+ " where: --16|-S do READ LONG(16) (default: "
+ "READ LONG(10))\n"
+ " --correct|-c use ECC to correct data "
"(default: don't)\n"
" --help|-h print out usage message\n"
" --lba=<num>|-l <num> logical block address"
- " (default 0)\n"
+ " (default: 0)\n"
" --out=<name>|-o <name> output to file <name>\n"
" --verbose|-v increase verbosity\n"
" --version|-V print version string and"
@@ -64,31 +68,48 @@ static void usage()
);
}
-/* Returns 0 if successful, else -1 */
-static int process_read_long(int sg_fd, int correct, unsigned long lba,
- void * data_out, int xfer_len, int verbose)
+/* Returns 0 if successful */
+static int process_read_long(int sg_fd, int do_16, int correct,
+ unsigned long long llba, void * data_out,
+ int xfer_len, int verbose)
{
int offset, res;
- res = sg_ll_read_long10(sg_fd, correct, lba, data_out, xfer_len,
- &offset, 1, verbose);
+ if (do_16)
+ res = sg_ll_read_long16(sg_fd, correct, llba, data_out, xfer_len,
+ &offset, 1, verbose);
+ else
+ res = sg_ll_read_long10(sg_fd, correct, (unsigned long)llba,
+ data_out, xfer_len, &offset, 1, verbose);
switch (res) {
case 0:
- return 0;
+ break;
+ case SG_LIB_CAT_NOT_READY:
+ fprintf(stderr, " SCSI READ LONG (%s) failed, device not ready\n",
+ (do_16 ? "16" : "10"));
+ break;
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ fprintf(stderr, " SCSI READ LONG (%s) failed, unit attention\n",
+ (do_16 ? "16" : "10"));
+ break;
case SG_LIB_CAT_INVALID_OP:
- fprintf(stderr, " SCSI READ LONG (10) command not supported\n");
- return -1;
+ fprintf(stderr, " SCSI READ LONG (%s) command not supported\n",
+ (do_16 ? "16" : "10"));
+ break;
case SG_LIB_CAT_ILLEGAL_REQ:
- fprintf(stderr, " SCSI READ LONG (10) command, bad field in cdb\n");
- return -1;
+ fprintf(stderr, " SCSI READ LONG (%s) command, bad field in cdb\n",
+ (do_16 ? "16" : "10"));
+ break;
case SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO:
fprintf(stderr, "<<< device indicates 'xfer_len' should be %d "
">>>\n", xfer_len - offset);
- return -1;
+ break;
default:
- fprintf(stderr, " SCSI READ LONG (10) command error\n");
- return -1;
+ fprintf(stderr, " SCSI READ LONG (%s) command error\n",
+ (do_16 ? "16" : "10"));
+ break;
}
+ return res;
}
@@ -99,20 +120,22 @@ int main(int argc, char * argv[])
void * rawp = NULL;
int correct = 0;
int xfer_len = 520;
- unsigned int lba = 0;
+ int do_16 = 0;
+ unsigned long long llba = 0;
int verbose = 0;
+ long long ll;
int got_stdout;
char device_name[256];
char out_fname[256];
char ebuff[EBUFF_SZ];
- int ret = 1;
+ int ret = 0;
memset(device_name, 0, sizeof device_name);
memset(out_fname, 0, sizeof out_fname);
while (1) {
int option_index = 0;
- c = getopt_long(argc, argv, "chl:o:vVx:", long_options,
+ c = getopt_long(argc, argv, "chl:o:SvVx:", long_options,
&option_index);
if (c == -1)
break;
@@ -126,15 +149,19 @@ int main(int argc, char * argv[])
usage();
return 0;
case 'l':
- lba = sg_get_num(optarg);
- if ((unsigned int)(-1) == lba) {
+ ll = sg_get_llnum(optarg);
+ if (-1 == ll) {
fprintf(stderr, "bad argument to '--lba'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
+ llba = (unsigned long long)ll;
break;
case 'o':
strncpy(out_fname, optarg, sizeof(out_fname));
break;
+ case 'S':
+ do_16 = 1;
+ break;
case 'v':
++verbose;
break;
@@ -145,13 +172,13 @@ int main(int argc, char * argv[])
xfer_len = sg_get_num(optarg);
if (-1 == xfer_len) {
fprintf(stderr, "bad argument to '--xfer_len'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
default:
fprintf(stderr, "unrecognised switch code 0x%x ??\n", c);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (optind < argc) {
@@ -165,42 +192,42 @@ int main(int argc, char * argv[])
fprintf(stderr, "Unexpected extra argument: %s\n",
argv[optind]);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (0 == device_name[0]) {
fprintf(stderr, "missing device name!\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (xfer_len >= MAX_XFER_LEN){
fprintf(stderr, "xfer_len (%d) is out of range ( < %d)\n",
xfer_len, MAX_XFER_LEN);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
if (sg_fd < 0) {
fprintf(stderr, ME "open error: %s: %s\n", device_name,
safe_strerror(-sg_fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (NULL == (rawp = malloc(MAX_XFER_LEN))) {
fprintf(stderr, ME "out of memory (query)\n");
sg_cmds_close_device(sg_fd);
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
readLongBuff = rawp;
memset(rawp, 0x0, MAX_XFER_LEN);
- fprintf(stderr, ME "issue read long (10) to device %s\n\t\txfer_len=%d "
- "(0x%x), lba=%d (0x%x), correct=%d\n", device_name, xfer_len,
- xfer_len, lba, lba, correct);
+ fprintf(stderr, ME "issue read long (%s) to device %s\n xfer_len=%d "
+ "(0x%x), lba=%llu (0x%llx), correct=%d\n", (do_16 ? "16" : "10"),
+ device_name, xfer_len, xfer_len, llba, llba, correct);
- if (process_read_long(sg_fd, correct, lba, readLongBuff, xfer_len,
- verbose))
+ if (process_read_long(sg_fd, do_16, correct, llba, readLongBuff,
+ xfer_len, verbose))
goto err_out;
if ('\0' == out_fname[0])
@@ -228,13 +255,13 @@ int main(int argc, char * argv[])
close(outfd);
}
- ret = 0;
err_out:
if (rawp) free(rawp);
res = sg_cmds_close_device(sg_fd);
if (res < 0) {
- fprintf(stderr, ME "close error: %s\n", safe_strerror(-res));
- return 1;
+ fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ return SG_LIB_FILE_ERROR;
}
- return ret;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_readcap.8 b/sg_readcap.8
index adabb159..8f6d77cd 100644
--- a/sg_readcap.8
+++ b/sg_readcap.8
@@ -1,4 +1,4 @@
-.TH SG_READCAP "8" "July 2005" "sg3_utils-1.16" SG3_UTILS
+.TH SG_READCAP "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_readcap \- calls a READ CAPACITY command on a SCSI device
.SH SYNOPSIS
@@ -59,18 +59,18 @@ and '-vvv' are also accepted yielding greater verbosity.
-V
outputs version string then exits.
.PP
-On exit this utility returns 0 if it succeeded otherwise a process status
-value of 1 is returned.
-.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 "sg_readcap /dev/sda"
and "sg_readcap /dev/hdd" (if /dev/hdd is a ATAPI CD/DVD device) will
work in the 2.6 series kernels.
+.SH EXIT STATUS
+The exit status of sg_readcap is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHORS
Written by Douglas Gilbert
.SH COPYRIGHT
-Copyright \(co 1999-2005 Douglas Gilbert
+Copyright \(co 1999-2006 Douglas Gilbert
.br
This software is distributed under the GPL version 2. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/sg_readcap.c b/sg_readcap.c
index 88de00cf..b5d7edb2 100644
--- a/sg_readcap.c
+++ b/sg_readcap.c
@@ -24,7 +24,7 @@
*/
-static char * version_str = "3.75 20060106";
+static char * version_str = "3.76 20060623";
#define ME "sg_readcap: "
@@ -63,6 +63,7 @@ int main(int argc, char * argv[])
int pmi = 0;
int do16 = 0;
int verbose = 0;
+ int ret = 0;
unsigned int last_blk_addr, block_size;
const char * file_name = 0;
const char * cp;
@@ -105,7 +106,7 @@ int main(int argc, char * argv[])
case '?':
case 'h':
usage();
- return 1;
+ return 0;
default:
jmp_out = 1;
break;
@@ -120,7 +121,7 @@ int main(int argc, char * argv[])
if (1 != num) {
printf("Bad value after 'lba=' option\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
llba = u;
if (llba > 0xfffffffeULL)
@@ -129,7 +130,7 @@ int main(int argc, char * argv[])
} else if (jmp_out) {
fprintf(stderr, "Unrecognized option: %s\n", cp);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == file_name)
file_name = cp;
@@ -137,30 +138,31 @@ int main(int argc, char * argv[])
fprintf(stderr, "too many arguments, got: %s, not expecting: "
"%s\n", file_name, cp);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (0 == file_name) {
fprintf(stderr, "No <device> argument given\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if ((0 == pmi) && (lba > 0)) {
fprintf(stderr, ME "lba can only be non-zero when pmi is set\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if ((sg_fd = sg_cmds_open_device(file_name,
(do16 ? 0 /* rw */ : 1), verbose)) < 0) {
fprintf(stderr, ME "error opening file: %s: %s\n", file_name,
safe_strerror(-sg_fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (! do16) {
res = sg_ll_readcap_10(sg_fd, pmi, lba, resp_buff, RCAP_REPLY_LEN,
0, verbose);
+ ret = res;
if (0 == res) {
last_blk_addr = ((resp_buff[0] << 24) | (resp_buff[1] << 16) |
(resp_buff[2] << 8) | resp_buff[3]);
@@ -206,13 +208,15 @@ int main(int argc, char * argv[])
< 0) {
fprintf(stderr, ME "error re-opening file: %s (rw): %s\n",
file_name, safe_strerror(-sg_fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (verbose)
fprintf(stderr, "READ CAPACITY (10) not supported, trying "
"READ CAPACITY (16)\n");
} else if (SG_LIB_CAT_ILLEGAL_REQ == res)
fprintf(stderr, "bad field in READ CAPACITY (10) cdb\n");
+ else if (SG_LIB_CAT_NOT_READY == res)
+ fprintf(stderr, "READ CAPACITY (10) failed, device not ready\n");
else if (! verbose)
fprintf(stderr, "READ CAPACITY (10) failed [res=%d], try "
"with '-v'\n", res);
@@ -220,6 +224,7 @@ int main(int argc, char * argv[])
if (do16) {
res = sg_ll_readcap_16(sg_fd, pmi, llba, resp_buff, RCAP16_REPLY_LEN,
0, verbose);
+ ret = res;
if (0 == res) {
for (k = 0, llast_blk_addr = 0; k < 8; ++k) {
llast_blk_addr <<= 8;
@@ -259,6 +264,8 @@ int main(int argc, char * argv[])
}
else if (SG_LIB_CAT_INVALID_OP == res)
fprintf(stderr, "READ CAPACITY (16) not supported\n");
+ else if (SG_LIB_CAT_NOT_READY == res)
+ fprintf(stderr, "READ CAPACITY (16) failed, device not ready\n");
else if (SG_LIB_CAT_ILLEGAL_REQ == res)
fprintf(stderr, "bad field in READ CAPACITY (10) cdb\n");
else if (! verbose)
@@ -267,10 +274,13 @@ int main(int argc, char * argv[])
}
if (brief)
printf("0x0 0x0\n");
- sg_cmds_close_device(sg_fd);
- return 1;
good:
- sg_cmds_close_device(sg_fd);
- return 0;
+ res = sg_cmds_close_device(sg_fd);
+ if (res < 0) {
+ fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ return SG_LIB_FILE_ERROR;
+ }
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_reassign.8 b/sg_reassign.8
index 8f91ef9f..979a93ad 100644
--- a/sg_reassign.8
+++ b/sg_reassign.8
@@ -1,4 +1,4 @@
-.TH SG_REASSIGN "8" "August 2005" "sg3_utils-1.16" SG3_UTILS
+.TH SG_REASSIGN "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_reassign \- reassign defective blocks on the given (disk) device
.SH SYNOPSIS
@@ -118,12 +118,15 @@ with the same addresses will cause the earlier addresses to be reassigned
again. At some stage the disk will run out of reserved locations.
So unless a large number of addresses are involved it may be safer to
reassign them one address at a time.
+.SH EXIT STATUS
+The exit status of sg_reassign is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHORS
Written by Douglas Gilbert.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2005 Douglas Gilbert
+Copyright \(co 2005-2006 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.
diff --git a/sg_reassign.c b/sg_reassign.c
index 5f5f8017..8dfdb07b 100644
--- a/sg_reassign.c
+++ b/sg_reassign.c
@@ -48,7 +48,7 @@
* vendor specific data is written.
*/
-static char * version_str = "1.05 20060312";
+static char * version_str = "1.06 20060623";
#define ME "sg_reassign: "
@@ -259,7 +259,7 @@ int main(int argc, char * argv[])
unsigned long long addr_arr[MAX_NUM_ADDR];
unsigned char param_arr[4 + (MAX_NUM_ADDR * 8)];
int param_len = 4;
- int ret = 1;
+ int ret = 0;
memset(device_name, 0, sizeof device_name);
while (1) {
@@ -276,7 +276,7 @@ int main(int argc, char * argv[])
if (0 != build_lba_arr(optarg, addr_arr, &addr_arr_len,
MAX_NUM_ADDR)) {
fprintf(stderr, "bad argument to '--address'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
got_addr = 1;
break;
@@ -289,7 +289,7 @@ int main(int argc, char * argv[])
eight = res;
else {
fprintf(stderr, "value for '--eight=' must be 0 or 1\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 'g':
@@ -305,7 +305,7 @@ int main(int argc, char * argv[])
longlist = res;
else {
fprintf(stderr, "value for '--longlist=' must be 0 or 1\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 'v':
@@ -317,7 +317,7 @@ int main(int argc, char * argv[])
default:
fprintf(stderr, "unrecognised switch code 0x%x ??\n", c);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (optind < argc) {
@@ -331,24 +331,24 @@ int main(int argc, char * argv[])
fprintf(stderr, "Unexpected extra argument: %s\n",
argv[optind]);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (0 == device_name[0]) {
fprintf(stderr, "missing device name!\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (grown) {
if (got_addr) {
fprintf(stderr, "can't have both '--grown' and '--address='\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if ((0 == got_addr) || (addr_arr_len < 1)) {
fprintf(stderr, "need at least one address (see '--address=')\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (got_addr) {
for (k = 0; k < addr_arr_len; ++k) {
@@ -359,7 +359,7 @@ int main(int argc, char * argv[])
} else if (0 == eight) {
fprintf(stderr, "address number %d exceeds 32 bits so "
"'--eight=0' invalid\n", k + 1);
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
}
@@ -393,8 +393,7 @@ int main(int argc, char * argv[])
if (sg_fd < 0) {
fprintf(stderr, ME "open error: %s: %s\n", device_name,
safe_strerror(-sg_fd));
- perror("");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (got_addr) {
@@ -409,17 +408,23 @@ int main(int argc, char * argv[])
}
res = sg_ll_reassign_blocks(sg_fd, eight, longlist, param_arr,
param_len, 1, verbose);
- if (SG_LIB_CAT_INVALID_OP == res) {
- fprintf(stderr, "REASSIGN BLOCKS not supported\n");
+ ret = res;
+ if (SG_LIB_CAT_NOT_READY == res) {
+ fprintf(stderr, "REASSIGN BLOCKS failed, device not ready\n");
+ goto err_out;
+ } else if (SG_LIB_CAT_UNIT_ATTENTION == res) {
+ fprintf(stderr, "REASSIGN BLOCKS, unit attention\n");
goto err_out;
} else if (SG_LIB_CAT_INVALID_OP == res) {
+ fprintf(stderr, "REASSIGN BLOCKS not supported\n");
+ goto err_out;
+ } else if (SG_LIB_CAT_ILLEGAL_REQ == res) {
fprintf(stderr, "bad field in REASSIGN BLOCKS cdb\n");
goto err_out;
} else if (0 != res) {
fprintf(stderr, "REASSIGN BLOCKS failed\n");
goto err_out;
}
- ret = 0;
} else /* if (grown) */ {
int dl_format = DEF_DEFECT_LIST_FORMAT;
int div = 0;
@@ -430,10 +435,15 @@ int main(int argc, char * argv[])
res = sg_ll_read_defect10(sg_fd, 0 /* req_plist */,
1 /* req_glist */, dl_format,
param_arr, param_len, 0, verbose);
- if (SG_LIB_CAT_INVALID_OP == res) {
- fprintf(stderr, "READ DEFECT DATA (10) not supported\n");
+ ret = res;
+ if (SG_LIB_CAT_NOT_READY == res) {
+ fprintf(stderr, "READ DEFECT DATA (10) failed, device not "
+ "ready\n");
goto err_out;
} else if (SG_LIB_CAT_INVALID_OP == res) {
+ fprintf(stderr, "READ DEFECT DATA (10) not supported\n");
+ goto err_out;
+ } else if (SG_LIB_CAT_ILLEGAL_REQ == res) {
fprintf(stderr, "bad field in READ DEFECT DATA (10) cdb\n");
goto err_out;
} else if (0 != res) {
@@ -478,8 +488,9 @@ int main(int argc, char * argv[])
err_out:
res = sg_cmds_close_device(sg_fd);
if (res < 0) {
- fprintf(stderr, ME "close error: %s\n", safe_strerror(-res));
- return 1;
+ fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ return SG_LIB_FILE_ERROR;
}
- return ret;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_requests.8 b/sg_requests.8
index 3d8b81ac..35a872ed 100644
--- a/sg_requests.8
+++ b/sg_requests.8
@@ -1,4 +1,4 @@
-.TH SG_REQUESTS "8" "March 2006" "sg3_utils-1.20" SG3_UTILS
+.TH SG_REQUESTS "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_requests \- send one or more SCSI REQUEST SENSE commands
.SH SYNOPSIS
@@ -54,6 +54,9 @@ to "no sense" while the asc/ascq code convey the information (e.g.
The REQUEST SENSE command is not marked as mandatory in SPC-3 (i.e. for
all SCSI devices) but is marked as mandatory in SBC-2 (i.e. for disks),
SSC-3 (i.e. for tapes) and MMC-4 (i.e. for CD/DVD drives).
+.SH EXIT STATUS
+The exit status of sg_requests is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHORS
Written by Douglas Gilbert.
.SH "REPORTING BUGS"
diff --git a/sg_requests.c b/sg_requests.c
index 1c5d5701..7b2f3493 100644
--- a/sg_requests.c
+++ b/sg_requests.c
@@ -44,7 +44,7 @@
* This program issues the SCSI command REQUEST SENSE to the given SCSI device.
*/
-static char * version_str = "1.11 20060315";
+static char * version_str = "1.13 20060623";
#define REQUEST_SENSE_BUFF_LEN 252
@@ -90,7 +90,7 @@ int main(int argc, char * argv[])
int do_time = 0;
int verbose = 0;
char device_name[256];
- int ret = 1;
+ int ret = 8;
struct timeval start_tm, end_tm;
memset(device_name, 0, sizeof device_name);
@@ -114,7 +114,7 @@ int main(int argc, char * argv[])
num_rs = sg_get_num(optarg);
if (num_rs < 1) {
fprintf(stderr, "bad argument to '--num'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 't':
@@ -129,7 +129,7 @@ int main(int argc, char * argv[])
default:
fprintf(stderr, "unrecognised switch code 0x%x ??\n", c);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (optind < argc) {
@@ -143,20 +143,20 @@ int main(int argc, char * argv[])
fprintf(stderr, "Unexpected extra argument: %s\n",
argv[optind]);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (0 == device_name[0]) {
fprintf(stderr, "missing device name!\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
sg_fd = sg_cmds_open_device(device_name, 1 /* ro */, verbose);
if (sg_fd < 0) {
fprintf(stderr, ME "open error: %s: %s\n", device_name,
safe_strerror(-sg_fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (do_time) {
@@ -169,24 +169,24 @@ int main(int argc, char * argv[])
memset(requestSenseBuff, 0x0, sizeof(requestSenseBuff));
res = sg_ll_request_sense(sg_fd, desc, requestSenseBuff,
sizeof(requestSenseBuff), 1, verbose);
+ ret = res;
if (0 == res) {
if (1 == num_rs) {
resp_len = requestSenseBuff[7] + 8;
- fprintf(stderr, "Decode response as sense data:\n");
+ fprintf(stderr, "Decode parameter data as sense data:\n");
sg_print_sense(NULL, requestSenseBuff, resp_len, 0);
if (verbose) {
- fprintf(stderr, "\nOutput response in hex\n");
+ fprintf(stderr, "\nParameter data in hex\n");
dStrHex((const char *)requestSenseBuff, resp_len, 1);
}
}
- ret = 0;
continue;
} else if (SG_LIB_CAT_INVALID_OP == res)
fprintf(stderr, "Request Sense command not supported\n");
else if (SG_LIB_CAT_ILLEGAL_REQ == res)
fprintf(stderr, "bad field in Request Sense cdb\n");
else {
- fprintf(stderr, "Request Sense command failed\n");
+ fprintf(stderr, "Request Sense command unexpectedly failed\n");
if (0 == verbose)
fprintf(stderr, " try the '-v' option for "
"more information\n");
@@ -216,8 +216,9 @@ int main(int argc, char * argv[])
}
res = sg_cmds_close_device(sg_fd);
if (res < 0) {
- fprintf(stderr, ME "close error: %s\n", safe_strerror(-res));
- return 1;
+ fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ return SG_LIB_FILE_ERROR;
}
- return ret;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_rmsn.8 b/sg_rmsn.8
index f2cbe3db..03129954 100644
--- a/sg_rmsn.8
+++ b/sg_rmsn.8
@@ -1,4 +1,4 @@
-.TH SG_RMSN "8" "May 2005" "sg3_utils-1.15" SG3_UTILS
+.TH SG_RMSN "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_rmsn \- read media serial number
.SH SYNOPSIS
@@ -40,12 +40,15 @@ number" page (VPD page 0x80).
The MMC-4 command set for CDs and DVDs has a "media serial number"
feature (0x109) [and a "logical unit serial number" feature]. These
can be viewed with sg_get_config.
+.SH EXIT STATUS
+The exit status of sg_rmsn is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHORS
Written by Douglas Gilbert.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2005 Douglas Gilbert
+Copyright \(co 2005-2006 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.
diff --git a/sg_rmsn.c b/sg_rmsn.c
index 37f7fc95..20f38f7c 100644
--- a/sg_rmsn.c
+++ b/sg_rmsn.c
@@ -44,7 +44,7 @@
* to the given SCSI device.
*/
-static char * version_str = "1.01 20060106";
+static char * version_str = "1.02 20060623";
#define ME "sg_rmsn: "
@@ -80,7 +80,7 @@ int main(int argc, char * argv[])
int raw = 0;
int verbose = 0;
char device_name[512];
- int ret = 1;
+ int ret = 0;
memset(device_name, 0, sizeof device_name);
while (1) {
@@ -108,7 +108,7 @@ int main(int argc, char * argv[])
default:
fprintf(stderr, "unrecognised switch code 0x%x ??\n", c);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (optind < argc) {
@@ -122,26 +122,27 @@ int main(int argc, char * argv[])
fprintf(stderr, "Unexpected extra argument: %s\n",
argv[optind]);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (0 == device_name[0]) {
fprintf(stderr, "missing device name!\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
if (sg_fd < 0) {
fprintf(stderr, ME "open error: %s: %s\n", device_name,
safe_strerror(-sg_fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
memset(rmsn_buff, 0x0, sizeof(rmsn_buff));
res = sg_ll_read_media_serial_num(sg_fd, rmsn_buff, sizeof(rmsn_buff),
1, verbose);
+ ret = res;
if (0 == res) {
sn_len = (rmsn_buff[0] << 24) + (rmsn_buff[1] << 16) +
(rmsn_buff[2] << 8) + rmsn_buff[3];
@@ -175,13 +176,18 @@ int main(int argc, char * argv[])
if (sn_len > 0)
dStrHex((const char *)ucp + 4, sn_len, 0);
}
- ret = 0;
}
}
if (0 != res) {
if (SG_LIB_CAT_INVALID_OP == res)
fprintf(stderr, "Read Media Serial Number command not "
"supported\n");
+ else if (SG_LIB_CAT_NOT_READY == res)
+ fprintf(stderr, "Read Media Serial Number failed, device not "
+ "ready\n");
+ else if (SG_LIB_CAT_UNIT_ATTENTION == res)
+ fprintf(stderr, "Read Media Serial Number failed, unit "
+ "attention\n");
else if (SG_LIB_CAT_ILLEGAL_REQ == res)
fprintf(stderr, "bad field in Read Media Serial Number cdb\n");
else {
@@ -196,8 +202,9 @@ err_out:
free(ucp);
res = sg_cmds_close_device(sg_fd);
if (res < 0) {
- fprintf(stderr, ME "close error: %s\n", safe_strerror(-res));
- return 1;
+ fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ return SG_LIB_FILE_ERROR;
}
- return ret;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_rtpg.8 b/sg_rtpg.8
index 20a77f1f..165960af 100644
--- a/sg_rtpg.8
+++ b/sg_rtpg.8
@@ -1,4 +1,4 @@
-.TH SG_RTPG "8" "January 2005" "sg3_utils-1.12" SG3_UTILS
+.TH SG_RTPG "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_rtpg \- report target port groups
.SH SYNOPSIS
@@ -35,12 +35,15 @@ print the version string and then exit.
The Report Target Port Groups command should be supported whenever the TPGS
bits in a standard INQUIRY response are greater than zero. [View with
sg_inq utility.]
+.SH EXIT STATUS
+The exit status of sg_rtpg is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHORS
Written by Douglas Gilbert.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2004-2005 Christophe Varoqui and Douglas Gilbert
+Copyright \(co 2004-2006 Christophe Varoqui 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.
diff --git a/sg_rtpg.c b/sg_rtpg.c
index 275a9791..48c3191c 100644
--- a/sg_rtpg.c
+++ b/sg_rtpg.c
@@ -44,7 +44,7 @@
* to the given SCSI device.
*/
-static char * version_str = "1.06 20060106";
+static char * version_str = "1.07 20060623";
#define REPORT_TGT_GRP_BUFF_LEN 1024
@@ -156,7 +156,7 @@ int main(int argc, char * argv[])
int hex = 0;
int verbose = 0;
char device_name[256];
- int ret = 1;
+ int ret = 0;
memset(device_name, 0, sizeof device_name);
while (1) {
@@ -187,7 +187,7 @@ int main(int argc, char * argv[])
default:
fprintf(stderr, "unrecognised switch code 0x%x ??\n", c);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (optind < argc) {
@@ -201,20 +201,20 @@ int main(int argc, char * argv[])
fprintf(stderr, "Unexpected extra argument: %s\n",
argv[optind]);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (0 == device_name[0]) {
fprintf(stderr, "missing device name!\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
if (sg_fd < 0) {
fprintf(stderr, ME "open error: %s: %s\n", device_name,
safe_strerror(-sg_fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
memset(reportTgtGrpBuff, 0x0, sizeof(reportTgtGrpBuff));
@@ -227,6 +227,7 @@ int main(int argc, char * argv[])
memcpy(reportTgtGrpBuff, dummy_resp, sizeof(dummy_resp));
res = 0;
#endif
+ ret = res;
if (0 == res) {
report_len = (reportTgtGrpBuff[0] << 24) +
(reportTgtGrpBuff[1] << 16) +
@@ -242,7 +243,6 @@ int main(int argc, char * argv[])
fprintf(stderr, "\nOutput response in hex\n");
dStrHex((const char *)reportTgtGrpBuff,
(trunc ? (int)sizeof(reportTgtGrpBuff) : report_len), 1);
- ret = 0;
goto err_out;
}
printf("Report target port groups:\n");
@@ -284,11 +284,12 @@ int main(int argc, char * argv[])
}
off = 8 + j;
}
- ret = 0;
} else if (SG_LIB_CAT_INVALID_OP == res)
fprintf(stderr, "Report Target Port Groups command not supported\n");
else if (SG_LIB_CAT_ILLEGAL_REQ == res)
fprintf(stderr, "bad field in Report Target Port Groups cdb\n");
+ else if (SG_LIB_CAT_UNIT_ATTENTION == res)
+ fprintf(stderr, "Report Target Port Groups, unit attention\n");
else {
fprintf(stderr, "Report Target Port Groups command failed\n");
if (0 == verbose)
@@ -298,8 +299,9 @@ int main(int argc, char * argv[])
err_out:
res = sg_cmds_close_device(sg_fd);
if (res < 0) {
- fprintf(stderr, ME "close error: %s\n", safe_strerror(-res));
- return 1;
+ fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ return SG_LIB_FILE_ERROR;
}
- return ret;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_scan.8 b/sg_scan.8
index 61e085ed..126ca972 100644
--- a/sg_scan.8
+++ b/sg_scan.8
@@ -1,4 +1,4 @@
-.TH SG_SCAN "8" "March 2006" "sg3_utils-1.20" SG3_UTILS
+.TH SG_SCAN "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_scan \- does a scan of sg devices (or given SCSI/ATAPI/ATA devices) and
prints the results
@@ -58,6 +58,9 @@ adapter has been removed) and still all active sg device nodes will
be listed. This utility assumes that sg device nodes are named using
the normal conventions and searches from /dev/sg0 to /dev/sg4095
inclusive.
+.SH EXIT STATUS
+The exit status of sg_scan is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHORS
Written by D. Gilbert and F. Jansen
.SH COPYRIGHT
diff --git a/sg_scan.c b/sg_scan.c
index 2110b2b6..16e51866 100644
--- a/sg_scan.c
+++ b/sg_scan.c
@@ -42,7 +42,7 @@
F. Jansen - modification to extend beyond 26 sg devices.
*/
-static char * version_str = "4.07 20060324";
+static char * version_str = "4.08 20060623";
#define ME "sg_scan: "
@@ -189,7 +189,7 @@ int main(int argc, char * argv[])
memset(gen_index_arr, 0, max_file_args * sizeof(int));
else {
printf(ME "Out of memory\n");
- return 1;
+ return SG_LIB_CAT_OTHER;
}
for (k = 1, j = 0; k < argc; ++k) {
@@ -208,7 +208,7 @@ int main(int argc, char * argv[])
printf("Scan sg device names and optionally do an "
"INQUIRY\n\n");
usage();
- return 1;
+ return 0;
case 'i':
do_inquiry = 1;
break;
@@ -239,7 +239,7 @@ int main(int argc, char * argv[])
if (jmp_out) {
fprintf(stderr, "Unrecognized option: %s\n", cp);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else {
if (j < max_file_args) {
@@ -247,7 +247,7 @@ int main(int argc, char * argv[])
gen_index_arr[j++] = k;
} else {
printf("Too many command line arguments\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
}
@@ -264,7 +264,7 @@ int main(int argc, char * argv[])
if (res < 0) {
snprintf(ebuff, EBUFF_SZ, ME "Error closing %s ", fname);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (has_file_args) {
if (gen_index_arr[j])
diff --git a/sg_senddiag.8 b/sg_senddiag.8
index 23ec8a30..009dad66 100644
--- a/sg_senddiag.8
+++ b/sg_senddiag.8
@@ -1,4 +1,4 @@
-.TH SG_SENDDIAG "8" "April 2006" "sg3_utils-1.20" SG3_UTILS
+.TH SG_SENDDIAG "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_senddiag \- performs a SCSI SEND DIAGNOSTIC command
.SH SYNOPSIS
@@ -26,7 +26,7 @@ utility, are listed.
set the Device Offline (DevOffL) bit (default is clear). Only significant
when '-t' option is set for the default self test. When set other operations
on any logical units controlled by the this device server (target) may be
-effected (delayed) while a default self test is underway.
+affected (delayed) while a default self test is underway.
.TP
-e
outputs the expected extended self test duration. The duration
@@ -82,7 +82,7 @@ Both the '-doff' and/or '-uoff' options can be used with this option.
-uoff
set the Unit Offline (UnitOffL) bit (default is clear). Only significant
when '-t' option is set for the default self test. When set other operations
-on this logical unit may be effected (delayed) while a default self test
+on this logical unit may be affected (delayed) while a default self test
is underway. Some devices (e.g. Fujitsu disks) do more tests when this
bit is set.
.TP
@@ -140,6 +140,9 @@ will work in the 2.6 series kernels.
To access SCSI enclosures see the sg_ses utility. sg_ses uses the
SEND DIAGNOSTIC and RECEIVE DIAGNOSTIC RESULTS SCSI commands as outlined
in the SES-2 (draft) standard.
+.SH EXIT STATUS
+The exit status of sg_senddiag is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHOR
Written by Doug Gilbert
.SH "REPORTING BUGS"
diff --git a/sg_senddiag.c b/sg_senddiag.c
index 33fa23f5..2eff490c 100644
--- a/sg_senddiag.c
+++ b/sg_senddiag.c
@@ -19,16 +19,14 @@
the SCSI RECEIVE DIAGNOSTIC command to list supported diagnostic pages.
*/
-static char * version_str = "0.28 20060314";
+static char * version_str = "0.29 20060623";
#define ME "sg_senddiag: "
#define MX_ALLOC_LEN (1024 * 4)
-/* Return of 0 -> success, SG_LIB_CAT_INVALID_OP -> Send diagnostic not
- * supported, SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other
- * failure */
+/* Return of 0 -> success, otherwise see sg_ll_send_diag() */
static int do_senddiag(int sg_fd, int sf_code, int pf_bit, int sf_bit,
int devofl_bit, int unitofl_bit, void * outgoing_pg,
int outgoing_len, int noisy, int verbose)
@@ -60,6 +58,12 @@ static int do_modes_0a(int sg_fd, void * resp, int mx_resp_len, int noisy,
else if (SG_LIB_CAT_ILLEGAL_REQ == res)
fprintf(stderr, "bad field in Mode sense (%s) command\n",
(mode6 ? "6" : "10"));
+ else if (SG_LIB_CAT_NOT_READY == res)
+ fprintf(stderr, "Mode sense (%s) failed, device not ready\n",
+ (mode6 ? "6" : "10"));
+ else if (SG_LIB_CAT_UNIT_ATTENTION == res)
+ fprintf(stderr, "Mode sense (%s) failed, unit attention\n",
+ (mode6 ? "6" : "10"));
return res;
}
@@ -265,7 +269,7 @@ static void usage()
int main(int argc, char * argv[])
{
- int sg_fd, k, num, rsp_len, plen, jmp_out;
+ int sg_fd, k, num, rsp_len, plen, jmp_out, res;
const char * file_name = 0;
unsigned char rsp_buff[MX_ALLOC_LEN];
int rsp_buff_size = MX_ALLOC_LEN;
@@ -283,7 +287,7 @@ int main(int argc, char * argv[])
int read_in_len = 0;
const char * cp;
unsigned char read_in[MX_ALLOC_LEN];
- int ret = 1;
+ int ret = 0;
for (k = 1; k < argc; ++k) {
cp = argv[k];
@@ -338,7 +342,7 @@ int main(int argc, char * argv[])
exit(0);
case '?':
usage();
- return 1;
+ return 0;
default:
jmp_out = 1;
break;
@@ -353,7 +357,7 @@ int main(int argc, char * argv[])
sizeof(read_in))) {
printf("Bad sequence after 'raw=' option\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
do_raw = 1;
} else if (0 == strncmp("s=", cp, 2)) {
@@ -361,13 +365,13 @@ int main(int argc, char * argv[])
if ((1 != num) || (u > 7)) {
printf("Bad page code after 's=' option\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
self_test_code = u;
} else if (jmp_out) {
fprintf(stderr, "Unrecognized option: %s\n", cp);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == file_name)
file_name = cp;
@@ -375,26 +379,26 @@ int main(int argc, char * argv[])
fprintf(stderr, "too many arguments, got: %s, not expecting: "
"%s\n", file_name, cp);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if ((do_doff || do_uoff) && (! do_def_test)) {
printf("setting -doff or -uoff only useful when -t is set\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if ((self_test_code > 0) && do_def_test) {
printf("either set -s=<num> or -t (not both)\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (do_raw) {
if ((self_test_code > 0) || do_def_test || do_ext_time || do_list) {
printf("'--raw=' cannot be used with self tests, '-e' or "
"'-l'\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (! do_pf)
printf(">>> warning, '-pf' probably should be used with "
@@ -407,16 +411,17 @@ int main(int argc, char * argv[])
}
fprintf(stderr, "No <scsi_device> argument given\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if ((sg_fd = sg_cmds_open_device(file_name, 0 /* rw */, verbose)) < 0) {
fprintf(stderr, ME "error opening file: %s: %s\n", file_name,
safe_strerror(-sg_fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (do_ext_time) {
- if (0 == do_modes_0a(sg_fd, rsp_buff, 32, 1, 0, verbose)) {
+ res = do_modes_0a(sg_fd, rsp_buff, 32, 1, 0, verbose);
+ if (0 == res) {
/* Assume mode sense(10) response without block descriptors */
num = (rsp_buff[0] << 8) + rsp_buff[1] - 6;
if (num >= 0xc) {
@@ -428,13 +433,15 @@ int main(int argc, char * argv[])
} else
printf("Extended self-test duration not available\n");
} else {
+ ret = res;
printf("Extended self-test duration (mode page 0xa) failed\n");
goto err_out9;
}
} else if (do_list) {
memset(rsp_buff, 0, sizeof(rsp_buff));
- if (0 == do_senddiag(sg_fd, 0, 1 /* pf */, 0, 0, 0, rsp_buff, 4, 1,
- verbose)) {
+ res = do_senddiag(sg_fd, 0, 1 /* pf */, 0, 0, 0, rsp_buff, 4, 1,
+ verbose);
+ if (0 == res) {
if (0 == sg_ll_receive_diag(sg_fd, 0, 0, rsp_buff,
rsp_buff_size, 1, verbose)) {
printf("Supported diagnostic pages response:\n");
@@ -449,31 +456,53 @@ int main(int argc, char * argv[])
}
}
} else {
+ ret = res;
fprintf(stderr, "RECEIVE DIAGNOSTIC command failed\n");
goto err_out9;
}
- } else
+ } else {
+ ret = res;
goto err_out;
+ }
} else if (do_raw) {
- if (do_senddiag(sg_fd, 0, do_pf, 0, 0, 0, read_in, read_in_len, 1,
- verbose))
+ res = do_senddiag(sg_fd, 0, do_pf, 0, 0, 0, read_in, read_in_len, 1,
+ verbose);
+ if (res) {
+ ret = res;
goto err_out;
- } else if (0 == do_senddiag(sg_fd, self_test_code, do_pf, do_def_test,
- do_doff, do_uoff, NULL, 0, 1, verbose)) {
- if ((5 == self_test_code) || (6 == self_test_code))
- printf("Foreground self test returned GOOD status\n");
- else if (do_def_test && (! do_doff) && (! do_uoff))
- printf("Default self test returned GOOD status\n");
- } else
- goto err_out;
- sg_cmds_close_device(sg_fd);
- return 0;
+ }
+ } else {
+ res = do_senddiag(sg_fd, self_test_code, do_pf, do_def_test,
+ do_doff, do_uoff, NULL, 0, 1, verbose);
+ if (0 == res) {
+ if ((5 == self_test_code) || (6 == self_test_code))
+ printf("Foreground self test returned GOOD status\n");
+ else if (do_def_test && (! do_doff) && (! do_uoff))
+ printf("Default self test returned GOOD status\n");
+ } else {
+ ret = res;
+ goto err_out;
+ }
+ }
+ res = sg_cmds_close_device(sg_fd);
+ if ((res < 0) && (0 == ret))
+ return SG_LIB_SYNTAX_ERROR;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
err_out:
fprintf(stderr, "SEND DIAGNOSTIC command failed\n");
+ if (SG_LIB_CAT_UNIT_ATTENTION == res)
+ fprintf(stderr, "SEND DIAGNOSTIC RESULTS, unit attention\n");
+ else if (SG_LIB_CAT_NOT_READY == res)
+ fprintf(stderr, "SEND DIAGNOSTIC RESULTS, device not "
+ "ready\n");
+ else
+ fprintf(stderr, "SEND DIAGNOSTIC RESULTS, failed\n");
err_out9:
if (verbose < 2)
fprintf(stderr, " try again with '-vv' for more information\n");
- sg_cmds_close_device(sg_fd);
- return ret;
+ res = sg_cmds_close_device(sg_fd);
+ if ((res < 0) && (0 == ret))
+ return SG_LIB_FILE_ERROR;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_ses.8 b/sg_ses.8
index ffaa0480..56105d72 100644
--- a/sg_ses.8
+++ b/sg_ses.8
@@ -1,4 +1,4 @@
-.TH SG_SES "8" "March 2006" "sg3_utils-1.20" SG3_UTILS
+.TH SG_SES "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_ses \- send controls and fetch status from a SCSI Enclosure
Services (SES) device
@@ -41,7 +41,7 @@ page to be specified. The string given should not include the first 4
bytes (i.e. page code and length). See next entry for using stdin.
.TP
--data=- | -d -
-reads a data string from stdin. Space, tabs and line feeds additionally
+reads a data string from stdin. Spaces, tabs and line feeds additionally
are permitted as separators.
.TP
--filter | -f
@@ -133,6 +133,9 @@ check try:
sg_ses --page=5 /dev/sda
.PP
again.
+.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 Douglas Gilbert.
.SH "REPORTING BUGS"
diff --git a/sg_ses.c b/sg_ses.c
index 03a317e8..e148e939 100644
--- a/sg_ses.c
+++ b/sg_ses.c
@@ -43,7 +43,7 @@
*commands tailored for SES (enclosure) devices.
*/
-static char * version_str = "1.24 20060329"; /* ses2r14 */
+static char * version_str = "1.27 20060623"; /* ses2r15 */
#define MX_ALLOC_LEN 4096
#define MX_ELEM_HDR 512
@@ -105,8 +105,8 @@ static void usage()
}
/* Return of 0 -> success, SG_LIB_CAT_INVALID_OP -> Send diagnostic not
- * supported, SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other
- * failure */
+ * supported, SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
+ * SG_LIB_CAT_NOT_READY, SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failures */
static int do_senddiag(int sg_fd, int pf_bit, void * outgoing_pg,
int outgoing_len, int noisy, int verbose)
{
@@ -320,20 +320,23 @@ truncated:
return;
}
+/* Returns number of elements written to 'ehp' or -1 if there is
+ a problem */
static int populate_element_hdr_arr(int fd, struct element_hdr * ehp,
unsigned int * generationp, int verbose)
{
- int resp_len, k, el, num_subs, sum_elem_types;
+ int resp_len, k, el, num_subs, sum_elem_types, res;
unsigned int gen_code;
unsigned char resp[MX_ALLOC_LEN];
int rsp_buff_size = MX_ALLOC_LEN;
const unsigned char * ucp;
const unsigned char * last_ucp;
- if (0 == sg_ll_receive_diag(fd, 1, 1, resp, rsp_buff_size, 1, verbose)) {
+ res = sg_ll_receive_diag(fd, 1, 1, resp, rsp_buff_size, 1, verbose);
+ if (0 == res) {
resp_len = (resp[2] << 8) + resp[3] + 4;
if (resp_len > rsp_buff_size) {
- fprintf(stderr, "<<< warning response buffer too small "
+ fprintf(stderr, "<<< warning: response buffer too small "
"[%d but need %d]>>>\n", rsp_buff_size, resp_len);
resp_len = rsp_buff_size;
}
@@ -383,7 +386,7 @@ static int populate_element_hdr_arr(int fd, struct element_hdr * ehp,
}
return sum_elem_types;
} else {
- fprintf(stderr, "populate: couldn't read config page\n");
+ fprintf(stderr, "populate: couldn't read config page, res=%d\n", res);
return -1;
}
p_truncated:
@@ -406,6 +409,9 @@ static char * find_sas_connector_type(int conn_type, char * buff,
snprintf(buff, buff_len, "Mini SAS 4x receptacle (SFF-8088) "
"[max 4 phys]");
break;
+ case 0xf:
+ snprintf(buff, buff_len, "Vendor specific external connector");
+ break;
case 0x10:
snprintf(buff, buff_len, "SAS 4i plug (SFF-8484) [max 4 phys]");
break;
@@ -426,6 +432,9 @@ static char * find_sas_connector_type(int conn_type, char * buff,
case 0x23:
snprintf(buff, buff_len, "SATA device plug [max 1 phy]");
break;
+ case 0x3f:
+ snprintf(buff, buff_len, "Vendor specific internal connector");
+ break;
default:
if (conn_type < 0x10)
snprintf(buff, buff_len, "unknown external connector type: 0x%x",
@@ -529,7 +538,7 @@ static void print_element_status(const char * pad,
!!(statp[1] & 0x80), !!(statp[3] & 0x40),
!!(statp[3] & 0x20), !!(statp[3] & 0x10));
printf("%sActual speed=%d rpm, Fan %s\n", pad,
- (((0x7 & statp[1]) << 8) + statp[2]) * 10, /* 05-372r0 */
+ (((0x7 & statp[1]) << 8) + statp[2]) * 10,
actual_speed_desc[7 & statp[3]]);
break;
case 4: /* temperature sensor */
@@ -615,10 +624,10 @@ static void print_element_status(const char * pad,
!!(statp[2] & 0x1), !!(statp[3] & 0x80), !!(statp[3] & 0x2),
!!(statp[3] & 0x1));
break;
- case 0xc: /* Display (ses2r14 + 05-011r1) */
+ case 0xc: /* Display (ses2r15) */
if ((! filter) || (0x80 & statp[1]))
printf("%sIdent=%d, Display mode status=%d, Display "
- "character=0x%x\n", pad,
+ "character status=0x%x\n", pad,
!!(statp[1] & 0x80), (statp[1] & 0x3),
((statp[2] << 8) & statp[3]));
break;
@@ -1397,9 +1406,9 @@ static int read_hex(const char * inp, unsigned char * arr, int * arr_len)
return 0;
}
-static void ses_process_status(int sg_fd, int page_code, int do_raw,
- int do_hex, int inner_hex, int filter,
- int verbose)
+static int ses_process_status(int sg_fd, int page_code, int do_raw,
+ int do_hex, int inner_hex, int filter,
+ int verbose)
{
int rsp_len, res;
unsigned int ref_gen_code;
@@ -1409,8 +1418,9 @@ static void ses_process_status(int sg_fd, int page_code, int do_raw,
memset(rsp_buff, 0, rsp_buff_size);
cp = find_in_page_code_desc(page_code);
- if (0 == sg_ll_receive_diag(sg_fd, 1, page_code, rsp_buff,
- rsp_buff_size, 1, verbose)) {
+ res = sg_ll_receive_diag(sg_fd, 1, page_code, rsp_buff,
+ rsp_buff_size, 1, verbose);
+ if (0 == res) {
rsp_len = (rsp_buff[2] << 8) + rsp_buff[3] + 4;
if (rsp_len > rsp_buff_size) {
fprintf(stderr, "<<< warning response buffer too small "
@@ -1534,6 +1544,7 @@ static void ses_process_status(int sg_fd, int page_code, int do_raw,
printf("Attempt to fetch status diagnostic page [0x%x] "
"failed\n", page_code);
}
+ return res;
}
@@ -1556,7 +1567,7 @@ int main(int argc, char * argv[])
unsigned char data_arr[1024];
int arr_len = 0;
int pd_type = 0;
- int ret = 1;
+ int ret = 0;
struct sg_simple_inquiry_resp inq_resp;
const char * cp;
@@ -1575,7 +1586,7 @@ int main(int argc, char * argv[])
if ((byte1 < 0) || (byte1 > 255)) {
fprintf(stderr, "bad argument to '--byte1' (0 to 255 "
"inclusive)\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 'c':
@@ -1585,7 +1596,7 @@ int main(int argc, char * argv[])
memset(data_arr, 0, sizeof(data_arr));
if (read_hex(optarg, data_arr + 4, &arr_len)) {
fprintf(stderr, "bad argument to '--data'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
do_data = 1;
break;
@@ -1610,7 +1621,7 @@ int main(int argc, char * argv[])
if ((page_code < 0) || (page_code > 255)) {
fprintf(stderr, "bad argument to '--page' (0 to 255 "
"inclusive)\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 'r':
@@ -1628,7 +1639,7 @@ int main(int argc, char * argv[])
default:
fprintf(stderr, "unrecognised switch code 0x%x ??\n", c);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (optind < argc) {
@@ -1642,7 +1653,7 @@ int main(int argc, char * argv[])
fprintf(stderr, "Unexpected extra argument: %s\n",
argv[optind]);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (do_list) {
@@ -1665,12 +1676,12 @@ int main(int argc, char * argv[])
if (do_control && do_status) {
fprintf(stderr, "cannot have both '--control' and '--status'\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
} else if (1 == do_control) {
if (! do_data) {
fprintf(stderr, "need to give '--data' in control mode\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == do_status)
do_status = 1; /* default to receiving status pages */
@@ -1678,19 +1689,20 @@ int main(int argc, char * argv[])
if (0 == device_name[0]) {
fprintf(stderr, "missing device name!\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
if (sg_fd < 0) {
fprintf(stderr, ME "open error: %s: %s\n", device_name,
safe_strerror(-sg_fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (! do_raw) {
if (sg_simple_inquiry(sg_fd, &inq_resp, 1, verbose)) {
fprintf(stderr, ME "%s doesn't respond to a SCSI INQUIRY\n",
device_name);
+ ret = SG_LIB_CAT_OTHER;
goto err_out;
} else {
printf(" %.8s %.16s %.4s\n", inq_resp.vendor,
@@ -1705,10 +1717,10 @@ int main(int argc, char * argv[])
printf(" %s device (not an enclosure)\n", cp);
}
}
- if (do_status)
- ses_process_status(sg_fd, page_code, do_raw, do_hex, inner_hex,
- do_filter, verbose);
- else { /* control page requested */
+ if (do_status) {
+ ret = ses_process_status(sg_fd, page_code, do_raw, do_hex,
+ inner_hex, do_filter, verbose);
+ } else { /* control page requested */
data_arr[0] = page_code;
data_arr[1] = byte1;
data_arr[2] = (arr_len >> 8) & 0xff;
@@ -1717,7 +1729,8 @@ int main(int argc, char * argv[])
case 0x2: /* Enclosure control diagnostic page */
printf("Sending Enclosure control [0x%x] page, with page "
"length=%d bytes\n", page_code, arr_len);
- if (do_senddiag(sg_fd, 1, data_arr, arr_len + 4, 1, verbose)) {
+ ret = do_senddiag(sg_fd, 1, data_arr, arr_len + 4, 1, verbose);
+ if (ret) {
fprintf(stderr, "couldn't send Enclosure control page\n");
goto err_out;
}
@@ -1725,7 +1738,8 @@ int main(int argc, char * argv[])
case 0x4: /* String Out diagnostic page */
printf("Sending String Out [0x%x] page, with page length=%d "
"bytes\n", page_code, arr_len);
- if (do_senddiag(sg_fd, 1, data_arr, arr_len + 4, 1, verbose)) {
+ ret = do_senddiag(sg_fd, 1, data_arr, arr_len + 4, 1, verbose);
+ if (ret) {
fprintf(stderr, "couldn't send String Out page\n");
goto err_out;
}
@@ -1733,7 +1747,8 @@ int main(int argc, char * argv[])
case 0x5: /* Threshold Out diagnostic page */
printf("Sending Threshold Out [0x%x] page, with page length=%d "
"bytes\n", page_code, arr_len);
- if (do_senddiag(sg_fd, 1, data_arr, arr_len + 4, 1, verbose)) {
+ ret = do_senddiag(sg_fd, 1, data_arr, arr_len + 4, 1, verbose);
+ if (ret) {
fprintf(stderr, "couldn't send Threshold Out page\n");
goto err_out;
}
@@ -1741,7 +1756,8 @@ int main(int argc, char * argv[])
case 0x6: /* Array control diagnostic page (obsolete) */
printf("Sending Array control [0x%x] page, with page "
"length=%d bytes\n", page_code, arr_len);
- if (do_senddiag(sg_fd, 1, data_arr, arr_len + 4, 1, verbose)) {
+ ret = do_senddiag(sg_fd, 1, data_arr, arr_len + 4, 1, verbose);
+ if (ret) {
fprintf(stderr, "couldn't send Array control page\n");
goto err_out;
}
@@ -1749,7 +1765,8 @@ int main(int argc, char * argv[])
case 0xc: /* Subenclosure String Out diagnostic page */
printf("Sending Subenclosure String Out [0x%x] page, with page "
"length=%d bytes\n", page_code, arr_len);
- if (do_senddiag(sg_fd, 1, data_arr, arr_len + 4, 1, verbose)) {
+ ret = do_senddiag(sg_fd, 1, data_arr, arr_len + 4, 1, verbose);
+ if (ret) {
fprintf(stderr, "couldn't send Subenclosure String Out "
"page\n");
goto err_out;
@@ -1758,16 +1775,17 @@ int main(int argc, char * argv[])
default:
fprintf(stderr, "Setting SES control page 0x%x not supported "
"yet\n", page_code);
+ ret = SG_LIB_SYNTAX_ERROR;
break;
}
}
- ret = 0;
err_out:
res = sg_cmds_close_device(sg_fd);
if (res < 0) {
- fprintf(stderr, ME "close error: %s\n", safe_strerror(-res));
- return 2;
+ fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ return SG_LIB_FILE_ERROR;
}
- return ret;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_start.8 b/sg_start.8
index 27b55cf6..3c244715 100644
--- a/sg_start.8
+++ b/sg_start.8
@@ -1,4 +1,4 @@
-.TH SG_START "8" "April 2006" "sg3_utils-1.20" SG3_UTILS
+.TH SG_START "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_start \- starts (spins-up) or stops (spins down) device, load or
eject medium
@@ -101,9 +101,6 @@ are equivalent. The single "-" form is for backward compatibility.
To avoid confusion, only one of "0", "1", "--load", "--eject", "--start"
and "--stop" can be given.
.PP
-If this utility succeeds then its process exits with a status of 0;
-otherwise it exits with a non-zero status.
-.PP
There is an associated "power condition" mode page (0x1a) in which timer
values can be set for transitioning to either idle or standby state after
a period of inactivity. The sdparm utility can be used to view the
@@ -129,6 +126,9 @@ Earlier versions had a '-s' option to perform a SYNCHRONIZE CACHE command
before the START STOP UNIT command was issued. According to recent SBC-2
drafts this is done implicitly if required. Hence the "-s" option has been
dropped.
+.SH EXIT STATUS
+The exit status of sg_start is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHOR
Written by K. Garloff and D. Gilbert
.SH "REPORTING BUGS"
diff --git a/sg_start.c b/sg_start.c
index 73825995..28227224 100644
--- a/sg_start.c
+++ b/sg_start.c
@@ -26,7 +26,7 @@
*/
-static char * version_str = "0.49 20060406";
+static char * version_str = "0.50 20060623";
void usage ()
@@ -78,6 +78,7 @@ int main(int argc, char * argv[])
int fl_num = -1;
int power_conds = 0;
int verbose = 0;
+ int ret = 0;
for (k = 1; k < argc; ++k) {
cp = argv[k];
@@ -103,7 +104,7 @@ int main(int argc, char * argv[])
exit(0);
case '?':
usage();
- return 1;
+ return 0;
case '-':
++cp;
--plen;
@@ -131,7 +132,7 @@ int main(int argc, char * argv[])
fprintf(stderr, "Bad value after "
"'fl=' option\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
fl_num = u;
} else if (0 == strncmp("imm=", cp, 4)) {
@@ -140,7 +141,7 @@ int main(int argc, char * argv[])
fprintf(stderr, "Bad value after "
"'imm=' option\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
immed = u;
} else if (0 == strncmp(cp, "load", 4)) {
@@ -157,7 +158,7 @@ int main(int argc, char * argv[])
fprintf(stderr, "Bad value after "
"after 'pc=' option\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
power_conds = u;
} else if (0 == strncmp(cp, "start", 5)) {
@@ -174,7 +175,7 @@ int main(int argc, char * argv[])
fprintf(stderr, "Unrecognized option: %s\n",
cp);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strcmp("0", cp)) {
if (startstop >= 0)
@@ -192,20 +193,20 @@ int main(int argc, char * argv[])
fprintf(stderr, "too many arguments, got: %s, not "
"expecting: %s\n", file_name, cp);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (ambigu) {
fprintf(stderr, "please, only one of 0, 1, --eject, "
"--load, --start or --stop\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (0 == file_name) {
fprintf(stderr, "No <scsi_device> argument given\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (fl_num >= 0) {
@@ -213,13 +214,13 @@ int main(int argc, char * argv[])
fprintf(stderr, "Giving '--fl=<n>' and '--stop' (or "
"'--eject') is invalid\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (power_conds > 0) {
fprintf(stderr, "Giving '--fl=<n>' and '--pc=<n>' "
"when <n> is non-zero is invalid\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else {
if ((startstop == -1) && loej)
@@ -232,7 +233,7 @@ int main(int argc, char * argv[])
if (fd < 0) {
fprintf(stderr, "Error trying to open %s: %s\n",
file_name, safe_strerror(-fd));
- return 2;
+ return SG_LIB_FILE_ERROR;
}
res = 0;
@@ -247,15 +248,22 @@ int main(int argc, char * argv[])
else if (startstop != -1)
res = sg_ll_start_stop_unit(fd, immed, 0, 0, 0, loej,
startstop, 1, verbose);
+ ret = res;
if (res) {
if (verbose < 2) {
if (SG_LIB_CAT_INVALID_OP == res)
fprintf(stderr, "command not supported\n");
+ else if (SG_LIB_CAT_NOT_READY == res)
+ fprintf(stderr, "device not ready\n");
+ else if (SG_LIB_CAT_UNIT_ATTENTION == res)
+ fprintf(stderr, "unit attention\n");
else if (SG_LIB_CAT_ILLEGAL_REQ == res)
fprintf(stderr, "command malformed\n");
}
fprintf(stderr, "START STOP UNIT command failed\n");
}
- sg_cmds_close_device(fd);
- return res ? 1 : 0;
+ res = sg_cmds_close_device(fd);
+ if ((res < 0) && (0 == ret))
+ return SG_LIB_FILE_ERROR;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_sync.8 b/sg_sync.8
index 069981d6..b0e40f76 100644
--- a/sg_sync.8
+++ b/sg_sync.8
@@ -1,4 +1,4 @@
-.TH SG_SYNC "8" "December 2005" "sg3_utils-1.19" SG3_UTILS
+.TH SG_SYNC "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_sync \- send the scsi command synchronize cache
.SH SYNOPSIS
@@ -72,12 +72,15 @@ suffixes can only be used for --count and --lba.
Alternatively numerical values can be given in hexadecimal preceded by
either "0x" or "0X" (or with a trailing "h" or "H"). When hex numbers are
given, multipliers cannot be used.
+.SH EXIT STATUS
+The exit status of sg_sync is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHORS
Written by Douglas Gilbert.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2004-2005 Douglas Gilbert
+Copyright \(co 2004-2006 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.
diff --git a/sg_sync.c b/sg_sync.c
index 0e7b93d9..7343170c 100644
--- a/sg_sync.c
+++ b/sg_sync.c
@@ -45,7 +45,7 @@
* (e.g. disks)
*/
-static char * version_str = "1.04 20060125";
+static char * version_str = "1.05 20060623";
#define ME "sg_sync: "
@@ -101,7 +101,7 @@ int main(int argc, char * argv[])
int sync_nv = 0;
int verbose = 0;
char device_name[256];
- int ret = 1;
+ int ret = 0;
memset(device_name, 0, sizeof device_name);
while (1) {
@@ -117,14 +117,14 @@ int main(int argc, char * argv[])
count = sg_get_llnum(optarg);
if (count < 0) {
fprintf(stderr, "bad argument to '--count'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 'g':
group = sg_get_num(optarg);
if ((group < 0) || (group > 31)) {
fprintf(stderr, "bad argument to '--group'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 'h':
@@ -138,7 +138,7 @@ int main(int argc, char * argv[])
lba = sg_get_llnum(optarg);
if (lba < 0) {
fprintf(stderr, "bad argument to '--lba'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 's':
@@ -153,7 +153,7 @@ int main(int argc, char * argv[])
default:
fprintf(stderr, "unrecognised switch code 0x%x ??\n", c);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (optind < argc) {
@@ -167,27 +167,32 @@ int main(int argc, char * argv[])
fprintf(stderr, "Unexpected extra argument: %s\n",
argv[optind]);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (0 == device_name[0]) {
fprintf(stderr, "missing device name!\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
if (sg_fd < 0) {
fprintf(stderr, ME "open error: %s: %s\n", device_name,
safe_strerror(-sg_fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
res = sg_ll_sync_cache_10(sg_fd, sync_nv, immed, group,
(unsigned int)lba, (unsigned int)count,
1, verbose);
+ ret = res;
if (0 == res)
- ret = 0;
+ ;
+ else if (SG_LIB_CAT_NOT_READY == res)
+ fprintf(stderr, "Synchronize cache failed, device not ready\n");
+ else if (SG_LIB_CAT_UNIT_ATTENTION == res)
+ fprintf(stderr, "Synchronize cache, unit attention\n");
else if (SG_LIB_CAT_INVALID_OP == res)
fprintf(stderr, "Synchronize cache command not supported\n");
else if (SG_LIB_CAT_ILLEGAL_REQ == res)
@@ -197,8 +202,9 @@ int main(int argc, char * argv[])
res = sg_cmds_close_device(sg_fd);
if (res < 0) {
- fprintf(stderr, ME "close error: %s\n", safe_strerror(-res));
- return 1;
+ fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ return SG_LIB_FILE_ERROR;
}
- return ret;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_test_rwbuf.8 b/sg_test_rwbuf.8
index 38f00424..08b12d8e 100644
--- a/sg_test_rwbuf.8
+++ b/sg_test_rwbuf.8
@@ -1,4 +1,4 @@
-.TH SG_TEST_RWBUF "8" "March 2005" "sg3_utils-1.13" SG3_UTILS
+.TH SG_TEST_RWBUF "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_test_rwbuf \- Tests the SCSI host adapter by issuing write and read
operations on a device's buffer and calculating checksums.
@@ -72,6 +72,9 @@ file systems on them.
Following this theme further, a disk with active mounted file systems may cause
the data read back to be different (due to caching activity) to what was written
and hence a checksum error.
+.SH EXIT STATUS
+The exit status of sg_test_rwbuf is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHORS
Written by D. Gilbert and K. Garloff
.SH COPYRIGHT
diff --git a/sg_test_rwbuf.c b/sg_test_rwbuf.c
index 03cddbbf..5939a0c3 100644
--- a/sg_test_rwbuf.c
+++ b/sg_test_rwbuf.c
@@ -35,7 +35,7 @@
#include "sg_io_linux.h"
-static char * version_str = "1.03 20051220";
+static char * version_str = "1.03 20060623";
#define BPI (signed)(sizeof(int))
@@ -82,7 +82,7 @@ int find_out_about_buffer (int sg_fd)
unsigned char rbBuff[RB_DESC_LEN];
unsigned char sense_buffer[32];
struct sg_io_hdr io_hdr;
- int k;
+ int k, res;
rbCmdBlk[1] = RB_MODE_DESC;
rbCmdBlk[8] = RB_DESC_LEN;
@@ -105,10 +105,11 @@ int find_out_about_buffer (int sg_fd)
}
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
perror(ME "SG_IO READ BUFFER descriptor error");
- return 1;
+ return -1;
}
/* now for the error processing */
- switch (sg_err_category3(&io_hdr)) {
+ res = sg_err_category3(&io_hdr);
+ switch (res) {
case SG_LIB_CAT_RECOVERED:
sg_chk_n_print3("READ BUFFER descriptor, continuing",
&io_hdr, 1);
@@ -117,7 +118,7 @@ int find_out_about_buffer (int sg_fd)
break;
default: /* won't bother decoding other categories */
sg_chk_n_print3("READ BUFFER descriptor error", &io_hdr, 1);
- return 1;
+ return res;
}
buf_capacity = ((rbBuff[1] << 16) | (rbBuff[2] << 8) | rbBuff[3]);
@@ -140,6 +141,7 @@ int mymemcmp (unsigned char *bf1, unsigned char *bf2, int len)
return 0;
}
+/* return 0 if good, else 2222 */
int do_checksum (int *buf, int len, int quiet)
{
int sum = base;
@@ -162,7 +164,7 @@ int do_checksum (int *buf, int len, int quiet)
((unsigned char*)buf)[i+diff]);
printf ("\n");
}
- return 2;
+ return 2222;
}
else {
if (verbose > 1)
@@ -214,7 +216,7 @@ int read_buffer (int sg_fd, unsigned size)
struct sg_io_hdr io_hdr;
if (NULL == rbBuff)
- return 1;
+ return -1;
rbCmdBlk[1] = RWB_MODE_DATA;
rbCmdBlk[6] = 0xff & (bufSize >> 16);
rbCmdBlk[7] = 0xff & (bufSize >> 8);
@@ -240,10 +242,11 @@ int read_buffer (int sg_fd, unsigned size)
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
perror(ME "SG_IO READ BUFFER data error");
free(rbBuff);
- return 1;
+ return -1;
}
/* now for the error processing */
- switch (sg_err_category3(&io_hdr)) {
+ res = sg_err_category3(&io_hdr);
+ switch (res) {
case SG_LIB_CAT_RECOVERED:
sg_chk_n_print3("READ BUFFER data, continuing", &io_hdr, 1);
/* fall through */
@@ -252,7 +255,7 @@ int read_buffer (int sg_fd, unsigned size)
default: /* won't bother decoding other categories */
sg_chk_n_print3("READ BUFFER data error", &io_hdr, 1);
free(rbBuff);
- return 1;
+ return res;
}
res = do_checksum ((int*)rbBuff, size, 0);
@@ -267,10 +270,10 @@ int write_buffer (int sg_fd, unsigned size)
unsigned char * wbBuff = malloc(bufSize);
unsigned char sense_buffer[32];
struct sg_io_hdr io_hdr;
- int k;
+ int k, res;
if (NULL == wbBuff)
- return 1;
+ return -1;
memset(wbBuff, 0, bufSize);
do_fill_buffer ((int*)wbBuff, size);
wbCmdBlk[1] = RWB_MODE_DATA;
@@ -298,10 +301,11 @@ int write_buffer (int sg_fd, unsigned size)
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
perror(ME "SG_IO WRITE BUFFER data error");
free(wbBuff);
- return 1;
+ return -1;
}
/* now for the error processing */
- switch (sg_err_category3(&io_hdr)) {
+ res = sg_err_category3(&io_hdr);
+ switch (res) {
case SG_LIB_CAT_RECOVERED:
sg_chk_n_print3("WRITE BUFFER data, continuing", &io_hdr, 1);
/* fall through */
@@ -310,10 +314,10 @@ int write_buffer (int sg_fd, unsigned size)
default: /* won't bother decoding other categories */
sg_chk_n_print3("WRITE BUFFER data error", &io_hdr, 1);
free(wbBuff);
- return 1;
+ return res;
}
free(wbBuff);
- return 0;
+ return res;
}
void usage ()
@@ -360,7 +364,7 @@ int main (int argc, char * argv[])
device_name[0] = '\0';
while (1) {
int option_index = 0;
- char c;
+ int c;
c = getopt_long(argc, argv, "hqr:s:t:w:vV",
long_options, &option_index);
@@ -378,21 +382,21 @@ int main (int argc, char * argv[])
addread = sg_get_num(optarg);
if (-1 == addread) {
fprintf(stderr, "bad argument to '--addrd'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 's':
size = sg_get_num(optarg);
if (-1 == size) {
fprintf(stderr, "bad argument to '--size'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 't':
times = sg_get_num(optarg);
if (-1 == times) {
fprintf(stderr, "bad argument to '--times'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 'v':
@@ -406,12 +410,12 @@ int main (int argc, char * argv[])
addwrite = sg_get_num(optarg);
if (-1 == addwrite) {
fprintf(stderr, "bad argument to '--addwr'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
default:
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (optind < argc) {
@@ -428,14 +432,14 @@ int main (int argc, char * argv[])
if (-1 == size) {
fprintf(stderr, "bad <sz>\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (++optind < argc) {
addwrite = sg_get_num(argv[optind]);
if (-1 == addwrite) {
fprintf(stderr, "bad [addwr]\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (++optind < argc) {
addread = sg_get_num(argv[optind]);
@@ -443,7 +447,7 @@ int main (int argc, char * argv[])
fprintf(stderr,
"bad [addrd]\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
}
@@ -454,30 +458,29 @@ int main (int argc, char * argv[])
fprintf(stderr, "Unexpected extra argument"
": %s\n", argv[optind]);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if ('\0' == device_name[0]) {
fprintf(stderr, "no device name given\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if ((size <= 0) && (! do_quick)) {
fprintf(stderr, "must give '--size' or '--quick' options "
"or <sz> argument\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
sg_fd = open(device_name, O_RDWR | O_NONBLOCK);
if (sg_fd < 0) {
perror("sg_test_rwbuf: open error");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
- if (find_out_about_buffer (sg_fd)) {
- ret = 1;
+ ret = find_out_about_buffer(sg_fd);
+ if (ret)
goto err_out;
- }
if (do_quick) {
printf ("READ BUFFER read descriptor reports a buffer "
"of %d bytes [%d KiB]\n", buf_capacity,
@@ -487,19 +490,20 @@ int main (int argc, char * argv[])
if (size > buf_capacity) {
fprintf (stderr, ME "sz=%i > buf_capacity=%i\n",
size, buf_capacity);
- ret = 2;
+ ret = SG_LIB_CAT_OTHER;
goto err_out;
}
cmpbuf = malloc (size);
for (k = 0; k < times; ++k) {
- if (write_buffer (sg_fd, size)) {
- ret = 3;
+ ret = write_buffer (sg_fd, size);
+ if (ret) {
goto err_out;
}
- res = read_buffer (sg_fd, size);
- if (res) {
- ret = res + 4;
+ ret = read_buffer (sg_fd, size);
+ if (ret) {
+ if (2222 == ret)
+ ret = SG_LIB_CAT_MALFORMED;
goto err_out;
}
}
@@ -510,11 +514,12 @@ err_out:
res = close(sg_fd);
if (res < 0) {
perror(ME "close error");
- ret = 6;
+ if (0 == ret)
+ ret = SG_LIB_FILE_ERROR;
}
if ((0 == ret) && (! do_quick))
printf ("Success\n");
else if (times > 1)
printf ("Failed after %d succesful cycles\n", k);
- return ret;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_turs.8 b/sg_turs.8
index e140500d..7eb0ab80 100644
--- a/sg_turs.8
+++ b/sg_turs.8
@@ -1,4 +1,4 @@
-.TH SG_TURS "8" "December 2005" "sg3_utils-1.19" SG3_UTILS
+.TH SG_TURS "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_turs \- executes a user specified number of TEST UNIT READY commands on
the given device
@@ -39,6 +39,9 @@ greater verbosity.
.TP
-V
print out version string then exit.
+.SH EXIT STATUS
+The exit status of sg_verify is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHORS
Written by D. Gilbert
.SH COPYRIGHT
diff --git a/sg_turs.c b/sg_turs.c
index fe8a002d..d9f48458 100644
--- a/sg_turs.c
+++ b/sg_turs.c
@@ -21,7 +21,7 @@
*/
-static char * version_str = "3.19 20060106";
+static char * version_str = "3.20 20060623";
static void usage()
@@ -42,7 +42,7 @@ static void usage()
int main(int argc, char * argv[])
{
- int sg_fd, k, plen, jmp_out;
+ int sg_fd, k, plen, jmp_out, res;
const char * file_name = 0;
const char * cp;
int num_turs = 1;
@@ -51,6 +51,8 @@ int main(int argc, char * argv[])
int num_errs = 0;
int do_time = 0;
int verbose = 0;
+ int reported = 0;
+ int ret = 0;
struct timeval start_tm, end_tm;
for (k = 1; k < argc; ++k) {
@@ -75,7 +77,7 @@ int main(int argc, char * argv[])
exit(0);
case '?':
usage();
- return 1;
+ return 0;
default:
jmp_out = 1;
break;
@@ -90,12 +92,12 @@ int main(int argc, char * argv[])
if (num_turs <= 0) {
printf("Couldn't decode number after 'n=' option\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (jmp_out) {
fprintf(stderr, "Unrecognized option: %s\n", cp);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == file_name)
file_name = cp;
@@ -103,30 +105,31 @@ int main(int argc, char * argv[])
fprintf(stderr, "too many arguments, got: %s, not expecting: "
"%s\n", file_name, cp);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (0 == file_name) {
fprintf(stderr, "No <scsi_device> argument given\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if ((sg_fd = sg_cmds_open_device(file_name, 1 /* ro */, verbose)) < 0) {
fprintf(stderr, "sg_turs: error opening file: %s: %s\n",
file_name, safe_strerror(-sg_fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (do_progress) {
for (k = 0; k < num_turs; ++k) {
if (k > 0)
sleep(30);
progress = -1;
- sg_ll_test_unit_ready_progress(sg_fd, k, &progress,
- ((1 == num_turs) ? 1 : 0), verbose);
- if (progress < 0)
+ res = sg_ll_test_unit_ready_progress(sg_fd, k, &progress,
+ ((1 == num_turs) ? 1 : 0), verbose);
+ if (progress < 0) {
+ ret = res;
break;
- else
+ } else
printf("Progress indication: %d%% done\n",
(progress * 100) / 65536);
}
@@ -140,9 +143,16 @@ int main(int argc, char * argv[])
gettimeofday(&start_tm, NULL);
}
for (k = 0; k < num_turs; ++k) {
- if (sg_ll_test_unit_ready(sg_fd, k, ((1 == num_turs) ? 1 : 0),
- verbose))
+ res = sg_ll_test_unit_ready(sg_fd, k, 0, verbose);
+ if (res) {
++num_errs;
+ ret = res;
+ if ((1 == num_turs) && (SG_LIB_CAT_NOT_READY == res)) {
+ printf("device not ready\n");
+ reported = 1;
+ break;
+ }
+ }
}
if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
struct timeval res_tm;
@@ -166,9 +176,10 @@ int main(int argc, char * argv[])
printf("\n");
}
- printf("Completed %d Test Unit Ready commands with %d errors\n",
- num_turs, num_errs);
+ if (((num_turs > 1) || (num_errs > 0)) && (! reported))
+ printf("Completed %d Test Unit Ready commands with %d errors\n",
+ num_turs, num_errs);
}
sg_cmds_close_device(sg_fd);
- return num_errs ? 1 : 0;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_verify.8 b/sg_verify.8
index aa4323ff..d73d1d4e 100644
--- a/sg_verify.8
+++ b/sg_verify.8
@@ -1,4 +1,4 @@
-.TH SG_VERIFY "8" "March 2006" "sg3_utils-1.20" SG3_UTILS
+.TH SG_VERIFY "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_verify \- invoke SCSI VERIFY command(s) on a block device
.SH SYNOPSIS
@@ -46,8 +46,9 @@ output the usage message then exit.
.TP
--lba=<n> | -l <n>
specifies the logical block address on the device to start the verify
-operation. <n> is assumed to be decimal unless prefixed by '0x' (see
-below). The default value is 0 (i.e. the start of the device).
+operation. <n> is assumed to be decimal unless prefixed by '0x' or
+a trailing 'h' (see below). The default value is 0 (i.e. the start of the
+device).
.TP
--verbose | -v
increase the level of verbosity, (i.e. debug output).
@@ -77,6 +78,9 @@ the DCR bit to 1, the verify retry count to 0 and the verify error
recovery timeout to 0. Mode pages can be modified with the
.B sginfo
utility.
+.SH EXIT STATUS
+The exit status of sg_verify is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHORS
Written by Douglas Gilbert.
.SH "REPORTING BUGS"
diff --git a/sg_verify.c b/sg_verify.c
index df336b1c..fb812c34 100644
--- a/sg_verify.c
+++ b/sg_verify.c
@@ -42,7 +42,7 @@
* This program issues the SCSI VERIFY command to the given SCSI block device.
*/
-static char * version_str = "1.06 20060322";
+static char * version_str = "1.07 20060623";
#define ME "sg_verify: "
@@ -91,7 +91,7 @@ int main(int argc, char * argv[])
unsigned long long orig_lba;
int verbose = 0;
char device_name[256];
- int ret = 1;
+ int ret = 0;
unsigned long info = 0;
memset(device_name, 0, sizeof device_name);
@@ -108,14 +108,14 @@ int main(int argc, char * argv[])
bpc = sg_get_num(optarg);
if (bpc < 1) {
fprintf(stderr, "bad argument to '--bpc'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 'c':
count = sg_get_llnum(optarg);
if (count < 0) {
fprintf(stderr, "bad argument to '--count'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 'd':
@@ -129,7 +129,7 @@ int main(int argc, char * argv[])
ll = sg_get_llnum(optarg);
if (-1 == ll) {
fprintf(stderr, "bad argument to '--lba'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
lba = (unsigned long long)ll;
break;
@@ -142,7 +142,7 @@ int main(int argc, char * argv[])
default:
fprintf(stderr, "unrecognised switch code 0x%x ??\n", c);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (optind < argc) {
@@ -156,18 +156,18 @@ int main(int argc, char * argv[])
fprintf(stderr, "Unexpected extra argument: %s\n",
argv[optind]);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (bpc > 0xffff) {
fprintf(stderr, "'bpc' cannot exceed 65535\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (lba > 0xffffffffLLU) {
fprintf(stderr, "'lba' cannot exceed 32 bits\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
orig_count = count;
orig_lba = lba;
@@ -175,13 +175,13 @@ int main(int argc, char * argv[])
if (0 == device_name[0]) {
fprintf(stderr, "missing device name!\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
if (sg_fd < 0) {
fprintf(stderr, ME "open error: %s: %s\n", device_name,
safe_strerror(-sg_fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
for (; count > 0; count -= bpc, lba +=bpc) {
@@ -189,7 +189,14 @@ int main(int argc, char * argv[])
res = sg_ll_verify10(sg_fd, dpo, bytechk, (unsigned long)lba, num,
NULL, 0, &info, 1, verbose);
if (0 != res) {
+ ret = res;
switch (res) {
+ case SG_LIB_CAT_NOT_READY:
+ fprintf(stderr, "Verify(10) failed, device not ready\n");
+ break;
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ fprintf(stderr, "Verify(10), unit attention\n");
+ break;
case SG_LIB_CAT_INVALID_OP:
fprintf(stderr, "Verify(10) command not supported\n");
break;
@@ -213,8 +220,6 @@ int main(int argc, char * argv[])
break;
}
}
- if (count <= 0)
- ret = 0;
if (verbose && (0 == ret) && (orig_count > 1))
fprintf(stderr, "Verified %lld [0x%llx] blocks from lba %llu "
@@ -223,8 +228,9 @@ int main(int argc, char * argv[])
res = sg_cmds_close_device(sg_fd);
if (res < 0) {
- fprintf(stderr, ME "close error: %s\n", safe_strerror(-sg_fd));
- return 1;
+ fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ return SG_LIB_FILE_ERROR;
}
- return ret;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_vpd.8 b/sg_vpd.8
new file mode 100644
index 00000000..95adc695
--- /dev/null
+++ b/sg_vpd.8
@@ -0,0 +1,97 @@
+.TH SG_VPD "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
+.SH NAME
+sg_vpd \- outputs information retrieved from a Vital Product Data (VPD)
+page held by a SCSI device
+.SH SYNOPSIS
+.B sg_vpd
+[\fI--enumerate\fR] [\fI--help\fR] [\fI--hex\fR] [\fI--ident\fR]
+[\fI--long\fR] [\fI--page=<vpd_page>\fR] [\fI--quiet\fR] [\fI--raw\fR]
+[\fI--verbose\fR] [\fI--version\fR] \fI<device>\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This utility fetches a Vital Product Data page and decodes it or
+outputs it in ASCII hexadecimal or binary. VPD pages are fetched
+from SCSI devices by the INQUIRY command.
+.PP
+Probably the most important page is the Device Identification
+VPD page (page number: 0x83). Since SPC-3, support for this page
+has been flagged as mandatory. This page can be fetched by
+using the '--ident' (or '-i') option.
+.PP
+When no options are given, other than a '<device>', then the "Supported
+VPD pages" (0x0) VPD page is fetched and decoded.
+.TP
+--enumerate | -e
+list the names of the known VPD pages, first the standard
+pages, then the vendor specific pages. Each group is sorted
+in abbreviation order. The '<device>' and other options
+are ignored and this utility exits after listing the VPD page
+names.
+.TP
+--help | -h
+outputs the usage message summarizing command line options
+then exits. Ignores <device> if given.
+.TP
+--hex | -H
+outputs the request VPD page in ASCII hexadecimal.
+.TP
+--ident | -i
+decode the device identification (0x83) VPD page. When used
+once this option has the same effect as '--page=di'.
+When use twice then the short form of the device identification
+VPD page's logical unit designator is decoded. In the latter
+case this option has the same effect as '--quiet --page=di_lu'.
+.TP
+--long | -l
+when decoding some VPD pages, give a little more output.
+For example the ATA Information VPD page only shows the
+signature (in hex) and the IDENTIFY (PACKET) DEVICE (in hex)
+when this option is given.
+.TP
+--page=<vpd_page> | -p <vpd_page>
+specifies the VPD page to be decoded or output. The '<vpd_page>'
+argument can either be an abbreviation, a number or a pair or
+numbers separated by a comma. The VPD page abbreviations can be
+seen by using the '--list' option. If a number is given it is
+assumed to be decimal unless it has a hexadecimal indicator which
+is either a leading '0x' or a trailing 'h'. If one number is given
+then it is assumed to be a VPD page number. If two numbers are given
+the second number indicates which vendor specific VPD page to
+decode when several pages share the same VPD page number.
+If this option is not given (nor '-i', '-l' or '-V') then the "Supported
+VPD pages" (0x0) VPD page is fetched and decoded.
+.TP
+--quiet | -q
+suppress the amount of decoding output.
+.TP
+--raw | -r
+output requested VPD page in binary. The output should be piped to a
+file or another utility when this option is used. The binary is
+sent to stdout, and errors are sent to stderr.
+.TP
+--verbose | -v
+increases the level or verbosity.
+.TP
+--version | -V
+print out version string
+.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 ATAPI DVDs) can also be specified. For example "sg_inq /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 EXIT STATUS
+The exit status of sg_vpd is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHOR
+Written by Doug Gilbert
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2006 Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_inq(sg3_utils), sdparm(sdparm), hdparm(hdparm)
diff --git a/sg_vpd.c b/sg_vpd.c
new file mode 100644
index 00000000..73c9aa5f
--- /dev/null
+++ b/sg_vpd.c
@@ -0,0 +1,1720 @@
+/*
+ * Copyright (c) 2006 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>
+
+#include "sg_lib.h"
+#include "sg_cmds.h"
+
+/* This utility program was originally written for the Linux SCSI subsystem.
+
+ This program fetches Vital Product Data (VPD) pages from the given
+ device and outputs it as directed. VPD pages are obtained via a
+ SCSI INQUIRY command. Most of the data in this program is obtained
+ from the SCSI SPC-4 document at http://www.t10.org .
+
+*/
+
+static char * version_str = "0.13 20050623"; /* spc-4 rev 5a */
+
+extern void svpd_enumerate_vendor(void);
+extern int svpd_decode_vendor(int sg_fd, int num_vpd, int subvalue,
+ int do_hex, int do_raw, int do_long,
+ int do_quiet, int verbose);
+extern const struct svpd_values_name_t *
+ svpd_find_vendor_by_acron(const char * ap);
+
+
+/* standard VPD pages */
+#define VPD_SUPPORTED_VPDS 0x0
+#define VPD_UNIT_SERIAL_NUM 0x80
+#define VPD_IMP_OP_DEF 0x81 /* obsolete in SPC-2 */
+#define VPD_ASCII_OP_DEF 0x82 /* obsolete in SPC-2 */
+#define VPD_DEVICE_ID 0x83
+#define VPD_SOFTW_INF_ID 0x84
+#define VPD_MAN_NET_ADDR 0x85
+#define VPD_EXT_INQ 0x86
+#define VPD_MODE_PG_POLICY 0x87
+#define VPD_SCSI_PORTS 0x88
+#define VPD_ATA_INFO 0x89
+#define VPD_BLOCK_LIMITS 0xb0 /* SBC-3 */
+#define VPD_SA_DEV_CAP 0xb0 /* SSC-3 */
+#define VPD_OSD_INFO 0xb0 /* OSD */
+#define VPD_MAN_ASS_SN 0xb1 /* SSC-3 */
+#define VPD_SECURITY_TOKEN 0xb1 /* OSD */
+#define VPD_TA_SUPPORTED 0xb2 /* SSC-3 */
+
+/* Device identification VPD page associations */
+#define VPD_ASSOC_LU 0
+#define VPD_ASSOC_TPORT 1
+#define VPD_ASSOC_TDEVICE 2
+
+/* values for selection one or more associations (2**vpd_assoc),
+ except _AS_IS */
+#define VPD_DI_SEL_LU 1
+#define VPD_DI_SEL_TPORT 2
+#define VPD_DI_SEL_TARGET 4
+#define VPD_DI_SEL_AS_IS 32
+
+
+#define DEF_ALLOC_LEN 252
+#define MX_ALLOC_LEN (0xc000 + 0x80)
+#define VPD_ATA_INFO_LEN 572
+
+
+/* This structure is a duplicate of one of the same name in sg_vpd_vendor.c .
+ Take care that both have the same fields (and types). */
+struct svpd_values_name_t {
+ int value;
+ int subvalue;
+ int pdt; /* peripheral device type id, -1 is the default */
+ /* (all or not applicable) value */
+ int ro_vendor; /* read-only or vendor flag */
+ const char * acron;
+ const char * name;
+};
+
+
+static unsigned char rsp_buff[MX_ALLOC_LEN + 2];
+
+static int decode_dev_ids(const char * print_if_found, unsigned char * buff,
+ int len, int m_assoc, int m_desig_type,
+ int m_code_set, int long_out, int quiet);
+static void decode_transport_id(const char * leadin, unsigned char * ucp,
+ int len);
+
+static struct option long_options[] = {
+ {"enumerate", 0, 0, 'e'},
+ {"help", 0, 0, 'h'},
+ {"hex", 0, 0, 'H'},
+ {"ident", 0, 0, 'i'},
+ {"long", 0, 0, 'l'},
+ {"page", 1, 0, 'p'},
+ {"quiet", 0, 0, 'q'},
+ {"raw", 0, 0, 'r'},
+ {"verbose", 0, 0, 'v'},
+ {"version", 0, 0, 'V'},
+ {0, 0, 0, 0},
+};
+
+static struct svpd_values_name_t standard_vpd_pg[] = {
+ {VPD_ATA_INFO, 0, -1, 0, "ai", "ATA information (SAT)"},
+ {VPD_ASCII_OP_DEF, 0, -1, 0, "aod",
+ "ASCII implemented operating definition (obs)"},
+ {VPD_BLOCK_LIMITS, 0, 0, 0, "bl", "Block limits (SBC)"},
+ {VPD_DEVICE_ID, 0, -1, 0, "di", "Device identification"},
+ {VPD_DEVICE_ID, VPD_DI_SEL_AS_IS, -1, 0, "di_asis", "Like 'di' "
+ "but designators ordered as found"},
+ {VPD_DEVICE_ID, VPD_DI_SEL_LU, -1, 0, "di_lu", "Device identification, "
+ "lu only"},
+ {VPD_DEVICE_ID, VPD_DI_SEL_TPORT, -1, 0, "di_port", "Device "
+ "identification, target port only"},
+ {VPD_DEVICE_ID, VPD_DI_SEL_TARGET, -1, 0, "di_target", "Device "
+ "identification, target device only"},
+ {VPD_EXT_INQ, 0, -1, 0, "ei", "Extended inquiry data"},
+ {VPD_IMP_OP_DEF, 0, -1, 0, "iod",
+ "Implemented operating definition (obs)"},
+ {VPD_MAN_ASS_SN, 0, 1, 0, "mas",
+ "Manufacturer assigned serial number (SSC)"},
+ {VPD_MAN_NET_ADDR, 0, -1, 0, "mna", "Management network addresses"},
+ {VPD_MODE_PG_POLICY, 0, -1, 0, "mpp", "Mode page policy"},
+ {VPD_OSD_INFO, 0, 0x11, 0, "oi", "OSD information"},
+ {VPD_SA_DEV_CAP, 0, 1, 0, "sad",
+ "Sequential access device capabilities (SSC)"},
+ {VPD_SOFTW_INF_ID, 0, -1, 0, "sii", "Software interface identification"},
+ {VPD_UNIT_SERIAL_NUM, 0, -1, 0, "sn", "Unit serial number"},
+ {VPD_SCSI_PORTS, 0, -1, 0, "sp", "SCSI ports"},
+ {VPD_SECURITY_TOKEN, 0, 0x11, 0, "st", "Security token (OSD)"},
+ {VPD_SUPPORTED_VPDS, 0, -1, 0, "sv", "Supported VPD pages"},
+ {VPD_TA_SUPPORTED, 0, 1, 0, "tas", "TapeAlert supported flags (SSC)"},
+ {0, 0, 0, 0, NULL, NULL},
+};
+
+static void usage()
+{
+ fprintf(stderr,
+ "Usage: sg_vpd [--enumerate] [--help] [--hex] [--ident] "
+ "[--long]\n"
+ " [--page=<vpd_page>] [--quiet] [--raw] "
+ "[--verbose] [--version]\n"
+ " <device>\n");
+ fprintf(stderr,
+ " --enumerate|-e enumerate known VPD pages names then "
+ "exit\n"
+ " --help|-h output this usage message then exit\n"
+ " --hex|-H output page in ASCII hexadecimal\n"
+ " --ident|-i output device identification VPD page, "
+ "twice for\n"
+ " short logical unit designator (equiv: "
+ "'-qp di_lu')\n"
+ " --long|-l perform extra decoding\n"
+ " --page=<vpd_page>|-p <vpd_page>\n"
+ " fetch given VPD page; numbers are "
+ "decimal\n"
+ " unless hex indicator, else acronym\n"
+ " --quiet|-q suppress some output when decoding\n"
+ " --raw|-r output page in binary\n"
+ " --verbose|-v increase verbosity\n"
+ " --version|-V print version string and exit\n\n"
+ " Fetch Vital Product Data (VPD) page using SCSI INQUIRY\n");
+}
+
+static const struct svpd_values_name_t *
+ sdp_get_vpd_detail(int page_num, int subvalue, int pdt)
+{
+ const struct svpd_values_name_t * vnp;
+ int sv, ty;
+
+ sv = (subvalue < 0) ? 1 : 0;
+ ty = (pdt < 0) ? 1 : 0;
+ for (vnp = standard_vpd_pg; vnp->acron; ++vnp) {
+ if ((page_num == vnp->value) &&
+ (sv || (subvalue == vnp->subvalue)) &&
+ (ty || (pdt == vnp->pdt)))
+ return vnp;
+ }
+ if (! ty)
+ return sdp_get_vpd_detail(page_num, subvalue, -1);
+ if (! sv)
+ return sdp_get_vpd_detail(page_num, -1, -1);
+ return NULL;
+}
+
+static const struct svpd_values_name_t *
+ sdp_find_vpd_by_acron(const char * ap)
+{
+ const struct svpd_values_name_t * vnp;
+
+ for (vnp = standard_vpd_pg; vnp->acron; ++vnp) {
+ if (0 == strcmp(vnp->acron, ap))
+ return vnp;
+ }
+ return NULL;
+}
+
+static void enumerate_vpds(int standard, int vendor)
+{
+ const struct svpd_values_name_t * vnp;
+
+ if (standard) {
+ for (vnp = standard_vpd_pg; vnp->acron; ++vnp) {
+ if (vnp->name && (0 == vnp->ro_vendor))
+ printf(" %-10s 0x%02x %s\n", vnp->acron, vnp->value,
+ vnp->name);
+ }
+ }
+ if (vendor)
+ svpd_enumerate_vendor();
+}
+
+/* Trying to decode multipliers as sg_get_num() [as sg_libs does] would
+ * only confuse things here, so use this local trimmed version */
+static int get_num(const char * buf)
+{
+ int res, len, num;
+ unsigned int unum;
+ const char * commap;
+
+ if ((NULL == buf) || ('\0' == buf[0]))
+ return -1;
+ len = strlen(buf);
+ commap = strchr(buf + 1, ',');
+ if (('0' == buf[0]) && (('x' == buf[1]) || ('X' == buf[1]))) {
+ res = sscanf(buf + 2, "%x", &unum);
+ num = unum;
+ } else if (commap && ('H' == toupper(*(commap - 1)))) {
+ res = sscanf(buf, "%x", &unum);
+ num = unum;
+ } else if ((NULL == commap) && ('H' == toupper(buf[len - 1]))) {
+ res = sscanf(buf, "%x", &unum);
+ num = unum;
+ } else
+ res = sscanf(buf, "%d", &num);
+ if (1 == res)
+ return num;
+ else
+ return -1;
+}
+
+static void dStrRaw(const char* str, int len)
+{
+ int k;
+
+ for (k = 0 ; k < len; ++k)
+ printf("%c", str[k]);
+}
+
+static const char * assoc_arr[] =
+{
+ "Addressed logical unit",
+ "Target port", /* that received request; unless SCSI ports VPD */
+ "Target device that contains addressed lu",
+ "Reserved [0x3]",
+};
+
+static void decode_id_vpd(unsigned char * buff, int len, int subvalue,
+ int do_long, int do_quiet)
+{
+ int m_a, m_d, m_cs;
+
+ if (len < 4) {
+ fprintf(stderr, "Device identification VPD page length too "
+ "short=%d\n", len);
+ return;
+ }
+ m_a = -1;
+ m_d = -1;
+ m_cs = -1;
+ if (0 == subvalue) {
+ decode_dev_ids(assoc_arr[VPD_ASSOC_LU], buff + 4, len - 4,
+ VPD_ASSOC_LU, m_d, m_cs, do_long, do_quiet);
+ decode_dev_ids(assoc_arr[VPD_ASSOC_TPORT], buff + 4, len - 4,
+ VPD_ASSOC_TPORT, m_d, m_cs, do_long, do_quiet);
+ decode_dev_ids(assoc_arr[VPD_ASSOC_TDEVICE], buff + 4, len - 4,
+ VPD_ASSOC_TDEVICE, m_d, m_cs, do_long, do_quiet);
+ } else if (VPD_DI_SEL_AS_IS == subvalue)
+ decode_dev_ids(NULL, buff + 4, len - 4, m_a, m_d, m_cs, do_long,
+ do_quiet);
+ else {
+ if (VPD_DI_SEL_LU & subvalue)
+ decode_dev_ids(assoc_arr[VPD_ASSOC_LU], buff + 4, len - 4,
+ VPD_ASSOC_LU, m_d, m_cs, do_long, do_quiet);
+ if (VPD_DI_SEL_TPORT & subvalue)
+ decode_dev_ids(assoc_arr[VPD_ASSOC_TPORT], buff + 4, len - 4,
+ VPD_ASSOC_TPORT, m_d, m_cs, do_long, do_quiet);
+ if (VPD_DI_SEL_TARGET & subvalue)
+ decode_dev_ids(assoc_arr[VPD_ASSOC_TDEVICE], buff + 4, len - 4,
+ VPD_ASSOC_TDEVICE, m_d, m_cs, do_long, do_quiet);
+ }
+}
+
+static const char * network_service_type_arr[] =
+{
+ "unspecified",
+ "storage configuration service",
+ "diagnostics",
+ "status",
+ "logging",
+ "code download",
+ "reserved[0x6]", "reserved[0x7]", "reserved[0x8]", "reserved[0x9]",
+ "reserved[0xa]", "reserved[0xb]", "reserved[0xc]", "reserved[0xd]",
+ "reserved[0xe]", "reserved[0xf]", "reserved[0x10]", "reserved[0x11]",
+ "reserved[0x12]", "reserved[0x13]", "reserved[0x14]", "reserved[0x15]",
+ "reserved[0x16]", "reserved[0x17]", "reserved[0x18]", "reserved[0x19]",
+ "reserved[0x1a]", "reserved[0x1b]", "reserved[0x1c]", "reserved[0x1d]",
+ "reserved[0x1e]", "reserved[0x1f]",
+};
+
+static void decode_net_man_vpd(unsigned char * buff, int len, int do_hex)
+{
+ int k, bump, na_len;
+ unsigned char * ucp;
+
+ if (len < 4) {
+ fprintf(stderr, "Management network addresses VPD page length too "
+ "short=%d\n", len);
+ return;
+ }
+ len -= 4;
+ ucp = buff + 4;
+ for (k = 0; k < len; k += bump, ucp += bump) {
+ printf(" %s, Service type: %s\n",
+ assoc_arr[(ucp[0] >> 5) & 0x3],
+ network_service_type_arr[ucp[0] & 0x1f]);
+ na_len = (ucp[2] << 8) + ucp[3];
+ bump = 4 + na_len;
+ if ((k + bump) > len) {
+ fprintf(stderr, "Management network addresses VPD page, short "
+ "descriptor length=%d, left=%d\n", bump, (len - k));
+ return;
+ }
+ if (na_len > 0) {
+ if (do_hex) {
+ printf(" Network address:\n");
+ dStrHex((const char *)(ucp + 4), na_len, 0);
+ } else
+ printf(" %s\n", ucp + 4);
+ }
+ }
+}
+
+static const char * mode_page_policy_arr[] =
+{
+ "shared",
+ "per target port",
+ "per initiator port",
+ "per I_T nexus",
+};
+
+static void decode_mode_policy_vpd(unsigned char * buff, int len, int do_hex)
+{
+ int k, bump;
+ unsigned char * ucp;
+
+ if (len < 4) {
+ fprintf(stderr, "Mode page policy VPD page length too short=%d\n",
+ len);
+ return;
+ }
+ len -= 4;
+ ucp = buff + 4;
+ for (k = 0; k < len; k += bump, ucp += bump) {
+ bump = 4;
+ if ((k + bump) > len) {
+ fprintf(stderr, "Mode page policy VPD page, short "
+ "descriptor length=%d, left=%d\n", bump, (len - k));
+ return;
+ }
+ if (do_hex)
+ dStrHex((const char *)ucp, 4, 1);
+ else {
+ printf(" Policy page code: 0x%x", (ucp[0] & 0x3f));
+ if (ucp[1])
+ printf(", subpage code: 0x%x\n", ucp[1]);
+ else
+ printf("\n");
+ printf(" MLUS=%d, Policy: %s\n", !!(ucp[2] & 0x80),
+ mode_page_policy_arr[ucp[2] & 0x3]);
+ }
+ }
+}
+
+static void decode_scsi_ports_vpd(unsigned char * buff, int len, int do_hex,
+ int do_long, int do_quiet)
+{
+ int k, bump, rel_port, ip_tid_len, tpd_len;
+ unsigned char * ucp;
+
+ if (len < 4) {
+ fprintf(stderr, "SCSI Ports VPD page length too short=%d\n", len);
+ return;
+ }
+ len -= 4;
+ ucp = buff + 4;
+ for (k = 0; k < len; k += bump, ucp += bump) {
+ rel_port = (ucp[2] << 8) + ucp[3];
+ printf("Relative port=%d\n", rel_port);
+ ip_tid_len = (ucp[6] << 8) + ucp[7];
+ bump = 8 + ip_tid_len;
+ if ((k + bump) > len) {
+ fprintf(stderr, "SCSI Ports VPD page, short descriptor "
+ "length=%d, left=%d\n", bump, (len - k));
+ return;
+ }
+ if (ip_tid_len > 0) {
+ if (do_hex) {
+ printf(" Initiator port transport id:\n");
+ dStrHex((const char *)(ucp + 8), ip_tid_len, 1);
+ } else
+ decode_transport_id(" ", ucp + 8, ip_tid_len);
+ }
+ tpd_len = (ucp[bump + 2] << 8) + ucp[bump + 3];
+ if ((k + bump + tpd_len + 4) > len) {
+ fprintf(stderr, "SCSI Ports VPD page, short descriptor(tgt) "
+ "length=%d, left=%d\n", bump, (len - k));
+ return;
+ }
+ if (tpd_len > 0) {
+ printf(" Target port descriptor(s):\n");
+ if (do_hex)
+ dStrHex((const char *)(ucp + bump + 4), tpd_len, 1);
+ else
+ decode_dev_ids("SCSI Ports", ucp + bump + 4, tpd_len,
+ VPD_ASSOC_TPORT, -1, -1, do_long, do_quiet);
+ }
+ bump += tpd_len + 4;
+ }
+}
+
+static const char * transport_proto_arr[] =
+{
+ "Fibre Channel (FCP-2)",
+ "Parallel SCSI (SPI-4)",
+ "SSA (SSA-S3P)",
+ "IEEE 1394 (SBP-3)",
+ "Remote Direct Memory Access (RDMA)",
+ "Internet SCSI (iSCSI)",
+ "Serial Attached SCSI (SAS)",
+ "Automation/Drive Interface (ADT)",
+ "ATA Packet Interface (ATA/ATAPI-7)",
+ "Ox9", "Oxa", "Oxb", "Oxc", "Oxd", "Oxe",
+ "No specific protocol"
+};
+
+static const char * code_set_arr[] =
+{
+ "Reserved [0x0]",
+ "Binary",
+ "ASCII",
+ "UTF-8",
+ "Reserved [0x4]", "Reserved [0x5]", "Reserved [0x6]", "Reserved [0x7]",
+ "Reserved [0x8]", "Reserved [0x9]", "Reserved [0xa]", "Reserved [0xb]",
+ "Reserved [0xc]", "Reserved [0xd]", "Reserved [0xe]", "Reserved [0xf]",
+};
+
+static const char * desig_type_arr[] =
+{
+ "vendor specific [0x0]",
+ "T10 vendor identification",
+ "EUI-64 based",
+ "NAA",
+ "Relative target port",
+ "Target port group",
+ "Logical unit group",
+ "MD5 logical unit identifier",
+ "SCSI name string",
+ "Reserved [0x9]", "Reserved [0xa]", "Reserved [0xb]",
+ "Reserved [0xc]", "Reserved [0xd]", "Reserved [0xe]", "Reserved [0xf]",
+};
+
+
+/* Prints outs an abridged set of device identification designators
+ selected by association, designator type and/or code set. */
+static int decode_dev_ids_quiet(unsigned char * buff, int len,
+ int m_assoc, int m_desig_type,
+ int m_code_set)
+{
+ int m, p_id, c_set, piv, assoc, desig_type, i_len;
+ int c_id, d_id, naa, vsi, off, u;
+ const unsigned char * ucp;
+ const unsigned char * ip;
+
+ off = -1;
+ while ((u = sg_vpd_dev_id_iter(buff, len, &off, m_assoc, m_desig_type,
+ m_code_set)) == 0) {
+ ucp = buff + off;
+ i_len = ucp[3];
+ if ((off + i_len + 4) > len) {
+ fprintf(stderr, " VPD page error: designator length longer "
+ "than\n remaining response length=%d\n", (len - off));
+ return SG_LIB_CAT_MALFORMED;
+ }
+ ip = ucp + 4;
+ p_id = ((ucp[0] >> 4) & 0xf);
+ c_set = (ucp[0] & 0xf);
+ piv = ((ucp[1] & 0x80) ? 1 : 0);
+ assoc = ((ucp[1] >> 4) & 0x3);
+ desig_type = (ucp[1] & 0xf);
+ switch (desig_type) {
+ case 0: /* vendor specific */
+ break;
+ case 1: /* T10 vendor identification */
+ break;
+ case 2: /* EUI-64 based */
+ if ((8 != i_len) && (12 != i_len) && (16 != i_len))
+ fprintf(stderr, " << expect 8, 12 and 16 byte "
+ "ids, got %d>>\n", i_len);
+ printf("0x");
+ for (m = 0; m < i_len; ++m)
+ printf("%02x", (unsigned int)ip[m]);
+ printf("\n");
+ break;
+ case 3: /* NAA */
+ if (1 != c_set) {
+ fprintf(stderr, " << expected binary code_set (1)>>\n");
+ dStrHex((const char *)ip, i_len, 0);
+ break;
+ }
+ naa = (ip[0] >> 4) & 0xff;
+ if (! ((2 == naa) || (5 == naa) || (6 == naa))) {
+ fprintf(stderr, " << expected naa [0x%x]>>\n", naa);
+ dStrHex((const char *)ip, i_len, 0);
+ break;
+ }
+ if (2 == naa) {
+ if (8 != i_len) {
+ fprintf(stderr, " << expected NAA 2 identifier "
+ "length: 0x%x>>\n", i_len);
+ dStrHex((const char *)ip, i_len, 0);
+ break;
+ }
+ d_id = (((ip[0] & 0xf) << 8) | ip[1]);
+ c_id = ((ip[2] << 16) | (ip[3] << 8) | ip[4]);
+ vsi = ((ip[5] << 16) | (ip[6] << 8) | ip[7]);
+ printf("0x");
+ for (m = 0; m < 8; ++m)
+ printf("%02x", (unsigned int)ip[m]);
+ printf("\n");
+ } else if (5 == naa) {
+ if (8 != i_len) {
+ fprintf(stderr, " << expected NAA 5 identifier "
+ "length: 0x%x>>\n", i_len);
+ dStrHex((const char *)ip, i_len, 0);
+ break;
+ }
+ printf("0x");
+ for (m = 0; m < 8; ++m)
+ printf("%02x", (unsigned int)ip[m]);
+ printf("\n");
+ } else if (6 == naa) {
+ if (16 != i_len) {
+ fprintf(stderr, " << expected NAA 6 identifier "
+ "length: 0x%x>>\n", i_len);
+ dStrHex((const char *)ip, i_len, 0);
+ break;
+ }
+ printf("0x");
+ for (m = 0; m < 16; ++m)
+ printf("%02x", (unsigned int)ip[m]);
+ printf("\n");
+ }
+ break;
+ case 4: /* Relative target port */
+ break;
+ case 5: /* Target port group */
+ break;
+ case 6: /* Logical unit group */
+ break;
+ case 7: /* MD5 logical unit identifier */
+ break;
+ case 8: /* SCSI name string */
+ if (3 != c_set) {
+ fprintf(stderr, " << expected UTF-8 code_set>>\n");
+ dStrHex((const char *)ip, i_len, 0);
+ break;
+ }
+ /* does %s print out UTF-8 ok??
+ * Seems to depend on the locale. Looks ok here with my
+ * locale setting: en_AU.UTF-8
+ */
+ printf("%s\n", (const char *)ip);
+ break;
+ default: /* reserved */
+ break;
+ }
+ }
+ if (-2 == u) {
+ fprintf(stderr, "VPD page error: short designator around "
+ "offset %d\n", off);
+ return SG_LIB_CAT_MALFORMED;
+ }
+ return 0;
+}
+
+/* Prints outs device identification designators selected by association,
+ designator type and/or code set. */
+static int decode_dev_ids(const char * print_if_found, unsigned char * buff,
+ int len, int m_assoc, int m_desig_type,
+ int m_code_set, int long_out, int quiet)
+{
+ int m, p_id, c_set, piv, assoc, desig_type, i_len;
+ int ci_off, c_id, d_id, naa, vsi, printed, off, u;
+ unsigned long long vsei;
+ unsigned long long id_ext;
+ const unsigned char * ucp;
+ const unsigned char * ip;
+
+ if (quiet)
+ return decode_dev_ids_quiet(buff, len, m_assoc, m_desig_type,
+ m_code_set);
+ off = -1;
+ printed = 0;
+ while ((u = sg_vpd_dev_id_iter(buff, len, &off, m_assoc, m_desig_type,
+ m_code_set)) == 0) {
+ ucp = buff + off;
+ i_len = ucp[3];
+ if ((off + i_len + 4) > len) {
+ fprintf(stderr, " VPD page error: designator length longer "
+ "than\n remaining response length=%d\n", (len - off));
+ return SG_LIB_CAT_MALFORMED;
+ }
+ ip = ucp + 4;
+ p_id = ((ucp[0] >> 4) & 0xf);
+ c_set = (ucp[0] & 0xf);
+ piv = ((ucp[1] & 0x80) ? 1 : 0);
+ assoc = ((ucp[1] >> 4) & 0x3);
+ desig_type = (ucp[1] & 0xf);
+ if (print_if_found && (0 == printed)) {
+ printed = 1;
+ printf(" %s:\n", print_if_found);
+ }
+ if (NULL == print_if_found)
+ printf(" %s:\n", assoc_arr[assoc]);
+ printf(" desig_type: %s, code_set: %s\n",
+ desig_type_arr[desig_type], code_set_arr[c_set]);
+ if (piv && ((1 == assoc) || (2 == assoc)))
+ printf(" transport: %s\n", transport_proto_arr[p_id]);
+ /* printf(" associated with the %s\n", assoc_arr[assoc]); */
+ switch (desig_type) {
+ case 0: /* vendor specific */
+ dStrHex((const char *)ip, i_len, 0);
+ break;
+ case 1: /* T10 vendor identification */
+ printf(" vendor id: %.8s\n", ip);
+ if (i_len > 8)
+ printf(" vendor specific: %.*s\n", i_len - 8, ip + 8);
+ break;
+ case 2: /* EUI-64 based */
+ if (! long_out) {
+ if ((8 != i_len) && (12 != i_len) && (16 != i_len)) {
+ fprintf(stderr, " << expect 8, 12 and 16 byte "
+ "ids, got %d>>\n", i_len);
+ dStrHex((const char *)ip, i_len, 0);
+ break;
+ }
+ printf(" 0x");
+ for (m = 0; m < i_len; ++m)
+ printf("%02x", (unsigned int)ip[m]);
+ printf("\n");
+ break;
+ }
+ printf(" EUI-64 based %d byte identifier\n", i_len);
+ if (1 != c_set) {
+ fprintf(stderr, " << expected binary code_set (1)>>\n");
+ dStrHex((const char *)ip, i_len, 0);
+ break;
+ }
+ ci_off = 0;
+ if (16 == i_len) {
+ ci_off = 8;
+ id_ext = 0;
+ for (m = 0; m < 8; ++m) {
+ if (m > 0)
+ id_ext <<= 8;
+ id_ext |= ip[m];
+ }
+ printf(" Identifier extension: 0x%llx\n", id_ext);
+ } else if ((8 != i_len) && (12 != i_len)) {
+ fprintf(stderr, " << can only decode 8, 12 and 16 "
+ "byte ids>>\n");
+ dStrHex((const char *)ip, i_len, 0);
+ break;
+ }
+ c_id = ((ip[ci_off] << 16) | (ip[ci_off + 1] << 8) |
+ ip[ci_off + 2]);
+ printf(" IEEE Company_id: 0x%x\n", c_id);
+ vsei = 0;
+ for (m = 0; m < 5; ++m) {
+ if (m > 0)
+ vsei <<= 8;
+ vsei |= ip[ci_off + 3 + m];
+ }
+ printf(" Vendor Specific Extension Identifier: 0x%llx\n",
+ vsei);
+ if (12 == i_len) {
+ d_id = ((ip[8] << 24) | (ip[9] << 16) | (ip[10] << 8) |
+ ip[11]);
+ printf(" Directory ID: 0x%x\n", d_id);
+ }
+ break;
+ case 3: /* NAA */
+ if (1 != c_set) {
+ fprintf(stderr, " << expected binary code_set (1)>>\n");
+ dStrHex((const char *)ip, i_len, 0);
+ break;
+ }
+ naa = (ip[0] >> 4) & 0xff;
+ if (! ((2 == naa) || (5 == naa) || (6 == naa))) {
+ fprintf(stderr, " << expected naa [0x%x]>>\n", naa);
+ dStrHex((const char *)ip, i_len, 0);
+ break;
+ }
+ if (2 == naa) {
+ if (8 != i_len) {
+ fprintf(stderr, " << expected NAA 2 identifier "
+ "length: 0x%x>>\n", i_len);
+ dStrHex((const char *)ip, i_len, 0);
+ break;
+ }
+ d_id = (((ip[0] & 0xf) << 8) | ip[1]);
+ c_id = ((ip[2] << 16) | (ip[3] << 8) | ip[4]);
+ vsi = ((ip[5] << 16) | (ip[6] << 8) | ip[7]);
+ if (long_out) {
+ printf(" NAA 2, vendor specific identifier A: "
+ "0x%x\n", d_id);
+ printf(" IEEE Company_id: 0x%x\n", c_id);
+ printf(" vendor specific identifier B: 0x%x\n", vsi);
+ printf(" [0x");
+ for (m = 0; m < 8; ++m)
+ printf("%02x", (unsigned int)ip[m]);
+ printf("]\n");
+ }
+ printf(" 0x");
+ for (m = 0; m < 8; ++m)
+ printf("%02x", (unsigned int)ip[m]);
+ printf("\n");
+ } else if (5 == naa) {
+ if (8 != i_len) {
+ fprintf(stderr, " << expected NAA 5 identifier "
+ "length: 0x%x>>\n", i_len);
+ dStrHex((const char *)ip, i_len, 0);
+ break;
+ }
+ c_id = (((ip[0] & 0xf) << 20) | (ip[1] << 12) |
+ (ip[2] << 4) | ((ip[3] & 0xf0) >> 4));
+ vsei = ip[3] & 0xf;
+ for (m = 1; m < 5; ++m) {
+ vsei <<= 8;
+ vsei |= ip[3 + m];
+ }
+ if (long_out) {
+ printf(" NAA 5, IEEE Company_id: 0x%x\n", c_id);
+ printf(" Vendor Specific Identifier: 0x%llx\n",
+ vsei);
+ printf(" [0x");
+ for (m = 0; m < 8; ++m)
+ printf("%02x", (unsigned int)ip[m]);
+ printf("]\n");
+ } else {
+ printf(" 0x");
+ for (m = 0; m < 8; ++m)
+ printf("%02x", (unsigned int)ip[m]);
+ printf("\n");
+ }
+ } else if (6 == naa) {
+ if (16 != i_len) {
+ fprintf(stderr, " << expected NAA 6 identifier "
+ "length: 0x%x>>\n", i_len);
+ dStrHex((const char *)ip, i_len, 0);
+ break;
+ }
+ c_id = (((ip[0] & 0xf) << 20) | (ip[1] << 12) |
+ (ip[2] << 4) | ((ip[3] & 0xf0) >> 4));
+ vsei = ip[3] & 0xf;
+ for (m = 1; m < 5; ++m) {
+ vsei <<= 8;
+ vsei |= ip[3 + m];
+ }
+ if (long_out) {
+ printf(" NAA 6, IEEE Company_id: 0x%x\n", c_id);
+ printf(" Vendor Specific Identifier: 0x%llx\n",
+ vsei);
+ vsei = 0;
+ for (m = 0; m < 8; ++m) {
+ if (m > 0)
+ vsei <<= 8;
+ vsei |= ip[8 + m];
+ }
+ printf(" Vendor Specific Identifier Extension: "
+ "0x%llx\n", vsei);
+ printf(" [0x");
+ for (m = 0; m < 16; ++m)
+ printf("%02x", (unsigned int)ip[m]);
+ printf("]\n");
+ } else {
+ printf(" 0x");
+ for (m = 0; m < 16; ++m)
+ printf("%02x", (unsigned int)ip[m]);
+ printf("\n");
+ }
+ }
+ break;
+ case 4: /* Relative target port */
+ if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
+ fprintf(stderr, " << expected binary code_set, target "
+ "port association, length 4>>\n");
+ dStrHex((const char *)ip, i_len, 0);
+ break;
+ }
+ d_id = ((ip[2] << 8) | ip[3]);
+ printf(" Relative target port: 0x%x\n", d_id);
+ break;
+ case 5: /* Target port group */
+ if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
+ fprintf(stderr, " << expected binary code_set, target "
+ "port association, length 4>>\n");
+ dStrHex((const char *)ip, i_len, 0);
+ break;
+ }
+ d_id = ((ip[2] << 8) | ip[3]);
+ printf(" Target port group: 0x%x\n", d_id);
+ break;
+ case 6: /* Logical unit group */
+ if ((1 != c_set) || (0 != assoc) || (4 != i_len)) {
+ fprintf(stderr, " << expected binary code_set, logical "
+ "unit association, length 4>>\n");
+ dStrHex((const char *)ip, i_len, 0);
+ break;
+ }
+ d_id = ((ip[2] << 8) | ip[3]);
+ printf(" Logical unit group: 0x%x\n", d_id);
+ break;
+ case 7: /* MD5 logical unit identifier */
+ if ((1 != c_set) || (0 != assoc)) {
+ printf(" << expected binary code_set, logical "
+ "unit association>>\n");
+ dStrHex((const char *)ip, i_len, 0);
+ break;
+ }
+ printf(" MD5 logical unit identifier:\n");
+ dStrHex((const char *)ip, i_len, 0);
+ break;
+ case 8: /* SCSI name string */
+ if (3 != c_set) {
+ fprintf(stderr, " << expected UTF-8 code_set>>\n");
+ dStrHex((const char *)ip, i_len, 0);
+ break;
+ }
+ printf(" SCSI name string:\n");
+ /* does %s print out UTF-8 ok??
+ * Seems to depend on the locale. Looks ok here with my
+ * locale setting: en_AU.UTF-8
+ */
+ printf(" %s\n", (const char *)ip);
+ break;
+ default: /* reserved */
+ dStrHex((const char *)ip, i_len, 0);
+ break;
+ }
+ }
+ if (-2 == u) {
+ fprintf(stderr, "VPD page error: short designator around "
+ "offset %d\n", off);
+ return SG_LIB_CAT_MALFORMED;
+ }
+ return 0;
+}
+
+/* Transport IDs are initiator port identifiers, typically other than the
+ initiator port issuing a SCSI command. Code borrowed from sg_persist.c */
+static void decode_transport_id(const char * leadin, unsigned char * ucp,
+ int len)
+{
+ int format_code, proto_id, num, j, k;
+ unsigned long long ull;
+ int bump;
+
+ for (k = 0, bump; k < len; k += bump, ucp += bump) {
+ if ((len < 24) || (0 != (len % 4)))
+ printf("%sTransport Id short or not multiple of 4 "
+ "[length=%d]:\n", leadin, len);
+ else
+ printf("%sTransport Id of initiator:\n", leadin);
+ format_code = ((ucp[0] >> 6) & 0x3);
+ proto_id = (ucp[0] & 0xf);
+ switch (proto_id) {
+ case 0: /* Fibre channel */
+ printf("%s FCP-2 World Wide Name:\n", leadin);
+ if (0 != format_code)
+ printf("%s [Unexpected format code: %d]\n", leadin,
+ format_code);
+ dStrHex((const char *)&ucp[8], 8, 0);
+ bump = 24;
+ break;
+ case 1: /* Parallel SCSI */
+ printf("%s Parallel SCSI initiator SCSI address: 0x%x\n",
+ leadin, ((ucp[2] << 8) | ucp[3]));
+ if (0 != format_code)
+ printf("%s [Unexpected format code: %d]\n", leadin,
+ format_code);
+ printf("%s relative port number (of corresponding target): "
+ "0x%x\n", leadin, ((ucp[6] << 8) | ucp[7]));
+ bump = 24;
+ break;
+ case 2: /* SSA */
+ printf("%s SSA (transport id not defined):\n", leadin);
+ printf("%s format code: %d\n", leadin, format_code);
+ dStrHex((const char *)ucp, ((len > 24) ? 24 : len), 0);
+ bump = 24;
+ break;
+ case 3: /* IEEE 1394 */
+ printf("%s IEEE 1394 EUI-64 name:\n", leadin);
+ if (0 != format_code)
+ printf("%s [Unexpected format code: %d]\n", leadin,
+ format_code);
+ dStrHex((const char *)&ucp[8], 8, 0);
+ bump = 24;
+ break;
+ case 4: /* Remote Direct Memory Access (RDMA) */
+ printf("%s RDMA initiator port identifier:\n", leadin);
+ if (0 != format_code)
+ printf("%s [Unexpected format code: %d]\n", leadin,
+ format_code);
+ dStrHex((const char *)&ucp[8], 16, 0);
+ bump = 24;
+ break;
+ case 5: /* iSCSI */
+ printf("%s iSCSI ", leadin);
+ num = ((ucp[2] << 8) | ucp[3]);
+ if (0 == format_code)
+ printf("name: %.*s\n", num, &ucp[4]);
+ else if (1 == format_code)
+ printf("world wide unique port id: %.*s\n", num, &ucp[4]);
+ else {
+ printf(" [Unexpected format code: %d]\n", format_code);
+ dStrHex((const char *)ucp, num + 4, 0);
+ }
+ bump = (((num + 4) < 24) ? 24 : num + 4);
+ break;
+ case 6: /* SAS */
+ ull = 0;
+ for (j = 0; j < 8; ++j) {
+ if (j > 0)
+ ull <<= 8;
+ ull |= ucp[4 + j];
+ }
+ printf("%s SAS address: 0x%llx\n", leadin, ull);
+ if (0 != format_code)
+ printf("%s [Unexpected format code: %d]\n", leadin,
+ format_code);
+ bump = 24;
+ break;
+ case 7: /* Automation/Drive Interface Transport Protocol */
+ printf("%s ADT:\n", leadin);
+ printf("%s format code: %d\n", leadin, format_code);
+ dStrHex((const char *)ucp, ((len > 24) ? 24 : len), 0);
+ bump = 24;
+ break;
+ case 8: /* ATAPI */
+ printf("%s ATAPI:\n", leadin);
+ printf("%s format code: %d\n", leadin, format_code);
+ dStrHex((const char *)ucp, ((len > 24) ? 24 : len), 0);
+ bump = 24;
+ break;
+ default:
+ fprintf(stderr, "%s unknown protocol id=0x%x "
+ "format_code=%d\n", leadin, proto_id, format_code);
+ dStrHex((const char *)ucp, ((len > 24) ? 24 : len), 0);
+ bump = 24;
+ break;
+ }
+ }
+}
+
+static void decode_x_inq_vpd(unsigned char * buff, int len, int do_hex)
+{
+ if (len < 7) {
+ fprintf(stderr, "Extended INQUIRY data VPD page length too "
+ "short=%d\n", len);
+ return;
+ }
+ if (do_hex) {
+ dStrHex((const char *)buff, len, 0);
+ return;
+ }
+ printf(" SPT=%d GRD_CHK=%d APP_CHK=%d REF_CHK=%d\n",
+ ((buff[4] >> 3) & 0x7), !!(buff[4] & 0x4), !!(buff[4] & 0x2),
+ !!(buff[4] & 0x1));
+ printf(" GRP_SUP=%d PRIOR_SUP=%d HEADSUP=%d ORDSUP=%d SIMPSUP=%d\n",
+ !!(buff[5] & 0x10), !!(buff[5] & 0x8), !!(buff[5] & 0x4),
+ !!(buff[5] & 0x2), !!(buff[5] & 0x1));
+ printf(" CORR_D_SUP=%d NV_SUP=%d V_SUP=%d\n", !!(buff[6] & 0x80),
+ !!(buff[6] & 0x2), !!(buff[6] & 0x1));
+}
+
+static void decode_softw_inf_id(unsigned char * buff, int len, int do_hex)
+{
+ int k;
+
+ if (do_hex) {
+ dStrHex((const char *)buff, len, 0);
+ return;
+ }
+ len -= 4;
+ buff += 4;
+ for ( ; len > 5; len -= 6, buff += 6) {
+ printf(" ");
+ for (k = 0; k < 6; ++k)
+ printf("%02x", (unsigned int)buff[k]);
+ printf("\n");
+ }
+}
+
+static void decode_ata_info_vpd(unsigned char * buff, int len, int do_long,
+ int do_hex)
+{
+ char b[80];
+ int num, is_be;
+ const char * cp;
+
+ if (len < 36) {
+ fprintf(stderr, "ATA information VPD page length too "
+ "short=%d\n", len);
+ return;
+ }
+ if (do_hex && (2 != do_hex)) {
+ dStrHex((const char *)buff, len, 0);
+ return;
+ }
+ memcpy(b, buff + 8, 8);
+ b[8] = '\0';
+ printf(" SAT Vendor identification: %s\n", b);
+ memcpy(b, buff + 16, 16);
+ b[16] = '\0';
+ printf(" SAT Product identification: %s\n", b);
+ memcpy(b, buff + 32, 4);
+ b[4] = '\0';
+ printf(" SAT Product revision level: %s\n", b);
+ if (len < 56)
+ return;
+ if (do_long) {
+ printf(" Signature (Device to host FIS):\n");
+ dStrHex((const char *)buff + 36, 20, 0);
+ }
+ if (len < 60)
+ return;
+ is_be = sg_is_big_endian();
+ if ((0xec == buff[56]) || (0xa1 == buff[56])) {
+ cp = (0xa1 == buff[56]) ? "PACKET " : "";
+ printf(" ATA command IDENTIFY %sDEVICE response summary:\n", cp);
+ num = sg_ata_get_chars((const unsigned short *)(buff + 60), 27, 20,
+ is_be, b);
+ b[num] = '\0';
+ printf(" model: %s\n", b);
+ num = sg_ata_get_chars((const unsigned short *)(buff + 60), 10, 10,
+ is_be, b);
+ b[num] = '\0';
+ printf(" serial number: %s\n", b);
+ num = sg_ata_get_chars((const unsigned short *)(buff + 60), 23, 4,
+ is_be, b);
+ b[num] = '\0';
+ printf(" firmware revision: %s\n", b);
+ if (do_long)
+ printf(" ATA command IDENTIFY %sDEVICE response in hex:\n", cp);
+ } else if (do_long)
+ printf(" ATA command 0x%x got following response:\n",
+ (unsigned int)buff[56]);
+ if (len < 572)
+ return;
+ if (2 == do_hex)
+ dStrHex((const char *)(buff + 60), 512, 0);
+ else if (do_long)
+ dWordHex((const unsigned short *)(buff + 60), 256, 0, is_be);
+}
+
+static void decode_b0_vpd(unsigned char * buff, int len, int do_hex, int pdt)
+{
+ unsigned int u;
+
+ if (do_hex) {
+ dStrHex((const char *)buff, len, 0);
+ return;
+ }
+ switch (pdt) {
+ case 0: case 4: case 7:
+ if (len < 16) {
+ fprintf(stderr, "Block limits VPD page length too "
+ "short=%d\n", len);
+ return;
+ }
+ u = (buff[6] << 8) | buff[7];
+ printf(" Optimal transfer length granularity: %u blocks\n", u);
+ u = (buff[8] << 24) | (buff[9] << 16) | (buff[10] << 8) |
+ buff[11];
+ printf(" Maximum transfer length: %u blocks\n", u);
+ u = (buff[12] << 24) | (buff[13] << 16) | (buff[14] << 8) |
+ buff[15];
+ printf(" Optimal transfer length: %u blocks\n", u);
+ break;
+ case 1: case 8:
+ printf(" WORM=%d\n", !!(buff[4] & 0x1));
+ break;
+ case 0x11:
+ default:
+ printf(" Unable to decode pdt=0x%x, in hex:\n", pdt);
+ dStrHex((const char *)buff, len, 0);
+ break;
+ }
+}
+
+/* Returns 0 if successful */
+static int svpd_unable_to_decode(int sg_fd, int num_vpd, int subvalue,
+ int do_hex, int do_raw, int do_long,
+ int do_quiet, int verbose)
+{
+ int len, t, res;
+
+ t = do_quiet; /* suppress warning */
+ if ((! do_hex) && (! do_raw))
+ printf("Only hex output supported\n");
+ if (!do_raw) {
+ if (subvalue)
+ printf("VPD page code=0x%.2x, subvalue=0x%.2x:\n", num_vpd,
+ subvalue);
+ else
+ printf("VPD page code=0x%.2x:\n", num_vpd);
+ }
+ res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, DEF_ALLOC_LEN,
+ 1, verbose);
+ if (0 == res) {
+ len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
+ if (num_vpd != rsp_buff[1]) {
+ fprintf(stderr, "invalid VPD response; probably a STANDARD "
+ "INQUIRY response\n");
+ return SG_LIB_CAT_MALFORMED;
+ }
+ if (len > MX_ALLOC_LEN) {
+ fprintf(stderr, "response length too long: %d > %d\n", len,
+ MX_ALLOC_LEN);
+ return SG_LIB_CAT_MALFORMED;
+ } else if (len > DEF_ALLOC_LEN) {
+ if (sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len, 1,
+ verbose))
+ return SG_LIB_CAT_OTHER;
+ }
+ if (do_raw)
+ dStrRaw((const char *)rsp_buff, len);
+ else
+ dStrHex((const char *)rsp_buff, len, (do_long ? 0 : 1));
+ return 0;
+ } else {
+ fprintf(stderr,
+ "fetching VPD page code=0x%.2x: failed\n", num_vpd);
+ return res;
+ }
+}
+
+/* Returns 0 if successful, else see sg_ll_inquiry() */
+static int svpd_decode_standard(int sg_fd, int num_vpd, int subvalue,
+ int do_hex, int do_raw, int do_long,
+ int do_quiet, int verbose)
+{
+ int len, pdt, num, k;
+ char buff[48];
+ const struct svpd_values_name_t * vnp;
+ int res = 0;
+
+ switch(num_vpd) {
+ case VPD_SUPPORTED_VPDS:
+ if ((! do_raw) && (! do_quiet))
+ printf("Supported VPD pages VPD page:\n");
+ res = sg_ll_inquiry(sg_fd, 0, 1, VPD_SUPPORTED_VPDS, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
+ len = rsp_buff[3] + 4;
+ if (VPD_SUPPORTED_VPDS != rsp_buff[1]) {
+ fprintf(stderr, "invalid VPD response; probably a STANDARD "
+ "INQUIRY response\n");
+ return SG_LIB_CAT_MALFORMED;
+ }
+ if (do_raw)
+ dStrRaw((const char *)rsp_buff, len);
+ else if (do_hex)
+ dStrHex((const char *)rsp_buff, len, 0);
+ else {
+ pdt = rsp_buff[0] & 0x1f;
+ if (verbose || do_long)
+ printf(" [PQual=%d Peripheral device type: %s]\n",
+ (rsp_buff[0] & 0xe0) >> 5,
+ sg_get_pdt_str(pdt, sizeof(buff), buff));
+ num = rsp_buff[3];
+ for (k = 0; k < num; ++k) {
+ vnp = sdp_get_vpd_detail(rsp_buff[4 + k], -1, pdt);
+ if (vnp)
+ printf(" %s [%s]\n", vnp->name, vnp->acron);
+ else
+ printf(" 0x%x\n", rsp_buff[4 + k]);
+ }
+ }
+ return 0;
+ }
+ break;
+ case VPD_UNIT_SERIAL_NUM:
+ if ((! do_raw) && (! do_quiet))
+ printf("Unit serial number VPD page:\n");
+ res = sg_ll_inquiry(sg_fd, 0, 1, VPD_UNIT_SERIAL_NUM, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
+ len = rsp_buff[3] + 4;
+ if (VPD_UNIT_SERIAL_NUM != rsp_buff[1]) {
+ fprintf(stderr, "invalid VPD response; probably a STANDARD "
+ "INQUIRY response\n");
+ return SG_LIB_CAT_MALFORMED;
+ }
+ if (do_raw)
+ dStrRaw((const char *)rsp_buff, len);
+ else {
+ char obuff[DEF_ALLOC_LEN];
+
+ pdt = rsp_buff[0] & 0x1f;
+ if (verbose || do_long)
+ printf(" [PQual=%d Peripheral device type: %s]\n",
+ (rsp_buff[0] & 0xe0) >> 5,
+ sg_get_pdt_str(pdt, sizeof(buff), buff));
+ memset(obuff, 0, sizeof(obuff));
+ len -= 4;
+ if (len >= (int)sizeof(obuff))
+ len = sizeof(obuff) - 1;
+ memcpy(obuff, rsp_buff + 4, len);
+ printf(" Unit serial number: %s\n", obuff);
+ }
+ return 0;
+ }
+ break;
+ case VPD_DEVICE_ID:
+ if ((! do_raw) && (! do_quiet))
+ printf("Device Identification VPD page:\n");
+ res = sg_ll_inquiry(sg_fd, 0, 1, VPD_DEVICE_ID, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
+ len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
+ if (VPD_DEVICE_ID != rsp_buff[1]) {
+ fprintf(stderr, "invalid VPD response; probably a STANDARD "
+ "INQUIRY response\n");
+ return SG_LIB_CAT_MALFORMED;
+ }
+ if (len > MX_ALLOC_LEN) {
+ fprintf(stderr, "response length too long: %d > %d\n", len,
+ MX_ALLOC_LEN);
+ return SG_LIB_CAT_MALFORMED;
+ } else if (len > DEF_ALLOC_LEN) {
+ if (sg_ll_inquiry(sg_fd, 0, 1, VPD_DEVICE_ID, rsp_buff, len,
+ 1, verbose))
+ return SG_LIB_CAT_OTHER;
+ }
+ if (do_raw)
+ dStrRaw((const char *)rsp_buff, len);
+ else if (do_hex)
+ dStrHex((const char *)rsp_buff, len, 0);
+ else {
+ pdt = rsp_buff[0] & 0x1f;
+ if (verbose || do_long)
+ printf(" [PQual=%d Peripheral device type: %s]\n",
+ (rsp_buff[0] & 0xe0) >> 5,
+ sg_get_pdt_str(pdt, sizeof(buff), buff));
+ decode_id_vpd(rsp_buff, len, subvalue, do_long, do_quiet);
+ }
+ return 0;
+ }
+ break;
+ case VPD_SOFTW_INF_ID:
+ if ((! do_raw) && (! do_quiet))
+ printf("Software interface identification VPD page:\n");
+ res = sg_ll_inquiry(sg_fd, 0, 1, VPD_SOFTW_INF_ID, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
+ len = rsp_buff[3] + 4;
+ if (VPD_SOFTW_INF_ID != rsp_buff[1]) {
+ fprintf(stderr, "invalid VPD response; probably a STANDARD "
+ "INQUIRY response\n");
+ return SG_LIB_CAT_MALFORMED;
+ }
+ if (do_raw)
+ dStrRaw((const char *)rsp_buff, len);
+ else {
+ pdt = rsp_buff[0] & 0x1f;
+ if (verbose || do_long)
+ printf(" [PQual=%d Peripheral device type: %s]\n",
+ (rsp_buff[0] & 0xe0) >> 5,
+ sg_get_pdt_str(pdt, sizeof(buff), buff));
+ decode_softw_inf_id(rsp_buff, len, do_hex);
+ }
+ return 0;
+ }
+ break;
+ case VPD_MAN_NET_ADDR:
+ if ((! do_raw) && (! do_quiet))
+ printf("Management network addresses VPD page:\n");
+ res = sg_ll_inquiry(sg_fd, 0, 1, VPD_MAN_NET_ADDR, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
+ len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
+ if (VPD_MAN_NET_ADDR != rsp_buff[1]) {
+ fprintf(stderr, "invalid VPD response; probably a STANDARD "
+ "INQUIRY response\n");
+ return SG_LIB_CAT_MALFORMED;
+ }
+ if (len > MX_ALLOC_LEN) {
+ fprintf(stderr, "response length too long: %d > %d\n", len,
+ MX_ALLOC_LEN);
+ return SG_LIB_CAT_MALFORMED;
+ } else if (len > DEF_ALLOC_LEN) {
+ if (sg_ll_inquiry(sg_fd, 0, 1, VPD_MAN_NET_ADDR, rsp_buff,
+ len, 1, verbose))
+ return SG_LIB_CAT_OTHER;
+ }
+ if (do_raw)
+ dStrRaw((const char *)rsp_buff, len);
+ else
+ decode_net_man_vpd(rsp_buff, len, do_hex);
+ return 0;
+ }
+ break;
+ case VPD_MODE_PG_POLICY:
+ if ((! do_raw) && (! do_quiet))
+ printf("Mode page VPD policy:\n");
+ res = sg_ll_inquiry(sg_fd, 0, 1, VPD_MODE_PG_POLICY, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
+ len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
+ if (VPD_MODE_PG_POLICY != rsp_buff[1]) {
+ fprintf(stderr, "invalid VPD response; probably a STANDARD "
+ "INQUIRY response\n");
+ return SG_LIB_CAT_MALFORMED;
+ }
+ if (len > MX_ALLOC_LEN) {
+ fprintf(stderr, "response length too long: %d > %d\n", len,
+ MX_ALLOC_LEN);
+ return SG_LIB_CAT_MALFORMED;
+ } else if (len > DEF_ALLOC_LEN) {
+ if (sg_ll_inquiry(sg_fd, 0, 1, VPD_MODE_PG_POLICY, rsp_buff,
+ len, 1, verbose))
+ return SG_LIB_CAT_OTHER;
+ }
+ if (do_raw)
+ dStrRaw((const char *)rsp_buff, len);
+ else {
+ pdt = rsp_buff[0] & 0x1f;
+ if (verbose || do_long)
+ printf(" [PQual=%d Peripheral device type: %s]\n",
+ (rsp_buff[0] & 0xe0) >> 5,
+ sg_get_pdt_str(pdt, sizeof(buff), buff));
+ decode_mode_policy_vpd(rsp_buff, len, do_hex);
+ }
+ return 0;
+ }
+ break;
+ case VPD_EXT_INQ:
+ if ((! do_raw) && (! do_quiet))
+ printf("extended INQUIRY data VPD page:\n");
+ res = sg_ll_inquiry(sg_fd, 0, 1, VPD_EXT_INQ, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
+ len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
+ if (VPD_EXT_INQ != rsp_buff[1]) {
+ fprintf(stderr, "invalid VPD response; probably a STANDARD "
+ "INQUIRY response\n");
+ return SG_LIB_CAT_MALFORMED;
+ }
+ if (len > MX_ALLOC_LEN) {
+ fprintf(stderr, "response length too long: %d > %d\n", len,
+ MX_ALLOC_LEN);
+ return SG_LIB_CAT_MALFORMED;
+ } else if (len > DEF_ALLOC_LEN) {
+ if (sg_ll_inquiry(sg_fd, 0, 1, VPD_EXT_INQ, rsp_buff, len,
+ 1, verbose))
+ return SG_LIB_CAT_OTHER;
+ }
+ if (do_raw)
+ dStrRaw((const char *)rsp_buff, len);
+ else {
+ pdt = rsp_buff[0] & 0x1f;
+ if (verbose || do_long)
+ printf(" [PQual=%d Peripheral device type: %s]\n",
+ (rsp_buff[0] & 0xe0) >> 5,
+ sg_get_pdt_str(pdt, sizeof(buff), buff));
+ decode_x_inq_vpd(rsp_buff, len, do_hex);
+ }
+ return 0;
+ }
+ break;
+ case VPD_ATA_INFO:
+ if ((! do_raw) && (! do_quiet))
+ printf("ATA information VPD page:\n");
+ res = sg_ll_inquiry(sg_fd, 0, 1, VPD_ATA_INFO, rsp_buff,
+ VPD_ATA_INFO_LEN, 1, verbose);
+ if (0 == res) {
+ len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
+ if (VPD_ATA_INFO != rsp_buff[1]) {
+ fprintf(stderr, "invalid VPD response; probably a STANDARD "
+ "INQUIRY response\n");
+ return SG_LIB_CAT_MALFORMED;
+ }
+ if (len > MX_ALLOC_LEN) {
+ fprintf(stderr, "response length too long: %d > %d\n", len,
+ MX_ALLOC_LEN);
+ return SG_LIB_CAT_MALFORMED;
+ } else if (len > VPD_ATA_INFO_LEN) {
+ if (sg_ll_inquiry(sg_fd, 0, 1, VPD_ATA_INFO, rsp_buff, len,
+ 1, verbose))
+ return SG_LIB_CAT_OTHER;
+ }
+ if ((2 == do_raw) || (3 == do_hex)) /* special for hdparm */
+ dWordHex((const unsigned short *)(rsp_buff + 60),
+ 256, -2, sg_is_big_endian());
+ else if (do_raw)
+ dStrRaw((const char *)rsp_buff, len);
+ else {
+ pdt = rsp_buff[0] & 0x1f;
+ if (verbose || do_long)
+ printf(" [PQual=%d Peripheral device type: %s]\n",
+ (rsp_buff[0] & 0xe0) >> 5,
+ sg_get_pdt_str(pdt, sizeof(buff), buff));
+ decode_ata_info_vpd(rsp_buff, len, do_long, do_hex);
+ }
+ return 0;
+ }
+ break;
+ case 0xb0: /* could be BLOCK LIMITS but need to know pdt to find out */
+ res = sg_ll_inquiry(sg_fd, 0, 1, 0xb0, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
+ pdt = rsp_buff[0] & 0x1f;
+ if ((! do_raw) && (! do_quiet)) {
+ switch (pdt) {
+ case 0: case 4: case 7:
+ printf("Block limits VPD page (SBC):\n");
+ break;
+ case 1: case 8:
+ printf("Sequential access device capabilities VPD page "
+ "(SSC):\n");
+ break;
+ case 0x11:
+ printf("OSD information (OSD) VPD page:\n");
+ break;
+ default:
+ printf("VPD page=0x%x, pdt=0x%x:\n", 0xb0, pdt);
+ break;
+ }
+ }
+ len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
+ if (0xb0 != rsp_buff[1]) {
+ fprintf(stderr, "invalid VPD response; probably a STANDARD "
+ "INQUIRY response\n");
+ return SG_LIB_CAT_MALFORMED;
+ }
+ if (len > MX_ALLOC_LEN) {
+ fprintf(stderr, "response length too long: %d > %d\n", len,
+ MX_ALLOC_LEN);
+ return SG_LIB_CAT_MALFORMED;
+ } else if (len > DEF_ALLOC_LEN) {
+ if (sg_ll_inquiry(sg_fd, 0, 1, 0xb0, rsp_buff,
+ len, 1, verbose))
+ return SG_LIB_CAT_OTHER;
+ }
+ if (do_raw)
+ dStrRaw((const char *)rsp_buff, len);
+ else {
+ pdt = rsp_buff[0] & 0x1f;
+ if (verbose || do_long)
+ printf(" [PQual=%d Peripheral device type: %s]\n",
+ (rsp_buff[0] & 0xe0) >> 5,
+ sg_get_pdt_str(pdt, sizeof(buff), buff));
+ decode_b0_vpd(rsp_buff, len, do_hex, pdt);
+ }
+ return 0;
+ } else if (! do_raw)
+ printf("VPD page=0xb0\n");
+ break;
+ case VPD_SCSI_PORTS:
+ if ((! do_raw) && (! do_quiet))
+ printf("SCSI Ports VPD page:\n");
+ res = sg_ll_inquiry(sg_fd, 0, 1, VPD_SCSI_PORTS, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
+ len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
+ if (VPD_SCSI_PORTS != rsp_buff[1]) {
+ fprintf(stderr, "invalid VPD response; probably a STANDARD "
+ "INQUIRY response\n");
+ return SG_LIB_CAT_MALFORMED;
+ }
+ if (len > MX_ALLOC_LEN) {
+ fprintf(stderr, "response length too long: %d > %d\n", len,
+ MX_ALLOC_LEN);
+ return SG_LIB_CAT_MALFORMED;
+ } else if (len > DEF_ALLOC_LEN) {
+ if (sg_ll_inquiry(sg_fd, 0, 1, VPD_SCSI_PORTS, rsp_buff, len,
+ 1, verbose))
+ return SG_LIB_CAT_OTHER;
+ }
+ if (do_raw)
+ dStrRaw((const char *)rsp_buff, len);
+ else {
+ pdt = rsp_buff[0] & 0x1f;
+ if (verbose || do_long)
+ printf(" [PQual=%d Peripheral device type: %s]\n",
+ (rsp_buff[0] & 0xe0) >> 5,
+ sg_get_pdt_str(pdt, sizeof(buff), buff));
+ decode_scsi_ports_vpd(rsp_buff, len, do_hex, do_long, do_quiet);
+ }
+ return 0;
+ }
+ break;
+ default:
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ return res;
+}
+
+
+int main(int argc, char * argv[])
+{
+ int sg_fd, c, res;
+ char device_name[256];
+ const struct svpd_values_name_t * vnp;
+ const char * page_str = NULL;
+ const char * cp;
+ int num_vpd = 0;
+ int do_hex = 0;
+ int do_ident = 0;
+ int do_long = 0;
+ int do_quiet = 0;
+ int do_raw = 0;
+ int do_verbose = 0;
+ int ret = 0;
+ int req_pdt = -1;
+ int subvalue = 0;
+
+ memset(device_name, 0, sizeof device_name);
+ while (1) {
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "ehHilp:qrvV", long_options,
+ &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'e':
+ printf("Standard VPD pages:\n");
+ enumerate_vpds(1, 1);
+ return 0;
+ case 'h':
+ case '?':
+ usage();
+ return 0;
+ case 'H':
+ ++do_hex;
+ break;
+ case 'i':
+ ++do_ident;
+ break;
+ case 'l':
+ ++do_long;
+ break;
+ case 'p':
+ if (page_str) {
+ fprintf(stderr, "only one '--page=' option permitted\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ } else
+ page_str = optarg;
+ break;
+ case 'q':
+ ++do_quiet;
+ break;
+ case 'r':
+ ++do_raw;
+ break;
+ case 'v':
+ ++do_verbose;
+ break;
+ case 'V':
+ fprintf(stderr, "version: %s\n", version_str);
+ return 0;
+ default:
+ fprintf(stderr, "unrecognised switch code 0x%x ??\n", c);
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+ 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 SG_LIB_SYNTAX_ERROR;
+ }
+ }
+ if (page_str) {
+ if (isalpha(page_str[0])) {
+ vnp = sdp_find_vpd_by_acron(page_str);
+ if (NULL == vnp) {
+ vnp = svpd_find_vendor_by_acron(page_str);
+ if (NULL == vnp) {
+ fprintf(stderr, "abbreviation doesn't match a VPD "
+ "page\n");
+ printf("available VPD pages:\n");
+ enumerate_vpds(1, 1);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+ num_vpd = vnp->value;
+ subvalue = vnp->subvalue;
+ req_pdt = vnp->pdt;
+ } else {
+ cp = strchr(page_str, ',');
+ num_vpd = get_num(page_str);
+ if ((num_vpd < 0) || (num_vpd > 255)) {
+ fprintf(stderr, "Bad page code value after '-p' "
+ "option\n");
+ printf("available VPD pages:\n");
+ enumerate_vpds(1, 1);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ if (cp) {
+ subvalue = get_num(cp + 1);
+ if ((subvalue < 0) || (subvalue > 255)) {
+ fprintf(stderr, "Bad subvalue code value after "
+ "'-p' option\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+ }
+ }
+
+ if (do_raw && do_hex) {
+ fprintf(stderr, "Can't do hex and raw at the same time\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ if (do_ident) {
+ num_vpd = VPD_DEVICE_ID;
+ req_pdt = -1;
+ if (do_ident > 1) {
+ if (0 == do_long)
+ ++do_quiet;
+ subvalue = VPD_DI_SEL_LU;
+ }
+ }
+ if ('\0' == device_name[0]) {
+ fprintf(stderr, "No <device> argument given\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+
+ if ((sg_fd = sg_cmds_open_device(device_name, 1 /* ro */,
+ do_verbose)) < 0) {
+ fprintf(stderr, "error opening file: %s: %s\n",
+ device_name, safe_strerror(-sg_fd));
+ return SG_LIB_FILE_ERROR;
+ }
+ memset(rsp_buff, 0, sizeof(rsp_buff));
+
+ res = svpd_decode_standard(sg_fd, num_vpd, subvalue, do_hex, do_raw,
+ do_long, do_quiet, do_verbose);
+ if (SG_LIB_SYNTAX_ERROR == res) {
+ res = svpd_decode_vendor(sg_fd, num_vpd, subvalue, do_hex, do_raw,
+ do_long, do_quiet, do_verbose);
+ if (SG_LIB_SYNTAX_ERROR == res)
+ res = svpd_unable_to_decode(sg_fd, num_vpd, subvalue, do_hex,
+ do_raw, do_long, do_quiet,
+ do_verbose);
+ }
+ ret = res;
+ res = sg_cmds_close_device(sg_fd);
+ if (res < 0) {
+ fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ return SG_LIB_FILE_ERROR;
+ }
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg_vpd_vendor.c b/sg_vpd_vendor.c
new file mode 100644
index 00000000..41cd70df
--- /dev/null
+++ b/sg_vpd_vendor.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright (c) 2006 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>
+
+#include "sg_lib.h"
+#include "sg_cmds.h"
+
+/* This is a companion file to sg_vpd.c . It contains logic to output and
+ decode vendor specific VPD pages
+
+ This program fetches Vital Product Data (VPD) pages from the given
+ device and outputs it as directed. VPD pages are obtained via a
+ SCSI INQUIRY command. Most of the data in this program is obtained
+ from the SCSI SPC-4 document at http://www.t10.org .
+
+ Acknowledgments:
+ - Lars Marowsky-Bree <lmb at suse dot de> contributed Unit Path Report
+ VPD page decoding for EMC CLARiiON devices [20041016]
+ - Hannes Reinecke <hare at suse dot de> contributed RDAC vendor
+ specific VPD pages [20060421]
+*/
+
+
+/* vendor VPD pages */
+#define VPD_V_FIRM_SEA 0xc0
+#define VPD_V_UPR_EMC 0xc0
+#define VPD_V_DATC_SEA 0xc1
+#define VPD_V_JUMP_SEA 0xc2
+#define VPD_V_SVER_RDAC 0xc2
+#define VPD_V_DEV_BEH_SEA 0xc3
+#define VPD_V_VAC_RDAC 0xc9
+
+
+#define DEF_ALLOC_LEN 252
+#define MX_ALLOC_LEN (0xc000 + 0x80)
+#define VPD_ATA_INFO_LEN 572
+
+/* This structure is a duplicate of one of the same name in sg_vpd.c .
+ Take care that both have the same fields (and types). */
+struct svpd_values_name_t {
+ int value;
+ int subvalue;
+ int pdt; /* peripheral device type id, -1 is the default */
+ /* (all or not applicable) value */
+ int ro_vendor; /* read-only or vendor flag */
+ const char * acron;
+ const char * name;
+};
+
+
+static unsigned char rsp_buff[MX_ALLOC_LEN + 2];
+
+
+/* Use subvalue to disambiguate between vendors */
+static struct svpd_values_name_t vendor_vpd_pg[] = {
+ {VPD_V_DATC_SEA, 0, -1, 1, "datc", "Date code (Seagate)"},
+ {VPD_V_DEV_BEH_SEA, 0, -1, 1, "devb", "Device behavior (Seagate)"},
+ {VPD_V_FIRM_SEA, 0, -1, 1, "firm", "Firmware numbers (Seagate)"},
+ {VPD_V_JUMP_SEA, 0, -1, 1, "jump", "Jump setting (Seagate)"},
+ {VPD_V_SVER_RDAC, 1, -1, 1, "sver", "Software version (RDAC)"},
+ {VPD_V_UPR_EMC, 1, -1, 1, "upr", "Unit path report (EMC)"},
+ {VPD_V_VAC_RDAC, 0, -1, 1, "vac", "Volume access control (RDAC)"},
+ {0, 0, 0, 0, NULL, NULL},
+};
+
+static const struct svpd_values_name_t *
+ svpd_get_v_detail(int page_num, int subvalue, int pdt)
+{
+ const struct svpd_values_name_t * vnp;
+ int sv, ty;
+
+ sv = (subvalue < 0) ? 1 : 0;
+ ty = (pdt < 0) ? 1 : 0;
+ for (vnp = vendor_vpd_pg; vnp->acron; ++vnp) {
+ if ((page_num == vnp->value) &&
+ (sv || (subvalue == vnp->subvalue)) &&
+ (ty || (pdt == vnp->pdt)))
+ return vnp;
+ }
+ if (! ty)
+ return svpd_get_v_detail(page_num, subvalue, -1);
+ if (! sv)
+ return svpd_get_v_detail(page_num, -1, -1);
+ return NULL;
+}
+
+const struct svpd_values_name_t *
+ svpd_find_vendor_by_acron(const char * ap)
+{
+ const struct svpd_values_name_t * vnp;
+
+ for (vnp = vendor_vpd_pg; vnp->acron; ++vnp) {
+ if (0 == strcmp(vnp->acron, ap))
+ return vnp;
+ }
+ return NULL;
+}
+
+void svpd_enumerate_vendor()
+{
+ const struct svpd_values_name_t * vnp;
+ int seen;
+
+ for (seen = 0, vnp = vendor_vpd_pg; vnp->acron; ++vnp) {
+ if (vnp->name) {
+ if (! seen) {
+ printf("\nVendor specific VPD pages:\n");
+ seen = 1;
+ }
+ printf(" %-10s 0x%02x,%d %s\n", vnp->acron,
+ vnp->value, vnp->subvalue, vnp->name);
+ }
+ }
+}
+
+static void dStrRaw(const char* str, int len)
+{
+ int k;
+
+ for (k = 0 ; k < len; ++k)
+ printf("%c", str[k]);
+}
+
+static const char * lun_state_arr[] =
+{
+ "LUN not bound or LUN_Z report",
+ "LUN bound, but not owned by this SP",
+ "LUN bound and owned by this SP",
+};
+
+static const char * ip_mgmt_arr[] =
+{
+ "No IP access",
+ "Reserved (undefined)",
+ "via IPv4",
+ "via IPv6",
+};
+
+static const char * sp_arr[] =
+{
+ "SP A",
+ "SP B",
+};
+
+static const char * lun_op_arr[] =
+{
+ "Normal operations",
+ "I/O Operations being rejected, SP reboot or NDU in progress",
+};
+
+static void decode_firm_vpd_c0_sea(unsigned char * buff, int len)
+{
+ if (len < 28) {
+ fprintf(stderr, "Seagate firmware numbers VPD page length too "
+ "short=%d\n", len);
+ return;
+ }
+ if (28 == len) {
+ printf(" SCSI firmware release number: %.8s\n", buff + 4);
+ printf(" Servo ROM release number: %.8s\n", buff + 20);
+ } else {
+ printf(" SCSI firmware release number: %.8s\n", buff + 4);
+ printf(" Servo ROM release number: %.8s\n", buff + 12);
+ printf(" SAP block point numbers (major/minor): %.8s\n", buff + 20);
+ if (len < 36)
+ return;
+ printf(" Servo firmware release date: %.4s\n", buff + 28);
+ printf(" Servo ROM release date: %.4s\n", buff + 32);
+ if (len < 44)
+ return;
+ printf(" SAP firmware release number: %.8s\n", buff + 36);
+ if (len < 52)
+ return;
+ printf(" SAP firmware release date: %.4s\n", buff + 44);
+ printf(" SAP firmware release year: %.4s\n", buff + 48);
+ if (len < 60)
+ return;
+ printf(" SAP manufacturing key: %.4s\n", buff + 52);
+ printf(" Servo firmware product family and product family "
+ "member: %.4s\n", buff + 56);
+ }
+}
+
+static void decode_upr_vpd_c0_emc(unsigned char * buff, int len)
+{
+ int k, ip_mgmt, failover_mode, vpp80, lun_z;
+
+ if (len < 3) {
+ fprintf(stderr, "EMC upr VPD page length too "
+ "short=%d\n", len);
+ return;
+ }
+ if (buff[9] != 0x00) {
+ fprintf(stderr, "Unsupported page revision %d, decoding not "
+ "possible.\n" , buff[9]);
+ return;
+ }
+ printf(" LUN WWN: ");
+ for (k = 0; k < 16; ++k)
+ printf("%02hhx", buff[10 + k]);
+ printf("\n");
+ printf(" Array Serial Number: ");
+ dStrRaw((const char *)&buff[50], buff[49]);
+ printf("\n");
+
+ printf(" LUN State: ");
+ if (buff[4] > 0x02)
+ printf("Unknown (%hhx)\n", buff[4]);
+ else
+ printf("%s\n", lun_state_arr[buff[4]]);
+
+ printf(" This path connects to: ");
+ if (buff[8] > 0x01)
+ printf("Unknown SP (%hhx)", buff[8]);
+ else
+ printf("%s", sp_arr[buff[8]]);
+ printf(", Port Number: %u\n", buff[7]);
+
+ printf(" Default Owner: ");
+ if (buff[5] > 0x01)
+ printf("Unknown (%hhx)\n", buff[5]);
+ else
+ printf("%s\n", sp_arr[buff[5]]);
+
+ printf(" NO_ATF: %s, Access Logix: %s\n",
+ buff[6] & 0x80 ? "set" : "not set",
+ buff[6] & 0x40 ? "supported" : "not supported");
+
+ ip_mgmt = (buff[6] >> 4) & 0x3;
+
+ printf(" SP IP Management Mode: %s\n", ip_mgmt_arr[ip_mgmt]);
+ if (ip_mgmt == 2)
+ printf(" SP IPv4 address: %u.%u.%u.%u\n",
+ buff[44], buff[45], buff[46], buff[47]);
+ else {
+ printf(" SP IPv6 address: ");
+ for (k = 0; k < 16; ++k)
+ printf("%02hhx", buff[32 + k]);
+ printf("\n");
+ }
+
+ failover_mode = buff[28] & 0x0f;
+ vpp80 = buff[30] & 0x08;
+ lun_z = buff[30] & 0x04;
+
+ printf(" System Type: %hhx, Failover mode: %s\n",
+ buff[27],
+ failover_mode == 4 ? "Set to 1" : "Unknown");
+
+ printf(" Inquiry VPP 0x80 returns: %s, Arraycommpath: %s\n",
+ vpp80 ? "array serial#" : "LUN serial#",
+ lun_z ? "Set to 1" : "Unknown");
+
+ printf(" Lun operations: %s\n",
+ buff[48] > 1 ? "undefined" : lun_op_arr[buff[48]]);
+
+ return;
+}
+
+static void decode_rdac_vpd_c2(unsigned char * buff, int len)
+{
+ if (len < 3) {
+ fprintf(stderr, "Software Version VPD page length too "
+ "short=%d\n", len);
+ return;
+ }
+ if (buff[4] != 's' && buff[5] != 'w' && buff[6] != 'r') {
+ fprintf(stderr, "Invalid page identifier %c%c%c%c, decoding "
+ "not possible.\n" , buff[4], buff[5], buff[6], buff[7]);
+ return;
+ }
+ printf(" Software Version: %d.%d.%d\n", buff[8], buff[9], buff[10]);
+ printf(" Software Date: %02x/%02x/%02x\n", buff[11], buff[12], buff[13]);
+ printf(" Features:");
+ if (buff[14] & 0x01)
+ printf(" Dual Active,");
+ if (buff[14] & 0x02)
+ printf(" Series 3,");
+ if (buff[14] & 0x04)
+ printf(" Multiple Sub-enclosures,");
+ if (buff[14] & 0x08)
+ printf(" DCE/DRM,");
+ if (buff[14] & 0x10)
+ printf(" AVT,");
+ printf("\n");
+ printf(" Max. #of LUNS: %d\n", buff[15]);
+ return;
+}
+
+static void decode_rdac_vpd_c9(unsigned char * buff, int len)
+{
+ if (len < 3) {
+ fprintf(stderr, "Volume Access Control VPD page length too "
+ "short=%d\n", len);
+ return;
+ }
+ if (buff[4] != 'v' && buff[5] != 'a' && buff[6] != 'c') {
+ fprintf(stderr, "Invalid page identifier %c%c%c%c, decoding "
+ "not possible.\n" , buff[4], buff[5], buff[6], buff[7]);
+ return;
+ }
+ if (buff[7] != '1') {
+ fprintf(stderr, "Invalid page version '%c' (should be 1)\n",
+ buff[7]);
+ }
+ printf(" AVT:");
+ if (buff[8] & 0x80) {
+ printf(" Enabled");
+ if (buff[8] & 0x40)
+ printf(" (Allow reads on sector 0)");
+ printf("\n");
+ } else {
+ printf(" Disabled\n");
+ }
+ printf(" Volume Access via: ");
+ if (buff[8] & 0x01)
+ printf("primary controller\n");
+ else
+ printf("alternate controller\n");
+
+ printf(" Path priority: %d ", buff[9] & 0xf);
+ switch(buff[9] & 0xf) {
+ case 0x1:
+ printf("(preferred path)\n");
+ break;
+ case 0x2:
+ printf("(secondary path)\n");
+ break;
+ default:
+ printf("(unknown)\n");
+ break;
+ }
+
+ return;
+}
+
+/* Returns 0 if successful, see sg_ll_inquiry() plus SG_LIB_SYNTAX_ERROR for
+ unsupported page */
+int svpd_decode_vendor(int sg_fd, int num_vpd, int subvalue, int do_hex,
+ int do_raw, int do_long, int do_quiet, int verbose)
+{
+ int len, t, res;
+ char name[64];
+ const struct svpd_values_name_t * vnp;
+
+ t = do_long; /* suppress warning */
+ vnp = svpd_get_v_detail(num_vpd, subvalue, -1);
+ if (vnp && vnp->name)
+ strcpy(name, vnp->name);
+ else
+ snprintf(name, sizeof(name) - 1, "Vendor VPD page=0x%x", num_vpd);
+ switch(num_vpd) {
+ case 0xc0:
+ if ((! do_raw) && (! do_quiet))
+ printf("%s VPD Page:\n", name);
+ res = sg_ll_inquiry(sg_fd, 0, 1, VPD_V_UPR_EMC, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
+ len = rsp_buff[3] + 4;
+ if (VPD_V_UPR_EMC != rsp_buff[1]) {
+ fprintf(stderr, "invalid VPD response; probably not "
+ "supported\n");
+ return SG_LIB_CAT_MALFORMED;
+ }
+ if (len > MX_ALLOC_LEN) {
+ fprintf(stderr, "response length too long: %d > %d\n", len,
+ MX_ALLOC_LEN);
+ return SG_LIB_CAT_MALFORMED;
+ } else if (len > DEF_ALLOC_LEN) {
+ if (sg_ll_inquiry(sg_fd, 0, 1, VPD_V_UPR_EMC, rsp_buff, len, 1,
+ verbose))
+ return SG_LIB_CAT_OTHER;
+ }
+ if (do_raw)
+ dStrRaw((const char *)rsp_buff, len);
+ else if (do_hex)
+ dStrHex((const char *)rsp_buff, len, 0);
+ else if (0 == subvalue)
+ decode_firm_vpd_c0_sea(rsp_buff, len);
+ else if (1 == subvalue)
+ decode_upr_vpd_c0_emc(rsp_buff, len);
+ else
+ dStrHex((const char *)rsp_buff, len, 0);
+ return 0;
+ }
+ break;
+ case VPD_V_SVER_RDAC:
+ if ((! do_raw) && (! do_quiet))
+ printf("%s VPD Page:\n", name);
+ res = sg_ll_inquiry(sg_fd, 0, 1, VPD_V_SVER_RDAC, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
+ len = rsp_buff[3] + 4;
+ if (VPD_V_SVER_RDAC != rsp_buff[1]) {
+ fprintf(stderr, "invalid VPD response; probably not "
+ "supported\n");
+ return SG_LIB_CAT_MALFORMED;
+ }
+ if (len > MX_ALLOC_LEN) {
+ fprintf(stderr, "response length too long: %d > %d\n", len,
+ MX_ALLOC_LEN);
+ return SG_LIB_CAT_MALFORMED;
+ } else if (len > DEF_ALLOC_LEN) {
+ if (sg_ll_inquiry(sg_fd, 0, 1, VPD_V_SVER_RDAC, rsp_buff, len, 1,
+ verbose))
+ return SG_LIB_CAT_OTHER;
+ }
+ if (do_raw)
+ dStrRaw((const char *)rsp_buff, len);
+ else if (do_hex)
+ dStrHex((const char *)rsp_buff, len, 0);
+ else if (1 == subvalue)
+ decode_rdac_vpd_c2(rsp_buff, len);
+ else
+ dStrHex((const char *)rsp_buff, len, 0);
+ return 0;
+ }
+ break;
+ case VPD_V_VAC_RDAC:
+ if ((! do_raw) && (! do_quiet))
+ printf("%s VPD Page:\n", name);
+ res = sg_ll_inquiry(sg_fd, 0, 1, VPD_V_VAC_RDAC, rsp_buff,
+ DEF_ALLOC_LEN, 1, verbose);
+ if (0 == res) {
+ len = rsp_buff[3] + 4;
+ if (VPD_V_VAC_RDAC != rsp_buff[1]) {
+ fprintf(stderr, "invalid VPD response; probably not "
+ "supported\n");
+ return SG_LIB_CAT_MALFORMED;
+ }
+ if (len > MX_ALLOC_LEN) {
+ fprintf(stderr, "response length too long: %d > %d\n", len,
+ MX_ALLOC_LEN);
+ return SG_LIB_CAT_MALFORMED;
+ } else if (len > DEF_ALLOC_LEN) {
+ if (sg_ll_inquiry(sg_fd, 0, 1, VPD_V_VAC_RDAC, rsp_buff, len, 1,
+ verbose))
+ return SG_LIB_CAT_OTHER;
+ }
+ if (do_raw)
+ dStrRaw((const char *)rsp_buff, len);
+ else if (do_hex)
+ dStrHex((const char *)rsp_buff, len, 0);
+ else if (0 == subvalue)
+ decode_rdac_vpd_c9(rsp_buff, len);
+ else
+ dStrHex((const char *)rsp_buff, len, 0);
+ return 0;
+ }
+ break;
+ default:
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ return res;
+}
diff --git a/sg_wr_mode.8 b/sg_wr_mode.8
index f66de061..54ccb657 100644
--- a/sg_wr_mode.8
+++ b/sg_wr_mode.8
@@ -1,4 +1,4 @@
-.TH SG_WR_MODE "8" "June 2005" "sg3_utils-1.15" SG3_UTILS
+.TH SG_WR_MODE "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_wr_mode \- write mode page to given device
.SH SYNOPSIS
@@ -173,12 +173,15 @@ after the next power cycle. The output from sg_modes is abridged.
>> Power condition (mmc), page_control: current
.br
00 1a 0a 00 03 00 00 00 37 00 00 01 2c
+.SH EXIT STATUS
+The exit status of sg_wr_mode is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHORS
Written by Douglas Gilbert.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2004 Douglas Gilbert
+Copyright \(co 2004-2006 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.
diff --git a/sg_wr_mode.c b/sg_wr_mode.c
index 4e047ae5..1aac193c 100644
--- a/sg_wr_mode.c
+++ b/sg_wr_mode.c
@@ -43,7 +43,7 @@
* mode page on the given device.
*/
-static char * version_str = "1.05 20060106";
+static char * version_str = "1.06 20060623";
#define ME "sg_wr_mode: "
@@ -296,7 +296,7 @@ int main(int argc, char * argv[])
unsigned char ref_md[MX_ALLOC_LEN];
char ebuff[EBUFF_SZ];
struct sg_simple_inquiry_resp inq_data;
- int ret = 1;
+ int ret = 0;
memset(device_name, 0, sizeof device_name);
while (1) {
@@ -313,7 +313,7 @@ int main(int argc, char * argv[])
if (0 != build_mode_page(optarg, read_in, &read_in_len,
sizeof(read_in))) {
fprintf(stderr, "bad argument to '--contents'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
got_contents = 1;
break;
@@ -333,7 +333,7 @@ int main(int argc, char * argv[])
mode_6 = (6 == res) ? 1 : 0;
else {
fprintf(stderr, "length (of cdb) must be 6 or 10\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 'm':
@@ -341,7 +341,7 @@ int main(int argc, char * argv[])
if (0 != build_mask(optarg, mask_in, &mask_in_len,
sizeof(mask_in))) {
fprintf(stderr, "bad argument to '--mask'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
got_mask = 1;
break;
@@ -351,21 +351,21 @@ int main(int argc, char * argv[])
if ((1 != num) || (u > 62)) {
fprintf(stderr, "Bad page code value after '--page' "
"switch\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
pg_code = u;
} else if (2 == sscanf(optarg, "%x,%x", &u, &uu)) {
if (uu > 254) {
fprintf(stderr, "Bad sub page code value after '--page'"
" switch\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
pg_code = u;
sub_pg_code = uu;
} else {
fprintf(stderr, "Bad page code, subpage code sequence after "
"'--page' switch\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 's':
@@ -380,7 +380,7 @@ int main(int argc, char * argv[])
default:
fprintf(stderr, "unrecognised switch code 0x%x ??\n", c);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (optind < argc) {
@@ -394,30 +394,30 @@ int main(int argc, char * argv[])
fprintf(stderr, "Unexpected extra argument: %s\n",
argv[optind]);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (0 == device_name[0]) {
fprintf(stderr, "missing device name!\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (pg_code < 0) {
fprintf(stderr, "need page code (see '--page=')\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (got_mask && force) {
fprintf(stderr, "cannot use both '--force' and '--mask'\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
if (sg_fd < 0) {
fprintf(stderr, ME "open error: %s: %s\n", device_name,
safe_strerror(-sg_fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (0 == sg_simple_inquiry(sg_fd, &inq_data, 0, verbose))
pdt = inq_data.peripheral_type;
@@ -434,10 +434,19 @@ int main(int argc, char * argv[])
res = sg_ll_mode_sense10(sg_fd, 0 /* llbaa */, dbd, 0 /* current */,
pg_code, sub_pg_code, ref_md, alloc_len, 1,
verbose);
+ ret = res;
if (SG_LIB_CAT_INVALID_OP == res) {
fprintf(stderr, "MODE SENSE (%d) not supported, try '--len=%d'\n",
(mode_6 ? 6 : 10), (mode_6 ? 10 : 6));
goto err_out;
+ } else if (SG_LIB_CAT_NOT_READY == res) {
+ fprintf(stderr, "MODE SENSE (%d) failed, device not ready\n",
+ (mode_6 ? 6 : 10));
+ goto err_out;
+ } else if (SG_LIB_CAT_UNIT_ATTENTION == res) {
+ fprintf(stderr, "MODE SENSE (%d) failed, unit attention\n",
+ (mode_6 ? 6 : 10));
+ goto err_out;
} else if (SG_LIB_CAT_ILLEGAL_REQ == res) {
fprintf(stderr, "bad field in MODE SENSE (%d) command\n",
(mode_6 ? 6 : 10));
@@ -523,10 +532,19 @@ int main(int argc, char * argv[])
else
res = sg_ll_mode_select10(sg_fd, 1, save, ref_md, md_len, 1,
verbose);
+ ret = res;
if (SG_LIB_CAT_INVALID_OP == res) {
fprintf(stderr, "MODE SELECT (%d) not supported\n",
(mode_6 ? 6 : 10));
goto err_out;
+ } else if (SG_LIB_CAT_NOT_READY == res) {
+ fprintf(stderr, "MODE SELECT (%d) failed, device not ready\n",
+ (mode_6 ? 6 : 10));
+ goto err_out;
+ } else if (SG_LIB_CAT_UNIT_ATTENTION == res) {
+ fprintf(stderr, "MODE SELECT (%d) failed, unit attention\n",
+ (mode_6 ? 6 : 10));
+ goto err_out;
} else if (SG_LIB_CAT_ILLEGAL_REQ == res) {
fprintf(stderr, "bad field in MODE SELECT (%d) command\n",
(mode_6 ? 6 : 10));
@@ -547,12 +565,12 @@ int main(int argc, char * argv[])
printf(" mode page:\n");
dStrHex((const char *)(ref_md + off), md_len - off, -1);
}
- ret = 0;
err_out:
res = sg_cmds_close_device(sg_fd);
if (res < 0) {
- fprintf(stderr, ME "close error: %s\n", safe_strerror(-res));
- return 1;
+ fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ return SG_LIB_FILE_ERROR;
}
- return ret;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sg_write_long.8 b/sg_write_long.8
index 4c9018db..8701665b 100644
--- a/sg_write_long.8
+++ b/sg_write_long.8
@@ -1,4 +1,4 @@
-.TH SG_WRITE_LONG "8" "Janauary 2006" "sg3_utils-1.19" SG3_UTILS
+.TH SG_WRITE_LONG "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sg_write_long \- send the SCSI command write long
.SH SYNOPSIS
@@ -96,6 +96,9 @@ MEDIUM ERROR since the ECC and associated data will be well formed.
The 10 byte WRITE LONG SCSI command is implemented (not the 16
byte variant). This limits the logical block address to a 32 bit
quantity.
+.SH EXIT STATUS
+The exit status of sg_write_long is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
.SH AUTHORS
Written by Saeed Bishara. Further work by Douglas Gilbert.
.SH "REPORTING BUGS"
diff --git a/sg_write_long.c b/sg_write_long.c
index 6b863811..2011af33 100644
--- a/sg_write_long.c
+++ b/sg_write_long.c
@@ -25,7 +25,7 @@
This code was contributed by Saeed Bishara
*/
-static char * version_str = "1.09 20060106";
+static char * version_str = "1.10 20060623";
#define MAX_XFER_LEN 10000
@@ -112,7 +112,7 @@ int main(int argc, char * argv[])
lba = sg_get_num(optarg);
if ((unsigned long)(-1) == lba) {
fprintf(stderr, "bad argument to '--lba'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
case 'v':
@@ -125,13 +125,13 @@ int main(int argc, char * argv[])
xfer_len = sg_get_num(optarg);
if (-1 == xfer_len) {
fprintf(stderr, "bad argument to '--xfer_len'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
break;
default:
fprintf(stderr, "unrecognised switch code 0x%x ??\n", c);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (optind < argc) {
@@ -145,32 +145,32 @@ int main(int argc, char * argv[])
fprintf(stderr, "Unexpected extra argument: %s\n",
argv[optind]);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (0 == device_name[0]) {
fprintf(stderr, "missing device name!\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (xfer_len >= MAX_XFER_LEN){
fprintf(stderr, "xfer_len (%d) is out of range ( < %d)\n",
xfer_len, MAX_XFER_LEN);
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
if (sg_fd < 0) {
fprintf(stderr, ME "open error: %s: %s\n", device_name,
safe_strerror(-sg_fd));
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (NULL == (rawp = malloc(MAX_XFER_LEN))) {
fprintf(stderr, ME "out of memory (query)\n");
sg_cmds_close_device(sg_fd);
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
writeLongBuff = rawp;
memset(rawp, 0xff, MAX_XFER_LEN);
@@ -207,26 +207,28 @@ int main(int argc, char * argv[])
res = sg_ll_write_long10(sg_fd, cor_dis, lba, writeLongBuff, xfer_len,
&offset, 1, verbose);
+ ret = res;
switch (res) {
case 0:
- ret = 0;
+ break;
+ case SG_LIB_CAT_NOT_READY:
+ fprintf(stderr, " SCSI WRITE LONG (10) failed, device not ready\n");
+ break;
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ fprintf(stderr, " SCSI WRITE LONG (10), unit attention\n");
break;
case SG_LIB_CAT_INVALID_OP:
fprintf(stderr, " SCSI WRITE LONG (10) command not supported\n");
- ret = 1;
break;
case SG_LIB_CAT_ILLEGAL_REQ:
fprintf(stderr, " SCSI WRITE LONG (10) command, bad field in cdb\n");
- ret = 1;
break;
case SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO:
fprintf(stderr, "<<< device indicates 'xfer_len' should be %d "
">>>\n", xfer_len - offset);
- ret = 1;
break;
default:
fprintf(stderr, " SCSI WRITE LONG (10) command error\n");
- ret = 1;
break;
}
@@ -235,8 +237,9 @@ err_out:
free(rawp);
res = sg_cmds_close_device(sg_fd);
if (res < 0) {
- fprintf(stderr, ME "close error: %s\n", safe_strerror(-sg_fd));
- return 1;
+ fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ return SG_LIB_FILE_ERROR;
}
- return ret;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sgm_dd.8 b/sgm_dd.8
index 579957d6..307e3c6b 100644
--- a/sgm_dd.8
+++ b/sgm_dd.8
@@ -1,4 +1,4 @@
-.TH SGM_DD "8" "March 2006" "sg3_utils-1.20" SG3_UTILS
+.TH SGM_DD "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sgm_dd \- copies data to and from files and devices. Specialized for
devices that understand the SCSI command set and does memory mapped
@@ -234,6 +234,12 @@ SIGPIPE output the number of remaining blocks to be transferred and
the records in + out counts; then they have their default action.
SIGUSR1 causes the same information to be output yet the copy continues.
All output caused by signals is sent to stderr.
+.SH EXIT STATUS
+The exit status of sgm_dd is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page. Since this utility works at a higher level
+than individual commands, and there are 'coe' and 'retries' flags,
+individual SCSI command failures do not necessary cause the process
+to exit.
.SH AUTHORS
Written by Doug Gilbert and Peter Allworth.
.SH "REPORTING BUGS"
diff --git a/sgm_dd.c b/sgm_dd.c
index dc11f003..6f3210e5 100644
--- a/sgm_dd.c
+++ b/sgm_dd.c
@@ -54,7 +54,7 @@
This version is designed for the linux kernel 2.4 and 2.6 series.
*/
-static char * version_str = "1.24 20060405";
+static char * version_str = "1.26 20060625";
#define DEF_BLOCK_SIZE 512
#define DEF_BLOCKS_PER_TRANSFER 128
@@ -238,9 +238,7 @@ void usage()
" --version print version information then exit\n");
}
-/* Return of 0 -> success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
- * SG_LIB_CAT_MEDIA_CHANGED -> media changed, SG_LIB_CAT_ILLEGAL_REQ
- * -> bad field in cdb, -1 -> other failure */
+/* Return of 0 -> success, see sg_ll_read_capacity*() otherwise */
int scsi_read_capacity(int sg_fd, long long * num_sect, int * sect_sz)
{
int k, res;
@@ -419,8 +417,9 @@ int sg_build_scsi_cdb(unsigned char * cdbp, int cdb_sz, unsigned int blocks,
return 0;
}
-/* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM),
- 2 -> try again */
+/* 0 -> successful, SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_SYNTAX_ERROR,
+ * SG_LIB_CAT_NOT_READY, SG_LIB_CAT_MEDIUM_HARD, SG_LIB_CAT_ILLEGAL_REQ,
+ * -2 -> recoverable (ENOMEM), -1 -> unrecoverable error */
int sg_read(int sg_fd, unsigned char * buff, int blocks, long long from_block,
int bs, int cdbsz, int fua, int dpo, int do_mmap)
{
@@ -432,7 +431,7 @@ int sg_read(int sg_fd, unsigned char * buff, int blocks, long long from_block,
if (sg_build_scsi_cdb(rdCmd, cdbsz, blocks, from_block, 0, fua, dpo)) {
fprintf(stderr, ME "bad rd cdb build, from_block=%lld, blocks=%d\n",
from_block, blocks);
- return -1;
+ return SG_LIB_SYNTAX_ERROR;
}
memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
io_hdr.interface_id = 'S';
@@ -468,7 +467,7 @@ int sg_read(int sg_fd, unsigned char * buff, int blocks, long long from_block,
;
if (res < 0) {
if (ENOMEM == errno)
- return 1;
+ return -2;
perror("reading (wr) on sg device, error");
return -1;
}
@@ -483,17 +482,21 @@ int sg_read(int sg_fd, unsigned char * buff, int blocks, long long from_block,
#endif
if (verbose > 2)
fprintf(stderr, " duration=%u ms\n", io_hdr.duration);
- switch (sg_err_category3(&io_hdr)) {
+ res = sg_err_category3(&io_hdr);
+ switch (res) {
case SG_LIB_CAT_CLEAN:
break;
case SG_LIB_CAT_RECOVERED:
sg_chk_n_print3("Reading, continuing", &io_hdr, verbose > 1);
break;
- case SG_LIB_CAT_MEDIA_CHANGED:
- return 2;
+ case SG_LIB_CAT_NOT_READY:
+ case SG_LIB_CAT_MEDIUM_HARD:
+ return res;
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ case SG_LIB_CAT_ILLEGAL_REQ:
default:
sg_chk_n_print3("reading", &io_hdr, verbose > 1);
- return -1;
+ return res;
}
sum_of_resids += io_hdr.resid;
#ifdef SG_DEBUG
@@ -502,8 +505,9 @@ int sg_read(int sg_fd, unsigned char * buff, int blocks, long long from_block,
return 0;
}
-/* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM),
- 2 -> try again */
+/* 0 -> successful, SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_SYNTAX_ERROR,
+ * SG_LIB_CAT_NOT_READY, SG_LIB_CAT_MEDIUM_HARD, SG_LIB_CAT_ILLEGAL_REQ,
+ * -2 -> recoverable (ENOMEM), -1 -> unrecoverable error */
int sg_write(int sg_fd, unsigned char * buff, int blocks, long long to_block,
int bs, int cdbsz, int fua, int dpo, int do_mmap, int * diop)
{
@@ -515,7 +519,7 @@ int sg_write(int sg_fd, unsigned char * buff, int blocks, long long to_block,
if (sg_build_scsi_cdb(wrCmd, cdbsz, blocks, to_block, 1, fua, dpo)) {
fprintf(stderr, ME "bad wr cdb build, to_block=%lld, blocks=%d\n",
to_block, blocks);
- return -1;
+ return SG_LIB_SYNTAX_ERROR;
}
memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
@@ -554,7 +558,7 @@ int sg_write(int sg_fd, unsigned char * buff, int blocks, long long to_block,
;
if (res < 0) {
if (ENOMEM == errno)
- return 1;
+ return -2;
perror("writing (wr) on sg device, error");
return -1;
}
@@ -569,17 +573,21 @@ int sg_write(int sg_fd, unsigned char * buff, int blocks, long long to_block,
#endif
if (verbose > 2)
fprintf(stderr, " duration=%u ms\n", io_hdr.duration);
- switch (sg_err_category3(&io_hdr)) {
+ res = sg_err_category3(&io_hdr);
+ switch (res) {
case SG_LIB_CAT_CLEAN:
break;
case SG_LIB_CAT_RECOVERED:
sg_chk_n_print3("Writing, continuing", &io_hdr, verbose > 1);
break;
- case SG_LIB_CAT_MEDIA_CHANGED:
- return 2;
+ case SG_LIB_CAT_NOT_READY:
+ case SG_LIB_CAT_MEDIUM_HARD:
+ return res;
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ case SG_LIB_CAT_ILLEGAL_REQ:
default:
sg_chk_n_print3("writing", &io_hdr, verbose > 1);
- return -1;
+ return res;
}
if (diop && *diop &&
((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
@@ -672,6 +680,7 @@ int main(int argc, char * argv[])
struct timeval start_tm, end_tm;
struct flags_t in_flags;
struct flags_t out_flags;
+ int ret = 0;
inf[0] = '\0';
outf[0] = '\0';
@@ -691,14 +700,14 @@ int main(int argc, char * argv[])
bpt = sg_get_num(buf);
if (-1 == bpt) {
fprintf(stderr, ME "bad argument to 'bpt'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
bpt_given = 1;
} else if (0 == strcmp(key,"bs")) {
bs = sg_get_num(buf);
if (-1 == bs) {
fprintf(stderr, ME "bad argument to 'bs'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strcmp(key,"cdbsz")) {
scsi_cdbsz_in = sg_get_num(buf);
@@ -708,7 +717,7 @@ int main(int argc, char * argv[])
dd_count = sg_get_llnum(buf);
if (-1LL == dd_count) {
fprintf(stderr, ME "bad argument to 'count'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strcmp(key,"dio"))
do_dio = sg_get_num(buf);
@@ -722,47 +731,47 @@ int main(int argc, char * argv[])
ibs = sg_get_num(buf);
if (-1 == ibs) {
fprintf(stderr, ME "bad argument to 'ibs'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (strcmp(key,"if") == 0) {
if ('\0' != inf[0]) {
fprintf(stderr, "Second 'if=' argument??\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
} else
strncpy(inf, buf, INOUTF_SZ);
} else if (0 == strcmp(key, "iflag")) {
if (process_flags(buf, &in_flags)) {
fprintf(stderr, ME "bad argument to 'iflag'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (strcmp(key,"of") == 0) {
if ('\0' != outf[0]) {
fprintf(stderr, "Second 'of=' argument??\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
} else
strncpy(outf, buf, INOUTF_SZ);
} else if (0 == strcmp(key, "oflag")) {
if (process_flags(buf, &out_flags)) {
fprintf(stderr, ME "bad argument to 'oflag'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strcmp(key,"obs")) {
obs = sg_get_num(buf);
if (-1 == obs) {
fprintf(stderr, ME "bad argument to 'obs'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strcmp(key,"seek")) {
seek = sg_get_llnum(buf);
if (-1LL == seek) {
fprintf(stderr, ME "bad argument to 'seek'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strcmp(key,"skip")) {
skip = sg_get_llnum(buf);
if (-1LL == skip) {
fprintf(stderr, ME "bad argument to 'skip'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strcmp(key,"sync"))
do_sync = sg_get_num(buf);
@@ -781,7 +790,7 @@ int main(int argc, char * argv[])
else {
fprintf(stderr, "Unrecognized option '%s'\n", key);
fprintf(stderr, "For more information use '--help'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (bs <= 0) {
@@ -791,19 +800,19 @@ int main(int argc, char * argv[])
if ((ibs && (ibs != bs)) || (obs && (obs != bs))) {
fprintf(stderr, "If 'ibs' or 'obs' given must be same as 'bs'\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if ((skip < 0) || (seek < 0)) {
fprintf(stderr, "skip and seek cannot be negative\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if ((out_flags.append > 0) && (seek > 0)) {
fprintf(stderr, "Can't use both append and seek switches\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (bpt < 1) {
fprintf(stderr, "bpt must be greater than 0\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
/* defaulting transfer size to 128*2048 for CD/DVDs is too large
for the block layer in lk 2.6 and results in an EIO on the
@@ -830,10 +839,10 @@ int main(int argc, char * argv[])
if (FT_ERROR == in_type) {
fprintf(stderr, ME "unable to access %s\n", inf);
- return 1;
+ return SG_LIB_FILE_ERROR;
} else if (FT_ST == in_type) {
fprintf(stderr, ME "unable to use scsi tape device %s\n", inf);
- return 1;
+ return SG_LIB_FILE_ERROR;
} else if (FT_SG == in_type) {
flags = O_RDWR | O_NONBLOCK;
if (in_flags.direct)
@@ -846,26 +855,26 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ,
ME "could not open %s for sg reading", inf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
res = ioctl(infd, SG_GET_VERSION_NUM, &t);
if ((res < 0) || (t < 30122)) {
fprintf(stderr, ME "sg driver prior to 3.1.22\n");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
in_res_sz = bs * bpt;
if (0 != (in_res_sz % psz)) /* round up to next page */
in_res_sz = ((in_res_sz / psz) + 1) * psz;
if (ioctl(infd, SG_GET_RESERVED_SIZE, &t) < 0) {
perror(ME "SG_GET_RESERVED_SIZE error");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (t < MIN_RESERVED_SIZE)
t = MIN_RESERVED_SIZE;
if (in_res_sz > t) {
if (ioctl(infd, SG_SET_RESERVED_SIZE, &in_res_sz) < 0) {
perror(ME "SG_SET_RESERVED_SIZE error");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
}
wrkMmap = mmap(NULL, in_res_sz, PROT_READ | PROT_WRITE,
@@ -874,7 +883,7 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ,
ME "error using mmap() on file: %s", inf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
}
else {
@@ -889,7 +898,7 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ,
ME "could not open %s for reading", inf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
else if (skip > 0) {
llse_loff_t offset = skip;
@@ -899,7 +908,7 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ, ME "couldn't skip to "
"required position on %s", inf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (verbose)
fprintf(stderr, " >> skip: llseek SEEK_SET, "
@@ -916,7 +925,7 @@ int main(int argc, char * argv[])
if (FT_ST == out_type) {
fprintf(stderr, ME "unable to use scsi tape device %s\n", outf);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
else if (FT_SG == out_type) {
flags = O_RDWR | O_NONBLOCK;
@@ -930,16 +939,16 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ, ME "could not open %s for "
"sg writing", outf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
res = ioctl(outfd, SG_GET_VERSION_NUM, &t);
if ((res < 0) || (t < 30122)) {
fprintf(stderr, ME "sg driver prior to 3.1.22\n");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (ioctl(outfd, SG_GET_RESERVED_SIZE, &t) < 0) {
perror(ME "SG_GET_RESERVED_SIZE error");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (t < MIN_RESERVED_SIZE)
t = MIN_RESERVED_SIZE;
@@ -947,7 +956,7 @@ int main(int argc, char * argv[])
if (out_res_sz > t) {
if (ioctl(outfd, SG_SET_RESERVED_SIZE, &out_res_sz) < 0) {
perror(ME "SG_SET_RESERVED_SIZE error");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
}
if (NULL == wrkMmap) {
@@ -957,7 +966,7 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ,
ME "error using mmap() on file: %s", outf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
}
}
@@ -978,7 +987,7 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ,
ME "could not open %s for writing", outf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
}
else {
@@ -986,7 +995,7 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ, ME "could not open %s "
"for raw writing", outf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
}
if (seek > 0) {
@@ -997,7 +1006,7 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ, ME "couldn't seek to "
"required position on %s", outf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (verbose)
fprintf(stderr, " >> seek: llseek SEEK_SET, "
@@ -1009,21 +1018,24 @@ int main(int argc, char * argv[])
fprintf(stderr,
"Can't have both 'if' as stdin _and_ 'of' as stdout\n");
fprintf(stderr, "For more information use '--help'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (dd_count < 0) {
in_num_sect = -1;
if (FT_SG == in_type) {
res = scsi_read_capacity(infd, &in_num_sect, &in_sect_sz);
- if (SG_LIB_CAT_MEDIA_CHANGED == res) {
+ if (SG_LIB_CAT_UNIT_ATTENTION == res) {
fprintf(stderr,
- "Unit attention, media changed(in), continuing\n");
+ "Unit attention(in), continuing\n");
res = scsi_read_capacity(infd, &in_num_sect, &in_sect_sz);
}
if (0 != res) {
- if (res == SG_LIB_CAT_INVALID_OP)
+ if (res == SG_LIB_CAT_INVALID_OP)
fprintf(stderr, "read capacity not supported on %s\n",
inf);
+ else if (res == SG_LIB_CAT_NOT_READY)
+ fprintf(stderr, "read capacity failed, %s not ready\n",
+ inf);
else
fprintf(stderr, "Unable to read capacity on %s\n", inf);
in_num_sect = -1;
@@ -1045,15 +1057,18 @@ int main(int argc, char * argv[])
out_num_sect = -1;
if (FT_SG == out_type) {
res = scsi_read_capacity(outfd, &out_num_sect, &out_sect_sz);
- if (SG_LIB_CAT_MEDIA_CHANGED == res) {
+ if (SG_LIB_CAT_UNIT_ATTENTION == res) {
fprintf(stderr,
- "Unit attention, media changed(out), continuing\n");
+ "Unit attention(out), continuing\n");
res = scsi_read_capacity(outfd, &out_num_sect, &out_sect_sz);
}
if (0 != res) {
- if (res == SG_LIB_CAT_INVALID_OP)
+ if (res == SG_LIB_CAT_INVALID_OP)
fprintf(stderr, "read capacity not supported on %s\n",
outf);
+ else if (res == SG_LIB_CAT_NOT_READY)
+ fprintf(stderr, "read capacity failed, %s not ready\n",
+ outf);
else
fprintf(stderr, "Unable to read capacity on %s\n", outf);
out_num_sect = -1;
@@ -1091,7 +1106,7 @@ int main(int argc, char * argv[])
if (dd_count < 0) {
fprintf(stderr, "Couldn't calculate count, please give one\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (! cdbsz_given) {
if ((FT_SG == in_type) && (MAX_SCSI_CDBSZ != scsi_cdbsz_in) &&
@@ -1134,7 +1149,7 @@ int main(int argc, char * argv[])
wrkBuff = malloc(bs * bpt + psz);
if (0 == wrkBuff) {
fprintf(stderr, "Not enough user memory for raw\n");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
wrkPos = (unsigned char *)(((unsigned long)wrkBuff + psz - 1) &
(~(psz - 1)));
@@ -1143,7 +1158,7 @@ int main(int argc, char * argv[])
wrkBuff = malloc(bs * bpt);
if (0 == wrkBuff) {
fprintf(stderr, "Not enough user memory\n");
- return 1;
+ return SG_LIB_FILE_ERROR;
}
wrkPos = wrkBuff;
}
@@ -1169,15 +1184,15 @@ int main(int argc, char * argv[])
while (dd_count > 0) {
blocks = (dd_count > blocks_per) ? blocks_per : dd_count;
if (FT_SG == in_type) {
- res = sg_read(infd, wrkPos, blocks, skip, bs, scsi_cdbsz_in,
+ ret = sg_read(infd, wrkPos, blocks, skip, bs, scsi_cdbsz_in,
in_flags.fua, in_flags.dpo, 1);
- if (2 == res) {
+ if (SG_LIB_CAT_UNIT_ATTENTION == ret) {
fprintf(stderr,
- "Unit attention, media changed, continuing (r)\n");
- res = sg_read(infd, wrkPos, blocks, skip, bs, scsi_cdbsz_in,
+ "Unit attention, continuing (r)\n");
+ ret = sg_read(infd, wrkPos, blocks, skip, bs, scsi_cdbsz_in,
in_flags.fua, in_flags.dpo, 1);
}
- if (0 != res) {
+ if (0 != ret) {
fprintf(stderr, "sg_read failed, skip=%lld\n", skip);
break;
}
@@ -1191,9 +1206,10 @@ int main(int argc, char * argv[])
if (verbose > 2)
fprintf(stderr, "read(unix): count=%d, res=%d\n",
blocks * bs, res);
- if (res < 0) {
+ if (ret < 0) {
snprintf(ebuff, EBUFF_SZ, ME "reading, skip=%lld ", skip);
perror(ebuff);
+ ret = -1;
break;
}
else if (res < blocks * bs) {
@@ -1214,15 +1230,16 @@ int main(int argc, char * argv[])
int do_mmap = (FT_SG == in_type) ? 0 : 1;
int dio_res = do_dio;
- res = sg_write(outfd, wrkPos, blocks, seek, bs, scsi_cdbsz_out,
+ ret = sg_write(outfd, wrkPos, blocks, seek, bs, scsi_cdbsz_out,
out_flags.fua, out_flags.dpo, do_mmap, &dio_res);
- if (2 == res) {
+ if (SG_LIB_CAT_UNIT_ATTENTION == ret) {
fprintf(stderr,
- "Unit attention, media changed, continuing (w)\n");
- res = sg_write(outfd, wrkPos, blocks, seek, bs, scsi_cdbsz_out,
+ "Unit attention, continuing (w)\n");
+ dio_res = do_dio;
+ ret = sg_write(outfd, wrkPos, blocks, seek, bs, scsi_cdbsz_out,
out_flags.fua, out_flags.dpo, do_mmap, &dio_res);
}
- else if (0 != res) {
+ if (0 != ret) {
fprintf(stderr, "sg_write failed, seek=%lld\n", seek);
break;
}
@@ -1287,9 +1304,8 @@ int main(int argc, char * argv[])
if (FT_SG == out_type) {
fprintf(stderr, ">> Synchronizing cache on %s\n", outf);
res = sg_ll_sync_cache_10(outfd, 0, 0, 0, 0, 0, 0, 0);
- if (2 == res) {
- fprintf(stderr,
- "Unit attention, media changed(in), continuing\n");
+ if (SG_LIB_CAT_UNIT_ATTENTION == res) {
+ fprintf(stderr, "Unit attention(out), continuing\n");
res = sg_ll_sync_cache_10(outfd, 0, 0, 0, 0, 0, 0, 0);
}
if (0 != res)
@@ -1302,10 +1318,10 @@ int main(int argc, char * argv[])
close(infd);
if ((STDOUT_FILENO != outfd) && (FT_DEV_NULL != out_type))
close(outfd);
- res = 0;
if (0 != dd_count) {
fprintf(stderr, "Some error occurred,");
- res = 2;
+ if (0 == ret)
+ ret = SG_LIB_CAT_OTHER;
}
print_stats();
if (sum_of_resids)
@@ -1314,5 +1330,5 @@ int main(int argc, char * argv[])
if (num_dio_not_done)
fprintf(stderr, ">> dio requested but _not_ done %d times\n",
num_dio_not_done);
- return res;
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}
diff --git a/sgp_dd.8 b/sgp_dd.8
index 79ee7593..16d0936b 100644
--- a/sgp_dd.8
+++ b/sgp_dd.8
@@ -1,4 +1,4 @@
-.TH SGP_DD "8" "April 2006" "sg3_utils-1.20" SG3_UTILS
+.TH SGP_DD "8" "June 2006" "sg3_utils-1.21" SG3_UTILS
.SH NAME
sgp_dd \- copies data to and from files and devices. Specialised for
devices that understand the SCSI command set.
@@ -274,6 +274,12 @@ To do a fast copy from one SCSI disk to another one with similar
geometry (stepping over errors on the source disk):
.PP
sgp_dd if=/dev/sg0 of=/dev/sg1 bs=512 coe=1
+.SH EXIT STATUS
+The exit status of sgp_dd is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page. Since this utility works at a higher level
+than individual commands, and there are 'coe' and 'retries' flags,
+individual SCSI command failures do not necessary cause the process
+to exit.
.SH AUTHORS
Written by Doug Gilbert and Peter Allworth.
.SH "REPORTING BUGS"
diff --git a/sgp_dd.c b/sgp_dd.c
index e4d8285c..7262a4c4 100644
--- a/sgp_dd.c
+++ b/sgp_dd.c
@@ -49,7 +49,7 @@
*/
-static char * version_str = "5.28 20060403";
+static char * version_str = "5.31 20060704";
#define DEF_BLOCK_SIZE 512
#define DEF_BLOCKS_PER_TRANSFER 128
@@ -184,6 +184,7 @@ static struct timeval start_tm;
static long long dd_count = -1;
static int num_threads = DEF_NUM_THREADS;
static int do_sync = 0;
+static int exit_status = 0;
static void calc_duration_throughput(int contin)
@@ -366,9 +367,7 @@ static void guarded_stop_both(Rq_coll * clp)
guarded_stop_out(clp);
}
-/* Return of 0 -> success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
- * SG_LIB_CAT_MEDIA_CHANGED -> media changed, SG_LIB_CAT_ILLEGAL_REQ
- * -> bad field in cdb, -1 -> other failure */
+/* Return of 0 -> success, see sg_ll_read_capacity*() otherwise */
static int scsi_read_capacity(int sg_fd, long long * num_sect, int * sect_sz)
{
int k, res;
@@ -795,19 +794,28 @@ static void sg_in_operation(Rq_coll * clp, Rq_elem * rep)
if (0 != status) err_exit(status, "unlock in_mutex");
res = sg_finish_io(rep->wr, rep, &clp->aux_mutex);
- if (res < 0) {
- if (clp->in_flags.coe) {
+ switch (res) {
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ /* try again with same addr, count info */
+ /* now re-acquire in mutex for balance */
+ /* N.B. This re-read could now be out of read sequence */
+ status = pthread_mutex_lock(&clp->in_mutex);
+ if (0 != status) err_exit(status, "lock in_mutex");
+ break;
+ case SG_LIB_CAT_MEDIUM_HARD:
+ if (0 == clp->in_flags.coe) {
+ fprintf(stderr, "error finishing sg in command (medium)\n");
+ if (exit_status <= 0)
+ exit_status = res;
+ guarded_stop_both(clp);
+ return;
+ } else {
memset(rep->buffp, 0, rep->num_blks * rep->bs);
fprintf(stderr, ">> substituted zeros for in blk=%lld for "
"%d bytes\n", rep->blk, rep->num_blks * rep->bs);
}
- else {
- fprintf(stderr, "error finishing sg in command\n");
- guarded_stop_both(clp);
- return;
- }
- }
- if (res <= 0) { /* looks good, going to return */
+ /* fall through */
+ case 0:
if (rep->dio_incomplete || rep->resid) {
status = pthread_mutex_lock(&clp->aux_mutex);
if (0 != status) err_exit(status, "lock aux_mutex");
@@ -822,12 +830,13 @@ static void sg_in_operation(Rq_coll * clp, Rq_elem * rep)
status = pthread_mutex_unlock(&clp->in_mutex);
if (0 != status) err_exit(status, "unlock in_mutex");
return;
+ default:
+ fprintf(stderr, "error finishing sg in command (%d)\n", res);
+ if (exit_status <= 0)
+ exit_status = res;
+ guarded_stop_both(clp);
+ return;
}
- /* else assume 1 == res so try again with same addr, count info */
- /* now re-acquire read mutex for balance */
- /* N.B. This re-read could now be out of read sequence */
- status = pthread_mutex_lock(&clp->in_mutex);
- if (0 != status) err_exit(status, "lock in_mutex");
}
}
@@ -854,17 +863,26 @@ static void sg_out_operation(Rq_coll * clp, Rq_elem * rep)
if (0 != status) err_exit(status, "unlock out_mutex");
res = sg_finish_io(rep->wr, rep, &clp->aux_mutex);
- if (res < 0) {
- if (clp->out_flags.coe)
- fprintf(stderr, ">> ignored error for out blk=%lld for "
- "%d bytes\n", rep->blk, rep->num_blks * rep->bs);
- else {
- fprintf(stderr, "error finishing sg out command\n");
+ switch (res) {
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ /* try again with same addr, count info */
+ /* now re-acquire out mutex for balance */
+ /* N.B. This re-write could now be out of write sequence */
+ status = pthread_mutex_lock(&clp->out_mutex);
+ if (0 != status) err_exit(status, "lock out_mutex");
+ break;
+ case SG_LIB_CAT_MEDIUM_HARD:
+ if (0 == clp->out_flags.coe) {
+ fprintf(stderr, "error finishing sg out command (medium)\n");
+ if (exit_status <= 0)
+ exit_status = res;
guarded_stop_both(clp);
return;
- }
- }
- if (res <= 0) {
+ } else
+ fprintf(stderr, ">> ignored error for out blk=%lld for "
+ "%d bytes\n", rep->blk, rep->num_blks * rep->bs);
+ /* fall through */
+ case 0:
if (rep->dio_incomplete || rep->resid) {
status = pthread_mutex_lock(&clp->aux_mutex);
if (0 != status) err_exit(status, "lock aux_mutex");
@@ -879,12 +897,13 @@ static void sg_out_operation(Rq_coll * clp, Rq_elem * rep)
status = pthread_mutex_unlock(&clp->out_mutex);
if (0 != status) err_exit(status, "unlock out_mutex");
return;
+ default:
+ fprintf(stderr, "error finishing sg out command (%d)\n", res);
+ if (exit_status <= 0)
+ exit_status = res;
+ guarded_stop_both(clp);
+ return;
}
- /* else assume 1 == res so try again with same addr, count info */
- /* now re-acquire out mutex for balance */
- /* N.B. This re-write could now be out of write sequence */
- status = pthread_mutex_lock(&clp->out_mutex);
- if (0 != status) err_exit(status, "lock out_mutex");
}
}
@@ -920,8 +939,6 @@ static int sg_start_io(Rq_elem * rep)
fprintf(stderr, "sg_start_io: SCSI %s, blk=%lld num_blks=%d\n",
rep->wr ? "WRITE" : "READ", rep->blk, rep->num_blks);
sg_print_command(hp->cmdp);
- fprintf(stderr, "dir=%d, len=%d, dxfrp=%p, cmd_len=%d\n",
- hp->dxfer_direction, hp->dxfer_len, hp->dxferp, hp->cmd_len);
}
while (((res = write(rep->wr ? rep->outfd : rep->infd, hp,
@@ -936,7 +953,8 @@ static int sg_start_io(Rq_elem * rep)
return 0;
}
-/* -1 -> unrecoverable error, 0 -> successful, 1 -> try again */
+/* 0 -> successful, SG_LIB_CAT_UNIT_ATTENTION -> try again,
+ SG_LIB_CAT_NOT_READY, SG_LIB_CAT_MEDIUM_HARD, -1 other errors */
static int sg_finish_io(int wr, Rq_elem * rep, pthread_mutex_t * a_mutp)
{
int res, status;
@@ -964,15 +982,17 @@ static int sg_finish_io(int wr, Rq_elem * rep, pthread_mutex_t * a_mutp)
memcpy(&rep->io_hdr, &io_hdr, sizeof(struct sg_io_hdr));
hp = &rep->io_hdr;
- switch (sg_err_category3(hp)) {
+ res = sg_err_category3(hp);
+ switch (res) {
case SG_LIB_CAT_CLEAN:
break;
case SG_LIB_CAT_RECOVERED:
sg_chk_n_print3((rep->wr ? "writing continuing":
"reading continuing"), hp, 0);
break;
- case SG_LIB_CAT_MEDIA_CHANGED:
- return 1;
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ return res;
+ case SG_LIB_CAT_NOT_READY:
default:
{
char ebuff[EBUFF_SZ];
@@ -984,7 +1004,7 @@ static int sg_finish_io(int wr, Rq_elem * rep, pthread_mutex_t * a_mutp)
sg_chk_n_print3(ebuff, hp, 0);
status = pthread_mutex_unlock(a_mutp);
if (0 != status) err_exit(status, "unlock aux_mutex");
- return -1;
+ return res;
}
}
#if 0
@@ -1112,14 +1132,14 @@ int main(int argc, char * argv[])
rcoll.bpt = sg_get_num(buf);
if (-1 == rcoll.bpt) {
fprintf(stderr, ME "bad argument to 'bpt'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
bpt_given = 1;
} else if (0 == strcmp(key,"bs")) {
rcoll.bs = sg_get_num(buf);
if (-1 == rcoll.bs) {
fprintf(stderr, ME "bad argument to 'bs'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strcmp(key,"cdbsz")) {
rcoll.cdbsz_in = sg_get_num(buf);
@@ -1132,7 +1152,7 @@ int main(int argc, char * argv[])
dd_count = sg_get_llnum(buf);
if (-1LL == dd_count) {
fprintf(stderr, ME "bad argument to 'count'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strncmp(key,"deb", 3))
rcoll.debug = sg_get_num(buf);
@@ -1148,47 +1168,47 @@ int main(int argc, char * argv[])
ibs = sg_get_num(buf);
if (-1 == ibs) {
fprintf(stderr, ME "bad argument to 'ibs'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (strcmp(key,"if") == 0) {
if ('\0' != inf[0]) {
fprintf(stderr, "Second 'if=' argument??\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
} else
strncpy(inf, buf, INOUTF_SZ);
} else if (0 == strcmp(key, "iflag")) {
if (process_flags(buf, &rcoll.in_flags)) {
fprintf(stderr, ME "bad argument to 'iflag'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strcmp(key,"obs")) {
obs = sg_get_num(buf);
if (-1 == obs) {
fprintf(stderr, ME "bad argument to 'obs'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (strcmp(key,"of") == 0) {
if ('\0' != outf[0]) {
fprintf(stderr, "Second 'of=' argument??\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
} else
strncpy(outf, buf, INOUTF_SZ);
} else if (0 == strcmp(key, "oflag")) {
if (process_flags(buf, &rcoll.out_flags)) {
fprintf(stderr, ME "bad argument to 'oflag'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strcmp(key,"seek")) {
seek = sg_get_llnum(buf);
if (-1LL == seek) {
fprintf(stderr, ME "bad argument to 'seek'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strcmp(key,"skip")) {
skip = sg_get_llnum(buf);
if (-1LL == skip) {
fprintf(stderr, ME "bad argument to 'skip'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strcmp(key,"sync"))
do_sync = sg_get_num(buf);
@@ -1208,7 +1228,7 @@ int main(int argc, char * argv[])
else {
fprintf(stderr, "Unrecognized option '%s'\n", key);
fprintf(stderr, "For more information use '--help'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
}
if (rcoll.bs <= 0) {
@@ -1219,19 +1239,19 @@ int main(int argc, char * argv[])
if ((ibs && (ibs != rcoll.bs)) || (obs && (obs != rcoll.bs))) {
fprintf(stderr, "If 'ibs' or 'obs' given must be same as 'bs'\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if ((skip < 0) || (seek < 0)) {
fprintf(stderr, "skip and seek cannot be negative\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if ((rcoll.out_flags.append > 0) && (seek > 0)) {
fprintf(stderr, "Can't use both append and seek switches\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (rcoll.bpt < 1) {
fprintf(stderr, "bpt must be greater than 0\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
/* defaulting transfer size to 128*2048 for CD/DVDs is too large
for the block layer in lk 2.6 and results in an EIO on the
@@ -1241,7 +1261,7 @@ int main(int argc, char * argv[])
if ((num_threads < 1) || (num_threads > MAX_NUM_THREADS)) {
fprintf(stderr, "too few or too many threads requested\n");
usage();
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (rcoll.debug)
fprintf(stderr, ME "if=%s skip=%lld of=%s seek=%lld count=%lld\n",
@@ -1259,10 +1279,10 @@ int main(int argc, char * argv[])
if (FT_ERROR == rcoll.in_type) {
fprintf(stderr, ME "unable to access %s\n", inf);
- return 1;
+ return SG_LIB_FILE_ERROR;
} else if (FT_ST == rcoll.in_type) {
fprintf(stderr, ME "unable to use scsi tape device %s\n", inf);
- return 1;
+ return SG_LIB_FILE_ERROR;
} else if (FT_SG == rcoll.in_type) {
flags = O_RDWR;
if (rcoll.in_flags.direct)
@@ -1276,10 +1296,10 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ,
ME "could not open %s for sg reading", inf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (sg_prepare(rcoll.infd, rcoll.bs, rcoll.bpt))
- return 1;
+ return SG_LIB_FILE_ERROR;
}
else {
flags = O_RDONLY;
@@ -1294,7 +1314,7 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ,
ME "could not open %s for reading", inf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
else if (skip > 0) {
llse_loff_t offset = skip;
@@ -1304,7 +1324,7 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ,
ME "couldn't skip to required position on %s", inf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
}
}
@@ -1314,7 +1334,7 @@ int main(int argc, char * argv[])
if (FT_ST == rcoll.out_type) {
fprintf(stderr, ME "unable to use scsi tape device %s\n", outf);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
else if (FT_SG == rcoll.out_type) {
flags = O_RDWR;
@@ -1329,11 +1349,11 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ,
ME "could not open %s for sg writing", outf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
if (sg_prepare(rcoll.outfd, rcoll.bs, rcoll.bpt))
- return 1;
+ return SG_LIB_FILE_ERROR;
}
else if (FT_DEV_NULL == rcoll.out_type)
rcoll.outfd = -1; /* don't bother opening */
@@ -1353,7 +1373,7 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ,
ME "could not open %s for writing", outf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
}
else { /* raw output file */
@@ -1361,7 +1381,7 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ,
ME "could not open %s for raw writing", outf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
}
if (seek > 0) {
@@ -1372,7 +1392,7 @@ int main(int argc, char * argv[])
snprintf(ebuff, EBUFF_SZ,
ME "couldn't seek to required position on %s", outf);
perror(ebuff);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
}
}
@@ -1381,7 +1401,7 @@ int main(int argc, char * argv[])
fprintf(stderr, "Can't have both 'if' as stdin _and_ 'of' as "
"stdout\n");
fprintf(stderr, "For more information use '--help'\n");
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
}
if (dd_count < 0) {
in_num_sect = -1;
@@ -1394,9 +1414,12 @@ int main(int argc, char * argv[])
&in_sect_sz);
}
if (0 != res) {
- if (res == SG_LIB_CAT_INVALID_OP)
+ if (res == SG_LIB_CAT_INVALID_OP)
fprintf(stderr, "read capacity not supported on %s\n",
inf);
+ else if (res == SG_LIB_CAT_NOT_READY)
+ fprintf(stderr, "read capacity failed, %s not ready\n",
+ inf);
else
fprintf(stderr, "Unable to read capacity on %s\n", inf);
in_num_sect = -1;
@@ -1426,9 +1449,12 @@ int main(int argc, char * argv[])
&out_sect_sz);
}
if (0 != res) {
- if (res == SG_LIB_CAT_INVALID_OP)
+ if (res == SG_LIB_CAT_INVALID_OP)
fprintf(stderr, "read capacity not supported on %s\n",
outf);
+ else if (res == SG_LIB_CAT_NOT_READY)
+ fprintf(stderr, "read capacity failed, %s not ready\n",
+ outf);
else
fprintf(stderr, "Unable to read capacity on %s\n", outf);
out_num_sect = -1;
@@ -1464,7 +1490,7 @@ int main(int argc, char * argv[])
"out_num_sect=%lld\n", dd_count, in_num_sect, out_num_sect);
if (dd_count < 0) {
fprintf(stderr, "Couldn't calculate count, please give one\n");
- return 1;
+ return SG_LIB_CAT_OTHER;
}
if (! cdbsz_given) {
if ((FT_SG == rcoll.in_type) && (MAX_SCSI_CDBSZ != rcoll.cdbsz_in) &&
@@ -1556,9 +1582,9 @@ int main(int argc, char * argv[])
if (FT_SG == rcoll.out_type) {
fprintf(stderr, ">> Synchronizing cache on %s\n", outf);
res = sg_ll_sync_cache_10(rcoll.outfd, 0, 0, 0, 0, 0, 0, 0);
- if (2 == res) {
+ if (SG_LIB_CAT_UNIT_ATTENTION == res) {
fprintf(stderr,
- "Unit attention, media changed(in), continuing\n");
+ "Unit attention(out), continuing\n");
res = sg_ll_sync_cache_10(rcoll.outfd, 0, 0, 0, 0, 0, 0, 0);
}
if (0 != res)
@@ -1572,11 +1598,12 @@ int main(int argc, char * argv[])
close(rcoll.infd);
if ((STDOUT_FILENO != rcoll.outfd) && (FT_DEV_NULL != rcoll.out_type))
close(rcoll.outfd);
- res = 0;
+ res = exit_status;
if (0 != rcoll.out_count) {
fprintf(stderr, ">>>> Some error occurred, remaining blocks=%lld\n",
rcoll.out_count);
- res = 2;
+ if (0 == res)
+ res = SG_LIB_CAT_OTHER;
}
print_stats("");
if (rcoll.dio_incomplete) {
@@ -1597,5 +1624,5 @@ int main(int argc, char * argv[])
if (rcoll.sum_of_resids)
fprintf(stderr, ">> Non-zero sum of residual counts=%d\n",
rcoll.sum_of_resids);
- return res;
+ return (res >= 0) ? res : SG_LIB_CAT_OTHER;
}
diff --git a/utils/Makefile b/utils/Makefile
index 79516f3f..a0da149b 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -7,7 +7,7 @@ MANDIR=$(DESTDIR)/$(PREFIX)/man
CC = gcc
LD = gcc
-EXECS = hxascdmp
+EXECS = hxascdmp sg_chk_asc
MAN_PGS =
MAN_PREF = man8
@@ -29,6 +29,9 @@ clean:
hxascdmp: hxascdmp.o
$(LD) -o $@ $(LDFLAGS) $^
+sg_chk_asc: sg_chk_asc.o ../sg_lib.o
+ $(LD) -o $@ $(LDFLAGS) $^
+
install: $(EXECS)
install -d $(INSTDIR)
diff --git a/utils/sg_chk_asc.c b/utils/sg_chk_asc.c
new file mode 100644
index 00000000..59f1a7eb
--- /dev/null
+++ b/utils/sg_chk_asc.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2006 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 <getopt.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "sg_lib.h"
+
+/* A utility program for the Linux OS SCSI subsystem.
+ *
+ * This program takes a asc_ascq.txt file from www.t10.org and
+ * checks it against the additional sense codes held in the
+ * sg_lib.c file.
+ * The online version of the asc_ascq codes can be found at:
+ * http://www.t10.org/lists/asc-num.txt
+ */
+
+static char * version_str = "1.02 20060706";
+
+#define ME "sg_chk_asc: "
+
+#define MAX_LINE_LEN 1024
+
+
+static struct option long_options[] = {
+ {"help", 0, 0, 'h'},
+ {"verbose", 0, 0, 'v'},
+ {"version", 0, 0, 'V'},
+ {0, 0, 0, 0},
+};
+
+static void usage()
+{
+ fprintf(stderr, "Usage: "
+ "sg_chk_asc [--help] [--verbose] [--version] <asc_ascq_file>\n"
+ " where: --help|-h print out usage message\n"
+ " --verbose|-v increase verbosity\n"
+ " --version|-V print version string and exit\n\n"
+ "Checks asc/ascq codes < www.t10.org/lists/asc-num.txt > against "
+ "sg_lib.c\n"
+ );
+
+}
+
+int main(int argc, char * argv[])
+{
+ int k, j, res, c, num, len, asc, ascq;
+ FILE * fp;
+ int verbose = 0;
+ char file_name[256];
+ char line[MAX_LINE_LEN];
+ char b[MAX_LINE_LEN];
+ char bb[MAX_LINE_LEN];
+ char * cp;
+ int ret = 1;
+
+ memset(file_name, 0, sizeof file_name);
+ memset(line, 0, sizeof file_name);
+ while (1) {
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "hvV", long_options,
+ &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ case '?':
+ usage();
+ return 0;
+ 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' == file_name[0]) {
+ strncpy(file_name, argv[optind], sizeof(file_name) - 1);
+ file_name[sizeof(file_name) - 1] = '\0';
+ ++optind;
+ }
+ if (optind < argc) {
+ for (; optind < argc; ++optind)
+ fprintf(stderr, "Unexpected extra argument: %s\n",
+ argv[optind]);
+ usage();
+ return 1;
+ }
+ }
+
+ if (0 == file_name[0]) {
+ fprintf(stderr, "missing file name!\n");
+ usage();
+ return 1;
+ }
+ fp = fopen(file_name, "r");
+ if (NULL == fp) {
+ fprintf(stderr, ME "open error: %s: %s\n", file_name,
+ safe_strerror(errno));
+ return 1;
+ }
+ for (k = 0; (cp = fgets(line, sizeof(line) - 1, fp)); ++k) {
+ len = strlen(line);
+ if (len < 1)
+ continue;
+ if (! isdigit(line[0]))
+ continue;
+ num = sscanf(line, "%xh/%xh", &asc, &ascq);
+ if (1 == num)
+ ascq = -1;
+ if (num < 1) {
+ if (verbose)
+ fprintf(stderr, "Badly formed line number %d (num=%d)\n",
+ k + 1, num);
+ continue;
+ }
+ if (len < 26)
+ continue;
+#if 0
+strncpy(b , line, sizeof(b) - 1);
+b[sizeof(b) - 1] = '\0';
+num = strlen(b);
+if (0xd == b[num - 2]) {
+ b[num - 2] = '\0';
+ b[num - 1] = '\0';
+}
+printf("\"%s\",\n", b);
+#endif
+ strncpy(b , line + 25, sizeof(b) - 1);
+ b[sizeof(b) - 1] = '\0';
+ num = strlen(b);
+ if (0xd == b[num - 2]) {
+ b[num - 2] = '\0';
+ b[num - 1] = '\0';
+ }
+ num = strlen(b);
+ for (j = 0; j < num; ++j)
+ b[j] = toupper(b[j]);
+
+ bb[0] = '\0';
+ if (ascq >= 0) {
+ cp = sg_get_asc_ascq_str(asc, ascq, sizeof(bb) - 1, bb);
+ if (NULL == cp) {
+ fprintf(stderr, "no entry for %x,%x : %s\n", asc, ascq, b);
+ continue;
+ }
+ num = strlen(cp);
+// fprintf(stderr, "file: asc=%x acsq=%x strlen=%d %s\n", asc, ascq, num, cp);
+// if (num < 20)
+// continue;
+ if (num > 20) {
+ cp += 18;
+ num -= 18;
+ for (j = 0; j < num; ++j)
+ cp[j] = toupper(cp[j]);
+ }
+ if (0 != strcmp(b, cp))
+ fprintf(stderr, "%x,%x differ, ref: %s, sg_lib: "
+ "%s\n", asc, ascq, b, cp);
+ }
+ }
+ if (NULL == cp) {
+ if (feof(fp)) {
+ if (verbose > 2)
+ fprintf(stderr, "EOF detected\n");
+ } else
+ fprintf(stderr, ME "fgets: %s\n", safe_strerror(errno));
+ } else
+ fprintf(stderr, "%s\n", line);
+
+ res = fclose(fp);
+ if (EOF == res) {
+ fprintf(stderr, ME "close error: %s\n", safe_strerror(errno));
+ return 1;
+ }
+ return ret;
+}
diff --git a/utils/sg_lib.h b/utils/sg_lib.h
new file mode 120000
index 00000000..f6ce0761
--- /dev/null
+++ b/utils/sg_lib.h
@@ -0,0 +1 @@
+../sg_lib.h \ No newline at end of file