aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2007-06-27 03:06:26 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2007-06-27 03:06:26 +0000
commite459cae20b2c98bf198995f8af9245f517225a80 (patch)
tree37188ac671123986293ca47c54f644fd4f434cd0
parent03ff550a733e3c9a8966158244269de408f9e1d9 (diff)
downloadsg3_utils-e459cae20b2c98bf198995f8af9245f517225a80.tar.gz
Load sg3_utils-1.13 into trunk/.
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@51 6180dd3e-e324-4e3e-922d-17de1ae2f315
-rw-r--r--CHANGELOG30
-rw-r--r--COVERAGE25
-rw-r--r--CREDITS22
-rw-r--r--Makefile17
-rw-r--r--README426
-rw-r--r--html/sg_dd.html807
-rw-r--r--html/u_index.html913
-rw-r--r--lib_no_lib/Makefile.no_lib13
-rw-r--r--lib_no_lib/sg3_utils.spec.no_lib12
-rw-r--r--sg3_utils.spec12
-rw-r--r--sg_cmds.c277
-rw-r--r--sg_cmds.h10
-rw-r--r--sg_dd.890
-rw-r--r--sg_dd.c1019
-rw-r--r--sg_emc_trespass.c11
-rw-r--r--sg_format.8242
-rw-r--r--sg_format.c744
-rw-r--r--sg_get_config.84
-rw-r--r--sg_get_config.c100
-rw-r--r--sg_inq.88
-rw-r--r--sg_inq.c22
-rw-r--r--sg_lib.c262
-rw-r--r--sg_lib.h50
-rw-r--r--sg_logs.86
-rw-r--r--sg_logs.c349
-rw-r--r--sg_luns.c2
-rw-r--r--sg_modes.819
-rw-r--r--sg_modes.c78
-rw-r--r--sg_opcodes.c27
-rw-r--r--sg_persist.86
-rw-r--r--sg_persist.c23
-rw-r--r--sg_prevent.c16
-rw-r--r--sg_rbuf.c10
-rw-r--r--sg_read.864
-rw-r--r--sg_read.c172
-rw-r--r--sg_read_long.823
-rw-r--r--sg_read_long.c216
-rw-r--r--sg_readcap.c19
-rw-r--r--sg_reassign.c13
-rw-r--r--sg_requests.c8
-rw-r--r--sg_rtpg.c12
-rw-r--r--sg_scan.c76
-rw-r--r--sg_senddiag.817
-rw-r--r--sg_senddiag.c13
-rw-r--r--sg_ses.839
-rw-r--r--sg_ses.c280
-rw-r--r--sg_sync.812
-rw-r--r--sg_sync.c6
-rw-r--r--sg_test_rwbuf.869
-rw-r--r--sg_test_rwbuf.c318
-rw-r--r--sg_turs.812
-rw-r--r--sg_verify.813
-rw-r--r--sg_verify.c27
-rw-r--r--sg_wr_mode.817
-rw-r--r--sg_wr_mode.c27
-rw-r--r--sg_write_long.828
-rw-r--r--sg_write_long.c30
-rw-r--r--sginfo.85
-rw-r--r--sginfo.c113
-rw-r--r--sgm_dd.814
-rw-r--r--sgm_dd.c77
-rw-r--r--sgp_dd.820
-rw-r--r--sgp_dd.c71
63 files changed, 5981 insertions, 1482 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 9ee62124..8621b9b6 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,6 +2,36 @@ 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.
+Changelog for sg3_utils-1.13 [20050313]
+ - sg_format: new utility to format disks (perhaps change block size)
+ - sg_ses: rename "device element" to "additional element" [SES-2 rev 10]
+ - add SAS expander and connector elements; add download
+ microcode and subenclosure nickname diagnostic pages
+ - fix additional element descriptor for SAS
+ - off by 1 error when no type descriptor text in config page
+ <David dot Baldwin at anu dot edu dot au>
+ - sg_logs: log page for background media scan results
+ - sginfo: add "-flba64" option for outputting 64 bit lba defect lists
+ - sg_get_config: additions for BD from MMC-5 rev 1b
+ - sg_lib: add SG_LIB_CAT_ILLEGAL_REQ sense category
+ - add sg_get_sense_progress_fld()
+ - SPC-3 rev21d updates: report + set timestamp
+ - sg_get_num() + sg_get_llnum(): switch to multipliers that
+ are compatible with SI and with IEC 60027-2. Used modern
+ GNU's dd command as guide.
+ - report field replaceable unit code in fixed format
+ - sg_dd: add logic to use read_long on unrecovered read errors when
+ 'coe' set, read just prior to error if 'coe' clear
+ - both 'odir' and 'blk_sgio" are honoured on block devices
+ - add 'verbose' switch, output some mode page info when verbose
+ - print out elapsed time/throughput when signal received
+ - add new web page discussing sg_dd, copy in html subdirectory
+ - sg_read: add 'blk_sgio' and 'odir' options
+ - sg_wr_mode: clear mode data length in mode select(10)
+ - sg_test_rwbuf: add long options, allow test to run multiple times
+ - sg_cmds: add sg_get_mode_page_types() [get current, changeable, etc]
+ - llseek.c: add Makefile rule to no "-std=c99", breaks on some archs
+
Changelog for sg3_utils-1.12 [20050121]
- sg_wr_mode: new utility to modify (i.e. write to) mode pages
- sg_reassign: new utility: issues Reassign Blocks command
diff --git a/COVERAGE b/COVERAGE
index 06faabb1..a5390ee0 100644
--- a/COVERAGE
+++ b/COVERAGE
@@ -7,14 +7,17 @@ on the right. The second table lists supported ATA commands.
SCSI command sg3_utils utilities that use this SCSI command
------------ ----------------------------------------------
GET CONFIGURATION sg_get_config
-INQUIRY sg_inq, sginfo, sg_logs, sg_map('-i'), sg_modes,
- sg_opcodes, sg_persist, sg_scan, sg_ses
+INQUIRY sg_dd, sg_inq, sginfo, sg_logs, sg_map('-i'), sg_modes,
+ sg_opcodes, sg_persist, sg_scan, sg_ses, sg_format
+FORMAT UNIT sg_format
LOG SELECT sg_logs('-r')
LOG SENSE sg_logs
-MODE SELECT(6) sg_wr_mode, sginfo, sg_emc_trespass
-MODE SELECT(10) sg_wr_mode, sginfo, sg_emc_trespass
-MODE SENSE(6) sg_modes, sg_wr_mode, sginfo, sg_senddiag('-e')
-MODE SENSE(10) sg_modes, sg_wr_mode, sginfo, sg_senddiag('-e')
+MODE SELECT(6) sg_wr_mode, sginfo, sg_format, sg_emc_trespass
+MODE SELECT(10) sg_wr_mode, sginfo, sg_format, sg_emc_trespass
+MODE SENSE(6) sg_modes, sg_wr_mode, sginfo, sg_format, sg_dd,
+ sg_senddiag('-e')
+MODE SENSE(10) sg_modes, sg_wr_mode, sginfo, sg_format, sg_dd,
+ sg_senddiag('-e')
PERSISTENT IN sg_persist
PERSISTENT OUT sg_persist
PREVENT ALLOW MEDIUM REMOVAL sg_prevent
@@ -23,11 +26,11 @@ READ(10) sg_dd, sgm_dd, sgp_dd, sg_read
READ(12) sg_dd, sgm_dd, sgp_dd, sg_read
READ(16) sg_dd, sgm_dd, sgp_dd, sg_read
READ BUFFER sg_rbuf, sg_test_rwbuf
-READ CAPACITY(10) sg_readcap, sg_dd, sgm_dd, sgp_dd
-READ CAPACITY(16) sg_readcap, sg_dd, sgm_dd, sgp_dd
+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
READ DEFECT(12) sginfo
-READ LONG sg_read_long
+READ LONG sg_read_long, sg_dd
REASSIGN BLOCKS sg_reassign
RECEIVE DIAGNOSTIC sg_senddiag, sg_ses
REPORT LUNS sg_luns
@@ -38,7 +41,7 @@ REQUEST SENSE sg_requests
SEND DIAGNOSTIC sg_senddiag, sg_ses
START STOP sg_start
SYNCHRONIZE CACHE sg_sync, sg_dd, sgm_dd, sgp_dd
-TEST UNIT READY sg_turs
+TEST UNIT READY sg_turs, sg_format
VERIFY(10) sg_verify
WRITE(6) sg_dd, sgm_dd, sgp_dd
WRITE(10) sg_dd, sgm_dd, sgp_dd
@@ -54,4 +57,4 @@ IDENTIFY sg_inq, sg_scan
Doug Gilbert
-8th January 2005
+5th March 2005
diff --git a/CREDITS b/CREDITS
index 69ca3e1c..93fb6f5d 100644
--- a/CREDITS
+++ b/CREDITS
@@ -1,10 +1,14 @@
The author of sg3_utils would like to thank the following people who
have made contributions:
+
Andries Brouwer <aebr at win dot tue dot nl> rewrite of isosize (original
written by Joerg Schilling). isosize is now found in the util-linux
package and in the archive directory of this package.
+Christophe Varoqui <christophe dot varoqui at free dot fr> original sg_rtpg
+ [20041229]
+
Clayton Weaver <cgweav at email dot com> contributed safe_strerror().
Eric Schwartz <emschwar at debian dot org> who wrote these man pages:
@@ -16,9 +20,15 @@ Eric Youngdale <eric at andante dot org> author of scsi_info on which sginfo
F. Jansen: additions to sg_scan
+Grant Grundler <grundler at parisc-linux dot org> co-author of blk512-linux
+ that has become sg_format [20050201]
+
Heiko Eissfeldt <heiko at colossus dot escape dot de> sg based example
programs for the original sg driver
+James Bottomley <jejb at parisc-linux dot org> co-author of blk512-linux
+ that has become sg_format [20050201]
+
Kurt Garloff <garloff at suse dot de> original sg_start and sg_test_rwbuf.
Additions to sginfo and sg_map.
@@ -31,8 +41,11 @@ Martin Schwenke <martin at meltin dot net> added the raw switch "-r" to sg_inq
Pat LaVarre <p.lavarre at ieee dot org> pointed out danger of negative bpt
values in sg_dd (and friends); also problems when reading /dev/null
-Peter Allworth <linsol at zeta dot org dot au> original dd clone design used by
- sg3_utils's dd variants (e.g. sg_dd).
+Peter Allworth <linsol at zeta dot org dot au> original dd clone design used
+ by sg3_utils's dd variants (e.g. sg_dd).
+
+Remy Card has his copyright notice on llseek.c which is borrowed from the
+ util-linux package.
Saeed Bishara contributed sg_write_long
@@ -42,9 +55,6 @@ Tom Steudten <steudten at gmx dot ch> sginfo addition: add '-Fhead' option
Trent Piepho <xyzzy at speakeasy dot org> print out some "sense key specific"
data and "-6" switch for sg_modes
-Christophe Varoqui <christophe dot varoqui at free dot fr> original sg_rtpg
- [20041229]
-
Doug Gilbert
-30th December 2004
+11th March 2005
diff --git a/Makefile b/Makefile
index 74881df2..837c0943 100644
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@ EXECS = sg_dd sgp_dd sgm_dd sg_read sg_map sg_scan sg_rbuf \
sg_start sg_reset sg_modes sg_logs sg_senddiag sg_opcodes \
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_get_config sg_wr_mode sg_rtpg sg_reassign sg_format
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 \
@@ -22,7 +22,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_opcodes.8 sg_persist.8 sg_write_long.8 sg_read_long.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_rtpg.8 sg_reassign.8 sg_format.8
MAN_PREF = man8
HEADERS = sg_lib.h sg_cmds.h
@@ -31,9 +31,12 @@ HEADERS = sg_lib.h sg_cmds.h
LARGE_FILE_FLAGS = -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
# CFLAGS = -O2 -Wall -W -D_REENTRANT $(LARGE_FILE_FLAGS)
-# CFLAGS = -g -O2 -Wall -W -D_REENTRANT $(LARGE_FILE_FLAGS)
+CFLAGS = -g -O2 -Wall -W -D_REENTRANT $(LARGE_FILE_FLAGS)
# CFLAGS = -g -O2 -W -D_REENTRANT -DSG_KERNEL_INCLUDES $(LARGE_FILE_FLAGS)
-CFLAGS = -g -O2 -Wall -pedantic -std=c99 -D_REENTRANT $(LARGE_FILE_FLAGS)
+# CFLAGS = -g -O2 -Wall -pedantic -std=c99 -D_REENTRANT $(LARGE_FILE_FLAGS)
+
+# Some architectures do not like "-std-c99" due to 'asm' keyword
+CFLAGS_LLSEEK = -g -O2 -Wall -W -D_REENTRANT $(LARGE_FILE_FLAGS)
LDFLAGS =
# LDFLAGS = -v -lm
@@ -59,6 +62,9 @@ libsgutils.la: sg_lib.lo sg_cmds.lo
libtool --mode=link $(LD) -o libsgutils.la sg_lib.lo sg_cmds.lo \
-rpath $(LIBDIR) -version-info 1:0:0
+llseek.o: llseek.c
+ $(CC) $(INCLUDES) $(CFLAGS_LLSEEK) -o $@ -c $^
+
sg_inq: sg_inq.o libsgutils.la
libtool --mode=link $(LD) -o $@ $(LDFLAGS) $^
@@ -155,6 +161,9 @@ sg_rtpg: sg_rtpg.o libsgutils.la
sg_reassign: sg_reassign.o libsgutils.la
libtool --mode=link $(LD) -o $@ $(LDFLAGS) $^
+sg_format: sg_format.o libsgutils.la
+ libtool --mode=link $(LD) -o $@ $(LDFLAGS) $^
+
install: $(EXECS)
install -d $(INSTDIR)
install -d $(LIBDIR)
diff --git a/README b/README
index 8558e71c..9440d0a5 100644
--- a/README
+++ b/README
@@ -2,21 +2,32 @@
====================
Introduction
============
-This package contains utilities and test programs for the Linux SCSI
-subsystem. In the Linux 2.6 kernel series almost all of these programs
+This package contains utilities for the Linux SCSI subsystem. In the
+Linux 2.6 kernel series almost all of these programs
will work on any devices that use the SCSI command set. Apart from SCSI
parallel devices, ATAPI devices (CD/DVDs and tapes), USB mass storage
devices, Fibre Channel disks and IEEE 1394 storage devices (that use
the "SBP" protocol) use the SCSI command set.
+Most utilities within the sg3_utils package work at the SCSI command
+level. For example the sg_inq utility issues a SCSI INQUIRY command
+and decodes the rsponse. To view the relationship between the sg3_utils'
+utility and the main SCSI command(s) that it issues see the COVERAGE file.
+The only utilities that interface at a slightly higher level are sg_dd,
+sgm_dd and sgp_dd. These are closely related to the Unix dd command and
+typically issue a sequence of SCSI READ and WRITE commands to copy data.
+
+Description
+===========
A web site supporting the sg3_utils package, its predecessor sg_utils,
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 .
+http://www.torque.net/sg/u_index.html . A copy of the "u_index.html"
+file is in the "html" subdirectory.
-In the Linux 2.4 kernel series these programs need to use the SCSI generic
+In the Linux 2.4 kernel series these utilities need to use the SCSI generic
(sg) driver to access SCSI devices. The name of this package (i.e.
sg3_utils) refers to version 3 of the SCSI generic (sg) driver which
was introduced at the beginning of the 2.4 linux kernel series. Significantly
@@ -25,10 +36,10 @@ that is more flexible than the older "sg_header" structure found in the
sg driver in the 2.2 and earlier linux kernel series. The sg_io_hdr structure
is also more flexible than the awkward (and limiting) interface to the
SCSI_IOCTL_SEND_COMMAND ioctl supported by the linux SCSI mid level.
-Also added in the version 3 sg driver was the SG_IO ioctl that is synchronous
+The version 3 sg driver also added the SG_IO ioctl that is synchronous
(i.e. it issues the requested SCSI command and waits for the response
(or a timeout) before the ioctl returns to the user space program that
-invoked it). The SG_IO ioctl is now found in other parts of the linux
+invoked it). The SG_IO ioctl is now supported in other parts of the linux
kernel in the 2.6 series.
Utilities that wish to use the asynchronous SCSI command interface
@@ -46,19 +57,30 @@ pdf renderings are also in that directory.
Older documentation for the sg version 3 driver can be found at:
http://www.torque.net/sg/p/scsi_generic_v3.txt .
-All programs are "GPL"-ed so you can incorporate all or part of them
-in your applications as you please. The "sg_lib.[hc]" files contain
-ASCII text corresponding to most of the error and warning conditions
-defined in recent drafts at www.t10.org which is the home site of
-SCSI (draft) standards.
-
-Additional information (including a version number) can be found
-towards the top of each ".c" file corresponding to each utility and
-test program.
-
+All utilities are either "GPL"-ed or have a FreeBSD license. The
+intention is that users may incorporate all or part of the code in their
+work as they please. Attribution is encouraged. Please check the code as
+other contributors (apart from the author) may also have copyright
+notices. For a list of contributors see the CREDITS file.
+
+The "sg_lib.[hc]" files contain ASCII text corresponding to most
+of the SCSI commands, errors and warning conditions. The "sg_cmds.[hc]"
+files contains code to invoke common SCSI commands and associated
+response processing. Both are guided by recent drafts at www.t10.org
+which is the home site of SCSI (draft) standards. Since almost all
+of the utilities use these files, a shared library called
+"libsgutils.so" is built. Use of this library cuts down the size of the
+binary distributions of sg3_utils significantly. The "Makefile" provided
+in the main directory builds libsgutils.so and then builds each utility
+to use that shared library. Alternatively there is a script called
+"make_no_lib.sh" that will build utilities without depending on
+libsgutils.so . For example "./make_no_lib.sh sg_dd" builds a version
+of the sg_dd utility that does not depend on libsgutils.so .
+
+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.
-Scope
-=====
The sg driver in Linux can be seen as having 3 distinct versions:
v1 lk < 2.2.6 sg_header based relatively unchanged since 1992
@@ -72,9 +94,10 @@ package has a subset of the utilities found in this package.
Some sg ioctls (notably SG_IO) are defined for many block devices
in lk 2.6 . In practice this means all SCSI block devices, ATAPI block devices
-(mainly CD and DVD players) but _not_ ATA disks (currently). Support for
-the SG_IO as been added to the scsi tape driver (st) in lk 2.6.6 (and
-support for osst will soon follow).
+(mainly CD and DVD players) but _not_ ATA disks (currently). SATA disks that
+use the libata kernel library accept SCSI commands and thus are supported.
+Support for the SG_IO as been added to the scsi tape driver (st) in lk 2.6.6
+(and support for osst and ch (medium changer) may soon follow).
No utilities in the main directory use the sg driver's older "sg_header"
interface; instead they use the newer "sg_io_hdr" interface. The "sg_io_hdr"
@@ -82,292 +105,84 @@ interface can be accessed two ways:
- using the SG_IO ioctl [for synchronous access]
- using a write()/read() sequence that convey instances of "sg_io_hdr"
-All utilities in the main directory use the SG_IO ioctl except for sgp_dd.
-This is due to the asynchronous nature of sgp_dd. Note that sgp_dd does _not_
-support the "blk_sgio" switch found in sg_dd. This is important since block
-devices often identify themselves (programmatically) as sg devices in lk 2.6
-and it would cause serious damage to do a write() of the sg driver's "sg_io_hdr"
-metadata (i.e. disk corruption).
+All utilities in the main directory that issue SCSI commands use the SG_IO
+ioctl except for sgp_dd. This is due to the asynchronous nature of sgp_dd.
+Note that sgp_dd does _not_ support the "blk_sgio" switch found in sg_dd.
+This is important since block devices often identify themselves
+(programmatically) as sg devices in lk 2.6 and it would cause serious damage
+to do a write() of the sg driver's "sg_io_hdr" meta data (i.e. disk
+corruption).
Utilities
=========
-Here is a (somewhat arbitrary) categorization of the utilities included
-in this package:
- 1) dd variants: sg_dd, sgp_dd, sgm_dd and sgq_dd
- 2) scanning and mapping: sg_scan, sg_map and scsi_devfs_scan
- 3) SCSI support: sg_inq, scsi_inquiry, sginfo, sg_readcap, sg_start,
- sg_modes, sg_logs, sg_senddiag, sg_reset, sg_opcodes, 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 and sg_reassign
- 4) timing and testing: sg_rbuf, sg_test_rwbuf, sg_read, sg_turs,
- and sg_debug
- 5) example programs: sg_simple1, sg_simple2, sg_simple3, sg_simple4
- and sg_simple16
- 6) miscellaneous programs: isosize, hxascdmp
-
-There is a brief description of each utility in the following sections.
-All utilities in the main directory have a "man" page in section 8
-which is a category for administration and privileged commands. Some
-utilities in the other (sub-)directories have man pages.
+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_inq, sg_logs, sg_luns, sg_map, sg_modes, sg_opcodes,
+ sg_persist, sg_prevent, sg_rbuff, sg_read, sg_readcap, sg_read_long,
+ sg_reassign, sg_request, sg_reset, 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
+
+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
+Makefile's in the subdirectories. Each utility in the main directory has a
+"man" page in section 8 (system administration commands). Binary
+distributions of the sg3_utils package (e.g. "rpm" and debian packages)
+typically contains the shared library, the utilities in the main directory,
+their associated man pages and some documentation files (e.g. README, INSTALL,
+CREDITS and COVERAGE). See the INSTALL file for instructions about building
+and installing from a "tarball".
+
+Man pages can be read (without building and installing the package) by
+going to the main directory and executing something like this:
+ $ man ./sg_dd.8
To see which SCSI commands (and ATA commands) are used by these
utilities see the COVERAGE file.
-
-1) dd variants
---------------
-The main utility is a variant of the standard Unix command "dd" that
-is called "sg_dd". This program takes a useful subset of the command
-line arguments that "dd" takes. Furthermore "sg_dd" will only work if
-one or both of the given files (ie "if" or "of") is an "sg" or a raw
-device. If "bs" (block size) is not given it is assumed to be 512 bytes.
-Available dd options:
- bs=<n> typically 512 or 2048
- ibs=<n> if given must be the same as "bs"
- obs=<n> if given must be the same as "bs"
- if=<name> like dd plus sg device or "-" (read from stdin)
- of=<name> like dd plus sg device or "-" (write to stdout)
- skip=<n> block offset to start reading "if"
- seek=<n> block offset to start writing "of"
-Extra options:
- bpt=<n> blocks per transfer (default 128)
- dio=<n> 0 or 1, request direct IO (default 0)
- cdbsz=6|10|12|16 allow the command size of SCSI READ and WRITE commands
- to be specified (default is 10)
- sync=0|1 when 1, do a SYNCHRONIZE CACHE on "of" after the transfer
- is complete
- fua=0|1|2|3 when 0, do not set 'force unit access' bit; when 1, set it
- on "of"; when 2, set it on "if"; when 3, set it on "if"+"of"
- time=0|1 times the transfer and calculates a throughput figure when
- 1, output sent to stderr. Default is 0
- coe=0|1 continue on error (only on sg devices) when 1. Default is 0.
-
-Numeric arguments can be given in decimal or hexadecimal. Hex arguments should
-be prefixed with "0x" or "0X". Decimal arguments can take multiplier suffixes:
- "c", "C" * 1
- "b", "B" * 512
- "k" * 1024 [2 ^ 10]
- "K" * 1000 [10 ^ 3]
- "m" * 1048576 [2 ^ 20]
- "M" * 1000000 [10 ^ 6]
- "g" * 1073741824 [2 ^ 30]
- "G" * 1000000000 [10 ^ 9]
- "t" * 1099511627776 [2 ^ 40]
- "T" * 1000000000000 [10 ^ 12]
-The 'skip' and 'seek' options lead to the use of the system command
-lseek() to a byte offset when used on raw devices and normal files.
-[For sg devices 32 bit block addresses are used thus limiting accesses
-on disks with 512 byte blocks to 1 TB.] On 32 bit architectures the
-normal lseek() is limited to a signed 32 bit byte offset (i.e. 2 GB).
-"sg_dd" bypasses this limit by using Linux's _llseek() [while modern
-"dd" commands use read loops to "walk" around the limit].
-If 'count' is not given then the SCSI READ CAPACITY command will be
-used (on sg devices) if appropriate. Block devices are queried for their
-length if 'count' is not given. [Note that READ CAPACITY often
-gives a 2 block overestimate for iso9660 file systems on CD-ROMs.
-See the "isosize" command below.] Disk partition information can
-be found with a command like "fdisk -ul /dev/sda". The 'dio' argument
-requests direct IO (only functions in 2.4 kernels). A warning is issued
-if direct IO is requested and /proc/scsi/sg/allow_dio == 0 .
-"of=/dev/null" causes writes to be skipped, and "of=." is accepted as
-an alias for "of=/dev/null" (convenient shorthand).
-
-"sgp_dd" uses POSIX threads and attempts to run multiple IO operations
-in parallel. The user can control the amount of parallelism from
-1 worker (i.e. single threaded) through to 16 worker threads. This is
-done via the "thr=<n>" option (default 4). Copies from one sg device to
-another can be considerably faster due to this parallelism. There is
-also some speed benefit when raw devices are used. To obtain fine level
-SCSI command level control, sg devices should be used rather than block
-devices which will be interpreted as normal Unix files.
-
-"sgm_dd" is very similar to sg_dd but it uses mmap-ed IO on one of its
-given sg device names. If both "if" and "of" are sg devices then mmap-ed
-IO is selected on "if" while normal IO is selected on "of". Direct IO
-cannot be selected with this command. To obtain fine level SCSI command
-level control, sg devices should be used rather than block devices which
-will be interpreted as normal Unix files.
-
-"sgq_dd" is yet another implementation found in the archive directory.
-From the user point of view it is very similar to sgp_dd but uses a
-non-blocking state machine rather then POSIX threads for parallelism.
-
-
-2) Scanning and mapping
------------------------
-"sg_scan" does a scan of all sg device and prints the results to
-standard output. Alternatively one or more device names can be
-given (e.g. 'sg_scan -i /dev/scd[01]') and these will be scanned instead
-of the sg devices. Additional information from the INQUIRY command can
-be obtained by using the "-i" option.
-
-"sg_map" shows the mapping between sg device names and those of the
-sd, sr and st device names. Some devices such as scanners have no
-corresponding sd, sr nor st device names.
-
-"scsi_devfs_scan" is a utility for doing a directory scan on a system
-running devfs to identify SCSI (and optionally IDE) devices. Various
-information (including an INQUIRY) can be listed for each found device.
-
-
-3) SCSI support
----------------
-"sg_inq" is a utility for examining the INQUIRY command which
-contains much interesting information. It is based on SCSI 3's SPC-2
-document. Has switches to output "vital product data" and "command support"
-(now obsolete) pages. It can output in formatted ASCII, hex or binary.
-Has '-i' to decode the now mandatory device identification VPD page (0x83).
-This command is applicable to SCSI 2 (and perhaps SCSI 1) devices as well.
-
+Here is a list in alphabetical order of utilities found in the archive
+subdirectory:
+ - isosize, scsi_devfs_scan, sg_debug, sgq_dd
+Some of these utilities have man pages.
+
+Here is a list in alphabetical order of utilities found in the examples
+subdirectory:
+ - scsi_inquiry, sg_simple1, sg_simple2, sg_simple3, sg_simple4,
+ sg_simple16.c
+
+Also in that subdirectory is a script to test sg_persist, an example
+data file for sg_persist (called "transport_ids.txt") and an example
+data file for sg_reassign (called "reassign_addr.txt").
+
+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.
+
+The "html" subdirectory contains copies of web pages relevant to
+the sg3_utils package. Currently it contains:
+ - u_index.html : a description of the sg3_utils package contents
+ - sg_dd.html : describes the sg_dd utility and compares it with the
+ Unix dd command
+
+
+Notes for utilities without man pages
+=====================================
The "scsi_inquiry" program shows the use of the SCSI_IOCTL_SEND_COMMAND
ioctl to send a SCSI INQUIRY command. That ioctl() is supported by the
SCSI sub system mid level and so is common to all sd, sr, st and sg devices.
This program has been placed in the "examples" subdirectory.
-"sginfo" is a port of the "scsiinfo" program by Eric Youngdale to use the sg
-devices. It can still take other SCSI device nodes (e.g. /dev/sda, /dev/st0
-or /dev/hdc (if "hdc" is an ATAPI device)). This program outputs mode
-page (and subpage) information and allows it to be modified. Additionally
-defect lists (for disks) can be output and information from the SCSI INQUIRY
-command is available.
-
-"sg_readcap" call a READ CAPACITY command on the given device. Now also
-supports "partial medium indicator" (PMI) flag that indicates where the
-next significant delay will be on the media (after a given address).
-
-"sg_start" has been provided by Kurt Garloff <garloff at suse dot de>
-for spinning up (or down) disks.
-see the README.sg_start file.
-
-"sg_modes" and "sg_logs" print out mode sense pages and log sense pages
-respectively.
-
-"sg_senddiag" permits various device self tests to be run. It can also
-list the diagnostic pages support by a device.
-
-"sg_reset" exercises the SCSI device/bus/host reset capability. It is
-supported by the sg driver in lk 2.2.16 and beyond but associated
-SCSI middle level driver changes have not been accepted into the
-standard kernel at this time. Many distributions contain the patch to
-the mid-level that activates this feature. This patch is in later
-lk 2.4 versions and in the lk 2.6 series. This utility will only
-work on sg device names.
-
-"sg_opcodes" lists available opcode names. It uses the REPORT SUPPORTED
-OPERATION CODES SCSI command which was introduced in February 2002
-(and replaced the Cmd modes (CmdDt bit) in the INQUIRY command). Opcode
-names include various SCSI commands with service actions (e.g.
-the Maintenance In command) and variable length commands. The command
-list is either unsorted (as returned by the device), numerically sorted
-(default) or alphabetically sorted. An individual opcode (and optionally
-a service action) can be given in which case the "changeable" bits of
-that command are output. With the '-t' command line option this utility
-will list supported task management functions.
-
-"sg_persist" accesses the Persistent Reservation In and Out commands.
-These are relatively complex commands and potential users are referred
-to the SPC-3 document at http://www.t10.org . That said, it is
-relatively safe to query the state of existing registrations and
-reservations with the PR In command (use the switch "--in" with
-sg_persist to enforce this).
-
-"sg_write_long" can be used to write a "long" block to the given device
-(typically a disk). A "long" block includes data, ECC information and
-and other vendor specific information.
-
-"sg_read_long" can be used to read a "long" block from the given device
-(typically a disk).
-
-"sg_requests" issues a REQUEST SENSE SCSI command. One SPC-3 addition
-supported by this utility is the 'DESC' flag to request sense data in
-descriptor format.
-
-"sg_ses" issues SEND and RECEIVE DIAGNOSTIC PAGE commands to the
-given device which is assumed to be either an enclosure or another
-type of device (e.g. disk) that supports emclosure services. The
-latter case is indicated by a device setting the "EncServ" bit in
-a standard INQUIRY response.
-
-"sg_verify" verifies media by invoking SCSI VERIFY commands.
-
-"sg_emc_trespass" permits modification of EMC specific trespass
-mode page.
-
-"sg_emc_trespass" sends a REPORT LUNS SCSI command to the given device
-and outputs the response. The '--select' options allows the "select
-report" field to be specified.
-
-"sg_luns" sends a REPORT LUNS SCSI command to the given device and
-outputs the luns returned in the reponse. When the '--decode' option
-is given the luns are decoded as defined in the SAM-3 draft standard.
-
-"sg_sync" sends a SYNCHRONIZE CACHE SCSI command to the given device.
-Various options allow part of the cache (and range of logical block
-addresses) and synchronized.
-
-"sg_prevent" sends a PREVENT ALLOW MEDIUM REMOVAL SCSI command to the
-given device. Implemented for general removable SCSI devices (see SPC-3)
-with a specialization for cd/dvd devices (see MMC-4).
-
-"sg_get_config" sends a GET CONFIGURATION SCSI command to the given device
-and decodes the response. The command is only implemented for devices that
-use the Multi-Media command set (MMC 3,4,5) such as CDs and DVDs. Even
-though this is a SCSI command set it is commonly found on ATAPI, USB and
-ieee1394 transports.
-
-"sg_wr_mode" allows mode pages to be altered. It views a mode page as a
-string of bytes which may have been obtained from 'sg_modes -r'. There
-is also a facility to change mode pages in sginfo however the facility
-in sg_wr_mode is lower level and more general. [sginfo can only modify
-known fields in known (i.e. standard) mode pages.]
-
-"sg_rtpg" sends a REPORT TARGET PORT GROUPS SCSI command to the given device
-and decodes the response. Useful for dual ported devices especially if
-the access is asymmetric (i.e. one port on FC and the other on iSCSI).
-
-"sg_reassign" sends a REASSIGN BLOCKS SCSI command to the given device.
-This command is defined in SBC-2 so it is only relevant for direct
-access devices (e.g. a disk). It is intended to reassign one or more
-logical blocks addresses from defective parts of the media to blocks
-held in reserve. Blocks with recoverable errors will be reassigned
-automatically if the ARRE (for reads) and/or AWRE (for writes) bits
-are set in the "Read Write Error Recovery" mode page.
-
-
-4) Timing and testing
----------------------
-"sg_rbuf" does repeated SCSI READ BUFFER commands which allows SCSI
-bus bandwidth and the SCSI sub-system throughput to be measured.
-This can be done in 4 modes: normal transfer to user space, no
-transfer to user space, direct IO or mmap-ed IO. The latter one wins
-on my hardware.
-
-"sg_test_rwbuf" is a program by Kurt Garloff <garloff at suse dot de> that
-has the following description: Program to test the SCSI host adapter by
-issuing write and read operations on a device's buffer and calculating
-checksums.
-
-"sg_read" reads multiple blocks of data starting at the same logical
-address. It can time the transfers (potentially ignoring the first
-issued command) and calculate a MB/sec figure. [In keeping with most
-disk manufacturers, "MB" is 1,000,000 bytes in this context.] Its
-command line syntax is modelled on "sg_dd". It allows SCSI device,
-SCSI bus bandwidth and the SCSI sub-system throughput to be measured.
-This can be done in 3 modes: normal transfer to user space, direct
-IO or mmap-ed IO.
-
-"sg_turs" executes a user specified number of TEST UNIT READY commands on
-the given device. This can be used to time SCSI command overhead.
+"sgq_dd" is yet another variant of dd found in the archive directory.
+From the user's point of view it is very similar to sgp_dd but uses a
+non-blocking state machine rather then POSIX threads for parallelism.
"sg_debug" is effectively defunct now. The user can instead do:
$ cat /proc/scsi/sg/debug . This command has been placed in the
archive directory.
-
-5) Example programs
--------------------
"sg_simple1" and "sg_simple2" are simple example programs demonstrating
calls to the SCSI INQUIRY and TEST UNIT READY commands. They only differ
in their error processing: sg_simple1 uses sg_lib.[hc] for error
@@ -384,35 +199,6 @@ 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).
-All these example programs (plus scsi_inquiry) have been placed in the
-"examples" subdirectory with their own Makefile.
-
-
-6) Miscellaneous
-----------------
-"isosize" is a utility that gives the number of bytes in an iso9660
-file system. It is a rewrite by Andries Brouwer<Andries.Brouwer at cwi dot nl>
-of a utility that first appeared in the cdwrite package but is now
-difficult to obtain. Note that the value given by isosize is usually
-2 or more blocks less than the READ CAPACITY SCSI command yields on
-a CD-ROM (due to run out sectors). This command has a "man" page
-[section 8]. This utility has been moved to the archive directory
-as isosize is now available in the util-linux-2.10s package (and later).
-
-Building
-========
-A Makefile is provided that builds the above utilities and test programs
-'make' and 'make all' will cause everything (that is stale) to be built.
-
-A complete rebuild can be forced by executing 'make clean' prior to
-any of the above make commands. Individual commands can be built be
-giving the executable name to make, for example: 'make sg_dd'.
-
-There is also a 'make dep' but that shouldn't be needed very often.
-A 'make install' will build if necessary and then install the
-executables into /usr/local/bin by default (controlled by variable
-INSTDIR). The "man" pages are loaded int /usr/local/man/man8 by default.
-
Header file problems
====================
These utilities include 2 special Linux header files:
@@ -433,7 +219,7 @@ That caused the following:
#include <linux/../scsi/sg.h>
#include <linux/../scsi/scsi.h>
to find the kernel supplied header files. However recent versions of
-glibc have removed this symlink! Hence this technique is no longer
+glibc have removed this symlink. Hence this technique is no longer
recommended.
The include file path issues are now all addressed in one file called
@@ -456,4 +242,4 @@ user). It is available for Linux and other operating systems.
See http://members.aol.com/plscsi .
Doug Gilbert
-21st January 2005
+13th March 2005
diff --git a/html/sg_dd.html b/html/sg_dd.html
new file mode 100644
index 00000000..6e639479
--- /dev/null
+++ b/html/sg_dd.html
@@ -0,0 +1,807 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <meta http-equiv="Content-Type"
+ content="text/html; charset=iso-8859-1">
+ <meta name="keywords" content="Linux, SCSI, sg_dd, dd variant">
+ <meta name="GENERATOR"
+ content="Mozilla/4.79 [en] (X11; U; Linux 2.5.31 i686) [Netscape]">
+ <title>Linux sg_dd utility (a dd variant)</title>
+</head>
+<body alink="#ff0000" background="paper.jpg" bgcolor="#ffffff"
+ link="#0000ff" text="#000000" vlink="#000080">
+<center>
+<h1> The&nbsp; Linux sg_dd utility<br>
+</h1>
+</center>
+<a href="#Introduction">Introduction</a> <br>
+<a href="#dd_like_features">dd like features</a> <br>
+<a href="#sg_dd_extras">sg_dd extras</a><br>
+<a href="#coe">Continue on error (coe)</a> <br>
+<a href="#Recovered_errors">Recovered errors</a> <br>
+<a href="#Verbose">Verbose</a><br>
+<a href="#Conclusion">Conclusion</a><br>
+&nbsp;
+<h2><a name="Introduction"></a>Introduction</h2>
+The <span style="font-weight: bold;">sg_dd</span> utility is a variant
+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 sg3_utils package which targets the linux kernel 2.4 and
+2.6 series.<br>
+<br>
+Not all SCSI device types are supported by the sg_dd utility. Obviously
+those device types that deal with enclosures and medium changers don't
+have addressable blocks and are not supported. The supported device
+types are direct access devices (e.g. disks) and cd/dvd devices. The
+SCSI tape device type is not be supported (but could be to a limited
+degree). When instructed, the sg_dd utility issues SCSI
+commands that are defined in SPC-3 (primary commands), SBC-2 (commands
+for direct access devices (e.g. disks)) and MMC-4 (commands for CD/DVD
+devices). These SCSI command sets can be found at <a
+ href="http://www.t10.org/">www.t10.org</a>
+. <br>
+<br>
+It is becoming common for non-SCSI devices (e.g. ATA disks) to appear
+as SCSI devices to an operating system via a protocol conversion in an
+external enclosure and via some transport such as USB or IEEE 1394. The
+sg_dd utility should work with most of these devices as it tends to use
+exactly the same SCSI commands that the normal block layer would uses.
+However, advanced options (e.g. using the 'cdbsz' and 'fua') most
+likely will be ignored. Apart from CD players over 10 years old, almost
+all CD/DVD players use the Multi Media Command set (MMC or&nbsp;
+MMC-2,3,4,5) as their native command set. The fact that the most common
+transport is ATAPI (i.e. the ATA packet interface) is irrelevant, to
+the
+sg_dd utility they are SCSI devices.<br>
+<br>
+This page outlines the features of the sg_dd utility version 5.38 found
+in the sg3_utils version 1.13 package. This was released on the 13th
+March 2005.<br>
+<h2><a name="dd_like_features"></a> 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
+almost all other standard Unix commands. Those familiar with the dd
+command
+should not be too surprised by the syntax and semantic of the sg_dd
+utility. Those not familiar with the dd syntax should be very careful,
+especially with the 'of' and 'seek' options, both with dd and sg_dd.
+The recent GNU implementation of the dd command is used as a reference
+point.<br>
+<br>
+The main options of dd are:<br>
+<ul>
+ <li>if=&lt;input_file&gt;</li>
+ <li>of=&lt;output_file&gt;</li>
+ <li>bs=&lt;block_size&gt;</li>
+ <li>count=&lt;count_of_blocks&gt;</li>
+</ul>
+Both dd and sg_dd given these options with suitable arguments will copy
+(bs * count) bytes from the beginning of &lt;input_file&gt; to the
+beginning of &lt;output_file&gt;. One restriction that sg_dd imposes
+when either the &lt;input_file&gt; or &lt;output_file&gt; are accessed
+via SCSI commands is that &lt;block_size&gt; must match that of the
+device. Further, if both the &lt;input_file&gt; and &lt;output_file&gt;
+are accessed via SCSI commands then &lt;block_size&gt; must match that
+of both devices. In both dd and sg_dd if the if=&lt;input_file&gt;
+option is not given then stdin is assumed. In both dd and sg_dd if
+of=&lt;output_file&gt; is not given then stdout is assumed.<br>
+<br>
+The following extensions are found in sg_dd. An &lt;input_file&gt; of
+"-" is interpreted as stdin; an &lt;output_file&gt; of "-" is
+interpreted as stdout while an &lt;output_file&gt; of "." is
+interpreted as <span style="font-family: monospace;">/dev/null</span>.
+[dd interprets input and output file names of
+"-" literally. dd interprets an output file of "." as the current
+directory and will not accept it.]<br>
+<br>
+If the 'count' option is not given then an attempt is made to determine
+the remaining blocks in the file, device or partition. If the input
+file is stdin and no count is given then a copy will continue until an
+EOF is detected on the input stream (or something else goes wrong). If
+the 'count' option is not given then the remaining blocks on both the
+input and output files are determined (if possible) and if both are
+found then the minimum of
+the two counts is used.&nbsp; The 'skip' option for an input file and
+the 'seek' option for an output file are taken into account when
+calculating the remaining number of blocks in a file, device or
+partition.<br>
+<br>
+If the 'count' option is given then no further checks regarding the
+remaining length of the input and output files are done and the sg_dd
+will attempt to copy that number of blocks. A 'count=0' option is valid
+and all the normal preparations are made including files being opened
+but no copy takes place. Hence the 'count=0' option can be used to
+check the syntax is in order and the files are present (see the
+"Verbose" section below).<br>
+<br>
+Other dd options supported by sg_dd:<br>
+<ul>
+ <li>seek=&lt;n_blocks_of&gt;</li>
+ <li>skip=&lt;n_blocks_if&gt;</li>
+ <li>ibs=&lt;block_size&gt;</li>
+ <li>obs=&lt;block_size&gt;</li>
+</ul>
+If ibs and obs are given to sg_dd then they must be the same as bs
+(i.e. they are essentially dummies). Both seek and skip are origin zero
+and default to zero (i.e the start of the output and input files
+respectively). The 'skip' option cannot be used on an input stream
+(e.g. stdin) while the 'seek' option cannot be used on an output stream
+(e.g. stdout).<br>
+<br>
+All numeric arguments can take a multiplier suffix. From sg3_utils
+version 1.13 sg_dd's multiplier prefixes have been brought into line
+with those of GNU's dd (in coreutils post a change on&nbsp; 2001-12-18):<br>
+<br>
+<table style="width: 50%; text-align: left;" border="1" cellpadding="2"
+ cellspacing="2">
+ <tbody>
+ <tr>
+ <td style="vertical-align: top;"><span style="font-weight: bold;">Multiplier</span><br>
+ </td>
+ <td style="vertical-align: top;"><span style="font-weight: bold;">Meaning</span><br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">x&lt;n&gt;<br>
+ </td>
+ <td style="vertical-align: top;">*&lt;n&gt;<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">c<br>
+ </td>
+ <td style="vertical-align: top;">*1<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">w<br>
+ </td>
+ <td style="vertical-align: top;">*2<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">b<br>
+ </td>
+ <td style="vertical-align: top;">*512<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">k&nbsp; K&nbsp; KiB<br>
+ </td>
+ <td style="vertical-align: top;">*1024<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">KB<br>
+ </td>
+ <td style="vertical-align: top;">*1000<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">m&nbsp; M&nbsp; MiB<br>
+ </td>
+ <td style="vertical-align: top;">*1048576 </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">MB<br>
+ </td>
+ <td style="vertical-align: top;">*1000000<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">g&nbsp; G&nbsp; GiB<br>
+ </td>
+ <td style="vertical-align: top;">2**30<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">GB<br>
+ </td>
+ <td style="vertical-align: top;">10**9<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">t&nbsp; T&nbsp; TiB<br>
+ </td>
+ <td style="vertical-align: top;">2**40 </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">TB<br>
+ </td>
+ <td style="vertical-align: top;">10**12<br>
+ </td>
+ </tr>
+ </tbody>
+</table>
+<br>
+The pattern that starts with "k" and proceeds to "m", "g" and "t" then
+to "p", "e", "z" and "y"&nbsp; (not shown in above table). sg_dd only
+implements as far as "p" (10**15 or 2**50). sg_dd only allows
+multipliers based on "t" and "p" for 'count', 'skip' and 'seek'.<br>
+<br>
+sg_dd allows numeric arguments to be given in hexadecimal in which case
+they must be prefixed by either "0x" or "0X". A numeric argument cannot
+both be in hex and have a suffix multiplier. Hence "0x9" is interpreted
+as hexadecimal 9 [not (0 * 9)==0]. This is valid "2x4x0xa" and yields
+80 (but it isn't very clear).<br>
+<br>
+The following dd options are <span style="font-weight: bold;">not</span>
+supported by sg_dd:<br>
+<ul>
+ <li>cbs=&lt;bytes&gt;</li>
+ <li>conv=&lt;keyword&gt;</li>
+</ul>
+Basically sg_dd does not supported the conversion features of dd. If a
+conversion is really needed then sg_dd could be piped through dd (or
+vice versa).<br>
+<h2><a name="sg_dd_extras"></a>sg_dd extras</h2>
+The extra options of sg_dd (not found in GNU's dd) are:<br>
+<br>
+<ul>
+</ul>
+<table style="width: 100%; text-align: left;" border="1" cellpadding="2"
+ cellspacing="2">
+ <tbody>
+ <tr>
+ <td style="vertical-align: top;"><span style="font-weight: bold;">extra
+options in sg_dd</span><br>
+ </td>
+ <td style="vertical-align: top;"><span style="font-weight: bold;">default</span><br>
+ </td>
+ <td style="vertical-align: top;"><span style="font-weight: bold;">Brief
+description</span><br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">append=0|1<br>
+ </td>
+ <td style="vertical-align: top;">0<br>
+ </td>
+ <td style="vertical-align: top;">append to output file (rather
+than overwrite)<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">blk_sgio=0|1<br>
+ </td>
+ <td style="vertical-align: top;">0<br>
+ </td>
+ <td style="vertical-align: top;">when set access devices via SCSI
+commands (SG_IO)<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">bpt<br>
+ </td>
+ <td style="vertical-align: top;">128<br>
+ </td>
+ <td style="vertical-align: top;">blocks_per_transfer (granularity
+of each IO)<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">cdbsz=6|10|12|16<br>
+ </td>
+ <td style="vertical-align: top;">10 or 16<br>
+ </td>
+ <td style="vertical-align: top;">cdb size of SCSI READ and/or
+WRITE commands<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">coe=0|1<br>
+ </td>
+ <td style="vertical-align: top;">0<br>
+ </td>
+ <td style="vertical-align: top;">continue_on_error when set<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">dio=0|1<br>
+ </td>
+ <td style="vertical-align: top;">0<br>
+ </td>
+ <td style="vertical-align: top;">direct IO (only via sg device
+nodes)<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">fua=0|1|2|3<br>
+ </td>
+ <td style="vertical-align: top;">0<br>
+ </td>
+ <td style="vertical-align: top;">force_unit_access, 1-&gt;if,
+2-&gt;of, 3-&gt;if+of<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">odir=0|1<br>
+ </td>
+ <td style="vertical-align: top;">0<br>
+ </td>
+ <td style="vertical-align: top;">O_DIRECT flag on open() when set<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sync=0|1<br>
+ </td>
+ <td style="vertical-align: top;">0<br>
+ </td>
+ <td style="vertical-align: top;">SYNCHRONIZE CACHE SCSI command
+after transfer when set<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">time=0|1<br>
+ </td>
+ <td style="vertical-align: top;">0<br>
+ </td>
+ <td style="vertical-align: top;">when set print elapsed time and
+throughput after copy<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">verbose=&lt;n&gt;<br>
+ </td>
+ <td style="vertical-align: top;">0<br>
+ </td>
+ <td style="vertical-align: top;">larger &lt;n&gt; is the greater
+the debug output<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">--version<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">print the version number and
+release date of
+sg_dd then exit<br>
+ </td>
+ </tr>
+ </tbody>
+</table>
+<br>
+<br>
+The sg_dd utility examines the files it is given and treats them
+differently depending on the category and, in some case,s the command
+line options given:<br>
+<br>
+<table style="width: 100%; text-align: left;" border="1" cellpadding="2"
+ cellspacing="2">
+ <tbody>
+ <tr>
+ <td style="vertical-align: top;"><span style="font-weight: bold;">File
+type</span><br>
+ </td>
+ <td style="vertical-align: top;"><span style="font-weight: bold;">open
+[when input]</span><br>
+ </td>
+ <td style="vertical-align: top;"><span style="font-weight: bold;">open
+[when output]</span><br>
+ </td>
+ <td style="vertical-align: top;"><span style="font-weight: bold;">IO
+method</span><br>
+ </td>
+ <td style="vertical-align: top;"><span style="font-weight: bold;">Notes</span><br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">normal<br>
+ </td>
+ <td style="vertical-align: top;">O_RDONLY<br>
+ <br>
+ </td>
+ <td style="vertical-align: top;">O_WRONLY | O_CREAT<br>
+[add O_APPEND if 'append=1']<br>
+ </td>
+ <td style="vertical-align: top;">Unix read() write()<br>
+ </td>
+ <td style="vertical-align: top;">Add O_DIRECT if<br>
+'odir=1'<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">stdin or stdout<br>
+ </td>
+ <td style="vertical-align: top;">[do nothing]<br>
+ </td>
+ <td style="vertical-align: top;">[do nothing]<br>
+ </td>
+ <td style="vertical-align: top;">Unix read() write()<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">/dev/null<br>
+ </td>
+ <td style="vertical-align: top;">O_RDONLY</td>
+ <td style="vertical-align: top;">[do nothing]<br>
+ </td>
+ <td style="vertical-align: top;">Unix read() if input<br>
+ </td>
+ <td style="vertical-align: top;">if output then no IO<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">block device<br>
+ </td>
+ <td style="vertical-align: top;">O_RDONLY</td>
+ <td style="vertical-align: top;">O_WRONLY | O_CREAT<br>
+[add O_APPEND if 'append=1']</td>
+ <td style="vertical-align: top;">Unix read() write()</td>
+ <td style="vertical-align: top;">Add O_DIRECT if<br>
+'odir=1'</td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">block device [blk_sgio=1]<br>
+ </td>
+ <td style="vertical-align: top;">O_RDWR or O_RDONLY</td>
+ <td style="vertical-align: top;">O_RDWR</td>
+ <td style="vertical-align: top;">SCSI commands</td>
+ <td style="vertical-align: top;">Opens input O_RDONLY <br>
+if O_RDWR fails. Adds <br>
+O_DIRECT if 'odir=1' </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg device<br>
+ </td>
+ <td style="vertical-align: top;">O_RDWR or O_RDONLY<br>
+ </td>
+ <td style="vertical-align: top;">O_RDWR<br>
+ </td>
+ <td style="vertical-align: top;">SCSI commands<br>
+ </td>
+ <td style="vertical-align: top;">Opens input O_RDONLY<br>
+if O_RDWR fails</td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">raw device<br>
+ </td>
+ <td style="vertical-align: top;">O_RDONLY</td>
+ <td style="vertical-align: top;">O_WRONLY<br>
+ </td>
+ <td style="vertical-align: top;">Unix read() write()<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">scsi tape device<br>
+ </td>
+ <td style="vertical-align: top;">x<br>
+ </td>
+ <td style="vertical-align: top;">x<br>
+ </td>
+ <td style="vertical-align: top;">no IO<br>
+ </td>
+ <td style="vertical-align: top;">error reported<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ </tr>
+ </tbody>
+</table>
+<br>
+Some of the above combinations are not sensible (e.g. 'append=1' on a
+block device). Raw devices were introduced in the lk 2.4 series and are
+still available in the lk 2.6 series but opening a block device or a
+normal file with O_DIRECT (in sg_dd using the option 'odir=1') is
+preferred.<br>
+<br>
+The 'blk_sgio=1' option is only valid in the lk 2.6 series. This option
+instructs the sg_dd utility to use the SG_IO ioctl to issue SCSI
+commands even though the file type is not a scsi generic (sg) device.
+Currently this will work for all cd/dvd devices irrespective of the
+transport (except for some very old devices ( &gt; 10 years old)) and
+block devices representing SCSI disks (e.g. /dev/sda). Even though SCSI
+tape (st) devices now accept the SG_IO ioctl in the lk 2.6 series, the
+sg_dd utility expects direct access devices (whereas tapes are
+sequential access) so sg_dd generates an error if a st device is
+detected. When the input or output file is a sg device then the
+'blk_sgio' option is ignored as IO with that file is always via SCSI
+commands.<br>
+<br>
+If a partition of block device is accessed (e.g. /dev/sda2) when
+'blk_sgio=0' (or is not given) then logical block address 0 for the
+purposes of sg_dd (and its skip and seek options) is the beginning of
+that partition while the calculated count (e.g. when a 'count' option
+is not given) is the extent of that partition. However if a partition
+of block device is accessed (e.g. /dev/sda2) when 'blk_sgio=1' then the
+partition is ignored and the underlying device is accessed. This means
+logical block address 0 for the purposes of sg_dd (and its skip and
+seek options) is the beginning of the device while the calculated count
+(e.g. when a 'count' option is not given) is the extent of that device.<br>
+<br>
+Note about CDs and count (perhaps DVDs don't have this problem). When a
+CD is accessed via SCSI commands and the 'count' option is not given
+then the READ CAPACITY SCSI command is used by sg_dd to determine the
+number of blocks to read. Unfortunately some MMC standards allow a few
+of the final blocks to be invalid ("run out" sectors) and this can
+result in IO errors when the sg_dd utility is used to copy such a CD.
+The isosize utility can be used to determine the actual size of an
+ISO9660 file system residing on such a CD. It is often a few blocks
+shorter than READ CAPACITY reports (for good reason).<br>
+<br>
+When an input or output file is being accessed via SCSI commands the
+following "extra" options being discussed in this section are active:<br>
+<ul>
+ <li>bpt: each cdb issued will be for this value or less blocks (with
+the block size implied)<br>
+ </li>
+ <li>cdbsz: defaults to a 10 byte cdb if IO fits in 32 bit lba space
+otherwise 16 byte cdb</li>
+ <li>coe: when set error on read get special treatment (see below)</li>
+ <li>dio: only active for sg devices (i.e. not block device with
+'blk_sgio=1')</li>
+ <li>fua: force_unit_access flag in cdb; 1-&gt;if, 2-&gt;of;
+3-&gt;if+of</li>
+ <li>odir: active when file is a block device (i.e. inactive for sg
+devices)<br>
+ </li>
+ <li>sync: when set issues SYNCHRONIZE CACHE command at the end of the
+copy</li>
+ <li>time</li>
+ <li>verbose: 1 and 2 print setup cdbs, 3 and 4 print all cdbs</li>
+</ul>
+When an input or output file is being accessed via normal Unix read()
+and write() commands the following "extra" options being discussed in
+this section are active:<br>
+<ul>
+ <li>append: one active on output file that are "normal" (i.e. neither
+devices nor streams)<br>
+ </li>
+ <li>bpt: each IO operation transfers (bs * bpt) bytes or less</li>
+ <li>odir: opens files with the O_DIRECT flag</li>
+ <li>time</li>
+ <li>verbose: 1 + 2 print setup system calls, 3 and 4 additional print
+read() and write() system calls<br>
+ </li>
+</ul>
+If a SIGUSR1 signal is sent to the process identifier (pid) of a
+running sg_dd utility then the number of blocks copies to that point is
+output. The copy continues.<br>
+<br>
+When the 'time' option is set, the elapsed time for the copy plus the
+throughput measured in megabytes (10**6 bytes)&nbsp; per second is
+output when the copy is complete (or an error stops the copy). If a
+SIGUSR1 signal is sent to the process identifier (pid) of a running
+sg_dd utility which has the 'time' option set the elapsed time and the
+throughput of the copy to that point is output and the copy continues.<br>
+<h2><a name="coe"></a>Continue on error (coe)</h2>
+Recent additions to the sg_dd utility allow it to be used as a copy "of
+last resort" from failing media. Read errors on an input file
+taking SCSI commands are "tolerated" when the 'coe' option is set.
+Write errors from SCSI commands are reported and ignored and the sg_dd
+utility continues when the 'coe' option is set. [In the case where
+media errors are causing write errors the
+reader should check the setting of the AWRE bit in the SCSI "read write
+error recovery" mode page (see SBC-2 at http://www.t10.org).] As for
+errors in normal files the reader may wish to examine the
+"conv=noerror" option of the dd command.<br>
+<br>
+When a SCSI READ command detects an unrecoverable read error it&nbsp;
+responds with a sense key of MEDIUM ERROR or HARDWARE ERROR.
+Additionally it responds with the logical block address of the first
+(lowest) block that it failed to read in the current READ command. Any
+valid blocks prior to the "bad" block may or may not have been
+transferred (depending on several other settings). If 'coe=0' then the
+sg_dd utility will simply terminate at this point (with a reasonable
+amount of debug information sent to stderr). If 'coe=1' then the first
+thing it will try to do is a truncated read up to but not including the
+bad block. The remaining discussion in this section assumes 'coe=1'.<br>
+<br>
+The "bad" block may or may not be readable by the device. If it is
+readable then most likely the associated ECC data indicates that it is
+corrupt and the ECC data cannot recover it. [It is possible that all
+the corruption is in the ECC data and the payload data is ok.] Such
+blocks can be retrieved with the READ LONG command which is what the
+sg_dd utility does. If the READ LONG fails then a block of zeroes is
+substituted for the bad block in the transfer buffer. Only SBC-2
+devices (primarily SCSI disks) optionally support READ LONG. CD/DVD
+devices (covered in the MMC-4 (draft) standard) do not so a block of
+zeroes is substituted for them.<br>
+<br>
+There still remains blocks after the "bad" block in the initial
+transfer to be fetched. Further bad blocks may be detected and if so
+the algorithm in the last two paragraph is repeated. The result of this
+process is an imperfect copy with blocks that were read properly placed
+in the correct relative position in the output. When the 'coe=1' option
+is given two addition counters are output on completion:<br>
+<ul>
+ <li>unrecovered errors</li>
+ <li>read_longs fetched part of unrecovered read errors</li>
+</ul>
+The first counter is the number of unrecovered (read) errors
+encountered. Any number greater than zero flags an imperfect copy. The
+second counter (always less than or equal to the first) is the number
+of "successful" READ LONG SCSI commands performed. If the source media
+is a CD or DVD then this number will be zero.<br>
+<br>
+<h2><a name="Recovered_errors"></a>Recovered errors</h2>
+Often errors are recovered using ECC data or by the device retrying
+(usually re-reading) the media. Also at the first sign of trouble,
+recoverable errors lead to the block in question being reassigned to
+another location on the media (automatically when the AWRE and ARRE
+bits are set in the "read write error recovery" mode page).&nbsp; The
+user of such a disk may be blissfully unaware that the disk may be
+reaching the end of its useful life. Error counters are maintained in
+the "Read error counter" and "Write error counter" logs pages which can
+be viewed with smartctl (from smartmontools) and sg_logs (from the same
+sg3_utils package that sg_dd comes from). Any block that is
+automatically or manually re-assigned add a new entry to the "grown"
+defect list which cal be viewed with 'sginfo -G' (also found in the
+sg3_utils package).<br>
+<br>
+A disk can be instructed to report RECOVERED ERRORs by setting the PER
+bit in the "read write error recover" mode page. Most often this bit is
+clear. When sg_dd detects RECOVERED ERRORs it reports them, counts
+them and continues the copy. Only the lba of the last recovered error
+in a READ or WRITE SCSI
+command is reported so there could be more than one recovered error per
+SCSI command. The 'bpt=1' option
+could be chosen to limit every SCSI command to a single block transfer
+(but that would slow things down a fair amount). If the count of
+recovered errors is greater than zero at the end of the copy then this
+count is output as well.<br>
+<br>
+There can be other reasons for a sense key of RECOVERED ERROR not
+directly related to data being currently read or written. SMART alerts
+(called in SCSI documents "Informational Exceptions") can be conveyed
+via a RECOVERED ERROR sense key (see the MRIE field in the
+Informational Exceptions mode page). Such alerts have additional sense
+codes like "Failure prediction threshold exceeded" and those that
+contain "impending failure".<br>
+<h2><a name="Verbose"></a>Verbose</h2>
+In the Unix style, sg_dd doesn't output anything (to stderr) during
+large IO transfers. To get a progress report the SIGUSR1 signal can be
+sent to the sg_dd process. In the Unix dd command style, sg_dd outputs
+two lines on completion that show the number of full and partial
+records
+in (on the first line) and out (on the second line). Some modern
+versions of the dd command also output elapsed time and throughput
+figures (to get this with sg_dd use the "time=1" option).<br>
+<br>
+The sg_dd has a 'verbose' option whose default value is zero. When set
+this option has the following actions:<br>
+<ol>
+ <li>show categorization and INQUIRY data (where applicable) for the
+input and output files. For files, other than streams, the file/device
+size (and device block size) are output. For SCSI devices mode data
+relevant to error recovery and caching is output.</li>
+ <li>same output as 1 plus data for Unix and SCSI commands (cdbs) that
+are not repeated (i.e. other than Unix read/write and SCSI READ/WRITE
+commands). Increased error reporting for all SCSI commands</li>
+ <li>same output as 2 plus data for Unix and SCSI commands (cdbs) that
+are repeated. For a large copy this will be a lot of output.</li>
+ <li>maximum amount of debug output. For a large copy this will be a
+lot of output.</li>
+</ol>
+All verbose output is sent to stderr (so that sg_dd usage with "of=-"
+(copy output to stdout) is not corrupted).<br>
+<br>
+Following is an example of using "verbose=1" to find information about
+/dev/sda . If no copy is required then setting "count=0" will see to
+that. Since "dev/sda" is a block device then it would normally be
+accessed via Unix system commands. The "verbose=1" output is relatively
+short when "blk_sgio=0" (its default value). The second invocation is
+with "blk_sgio=1" and a lot more is output. That includes INQUIRY
+standard response data (e.g. "SEAGATE ..." line) followed by the
+setting of various mode pages relevant to error processing and caching.
+See the SBC-2 drafts at <a href="http://www.t10.org/">www.t10.org</a>
+for more information. As an example a AWRE bit of 1 means that the disk
+will automatically reallocate a logical block address (to another
+sector)
+if a recoverable error is detected on a WRITE operation. RECOVERED
+ERRORs are only report on read and write operations when the PER [Post
+Error] bit is set.<br>
+<br>
+<span style="font-family: monospace;">$ sg_dd if=/dev/sda of=. bs=512
+verbose=1 count=0 blk_sgio=0</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&gt;&gt; Input file type:
+block device</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+open input, flags=0x0</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&gt;&gt; Output file type:
+null device</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+[bgs64] number of blocks=17781521 [0x10f5311], block size=512</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">0+0 records in</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">0+0 records out</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;"><br>
+<br>
+$ sg_dd if=/dev/sda of=. bs=512 verbose=1 count=0 blk_sgio=1</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&gt;&gt; Input file type:
+block device</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+open input(sg_io), flags=0x2</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp; /dev/sda:
+SEAGATE&nbsp;&nbsp;
+ST39173LC&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1234&nbsp;
+[pdt=0]</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp; Read write error recovery
+mode page:</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp;
+AWRE:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp; [Changeable: y, def: 1,
+saved: 1]</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp;
+ARRE:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp; [Changeable: y, def: 1,
+saved: 1]</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp;
+RC:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp; [Changeable: y,
+def: 0, saved: 0]</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp;
+EER:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp; [Changeable: y, def:
+0, saved: 1]</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp;
+PER:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp; [Changeable: y, def:
+0, saved: 1]</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp;
+DTE:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp; [Changeable: y, def:
+0, saved: 0]</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp;
+DCR:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp; [Changeable: y, def:
+0, saved: 0]</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp; Caching mode page:</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp;
+WRE:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp; [Changeable: y, def:
+1, saved: 1]</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp;
+RCD:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp; [Changeable: y, def:
+0, saved: 0]</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp; Control mode page:</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp;
+SWP:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp; [Changeable: n, def:
+0, saved: 0]</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&gt;&gt; Output file type:
+null device</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+number of blocks=17781521 [0x10f5311], block size=512</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">0+0 records in</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">0+0 records out</span><br>
+<br>
+Some of the output above is out of order, the "number of blocks" line
+relates to the input file.<br>
+<h2><a name="Conclusion"></a>Conclusion</h2>
+The sg_dd utility maintains syntactic and semantic compatibility with
+the Unix dd command while allowing precise control over SCSI devices
+such as SCSI disks and CD/DVD drives. Amongst other uses it can copy
+data from failing media, continuing as best it can when medium errors
+are encountered.<br>
+<br>
+<br>
+<p>Return to <a href="index.html">main</a> page. </p>
+<center>
+<p>Last updated: 13th March 2005<br>
+<br>
+</p>
+</center>
+</body>
+</html>
diff --git a/html/u_index.html b/html/u_index.html
new file mode 100644
index 00000000..56ef7109
--- /dev/null
+++ b/html/u_index.html
@@ -0,0 +1,913 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <meta http-equiv="Content-Type"
+ content="text/html; charset=iso-8859-1">
+ <meta name="GENERATOR"
+ content="Mozilla/4.79 [en] (X11; U; Linux 2.5.31 i686) [Netscape]">
+ <title>Linux sg3_utils package</title>
+</head>
+<body alink="#ff0000" background="paper.jpg" bgcolor="#ffffff"
+ link="#0000ff" text="#000000" vlink="#000080">
+<center>
+<h1> The&nbsp; Linux sg3_utils package</h1>
+</center>
+<a href="#intro">Introduction</a> <br>
+<a href="#contents">Contents </a><br>
+<a href="#Examples"><span style="text-decoration: underline;">Examples</span></a><br>
+<a href="#notes">Notes</a> <br>
+<a href="#download">Download</a> <br>
+<a href="#others">Programs from other sources</a> <br>
+&nbsp;
+<h2> <a name="intro"></a>Introduction</h2>
+The <span style="font-weight: bold;">sg3_utils</span> package contains
+utilities
+that send SCSI commands to Linux devices. As well as devices on SCSI
+transports (e.g. Fibre channel and the SCSI parallel interface) many
+other devices use SCSI command sets. Examples are ATAPI cd/dvd writers
+and SATA disks (typically via a translation layer or bridge device).
+SCSI command sets are divided into what are called "primary" commands
+(e.g. SPC-3) and device class specific sets (e.g. SBC-2 for disks and
+MMC-4 for cd/dvd devices).&nbsp; SCSI command sets and transports
+definitions can be found at <a href="http://www.t10.org">www.t10.org</a>
+.<br>
+<br>
+The
+<span style="font-weight: bold;">sg3_utils</span> pacakge targets the
+linux kernel 2.4 and 2.6 series and is
+still under active development. An earlier package called sg_utils
+targeted the&nbsp; 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>&nbsp;
+for further information about sg_utils.&nbsp; This
+document describes
+version 1.13 of sg3_utils . <br>
+<br>
+In the linux kernel 2.4 series 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>
+and
+<span style="font-family: monospace;">/dev/hdd</span> (if it is an
+ATAPI device)). The SCSI generic (sg) interface
+still represents a cleaner interface than the primary device names
+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 (e.g. the cdrom driver interferes with attempts to
+prevent media removal with sg_prevent).<br>
+<p>SCSI utility programs for Linux from other sources are listed and
+briefly described in
+the final section. </p>
+<h2> <a name="contents"></a>Contents of sg3_utils</h2>
+Here is a listing in alphabetical order of the utilities in the
+sg3_utils package:<br>
+<br>
+<table style="width: 100%; text-align: left;" border="1" cellpadding="2"
+ cellspacing="2">
+ <tbody>
+ <tr>
+ <td style="vertical-align: top;"><span style="font-weight: bold;">Utility</span><br>
+ </td>
+ <td style="vertical-align: top;"><span style="font-weight: bold;">Main
+SCSI commands</span><br>
+ </td>
+ <td style="vertical-align: top;"><span style="font-weight: bold;">subdirectory</span><br>
+ </td>
+ <td style="vertical-align: top;"><span style="font-weight: bold;">Notes</span><br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">hxascdmp<br>
+ </td>
+ <td style="vertical-align: top;">-<br>
+ </td>
+ <td style="vertical-align: top;">utils<br>
+ </td>
+ <td style="vertical-align: top;">converts stdin (assumed binary)
+to ASCII hex and ASCII on stdout (like Unix <span
+ style="font-weight: bold;">od</span> command)<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">isosize<br>
+ </td>
+ <td style="vertical-align: top;">-<br>
+ </td>
+ <td style="vertical-align: top;">archive<br>
+ </td>
+ <td style="vertical-align: top;">size of an ISO 9660 file system,
+now in the <span style="font-weight: bold;">util-linux</span> package<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">rescan-scsi-bus.sh<br>
+ </td>
+ <td style="vertical-align: top;">-<br>
+ </td>
+ <td style="vertical-align: top;">archive<br>
+ </td>
+ <td style="vertical-align: top;">copy of Kurt Garloff's useful
+script<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">scsi_devfs_scan<br>
+ </td>
+ <td style="vertical-align: top;">INQUIRY<br>
+ </td>
+ <td style="vertical-align: top;">archive<br>
+ </td>
+ <td style="vertical-align: top;">more relevant to lk 2.4 (in lk
+2.6 sysfs replaces devfs; see <a href="../scsi/lsscsi.html">lsscsi</a>
+)<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">scsi_inquiry<br>
+ </td>
+ <td style="vertical-align: top;">INQUIRY<br>
+ </td>
+ <td style="vertical-align: top;">examples<br>
+ </td>
+ <td style="vertical-align: top;">uses deprecated
+SCSI_IOCTL_SEND_COMMAND ioctl<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sginfo<br>
+ </td>
+ <td style="vertical-align: top;">MODE SENSE/SELECT, READ DEFECT<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">symbolic decoding (optional
+changing) of mode pages. Can also output (disk) defect lists<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sgm_dd<br>
+ </td>
+ <td style="vertical-align: top;">READ, WRITE<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">sg_dd variant that uses memory
+mapped IO (only on sg devices)<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sgp_dd<br>
+ </td>
+ <td style="vertical-align: top;">READ, WRITE<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">sg_dd variant that uses POSIX
+threads<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sgq_dd<br>
+ </td>
+ <td style="vertical-align: top;">READ, WRITE<br>
+ </td>
+ <td style="vertical-align: top;">archive<br>
+ </td>
+ <td style="vertical-align: top;">sg_dd variant that uses
+asynchronous sg IO calls<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_dd<br>
+ </td>
+ <td style="vertical-align: top;">READ, WRITE<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">Unix <span
+ style="font-weight: bold;">dd</span> command variant, uses
+SG_IO ioctl to send SCSI commands to do reads and writes when
+instructed. See the <a href="sg_dd.html">sg_dd</a> page. </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_emc_trepass<br>
+ </td>
+ <td style="vertical-align: top;">MODE SELECT<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">utility specialized for EMC
+Clariion series<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_format<br>
+ </td>
+ <td style="vertical-align: top;">FORMAT<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">format or resize a SCSI disk<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_get_config<br>
+ </td>
+ <td style="vertical-align: top;">GET CONFIGURATION<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">fetch features and profiles of a
+cd/dvd drive and/or its current media<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_inq<br>
+ </td>
+ <td style="vertical-align: top;">INQUIRY<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">fetch standard reponse, VPD
+pages (including device identification) or version decsriptors<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_logs<br>
+ </td>
+ <td style="vertical-align: top;">LOG SENSE<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">fetch log sense pages, decode
+standard pages<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_luns<br>
+ </td>
+ <td style="vertical-align: top;">REPORT LUNS<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">fetch luns reported by a device
+(lun 0 or "well known lu")<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_map<br>
+ </td>
+ <td style="vertical-align: top;">INQUIRY<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">shows mapping between sg devices
+and primary device node (if any). In lk 2.6 see <a
+ href="../scsi/lsscsi.html">lsscsi</a> .<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_modes<br>
+ </td>
+ <td style="vertical-align: top;">MODE SENSE<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">dump mode pages (mainly in hex)<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_opcodes<br>
+ </td>
+ <td style="vertical-align: top;">REPORT SUPPORTED OPERATION CODES<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">fetch supported SCSI commands or
+supported task management functions<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_persist<br>
+ </td>
+ <td style="vertical-align: top;">PERSISTENT RESERVATION IN/OUT<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">control reservations and report
+reservation status<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_prevent<br>
+ </td>
+ <td style="vertical-align: top;">PREVENT ALLOW MEDIUM REMOVAL<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">control media removal, mainly
+for those SCSI devices which have removable media (mainly CD/DVD drives)<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_rbuff<br>
+ </td>
+ <td style="vertical-align: top;">READ BUFFER<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">read from SCSI device cache.
+Typically for testing the SCSI transport (for throughput or errors)<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_read<br>
+ </td>
+ <td style="vertical-align: top;">READ<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">read continually from same
+offset. Syntax similar to sg_dd (without write side). Can test SCSI
+device cache and transport performance (like sg_rbuff).<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_readcap<br>
+ </td>
+ <td style="vertical-align: top;">READ CAPACITY<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">fetch the number of blocks and
+the individual block size for disks and CD/DVD media<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_read_long<br>
+ </td>
+ <td style="vertical-align: top;">READ LONG<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">read from given lba data which
+includes the block and ECC data.<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_reassign<br>
+ </td>
+ <td style="vertical-align: top;">REASSIGN BLOCKS<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">reassign a lba from one sector
+on a disk (typically damaged) to a new (spare) sector. User data copied
+if it is recoverable.<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_request<br>
+ </td>
+ <td style="vertical-align: top;">REQUEST SENSE<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">fetch sense data from the given
+SCSI device. Useful if no autosense , to get progress indication (e.g.
+during a format) or find the power condition state.<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_reset<br>
+ </td>
+ <td style="vertical-align: top;">-<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">Issue a driver, (SCSI) bus or
+device (target or lun?) reset.<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_rtpg<br>
+ </td>
+ <td style="vertical-align: top;">REPORT TARGET PORT GROUPS<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">Specialized for multi-ported
+SCSI devices where one port (or a group of them) is preferred for IO
+over another (or others).<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_scan<br>
+ </td>
+ <td style="vertical-align: top;">[INQUIRY]<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">maps each sg device name to
+the corresponding numeric &lt;host, channel, target', lun&gt; tuple. In
+lk 2.6 series the "<a href="scsi/lsscsi.html">lsscsi</a> -g" command is
+similar.<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_senddiag<br>
+ </td>
+ <td style="vertical-align: top;">SEND DIAGNOSTIC<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">Issues either a default self
+test or a short/extended foreground/background self test. With no
+arguments it uses RECEIVE DIAGNOSTIC to list all supported diagnostic
+pages.<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_ses<br>
+ </td>
+ <td style="vertical-align: top;">SEND/RECEIVE DIAGNOSTIC<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">Fetches status diagnostic
+pages from, and sends some control pages to, a SCSI Enclosure Services
+(SES) device.<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_simple1,2,3,4<br>
+ </td>
+ <td style="vertical-align: top;">INQUIRY, TEST UNIT READY<br>
+ </td>
+ <td style="vertical-align: top;">examples<br>
+ </td>
+ <td style="vertical-align: top;">Simple code examples of using
+the scsi generic (sg) driver interface<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_simple16<br>
+ </td>
+ <td style="vertical-align: top;">READ<br>
+ </td>
+ <td style="vertical-align: top;">examples<br>
+ </td>
+ <td style="vertical-align: top;">Simple code example of sending a
+16 byte cdb SCSI READ command<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_start<br>
+ </td>
+ <td style="vertical-align: top;">START STOP UNIT<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">Controls the power condition
+state of a SCSI device. Primary use is to spin up and down SCSI disks.<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_sync<br>
+ </td>
+ <td style="vertical-align: top;">SYNCHRONIZE CACHE<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">Causes disk caches to be flushed
+to media<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_test_rwbuf<br>
+ </td>
+ <td style="vertical-align: top;">READ/WRITE BUFFER<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">Random pattern written to SCSI
+device buffer then read back and checked. Used in testing for data
+corruption.<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_turs<br>
+ </td>
+ <td style="vertical-align: top;">TEST UNIT READY<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">Issue one or more Test Unit
+Ready commands. Can be used to time SCSI command overhead.<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_verify</td>
+ <td style="vertical-align: top;">VERIFY</td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">reads indicated blocks on a SCSI
+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_write_long<br>
+ </td>
+ <td style="vertical-align: top;">WRITE LONG<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">writes to a lba, data which
+includes the block and ECC data. Suitable data typically fetched by
+prior
+sg_read_long utility.<br>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;">sg_wr_mode<br>
+ </td>
+ <td style="vertical-align: top;">MODE SELECT<br>
+ </td>
+ <td style="vertical-align: top;"><br>
+ </td>
+ <td style="vertical-align: top;">writes mode pages supplied in
+ASCII hex (e.g. from "sg_modes -r") to the given SCSI device<br>
+ </td>
+ </tr>
+ </tbody>
+</table>
+<br>
+More SCSI commands may be issued than shown in the "main SCSI
+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
+issued by the sg3_utils utilities.<br>
+<br>
+If the "subdirectory"
+column is empty in the above table then that utility is in the main
+directory and more
+documentation can be found in its "man" page. All utilities in the main
+directory have "man" pages and some in the subdirectories have "man"
+pages.<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
+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
+given device node. If the device node does not support the SG_IO ioctl
+then that is reported and the utility exits.<br>
+<br>
+Irrespective of the Unix device node used to access a device, care
+should be taken not to interfere with a device while it is "active".
+Obviously invoking a "sg_format -F" utility a disk with mounted file
+systems on it is going to cause damage.<br>
+<h2><a name="Examples"></a>Examples</h2>
+The fundemental SCSI command whose support is mandatory for all SCSI
+devices is INQUIRY. All devices should respond to a "standard" (i.e.
+when no Vital Product Pages are requested) INQUIRY.<br>
+<br>
+<span style="font-family: monospace;">$ sg_inq /dev/sda</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">standard INQUIRY:</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp; PQual=0&nbsp;
+Device_type=0&nbsp; RMB=0&nbsp; version=0x03&nbsp; [SPC]</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp; [AERC=0]&nbsp;
+[TrmTsk=0]&nbsp; NormACA=0&nbsp; HiSUP=1&nbsp; Resp_data_format=2</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp; SCCS=0&nbsp; ACC=0&nbsp;
+TGPS=0&nbsp; 3PC=0&nbsp; Protect=0</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp; BQue=0&nbsp;
+EncServ=0&nbsp; MultiP=0&nbsp; MChngr=0&nbsp; [ACKREQQ=0]&nbsp; Addr16=1</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp; [RelAdr=0]&nbsp;
+WBus16=1&nbsp; Sync=1&nbsp; Linked=1&nbsp; [TranDis=1]&nbsp; CmdQue=1</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp; Clocking=0x3&nbsp;
+QAS=0&nbsp; IUS=0</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp; length=144
+(0x90)&nbsp;&nbsp; Peripheral device type: disk</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;Vendor identification:
+SEAGATE</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;Product identification:
+ST318451LW</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;Product revision level: 0003</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;Unit serial number:
+xxxxxxxxx</span><br>
+<br>
+Some SCSI devices have version descriptor information showing which
+standards (and drafts) they support:<br>
+<br>
+<span style="font-family: monospace;">$ sg_inq -d /dev/sdb</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">standard INQUIRY:</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp; PQual=0&nbsp;
+Device_type=0&nbsp; RMB=0&nbsp; version=0x03&nbsp; [SPC]</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp; [AERC=0]&nbsp;
+[TrmTsk=0]&nbsp; NormACA=0&nbsp; HiSUP=0&nbsp; Resp_data_format=2</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp; SCCS=0&nbsp; ACC=0&nbsp;
+TGPS=0&nbsp; 3PC=0&nbsp; Protect=0</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp; BQue=0&nbsp;
+EncServ=0&nbsp; MultiP=0&nbsp; MChngr=0&nbsp; [ACKREQQ=0]&nbsp; Addr16=1</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp; [RelAdr=0]&nbsp;
+WBus16=1&nbsp; Sync=1&nbsp; Linked=1&nbsp; [TranDis=1]&nbsp; CmdQue=1</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp; Clocking=0x0&nbsp;
+QAS=0&nbsp; IUS=0</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp; length=96
+(0x60)&nbsp;&nbsp; Peripheral device type: disk</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;Vendor identification:
+FUJITSU</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;Product identification:
+MAM3184MP</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;Product revision level: 0106</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;Unit serial number:
+xxxxxxxxx</span><br style="font-family: monospace;">
+<br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp; Version descriptors:</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp; SAM-2 (no
+version claimed)</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp; SPI-3
+T10/1302-D revision 10</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp; SPC ANSI
+X3.301:1997</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp; SBC T10/0996-D
+revision 08c</span><br>
+<br>
+Many modern SCSI devices also support "Vital Product Data" (VPD) pages.
+Here is a request to list available VPD pages:<br>
+<br>
+<span style="font-family: monospace;">$ sg_inq -e /dev/sg1</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">VPD INQUIRY, page code=0x00:</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp; [PQual=0&nbsp;
+Peripheral device type: disk]</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp; Supported VPD pages:</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp;&nbsp;
+0x0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Supported VPD pages</span><br
+ style="font-family: monospace;">
+<span style="font-family: monospace;">&nbsp;&nbsp;&nbsp;&nbsp;
+0x80&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Unit serial number</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>
+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>
+<p>Example: given these 3 SCSI devices: <br>
+<tt style="font-family: monospace;">$ cat /proc/scsi/scsi</tt><span
+ style="font-family: monospace;"> </span><br
+ style="font-family: monospace;">
+<tt style="font-family: monospace;">Attached devices:</tt><span
+ style="font-family: monospace;"> </span><br
+ style="font-family: monospace;">
+<tt style="font-family: monospace;">Host: scsi1 Channel: 00 Id: 00 Lun:
+00</tt><span style="font-family: monospace;"> </span><br
+ style="font-family: monospace;">
+<tt style="font-family: monospace;">&nbsp; Vendor: SEAGATE&nbsp; Model:
+ST318451LW&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Rev: 0003</tt><span
+ style="font-family: monospace;"> </span><br
+ style="font-family: monospace;">
+<tt style="font-family: monospace;">&nbsp; Type:&nbsp;&nbsp;
+Direct-Access&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ANSI SCSI revision: 03</tt><span style="font-family: monospace;"> </span><br
+ style="font-family: monospace;">
+<tt style="font-family: monospace;">Host: scsi2 Channel: 00 Id: 04 Lun:
+00</tt><span style="font-family: monospace;"> </span><br
+ style="font-family: monospace;">
+<tt style="font-family: monospace;">&nbsp; Vendor: PIONEER&nbsp; Model:
+DVD-ROM DVD-303&nbsp; Rev: 1.10</tt><span
+ style="font-family: monospace;">
+</span><br style="font-family: monospace;">
+<tt style="font-family: monospace;">&nbsp; Type:&nbsp;&nbsp;
+CD-ROM&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ANSI SCSI revision: 02</tt><span style="font-family: monospace;"> </span><br
+ style="font-family: monospace;">
+<tt style="font-family: monospace;">Host: scsi2 Channel: 00 Id: 06 Lun:
+00</tt><span style="font-family: monospace;"> </span><br
+ style="font-family: monospace;">
+<tt style="font-family: monospace;">&nbsp; Vendor: YAMAHA&nbsp;&nbsp;
+Model:
+CRW4416S&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Rev: 1.0g</tt><span
+ style="font-family: monospace;">
+</span><br style="font-family: monospace;">
+<tt style="font-family: monospace;">&nbsp; Type:&nbsp;&nbsp;
+CD-ROM&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ANSI SCSI revision: 02</tt> </p>
+<p>then the output from sg_scan is: <br>
+<tt style="font-family: monospace;">$ sg_scan</tt><span
+ style="font-family: monospace;"> </span><br
+ style="font-family: monospace;">
+<tt style="font-family: monospace;">/dev/sg0: scsi1 channel=0 id=0
+lun=0&nbsp; type=0</tt><span style="font-family: monospace;"> </span><tt
+ style="font-family: monospace;"><br>
+/dev/sg1: scsi2 channel=0 id=4 lun=0&nbsp; type=5</tt><span
+ style="font-family: monospace;"> </span><br
+ style="font-family: monospace;">
+<tt style="font-family: monospace;">/dev/sg2: scsi2 channel=0 id=6
+lun=0&nbsp; type=5</tt> </p>
+<p>INQUIRY data can be added to that output with the "-i" option. The
+sg_map utility shows the mapping between scsi generic (sg) devices and
+the corresponding primary device node. For some peripheral device
+types, SCSI enclosures for example, there is no mapping and the
+enclosure device must be accessed via a sg device node.<br>
+</p>
+<p style="font-family: monospace;">$ sg_map <br>
+/dev/sg0&nbsp; /dev/sda <br>
+/dev/sg1&nbsp; /dev/scd0 <br>
+/dev/sg2&nbsp; /dev/scd1</p>
+In the lk 2.6 series of kernels sg_scan and sg_map are less important
+as most
+utilities in the sg3_utils package can be issued directly against the
+primary device node (e.g. /dev/sda).&nbsp; However the sg driver is
+still needed to "talk" to devices such as enclosures which have no
+specialized driver in linux.<br>
+<h2> <a name="notes"></a>Notes</h2>
+Starting with sg3_utils-1.09 a library called libsgutils is built and
+is required by most utilities. The library is made up of two source
+files: sg_lib.c and sg_cmds.c; and their associated header files . The
+sg_lib.c file contains SCSI
+command,
+sense key and additional sense code strings and various related helper
+functions. The sg_cmds.c file contains low level wrapper functions for
+commonly used SCSI commands. The library is built both as a shared
+object and a static library. The binary rpm package only contains the
+shared library while the sg3_utils-devel rpm package contains the
+static library, sg_lib.h and sg_cmds.h . If users do not want libraries
+(shared or static) then they can refer to the "lib_no_lib" subdirectory
+in the tarball which contains "no_lib" versions of the Makefile and the
+rpm "spec" file.<br>
+<br>
+Many of the utilities in the sg3_utils package use the SG_IO ioctl
+(rather than the older write()/read() interface) in the sg driver. In
+the lk 2.6&nbsp; series the SG_IO ioctl (or at least a stripped
+down version of it) has been implemented in the block subsystem. That
+means that commands that previously only worked on sg device names will
+now work on block device names as well (e.g. "sg_inq /dev/sda"). To use
+this facility safely version sg3_utils-1.02 or later should be used.
+Note that SCSI tape devices (both st and osst device drivers) are char
+devices and support the SG_IO ioctl from lk 2.6.6 onwards.<br>
+<p>See the notes about header file problems in the README file inside
+each package. </p>
+<h2> <a name="download"></a>Download</h2>
+Various tarballs that include the source, a README, CHANGELOG, INSTALL
+and a Makefile can be found in the <u>sg_utils tables</u> on the main
+page (link at the bottom of this 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 name="others"></a>Programs from other sources</h2>
+<h3> 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 <a
+ href="http://www.bit-net.com/%7Ermiller/scu.html">www.bit-net.com/~rmiller/scu.html</a>
+for more details.
+<h3> 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 <a
+ href="http://www.bit-net.com/%7Ermiller/dt.html">www.bit-net.com/~rmiller/dt.html</a>
+for more details. Both <b>scu</b> and <b>dt </b>are written by Robin
+T. Miller &lt;Robin.Miller@hp.com&gt;
+<h3> 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> 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>
+<h3> 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> 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> 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>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>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>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> 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>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>
+<br>
+<p>Return to <a href="index.html">main</a> page. </p>
+<center>
+<p>Last updated: 13th March 2005, 13:00<br>
+<br>
+</p>
+</center>
+</body>
+</html>
diff --git a/lib_no_lib/Makefile.no_lib b/lib_no_lib/Makefile.no_lib
index 789dc835..32eaf146 100644
--- a/lib_no_lib/Makefile.no_lib
+++ b/lib_no_lib/Makefile.no_lib
@@ -14,7 +14,7 @@ EXECS = sg_dd sgp_dd sgm_dd sg_read sg_map sg_scan sg_rbuf \
sg_start sg_reset sg_modes sg_logs sg_senddiag sg_opcodes \
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_get_config sg_wr_mode sg_rtpg sg_reassign sg_format
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 \
@@ -22,7 +22,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_opcodes.8 sg_persist.8 sg_write_long.8 sg_read_long.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_rtpg.8 sg_reassign.8 sg_format.8
MAN_PREF = man8
LARGE_FILE_FLAGS = -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
@@ -32,6 +32,9 @@ CFLAGS = -g -O2 -Wall -W -D_REENTRANT $(LARGE_FILE_FLAGS)
# CFLAGS = -g -O2 -W -D_REENTRANT -DSG_KERNEL_INCLUDES $(LARGE_FILE_FLAGS)
# CFLAGS = -g -O2 -Wall -pedantic -std=c99 -D_REENTRANT $(LARGE_FILE_FLAGS)
+# Some architectures do not like "-std-c99" due to 'asm' keyword
+CFLAGS_LLSEEK = -g -O2 -Wall -W -D_REENTRANT $(LARGE_FILE_FLAGS)
+
LDFLAGS =
# LDFLAGS = -v -lm
@@ -45,6 +48,9 @@ clean:
/bin/rm -f *.o $(EXECS) core* .depend *.a *.la *.lo
/bin/rm -rf .libs
+llseek.o: llseek.c
+ $(CC) $(INCLUDES) $(CFLAGS_LLSEEK) -o $@ -c $^
+
sg_dd: sg_dd.o sg_lib.o sg_cmds.o llseek.o
$(LD) -o $@ $(LDFLAGS) $^
@@ -141,6 +147,9 @@ sg_rtpg: sg_rtpg.o sg_lib.o sg_cmds.o
sg_reassign: sg_reassign.o sg_lib.o
$(LD) -o $@ $(LDFLAGS) $^
+sg_format: sg_format.o sg_lib.o sg_cmds.o
+ $(LD) -o $@ $(LDFLAGS) $^
+
install: $(EXECS)
install -d $(INSTDIR)
for name in $^; \
diff --git a/lib_no_lib/sg3_utils.spec.no_lib b/lib_no_lib/sg3_utils.spec.no_lib
index 922cd7a6..58fc204f 100644
--- a/lib_no_lib/sg3_utils.spec.no_lib
+++ b/lib_no_lib/sg3_utils.spec.no_lib
@@ -1,6 +1,6 @@
Summary: Utilities for SCSI devices in Linux
Name: sg3_utils
-Version: 1.12
+Version: 1.13
Release: 1
Packager: Douglas Gilbert <dgilbert at interlog dot com>
License: GPL/FreeBSD
@@ -27,9 +27,7 @@ incorrect usage may render your system inoperable.
Authors:
--------
Doug Gilbert <dgilbert at interlog dot com>
- Kurt Garloff <garloff at suse dot de> [sg_test_rwbuff]
- Peter Allworth [contribution to sg_dd and sgp_dd]
- Martin Schwenke <martin at meltin dot net> [contribution to sg_inq]
+ See CREDITS file
%prep
%setup
@@ -82,6 +80,7 @@ rm -rf $RPM_BUILD_ROOT
%attr(755,root,root) %{_bindir}/sg_wr_mode
%attr(755,root,root) %{_bindir}/sg_rtpg
%attr(755,root,root) %{_bindir}/sg_reassign
+%attr(755,root,root) %{_bindir}/sg_format
# 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*
@@ -115,9 +114,14 @@ rm -rf $RPM_BUILD_ROOT
%attr(-,root,root) %doc %{_mandir}/man8/sg_wr_mode.8*
%attr(-,root,root) %doc %{_mandir}/man8/sg_rtpg.8*
%attr(-,root,root) %doc %{_mandir}/man8/sg_reassign.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_format.8*
%changelog
+* Sun Mar 13 2005 - dgilbert at interlog dot com
+- add sg_format, sg_dd extensions
+ * sg3_utils-1.13
+
* Fri Jan 21 2005 - dgilbert at interlog dot com
- add sg_wr_mode, sg_rtpg + sg_reassign; sginfo sas tweaks
* sg3_utils-1.12
diff --git a/sg3_utils.spec b/sg3_utils.spec
index 38a0c191..3dfc4873 100644
--- a/sg3_utils.spec
+++ b/sg3_utils.spec
@@ -1,6 +1,6 @@
Summary: Utilities for SCSI devices in Linux
Name: sg3_utils
-Version: 1.12
+Version: 1.13
Release: 1
Packager: Douglas Gilbert <dgilbert at interlog dot com>
License: GPL/FreeBSD
@@ -27,9 +27,7 @@ incorrect usage may render your system inoperable.
Authors:
--------
Doug Gilbert <dgilbert at interlog dot com>
- Kurt Garloff <garloff at suse dot de> [sg_test_rwbuff]
- Peter Allworth [contribution to sg_dd and sgp_dd]
- Martin Schwenke <martin at meltin dot net> [contribution to sg_inq]
+ See CREDITS file
%package devel
Summary: Files needed for developing applications using the SCSI command set
@@ -91,6 +89,7 @@ rm -rf $RPM_BUILD_ROOT
%attr(755,root,root) %{_bindir}/sg_wr_mode
%attr(755,root,root) %{_bindir}/sg_rtpg
%attr(755,root,root) %{_bindir}/sg_reassign
+%attr(755,root,root) %{_bindir}/sg_format
%attr(755,root,root) %{_libdir}/libsgutils.so
%attr(755,root,root) %{_libdir}/libsgutils.so.1
%attr(755,root,root) %{_libdir}/libsgutils.so.1.0.0
@@ -127,6 +126,7 @@ rm -rf $RPM_BUILD_ROOT
%attr(-,root,root) %doc %{_mandir}/man8/sg_wr_mode.8*
%attr(-,root,root) %doc %{_mandir}/man8/sg_rtpg.8*
%attr(-,root,root) %doc %{_mandir}/man8/sg_reassign.8*
+%attr(-,root,root) %doc %{_mandir}/man8/sg_format.8*
%files devel
%defattr(-,root,root)
@@ -137,6 +137,10 @@ rm -rf $RPM_BUILD_ROOT
%changelog
+* Sun Mar 13 2005 - dgilbert at interlog dot com
+- add sg_format, sg_dd extensions
+ * sg3_utils-1.13
+
* Fri Jan 21 2005 - dgilbert at interlog dot com
- add sg_wr_mode, sg_rtpg + sg_reassign; sginfo sas tweaks
* sg3_utils-1.12
diff --git a/sg_cmds.c b/sg_cmds.c
index 7b24d0b7..cdffb9e7 100644
--- a/sg_cmds.c
+++ b/sg_cmds.c
@@ -54,7 +54,7 @@
#include "sg_lib.h"
#include "sg_cmds.h"
-static char * version_str = "1.05 20050121";
+static char * version_str = "1.06 20050309";
#define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */
@@ -84,7 +84,7 @@ static char * version_str = "1.05 20050121";
#define REPORT_LUNS_CMDLEN 12
#define MAINTENANCE_IN_CMD 0xa3
#define MAINTENANCE_IN_CMDLEN 12
-#define REPORT_TGT_GRP_SA 0x0a
+#define REPORT_TGT_PRT_GRP_SA 0x0a
#define LOG_SENSE_CMD 0x4d
#define LOG_SENSE_CMDLEN 10
#define LOG_SELECT_CMD 0x4c
@@ -94,6 +94,7 @@ static char * version_str = "1.05 20050121";
#define MODE6_RESP_HDR_LEN 4
#define MODE10_RESP_HDR_LEN 8
+#define MODE_RESP_ARB_LEN 1024
const char * sg_cmds_version()
@@ -147,8 +148,11 @@ int sg_ll_inquiry(int sg_fd, int cmddt, int evpd, int pg_op,
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ if (noisy || verbose)
+ sg_chk_n_print3("Inquiry", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
if (verbose && io_hdr.resid)
fprintf(sg_warnings_str, " inquiry: resid=%d\n", io_hdr.resid);
return 0;
@@ -214,8 +218,11 @@ int sg_simple_inquiry(int sg_fd, struct sg_simple_inquiry_resp * inq_data,
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ if (noisy || verbose)
+ sg_chk_n_print3("Inquiry", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
if (inq_data) {
inq_data->peripheral_qualifier = (inq_resp[0] >> 5) & 0x7;
inq_data->peripheral_type = inq_resp[0] & 0x1f;
@@ -290,8 +297,9 @@ 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, 2 -> try again */
+/* 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 */
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)
@@ -348,10 +356,8 @@ int sg_ll_sync_cache_10(int sg_fd, int sync_nv, int immed, int group,
case SG_LIB_CAT_CLEAN:
return 0;
case SG_LIB_CAT_MEDIA_CHANGED:
- if (verbose > 1)
- sg_chk_n_print3("synchronize cache", &io_hdr);
- return 2; /* probably have another go ... */
case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_ILLEGAL_REQ:
if (verbose > 1)
sg_chk_n_print3("synchronize cache", &io_hdr);
return res;
@@ -364,9 +370,9 @@ int sg_ll_sync_cache_10(int sg_fd, int sync_nv, int immed, int group,
}
-/* Invokes a SCSI READ CAPACITY (16) command. Return of 0 -> success,
- * SG_LIB_CAT_INVALID_OP -> invalid opcode, 2 -> media changed,
- * -1 -> other failure */
+/* 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 */
int sg_ll_readcap_16(int sg_fd, int pmi, unsigned long long llba,
void * resp, int mx_resp_len, int verbose)
{
@@ -420,20 +426,21 @@ int sg_ll_readcap_16(int sg_fd, int pmi, unsigned long long llba,
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ if (verbose)
+ sg_chk_n_print3("Read capacity (16)", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
if (verbose && io_hdr.resid)
fprintf(sg_warnings_str, " read_capacity16: resid=%d\n",
io_hdr.resid);
return 0;
case SG_LIB_CAT_INVALID_OP:
- if (verbose > 1)
- sg_chk_n_print3("READ CAPACITY 16 command error", &io_hdr);
- return res;
+ case SG_LIB_CAT_ILLEGAL_REQ:
case SG_LIB_CAT_MEDIA_CHANGED:
if (verbose > 1)
sg_chk_n_print3("READ CAPACITY 16 command error", &io_hdr);
- return 2;
+ return res;
default:
sg_chk_n_print3("READ CAPACITY 16 command error", &io_hdr);
return -1;
@@ -441,7 +448,8 @@ int sg_ll_readcap_16(int sg_fd, int pmi, unsigned long long llba,
}
/* Invokes a SCSI READ CAPACITY (10) command. Return of 0 -> success,
- * SG_LIB_CAT_INVALID_OP -> invalid opcode, 2 -> media changed,
+ * 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 */
int sg_ll_readcap_10(int sg_fd, int pmi, unsigned int lba,
void * resp, int mx_resp_len, int verbose)
@@ -486,20 +494,21 @@ int sg_ll_readcap_10(int sg_fd, int pmi, unsigned int lba,
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ if (verbose)
+ sg_chk_n_print3("Read capacity (10)", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
if (verbose && io_hdr.resid)
fprintf(sg_warnings_str, " read_capacity10: resid=%d\n",
io_hdr.resid);
return 0;
case SG_LIB_CAT_INVALID_OP:
- if (verbose > 1)
- sg_chk_n_print3("READ CAPACITY 10 command error", &io_hdr);
- return res;
+ case SG_LIB_CAT_ILLEGAL_REQ:
case SG_LIB_CAT_MEDIA_CHANGED:
if (verbose > 1)
sg_chk_n_print3("READ CAPACITY 10 command error", &io_hdr);
- return 2;
+ return res;
default:
sg_chk_n_print3("READ CAPACITY 10 command error", &io_hdr);
return -1;
@@ -507,7 +516,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, -1 -> other failure */
+ * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
+ * bad field in cdb, -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)
{
@@ -553,8 +563,11 @@ int sg_ll_mode_sense6(int sg_fd, int dbd, int pc, int pg_code, int sub_pg_code,
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ if (noisy || verbose)
+ sg_chk_n_print3("Mode sense (6)", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
if (verbose && io_hdr.resid)
fprintf(sg_warnings_str, " mode sense (6): resid=%d\n",
io_hdr.resid);
@@ -568,6 +581,7 @@ int sg_ll_mode_sense6(int sg_fd, int dbd, int pc, int pg_code, int sub_pg_code,
}
return 0;
case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_ILLEGAL_REQ:
if (verbose > 1)
sg_chk_n_print3("Mode sense (6) error", &io_hdr);
return res;
@@ -585,7 +599,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, -1 -> other failure */
+ * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
+ * bad field in cdb, -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)
@@ -633,8 +648,11 @@ int sg_ll_mode_sense10(int sg_fd, int llbaa, int dbd, int pc, int pg_code,
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ if (noisy || verbose)
+ sg_chk_n_print3("Mode sense (10)", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
if (verbose && io_hdr.resid)
fprintf(sg_warnings_str, " mode sense (10): resid=%d\n",
io_hdr.resid);
@@ -648,6 +666,7 @@ int sg_ll_mode_sense10(int sg_fd, int llbaa, int dbd, int pc, int pg_code,
}
return 0;
case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_ILLEGAL_REQ:
if (verbose > 1)
sg_chk_n_print3("Mode sense (10) error", &io_hdr);
return res;
@@ -665,7 +684,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, -1 -> other failure */
+ * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
+ * bad field in cdb, -1 -> other failure */
int sg_ll_mode_select6(int sg_fd, int pf, int sp, void * paramp,
int param_len, int noisy, int verbose)
{
@@ -713,10 +733,14 @@ int sg_ll_mode_select6(int sg_fd, int pf, int sp, void * paramp,
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ if (noisy || verbose)
+ sg_chk_n_print3("Mode select (6)", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
return 0;
case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_ILLEGAL_REQ:
if (verbose > 1)
sg_chk_n_print3("Mode select (6) error", &io_hdr);
return res;
@@ -733,7 +757,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, -1 -> other failure */
+ * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
+ * bad field in cdb, -1 -> other failure */
int sg_ll_mode_select10(int sg_fd, int pf, int sp, void * paramp,
int param_len, int noisy, int verbose)
{
@@ -782,10 +807,14 @@ int sg_ll_mode_select10(int sg_fd, int pf, int sp, void * paramp,
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ if (noisy || verbose)
+ sg_chk_n_print3("Mode select (10)", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
return 0;
case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_ILLEGAL_REQ:
if (verbose > 1)
sg_chk_n_print3("Mode select (10) error", &io_hdr);
return res;
@@ -844,13 +873,13 @@ int sg_mode_page_offset(const unsigned char * resp, int resp_len,
return offset;
}
-/* Invokes a SCSI REQUEST SENSE command */
-/* Return of 0 -> success, SG_LIB_CAT_INVALID_OP -> Request Sense not
- * supported??, -1 -> other failure */
+/* Invokes a SCSI REQUEST SENSE command. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> Request Sense not * supported??,
+ * SG_LIB_CAT_ILEGAL_REQ -> bad field in cdb, -1 -> other failure */
int sg_ll_request_sense(int sg_fd, int desc, void * resp, int mx_resp_len,
int verbose)
{
- int k;
+ int k, res;
unsigned char rsCmdBlk[REQUEST_SENSE_CMDLEN] =
{REQUEST_SENSE_CMD, 0, 0, 0, 0, 0};
unsigned char sense_b[SENSE_BUFF_LEN];
@@ -892,38 +921,35 @@ int sg_ll_request_sense(int sg_fd, int desc, void * resp, int mx_resp_len,
}
/* shouldn't get errors on Request Sense but it is best to be safe */
- switch (sg_err_category3(&io_hdr)) {
+ res = sg_err_category3(&io_hdr);
+ switch (res) {
+ case SG_LIB_CAT_RECOVERED:
+ if (verbose)
+ sg_chk_n_print3("Request sense", &io_hdr);
+ /* fall through */
case SG_LIB_CAT_CLEAN:
- if (verbose && io_hdr.resid)
- fprintf(sg_warnings_str, " request sense: resid=%d\n",
- io_hdr.resid);
if ((mx_resp_len >= 8) && (io_hdr.resid > (mx_resp_len - 8))) {
- if (verbose)
- fprintf(sg_warnings_str, " request sense: resid=%d "
- "indicates response too short\n", io_hdr.resid);
+ fprintf(sg_warnings_str, " request sense: resid=%d "
+ "indicates response too short\n", io_hdr.resid);
return -1;
- }
- return 0;
- case SG_LIB_CAT_RECOVERED:
- fprintf(sg_warnings_str, "Recovered error on REQUEST SENSE command, "
- "continuing\n");
- if (verbose && io_hdr.resid)
+ } else if (verbose && io_hdr.resid)
fprintf(sg_warnings_str, " request sense: resid=%d\n",
io_hdr.resid);
return 0;
case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_ILLEGAL_REQ:
if (verbose > 1)
sg_chk_n_print3("REQUEST SENSE command problem", &io_hdr);
- return SG_LIB_CAT_INVALID_OP;
+ return res;
default:
sg_chk_n_print3("REQUEST SENSE command problem", &io_hdr);
return -1;
}
}
-/* Invokes a SCSI REPORT LUNS command */
-/* Return of 0 -> success, SG_LIB_CAT_INVALID_OP -> Report Luns not
- * supported, -1 -> other failure */
+/* Invokes a SCSI REPORT LUNS command. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> Report Luns not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
int sg_ll_report_luns(int sg_fd, int select_report, void * resp,
int mx_resp_len, int noisy, int verbose)
{
@@ -965,13 +991,17 @@ int sg_ll_report_luns(int sg_fd, int select_report, void * resp,
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ if (noisy || verbose)
+ sg_chk_n_print3("Report luns", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
if (verbose && io_hdr.resid)
fprintf(sg_warnings_str, " report_luns: resid=%d\n",
io_hdr.resid);
return 0;
case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_ILLEGAL_REQ:
if (verbose > 1)
sg_chk_n_print3("REPORTS LUNS command error", &io_hdr);
return res;
@@ -984,9 +1014,9 @@ 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, -1 -> other failure */
+/* 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 */
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)
@@ -1035,16 +1065,20 @@ int sg_ll_log_sense(int sg_fd, int ppc, int sp, int pc, int pg_code,
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ if (noisy || verbose)
+ sg_chk_n_print3("Log sense", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
if (verbose && io_hdr.resid)
fprintf(sg_warnings_str, " log_sense: resid=%d\n",
io_hdr.resid);
return 0;
case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_ILLEGAL_REQ:
if (verbose > 1)
sg_chk_n_print3("log_sense error", &io_hdr);
- return SG_LIB_CAT_INVALID_OP;
+ return res;
default:
if (noisy || verbose) {
char ebuff[EBUFF_SZ];
@@ -1058,9 +1092,9 @@ int sg_ll_log_sense(int sg_fd, int ppc, int sp, int pc, int pg_code,
}
-/* Invokes a SCSI LOG SELECT command */
-/* Return of 0 -> success, SG_LIB_CAT_INVALID_OP -> Log Select not
- * supported, -1 -> other failure */
+/* 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 */
int sg_ll_log_select(int sg_fd, int pcr, int sp, int pc,
unsigned char * paramp, int param_len,
int noisy, int verbose)
@@ -1107,16 +1141,20 @@ int sg_ll_log_select(int sg_fd, int pcr, int sp, int pc,
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ if (noisy || verbose)
+ sg_chk_n_print3("Log select", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
if (verbose && io_hdr.resid)
fprintf(sg_warnings_str, " log_select: resid=%d\n",
io_hdr.resid);
return 0;
case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_ILLEGAL_REQ:
if (verbose > 1)
sg_chk_n_print3("log_select error", &io_hdr);
- return SG_LIB_CAT_INVALID_OP;
+ return res;
default:
if (noisy || verbose) {
char ebuff[EBUFF_SZ];
@@ -1128,59 +1166,63 @@ 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, -1 -> other failure */
-int sg_ll_report_tgt_grp(int sg_fd, void * resp,
- int mx_resp_len, int noisy, int verbose)
+/* 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 */
+int sg_ll_report_tgt_prt_grp(int sg_fd, void * resp,
+ int mx_resp_len, int noisy, int verbose)
{
int k, res;
- unsigned char rtgCmdBlk[MAINTENANCE_IN_CMDLEN] =
- {MAINTENANCE_IN_CMD, REPORT_TGT_GRP_SA,
+ unsigned char rtpgCmdBlk[MAINTENANCE_IN_CMDLEN] =
+ {MAINTENANCE_IN_CMD, REPORT_TGT_PRT_GRP_SA,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
unsigned char sense_b[SENSE_BUFF_LEN];
struct sg_io_hdr io_hdr;
- rtgCmdBlk[6] = (mx_resp_len >> 24) & 0xff;
- rtgCmdBlk[7] = (mx_resp_len >> 16) & 0xff;
- rtgCmdBlk[8] = (mx_resp_len >> 8) & 0xff;
- rtgCmdBlk[9] = mx_resp_len & 0xff;
+ rtpgCmdBlk[6] = (mx_resp_len >> 24) & 0xff;
+ rtpgCmdBlk[7] = (mx_resp_len >> 16) & 0xff;
+ rtpgCmdBlk[8] = (mx_resp_len >> 8) & 0xff;
+ rtpgCmdBlk[9] = mx_resp_len & 0xff;
if (NULL == sg_warnings_str)
sg_warnings_str = stderr;
if (verbose) {
fprintf(sg_warnings_str, " report target port groups cdb: ");
for (k = 0; k < MAINTENANCE_IN_CMDLEN; ++k)
- fprintf(sg_warnings_str, "%02x ", rtgCmdBlk[k]);
+ fprintf(sg_warnings_str, "%02x ", rtpgCmdBlk[k]);
fprintf(sg_warnings_str, "\n");
}
memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
memset(sense_b, 0, sizeof(sense_b));
io_hdr.interface_id = 'S';
- io_hdr.cmd_len = sizeof(rtgCmdBlk);
+ io_hdr.cmd_len = sizeof(rtpgCmdBlk);
io_hdr.mx_sb_len = sizeof(sense_b);
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
io_hdr.dxfer_len = mx_resp_len;
io_hdr.dxferp = resp;
- io_hdr.cmdp = rtgCmdBlk;
+ io_hdr.cmdp = rtpgCmdBlk;
io_hdr.sbp = sense_b;
io_hdr.timeout = DEF_TIMEOUT;
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
- fprintf(sg_warnings_str, "report_tgt_grp (SG_IO) error: %s\n",
+ fprintf(sg_warnings_str, "report_tgt_prt_grp (SG_IO) error: %s\n",
safe_strerror(errno));
return -1;
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ if (noisy || verbose)
+ sg_chk_n_print3("Report target port groups", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
if (verbose && io_hdr.resid)
- fprintf(sg_warnings_str, " report_tgt_grp: resid=%d\n",
+ fprintf(sg_warnings_str, " report_tgt_prt_grp: resid=%d\n",
io_hdr.resid);
return 0;
case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_ILLEGAL_REQ:
if (verbose > 1)
- sg_chk_n_print3("synchronize cache", &io_hdr);
+ sg_chk_n_print3("REPORT TARGET PORT GROUPS", &io_hdr);
return res;
default:
if (noisy || verbose)
@@ -1188,3 +1230,76 @@ int sg_ll_report_tgt_grp(int sg_fd, void * resp,
return -1;
}
}
+
+/* Fetches current, changeable, default and/or saveable modes pages as
+ * indicated (by those "*_mp" arguments that are not NULL).
+ * Return of 0 -> overall success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure.
+ * If success_mask pointer is not NULL then zeroes it then sets bit 0 if
+ * current fetched ok, bit 1 if changeable fetched ok, bit 2 if default
+ * fetched ok and bit 3 if saved fetched ok. Continues if an error is
+ * detected but returns the first error encountered. */
+int sg_get_mode_page_types(int sg_fd, int mode6, int pg_code,
+ int sub_pg_code, int mx_mpage_len,
+ int * success_mask, void * current_mp,
+ void * changeable_mp, void * default_mp,
+ void * saved_mp, int verbose)
+{
+ int k, res, offset, calc_len;
+ unsigned char buff[MODE_RESP_ARB_LEN];
+ char ebuff[EBUFF_SZ];
+ void * pc_arr[4];
+ int first_err = 0;
+ int mx_mode_resp_len;
+
+ if (success_mask)
+ *success_mask = 0;
+ if (mx_mpage_len < 4)
+ return 0;
+ if (NULL == sg_warnings_str)
+ sg_warnings_str = stderr;
+ if (mode6)
+ mx_mode_resp_len =
+ (MODE_RESP_ARB_LEN < 252) ? MODE_RESP_ARB_LEN : 252;
+ else
+ mx_mode_resp_len = MODE_RESP_ARB_LEN;
+ memset(ebuff, 0, sizeof(ebuff));
+ pc_arr[0] = current_mp;
+ pc_arr[1] = changeable_mp;
+ pc_arr[2] = default_mp;
+ pc_arr[3] = saved_mp;
+ for (k = 0; k < 4; ++k) {
+ if (NULL == pc_arr[k])
+ continue;
+ if (mode6)
+ res = sg_ll_mode_sense6(sg_fd, 0 /* dbd */, k /* pc */,
+ pg_code, sub_pg_code, buff,
+ mx_mode_resp_len, 0, verbose);
+ else
+ res = sg_ll_mode_sense10(sg_fd, 0 /* llbaa */, 0 /* dbd */,
+ k /* pc */, pg_code, sub_pg_code,
+ buff, mx_mode_resp_len, 0, verbose);
+ if (0 != res) {
+ if (0 == first_err)
+ first_err = res;
+ continue;
+ }
+ offset = sg_mode_page_offset(buff, mx_mode_resp_len, mode6,
+ ebuff, EBUFF_SZ);
+ if (verbose && (offset < 0)) {
+ if (('\0' != ebuff[0]) && (verbose > 0))
+ fprintf(sg_warnings_str, "sg_get_mode_page_types: "
+ "pc=%d: %s\n", k, ebuff);
+ return offset;
+ }
+ calc_len = mode6 ? (buff[0] + 1) : ((buff[0] << 8) + buff[1] + 2);
+ calc_len = (calc_len < MODE_RESP_ARB_LEN) ? calc_len :
+ MODE_RESP_ARB_LEN;
+ calc_len -= offset;
+ calc_len = (calc_len < mx_mpage_len) ? calc_len : mx_mpage_len;
+ memcpy(pc_arr[k], buff + offset, calc_len);
+ if (success_mask)
+ *success_mask |= (1 << k);
+ }
+ return first_err;
+}
diff --git a/sg_cmds.h b/sg_cmds.h
index 6b6f2f92..d98bca51 100644
--- a/sg_cmds.h
+++ b/sg_cmds.h
@@ -41,8 +41,8 @@ extern int sg_ll_request_sense(int sg_fd, int desc, void * resp,
extern int sg_ll_report_luns(int sg_fd, int select_report, void * resp,
int mx_resp_len, int noisy, int verbose);
-extern int sg_ll_report_tgt_grp(int sg_fd, void * resp,
- int mx_resp_len, int noisy, int verbose);
+extern int sg_ll_report_tgt_prt_grp(int sg_fd, void * resp,
+ int mx_resp_len, int noisy, int verbose);
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,
@@ -73,4 +73,10 @@ extern int sg_simple_inquiry(int sg_fd,
extern int sg_mode_page_offset(const unsigned char * resp, int resp_len,
int mode_sense_6, char * err_buff,
int err_buff_len);
+
+extern int sg_get_mode_page_types(int sg_fd, int mode6, int pg_code,
+ int sub_pg_code, int mx_mpage_len,
+ int * success_mask, void * current_mp,
+ void * changeable_mp, void * default_mp,
+ void * saved_mp, int verbose);
#endif
diff --git a/sg_dd.8 b/sg_dd.8
index 4122a4cb..faf4d890 100644
--- a/sg_dd.8
+++ b/sg_dd.8
@@ -1,4 +1,4 @@
-.TH SG_DD "8" "October 2004" "sg3_utils-1.10" SG3_UTILS
+.TH SG_DD "8" "March 2005" "sg3_utils-1.13" SG3_UTILS
.SH NAME
sg_dd \- copies data to and from files and devices. Specialised for
devices that understand the SCSI command set.
@@ -8,12 +8,12 @@ devices that understand the SCSI command set.
[\fIcdbsz=6|10|12|16\fR] [\fIcoe=0|1\fR] [\fIcount=<n>\fR] [\fIdio=0|1\fR]
[\fIfua=0|1|2|3\fR] [\fIibs=<n>\fR] [\fIif=<ifile>\fR] [\fIobs=<n>\fR]
[\fIodir=0|1\fR] [\fIof=<ofile>\fR] [\fIseek=<n>\fR] [\fIskip=<n>\fR]
-[\fIsync=0|1\fR] [\fItime=0|1\fR] [\fI--version\fR]
+[\fIsync=0|1\fR] [\fItime=0|1\fR] [\fIverbose=<n>\fR] [\fI--version\fR]
.SH DESCRIPTION
.\" Add any additional description here
.PP
Copy data to and from any files. Specialised for "files" that are
-Linux SCSI generic (sg) devices, raw devices and those block devices
+Linux SCSI generic (sg) devices, raw devices or other devices
that support the SG_IO ioctl (which are only found in the lk 2.6
series). Similar syntax and semantics to
.B dd(1)
@@ -24,8 +24,8 @@ when set to 1 the output will be appended to the normal file given
to the "of=<name>" argument. Appending only takes place to normal files:
not to pipes nor raw files nor sg devices nor block devices. Error
message produced if append=1 and seek=<n> where <n> > 0. Default is 0
-which starts output at offset of a normal file (subject to the "seek="
-argument).
+which starts output at the beginning of a normal file (or at some other
+offset if the "seek=" argument is given).
.TP
blk_sgio=0 | 1
when set to 0, block devices (e.g. /dev/sda) are treated like normal
@@ -33,14 +33,14 @@ files (i.e.
.B read(2)
and
.B write(2)
-are used for IO). When set to 1, block
-devices are assumed to accept the SG_IO ioctl and SCSI commands are
-issued for IO. This is only supported for 2.6 series kernels. Note
-that ATAPI devices (e.g. cd/dvd players) use the SCSI command set
-but ATA disks do not. If the input or output device is a block device
-partition then setting this option causes the partition information
-to be ignored (since access is directly to the underlying device).
-Default is 0.
+are used for IO). When set to 1, block devices are assumed to accept the
+SG_IO ioctl and SCSI commands are issued for IO. This is only supported
+for 2.6 series kernels. Note that ATAPI devices (e.g. cd/dvd players) use
+the SCSI command set but ATA disks do not (unless there is a protocol
+conversion as often occurs in the USB mass storage class). If the input
+or output device is a block device partition (e.g. /dev/sda3) then setting
+this option causes the partition information to be ignored (since access
+is directly to the underlying device). Default is 0.
.TP
bpt=BLOCKS
each IO transaction will be made using this number of blocks (or less if
@@ -51,11 +51,13 @@ transfer or memory restrictions).
bs=BYTES
this
.B must
-be the block size of the physical device. Note that this differs from
+be the block size of the physical device (if either the input or output
+files are accessed via SCSI commands). Note that this differs from
.B dd(1)
which permits "bs" to be an integral multiple. Default is 512 which
is usually correct for disks but incorrect for cdroms (which normally
-have 2048 byte blocks).
+have 2048 byte blocks). For this utility the maximum size of each individual
+IO operation is 'bs * bpt' bytes.
.TP
cdbsz=6 | 10 | 12 | 16
size of SCSI READ and/or WRITE commands issued on sg device
@@ -109,9 +111,10 @@ if given must be the same as bs
odir=0 | 1
when set to one opens block devices (e.g. /dev/sda) with the O_DIRECT
flag. User memory buffers are aligned to the page size when set. The
-default is 0 (i.e. the O_DIRECT flag is not used). The blk_sgio flag
-takes precedence if it is also set. Has no effect on sg, normal or raw
-files.
+default is 0 (i.e. the O_DIRECT flag is not used). Has no effect on sg,
+normal or raw files. If blk_sgio is also set then both are honoured:
+block devices are opened with the O_DIRECT flag and SCSI commands are
+issued via the SG_IO ioctl.
.TP
of=FILE
write to FILE instead of stdout. A file name of - is taken to be stdout.
@@ -137,28 +140,32 @@ time=0 | 1
when 1, times transfer and does throughput calculation, outputting the
results (to stderr) at completion. When 0 (default) doesn't perform timing
.TP
+verbose=<n>
+as <n> increases so does the amount of debug output sent to stderr.
+Default value is zero which yields the minimum amount of debug output.
+A value of 1 reports extra information that is not repetitive. A value
+2 reports cdbs and responses for SCSI commands that are not repetitive
+(i.e. other that READ and WRITE). Error processing is not considered
+repetitive. Values of 3 and 4 yield ouput for all SCSI commands (and
+Unix read() and write() calls).
+.TP
--version
outputs version number information and exits
.PP
-A raw device must be bound to a block device prior to using sg_dd.
-See
-.B raw(8)
-for more information about binding raw devices. To be safe, the sg device
-mapping to SCSI block devices should be checked with "cat /proc/scsi/scsi",
-or sg_map before use.
-.PP
-Raw disk partition information can often be found with
-.B fdisk(8)
-[the "-ul" argument is useful in this respect].
+The exit status of the sg_dd utilty 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; b B *512; k *1,024; K *1,000; m *1,048,576; M *1,000,000;
-g *1,073,741,824; G *1,000,000,000; t *1,099,511,627,776 and
-T *1,000,000,000,000 (the latter two can only be used for count, skip
-and seek values).
+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
+suffixes can only be used for count, skip and seek values). Also a suffix of
+the form "x<n>" multiplies the leading number by <n>. These multiplicative
+suffixes are compatible with GNU's dd command (since 2002) which claims
+compliance with SI and with IEC 60027-2.
.PP
Alternatively numerical values can be given in hexadecimal preceded by
-either "0x" or "0X". When hex numbers are given multipliers cannot be
+either "0x" or "0X". When hex numbers are given, multipliers cannot be
used.
.PP
The count, skip and seek parameters can take 64 bit values (i.e. very
@@ -177,11 +184,22 @@ configuration change to activate it. This is typically done with
All informative, warning and error output is sent to stderr so that
dd's output file can be stdout and remain unpolluted. If no options
are given, then the usage message is output and nothing else happens.
+.PP
+A raw device must be bound to a block device prior to using sg_dd.
+See
+.B raw(8)
+for more information about binding raw devices. To be safe, the sg device
+mapping to SCSI block devices should be checked with "cat /proc/scsi/scsi",
+or sg_map before use.
+.PP
+Raw disk partition information can often be found with
+.B fdisk(8)
+[the "-ul" argument is useful in this respect].
.SH EXAMPLES
.PP
Looks quite similar in usage to dd:
.PP
- sg_dd if=/dev/sg0 of=t bs=512 count=1M
+ sg_dd if=/dev/sg0 of=t bs=512 count=1MB
.PP
This will copy 1 million 512 byte blocks from the device associated with
/dev/sg0 (which should have 512 byte blocks) to a file called t.
@@ -195,7 +213,7 @@ reduced. Using a raw device to do something similar on a IDE disk:
.PP
raw /dev/raw/raw1 /dev/hda
.br
- sg_dd if=/dev/raw/raw1 of=t bs=512 count=1M
+ sg_dd if=/dev/raw/raw1 of=t bs=512 count=1MB
.PP
To copy a SCSI disk partition to an IDE disk partition:
.PP
@@ -243,7 +261,7 @@ Written by Doug Gilbert and Peter Allworth.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2000-2004 Douglas Gilbert
+Copyright \(co 2000-2005 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_dd.c b/sg_dd.c
index ceea400b..24890146 100644
--- a/sg_dd.c
+++ b/sg_dd.c
@@ -37,12 +37,7 @@
assumed to be 512 if not given. This program complains if 'ibs' or
'obs' are given with a value that differs from 'bs' (or the default 512).
If 'if' is not given or 'if=-' then stdin is assumed. If 'of' is
- not given or 'of=-' then stdout assumed. Multipliers:
- 'c','C' *1 'b','B' *512 'k' *1024 'K' *1000
- 'm' *(1024^2) 'M' *(1000^2) 'g' *(1024^3) 'G' *(1000^3)
- 't' *(1024^4) 'T' *(1000^4)
- This convention (lower case powers of two, upper case powers of 10
- (apart from 'B')) is borrowed from lmdd from the lmbench suite.
+ not given or 'of=-' then stdout assumed.
A non-standard argument "bpt" (blocks per transfer) is added to control
the maximum number of blocks in each transfer. The default value is 128.
@@ -54,21 +49,32 @@
This version is designed for the linux kernel 2.4 and 2.6 series.
*/
-static char * version_str = "5.37 20050113";
+static char * version_str = "5.38 20050309";
+#define ME "sg_dd: "
+
+/* #define SG_DEBUG */
+
+#define STR_SZ 1024
+#define INOUTF_SZ 512
+#define EBUFF_SZ 512
#define DEF_BLOCK_SIZE 512
#define DEF_BLOCKS_PER_TRANSFER 128
#define DEF_SCSI_CDBSZ 10
#define MAX_SCSI_CDBSZ 16
-#define ME "sg_dd: "
-
-/* #define SG_DEBUG */
+#define DEF_MODE_CDB_SZ 10
+#define DEF_MODE_RESP_LEN 252
+#define RW_ERR_RECOVERY_MP 1
+#define CACHING_MP 8
+#define CONTROL_MP 0xa
#define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */
#define READ_CAP_REPLY_LEN 8
#define RCAP16_REPLY_LEN 32
+#define READ_LONG_OPCODE 0x3E
+#define READ_LONG_CMD_LEN 10
#define DEF_TIMEOUT 60000 /* 60,000 millisecs == 60 seconds */
@@ -94,14 +100,26 @@ static char * version_str = "5.37 20050113";
static int sum_of_resids = 0;
static long long dd_count = -1;
+static long long req_count = 0;
static long long in_full = 0;
static int in_partial = 0;
static long long out_full = 0;
static int out_partial = 0;
+static int recovered_errs = 0;
+static int unrecovered_errs = 0;
+static int read_longs = 0;
+
static int do_coe = 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 const char * proc_allow_dio = "/proc/scsi/sg/allow_dio";
+static void calc_duration_throughput();
+
static void install_handler (int sig_num, void (*sig_handler) (int sig))
{
struct sigaction sigact;
@@ -115,7 +133,7 @@ static void install_handler (int sig_num, void (*sig_handler) (int sig))
}
}
-void print_stats(const char * str)
+static void print_stats(const char * str)
{
if (0 != dd_count)
fprintf(stderr, " remaining block count=%lld\n", dd_count);
@@ -123,6 +141,15 @@ void print_stats(const char * str)
in_partial);
fprintf(stderr, "%s%lld+%d records out\n", str, out_full - out_partial,
out_partial);
+ if (recovered_errs > 0)
+ fprintf(stderr, "%s%d recovered errors\n", str, recovered_errs);
+ if (do_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,
+ unrecovered_errs);
}
static void interrupt_handler(int sig)
@@ -134,6 +161,8 @@ static void interrupt_handler(int sig)
sigact.sa_flags = 0;
sigaction(sig, &sigact, NULL);
fprintf(stderr, "Interrupted by signal,");
+ if (do_time)
+ calc_duration_throughput();
print_stats("");
kill(getpid (), sig);
}
@@ -142,10 +171,12 @@ static void siginfo_handler(int sig)
{
sig = sig; /* dummy to stop -W warning messages */
fprintf(stderr, "Progress report, continuing ...\n");
+ if (do_time)
+ calc_duration_throughput();
print_stats(" ");
}
-int dd_filetype(const char * filename)
+static int dd_filetype(const char * filename)
{
struct stat st;
size_t len = strlen(filename);
@@ -169,7 +200,26 @@ int dd_filetype(const char * filename)
return FT_OTHER;
}
-void usage()
+static char * dd_filetype_str(int ft, char * buff)
+{
+ int off = 0;
+
+ if (FT_DEV_NULL & ft)
+ off += snprintf(buff + off, 32, "null device ");
+ if (FT_SG & ft)
+ off += snprintf(buff + off, 32, "SCSI generic (sg) device ");
+ if (FT_BLOCK & ft)
+ off += snprintf(buff + off, 32, "block device ");
+ if (FT_ST & ft)
+ off += snprintf(buff + off, 32, "SCSI tape device ");
+ if (FT_RAW & ft)
+ off += snprintf(buff + off, 32, "raw device ");
+ if (FT_OTHER & ft)
+ off += snprintf(buff + off, 32, "other (perhaps name file) ");
+ return buff;
+}
+
+static void usage()
{
fprintf(stderr, "Usage: "
"sg_dd [if=<infile>] [skip=<n>] [of=<ofile>] [seek=<n> | "
@@ -178,26 +228,48 @@ void usage()
" [dio=0|1]\n"
" [sync=0|1] [cdbsz=6|10|12|16] [fua=0|1|2|3]"
" [coe=0|1]\n"
- " [odir=0|1] [blk_sgio=0|1] [--version]\n"
- " 'append' 1->append output to normal <ofile>, (default is 0)\n"
- " 'bpt' is blocks_per_transfer (default is 128)\n"
- " 'dio' is direct IO, 1->attempt, 0->indirect IO (def)\n"
- " 'coe' 1->continue on sg error, 0->exit on error (def)\n"
- " 'time' 0->no timing(def), 1->time plus calculate throughput\n"
- " 'fua' force unit access: 0->don't(def), 1->of, 2->if, 3->of+if\n"
- " 'odir' 1->use O_DIRECT when opening block dev, 0->don't(def)\n"
- " 'sync' 0->no sync(def), 1->SYNCHRONIZE CACHE on of after xfer\n"
- " 'cdbsz' size of SCSI READ or WRITE command (default is 10)\n"
- " 'blk_sgio' 0->block device use normal I/O(def), 1->use SG_IO\n");
+ " [odir=0|1] [blk_sgio=0|1] [verbose=<n>]"
+ " [--version]\n"
+ " where:\n"
+ " append 1->append output to normal <ofile>, (default is 0)\n"
+ " blk_sgio 0->block device use normal I/O(def), 1->use SG_IO\n"
+ " bpt is blocks_per_transfer (default is 128)\n"
+ " bs block size (default is 512)\n");
+ fprintf(stderr,
+ " cdbsz size of SCSI READ or WRITE command (default is 10)\n"
+ " coe 0->exit on error (def), 1->continue on sg error (zero\n"
+ " fill), try read_long on unrecovered read block\n"
+ " dio is direct IO, 1->attempt, 0->indirect IO (def)\n"
+ " fua force unit access: 0->don't(def), 1->of, 2->if, "
+ "3->of+if\n"
+ " ibs input block size (if given must be same as 'bs')\n"
+ " if file or device to read from (def stdin)\n"
+ " obs output block size (if given must be same as 'bs')\n"
+ " odir 1->use O_DIRECT when opening block dev, 0->don't(def)\n"
+ " of file or device to write to (def stdout), name '.' "
+ "translated to\n");
+ fprintf(stderr,
+ " /dev/null\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 "
+ "xfer\n"
+ " time 0->no timing(def), 1->time plus calculate throughput\n"
+ " verbose 0->quiet(def), 1->some noise, 2->more noise, etc\n"
+ " --version print version information then exit\n");
}
-/* Return of 0 -> success, -1 -> failure, 2 -> try again */
-int scsi_read_capacity(int sg_fd, long long * num_sect, int * sect_sz)
+/* 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 */
+static int scsi_read_capacity(int sg_fd, long long * num_sect, int * sect_sz)
{
int k, res;
unsigned char rcBuff[RCAP16_REPLY_LEN];
+ int verb;
- res = sg_ll_readcap_10(sg_fd, 0, 0, rcBuff, READ_CAP_REPLY_LEN, 0);
+ verb = (verbose ? verbose - 1: 0);
+ res = sg_ll_readcap_10(sg_fd, 0, 0, rcBuff, READ_CAP_REPLY_LEN, verb);
if (0 != res)
return res;
@@ -205,7 +277,7 @@ int scsi_read_capacity(int sg_fd, long long * num_sect, int * sect_sz)
(0xff == rcBuff[3])) {
long long ls;
- res = sg_ll_readcap_16(sg_fd, 0, 0, rcBuff, RCAP16_REPLY_LEN, 0);
+ res = sg_ll_readcap_16(sg_fd, 0, 0, rcBuff, RCAP16_REPLY_LEN, verb);
if (0 != res)
return res;
for (k = 0, ls = 0; k < 8; ++k) {
@@ -221,12 +293,16 @@ int scsi_read_capacity(int sg_fd, long long * num_sect, int * sect_sz)
*sect_sz = (rcBuff[4] << 24) | (rcBuff[5] << 16) |
(rcBuff[6] << 8) | rcBuff[7];
}
+ if (verbose)
+ fprintf(stderr, " number of blocks=%lld [0x%llx], block "
+ "size=%d\n", *num_sect, *num_sect, *sect_sz);
return 0;
}
/* Return of 0 -> success, -1 -> failure. BLKGETSIZE64, BLKGETSIZE and */
/* BLKSSZGET macros problematic (from <linux/fs.h> or <sys/mount.h>). */
-int read_blkdev_capacity(int sg_fd, long long * num_sect, int * sect_sz)
+static int read_blkdev_capacity(int sg_fd, long long * num_sect,
+ int * sect_sz)
{
#ifdef BLKSSZGET
if ((ioctl(sg_fd, BLKSSZGET, sect_sz) < 0) && (*sect_sz > 0)) {
@@ -242,6 +318,9 @@ int read_blkdev_capacity(int sg_fd, long long * num_sect, int * sect_sz)
return -1;
}
*num_sect = ((long long)ull / (long long)*sect_sz);
+ if (verbose)
+ fprintf(stderr, " [bgs64] number of blocks=%lld [0x%llx], "
+ "block size=%d\n", *num_sect, *num_sect, *sect_sz);
#else
unsigned long ul;
@@ -250,19 +329,146 @@ int read_blkdev_capacity(int sg_fd, long long * num_sect, int * sect_sz)
return -1;
}
*num_sect = (long long)ul;
+ if (verbose)
+ fprintf(stderr, " [bgs] number of blocks=%lld [0x%llx], "
+ " block size=%d\n", *num_sect, *num_sect, *sect_sz);
#endif
}
return 0;
#else
+ if (verbose)
+ fprintf(stderr, " BLKSSZGET+BLKGETSIZE ioctl not available\n");
*num_sect = 0;
*sect_sz = 0;
return -1;
#endif
}
-int sg_build_scsi_cdb(unsigned char * cdbp, int cdb_sz, unsigned int blocks,
- long long start_block, int write_true, int fua,
- int dpo)
+static int info_offset(unsigned char * sensep, int sb_len)
+{
+ int resp_code;
+
+ if (sb_len < 8)
+ return 0;
+ resp_code = (0x7f & sensep[0]);
+ if (resp_code >= 0x72) { /* descriptor format */
+ unsigned long long ull = 0;
+
+ /* if Information field, fetch it; contains signed number */
+ if (sg_get_sense_info_fld(sensep, sb_len, &ull))
+ return (int)(long long)ull;
+ } else if (sensep[0] & 0x80) { /* fixed, valid set */
+ if ((0 == sensep[3]) && (0 == sensep[4]))
+ return ((sensep[5] << 8) + sensep[6]);
+ else if ((0xff == sensep[3]) && (0xff == sensep[4]))
+ return ((sensep[5] << 8) + sensep[6] - (int)0x10000);
+ }
+ return 0;
+}
+
+static int has_blk_ili(unsigned char * sensep, int sb_len)
+{
+ int resp_code;
+ const unsigned char * cup;
+
+ if (sb_len < 8)
+ return 0;
+ resp_code = (0x7f & sensep[0]);
+ if (resp_code >= 0x72) { /* descriptor format */
+ /* find block command descriptor */
+ if ((cup = sg_scsi_sense_desc_find(sensep, sb_len, 0x5)))
+ return ((cup[3] & 0x20) ? 1 : 0);
+ } else /* fixed */
+ return ((sensep[2] & 0x20) ? 1 : 0);
+ return 0;
+}
+
+/* Invokes a SCSI READ LONG (10) command. Return of 0 -> success,
+ * 1 -> ILLEGAL REQUEST with info field written to offsetp,
+ * SG_LIB_CAT_INVALID_OP -> Verify(10) not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+static int sg_ll_read_long10(int sg_fd, int correct, unsigned long lba,
+ void * data_out, int xfer_len, int * offsetp,
+ int vverbose)
+{
+ int k, res, offset;
+ unsigned char readLongCmdBlk[READ_LONG_CMD_LEN];
+ struct sg_io_hdr io_hdr;
+ struct sg_scsi_sense_hdr ssh;
+ unsigned char sense_buffer[SENSE_BUFF_LEN];
+
+ memset(readLongCmdBlk, 0, READ_LONG_CMD_LEN);
+ readLongCmdBlk[0] = READ_LONG_OPCODE;
+ 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);
+
+ if (vverbose) {
+ fprintf(stderr, " Read Long (10) cmd: ");
+ for (k = 0; k < READ_LONG_CMD_LEN; ++k)
+ fprintf(stderr, "%02x ", readLongCmdBlk[k]);
+ fprintf(stderr, "\n");
+ }
+
+ memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
+ io_hdr.interface_id = 'S';
+ io_hdr.cmd_len = sizeof(readLongCmdBlk);
+ io_hdr.mx_sb_len = sizeof(sense_buffer);
+ io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+ io_hdr.dxfer_len = xfer_len;
+ io_hdr.dxferp = data_out;
+ io_hdr.cmdp = readLongCmdBlk;
+ io_hdr.sbp = sense_buffer;
+ io_hdr.timeout = 60000; /* 60000 millisecs == 60 seconds */
+
+ if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+ perror(ME "SG_IO ioctl READ LONG(10) error");
+ return -1;
+ }
+
+ /* now for the error processing */
+ res = sg_err_category3(&io_hdr);
+ switch (res) {
+ case SG_LIB_CAT_RECOVERED:
+ sg_chk_n_print3("READ LONG(10), continuing", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
+ return 0;
+ case SG_LIB_CAT_INVALID_OP:
+ if (vverbose > 1)
+ sg_chk_n_print3("READ LONG(10) command problem", &io_hdr);
+ return res;
+ default:
+ if (vverbose > 1)
+ sg_chk_n_print3("READ LONG(10) sense", &io_hdr);
+ if ((sg_normalize_sense(&io_hdr, &ssh)) &&
+ (ssh.sense_key == ILLEGAL_REQUEST) &&
+ ((offset = info_offset(io_hdr.sbp, io_hdr.sb_len_wr)))) {
+ if (has_blk_ili(io_hdr.sbp, io_hdr.sb_len_wr)) {
+ if (offsetp)
+ *offsetp = offset;
+ return 1;
+ } else if (vverbose)
+ fprintf(stderr, " info field [%d], but ILI clear ??\n",
+ offset);
+ }
+ if (SG_LIB_CAT_ILLEGAL_REQ == res)
+ return res;
+ return -1;
+ }
+}
+
+static int sg_build_scsi_cdb(unsigned char * cdbp, int cdb_sz,
+ unsigned int blocks, long long start_block,
+ int write_true, int fua, int dpo)
{
int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88};
int wr_opcode[] = {0xa, 0x2a, 0xaa, 0x8a};
@@ -352,20 +558,22 @@ 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 */
-int sg_read(int sg_fd, unsigned char * buff, int blocks, long long from_block,
- int bs, int cdbsz, int fua, int * diop)
+/* 0 -> successful, 1 -> recoverable (ENOMEM), 2 -> try again (ua),
+ 3 -> unrecoverable error with io_addr, -2 -> ioctl or request error,
+ -1 -> other SCSI error */
+static int sg_read_low(int sg_fd, unsigned char * buff, int blocks,
+ long long from_block, int bs, int cdbsz, int fua,
+ int pdt, int * diop, unsigned long long * io_addrp)
{
unsigned char rdCmd[MAX_SCSI_CDBSZ];
unsigned char senseBuff[SENSE_BUFF_LEN];
struct sg_io_hdr io_hdr;
- int res;
+ int res, k, info_valid;
if (sg_build_scsi_cdb(rdCmd, cdbsz, blocks, from_block, 0, fua, 0)) {
fprintf(stderr, ME "bad rd cdb build, from_block=%lld, blocks=%d\n",
from_block, blocks);
- return -1;
+ return -2;
}
memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
@@ -382,53 +590,236 @@ int sg_read(int sg_fd, unsigned char * buff, int blocks, long long from_block,
if (diop && *diop)
io_hdr.flags |= SG_FLAG_DIRECT_IO;
+ if (verbose > 2) {
+ fprintf(stderr, " read cdb: ");
+ for (k = 0; k < cdbsz; ++k)
+ fprintf(stderr, "%02x ", rdCmd[k]);
+ fprintf(stderr, "\n");
+ }
while (((res = ioctl(sg_fd, SG_IO, &io_hdr)) < 0) && (EINTR == errno))
;
if (res < 0) {
if (ENOMEM == errno)
return 1;
perror("reading (SG_IO) on sg device, error");
- return -1;
+ return -2;
}
switch (sg_err_category3(&io_hdr)) {
case SG_LIB_CAT_CLEAN:
break;
case SG_LIB_CAT_RECOVERED:
- fprintf(stderr, "Recovered error while reading block=%lld, num=%d\n",
- from_block, blocks);
+ ++recovered_errs;
+ info_valid = sg_get_sense_info_fld(io_hdr.sbp, io_hdr.sb_len_wr,
+ io_addrp);
+ if (info_valid) {
+ fprintf(stderr, " lba of last recovered error in this "
+ "READ=0x%llx\n", *io_addrp);
+ if (verbose > 1)
+ sg_chk_n_print3("reading", &io_hdr);
+ } else {
+ fprintf(stderr, "Recovered error: [no info] reading from "
+ "block=0x%llx, num=%d\n", from_block, blocks);
+ sg_chk_n_print3("reading", &io_hdr);
+ }
break;
case SG_LIB_CAT_MEDIA_CHANGED:
return 2;
+ case SG_LIB_CAT_MEDIUM_HARD:
+ if (verbose > 1)
+ sg_chk_n_print3("reading", &io_hdr);
+ ++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 */
+ else {
+ fprintf(stderr, "Medium or hardware error but no lba of failure"
+ " given\n");
+ return -1;
+ }
+ break;
default:
sg_chk_n_print3("reading", &io_hdr);
- if (do_coe) {
- memset(buff, 0, bs * blocks);
- fprintf(stderr, ">> unable to read at blk=%lld for "
- "%d bytes, use zeros\n", from_block, bs * blocks);
- return 0; /* fudge success */
- }
- else
- return -1;
+ return -1;
}
if (diop && *diop &&
((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
*diop = 0; /* flag that dio not done (completely) */
sum_of_resids += io_hdr.resid;
-#ifdef SG_DEBUG
- fprintf(stderr, "duration=%u ms\n", io_hdr.duration);
-#endif
+ if (verbose > 3)
+ fprintf(stderr, " duration=%u ms\n", io_hdr.duration);
return 0;
}
-/* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM),
- 2 -> try again */
-int sg_write(int sg_fd, unsigned char * buff, int blocks, long long to_block,
- int bs, int cdbsz, int fua, int * diop)
+/* Returns >= 0 -> number of blocks read, -1 -> unrecoverable error,
+ -2 -> recoverable (ENOMEM) */
+static int sg_read(int sg_fd, unsigned char * buff, int blocks,
+ long long from_block, int bs, int cdbsz, int fua,
+ int * diop, int pdt)
+{
+ unsigned long long io_addr;
+ long long lba;
+ int res, blks, cont, xferred;
+ unsigned char * bp;
+
+ 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, pdt,
+ 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;
+ break;
+ case -1:
+ goto err_out;
+ case -2:
+ do_coe = 0;
+ goto err_out;
+ case 3:
+ break; /* unrecovered read error at lba=io_addr */
+ default:
+ fprintf(stderr, ">> unexpected result=%d from sg_read_low()\n",
+ res);
+ return -1;
+ }
+ if (cont)
+ continue;
+ if ((io_addr < (unsigned long long)lba) ||
+ (io_addr >= (unsigned long long)(lba + blks))) {
+ fprintf(stderr, " Unrecovered error lba 0x%llx not in "
+ "correct range:\n\t[0x%llx,0x%llx]\n", io_addr,
+ (unsigned long long)lba,
+ (unsigned long long)(lba + blks - 1));
+ goto err_out;
+ }
+ blks = (int)(io_addr - (unsigned long long)lba);
+ if (blks > 0) {
+ res = sg_read_low(sg_fd, bp, blks, lba, bs, cdbsz, fua,
+ pdt, diop, &io_addr);
+ switch (res) {
+ case 0:
+ break;
+ case 1:
+ fprintf(stderr, "ENOMEM again, unexpected (r)\n");
+ return -1;
+ case 2:
+ fprintf(stderr,
+ "Unit attention, media changed, unexpected (r)\n");
+ return -1;
+ case -2:
+ do_coe = 0;
+ goto err_out;
+ case -1: case 3:
+ goto err_out;
+ default:
+ fprintf(stderr, ">> unexpected result=%d from "
+ "sg_read_low() 2\n", res);
+ return -1;
+ }
+ }
+ xferred += blks;
+ if (! do_coe)
+ return xferred; /* give up at block before problem unless 'coe' */
+ 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) {
+ fprintf(stderr, ">> unrecovered read error at blk=%lld, "
+ "pdt=%d, use zeros\n", lba, pdt);
+ memset(bp, 0, bs);
+ } else if (io_addr < UINT_MAX) {
+ unsigned char * buffp;
+ int offset, nl, r, ok;
+
+ buffp = malloc(bs * 2);
+ if (NULL == buffp) {
+ fprintf(stderr, ">> heap problems\n");
+ return -1;
+ }
+ res = sg_ll_read_long10(sg_fd, 0 /*corrct */, lba,
+ buffp, bs + 8, &offset, verbose);
+ ok = 0;
+ switch (res) {
+ case 0:
+ ok = 1;
+ ++read_longs;
+ break;
+ case 1:
+ nl = bs + 8 - offset;
+ if ((nl < 32) || (nl > (bs * 2))) {
+ fprintf(stderr, ">> read_long(10) len=%d unexpected\n",
+ nl);
+ break;
+ }
+ r = sg_ll_read_long10(sg_fd, 0 /*corrct */, lba,
+ buffp, nl, &offset, verbose);
+ if (0 == r) {
+ ok = 1;
+ ++read_longs;
+ break;
+ } else
+ fprintf(stderr, ">> unexpected result=%d on second "
+ "read_long(10)\n", r);
+ break;
+ case SG_LIB_CAT_INVALID_OP:
+ fprintf(stderr, ">> read_long(10) not supported\n");
+ break;
+ case SG_LIB_CAT_ILLEGAL_REQ:
+ fprintf(stderr, ">> read_long(10) bad cdb field\n");
+ break;
+ default:
+ fprintf(stderr, ">> read_long(10) problem\n");
+ break;
+ }
+ if (ok)
+ memcpy(bp, buffp, bs);
+ else
+ memset(bp, 0, bs);
+ free(buffp);
+ } else {
+ fprintf(stderr, ">> read_long(10) cannot handle blk=%lld, "
+ "use zeros\n", lba);
+ memset(bp, 0, bs);
+ }
+ ++xferred;
+ ++blks;
+ bp += bs;
+ ++lba;
+ }
+ return xferred;
+
+err_out:
+ if (do_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 */
+ } else
+ return -1;
+}
+
+/* 0 -> successful, -1 -> unrecoverable error, -2 -> recoverable (ENOMEM),
+ -3 -> try again (media changed unit attention) */
+static int sg_write(int sg_fd, unsigned char * buff, int blocks,
+ long long to_block, int bs, int cdbsz, int fua,
+ int * diop)
{
unsigned char wrCmd[MAX_SCSI_CDBSZ];
unsigned char senseBuff[SENSE_BUFF_LEN];
struct sg_io_hdr io_hdr;
- int res;
+ int res, k, info_valid;
+ unsigned long long io_addr = 0;
if (sg_build_scsi_cdb(wrCmd, cdbsz, blocks, to_block, 1, fua, 0)) {
fprintf(stderr, ME "bad wr cdb build, to_block=%lld, blocks=%d\n",
@@ -450,11 +841,17 @@ int sg_write(int sg_fd, unsigned char * buff, int blocks, long long to_block,
if (diop && *diop)
io_hdr.flags |= SG_FLAG_DIRECT_IO;
+ if (verbose > 2) {
+ fprintf(stderr, " write cdb: ");
+ for (k = 0; k < cdbsz; ++k)
+ fprintf(stderr, "%02x ", wrCmd[k]);
+ fprintf(stderr, "\n");
+ }
while (((res = ioctl(sg_fd, SG_IO, &io_hdr)) < 0) && (EINTR == errno))
;
if (res < 0) {
if (ENOMEM == errno)
- return 1;
+ return -2;
perror("writing (SG_IO) on sg device, error");
return -1;
}
@@ -462,11 +859,22 @@ int sg_write(int sg_fd, unsigned char * buff, int blocks, long long to_block,
case SG_LIB_CAT_CLEAN:
break;
case SG_LIB_CAT_RECOVERED:
- fprintf(stderr, "Recovered error while writing block=%lld, num=%d\n",
- to_block, blocks);
+ ++recovered_errs;
+ info_valid = sg_get_sense_info_fld(io_hdr.sbp, io_hdr.sb_len_wr,
+ &io_addr);
+ if (info_valid) {
+ fprintf(stderr, " lba of last recovered error in this "
+ "WRITE=0x%llx\n", io_addr);
+ if (verbose > 1)
+ sg_chk_n_print3("writing", &io_hdr);
+ } else {
+ fprintf(stderr, "Recovered error: [no info] writing to "
+ "block=0x%llx, num=%d\n", to_block, blocks);
+ sg_chk_n_print3("writing", &io_hdr);
+ }
break;
case SG_LIB_CAT_MEDIA_CHANGED:
- return 2;
+ return -3;
default:
sg_chk_n_print3("writing", &io_hdr);
if (do_coe) {
@@ -480,19 +888,148 @@ int sg_write(int sg_fd, unsigned char * buff, int blocks, long long to_block,
if (diop && *diop &&
((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
*diop = 0; /* flag that dio not done (completely) */
+ if (verbose > 3)
+ fprintf(stderr, " duration=%u ms\n", io_hdr.duration);
return 0;
}
-#define STR_SZ 1024
-#define INOUTF_SZ 512
-#define EBUFF_SZ 512
+static void calc_duration_throughput()
+{
+ struct timeval end_tm, res_tm;
+ double a, b;
+
+ if (start_tm_valid && (start_tm.tv_sec || start_tm.tv_usec)) {
+ gettimeofday(&end_tm, NULL);
+ res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
+ res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
+ if (res_tm.tv_usec < 0) {
+ --res_tm.tv_sec;
+ res_tm.tv_usec += 1000000;
+ }
+ a = res_tm.tv_sec;
+ a += (0.000001 * res_tm.tv_usec);
+ b = (double)blk_sz * (req_count - dd_count);
+ fprintf(stderr, " time to transfer data: %d.%06d secs",
+ (int)res_tm.tv_sec, (int)res_tm.tv_usec);
+ if ((a > 0.00001) && (b > 511))
+ fprintf(stderr, " at %.2f MB/sec\n", b / (a * 1000000.0));
+ else
+ fprintf(stderr, "\n");
+ }
+}
+
+static void print_mp_bit(const char * pre, int smask, int byte_off,
+ int bit_mask, const unsigned char * cur_mp,
+ const unsigned char * cha_mp, const unsigned char * def_mp,
+ const unsigned char * sav_mp)
+{
+ int sep = 0;
+
+ fprintf(stderr, "%s%d", pre, !!(cur_mp[byte_off] & bit_mask));
+ if (smask & 0xe) {
+ fprintf(stderr, " [");
+ if (smask & 2) {
+ fprintf(stderr, "Changeable: %s",
+ (cha_mp[byte_off] & bit_mask) ? "y" : "n");
+ sep = 1;
+ }
+ if (smask & 4) {
+ fprintf(stderr, "%sdef: %d", (sep ? ", " : " "),
+ !!(def_mp[2] & bit_mask));
+ sep = 1;
+ }
+ if (smask & 8)
+ fprintf(stderr, "%ssaved: %d", (sep ? ", " : " "),
+ !!(sav_mp[2] & bit_mask));
+ fprintf(stderr, "]\n");
+ } else
+ fprintf(stderr, "\n");
+}
+
+static void print_scsi_dev_info(int sg_fd, int pdt)
+{
+ int res, verb, smask;
+ unsigned char cur_mp[DEF_MODE_RESP_LEN];
+ unsigned char cha_mp[DEF_MODE_RESP_LEN];
+ unsigned char def_mp[DEF_MODE_RESP_LEN];
+ unsigned char sav_mp[DEF_MODE_RESP_LEN];
+ int mode6;
+
+ mode6 = (6 == DEF_MODE_CDB_SZ) ? 1 : 0;
+ verb = (verbose > 0) ? verbose - 1 : 0;
+ res = sg_get_mode_page_types(sg_fd, mode6, RW_ERR_RECOVERY_MP,
+ 0 /* subpage */ , DEF_MODE_RESP_LEN,
+ &smask, cur_mp, cha_mp, def_mp, sav_mp, verb);
+ if (SG_LIB_CAT_INVALID_OP == res) {
+ mode6 = !mode6; /* flip between mode sense(10) and (6) */
+ res = sg_get_mode_page_types(sg_fd, mode6, RW_ERR_RECOVERY_MP,
+ 0 /* subpage */ , DEF_MODE_RESP_LEN,
+ &smask, cur_mp, cha_mp, def_mp, sav_mp, verb);
+ }
+ if (0 == (smask & 1)) {
+ if (verbose > 1)
+ fprintf(stderr, " Read write error recovery mode page not "
+ "supported, res=%d\n", res);
+ } else if (cur_mp[1] < 0xa)
+ fprintf(stderr, " Read write error recovery mode page too "
+ "short, page len=%d\n", cur_mp[1]);
+ else {
+ fprintf(stderr, " Read write error recovery mode page:\n");
+ print_mp_bit(" AWRE: ", smask, 2, 0x80, cur_mp, cha_mp,
+ def_mp, sav_mp);
+ print_mp_bit(" ARRE: ", smask, 2, 0x40, cur_mp, cha_mp,
+ def_mp, sav_mp);
+ print_mp_bit(" RC: ", smask, 2, 0x10, cur_mp, cha_mp,
+ def_mp, sav_mp);
+ if (0 == pdt)
+ print_mp_bit(" EER: ", smask, 2, 0x8, cur_mp, cha_mp,
+ def_mp, sav_mp);
+ print_mp_bit(" PER: ", smask, 2, 0x4, cur_mp, cha_mp,
+ def_mp, sav_mp);
+ print_mp_bit(" DTE: ", smask, 2, 0x2, cur_mp, cha_mp,
+ def_mp, sav_mp);
+ print_mp_bit(" DCR: ", smask, 2, 0x1, cur_mp, cha_mp,
+ def_mp, sav_mp);
+ }
+ res = sg_get_mode_page_types(sg_fd, mode6, CACHING_MP,
+ 0 /* subpage */ , DEF_MODE_RESP_LEN,
+ &smask, cur_mp, cha_mp, def_mp, sav_mp, verb);
+ if (0 == (smask & 1)) {
+ if (verbose > 1)
+ fprintf(stderr, " Caching mode page not "
+ "supported, res=%d\n", res);
+ } else if (cur_mp[1] < 0xa)
+ fprintf(stderr, " Caching mode page too "
+ "short, page len=%d\n", cur_mp[1]);
+ else {
+ fprintf(stderr, " Caching mode page:\n");
+ print_mp_bit(" WRE: ", smask, 2, 0x4, cur_mp, cha_mp,
+ def_mp, sav_mp);
+ print_mp_bit(" RCD: ", smask, 2, 0x1, cur_mp, cha_mp,
+ def_mp, sav_mp);
+ }
+ res = sg_get_mode_page_types(sg_fd, mode6, CONTROL_MP,
+ 0 /* subpage */ , DEF_MODE_RESP_LEN,
+ &smask, cur_mp, cha_mp, def_mp, sav_mp, verb);
+ if (0 == (smask & 1)) {
+ if (verbose > 1)
+ fprintf(stderr, " Control mode page not "
+ "supported, res=%d\n", res);
+ } else if (cur_mp[1] < 0xa)
+ fprintf(stderr, " Control mode page too "
+ "short, page len=%d\n", cur_mp[1]);
+ else {
+ fprintf(stderr, " Control mode page:\n");
+ print_mp_bit(" SWP: ", smask, 4, 0x8, cur_mp, cha_mp,
+ def_mp, sav_mp);
+ }
+}
int main(int argc, char * argv[])
{
long long skip = 0;
long long seek = 0;
- int bs = 0;
int ibs = 0;
int obs = 0;
int bpt = DEF_BLOCKS_PER_TRANSFER;
@@ -505,7 +1042,6 @@ int main(int argc, char * argv[])
int out_type = FT_OTHER;
int dio = 0;
int dio_incomplete = 0;
- int do_time = 0;
int do_odir = 0;
int scsi_cdbsz_in = DEF_SCSI_CDBSZ;
int scsi_cdbsz_out = DEF_SCSI_CDBSZ;
@@ -513,8 +1049,9 @@ int main(int argc, char * argv[])
int do_sync = 0;
int do_blk_sgio = 0;
int do_append = 0;
- int res, k, t, buf_sz, dio_tmp;
- int infd, outfd, blocks;
+ int verb = 0;
+ int res, k, t, buf_sz, dio_tmp, flags;
+ int infd, outfd, blocks, in_pdt, out_pdt;
unsigned char * wrkBuff;
unsigned char * wrkPos;
long long in_num_sect = -1;
@@ -522,12 +1059,13 @@ int main(int argc, char * argv[])
int in_sect_sz, out_sect_sz;
char ebuff[EBUFF_SZ];
int blocks_per;
- long long req_count;
- struct timeval start_tm, end_tm;
+ struct sg_simple_inquiry_resp sir;
inf[0] = '\0';
outf[0] = '\0';
if (argc < 2) {
+ fprintf(stderr,
+ "Can't have both 'if' as stdin _and_ 'of' as stdout\n");
usage();
return 1;
}
@@ -559,17 +1097,37 @@ int main(int argc, char * argv[])
ibs = sg_get_num(buf);
else if (0 == strcmp(key,"obs"))
obs = sg_get_num(buf);
- else if (0 == strcmp(key,"bs"))
- bs = sg_get_num(buf);
- else if (0 == strcmp(key,"bpt"))
+ 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;
+ }
+ } else if (0 == strcmp(key,"bpt")) {
bpt = sg_get_num(buf);
- else if (0 == strcmp(key,"skip"))
+ if (-1 == bpt) {
+ fprintf(stderr, ME "bad argument to 'bpt'\n");
+ return 1;
+ }
+ } else if (0 == strcmp(key,"skip")) {
skip = sg_get_llnum(buf);
- else if (0 == strcmp(key,"seek"))
+ if (-1LL == skip) {
+ fprintf(stderr, ME "bad argument to 'skip'\n");
+ return 1;
+ }
+ } else if (0 == strcmp(key,"seek")) {
seek = sg_get_llnum(buf);
- else if (0 == strcmp(key,"count"))
+ if (-1LL == seek) {
+ fprintf(stderr, ME "bad argument to 'seek'\n");
+ return 1;
+ }
+ } else if (0 == strcmp(key,"count")) {
dd_count = sg_get_llnum(buf);
- else if (0 == strcmp(key,"dio"))
+ if (-1LL == dd_count) {
+ fprintf(stderr, ME "bad argument to 'count'\n");
+ return 1;
+ }
+ } else if (0 == strcmp(key,"dio"))
dio = sg_get_num(buf);
else if (0 == strcmp(key,"coe"))
do_coe = sg_get_num(buf);
@@ -588,22 +1146,24 @@ int main(int argc, char * argv[])
do_blk_sgio = sg_get_num(buf);
else if (0 == strncmp(key,"app", 3))
do_append = sg_get_num(buf);
- else if (0 == strncmp(key, "--vers", 6)) {
- fprintf(stderr, ME "for Linux sg version 3 driver: %s\n",
- version_str);
+ else if (0 == strncmp(key, "verb", 4)) {
+ verbose = sg_get_num(buf);
+ verb = (verbose ? verbose - 1: 0);
+ } else if (0 == strncmp(key, "--vers", 6)) {
+ fprintf(stderr, ME "%s\n", version_str);
return 0;
- }
- else {
+ } else {
fprintf(stderr, "Unrecognized argument '%s'\n", key);
usage();
return 1;
}
}
- if (bs <= 0) {
- bs = DEF_BLOCK_SIZE;
- fprintf(stderr, "Assume default 'bs' (block size) of %d bytes\n", bs);
+ if (blk_sz <= 0) {
+ blk_sz = DEF_BLOCK_SIZE;
+ fprintf(stderr, "Assume default 'bs' (block size) of %d bytes\n",
+ blk_sz);
}
- if ((ibs && (ibs != bs)) || (obs && (obs != bs))) {
+ if ((ibs && (ibs != blk_sz)) || (obs && (obs != blk_sz))) {
fprintf(stderr, "If 'ibs' or 'obs' given must be same as 'bs'\n");
usage();
return 1;
@@ -631,9 +1191,14 @@ int main(int argc, char * argv[])
infd = STDIN_FILENO;
outfd = STDOUT_FILENO;
+ in_pdt = -1;
+ out_pdt = -1;
if (inf[0] && ('-' != inf[0])) {
in_type = dd_filetype(inf);
+ if (verbose)
+ fprintf(stderr, " >> Input file type: %s\n",
+ dd_filetype_str(in_type, ebuff));
if ((FT_BLOCK & in_type) && do_blk_sgio)
in_type |= FT_SG;
@@ -642,48 +1207,77 @@ int main(int argc, char * argv[])
return 1;
}
else if (FT_SG & in_type) {
- if ((infd = open(inf, O_RDWR)) < 0) {
- if ((infd = open(inf, O_RDONLY)) < 0) {
+ flags = O_RDWR;
+ if ((do_odir && (FT_BLOCK & in_type)))
+ flags |= O_DIRECT;
+ if ((infd = open(inf, flags)) < 0) {
+ flags = O_RDONLY;
+ if ((do_odir && (FT_BLOCK & in_type)))
+ flags |= O_DIRECT;
+ if ((infd = open(inf, flags)) < 0) {
snprintf(ebuff, EBUFF_SZ,
ME "could not open %s for sg reading", inf);
perror(ebuff);
return 1;
}
}
- t = bs * bpt;
- res = ioctl(infd, SG_SET_RESERVED_SIZE, &t);
- if (res < 0)
- perror(ME "SG_SET_RESERVED_SIZE error");
- res = ioctl(infd, SG_GET_VERSION_NUM, &t);
- if ((res < 0) || (t < 30000)) {
- if (FT_BLOCK & in_type)
- fprintf(stderr, ME "SG_IO unsupported on this block"
- " device\n");
- else
- fprintf(stderr, ME "sg driver prior to 3.x.y\n");
- return 1;
+ if (verbose)
+ fprintf(stderr, " open input(sg_io), flags=0x%x\n",
+ flags);
+ if (sg_simple_inquiry(infd, &sir, 0, verb)) {
+ fprintf(stderr, "INQUIRY failed on %s\n", inf);
+ return -1;
+ }
+ in_pdt = sir.peripheral_type;
+ if (verbose)
+ fprintf(stderr, " %s: %.8s %.16s %.4s [pdt=%d]\n",
+ inf, sir.vendor, sir.product, sir.revision, in_pdt);
+ if (! (FT_BLOCK & in_type)) {
+ t = blk_sz * bpt;
+ res = ioctl(infd, SG_SET_RESERVED_SIZE, &t);
+ if (res < 0)
+ perror(ME "SG_SET_RESERVED_SIZE error");
+ res = ioctl(infd, SG_GET_VERSION_NUM, &t);
+ if ((res < 0) || (t < 30000)) {
+ if (FT_BLOCK & in_type)
+ fprintf(stderr, ME "SG_IO unsupported on this block"
+ " device\n");
+ else
+ fprintf(stderr, ME "sg driver prior to 3.x.y\n");
+ return 1;
+ }
}
+ if (verbose)
+ print_scsi_dev_info(infd, in_pdt);
}
else {
+ flags = O_RDONLY;
if (do_odir && (FT_BLOCK & in_type))
- infd = open(inf, O_RDONLY | O_DIRECT);
- else
- infd = open(inf, O_RDONLY);
+ flags |= O_DIRECT;
+ infd = open(inf, flags);
if (infd < 0) {
snprintf(ebuff, EBUFF_SZ,
ME "could not open %s for reading", inf);
perror(ebuff);
return 1;
}
- else if (skip > 0) {
- llse_loff_t offset = skip;
-
- offset *= bs; /* could exceed 32 bits here! */
- if (llse_llseek(infd, offset, SEEK_SET) < 0) {
- snprintf(ebuff, EBUFF_SZ,
- ME "couldn't skip to required position on %s", inf);
- perror(ebuff);
- return 1;
+ else {
+ if (verbose)
+ fprintf(stderr, " open input, flags=0x%x\n",
+ flags);
+ if (skip > 0) {
+ llse_loff_t offset = skip;
+
+ offset *= blk_sz; /* could exceed 32 bits here! */
+ if (llse_llseek(infd, offset, SEEK_SET) < 0) {
+ snprintf(ebuff, EBUFF_SZ, ME "couldn't skip to "
+ "required position on %s", inf);
+ perror(ebuff);
+ return 1;
+ }
+ if (verbose)
+ fprintf(stderr, " >> skip: llseek SEEK_SET, "
+ "byte offset=0x%llx\n", offset);
}
}
}
@@ -692,6 +1286,9 @@ int main(int argc, char * argv[])
if (outf[0] && ('-' != outf[0])) {
out_type = dd_filetype(outf);
+ if (verbose)
+ fprintf(stderr, " >> Output file type: %s\n",
+ dd_filetype_str(out_type, ebuff));
if ((FT_BLOCK & out_type) && do_blk_sgio)
out_type |= FT_SG;
@@ -700,28 +1297,45 @@ int main(int argc, char * argv[])
return 1;
}
else if (FT_SG & out_type) {
- if ((outfd = open(outf, O_RDWR)) < 0) {
+ flags = O_RDWR;
+ if ((do_odir && (FT_BLOCK & out_type)))
+ flags |= O_DIRECT;
+ if ((outfd = open(outf, flags)) < 0) {
snprintf(ebuff, EBUFF_SZ,
ME "could not open %s for sg writing", outf);
perror(ebuff);
return 1;
}
- t = bs * bpt;
- res = ioctl(outfd, SG_SET_RESERVED_SIZE, &t);
- if (res < 0)
- perror(ME "SG_SET_RESERVED_SIZE error");
- 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;
+ 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;
+ }
+ out_pdt = sir.peripheral_type;
+ if (verbose)
+ fprintf(stderr, " %s: %.8s %.16s %.4s [pdt=%d]\n",
+ outf, sir.vendor, sir.product, sir.revision, out_pdt);
+ if (! (FT_BLOCK & out_type)) {
+ t = blk_sz * bpt;
+ res = ioctl(outfd, SG_SET_RESERVED_SIZE, &t);
+ if (res < 0)
+ perror(ME "SG_SET_RESERVED_SIZE error");
+ 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;
+ }
}
+ if (verbose)
+ print_scsi_dev_info(outfd, out_pdt);
}
else if (FT_DEV_NULL & out_type)
outfd = -1; /* don't bother opening */
else {
if (! (FT_RAW & out_type)) {
- int flags = O_WRONLY | O_CREAT;
-
+ flags = O_WRONLY | O_CREAT;
if (do_odir && (FT_BLOCK & out_type) && (! (FT_SG & out_type)))
flags |= O_DIRECT;
else if (do_append && (! (FT_BLOCK & out_type)))
@@ -734,23 +1348,29 @@ int main(int argc, char * argv[])
}
}
else {
- if ((outfd = open(outf, O_WRONLY)) < 0) {
+ flags = O_WRONLY;
+ if ((outfd = open(outf, flags)) < 0) {
snprintf(ebuff, EBUFF_SZ,
ME "could not open %s for raw writing", outf);
perror(ebuff);
return 1;
}
}
+ if (verbose)
+ fprintf(stderr, " open output, flags=0x%x\n", flags);
if (seek > 0) {
llse_loff_t offset = seek;
- offset *= bs; /* could exceed 32 bits here! */
+ offset *= blk_sz; /* could exceed 32 bits here! */
if (llse_llseek(outfd, offset, SEEK_SET) < 0) {
snprintf(ebuff, EBUFF_SZ,
ME "couldn't seek to required position on %s", outf);
perror(ebuff);
return 1;
}
+ if (verbose)
+ fprintf(stderr, " >> seek: llseek SEEK_SET, "
+ "byte offset=0x%llx\n", offset);
}
}
}
@@ -760,17 +1380,21 @@ int main(int argc, char * argv[])
return 1;
}
- if (dd_count < 0) {
+ 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 (2 == res) {
+ if (SG_LIB_CAT_MEDIA_CHANGED == res) {
fprintf(stderr,
"Unit attention, media changed(in), continuing\n");
res = scsi_read_capacity(infd, &in_num_sect, &in_sect_sz);
}
if (0 != res) {
- fprintf(stderr, "Unable to read capacity on %s\n", inf);
+ if (res == SG_LIB_CAT_INVALID_OP)
+ fprintf(stderr, "read capacity not supported on %s\n",
+ inf);
+ else
+ fprintf(stderr, "Unable to read capacity on %s\n", inf);
in_num_sect = -1;
}
} else if (FT_BLOCK & in_type) {
@@ -778,9 +1402,9 @@ int main(int argc, char * argv[])
fprintf(stderr, "Unable to read block capacity on %s\n", inf);
in_num_sect = -1;
}
- if (bs != in_sect_sz) {
- fprintf(stderr, "block size on %s confusion; bs=%d, from "
- "device=%d\n", inf, bs, in_sect_sz);
+ if (blk_sz != in_sect_sz) {
+ fprintf(stderr, "block size on %s confusion; bs=%d, "
+ "device claims=%d\n", inf, blk_sz, in_sect_sz);
in_num_sect = -1;
}
}
@@ -790,13 +1414,17 @@ 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 (2 == res) {
+ if (SG_LIB_CAT_MEDIA_CHANGED == res) {
fprintf(stderr,
"Unit attention, media changed(out), continuing\n");
res = scsi_read_capacity(outfd, &out_num_sect, &out_sect_sz);
}
if (0 != res) {
- fprintf(stderr, "Unable to read capacity on %s\n", outf);
+ if (res == SG_LIB_CAT_INVALID_OP)
+ fprintf(stderr, "read capacity not supported on %s\n",
+ outf);
+ else
+ fprintf(stderr, "Unable to read capacity on %s\n", outf);
out_num_sect = -1;
}
} else if (FT_BLOCK & out_type) {
@@ -806,9 +1434,9 @@ int main(int argc, char * argv[])
outf);
out_num_sect = -1;
}
- if (bs != out_sect_sz) {
- fprintf(stderr, "block size on %s confusion: bs=%d, from "
- "device=%d\n", outf, bs, out_sect_sz);
+ if (blk_sz != out_sect_sz) {
+ fprintf(stderr, "block size on %s confusion: bs=%d, "
+ "device claims=%d\n", outf, blk_sz, out_sect_sz);
out_num_sect = -1;
}
}
@@ -819,15 +1447,17 @@ int main(int argc, char * argv[])
"Start of loop, count=%lld, in_num_sect=%lld, out_num_sect=%lld\n",
dd_count, in_num_sect, out_num_sect);
#endif
- if (in_num_sect > 0) {
- if (out_num_sect > 0)
- dd_count = (in_num_sect > out_num_sect) ? out_num_sect :
- in_num_sect;
+ if (dd_count < 0) {
+ if (in_num_sect > 0) {
+ if (out_num_sect > 0)
+ dd_count = (in_num_sect > out_num_sect) ? out_num_sect :
+ in_num_sect;
+ else
+ dd_count = in_num_sect;
+ }
else
- dd_count = in_num_sect;
+ dd_count = out_num_sect;
}
- else
- dd_count = out_num_sect;
}
if (dd_count < 0) {
@@ -849,7 +1479,7 @@ int main(int argc, char * argv[])
if (dio || do_odir || (FT_RAW & in_type) || (FT_RAW & out_type)) {
size_t psz = getpagesize();
- wrkBuff = malloc(bs * bpt + psz);
+ wrkBuff = malloc(blk_sz * bpt + psz);
if (0 == wrkBuff) {
fprintf(stderr, "Not enough user memory for raw\n");
return 1;
@@ -858,7 +1488,7 @@ int main(int argc, char * argv[])
(~(psz - 1)));
}
else {
- wrkBuff = malloc(bs * bpt);
+ wrkBuff = malloc(blk_sz * bpt);
if (0 == wrkBuff) {
fprintf(stderr, "Not enough user memory\n");
return 1;
@@ -868,13 +1498,14 @@ int main(int argc, char * argv[])
blocks_per = bpt;
#ifdef SG_DEBUG
- fprintf(stderr, "Start of loop, count=%lld, blocks_per=%d\n",
+ fprintf(stderr, "Start of loop, count=%lld, blocks_per=%d\n",
dd_count, blocks_per);
#endif
if (do_time) {
start_tm.tv_sec = 0;
start_tm.tv_usec = 0;
gettimeofday(&start_tm, NULL);
+ start_tm_valid = 1;
}
req_count = dd_count;
@@ -885,52 +1516,53 @@ int main(int argc, char * argv[])
int fua = fua_mode & 2;
dio_tmp = dio;
- res = sg_read(infd, wrkPos, blocks, skip, bs, scsi_cdbsz_in, fua,
- &dio_tmp);
- if (1 == res) { /* ENOMEM, find what's available+try that */
+ res = sg_read(infd, wrkPos, blocks, skip, blk_sz, scsi_cdbsz_in,
+ fua, &dio_tmp, in_pdt);
+ 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");
break;
}
- blocks_per = (buf_sz + bs - 1) / bs;
+ blocks_per = (buf_sz + blk_sz - 1) / blk_sz;
if (blocks_per < blocks) {
blocks = blocks_per;
fprintf(stderr, "Reducing read to %d blocks per "
"loop\n", blocks_per);
- res = sg_read(infd, wrkPos, blocks, skip, bs,
- scsi_cdbsz_in, fua, &dio_tmp);
+ res = sg_read(infd, wrkPos, blocks, skip, blk_sz,
+ scsi_cdbsz_in, fua, &dio_tmp, in_pdt);
}
}
- else if (2 == res) {
- fprintf(stderr,
- "Unit attention, media changed, continuing (r)\n");
- res = sg_read(infd, wrkPos, blocks, skip, bs, scsi_cdbsz_in,
- fua, &dio_tmp);
- }
- if (0 != res) {
- fprintf(stderr, "sg_read failed,%s seek=%lld\n",
- ((1 == res) ? " try reducing bpt," : ""), seek);
+ if (res < 0) {
+ fprintf(stderr, "sg_read failed,%s at or after lba=%lld "
+ "[0x%llx]\n",
+ ((-2 == res) ? " try reducing bpt," : ""), skip, skip);
break;
- }
- else {
+ } else {
+ if (res < blocks) {
+ dd_count = 0; /* force exit after write */
+ blocks = res;
+ }
in_full += blocks;
if (dio && (0 == dio_tmp))
dio_incomplete++;
}
}
else {
- while (((res = read(infd, wrkPos, blocks * bs)) < 0) &&
+ while (((res = read(infd, wrkPos, blocks * blk_sz)) < 0) &&
(EINTR == errno))
;
+ if (verbose > 2)
+ fprintf(stderr, "read(unix): count=%d, res=%d\n",
+ blocks * blk_sz, res);
if (res < 0) {
snprintf(ebuff, EBUFF_SZ, ME "reading, skip=%lld ", skip);
perror(ebuff);
break;
}
- else if (res < blocks * bs) {
+ else if (res < blocks * blk_sz) {
dd_count = 0;
- blocks = res / bs;
- if ((res % bs) > 0) {
+ blocks = res / blk_sz;
+ if ((res % blk_sz) > 0) {
blocks++;
in_partial++;
}
@@ -945,31 +1577,31 @@ int main(int argc, char * argv[])
int fua = fua_mode & 1;
dio_tmp = dio;
- res = sg_write(outfd, wrkPos, blocks, seek, bs, scsi_cdbsz_out,
- fua, &dio_tmp);
- if (1 == res) { /* ENOMEM, find what's available+try that */
+ res = sg_write(outfd, wrkPos, blocks, seek, blk_sz,
+ scsi_cdbsz_out, fua, &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");
break;
}
- blocks_per = (buf_sz + bs - 1) / bs;
+ 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, bs,
+ res = sg_write(outfd, wrkPos, blocks, seek, blk_sz,
scsi_cdbsz_out, fua, &dio_tmp);
}
}
- else if (2 == res) {
+ else if (-3 == res) {
fprintf(stderr,
"Unit attention, media changed, continuing (w)\n");
- res = sg_write(outfd, wrkPos, blocks, seek, bs, scsi_cdbsz_out,
- fua, &dio_tmp);
+ res = sg_write(outfd, wrkPos, blocks, seek, blk_sz,
+ scsi_cdbsz_out, fua, &dio_tmp);
}
- if (0 != res) {
+ if (res < 0) {
fprintf(stderr, "sg_write failed,%s seek=%lld\n",
- ((1 == res) ? " try reducing bpt," : ""), seek);
+ ((-2 == res) ? " try reducing bpt," : ""), seek);
break;
}
else {
@@ -981,19 +1613,22 @@ int main(int argc, char * argv[])
else if (FT_DEV_NULL & out_type)
out_full += blocks; /* act as if written out without error */
else {
- while (((res = write(outfd, wrkPos, blocks * bs)) < 0)
+ while (((res = write(outfd, wrkPos, blocks * blk_sz)) < 0)
&& (EINTR == errno))
;
+ if (verbose > 2)
+ fprintf(stderr, "write(unix): count=%d, res=%d\n",
+ blocks * blk_sz, res);
if (res < 0) {
snprintf(ebuff, EBUFF_SZ, ME "writing, seek=%lld ", seek);
perror(ebuff);
break;
}
- else if (res < blocks * bs) {
+ else if (res < blocks * blk_sz) {
fprintf(stderr, "output file probably full, seek=%lld ", seek);
- blocks = res / bs;
+ blocks = res / blk_sz;
out_full += blocks;
- if ((res % bs) > 0)
+ if ((res % blk_sz) > 0)
out_partial++;
break;
}
@@ -1006,27 +1641,9 @@ int main(int argc, char * argv[])
seek += blocks;
} /* end of main loop that does the copy ... */
- if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
- struct timeval res_tm;
- double a, b;
+ if (do_time)
+ calc_duration_throughput();
- gettimeofday(&end_tm, NULL);
- res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
- res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
- if (res_tm.tv_usec < 0) {
- --res_tm.tv_sec;
- res_tm.tv_usec += 1000000;
- }
- a = res_tm.tv_sec;
- a += (0.000001 * res_tm.tv_usec);
- b = (double)bs * (req_count - dd_count);
- fprintf(stderr, "time to transfer data was %d.%06d secs",
- (int)res_tm.tv_sec, (int)res_tm.tv_usec);
- if ((a > 0.00001) && (b > 511))
- fprintf(stderr, ", %.2f MB/sec\n", b / (a * 1000000.0));
- else
- fprintf(stderr, "\n");
- }
if (do_sync) {
if (FT_SG & out_type) {
fprintf(stderr, ">> Synchronizing cache on %s\n", outf);
diff --git a/sg_emc_trespass.c b/sg_emc_trespass.c
index 89a49e13..ef08bbd1 100644
--- a/sg_emc_trespass.c
+++ b/sg_emc_trespass.c
@@ -25,7 +25,7 @@
* any later version.
*/
-static char * version_str = "0.11 20041019";
+static char * version_str = "0.12 20050210";
static int debug = 0;
@@ -37,7 +37,7 @@ static void do_trespass(int fd, int hr, int short_cmd)
{ 0, 0, 0, 0, 0, 0, 0, 0x00,
TRESPASS_PAGE, /* Page code */
0x09, /* Page length - 2 */
- hr ? 0x01 : 0x81, /* Trespass code + Honor reservation bit */
+ 0x81, /* Trespass code + Honor reservation bit */
0xff, 0xff, /* Trespass target */
0, 0, 0, 0, 0, 0 /* Reserved bytes / unknown */
};
@@ -45,11 +45,15 @@ static void do_trespass(int fd, int hr, int short_cmd)
{ 0, 0, 0, 0,
TRESPASS_PAGE, /* Page code */
0x02, /* Page length - 2 */
- hr ? 0x01 : 0x81, /* Trespass code + Honor reservation bit */
+ 0x81, /* Trespass code + Honor reservation bit */
0xff, /* Trespass target */
};
int res;
+ if (hr) { /* override Trespass code + Honor reservation bit */
+ short_trespass_pg[6] = 0x01;
+ long_trespass_pg[10] = 0x01;
+ }
if (short_cmd)
res = sg_ll_mode_select6(fd, 1 /* pf */, 0 /* sp */,
short_trespass_pg, sizeof(short_trespass_pg),
@@ -66,6 +70,7 @@ static void do_trespass(int fd, int hr, int short_cmd)
short_cmd ? "short" : "long");
break;
case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_ILLEGAL_REQ:
fprintf(stderr, "%s form trepass page failed, try again %s "
"'-s' option\n", short_cmd ? "short" : "long",
short_cmd ? "without" : "with");
diff --git a/sg_format.8 b/sg_format.8
new file mode 100644
index 00000000..b5f72e8e
--- /dev/null
+++ b/sg_format.8
@@ -0,0 +1,242 @@
+.TH SG_FORMAT "8" "March 2005" "sg3_utils-1.13" 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<scsi_device>\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Not all SCSI direct access devices need to be formatted and some have
+vendor specific formatting procedures. SCSI disks with rotating media are
+probably the largest group that do support a 'standard' format operation.
+They are typically factory formatted to a block size of 512 bytes with the
+largest number of blocks that the manufacturer recommends. That number of
+blocks typically leaves aside a certain number of tracks and sectors for
+reassignment of logical block addresses during the life of the disk.
+.PP
+This utility can format modern SCSI disks and potentially change their
+block size (if permitted) and the block count (i.e. number of accessible
+blocks on the media). Resizing the block count while not changing the
+block size may not require a format operation. Recent changes to the SBC-2
+draft standard (see www.t10.org) have obsoleted the "format device" mode
+page. Many of the low level details found in that mode page are now left
+up to the discretion of the manufacturer.
+.PP
+When this utility is used without options (apart from a device name)
+then it prints out the existing block size and block count derived
+from two sources. These two sources are a block descriptor in the response
+to a MODE SENSE command and the response to a READ CAPACITY command.
+The reason for this double check is to detect a "format corrupt"
+state (see NOTES section).
+.PP
+A recent addition in the SBC-2 is "protection information". See
+the section of that name (section 4.16 in draft SBC-2 rev 16). It adds
+an extra 8 bytes of protection information to each block (a 2 byte "logical
+block guard" (CRC), a 2 byte "logical block application guard", and a
+4 byte "logical block reference tag"). A device that supports
+protection information sets the "protect" bit in a standard INQUIRY
+response. The "FMTPINFO" and "RTO_REQ" bits in the FORMAT command cdb
+are associated with protection information and can be set by this
+utility.
+.TP
+--count=<n> | -c <n>
+count of blocks to be formatted (or media to be resized to). Defaults
+to zero whose interpretation depends on the block size:
+.br
+ - [block size unchanged]: use existing block count
+.br
+ - [block size changed]: recommended maximum block count
+.br
+If '-1' is given then disk manufacturer's recommended (maximum) count
+is used (see NOTES section) for a "--resize". This option only has an
+effect when used with either "--format" or "--resize". When the "--resize"
+option is used, this option is mandatory.
+.TP
+--early | -e
+this option is active when "--format" is given. The default action of this
+utility is to poll the disk every 30 seconds to determine the progress of
+the format operation until it is finished. When this option is given this
+utility will exit "early" as soon as the format has commenced. Then the
+user can monitor the progress of the ongoing format operation with other
+utilities (e.g. sg_turs or sg_requests). This option and "--wait" cannot
+both be given.
+.TP
+--format | -F
+issue a SCSI FORMAT command.
+.B This will destroy all the data held on the media.
+This option is required to change the block size of a disk.
+See NOTES section for implementation details and EXAMPLES
+section for typical use.
+.TP
+--help | -h
+print out the usage information then exit.
+.TP
+--long | -l
+the default action of this utility is to assume 32 bit logical block
+addresses. With 512 byte block size this permits almost 2
+terabytes (almost 2 ** 41 bytes) on a single disk. This option selects
+commands and parameters that allow for 64 bit logical block addresses.
+Specifically this is the "longlba" flag in the MODE SENSE (10) command
+and READ CAPACITY (16) rather than READ CAPACITY (10). When a disk
+supports "protection information" then this option may also be useful.
+.TP
+--pinfo | -p
+instructs a '--format' to add an extra 8 bytes of protection information.
+Default action is not to format with protection information. Has no action
+unless '--format' is given.
+.TP
+--resize | -r
+rather than format the disk, it can be resized. This means changing the
+number of blocks on the device reported by the READ CAPACITY command.
+This option cannot be used together with either "--format" or a "--size"
+whose argument is different to the existing block size.
+.TP
+--rto_req | -R
+instructs a format to enable application client ownership of
+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
+--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
+in a MODE SENSE command. This option is only active when the "--format"
+option is also given. If the block size given by this option is different
+from the current value then a MODE SELECT command is used to change it
+prior to the FORMAT command being started (as recommended in the draft
+standard).
+.TP
+--verbose | -v
+increase the level of verbosity, (i.e. debug output). "-vvv" gives
+the maximum debug output.
+.TP
+--version | -V
+print the version string and then exit.
+.TP
+--wait | -w
+this option only has an effect when used together with the "--format"
+option. The default format action is to set the "IMMED" bit in the FORMAT
+UNIT command's (short) parameter header. If this option (i.e. "--wait") is
+given then the "IMMED" bit is not set. Then the FORMAT UNIT command waits
+until the format operation completes before returning its response. This
+can be several hours on large disks. This utility sets a four hour timeout
+on such a FORMAT UNIT command.
+.SH NOTES
+The format command is still quite complicated and a large set
+of variables in the format command itself and associated parameters
+are set to default values by this utility. These include: LONGLIST,
+CMPLIST and the defect list format (in the command) and all flags within
+the parameter header apart from IMMED (as explained in the "--wait" option).
+.PP
+The SBC-2 draft standard (revision 16) says that the REQUEST SENSE command
+should be used for obtaining a progress indication when the format
+command returns prior to the completion of the format operation.
+However, tests on a selection of recent disks shows that TEST UNIT READY
+commands yield progress indications (but not REQUEST SENSE commands). A
+new option may be required to handle this when disks catch up to the current
+draft.
+.PP
+When the "--format" option is given then there is a 10 second window
+during which the user is invited to abort sg_format. This is just prior
+the FORMAT UNIT SCSI command being issued. If the "--wait" option is not
+given then the FORMAT UNIT SCSI command is issued with the IMMED bit set
+which causes the SCSI command to return after it has started the format
+operation. The "--early" option will cause sg_format to exit at that
+point. Otherwise the given device is polled every 30 seconds with
+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".
+.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
+send any other SCSI commands to the disk as it may not respond leaving
+those commands queued behind the active format command. This may
+cause a timeout in the OS driver (in a lot shorter period than 4 hours
+applicable to the format command). This may result in the OS resetting
+the disk leaving the format operation incomplete. This may leave the
+disk in a "format corrupt" state requiring another format to remedy
+the situation.
+.PP
+When the block size (i.e. the number of bytes in each block) is changed
+on a disk two SCSI commands must be sent: a MODE SELECT to change the block
+size followed by a FORMAT command. If the MODE SELECT command succeeds and
+the FORMAT fails then the disk may be in a state that the draft standard
+calls "format corrupt". A block descriptor in a subsequent MODE SENSE
+will report the requested new block size while a READ CAPACITY command
+will report the existing (i.e. different) block size. Alternatively
+the READ CAPACITY command may fail, reporting the device is not ready,
+potentially requiring a format. The solution to this situation is to
+do a format again (and this time the new block size does not have to
+be given) or change the block size back to the original size.
+.PP
+The draft SBC-2 standard states that the block count can be set back
+to the manufacturer's maximum recommended value in a format or resize
+operation. This can be done by placing an address of 0xffffffff (or the
+64 bit equivalent) in the appropriate block descriptor field to a MODE
+SELECT command. In signed (two's complement) arithmetic that value
+corresponds to '-1'. So a "--count" argument of '-1' causes the block count
+to be set back to the manufacturer's maximum recommended value. To see
+exactly which SCSI commands are being executed and parameters passed
+add "-vvv" to the sg_format command line.
+.PP
+The argument to "--count" is a number which 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". Also a suffix of the form "x<n>" multiplies the
+leading number by <n>. Alternatively numerical values can be given in
+hexadecimal preceded by either "0x" or "0X". When hex numbers are given,
+multipliers cannot be used.
+.SH EXAMPLES
+First, do nothing, but print out the existing block count and size
+derived from two sources: a block descriptor in a MODE SELECT command
+response and from the response of a READ CAPACITY commands:
+.PP
+ sg_format /dev/sdm
+.PP
+Now a simple format, leaving the block count and size as they
+were previously, executing the format command in IMMED mode and
+polling every 30 seconds to print out a progress indication:
+.PP
+ sg_format --format /dev/sdm
+.PP
+Now the same format, but waiting (passively) until the format
+operation is complete:
+.PP
+ sg_format --format --wait /dev/sdm
+.PP
+Next is a format in which the block size is changed to 520 bytes
+and the block count is set to the manufacturer's maximum
+value (for that block size). Note, not all disks support changing
+the block size:
+.PP
+ sg_format --format --size=520 /dev/sdm
+.PP
+Now a resize operation to hide all but the first 0x1000 (4096)
+blocks on a disk:
+.PP
+ sg_format --resize --count=0x1000 /dev/sdm
+.PP
+Now resize the disk back to its normal (maximum) block count:
+.PP
+ sg_format --resize --count=-1 /dev/sdm
+.PP
+.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
+.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_turs, sg_requests, sg_inq, sg_modes, sginfo, sg_wr_mode
+(all in sg3_utils)
diff --git a/sg_format.c b/sg_format.c
new file mode 100644
index 00000000..ae22cd54
--- /dev/null
+++ b/sg_format.c
@@ -0,0 +1,744 @@
+/*
+** sg_format : format a SCSI disk (potentially with a different block size)
+**
+** formerly called blk512-linux.c (v0.4)
+**
+** Copyright (C) 2003 Grant Grundler grundler at parisc-linux dot org
+** Copyright (C) 2003 James Bottomley jejb at parisc-linux dot org
+** Copyright (C) 2005 Douglas Gilbert dgilbert at interlog dot com
+**
+** 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.
+**
+** http://www.t10.org/scsi-3.htm
+** http://www.tldp.org/HOWTO/SCSI-Generic-HOWTO
+**
+**
+** List of some (older) disk manufacturers' block counts.
+** These are not needed in newer disks which will automatically use
+** the manufacturers' recommended block count if a count of -1 is given.
+** Inquiry Block Count (@512 byte blocks)
+** ST150150N 8388315
+** IBM_DCHS04F 8888543
+** IBM_DGHS09Y 17916240
+** ST336704FC 71132960
+** ST318304FC 35145034 (Factory spec is 35885167 sectors)
+** ST336605FC ???
+** ST336753FC 71132960 (Factory spec is 71687372 sectors)
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_ioctl.h>
+#include <scsi/sg.h>
+#include <sys/errno.h>
+
+#include "sg_lib.h"
+#include "sg_cmds.h"
+
+#define RW_ERROR_RECOVERY_PAGE 1 /* every disk should have one */
+#define FORMAT_DEV_PAGE 3 /* Format Device Mode Page [now obsolete] */
+#define CONTROL_MODE_PAGE 0xa /* alternative page all devices have?? */
+
+#define CDB_SIZE 6 /* SCSI Command Block */
+#define MODE_HDR_SIZE 4 /* Mode Sense Header */
+#define BLOCK_DESCR_SIZE 8 /* Block Descriptor Header */
+
+#define LOGICAL_UNIT_NOT_READY 4 /* ASC */
+#define FORMAT_IN_PROGRESS 4 /* ASCQ */
+
+#define SHORT_TIMEOUT 20000 /* 20 seconds unless immed=0 ... */
+#define FORMAT_TIMEOUT (4 * 3600 * 1000) /* 4 hours ! */
+
+#define POLL_DURATION_SECS 30
+
+
+#define MAX_SENSE_SZ 32
+static unsigned char sbuff[MAX_SENSE_SZ];
+
+#define MAX_BUFF_SZ 252
+static unsigned char dbuff[MAX_BUFF_SZ];
+
+static char * version_str = "1.03 20050313";
+
+static struct option long_options[] = {
+ {"count", 1, 0, 'c'},
+ {"early", 0, 0, 'e'},
+ {"format", 0, 0, 'F'},
+ {"help", 0, 0, 'h'},
+ {"long", 0, 0, 'l'},
+ {"pinfo", 0, 0, 'p'},
+ {"resize", 0, 0, 'r'},
+ {"rto_req", 0, 0, 'R'},
+ {"size", 1, 0, 's'},
+ {"verbose", 0, 0, 'v'},
+ {"version", 0, 0, 'V'},
+ {"wait", 0, 0, 'w'},
+ {0, 0, 0, 0},
+};
+
+static const char * scsi_ptype_strs[] = {
+ "disk", /* 0x0 */
+ "tape",
+ "printer",
+ "processor",
+ "write once optical disk",
+ "cd/dvd",
+ "scanner",
+ "optical memory device",
+ "medium changer", /* 0x8 */
+ "communications",
+ "graphics [0xa]",
+ "graphics [0xb]",
+ "storage array controller",
+ "enclosure services device",
+ "simplified direct access device",
+ "optical card reader/writer device",
+ "bridge controller commands", /* 0x10 */
+ "object storage device",
+ "automation/drive interface",
+ "0x13", "0x14", "0x15", "0x16", "0x17", "0x18",
+ "0x19", "0x1a", "0x1b", "0x1c", "0x1d",
+ "well known logical unit",
+ "no physical device on this lu",
+};
+
+static const char * get_ptype_str(int scsi_ptype)
+{
+ int num = sizeof(scsi_ptype_strs) / sizeof(scsi_ptype_strs[0]);
+
+ return (scsi_ptype < num) ? scsi_ptype_strs[scsi_ptype] : "";
+}
+
+
+/* Return 0 on success, else -1 */
+static int
+scsi_format(int fd, int pinfo, int rto_req, int immed, int early, int verbose)
+{
+ int k, res;
+ const char FORMAT_HEADER_SIZE = 4;
+ unsigned char cdb[CDB_SIZE], fmt_hdr[FORMAT_HEADER_SIZE];
+ sg_io_hdr_t io_hdr;
+
+ cdb[0] = FORMAT_UNIT;
+ cdb[1] = (pinfo ? 0x80 : 0) | (rto_req ? 0x40 : 0) |
+ (immed ? 0x10 : 0);
+ cdb[2] = 0; /* vendor specific */
+ cdb[3] = 0; /* interleave MSB */
+ cdb[4] = 0; /* interleave LSB */
+ cdb[5] = 0; /* control */
+
+ /* fmt_hdr is a short format header, only used when 'immed' is set */
+ fmt_hdr[0] = 0; /* reserved */
+ fmt_hdr[1] = 0x02; /* use device defaults, IMMED return */
+ fmt_hdr[2] = 0; /* defect list length MSB */
+ fmt_hdr[3] = 0; /* defect list length LSB */
+
+ memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+ memset(sbuff, 0, MAX_SENSE_SZ);
+ io_hdr.interface_id = 'S';
+ io_hdr.dxfer_direction = immed ? SG_DXFER_TO_DEV : SG_DXFER_NONE;
+ io_hdr.cmd_len = CDB_SIZE;
+ io_hdr.mx_sb_len = MAX_SENSE_SZ;
+ io_hdr.iovec_count = 0; /* no scatter gather */
+ if (immed) {
+ io_hdr.dxfer_len = FORMAT_HEADER_SIZE;
+ io_hdr.dxferp = fmt_hdr;
+ }
+ io_hdr.cmdp = cdb;
+ io_hdr.sbp = sbuff;
+ io_hdr.timeout = immed ? SHORT_TIMEOUT : FORMAT_TIMEOUT;
+
+ if (verbose) {
+ fprintf(stderr, " format cdb: ");
+ for (k = 0; k < 6; ++k)
+ fprintf(stderr, "%02x ", cdb[k]);
+ fprintf(stderr, "\n");
+ }
+ if ((verbose > 1) && immed) {
+ fprintf(stderr, " format parameter block\n");
+ dStrHex((const char *)fmt_hdr, FORMAT_HEADER_SIZE, -1);
+ }
+
+ if (ioctl(fd, SG_IO, &io_hdr) < 0) {
+ perror("FORMAT UNIT ioctl error");
+ return -1;
+ }
+ res = sg_err_category3(&io_hdr);
+ switch (res) {
+ case SG_LIB_CAT_RECOVERED:
+ sg_chk_n_print3("Format, continuing", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
+ break;
+ case SG_LIB_CAT_INVALID_OP:
+ fprintf(stderr, "Format command not supported\n");
+ if (verbose > 1)
+ sg_chk_n_print3("Format", &io_hdr);
+ return -1;
+ case SG_LIB_CAT_ILLEGAL_REQ:
+ fprintf(stderr, "Format command illegal parameter\n");
+ if (verbose > 1)
+ sg_chk_n_print3("Format", &io_hdr);
+ return -1;
+ default:
+ if (verbose > 1)
+ sg_chk_n_print3("Format", &io_hdr);
+ return -1;
+ }
+ if (! immed)
+ return 0;
+
+ printf("\nFormat has started\n");
+ if (early) {
+ if (immed)
+ printf("Format continuing, use request sense or "
+ "test unit ready to monitor progress\n");
+ return 0;
+ }
+
+ for(;;) {
+ int progress;
+ struct sg_scsi_sense_hdr sshdr;
+
+ sleep(POLL_DURATION_SECS);
+ cdb[0] = TEST_UNIT_READY; /* draft say REQUEST SENSE */
+ cdb[1] = 0;
+ cdb[2] = 0;
+ cdb[3] = 0;
+ cdb[4] = 0;
+ cdb[5] = 0;
+
+ memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+ memset(sbuff, 0, MAX_SENSE_SZ);
+
+ io_hdr.interface_id = 'S';
+ io_hdr.dxfer_direction = SG_DXFER_NONE;
+ io_hdr.cmd_len = CDB_SIZE;
+ io_hdr.mx_sb_len = MAX_SENSE_SZ;
+ io_hdr.iovec_count = 0; /* no scatter gather */
+ io_hdr.dxfer_len = 0;
+ io_hdr.dxferp = NULL;
+ io_hdr.cmdp = cdb;
+ io_hdr.sbp = sbuff;
+ io_hdr.timeout = SHORT_TIMEOUT;
+
+ if (verbose) {
+ fprintf(stderr, " test unit ready cdb: ");
+ for (k = 0; k < 6; ++k)
+ fprintf(stderr, "%02x ", cdb[k]);
+ fprintf(stderr, "\n");
+ }
+
+ if (ioctl(fd, SG_IO, &io_hdr) < 0) {
+ perror("Test Unit Ready SG_IO ioctl error");
+ return -1;
+ }
+ if (sg_normalize_sense(&io_hdr, &sshdr)) {
+ if (sg_get_sense_progress_fld(sbuff,
+ io_hdr.sb_len_wr, &progress)) {
+ printf("Format in progress, %d%% done\n",
+ progress * 100 / 65536);
+ if (verbose > 1)
+ sg_print_sense("tur", sbuff,
+ io_hdr.sb_len_wr);
+ continue;
+ } else {
+ sg_print_sense("tur: unexpected sense", sbuff,
+ io_hdr.sb_len_wr);
+ continue;
+ }
+ } else
+ break;
+ }
+ printf("FORMAT Complete\n");
+ return 0;
+}
+
+#define RCAP_REPLY_LEN 32
+
+static int
+print_read_cap(int fd, int do_16, int verbose)
+{
+ int res, k;
+ unsigned char resp_buff[RCAP_REPLY_LEN];
+ unsigned int last_blk_addr, block_size;
+ unsigned long long llast_blk_addr;
+
+ if (do_16) {
+ res = sg_ll_readcap_16(fd, 0 /* pmi */, 0 /* llba */,
+ resp_buff, 32, verbose);
+ if (0 == res) {
+ for (k = 0, llast_blk_addr = 0; k < 8; ++k) {
+ llast_blk_addr <<= 8;
+ llast_blk_addr |= resp_buff[k];
+ }
+ block_size = ((resp_buff[8] << 24) |
+ (resp_buff[9] << 16) |
+ (resp_buff[10] << 8) |
+ resp_buff[11]);
+ printf("Read Capacity (16) results:\n");
+ printf(" Protection: prot_en=%d, rto_en=%d\n",
+ !!(resp_buff[12] & 0x1),
+ !!(resp_buff[12] & 0x2));
+ printf(" Number of blocks=%llu\n",
+ llast_blk_addr + 1);
+ printf(" Block size=%u bytes\n", block_size);
+ return (int)block_size;
+ }
+ } else {
+ res = sg_ll_readcap_10(fd, 0 /* pmi */, 0 /* lba */,
+ resp_buff, 8, verbose);
+ if (0 == res) {
+ last_blk_addr = ((resp_buff[0] << 24) |
+ (resp_buff[1] << 16) |
+ (resp_buff[2] << 8) |
+ resp_buff[3]);
+ block_size = ((resp_buff[4] << 24) |
+ (resp_buff[5] << 16) |
+ (resp_buff[6] << 8) |
+ resp_buff[7]);
+ printf("Read Capacity (10) results:\n");
+ printf(" Number of blocks=%u\n",
+ last_blk_addr + 1);
+ printf(" Block size=%u bytes\n", block_size);
+ return (int)block_size;
+ }
+ }
+ 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)
+ fprintf(stderr, "bad field in READ CAPACITY (%d) "
+ "cdb\n", (do_16 ? 16 : 10));
+ if (verbose)
+ fprintf(stderr, "READ CAPACITY (%d) failed "
+ "[res=%d]\n", (do_16 ? 16 : 10), res);
+ return -1;
+}
+
+static void usage()
+{
+ printf("usage: sg_format [--count=<block count>] [--early] [--format]"
+ " [--help]\n"
+ " [--long] [--pinfo] [--resize] [--rto_req]\n"
+ " [--size=<block size>] [--verbose]"
+ " [--version] [--wait]\n"
+ " <scsi_disk>\n"
+ " where:\n"
+ " --count=<block count> | -c <block count>\n"
+ " best left alone (defaults to "
+ "max allowable)\n"
+ " --early | -e exit once format started (user can "
+ "monitor progress)\n"
+ " --format | -F format unit (default report current count"
+ " and size)\n"
+ " --help | -h prints out this usage message\n"
+ " --long | -l allow for 64 bit lbas (default: assume "
+ "32 bit lbas)\n"
+ " --pinfo | -p set the FMTPINFO bit to format with "
+ "protection\n");
+ printf( " information (defaults to no protection "
+ "information)\n"
+ " --resize | -r resize (rather than format) to '--count' "
+ "value\n"
+ " --rto_req | -R set the RTO_REQ bit in format (only valid "
+ "with '--pinfo')\n"
+ " --size=<block size> | -s <block size>\n"
+ " only needed to change block size"
+ " (default to\n"
+ " current device's block size)\n"
+ " --verbose | -v verbosity (show commands + parameters "
+ "sent)\n"
+ " use multiple time for more verbosity\n"
+ " --version | -V print version details and exit\n"
+ " --wait | -w format command waits till complete (def: "
+ "poll)\n"
+ "\t(e.g. sg_format -s 512 /dev/sdc)\n");
+ printf("\nWARNING: This program will destroy all the data on the "
+ "target device when\n\t '--format' is given. Check that you "
+ "have the correct device.\n");
+}
+
+
+int main(int argc, char **argv)
+{
+ const int mode_page = RW_ERROR_RECOVERY_PAGE;
+ int fd, res, calc_len, bd_len, dev_specific_param;
+ int offset, j, bd_blk_len, prob, len;
+ unsigned long long ull;
+ long long blk_count = 0; /* -c value */
+ int blk_size = 0; /* -s value */
+ int format = 0; /* -F */
+ int resize = 0; /* -r */
+ int verbose = 0; /* -v */
+ int fwait = 0; /* -w */
+ int mode6 = 0;
+ int pinfo = 0;
+ int rto_req = 0;
+ int do_rcap16 = 0;
+ int long_lba = 0;
+ int early = 0;
+ char device_name[256];
+ struct sg_simple_inquiry_resp inq_out;
+ int ret = 1;
+
+ device_name[0] = '\0';
+ while (1) {
+ int option_index = 0;
+ char c;
+
+ c = getopt_long(argc, argv, "c:eFhlprRs:vVw",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'c':
+ if (0 == strcmp("-1", optarg))
+ blk_count = -1;
+ else {
+ blk_count = sg_get_llnum(optarg);
+ if (-1 == blk_count) {
+ fprintf(stderr, "bad argument to "
+ "'--count'\n");
+ return 1;
+ }
+ }
+ break;
+ case 'e':
+ early = 1;
+ break;
+ case 'F':
+ format = 1;
+ break;
+ case 'h':
+ usage();
+ return 0;
+ case 'l':
+ long_lba = 1;
+ do_rcap16 = 1;
+ break;
+ case 'p':
+ pinfo = 1;
+ break;
+ case 'r':
+ resize = 1;
+ break;
+ case 'R':
+ rto_req = 1;
+ break;
+ case 's':
+ blk_size = sg_get_num(optarg);
+ if (blk_size <= 0) {
+ fprintf(stderr, "bad argument to '--size', "
+ "want arg > 0)\n");
+ return 1;
+ }
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'V':
+ fprintf(stderr, "sg_format version: %s\n",
+ version_str);
+ return 0;
+ case 'w':
+ fwait = 1;
+ break;
+ default:
+ usage();
+ return 1;
+ }
+ }
+ if (optind < argc) {
+ if ('\0' == device_name[0]) {
+ strncpy(device_name, argv[optind],
+ sizeof(device_name) - 1);
+ device_name[sizeof(device_name) - 1] = '\0';
+ ++optind;
+ }
+ }
+ if (optind < argc) {
+ for (; optind < argc; ++optind)
+ fprintf(stderr, "Unexpected extra argument: %s\n",
+ argv[optind]);
+ usage();
+ return 1;
+ }
+ if ('\0' == device_name[0]) {
+ fprintf(stderr, "no device name given\n");
+ usage();
+ return 1;
+ }
+ if (resize) {
+ if (format) {
+ fprintf(stderr, "both '--format' and '--resize'"
+ "not permitted\n");
+ usage();
+ return 1;
+ } else if (0 == blk_count) {
+ fprintf(stderr, "'--resize' needs a '--count' (other"
+ " than 0)\n");
+ usage();
+ return 1;
+ } else if (0 != blk_size) {
+ fprintf(stderr, "'--resize' not compatible with "
+ "'--size')\n");
+ usage();
+ return 1;
+ }
+ }
+
+ /* FIXME: add more sanity checks:
+ ** o block size/count might already be set...don't repeat
+ ** o verify SCSI device is a disk (get inquiry data first)
+ */
+
+ if ((fd = open(device_name, O_RDWR)) < 0) {
+ char ebuff[128];
+ sprintf(ebuff, "error opening device file: %s", device_name);
+ perror(ebuff);
+ return 1;
+ }
+
+ if (sg_simple_inquiry(fd, &inq_out, 1, verbose)) {
+ fprintf(stderr, "%s doesn't respond to a SCSI INQUIRY\n",
+ device_name);
+ goto out;
+ }
+ printf(" %.8s %.16s %.4s peripheral_type: %s [0x%x]\n",
+ inq_out.vendor, inq_out.product, inq_out.revision,
+ get_ptype_str(inq_out.peripheral_type),
+ inq_out.peripheral_type);
+ if (verbose)
+ printf(" PROTECT=%d\n", !!(inq_out.byte_5 & 1));
+ if (inq_out.byte_5 & 1)
+ printf(" << supports 'protection information'>>\n");
+
+ if ((0 != inq_out.peripheral_type) &&
+ (0xe != inq_out.peripheral_type)) {
+ fprintf(stderr, "This format is only defined for disks "
+ "(using SBC-2 or RBC)\n");
+ goto out;
+ }
+
+ memset(dbuff, 0, MAX_BUFF_SZ);
+ if (mode6)
+ res = sg_ll_mode_sense6(fd, 0 /* DBD */, 0 /* current */,
+ mode_page, 0 /* subpage */, dbuff,
+ MAX_BUFF_SZ, 1, verbose);
+ else
+ res = sg_ll_mode_sense10(fd, long_lba, 0 /* DBD */,
+ 0 /* current */, mode_page,
+ 0 /* subpage */, dbuff,
+ MAX_BUFF_SZ, 1, verbose);
+ if (res) {
+ 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) {
+ if (long_lba && (! mode6))
+ fprintf(stderr, "bad field in MODE SENSE "
+ "(%d) [longlba flag not supported?]"
+ "\n", (mode6 ? 6 : 10));
+ else
+ fprintf(stderr, "bad field in MODE SENSE "
+ "(%d) [mode_page %d not supported?]"
+ "\n", (mode6 ? 6 : 10), mode_page);
+ } else
+ fprintf(stderr, "MODE SENSE (%d) command failed\n",
+ (mode6 ? 6 : 10));
+ goto out;
+ }
+ if (mode6) {
+ calc_len = dbuff[0] + 1;
+ dev_specific_param = dbuff[2];
+ bd_len = dbuff[3];
+ long_lba = 0;
+ offset = 4;
+ /* prepare for mode select */
+ dbuff[0] = 0;
+ dbuff[1] = 0;
+ dbuff[2] = 0;
+ } else {
+ calc_len = (dbuff[0] << 8) + dbuff[1] + 2;
+ dev_specific_param = dbuff[3];
+ bd_len = (dbuff[6] << 8) + dbuff[7];
+ long_lba = (dbuff[4] & 1);
+ offset = 8;
+ /* prepare for mode select */
+ dbuff[0] = 0;
+ dbuff[1] = 0;
+ dbuff[2] = 0;
+ dbuff[3] = 0;
+ }
+ if ((offset + bd_len) < calc_len)
+ dbuff[offset + bd_len] &= 0x7f; /* clear PS bit in mpage */
+ prob = 0;
+ bd_blk_len = 0;
+ printf("Mode sense (block descriptor) data, prior to changes:\n");
+ if (dev_specific_param & 0x40)
+ printf(" <<< Write Protect (WP) bit set >>>\n");
+ if (bd_len > 0) {
+ ull = 0;
+ for (j = 0; j < (long_lba ? 8 : 4); ++j) {
+ if (j > 0)
+ ull <<= 8;
+ ull |= dbuff[offset + j];
+ }
+ if (long_lba)
+ bd_blk_len = (dbuff[offset + 12] << 24) +
+ (dbuff[offset + 13] << 16) +
+ (dbuff[offset + 14] << 8) +
+ dbuff[offset + 15];
+ else
+ bd_blk_len = (dbuff[offset + 5] << 16) +
+ (dbuff[offset + 6] << 8) +
+ dbuff[offset + 7];
+ if (long_lba) {
+ printf(" <<< longlba flag set (64 bit lba) >>>\n");
+ if (bd_len != 16)
+ prob = 1;
+ } else if (bd_len != 8)
+ prob = 1;
+ printf(" Number of blocks=%llu [0x%llx]\n", ull, ull);
+ printf(" Block size=%d [0x%x]\n", bd_blk_len, bd_blk_len);
+ } else {
+ printf(" No block descriptors present\n");
+ prob = 1;
+ }
+ if (resize ||
+ (format && ((blk_count != 0) ||
+ ((blk_size > 0) && (blk_size != bd_blk_len))))) {
+ /* want to run MODE SELECT */
+
+/* Working Draft SCSI Primary Commands - 3 (SPC-3) pg 255
+**
+** If the SCSI device doesn't support changing its capacity by changing
+** the NUMBER OF BLOCKS field using the MODE SELECT command, the value
+** in the NUMBER OF BLOCKS field is ignored. If the device supports changing
+** its capacity by changing the NUMBER OF BLOCKS field, then the
+** NUMBER OF BLOCKS field is interpreted as follows:
+** a) If the number of blocks is set to zero, the device shall retain
+** its current capacity if the block size has not changed. If the
+** number of blocks is set to zero and the block size has changed,
+** the device shall be set to its maximum capacity when the new
+** block size takes effect;
+**
+** b) If the number of blocks is greater than zero and less than or
+** equal to its maximum capacity, the device shall be set to that
+** number of blocks. If the block size has not changed, the device
+** shall not become format corrupted. This capacity setting shall be
+** retained through power cycles, hard resets, logical unit resets,
+** and I_T nexus losses;
+**
+** c) If the number of blocks field is set to a value greater than the
+** maximum capacity of the device and less than FFFF FFFFh, then the
+** command is terminated with a CHECK CONDITION status. The sense key
+** is set to ILLEGAL REQUEST. The device shall retain its previous
+** block descriptor settings; or
+**
+** d) If the number of blocks is set to FFFF FFFFh, the device shall be
+** set to its maximum capacity. If the block size has not changed,
+** the device shall not become format corrupted. This capacity setting
+** shall be retained through power cycles, hard resets, logical unit
+** resets, and I_T nexus losses.
+*/
+
+ if (prob) {
+ fprintf(stderr, "Need to perform MODE SELECT (to "
+ "change number or blocks or block length)\n");
+ fprintf(stderr, "but (single) block descriptor not "
+ "found in earlier MODE SENSE\n");
+ goto out;
+ }
+ if (blk_count != 0) {
+ len = (long_lba ? 8 : 4);
+ for (j = 0; j < len; ++j)
+ dbuff[offset + j] =
+ (blk_count >> ((len - j - 1) * 8)) & 0xff;
+ } else if ((blk_size > 0) && (blk_size != bd_blk_len)) {
+ len = (long_lba ? 8 : 4);
+ for (j = 0; j < len; ++j)
+ dbuff[offset + j] = 0;
+ }
+ if ((blk_size > 0) && (blk_size != bd_blk_len)) {
+ if (long_lba) {
+ dbuff[offset + 12] = (blk_size >> 24) & 0xff;
+ dbuff[offset + 13] = (blk_size >> 16) & 0xff;
+ dbuff[offset + 14] = (blk_size >> 8) & 0xff;
+ dbuff[offset + 15] = blk_size & 0xff;
+ } else {
+ dbuff[offset + 5] = (blk_size >> 16) & 0xff;
+ dbuff[offset + 6] = (blk_size >> 8) & 0xff;
+ dbuff[offset + 7] = blk_size & 0xff;
+ }
+ }
+ if (mode6)
+ res = sg_ll_mode_select6(fd, 1 /* PF */, 1 /* SP */,
+ dbuff, calc_len, 1, verbose);
+ else
+ res = sg_ll_mode_select10(fd, 1 /* PF */, 1 /* SP */,
+ dbuff, calc_len, 1, verbose);
+ if (res) {
+ 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)
+ fprintf(stderr, "bad field in MODE SELECT "
+ "(%d)\n", (mode6 ? 6 : 10));
+ else
+ fprintf(stderr, "MODE SELECT (%d) command "
+ "failed\n", (mode6 ? 6 : 10));
+ 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) && (bd_blk_len > 0) &&
+ (res != (int)bd_blk_len)) {
+ printf(" Warning: mode sense and read capacity "
+ "report different block sizes [%d,%d]\n",
+ bd_blk_len, res);
+ printf(" Probably needs format\n");
+ }
+ printf("No changes made. To format use '--format'. To "
+ "resize use '--resize'\n");
+ ret = 0;
+ goto out;
+ }
+
+ if(format)
+#if 1
+ printf("\nA FORMAT will commence in 10 seconds\n");
+ printf(" ALL data on %s will be DESTROYED\n", device_name);
+ printf(" Press control-C to abort\n");
+ sleep(5);
+ printf("A FORMAT will commence in 5 seconds\n");
+ printf(" ALL data on %s will be DESTROYED\n", device_name);
+ printf(" Press control-C to abort\n");
+ sleep(5);
+ scsi_format(fd, pinfo, rto_req, ! fwait, early, verbose);
+#else
+ fprintf(stderr, "FORMAT ignored, testing\n");
+#endif
+ ret = 0;
+
+out:
+ close(fd);
+ return ret;
+}
diff --git a/sg_get_config.8 b/sg_get_config.8
index fd6e99cc..65bac085 100644
--- a/sg_get_config.8
+++ b/sg_get_config.8
@@ -1,4 +1,4 @@
-.TH SG_GET_CONFIG "8" "November 2004" "sg3_utils-1.11" SG3_UTILS
+.TH SG_GET_CONFIG "8" "February 2005" "sg3_utils-1.13" SG3_UTILS
.SH NAME
sg_get_config \- invoke SCSI GET CONFIGURATION command on a (cd/dvd) device
.SH SYNOPSIS
@@ -103,7 +103,7 @@ 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-2005 Douglas Gilbert
.br
This software is distributed under a FreeBSD license. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/sg_get_config.c b/sg_get_config.c
index 51d101e5..227be488 100644
--- a/sg_get_config.c
+++ b/sg_get_config.c
@@ -49,7 +49,7 @@
*/
-static char * version_str = "0.13 20050110";
+static char * version_str = "0.15 20050309";
#define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */
@@ -83,7 +83,8 @@ static struct option long_options[] = {
};
/* Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not
- supported, else -1 */
+ supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
+ else -1 */
static int sg_ll_get_config(int sg_fd, int rt, int starting, void * resp,
int mx_resp_len, int noisy, int verbose)
{
@@ -134,15 +135,18 @@ static int sg_ll_get_config(int sg_fd, int rt, int starting, void * resp,
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ sg_chk_n_print3("Get config, continuing", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
if (verbose && io_hdr.resid)
fprintf(stderr, " get config: resid=%d\n", io_hdr.resid);
return 0;
case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_ILLEGAL_REQ:
if (verbose > 1)
sg_chk_n_print3("get config error", &io_hdr);
- return SG_LIB_CAT_INVALID_OP;
+ return res;
default:
if (verbose | noisy) {
snprintf(ebuff, EBUFF_SZ, "get config error, rt=%d, "
@@ -203,18 +207,15 @@ static const char * scsi_ptype_strs[] = {
"automation/driver interface",
"0x13", "0x14", "0x15", "0x16", "0x17", "0x18",
"0x19", "0x1a", "0x1b", "0x1c", "0x1d",
+ "well known logical unit",
+ "no physical device on this lu",
};
static const char * get_ptype_str(int scsi_ptype)
{
int num = sizeof(scsi_ptype_strs) / sizeof(scsi_ptype_strs[0]);
- if (0x1f == scsi_ptype)
- return "no physical device on this lu";
- else if (0x1e == scsi_ptype)
- return "well known logical unit";
- else
- return (scsi_ptype < num) ? scsi_ptype_strs[scsi_ptype] : "";
+ return (scsi_ptype < num) ? scsi_ptype_strs[scsi_ptype] : "";
}
struct code_desc {
@@ -247,7 +248,7 @@ static struct code_desc profile_desc_arr[] = {
{0x2b, "DVD+R double layer"},
{0x40, "BD-ROM"},
{0x41, "BD-R sequential recording"},
- {0x42, "BD-R random recording"},
+ {0x42, "BD-R random recording (RRM)"},
{0x43, "BD-RE"},
{0xffff, "Non-conforming profile"},
};
@@ -298,8 +299,10 @@ static struct code_desc feature_desc_arr[] = {
{0x32, "Double density CD-RW write"},
{0x33, "Layer jump recording"},
{0x37, "CD-RW media write support"},
+ {0x38, "BD-R Pseudo-overwrite (POW)"},
{0x3b, "DVD+R double layer"},
{0x40, "BD read"},
+ {0x41, "BD write"},
{0x100, "Power management"},
{0x101, "SMART"},
{0x102, "Embedded changer"},
@@ -426,7 +429,7 @@ static void decode_feature(int feature, unsigned char * ucp, int len)
printf(" additional length [%d] too short\n", len - 4);
break;
}
- printf(" WDCB=%d, SPWP=%d, SSWPP=%d\n", !!(ucp[4] & 4),
+ printf(" WDCB=%d, SPWP=%d, SSWPP=%d\n", !!(ucp[4] & 0x4),
!!(ucp[4] & 0x2), !!(ucp[4] & 0x1));
break;
case 0x10: /* Random readable */
@@ -446,9 +449,11 @@ static void decode_feature(int feature, unsigned char * ucp, int len)
case 0x22: /* Sector erasable */
case 0x26: /* Restricted overwrite */
case 0x27: /* CDRW CAV write */
- case 0x40: /* BD read */
+ case 0x38: /* BD-R pseudo-overwrite (POW) */
case 0x100: /* Power management */
+ case 0x104: /* Firmware upgrade */
case 0x109: /* Media serial number */
+ case 0x110: /* VCPS */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
feature);
@@ -498,9 +503,11 @@ static void decode_feature(int feature, unsigned char * ucp, int len)
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
feature);
if (len > 4)
- printf(" RENoSA=%d, Expand=%d, QCert=%d, Cert=%d\n",
+ printf(" BD-RE: RENoSA=%d, Expand=%d, QCert=%d, Cert=%d\n",
!!(ucp[4] & 0x8), !!(ucp[4] & 0x4), !!(ucp[4] & 0x2),
!!(ucp[4] & 0x1));
+ if (len > 8)
+ printf(" BD-R: RRM=%d\n", !!(ucp[8] & 0x1));
break;
case 0x24: /* Hardware defect management */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
@@ -587,7 +594,7 @@ static void decode_feature(int feature, unsigned char * ucp, int len)
printf(" CD-RW=%d, R-W sub-code=%d\n",
!!(ucp[4] & 0x2), !!(ucp[4] & 0x1));
break;
- case 0x2e: /* CD mastering (seesion at once) */
+ case 0x2e: /* CD mastering (session at once) */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
feature);
@@ -623,7 +630,7 @@ static void decode_feature(int feature, unsigned char * ucp, int len)
printf(" additional length [%d] too short\n", len - 4);
break;
}
- printf(" CD-RW media sub-type support=0x%x\n", ucp[5]);
+ printf(" CD-RW media sub-type support (bitmask)=0x%x\n", ucp[5]);
break;
case 0x3b: /* DVD+R double layer */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
@@ -635,6 +642,60 @@ static void decode_feature(int feature, unsigned char * ucp, int len)
}
printf(" Write=%d\n", !!(ucp[4] & 0x1));
break;
+ case 0x40: /* BD Read */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+ feature);
+ if (len < 32) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" Bitmaps for BD-RE read support:\n");
+ printf(" Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
+ "Class 3=0x%x\n", (ucp[8] << 8) + ucp[9],
+ (ucp[10] << 8) + ucp[11],
+ (ucp[12] << 8) + ucp[13],
+ (ucp[14] << 8) + ucp[15]);
+ printf(" Bitmaps for BD-R read support:\n");
+ printf(" Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
+ "Class 3=0x%x\n", (ucp[16] << 8) + ucp[17],
+ (ucp[18] << 8) + ucp[19],
+ (ucp[20] << 8) + ucp[21],
+ (ucp[22] << 8) + ucp[23]);
+ printf(" Bitmaps for BD-ROM read support:\n");
+ printf(" Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
+ "Class 3=0x%x\n", (ucp[24] << 8) + ucp[25],
+ (ucp[26] << 8) + ucp[27],
+ (ucp[28] << 8) + ucp[29],
+ (ucp[30] << 8) + ucp[31]);
+ break;
+ case 0x41: /* BD Write */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+ feature);
+ if (len < 32) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" Bitmaps for BD-RE write support:\n");
+ printf(" Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
+ "Class 3=0x%x\n", (ucp[8] << 8) + ucp[9],
+ (ucp[10] << 8) + ucp[11],
+ (ucp[12] << 8) + ucp[13],
+ (ucp[14] << 8) + ucp[15]);
+ printf(" Bitmaps for BD-R write support:\n");
+ printf(" Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
+ "Class 3=0x%x\n", (ucp[16] << 8) + ucp[17],
+ (ucp[18] << 8) + ucp[19],
+ (ucp[20] << 8) + ucp[21],
+ (ucp[22] << 8) + ucp[23]);
+ printf(" Bitmaps for BD-ROM write support:\n");
+ printf(" Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
+ "Class 3=0x%x\n", (ucp[24] << 8) + ucp[25],
+ (ucp[26] << 8) + ucp[27],
+ (ucp[28] << 8) + ucp[29],
+ (ucp[30] << 8) + ucp[31]);
+ break;
case 0x101: /* SMART */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
@@ -668,11 +729,6 @@ static void decode_feature(int feature, unsigned char * ucp, int len)
!!(ucp[4] & 0x4), !!(ucp[4] & 0x2), !!(ucp[4] & 0x1),
(ucp[6] << 8) + ucp[7]);
break;
- case 0x104: /* Firmware upgrade */
- printf(" version=%d, persist=%d, current=%d [0x%x]\n",
- ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
- feature);
- break;
case 0x105: /* Timeout */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
@@ -950,6 +1006,8 @@ int main(int argc, char * argv[])
inner_hex);
} else if (SG_LIB_CAT_INVALID_OP == res)
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
fprintf(stderr, "Get Configuration command failed\n");
diff --git a/sg_inq.8 b/sg_inq.8
index 3d0c1328..e6875ec5 100644
--- a/sg_inq.8
+++ b/sg_inq.8
@@ -1,4 +1,4 @@
-.TH SG_INQ "8" "November 2004" "sg3_utils-1.11" SG3_UTILS
+.TH SG_INQ "8" "February 2005" "sg3_utils-1.13" SG3_UTILS
.SH NAME
sg_inq \- outputs data retrieved from the SCSI INQUIRY command
.SH SYNOPSIS
@@ -26,9 +26,9 @@ ioctl is unknown) then an ATA IDENTIFY is tried. If it succeeds then
device identification strings are output. If the "-r" option is given
then the 256 byte IDENTIFY block is output in binary.
.PP
-The reference document used for interpreting an INQUIRY is T10/1416-D Revision
-21a (9th November 2004) found at http://www.t10.org . Obsolete items in the
-standard INQUIRY response are displayed in brackets.
+The reference document used for interpreting an INQUIRY is T10/1416-D Revision
+21d (SPC-3, 14th February 2005) found at http://www.t10.org . Obsolete items
+in the standard INQUIRY response are displayed in brackets.
.TP
-c
set the Command Support Data (CmdDt) bit (defaults to clear(0)). Used
diff --git a/sg_inq.c b/sg_inq.c
index 6866e2c8..9129d6c4 100644
--- a/sg_inq.c
+++ b/sg_inq.c
@@ -35,7 +35,7 @@ in the future.
*/
-static char * version_str = "0.45 20050114";
+static char * version_str = "0.46 20050215";
#define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */
@@ -125,18 +125,15 @@ static const char * scsi_ptype_strs[] = {
"automation/driver interface",
"0x13", "0x14", "0x15", "0x16", "0x17", "0x18",
"0x19", "0x1a", "0x1b", "0x1c", "0x1d",
+ "well known logical unit",
+ "no physical device on this lu",
};
static const char * get_ptype_str(int scsi_ptype)
{
int num = sizeof(scsi_ptype_strs) / sizeof(scsi_ptype_strs[0]);
- if (0x1f == scsi_ptype)
- return "no physical device on this lu";
- else if (0x1e == scsi_ptype)
- return "well known logical unit";
- else
- return (scsi_ptype < num) ? scsi_ptype_strs[scsi_ptype] : "";
+ return (scsi_ptype < num) ? scsi_ptype_strs[scsi_ptype] : "";
}
struct vpd_name {
@@ -149,7 +146,7 @@ static struct vpd_name vpd_name_arr[] = {
{0x0, 0, "Supported VPD pages"},
{0x80, 0, "Unit serial number"},
{0x81, 0, "Implemented operating definitions"},
- {0x82, 0, "ASCII implemented operating definition"},
+ {0x82, 0, "ASCII implemented operating definition (obsolete)"},
{0x83, 0, "Device identification"},
{0x84, 0, "Software interface identification"},
{0x85, 0, "Management network addresses"},
@@ -515,9 +512,10 @@ static void decode_dev_ids(const char * leadin, unsigned char * buff,
break;
}
printf(" MD5 logical unit identifier:\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
+ /* 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 */
@@ -1492,10 +1490,10 @@ static void printswap(char *output, char *in, unsigned int n)
}
#define ATA_IDENTIFY_BUFF_SZ sizeof(struct ata_identify_device)
+#define HDIO_DRIVE_CMD_OFFSET 4
static int ata_command_interface(int device, char *data)
{
- const int HDIO_DRIVE_CMD_OFFSET = 4;
unsigned char buff[ATA_IDENTIFY_BUFF_SZ + HDIO_DRIVE_CMD_OFFSET];
int retval;
diff --git a/sg_lib.c b/sg_lib.c
index c821f187..0324f979 100644
--- a/sg_lib.c
+++ b/sg_lib.c
@@ -64,10 +64,11 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <ctype.h>
#include "sg_include.h"
#include "sg_lib.h"
-static char * version_str = "1.05 20050120";
+static char * version_str = "1.07 20050308";
FILE * sg_warnings_str = NULL; /* would like to default to stderr */
@@ -227,7 +228,7 @@ static const struct value_name_t normal_opcodes[] = {
{0xa5, 0, "Move medium"},
{0xa5, 5, "Play audio(12)"},
{0xa6, 0, "Exchange medium"},
- {0xa6, 5, "Load/unload cd/dvd"},
+ {0xa6, 5, "Load/unload medium"},
{0xa7, 0, "Move medium attached"},
{0xa7, 5, "Set read ahead"},
{0xa8, 0, "Read(12)"},
@@ -236,7 +237,7 @@ static const struct value_name_t normal_opcodes[] = {
{0xab, 0, "Service action in(12)"},
{0xac, 0, "erase(12)"},
{0xac, 5, "Get performance"},
- {0xad, 5, "Read dvd structure"},
+ {0xad, 5, "Read DVD/BD structure"},
{0xae, 0, "Write and verify(12)"},
{0xaf, 0, "Verify(12)"},
{0xb0, 0, "Search data high(12)"},
@@ -261,7 +262,7 @@ static const struct value_name_t normal_opcodes[] = {
{0xbe, 0, "Volume set in"},
{0xbe, 5, "Read CD"},
{0xbf, 0, "Volume set out"},
- {0xbf, 5, "Send DVD structure"},
+ {0xbf, 5, "Send DVD/BD structure"},
};
#define NORMAL_OPCODES_SZ \
@@ -275,6 +276,7 @@ static const struct value_name_t maint_in_arr[] = {
{0xc, 0, "Report supported operation codes"},
{0xd, 0, "Report supported task management functions"},
{0xe, 0, "Report priority"},
+ {0xf, 0, "Report timeout"},
};
#define MAINT_IN_SZ \
@@ -285,6 +287,7 @@ static const struct value_name_t maint_out_arr[] = {
{0xa, 0, "Set target port groups"},
{0xb, 0, "Change aliases"},
{0xe, 0, "Set priority"},
+ {0xf, 0, "Set timeout"},
};
#define MAINT_OUT_SZ \
@@ -606,10 +609,10 @@ static struct error_info additional[] =
{0x17,0x03,"Recovered data with negative head offset"},
{0x17,0x04,"Recovered data with retries and/or circ applied"},
{0x17,0x05,"Recovered data using previous sector id"},
- {0x17,0x06,"Recovered data without ecc - data auto-reallocated"},
- {0x17,0x07,"Recovered data without ecc - recommend reassignment"},
- {0x17,0x08,"Recovered data without ecc - recommend rewrite"},
- {0x17,0x09,"Recovered data without ecc - data rewritten"},
+ {0x17,0x06,"Recovered data without ECC - data auto-reallocated"},
+ {0x17,0x07,"Recovered data without ECC - recommend reassignment"},
+ {0x17,0x08,"Recovered data without ECC - recommend rewrite"},
+ {0x17,0x09,"Recovered data without ECC - data rewritten"},
{0x18,0x00,"Recovered data with error correction applied"},
{0x18,0x01,"Recovered data with error corr. & retries applied"},
{0x18,0x02,"Recovered data - data auto-reallocated"},
@@ -617,7 +620,7 @@ static struct error_info additional[] =
{0x18,0x04,"Recovered data with L-EC"},
{0x18,0x05,"Recovered data - recommend reassignment"},
{0x18,0x06,"Recovered data - recommend rewrite"},
- {0x18,0x07,"Recovered data with ecc - data rewritten"},
+ {0x18,0x07,"Recovered data with ECC - data rewritten"},
{0x18,0x08,"Recovered data with linking"},
{0x19,0x00,"Defect list error"},
{0x19,0x01,"Defect list not available"},
@@ -629,7 +632,7 @@ static struct error_info additional[] =
{0x1C,0x01,"Primary defect list not found"},
{0x1C,0x02,"Grown defect list not found"},
{0x1D,0x00,"Miscompare during verify operation"},
- {0x1E,0x00,"Recovered id with ecc correction"},
+ {0x1E,0x00,"Recovered id with ECC correction"},
{0x1F,0x00,"Partial defect list transfer"},
{0x20,0x00,"Invalid command operation code"},
{0x20,0x01,"Access denied - initiator pending-enrolled"},
@@ -697,6 +700,7 @@ static struct error_info additional[] =
{0x2A,0x07,"Implicit asymmetric access state transition failed"},
{0x2A,0x08,"Priority changed"},
{0x2A,0x09,"Capacity data has changed"},
+ {0x2A,0x10,"Timestamp changed"},
{0x2B,0x00,"Copy cannot execute since host cannot disconnect"},
{0x2C,0x00,"Command sequence error"},
{0x2C,0x01,"Too many windows specified"},
@@ -1052,6 +1056,15 @@ static const char *sense_key_desc[] = {
"Key=15" /* Reserved */
};
+char * sg_get_sense_key_str(int sense_key, int buff_len, char * buff)
+{
+ if ((sense_key >= 0) && (sense_key < 16))
+ snprintf(buff, buff_len, "%s", sense_key_desc[sense_key]);
+ else
+ snprintf(buff, buff_len, "invalid value: 0x%x", sense_key);
+ return buff;
+}
+
char * sg_get_asc_ascq_str(int asc, int ascq, int buff_len, char * buff)
{
int k, num, rlen;
@@ -1114,7 +1127,7 @@ const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep,
desc_len = add_len + 2;
if (descp[0] == desc_type)
return descp;
- if (add_len < 0) // short descriptor ??
+ if (add_len < 0) /* short descriptor ?? */
break;
}
return NULL;
@@ -1132,13 +1145,10 @@ int sg_get_sense_info_fld(const unsigned char * sensep, int sb_len,
switch (sensep[0] & 0x7f) {
case 0x70:
case 0x71:
- if (sensep[0] & 0x80) {
- if (info_outp)
- *info_outp = (sensep[3] << 24) + (sensep[4] << 16) +
- (sensep[5] << 8) + sensep[6];
- return 1;
- } else
- return 0;
+ if (info_outp)
+ *info_outp = (sensep[3] << 24) + (sensep[4] << 16) +
+ (sensep[5] << 8) + sensep[6];
+ return (sensep[0] & 0x80) ? 1 : 0;
case 0x72:
case 0x73:
ucp = sg_scsi_sense_desc_find(sensep, sb_len, 0 /* info desc */);
@@ -1151,6 +1161,43 @@ int sg_get_sense_info_fld(const unsigned char * sensep, int sb_len,
}
if (info_outp)
*info_outp = ull;
+ return !!(ucp[2] & 0x80);
+ } else
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+int sg_get_sense_progress_fld(const unsigned char * sensep,
+ int sb_len, int * progress_outp)
+{
+ const unsigned char * ucp;
+ int sk;
+
+ if (sb_len < 7)
+ return 0;
+ switch (sensep[0] & 0x7f) {
+ case 0x70:
+ case 0x71:
+ sk = (sensep[2] & 0xf);
+ if ((sb_len < 18) || ((NO_SENSE != sk) && (NOT_READY != sk)))
+ return 0;
+ if (sensep[15] & 0x80) {
+ if (progress_outp)
+ *progress_outp = (sensep[16] << 8) + sensep[17];
+ return 1;
+ } else
+ return 0;
+ case 0x72:
+ case 0x73:
+ sk = (sensep[1] & 0xf);
+ if ((NO_SENSE != sk) && (NOT_READY != sk))
+ return 0;
+ ucp = sg_scsi_sense_desc_find(sensep, sb_len, 2 /* sense key spec. */);
+ if (ucp && (0x6 == ucp[1]) && (0x80 & ucp[4])) {
+ if (progress_outp)
+ *progress_outp = (ucp[5] << 8) + ucp[6];
return 1;
} else
return 0;
@@ -1266,7 +1313,7 @@ static void sg_print_sense_descriptors(const unsigned char * sense_buffer,
case 3:
fprintf(sg_warnings_str, "Field replaceable unit\n");
if (add_len >= 2)
- fprintf(sg_warnings_str, " 0x%x\n", descp[3]);
+ fprintf(sg_warnings_str, " code=0x%x\n", descp[3]);
else
processed = 0;
break;
@@ -1427,8 +1474,8 @@ void sg_print_sense(const char * leadin, const unsigned char * sense_buffer,
fprintf(sg_warnings_str, " Info fld=0x%x [%u] ", info,
info);
else if (info > 0)
- fprintf(sg_warnings_str, " vendor specific Info "
- "fld=0x%x ", info);
+ fprintf(sg_warnings_str, " Valid=0, Info fld=0x%x [%u] ",
+ info, info);
}
if (sense_buffer[2] & 0xe0) {
if (sense_buffer[2] & 0x80)
@@ -1442,7 +1489,11 @@ void sg_print_sense(const char * leadin, const unsigned char * sense_buffer,
/* incorrect block length requested */
fprintf(sg_warnings_str, "\n");
}
+ if ((len >= 14) && sense_buffer[14])
+ fprintf(sg_warnings_str, " Field replaceable unit code: "
+ "%d\n", sense_buffer[14]);
if ((len >= 18) && (sense_buffer[15] & 0x80)) {
+ /* sense key specific decoding */
switch (ssh.sense_key) {
case ILLEGAL_REQUEST:
fprintf(sg_warnings_str, " Sense Key Specific: Error in "
@@ -1579,6 +1630,8 @@ void sg_print_driver_status(int driver_status)
fprintf(sg_warnings_str, " [%s, %s] ", driv_cp, sugg_cp);
}
+/* Returns 1 if no errors found and thus nothing printed; otherwise
+ prints error/warning (prefix by 'leadin') and returns 0. */
static int sg_sense_print(const char * leadin, int scsi_status,
int host_status, int driver_status,
const unsigned char * sense_buffer, int sb_len)
@@ -1678,6 +1731,8 @@ int sg_normalize_sense(const struct sg_io_hdr * hp,
return sg_scsi_normalize_sense(hp->sbp, hp->sb_len_wr, sshp);
}
+/* Returns 1 if no errors found and thus nothing printed; otherwise
+ returns 0. */
int sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp)
{
return sg_sense_print(leadin, hp->status, hp->host_status,
@@ -1685,6 +1740,8 @@ int sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp)
}
#endif
+/* Returns 1 if no errors found and thus nothing printed; otherwise
+ returns 0. */
int sg_chk_n_print(const char * leadin, int masked_status,
int host_status, int driver_status,
const unsigned char * sense_buffer, int sb_len)
@@ -1729,17 +1786,24 @@ int sg_err_category_new(int scsi_status, int host_status, int driver_status,
if ((sense_buffer && (sb_len > 2)) &&
(sg_scsi_normalize_sense(sense_buffer, sb_len, &ssh))) {
- if (RECOVERED_ERROR == ssh.sense_key)
+ switch (ssh.sense_key) {
+ case RECOVERED_ERROR:
return SG_LIB_CAT_RECOVERED;
- else if (UNIT_ATTENTION == ssh.sense_key) {
+ case MEDIUM_ERROR:
+ case HARDWARE_ERROR:
+ return SG_LIB_CAT_MEDIUM_HARD;
+ case UNIT_ATTENTION:
if (0x28 == ssh.asc)
return SG_LIB_CAT_MEDIA_CHANGED;
if (0x29 == ssh.asc)
return SG_LIB_CAT_RESET;
- }
- else if (ILLEGAL_REQUEST == ssh.sense_key) {
+ break;
+ case ILLEGAL_REQUEST:
if ((0x20 == ssh.asc) && (0x0 == ssh.ascq))
return SG_LIB_CAT_INVALID_OP;
+ else
+ return SG_LIB_CAT_ILLEGAL_REQ;
+ break;
}
}
return SG_LIB_CAT_SENSE;
@@ -2040,12 +2104,15 @@ static void dStrHexErr(const char* str, int len)
/* If the number in 'buf' can be decoded or the multiplier is unknown
then -1 is returned. Accepts a hex prefix (0x or 0X) or a decimal
- multiplier suffix. */
+ multiplier suffix (as per GNU's dd (since 2002: SI and IEC 60027-2)).
+ Main (SI) multipliers supported: K, M, G. */
int sg_get_num(const char * buf)
{
- int res, num;
+ int res, num, n;
unsigned int unum;
+ char * cp;
char c = 'c';
+ char c2, c3;
if ((NULL == buf) || ('\0' == buf[0]))
return -1;
@@ -2053,31 +2120,57 @@ int sg_get_num(const char * buf)
res = sscanf(buf + 2, "%x", &unum);
num = (int)unum;
} else
- res = sscanf(buf, "%d%c", &num, &c);
- if (1 == res)
+ res = sscanf(buf, "%d%c%c%c", &num, &c, &c2, &c3);
+ if (res < 1)
+ return -1LL;
+ else if (1 == res)
return num;
- else if (2 != res)
- return -1;
else {
- switch (c) {
- case 'c':
+ if (res > 2)
+ c2 = toupper(c2);
+ if (res > 3)
+ c3 = toupper(c3);
+ switch (toupper(c)) {
case 'C':
return num;
- case 'b':
+ case 'W':
+ return num * 2;
case 'B':
return num * 512;
- case 'k':
- return num * 1024;
case 'K':
- return num * 1000;
- case 'm':
- return num * 1024 * 1024;
+ if (2 == res)
+ return num * 1024;
+ if (('B' == c2) || ('D' == c2))
+ return num * 1000;
+ if (('I' == c2) && (4 == res) && ('B' == c3))
+ return num * 1024;
+ return -1;
case 'M':
- return num * 1000000;
- case 'g':
- return num * 1024 * 1024 * 1024;
+ if (2 == res)
+ return num * 1048576;
+ if (('B' == c2) || ('D' == c2))
+ return num * 1000000;
+ if (('I' == c2) && (4 == res) && ('B' == c3))
+ return num * 1048576;
+ return -1;
case 'G':
- return num * 1000000000;
+ if (2 == res)
+ return num * 1073741824;
+ if (('B' == c2) || ('D' == c2))
+ return num * 1000000000;
+ if (('I' == c2) && (4 == res) && ('B' == c3))
+ return num * 1073741824;
+ return -1;
+ case 'X':
+ cp = strchr(buf, 'x');
+ if (NULL == cp)
+ cp = strchr(buf, 'X');
+ if (cp) {
+ n = sg_get_num(cp + 1);
+ if (-1 != n)
+ return num * n;
+ }
+ return -1;
default:
if (NULL == sg_warnings_str)
sg_warnings_str = stderr;
@@ -2089,13 +2182,16 @@ int sg_get_num(const char * buf)
/* If the number in 'buf' can be decoded or the multiplier is unknown
then -1LL is returned. Accepts a hex prefix (0x or 0X) or a decimal
- multiplier suffix. */
+ multiplier suffix (as per GNU's dd (since 2002: SI and IEC 60027-2)).
+ Main (SI) multipliers supported: K, M, G, T, P. */
long long sg_get_llnum(const char * buf)
{
int res;
- long long num;
+ long long num, ll;
unsigned long long unum;
+ char * cp;
char c = 'c';
+ char c2, c3;
if ((NULL == buf) || ('\0' == buf[0]))
return -1LL;
@@ -2103,35 +2199,73 @@ long long sg_get_llnum(const char * buf)
res = sscanf(buf + 2, "%llx", &unum);
num = unum;
} else
- res = sscanf(buf, "%lld%c", &num, &c);
- if (1 == res)
- return num;
- else if (2 != res)
+ res = sscanf(buf, "%lld%c%c%c", &num, &c, &c2, &c3);
+ if (res < 1)
return -1LL;
+ else if (1 == res)
+ return num;
else {
- switch (c) {
- case 'c':
+ if (res > 2)
+ c2 = toupper(c2);
+ if (res > 3)
+ c3 = toupper(c3);
+ switch (toupper(c)) {
case 'C':
return num;
- case 'b':
+ case 'W':
+ return num * 2;
case 'B':
return num * 512;
- case 'k':
- return num * 1024;
case 'K':
- return num * 1000;
- case 'm':
- return num * 1024 * 1024;
+ if (2 == res)
+ return num * 1024;
+ if (('B' == c2) || ('D' == c2))
+ return num * 1000;
+ if (('I' == c2) && (4 == res) && ('B' == c3))
+ return num * 1024;
+ return -1LL;
case 'M':
- return num * 1000000;
- case 'g':
- return num * 1024 * 1024 * 1024;
+ if (2 == res)
+ return num * 1048576;
+ if (('B' == c2) || ('D' == c2))
+ return num * 1000000;
+ if (('I' == c2) && (4 == res) && ('B' == c3))
+ return num * 1048576;
+ return -1LL;
case 'G':
- return num * 1000000000;
- case 't':
- return num * 1024LL * 1024LL * 1024LL * 1024LL;
+ if (2 == res)
+ return num * 1073741824;
+ if (('B' == c2) || ('D' == c2))
+ return num * 1000000000;
+ if (('I' == c2) && (4 == res) && ('B' == c3))
+ return num * 1073741824;
+ return -1LL;
case 'T':
- return num * 1000000000000LL;
+ if (2 == res)
+ return num * 1099511627776LL;
+ if (('B' == c2) || ('D' == c2))
+ return num * 1000000000000LL;
+ if (('I' == c2) && (4 == res) && ('B' == c3))
+ return num * 1099511627776LL;
+ return -1LL;
+ case 'P':
+ if (2 == res)
+ return num * 1099511627776LL * 1024;
+ if (('B' == c2) || ('D' == c2))
+ return num * 1000000000000LL * 1000;
+ if (('I' == c2) && (4 == res) && ('B' == c3))
+ return num * 1099511627776LL * 1024;
+ return -1LL;
+ case 'X':
+ cp = strchr(buf, 'x');
+ if (NULL == cp)
+ cp = strchr(buf, 'X');
+ if (cp) {
+ ll = sg_get_llnum(cp + 1);
+ if (-1LL != ll)
+ return num * ll;
+ }
+ return -1LL;
default:
if (NULL == sg_warnings_str)
sg_warnings_str = stderr;
diff --git a/sg_lib.h b/sg_lib.h
index 156012fb..47164e62 100644
--- a/sg_lib.h
+++ b/sg_lib.h
@@ -30,7 +30,7 @@
*
*/
-/* Version 1.00 [20041015]
+/* Version 1.07 [20050227]
* 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
* as open source and encourage their unencumbered use.
@@ -128,15 +128,26 @@ extern int sg_scsi_normalize_sense(const unsigned char * sensep,
extern const unsigned char * sg_scsi_sense_desc_find(
const unsigned char * sensep, int sense_len, int desc_type);
+/* Yield string associated with sense_key value. Returns 'buff'. */
+extern char * sg_get_sense_key_str(int sense_key,int buff_len, char * buff);
+
/* Yield string associated with ASC/ASCQ values. Returns 'buff'. */
extern char * sg_get_asc_ascq_str(int asc, int ascq, int buff_len,
char * buff);
-/* Returns 1 if information field valid in sense data and places data
- where info_outp points. If information field is not available (valid)
- returns 0. Handles both fixed and descriptor sense formats. */
+/* Returns 1 if valid bit set, 0 if valid bit clear. Irrespective the
+ information field is written out via 'info_outp' (except when it is
+ NULL). Handles both fixed and descriptor sense formats. */
extern int sg_get_sense_info_fld(const unsigned char * sensep, int sb_len,
- unsigned long long * info_outp);
+ unsigned long long * info_outp);
+
+/* Returns 1 if sense key is NO_SENSE or NOT_READY and SKSV is set. Places
+ progress field from sense data where progress_outp points. If progress
+ field is not available returns 0. Handles both fixed and descriptor
+ sense formats. N.B. App should multiply by 100 and divide by 65536
+ to get percentage completion from given value. */
+extern int sg_get_sense_progress_fld(const unsigned char * sensep,
+ int sb_len, int * progress_outp);
/* <<< General purpose (i.e. not SCSI specific) utility functions >>> */
@@ -157,16 +168,17 @@ extern void dStrHex(const char* str, int len, int no_ascii);
/* If the number in 'buf' can not be decoded or the multiplier is unknown
then -1 is returned. Accepts a hex prefix (0x or 0X) or a decimal
- multiplier suffix. Recognised multipliers: c C *1; b B *512;
- k *1,024; K *1,000; m *1,048,576; M *1,000,000; g *1,073,741,824;
- G *1,000,000,000 . */
+ multiplier suffix (not both). Recognised multipliers: 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; g G GiB *1,073,741,824; GB *1,000,000,000 and x<m>
+ which multiplies the leading number by <n> . */
extern int sg_get_num(const char * buf);
/* If the number in 'buf' can not be decoded or the multiplier is unknown
then -1LL is returned. Accepts a hex prefix (0x or 0X) or a decimal
- multiplier suffix. In addition to supporting the multipliers of
- sg_get_num(), this function supports: t *1,099,511,627,776 and
- T *1,000,000,000,000 . */
+ multiplier suffix (not both). In addition to supporting the multipliers
+ of sg_get_num(), this function supports: t T TiB *(2**40); TB *(10**12);
+ p P PiB *(2**50); PB *(10**15) . */
extern long long sg_get_llnum(const char * buf);
extern const char * sg_lib_version();
@@ -274,13 +286,17 @@ extern void sg_print_host_status(int host_status);
extern void sg_print_driver_status(int driver_status);
/* sg_chk_n_print() returns 1 quietly if there are no errors/warnings
- else it prints to stderr and returns 0. */
+ else it prints errors/warnings (prefixed by 'leadin') to
+ 'sg_warnings_fd' and returns 0. */
extern int sg_chk_n_print(const char * leadin, int masked_status,
int host_status, int driver_status,
const unsigned char * sense_buffer, int sb_len);
/* The following function declaration is for the sg version 3 driver. */
struct sg_io_hdr;
+/* sg_chk_n_print3() returns 1 quietly if there are no errors/warnings;
+ else it prints errors/warnings (prefixed by 'leadin') to
+ 'sg_warnings_fd' and returns 0. */
extern int sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp);
/* Calls sg_scsi_normalize_sense() after obtaining the sense buffer and
@@ -293,10 +309,18 @@ extern int sg_normalize_sense(const struct sg_io_hdr * hp,
/* The following "category" function returns one of the following */
#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_RESET 2 /* interpreted from sense buffer */
+ /* [sk,asc,ascq: 0x6,0x29,*] */
#define SG_LIB_CAT_TIMEOUT 3
#define SG_LIB_CAT_RECOVERED 4 /* Successful command after recovered err */
-#define SG_LIB_CAT_INVALID_OP 5 /* Invalid operation code */
+ /* [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 */
+ /* opcode): [sk,asc,ascq: 0x5,*,*] */
#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 */
diff --git a/sg_logs.8 b/sg_logs.8
index 1f01736d..8728904b 100644
--- a/sg_logs.8
+++ b/sg_logs.8
@@ -1,4 +1,4 @@
-.TH SG_LOGS "8" "November 2004" "sg3_utils-1.11" SG3_UTILS
+.TH SG_LOGS "8" "January 2005" "sg3_utils-1.13" SG3_UTILS
.SH NAME
sg_logs \- read SCSI device log pages
.SH SYNOPSIS
@@ -105,6 +105,8 @@ the "0x" prefix when using them as an argument to the '-p=' option]:
.br
0xb Last n deferred errors or asynchronous events
.br
+0xc Sequential access device (ssc-2)
+.br
0xd Temperature
.br
0xe Start-stop cycle counter
@@ -130,7 +132,7 @@ Written by Doug Gilbert
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2002-2004 Douglas Gilbert
+Copyright \(co 2002-2005 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 40696658..4b0adb17 100644
--- a/sg_logs.c
+++ b/sg_logs.c
@@ -13,7 +13,7 @@
#include "sg_cmds.h"
/* A utility program for the Linux OS SCSI generic ("sg") device driver.
-* Copyright (C) 2000-2004 D. Gilbert
+* Copyright (C) 2000-2005 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)
@@ -23,7 +23,7 @@
*/
-static char * version_str = "0.37 20041209";
+static char * version_str = "0.38 20050305";
#define ME "sg_logs: "
@@ -36,7 +36,9 @@ static char * version_str = "0.37 20041209";
actual length of response; then a second time requesting the
min(actual_len, mx_resp_len) bytes. If the calculated length for the
second fetch is odd then it is incremented (perhaps should be made modulo 4
- in the future for SAS). */
+ 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. */
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)
@@ -47,8 +49,8 @@ static int do_logs(int sg_fd, int ppc, int sp, int pc, int pg_code,
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)
- fprintf(stderr, "log_sense: not supported\n");
+ if ((SG_LIB_CAT_INVALID_OP == res) || (SG_LIB_CAT_ILLEGAL_REQ == res))
+ return res;
return -1;
}
actual_len = (resp[2] << 8) + resp[3] + 4;
@@ -59,8 +61,8 @@ static int do_logs(int sg_fd, int ppc, int sp, int pc, int pg_code,
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)
- fprintf(stderr, "log_sense: not supported (b)\n");
+ if ((SG_LIB_CAT_INVALID_OP == res) || (SG_LIB_CAT_ILLEGAL_REQ == res))
+ return res;
return -1;
}
return 0;
@@ -231,43 +233,47 @@ static void show_buffer_under_overrun_page(unsigned char * resp, int len,
while (num > 3) {
pl = ucp[3] + 4;
count_basis = (ucp[1] >> 5) & 0x7;
- printf(" Count basis: ");
- switch (count_basis) {
- case 0 : printf("undefined"); break;
- case 1 : printf("per command"); break;
- case 2 : printf("per failed reconnect"); break;
- case 3 : printf("per unit of time"); break;
- default: printf("reserved [0x%x]", count_basis); break;
- }
cause = (ucp[1] >> 1) & 0xf;
- printf(", Cause: ");
- switch (cause) {
- case 0 : printf("undefined"); break;
- case 1 : printf("bus busy"); break;
- case 2 : printf("transfer rate too slow"); break;
- default: printf("reserved [0x%x]", cause); break;
- }
- printf(", Type: ");
- if (ucp[1] & 1)
- printf("over-run");
- else
- printf("under-run");
- pcb = ucp[2];
- printf(", count");
- k = pl - 4;
- xp = ucp + 4;
- if (k > (int)sizeof(ull)) {
- xp += (k - sizeof(ull));
- k = sizeof(ull);
- }
- ull = 0;
- for (j = 0; j < k; ++j) {
- if (j > 0)
- ull <<= 8;
- ull |= xp[j];
+ if ((0 == count_basis) && (0 == cause))
+ printf("Count basis+Cause both undefined(0), unsupported??");
+ else {
+ printf(" Count basis: ");
+ switch (count_basis) {
+ case 0 : printf("undefined"); break;
+ case 1 : printf("per command"); break;
+ case 2 : printf("per failed reconnect"); break;
+ case 3 : printf("per unit of time"); break;
+ default: printf("reserved [0x%x]", count_basis); break;
+ }
+ printf(", Cause: ");
+ switch (cause) {
+ case 0 : printf("undefined"); break;
+ case 1 : printf("bus busy"); break;
+ case 2 : printf("transfer rate too slow"); break;
+ default: printf("reserved [0x%x]", cause); break;
+ }
+ printf(", Type: ");
+ if (ucp[1] & 1)
+ printf("over-run");
+ else
+ printf("under-run");
+ printf(", count");
+ k = pl - 4;
+ xp = ucp + 4;
+ if (k > (int)sizeof(ull)) {
+ xp += (k - sizeof(ull));
+ k = sizeof(ull);
+ }
+ ull = 0;
+ for (j = 0; j < k; ++j) {
+ if (j > 0)
+ ull <<= 8;
+ ull |= xp[j];
+ }
+ printf(" = %llu", ull);
}
- printf(" = %llu", ull);
if (show_pcb) {
+ pcb = ucp[2];
get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
printf(" <%s>\n", pcb_str);
} else
@@ -310,7 +316,7 @@ static void show_error_counter_page(unsigned char * resp, int len,
pcb = ucp[2];
pl = ucp[3] + 4;
switch (pc) {
- case 0: printf(" Errors corrected without substantion delay"); break;
+ case 0: printf(" Errors corrected without substantial delay"); break;
case 1: printf(" Errors corrected with possible delays"); break;
case 2: printf(" Total operations"); break;
case 3: printf(" Total errors corrected"); break;
@@ -463,12 +469,12 @@ static void show_last_n_deferred_error_page(unsigned char * resp,
}
}
-const char * self_test_code[] = {
+static const char * self_test_code[] = {
"default", "background short", "background extended", "reserved",
"aborted background", "foreground short", "foreground extended",
"reserved"};
-const char * self_test_result[] = {
+static const char * self_test_result[] = {
"completed without error",
"aborted by SEND DIAGNOSTIC",
"aborted other than by SEND DIAGNOSTIC",
@@ -880,7 +886,8 @@ static void show_non_volatile_cache_page(unsigned char * resp, int len,
} else
printf("<unexpected parameter length=%d>\n", ucp[4]);
break;
- case 1: printf(" Maximum non-volatile time: ");
+ case 1:
+ printf(" Maximum non-volatile time: ");
if (3 == ucp[4]) {
j = (ucp[5] << 16) + (ucp[6] << 8) + ucp[7];
switch (j) {
@@ -914,6 +921,205 @@ static void show_non_volatile_cache_page(unsigned char * resp, int len,
}
}
+static const char * bms_status[] = {
+ "no scans active",
+ "background medium scan is active",
+ "pre-scan is active",
+ "scan halted due to fatal error",
+ "scan halted due to unusual pattern of error",
+ "scan halted due to medium formatted without P-List",
+ "scan halted - vendor specific cause",
+ "scan halted due to temperature out of range",
+ "scan suspended until BMS Interval Time expires",
+};
+
+static const char * reassign_status[] = {
+ "No assignment needed",
+ "Reassignment pending receipt of Reassign command or Write command",
+ "LBA successfully reassigned by drive",
+ "Reassign status: Reserved [0x3]",
+ "Reassignment failed",
+ "LBA recovered via re-write",
+};
+
+static void show_background_scan_results_page(unsigned char * resp, int len,
+ int show_pcb, int verbose)
+{
+ int j, m, num, pl, pc, pcb;
+ unsigned char * ucp;
+ char str[128];
+
+ printf("Background scan results page (sbc-2) [0x15]\n");
+ num = len - 4;
+ ucp = &resp[0] + 4;
+ while (num > 3) {
+ pc = (ucp[0] << 8) | ucp[1];
+ pcb = ucp[2];
+ pl = ucp[3] + 4;
+ switch (pc) {
+ case 0:
+ printf(" Power on time: ");
+ j = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7];
+ printf("%d minutes [%d:%d]\n", j, (j / 60), (j % 60));
+ break;
+ printf(" BMS status: ");
+ j = ucp[9];
+ if (j < (int)(sizeof(bms_status) / sizeof(bms_status[0])))
+ printf("%s\n", bms_status[j]);
+ else
+ printf("unknown [0x%x]\n", j);
+ printf(" Number of scan performed: %d\n",
+ (ucp[10] << 8) + ucp[11]);
+ printf(" Progress of medium scan: %d%%\n",
+ ((ucp[12] << 8) + ucp[13]) * 100 / 65536);
+
+ break;
+ default:
+ printf(" Medium scan parameter # %d\n", pc);
+ if (pl < 24) {
+ fprintf(stderr, " parameter length >= 24 expected, "
+ "got %d\n", pl);
+ break;
+ }
+ printf(" Power on time when error detected: ");
+ j = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7];
+ printf("%d minutes [%d:%d]\n", j, (j / 60), (j % 60));
+ j = (ucp[8] >> 4) & 0xf;
+ if (j <
+ (int)(sizeof(reassign_status) / sizeof(reassign_status[0])))
+ printf(" %s\n", reassign_status[j]);
+ else
+ printf(" Reassign status: reserved [0x%x]\n", j);
+ printf(" sense key: %s sk,asc,ascq: 0x%x,0x%x,0x%x\n",
+ sg_get_sense_key_str(ucp[8] & 0xf, sizeof(str), str),
+ ucp[8] & 0xf, ucp[9], ucp[10]);
+ printf(" %s\n", sg_get_asc_ascq_str(ucp[9], ucp[10],
+ sizeof(str), str));
+ if (verbose) {
+ printf(" vendor bytes [11 -> 15]: ");
+ for (m = 0; m < 5; ++m)
+ printf("0x%02x ", ucp[11 + m]);
+ printf("\n");
+ }
+ printf(" LBA (of medium error): 0x");
+ for (m = 0; m < 8; ++m)
+ printf("%02x", ucp[16 + m]);
+ printf("\n");
+ break;
+ }
+ if (show_pcb) {
+ get_pcb_str(pcb, str, sizeof(str));
+ printf(" <%s>\n", str);
+ }
+ num -= pl;
+ ucp += pl;
+ }
+}
+
+static void show_sequential_access_page(unsigned char * resp, int len,
+ int show_pcb, int verbose)
+{
+ int k, j, num, pl, pc, pcb;
+ unsigned char * ucp;
+ unsigned char * xp;
+ unsigned long long ull, gbytes;
+ char pcb_str[64];
+
+ printf("Sequential access device page (ssc-3)\n");
+ num = len - 4;
+ ucp = &resp[0] + 4;
+ while (num > 3) {
+ pc = (ucp[0] << 8) | ucp[1];
+ pcb = ucp[2];
+ pl = ucp[3] + 4;
+ k = pl - 4;
+ xp = ucp + 4;
+ if (k > (int)sizeof(ull)) {
+ xp += (k - sizeof(ull));
+ k = sizeof(ull);
+ }
+ ull = 0;
+ for (j = 0; j < k; ++j) {
+ if (j > 0)
+ ull <<= 8;
+ ull |= xp[j];
+ }
+ gbytes = ull / 1000000000;
+ switch (pc) {
+ case 0:
+ printf(" Data bytes received with WRITE commands: %llu GB",
+ gbytes);
+ if (verbose)
+ printf(" [%llu bytes]", ull);
+ printf("\n");
+ break;
+ case 1:
+ printf(" Data bytes written to media by WRITE commands: %llu "
+ "GB", gbytes);
+ if (verbose)
+ printf(" [%llu bytes]", ull);
+ printf("\n");
+ break;
+ case 2:
+ printf(" Data bytes read from media by READ commands: %llu "
+ "GB", gbytes);
+ if (verbose)
+ printf(" [%llu bytes]", ull);
+ printf("\n");
+ break;
+ case 3:
+ printf(" Data bytes transferred by READ commands: %llu "
+ "GB", gbytes);
+ if (verbose)
+ printf(" [%llu bytes]", ull);
+ printf("\n");
+ break;
+ case 4:
+ printf(" Native capacity from BOP to EOD: %llu MB\n", ull);
+ break;
+ case 5:
+ printf(" Native capacity from BOP to EW of current partition: "
+ "%llu MB\n", ull);
+ break;
+ case 6:
+ printf(" Minimum native capacity from EW to EOP of current "
+ "partition: %llu MB\n", ull);
+ break;
+ case 7:
+ printf(" Native capacity from BOP to current position: %llu "
+ "MB\n", ull);
+ break;
+ case 8:
+ printf(" Maximum native capacity in device object buffer: %llu "
+ "MB\n", ull);
+ break;
+ case 0x100:
+ if (ull > 0)
+ printf(" Cleaning action required\n");
+ else
+ printf(" Cleaning action not required (or completed)\n");
+ if (verbose)
+ printf(" cleaning value: %llu\n", ull);
+ break;
+ default:
+ if (pc >= 0x8000)
+ printf(" Vendor specific parameter [0x%x] value: %llu\n",
+ pc, ull);
+ else
+ printf(" Reserved parameter [0x%x] value: %llu\n",
+ pc, ull);
+ break;
+ }
+ if (show_pcb) {
+ get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+ printf(" <%s>\n", pcb_str);
+ } else
+ printf("\n");
+ num -= pl;
+ ucp += pl;
+ }
+}
+
static void show_seagate_cache_page(unsigned char * resp, int len,
int show_pcb)
{
@@ -1014,7 +1220,8 @@ static void show_seagate_factory_page(unsigned char * resp, int len,
}
static void show_ascii_page(unsigned char * resp, int len, int show_pcb,
- struct sg_simple_inquiry_resp * inq_dat)
+ struct sg_simple_inquiry_resp * inq_dat,
+ int verbose)
{
int k, num, done;
@@ -1057,9 +1264,23 @@ static void show_ascii_page(unsigned char * resp, int len, int show_pcb,
break;
}
}
+ break;
case 0xb:
show_last_n_deferred_error_page(resp, len, show_pcb);
break;
+ case 0xc:
+ {
+ switch (inq_dat->peripheral_type) {
+ case 1: case 2: case 8:
+ /* tape, (printer) and medium changer type devices */
+ show_sequential_access_page(resp, len, show_pcb, verbose);
+ break;
+ default:
+ done = 0;
+ break;
+ }
+ }
+ break;
case 0xd:
show_Temperature_page(resp, len, show_pcb, 1, 1);
break;
@@ -1069,6 +1290,20 @@ static void show_ascii_page(unsigned char * resp, int len, int show_pcb,
case 0x10:
show_self_test_page(resp, len, show_pcb);
break;
+ case 0x15:
+ {
+ switch (inq_dat->peripheral_type) {
+ case 0: case 4: case 7: case 0xe:
+ /* disk (direct access) type devices */
+ show_background_scan_results_page(resp, len, show_pcb,
+ verbose);
+ break;
+ default:
+ done = 0;
+ break;
+ }
+ }
+ break;
case 0x17:
{
switch (inq_dat->peripheral_type) {
@@ -1110,7 +1345,7 @@ static void show_ascii_page(unsigned char * resp, int len, int show_pcb,
break;
case 1: case 2: case 8:
/* streaming or medium changer devices */
- // call ssc_device_status_log_page()
+ /* call ssc_device_status_log_page() */
break;
default:
done = 0;
@@ -1156,7 +1391,7 @@ static int fetchTemperature(int sg_fd, unsigned char * resp, int max_len,
int main(int argc, char * argv[])
{
- int sg_fd, k, num, pg_len;
+ int sg_fd, k, num, pg_len, res;
char * file_name = 0;
char ebuff[EBUFF_SZ];
unsigned char rsp_buff[MX_ALLOC_LEN];
@@ -1271,9 +1506,9 @@ int main(int argc, char * argv[])
fprintf(stderr, "log_select: not supported\n");
return k ? 1 : 0;
}
- if (0 == 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, 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 output\n",
@@ -1286,8 +1521,13 @@ int main(int argc, char * argv[])
dStrHex((const char *)rsp_buff, pg_len + 4, 1);
}
else
- show_ascii_page(rsp_buff, pg_len + 4, do_pcb, &inq_out);
- }
+ 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");
+
if (do_all && (pg_len > 1)) {
int my_len = pg_len - 1;
unsigned char parr[256];
@@ -1311,7 +1551,8 @@ int main(int argc, char * argv[])
dStrHex((const char *)rsp_buff, pg_len + 4, 1);
}
else
- show_ascii_page(rsp_buff, pg_len + 4, do_pcb, &inq_out);
+ show_ascii_page(rsp_buff, pg_len + 4, do_pcb, &inq_out,
+ do_verbose);
}
}
}
diff --git a/sg_luns.c b/sg_luns.c
index 65023b35..866619d1 100644
--- a/sg_luns.c
+++ b/sg_luns.c
@@ -296,6 +296,8 @@ int main(int argc, char * argv[])
} else if (SG_LIB_CAT_INVALID_OP == res)
fprintf(stderr, "Report Luns command not supported (support "
"mandatory in SPC-3)\n");
+ else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+ fprintf(stderr, "Report Luns command has bad fields in cdb\n");
res = close(sg_fd);
if (res < 0) {
diff --git a/sg_modes.8 b/sg_modes.8
index 5be574a9..2decb495 100644
--- a/sg_modes.8
+++ b/sg_modes.8
@@ -1,10 +1,11 @@
-.TH SG_MODES "8" "December 2004" "sg3_utils-1.12" SG3_UTILS
+.TH SG_MODES "8" "February 2005" "sg3_utils-1.13" SG3_UTILS
.SH NAME
sg_modes \- reads SCSI MODE SENSE pages
.SH SYNOPSIS
.B sg_modes
-[\fI-a\fR] [\fI-A\fR] [\fI-c=<page_control>\fR] [\fI-d\fR] [\fI-h\fR]
-[\fI-l\fR] [\fI-p=<page_code>\fR] [\fI-p=<page_code>,<sub_page_code>\fR]
+[\fI-a\fR] [\fI-A\fR] [\fI-c=<page_control>\fR] [\fI-d\fR] [\fI-D\fR]
+[\fI-h\fR] [\fI-l\fR] [\fI-p=<page_code>\fR]
+[\fI-p=<page_code>,<sub_page_code>\fR]
[\fI-r\fR] [\fI-subp=<sub_page_code>\fR] [\fI-v\fR] [\fI-V\fR] [\fI-6\fR]
[\fI-?\fR] [\fI<scsi_device>\fR]
.SH DESCRIPTION
@@ -31,8 +32,16 @@ is power cycled). If this option is not given then current values [0]
are assumed.
.TP
-d
-disable block descriptors. By default MODE SENSE returns in its response
-block descriptors and this parameter can be used to suppress them.
+disable block descriptors. By default, block descriptors (usually one or
+none) are returned in a MODE SENSE response. This option sets the "disable
+block descriptors" (DBD) bit in the cdb which instructs the device not
+to return any block descriptors in its response. Older devices may not
+support this setting and should return an "illegal request" status;
+alternatively they may ignore it.
+.TP
+-D
+disable outputting block descriptors. Irrespective of whether block
+descriptors are present in the response or not, they are not output.
.TP
-h
currently only suppresses the printing out of known page code descriptions
diff --git a/sg_modes.c b/sg_modes.c
index 33116fc4..f2ea9d98 100644
--- a/sg_modes.c
+++ b/sg_modes.c
@@ -24,7 +24,7 @@
*/
-static char * version_str = "0.31 20050121";
+static char * version_str = "0.34 20050223";
#define ME "sg_modes: "
@@ -61,18 +61,15 @@ static const char * scsi_ptype_strs[] = {
"automation/drive interface",
"0x13", "0x14", "0x15", "0x16", "0x17", "0x18",
"0x19", "0x1a", "0x1b", "0x1c", "0x1d",
+ "well known logical unit",
+ "no physical device on this lu",
};
static const char * get_ptype_str(int scsi_ptype)
{
int num = sizeof(scsi_ptype_strs) / sizeof(scsi_ptype_strs[0]);
- if (0x1f == scsi_ptype)
- return "no physical device on this lu";
- else if (0x1e == scsi_ptype)
- return "well known logical unit";
- else
- return (scsi_ptype < num) ? scsi_ptype_strs[scsi_ptype] : "";
+ return (scsi_ptype < num) ? scsi_ptype_strs[scsi_ptype] : "";
}
static const char * transport_proto_arr[] =
@@ -134,6 +131,7 @@ static struct page_code_desc pc_desc_tape[] = {
{0x13, 0x0, "Medium Partition [3]"},
{0x14, 0x0, "Medium Partition [4]"},
{0x1c, 0x0, "Informational exceptions control (tape version)"},
+ {0x1d, 0x0, "Medium configuration"},
};
static struct page_code_desc pc_desc_cddvd[] = {
@@ -154,6 +152,7 @@ static struct page_code_desc pc_desc_smc[] = {
{0x1d, 0x0, "Element address assignment"},
{0x1e, 0x0, "Transport geometry parameters"},
{0x1f, 0x0, "Device capabilities"},
+ {0x1f, 0x1, "Extended device capabilities"},
};
static struct page_code_desc pc_desc_scc[] = {
@@ -377,14 +376,16 @@ static const char * pg_control_str_arr[] = {
static void usage()
{
- printf("Usage: 'sg_modes [-a] [-A] [-c=<page_control] [-d] [-h] [-l]\n\t\t"
+ printf("Usage: 'sg_modes [-a] [-A] [-c=<page_control] [-d] [-D] [-h] "
+ "[-l]\n\t\t"
" [-p=<page_number>[,<sub_page_code>]] [-r]"
"\n\t\t [-subp=<sub_page_code>] [-v] [-V] [-6] [<scsi_device>]'\n"
" where -a get all mode pages supported by device\n"
" -A get all mode pages and subpages supported by device\n"
" -c=<page_control> page control (def: 0 [current],"
" 1 [changeable],\n 2 [default], 3 [saved])\n"
- " -d disable block descriptors\n"
+ " -d disable block descriptors (field in cdb)\n"
+ " -D disable block descriptor output\n"
" -h output in hex\n"
" -l list common page codes for device peripheral type,\n"
" if no device given then assume disk type\n"
@@ -416,6 +417,7 @@ int main(int argc, char * argv[])
int do_all = 0;
int do_all_sub = 0;
int do_dbd = 0;
+ int no_desc_out = 0;
int do_hex = 0;
int do_mode6 = 0; /* Use MODE SENSE(6) instead of MODE SENSE(10) */
int do_list = 0;
@@ -443,6 +445,8 @@ int main(int argc, char * argv[])
pc = u;
} else if (0 == strcmp("-d", argv[k]))
do_dbd = 1;
+ else if (0 == strcmp("-D", argv[k]))
+ no_desc_out = 1;
else if (0 == strcmp("-h", argv[k]))
do_hex = 1;
else if (0 == strcmp("-l", argv[k]))
@@ -584,12 +588,18 @@ int main(int argc, char * argv[])
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, "bad field in cdb (perhaps subpages "
+ "not supported\n");
} else {
res = sg_ll_mode_sense10(sg_fd, 0 /* llbaa */, do_dbd, pc, pg_code,
sub_pg_code, rsp_buff, rsp_buff_size, 1, do_verbose);
if (SG_LIB_CAT_INVALID_OP == res)
fprintf(stderr, ">>>>>> try again with a '-6' "
"switch for a 6 byte MODE SENSE command\n");
+ else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+ fprintf(stderr, "bad field in cdb (perhaps subpages "
+ "not supported\n");
}
if (0 == res) {
int medium_type, specific, headerlen;
@@ -640,31 +650,33 @@ int main(int argc, char * argv[])
if (bd_len + headerlen > rsp_buff_size)
bd_len = rsp_buff_size - headerlen;
}
- printf(" Block descriptor length=%d\n", bd_len);
- if (bd_len > 0) {
- len = 8;
- density_code_off = 0;
- num = bd_len;
- if (longlba) {
- printf("> longlba block descriptors:\n");
- len = 16;
- density_code_off = 8;
- }
- else if (0 == inq_out.peripheral_type) {
- printf("> Direct access device block descriptors:\n");
- density_code_off = 4;
- }
- else
- printf("> General mode parameter block descriptors:\n");
-
- ucp = rsp_buff + headerlen;
- while (num > 0) {
- printf(" Density code=0x%x\n", *(ucp + density_code_off));
- dStrHex((const char *)ucp, len, 1);
- ucp += len;
- num -= len;
+ if (! no_desc_out) {
+ printf(" Block descriptor length=%d\n", bd_len);
+ if (bd_len > 0) {
+ len = 8;
+ density_code_off = 0;
+ num = bd_len;
+ if (longlba) {
+ printf("> longlba block descriptors:\n");
+ len = 16;
+ density_code_off = 8;
+ }
+ else if (0 == inq_out.peripheral_type) {
+ printf("> Direct access device block descriptors:\n");
+ density_code_off = 4;
+ }
+ else
+ printf("> General mode parameter block descriptors:\n");
+
+ ucp = rsp_buff + headerlen;
+ while (num > 0) {
+ printf(" Density code=0x%x\n", *(ucp + density_code_off));
+ dStrHex((const char *)ucp, len, 1);
+ ucp += len;
+ num -= len;
+ }
+ printf("\n");
}
- printf("\n");
}
ucp = rsp_buff + bd_len + headerlen; /* start of mode page(s) */
md_len -= bd_len + headerlen; /* length of mode page(s) */
diff --git a/sg_opcodes.c b/sg_opcodes.c
index bce061bf..ba49a616 100644
--- a/sg_opcodes.c
+++ b/sg_opcodes.c
@@ -13,7 +13,7 @@
#include "sg_cmds.h"
/* A utility program for the Linux OS SCSI subsystem.
-* Copyright (C) 2004 D. Gilbert
+* Copyright (C) 2004-2005 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 @@
*/
-static char * version_str = "0.16 20041210";
+static char * version_str = "0.17 20050309";
#define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */
@@ -43,8 +43,8 @@ static char * version_str = "0.16 20041210";
static int peri_type = 0; /* ugly but not easy to pass to alpha compare */
-// <<<<<<<<<<<<<<< start of test code
-// #define TEST_CODE
+/* <<<<<<<<<<<<<<< start of test code */
+/* #define TEST_CODE */
#ifdef TEST_CODE
@@ -88,7 +88,7 @@ static unsigned char dummy_1_cmd[] = {
static unsigned char dummy_rsmft_r0 = 0xff;
#endif
-// <<<<<<<<<<<<<<< end of test code
+/* <<<<<<<<<<<<<<< end of test code */
/* Report Supported Operation Codes */
@@ -139,8 +139,10 @@ static int do_rsoc(int sg_fd, int rep_opts, int rq_opcode, int rq_servact,
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ sg_chk_n_print3("Report supported operation codes", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
return 0;
default:
if (noisy | verbose) {
@@ -199,8 +201,10 @@ static int do_rstmf(int sg_fd, void * resp, int mx_resp_len, int noisy,
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ sg_chk_n_print3("Report supported task management fns", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
return 0;
default:
if (noisy | verbose) {
@@ -251,18 +255,15 @@ static const char * scsi_ptype_strs[] = {
"automation/driver interface",
"0x13", "0x14", "0x15", "0x16", "0x17", "0x18",
"0x19", "0x1a", "0x1b", "0x1c", "0x1d",
+ "well known logical unit",
+ "no physical device on this lu",
};
static const char * get_ptype_str(int scsi_ptype)
{
int num = sizeof(scsi_ptype_strs) / sizeof(scsi_ptype_strs[0]);
- if (0x1f == scsi_ptype)
- return "no physical device on this lu";
- else if (0x1e == scsi_ptype)
- return "well known logical unit";
- else
- return (scsi_ptype < num) ? scsi_ptype_strs[scsi_ptype] : "";
+ return (scsi_ptype < num) ? scsi_ptype_strs[scsi_ptype] : "";
}
/* returns -1 when left < right, 0 when left == right, else returns 1 */
diff --git a/sg_persist.8 b/sg_persist.8
index e9d1ea89..38c73bb1 100644
--- a/sg_persist.8
+++ b/sg_persist.8
@@ -1,4 +1,4 @@
-.TH SG_PERSIST "8" "December 2004" "sg3_utils-1.12" SG3_UTILS
+.TH SG_PERSIST "8" "February 2005" "sg3_utils-1.13" SG3_UTILS
.SH NAME
sg_persist \- use the Persistent Reservation In and Out SCSI commands
to manipulate registrations and reservations
@@ -22,8 +22,8 @@ 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 21
-dated 22nd September 2004) are sections 5.6 (general information), 6.11 (for
+SCSI Primary Commands document (SPC-3 most recent draft revision 21d
+dated 14th February 2005) 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.
diff --git a/sg_persist.c b/sg_persist.c
index 6511b111..ba7e7578 100644
--- a/sg_persist.c
+++ b/sg_persist.c
@@ -14,7 +14,7 @@
#include "sg_cmds.h"
/* A utility program for the Linux OS SCSI subsystem.
-* Copyright (C) 2004 D. Gilbert
+* Copyright (C) 2004-2005 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 @@
*/
-static char * version_str = "0.21 20041224";
+static char * version_str = "0.22 20050309";
#define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */
@@ -152,8 +152,10 @@ static int do_prin(int sg_fd, int rq_servact, void * resp, int mx_resp_len,
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ sg_chk_n_print3("PRIN, continuing", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
return 0;
default:
if (noisy) {
@@ -213,8 +215,10 @@ static int do_prout(int sg_fd, int rq_servact, int rq_scope,
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ sg_chk_n_print3("PROUT, continuing", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
return 0;
default:
if (noisy) {
@@ -295,18 +299,17 @@ static const char * scsi_ptype_strs[] = {
/* 0x10 */ "bridging expander",
"object based storage",
"automation/driver interface",
+ "0x13", "0x14", "0x15", "0x16", "0x17", "0x18",
+ "0x19", "0x1a", "0x1b", "0x1c", "0x1d",
+ "well known logical unit",
+ "no physical device on this lu",
};
static const char * get_ptype_str(int scsi_ptype)
{
int num = sizeof(scsi_ptype_strs) / sizeof(scsi_ptype_strs[0]);
- if (0x1f == scsi_ptype)
- return "no physical device on this lu";
- else if (0x1e == scsi_ptype)
- return "well known logical unit";
- else
- return (scsi_ptype < num) ? scsi_ptype_strs[scsi_ptype] : "";
+ return (scsi_ptype < num) ? scsi_ptype_strs[scsi_ptype] : "";
}
static const char * pr_type_strs[] = {
diff --git a/sg_prevent.c b/sg_prevent.c
index 03fd1777..d6b5daeb 100644
--- a/sg_prevent.c
+++ b/sg_prevent.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004 Douglas Gilbert.
+ * Copyright (c) 2004-2005 Douglas Gilbert.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -47,7 +47,7 @@
* given SCSI device.
*/
-static char * version_str = "1.01 20041229";
+static char * version_str = "1.02 20050309";
#define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */
#define DEF_TIMEOUT 60000 /* 60,000 millisecs == 60 seconds */
@@ -92,7 +92,7 @@ static void usage()
command not supported */
int sg_ll_prevent(int sg_fd, int prevent, int verbose)
{
- int k;
+ int k, res;
unsigned char pCmdBlk[PREVENT_REMOVAL_CMDLEN] =
{PREVENT_REMOVAL_CMD, 0, 0, 0, 0, 0};
unsigned char sense_b[SENSE_BUFF_LEN];
@@ -126,15 +126,19 @@ int sg_ll_prevent(int sg_fd, int prevent, int verbose)
safe_strerror(errno));
return -1;
}
- switch (sg_err_category3(&io_hdr)) {
- case SG_LIB_CAT_CLEAN:
+ res = sg_err_category3(&io_hdr);
+ switch (res) {
case SG_LIB_CAT_RECOVERED:
+ sg_chk_n_print3("Prevent allow medium removal", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
return 0;
case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_ILLEGAL_REQ:
if (verbose > 1)
sg_chk_n_print3("Prevent allow medium removal command problem",
&io_hdr);
- return SG_LIB_CAT_INVALID_OP;
+ return res;
default:
sg_chk_n_print3("Prevent allow medium removal command problem",
&io_hdr);
diff --git a/sg_rbuf.c b/sg_rbuf.c
index 4e953019..140eec80 100644
--- a/sg_rbuf.c
+++ b/sg_rbuf.c
@@ -55,7 +55,7 @@
#define ME "sg_rbuf: "
-static char * version_str = "4.78 20050106";
+static char * version_str = "4.79 20050309";
static void usage()
{
@@ -184,10 +184,10 @@ int main(int argc, char * argv[])
/* now for the error processing */
switch (sg_err_category3(&io_hdr)) {
- case SG_LIB_CAT_CLEAN:
- break;
case SG_LIB_CAT_RECOVERED:
- printf("Recovered error on READ BUFFER descriptor, continuing\n");
+ sg_chk_n_print3("READ BUFFER descriptor, continuing", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
break;
default: /* won't bother decoding other categories */
sg_chk_n_print3("READ BUFFER descriptor error", &io_hdr);
@@ -297,7 +297,7 @@ int main(int argc, char * argv[])
case SG_LIB_CAT_CLEAN:
break;
case SG_LIB_CAT_RECOVERED:
- printf("Recovered error on READ BUFFER data, continuing\n");
+ sg_chk_n_print3("READ BUFFER data, continuing", &io_hdr);
break;
default: /* won't bother decoding other categories */
sg_chk_n_print3("READ BUFFER data error", &io_hdr);
diff --git a/sg_read.8 b/sg_read.8
index c44e0ff4..e0cf29d3 100644
--- a/sg_read.8
+++ b/sg_read.8
@@ -1,17 +1,26 @@
-.TH SG_READ "8" "September 2004" "sg3_utils-1.09" SG3_UTILS
+.TH SG_READ "8" "March 2005" "sg3_utils-1.13" SG3_UTILS
.SH NAME
sg_read \- read blocks of data continually from same offset
.SH SYNOPSIS
-.B sg_read
-[\fIOPTION\fR]...
+.B sg_dd
+[\fIblk_sgio=0|1\fR] [\fIbpt=<n>\fR] [\fIbs=<n>\fR] [\fIcdbsz=6|10|12|16\fR]
+\fIcount=<n>\fR [\fIdio=0|1\fR] [\fIif=<ifile>\fR] [\fIodir=0|1\fR]
+[\fIskip=<n>\fR] [\fItime=<n>\fR] [\fI--version\fR]
.SH DESCRIPTION
.\" Add any additional description here
.PP
-Read data from a Linux SCSI generic (sg) device, a raw devices or
-a normal file with each read command issued to the same offset. This
-will test (or time) disk caching and/or SCSI (or some other) bus
+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 will test (or time) disk caching and/or SCSI (or some other) bus
throughput.
.TP
+blk_sgio=0 | 1
+The default action of this utility is to use the Unix read() command when
+the <ifile> is a block device. In lk 2.6 many block devices can handle
+SCSI commands issued via the SG_IO ioctl. So when this option is set
+the SG_IO ioctl sends SCSI READ commands to <ifile> if it is a block
+device.
+.TP
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
@@ -20,11 +29,12 @@ starts at the same offset (as given by skip or 0).
bs=BYTES
this
.B must
-be the block size of the physical device (defaults to 512)
+be the block size of the physical device (defaults to 512) if SCSI commands
+are being issued to <ifile>.
.TP
cdbsz=6 | 10 | 12 | 16
size of SCSI READ commands issued on sg device names.
-Default is 10 byte SCSI READ command blocks
+Default is 10 byte SCSI READ cdbs
.TP
count=BLOCKS
read this number of blocks. This argument must be given
@@ -32,26 +42,36 @@ read this number of blocks. This argument must be given
dio=0 | 1
default is 0 which selects indirect IO. Value of 1 attempts direct
IO which, if not available, falls back to indirect IO and notes this
-at completion. If direct IO is selected and /proc/scsi/sg/allow_dio
+at completion. This option is only actice if <ifile> is an sg device.
+If direct IO is selected and /proc/scsi/sg/allow_dio
has the value of 0 then a warning is issued (and indirect IO is performed)
.TP
-if=FILE
-read from this FILE. This argument must be given
+if=<ifile>
+read from this <ifile>. This argument must be given. If the <ifile> is a
+normal file then it must be seekable (if (count > bpt) or skip is given).
+Hence stdin is not acceptable (and giving "-" as the <ifile> argument is
+reported as an error).
.TP
mmap= 0 | 1
default is 0 which selects indirect IO. Value of 1 causes memory mapped
-IO to be performed. Selecting both dio and mmap is an error
+IO to be performed. Selecting both dio and mmap is an error. This option
+is only active if <ifile> is an sg device.
+.TP
+odir= 0 | 1
+when set opens an <ifile> which is a block device with an additional
+O_DIRECT flag. Active when blk_sgio is either 0 or 1. The default value
+is 0 (i.e. don't open block devices O_DIRECT).
.TP
skip=BLOCKS
all read operations will start offset by BLOCKS bs-sized blocks
from the start of input (file or device)
.TP
-time=0 | 1 | 2
+time=<n>
When 0 (default) doesn't perform timing.
when 1, times transfer and does throughput calculation, starting at the
first issued command until completion. When 2, times transfer and does
throughput calculation, starting at the second issued command until
-completion
+completion. When 3 times from third command, etc.
.PP
The input file must be either a sg device, a raw device or a normal file.
A raw device must be bound to a block device prior to using sg_raw.
@@ -59,10 +79,16 @@ See
.B raw(8)
for more information about binding raw devices.
.PP
-BYTES and BLOCKS may be followed by the following multiplicative suffixes:
-c C *1; b B *512; k *1,024; K *1,000; m *1,048,576; M *1,000,000;
-g *1,073,741,824; and G *1,000,000,000 . Alternatively a hex number may
-be given, prefixed by either 0x or 0X.
+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; g G Gib *(2**30); GB *(10**9). Also a suffix of
+the form "x<n>" multiplies the leading number by <n>. These multiplicative
+suffixes are compatible with GNU's dd command (since 2002) which claims
+compliance with SI and with IEC 60027-2.
+.PP
+Alternatively numerical values can be given in hexadecimal preceded by
+either "0x" or "0X". When hex numbers are given multipliers cannot be
+used.
.PP
Data usually gets to the user space in a 2 stage process: first the
SCSI adapter DMAs into kernel buffers and then the sg driver copies
@@ -78,7 +104,7 @@ configuration change to activate it. This is typically done with
Let us assume that /dev/sg0 is a disk and we wish to time the disk's
cache performance.
.PP
- sg_read if=/dev/sg0 bs=512 count=1M mmap=1 time=2
+ sg_read if=/dev/sg0 bs=512 count=1MB mmap=1 time=2
.PP
This command will continually read 128 512 byte blocks from block 0.
The "128" is the default value for "bpt" while "block 0" is chosen
diff --git a/sg_read.c b/sg_read.c
index def2fba8..78e3c12f 100644
--- a/sg_read.c
+++ b/sg_read.c
@@ -21,7 +21,7 @@
#include "llseek.h"
/* A utility program for the Linux OS SCSI generic ("sg") device driver.
-* Copyright (C) 2001 - 2004 D. Gilbert
+* Copyright (C) 2001 - 2005 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)
@@ -31,24 +31,16 @@
or cdrom) and discards that data. Its primary goal is to time
multiple reads all from the same logical address. Its interface
is a subset of another member of this package: sg_dd which is a
- "dd" variant. The input file can be a scsi generic device, a raw device
- or a seekable file. Streams such as stdin are not acceptable.
+ "dd" variant. The input file can be a scsi generic device, a block device,
+ a raw device or a seekable file. Streams such as stdin are not acceptable.
The block size ('bs') is assumed to be 512 if not given.
- Various arguments can take a multiplier suffix:
- 'c','C' *1 'b','B' *512 'k' *1024 'K' *1000
- 'm' *(1024^2) 'M' *(1000^2) 'g' *(1024^3) 'G' *(1000^3)
-
- The "bpt" (blocks per transfer) argument controls the maximum number
- of blocks in each transfer. The default value is 128.
- For example if "bs=512" and "bpt=32" then a maximum of 32 blocks (16 KiB
- in this case) is read from the sg device in a single SCSI command.
This version should compile with Linux sg drivers with version numbers
>= 30000 . For mmap-ed IO the sg version number >= 30122 .
*/
-static const char * version_str = "1.01 20041011";
+static const char * version_str = "1.04 20050309";
#define DEF_BLOCK_SIZE 512
#define DEF_BLOCKS_PER_TRANSFER 128
@@ -73,6 +65,7 @@ static const char * version_str = "1.01 20041011";
#define FT_OTHER 0 /* filetype other than sg or raw device */
#define FT_SG 1 /* filetype is sg char device */
#define FT_RAW 2 /* filetype is raw char device */
+#define FT_BLOCK 4 /* filetype is block device */
static int sum_of_resids = 0;
@@ -140,30 +133,38 @@ int dd_filetype(const char * filename)
return FT_RAW;
else if (SCSI_GENERIC_MAJOR == major(st.st_rdev))
return FT_SG;
- }
+ } else if (S_ISBLK(st.st_mode))
+ return FT_BLOCK;
return FT_OTHER;
}
void usage()
{
fprintf(stderr, "Usage: "
- "sg_read if=<infile> [skip=<num>] [bs=<num>] [bpt=<num>] "
- "count=<num>\n"
- " [dio=<num>] [mmap=<num>] [time=<num>]"
- " [cdbsz=<6|10|12|16>]\n"
- " 'if' is an sg or raw device, or a seekable file (not stdin)\n"
- " 'bs' must match sector size (when 'if' is sg device) "
- "(def=512)\n"
- " 'skip' each transfer starts at this logical address (def=0)\n"
- " 'count' total bytes read will be 'bs'*'count' (if no error)\n"
- " 'bpt' is blocks_per_transfer (default is 128, or 64 KiB for "
+ "sg_read if=<infile> count=<num> [blk_sgio=0|1] [bpt=<num>] "
+ "[bs=<num>]\n"
+ " [cdbsz=6|10|12|16] [dio=0|1] [mmap=0|1] "
+ "[odir=0|1]\n"
+ " [skip=<num>] [time=<num>] [--version]\n"
+ " blk_sgio 0->normal IO for block devices, 1->SCSI commands via "
+ "SG_IO\n"
+ " bpt is blocks_per_transfer (default is 128, or 64 KiB for "
"def 'bs')\n"
- " 'dio' is direct IO, 1->attempt, 0->indirect IO (def)\n"
- " 'mmap' is mmap-ed IO, 1->perform, 0->indirect IO (def)\n"
- " 'time' 0->do nothing(def), 1->time from 1st cmd, 2->time "
- "from 2nd cmd\n"
- " 'cdbsz' size of SCSI READ command (default is 10)\n");
- fprintf(stderr, "\nVersion: %s\n", version_str);
+ " bs must match sector size if 'if' accessed via SCSI "
+ "commands (def=512)\n"
+ " cdbsz size of SCSI READ command (default is 10)\n"
+ " count total bytes read will be 'bs'*'count' (if no error)\n"
+ " dio 1-> attempt direct IO on sg device, 0->indirect IO "
+ "(def)\n"
+ " if an sg, block or raw device, or a seekable file (not "
+ "stdin)\n"
+ " mmap 1->perform mmaped IO on sg device, 0->indirect IO "
+ "(def)\n"
+ " odir 1->open block device O_DIRECT, 0->don't (def)\n"
+ " skip each transfer starts at this logical address (def=0)\n"
+ " time 0->do nothing(def), 1->time from 1st cmd, 2->time "
+ "from 2nd, ...\n"
+ " --version print version number then exit\n");
}
int sg_build_scsi_cdb(unsigned char * cdbp, int cdb_sz, unsigned int blocks,
@@ -279,11 +280,10 @@ int sg_bread(int sg_fd, unsigned char * buff, int blocks, int from_block,
}
switch (sg_err_category3(&io_hdr)) {
- case SG_LIB_CAT_CLEAN:
- break;
case SG_LIB_CAT_RECOVERED:
- fprintf(stderr, "Recovered error while reading block=%d, num=%d\n",
- from_block, blocks);
+ sg_chk_n_print3("reading, continuing", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
break;
case SG_LIB_CAT_MEDIA_CHANGED:
return 2;
@@ -317,16 +317,17 @@ int main(int argc, char * argv[])
char inf[INF_SZ];
int in_type = FT_OTHER;
int do_dio = 0;
+ int do_odir = 0;
+ int do_blk_sgio = 0;
int do_mmap = 0;
int do_time = 0;
int scsi_cdbsz = DEF_SCSI_CDBSZ;
int dio_incomplete = 0;
int res, k, t, buf_sz, dio_tmp, iters, orig_count;
- int infd, blocks;
+ int infd, blocks, flags, blocks_per;
unsigned char * wrkBuff = NULL;
unsigned char * wrkPos;
char ebuff[EBUFF_SZ];
- int blocks_per;
struct timeval start_tm, end_tm;
size_t psz = getpagesize();
@@ -350,15 +351,31 @@ int main(int argc, char * argv[])
*buf++ = '\0';
if (strcmp(key,"if") == 0)
strncpy(inf, buf, INF_SZ);
- else if (0 == strcmp(key,"bs"))
+ else if (0 == strcmp(key,"bs")) {
bs = sg_get_num(buf);
- else if (0 == strcmp(key,"bpt"))
+ if (-1 == bs) {
+ fprintf(stderr, ME "bad argument to 'bs'\n");
+ return 1;
+ }
+ } else if (0 == strcmp(key,"bpt")) {
bpt = sg_get_num(buf);
- else if (0 == strcmp(key,"skip"))
+ if (-1 == bpt) {
+ fprintf(stderr, ME "bad argument to 'bpt'\n");
+ return 1;
+ }
+ } else if (0 == strcmp(key,"skip")) {
skip = sg_get_num(buf);
- else if (0 == strcmp(key,"count"))
+ if (-1 == skip) {
+ fprintf(stderr, ME "bad argument to 'skip'\n");
+ return 1;
+ }
+ } else if (0 == strcmp(key,"count")) {
dd_count = sg_get_num(buf);
- else if (0 == strcmp(key,"dio"))
+ if (-1 == dd_count) {
+ fprintf(stderr, ME "bad argument to 'count'\n");
+ return 1;
+ }
+ } else if (0 == strcmp(key,"dio"))
do_dio = sg_get_num(buf);
else if (0 == strcmp(key,"mmap"))
do_mmap = sg_get_num(buf);
@@ -366,7 +383,14 @@ int main(int argc, char * argv[])
do_time = sg_get_num(buf);
else if (0 == strcmp(key,"cdbsz"))
scsi_cdbsz = sg_get_num(buf);
- else {
+ else if (0 == strcmp(key,"blk_sgio"))
+ do_blk_sgio = sg_get_num(buf);
+ else if (0 == strcmp(key,"odir"))
+ do_odir = sg_get_num(buf);
+ else if (0 == strncmp(key, "--vers", 6)) {
+ fprintf(stderr, ME ": %s\n", version_str);
+ return 0;
+ } else {
fprintf(stderr, "Unrecognized argument '%s'\n", key);
usage();
return 1;
@@ -407,30 +431,48 @@ int main(int argc, char * argv[])
usage();
return 1;
}
+ if (0 == strcmp("-", inf)) {
+ fprintf(stderr, "'-' (stdin) invalid as <filename>\n");
+ usage();
+ return 1;
+ }
in_type = dd_filetype(inf);
- if (FT_SG == in_type) {
- if ((infd = open(inf, O_RDWR)) < 0) {
- snprintf(ebuff, EBUFF_SZ,
- ME "could not open %s for sg reading", inf);
- perror(ebuff);
- return 1;
- }
- t = bs * bpt;
- if ((do_mmap) && (0 != (t % psz)))
- t = ((t / psz) + 1) * psz; /* round up to next pagesize */
- res = ioctl(infd, SG_SET_RESERVED_SIZE, &t);
- if (res < 0)
- perror(ME "SG_SET_RESERVED_SIZE error");
- 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;
+ if ((FT_BLOCK & in_type) && do_blk_sgio)
+ in_type |= FT_SG;
+
+ if (FT_SG & in_type) {
+ flags = O_RDWR;
+ if ((do_odir && (FT_BLOCK & in_type)))
+ flags |= O_DIRECT;
+ if ((infd = open(inf, flags)) < 0) {
+ flags = O_RDONLY;
+ if ((do_odir && (FT_BLOCK & in_type)))
+ flags |= O_DIRECT;
+ if ((infd = open(inf, flags)) < 0) {
+ snprintf(ebuff, EBUFF_SZ,
+ ME "could not open %s for sg reading", inf);
+ perror(ebuff);
+ return 1;
+ }
}
- if (do_mmap && (t < 30122)) {
- fprintf(stderr, ME "mmap-ed IO needs a sg driver version "
- ">= 3.1.22\n");
- return 1;
+ if (! (FT_BLOCK & in_type)) {
+ t = bs * bpt;
+ if ((do_mmap) && (0 != (t % psz)))
+ t = ((t / psz) + 1) * psz; /* round up to next pagesize */
+ res = ioctl(infd, SG_SET_RESERVED_SIZE, &t);
+ if (res < 0)
+ perror(ME "SG_SET_RESERVED_SIZE error");
+ 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;
+ }
+ if (do_mmap && (t < 30122)) {
+ fprintf(stderr, ME "mmap-ed IO needs a sg driver version "
+ ">= 3.1.22\n");
+ return 1;
+ }
}
}
else {
@@ -462,7 +504,7 @@ int main(int argc, char * argv[])
return 0;
orig_count = dd_count;
- if (do_dio || (FT_RAW == in_type)) {
+ if (do_dio || (FT_RAW & in_type)) {
wrkBuff = malloc(bs * bpt + psz);
if (0 == wrkBuff) {
fprintf(stderr, "Not enough user memory for raw\n");
@@ -501,7 +543,7 @@ int main(int argc, char * argv[])
if ((do_time > 0) && (iters == (do_time - 1)))
gettimeofday(&start_tm, NULL);
blocks = (dd_count > blocks_per) ? blocks_per : dd_count;
- if (FT_SG == in_type) {
+ if (FT_SG & in_type) {
dio_tmp = do_dio;
res = sg_bread(infd, wrkPos, blocks, skip, bs, scsi_cdbsz,
&dio_tmp, do_mmap);
@@ -626,7 +668,7 @@ int main(int argc, char * argv[])
fprintf(stderr, "Some error occurred,");
res = 2;
}
- if (FT_SG == in_type)
+ if (FT_SG & in_type)
print_stats(iters);
else
print_stats(0);
diff --git a/sg_read_long.8 b/sg_read_long.8
index b98a5335..eec7d3b1 100644
--- a/sg_read_long.8
+++ b/sg_read_long.8
@@ -1,4 +1,4 @@
-.TH SG_READ_LONG "8" "January 2005" "sg3_utils-1.12" SG3_UTILS
+.TH SG_READ_LONG "8" "March 2005" "sg3_utils-1.13" SG3_UTILS
.SH NAME
sg_read_long \- send the scsi command read long
.SH SYNOPSIS
@@ -10,8 +10,11 @@ sg_read_long \- send the scsi command read long
.\" Add any additional description here
.PP
Send READ LONG command to a Linux SCSI device. The read
-buffer is output in hex and ASCII to stdout or placed in a file..
-.TP
+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
--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
@@ -42,22 +45,28 @@ print the version string and then exit.
the transfer length in bytes (default to 520). If the given value (or the
default) does not match the "long" block size of the device, the
appropriate xfer_len value is derived from the error response and
-printed (to stderr).
+printed (to stderr). The idea is that the user will retry this utility
+with the correct transfer length.
.PP
The lba and xfer_len numerical arguments may be followed by the following
multiplicative suffixes:
-c C *1; b B *512; k *1,024; K *1,000; m *1,048,576; M *1,000,000;
-g *1,073,741,824; and G *1,000,000,000
+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; g G GiB *1,073,741,824; and GB *1,000,000,000 . Also a suffix
+of the form "x<n>" multiplies the leading number by <n>.
.PP
Alternatively numerical values can be given in hexadecimal preceded by
either "0x" or "0X". When hex numbers are given multipliers cannot be
used.
+.PP
+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 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-2005 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_read_long.c b/sg_read_long.c
index fb45dbbb..0cd080d5 100644
--- a/sg_read_long.c
+++ b/sg_read_long.c
@@ -25,10 +25,12 @@
the sector data and the ECC bytes.
*/
-static char * version_str = "1.04 20050118";
+static char * version_str = "1.05 20050309";
#define READ_LONG_OPCODE 0x3E
#define READ_LONG_CMD_LEN 10
+#define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */
+#define MAX_XFER_LEN 10000
#define ME "sg_read_long: "
@@ -61,7 +63,7 @@ static void usage()
" --verbose|-v increase verbosity\n"
" --version|-V print version string and"
" exit\n"
- " --xfer_len=<num>|-x <num> transfer length (<1000)"
+ " --xfer_len=<num>|-x <num> transfer length (< 10000)"
" default 520\n"
);
}
@@ -73,7 +75,7 @@ static int info_offset(unsigned char * sensep, int sb_len)
if (sb_len < 8)
return 0;
resp_code = (0x7f & sensep[0]);
- if (resp_code>= 0x72) { /* descriptor format */
+ if (resp_code >= 0x72) { /* descriptor format */
unsigned long long ull = 0;
/* if Information field, fetch it; contains signed number */
@@ -88,7 +90,7 @@ static int info_offset(unsigned char * sensep, int sb_len)
return 0;
}
-static int has_ili(unsigned char * sensep, int sb_len)
+static int has_blk_ili(unsigned char * sensep, int sb_len)
{
int resp_code;
const unsigned char * cup;
@@ -96,7 +98,7 @@ static int has_ili(unsigned char * sensep, int sb_len)
if (sb_len < 8)
return 0;
resp_code = (0x7f & sensep[0]);
- if (resp_code>= 0x72) { /* descriptor format */
+ if (resp_code >= 0x72) { /* descriptor format */
/* find block command descriptor */
if ((cup = sg_scsi_sense_desc_find(sensep, sb_len, 0x5)))
return ((cup[3] & 0x20) ? 1 : 0);
@@ -105,27 +107,134 @@ static int has_ili(unsigned char * sensep, int sb_len)
return 0;
}
+/* Invokes a SCSI READ LONG (10) command. Return of 0 -> success,
+ * 1 -> ILLEGAL REQUEST with info field written to offsetp,
+ * SG_LIB_CAT_INVALID_OP -> Verify(10) not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
+static int sg_ll_read_long10(int sg_fd, int correct, unsigned long lba,
+ void * data_out, int xfer_len, int * offsetp,
+ int verbose)
+{
+ int k, res, offset;
+ unsigned char readLongCmdBlk[READ_LONG_CMD_LEN];
+ struct sg_io_hdr io_hdr;
+ struct sg_scsi_sense_hdr ssh;
+ unsigned char sense_buffer[SENSE_BUFF_LEN];
+
+ memset(readLongCmdBlk, 0, READ_LONG_CMD_LEN);
+ readLongCmdBlk[0] = READ_LONG_OPCODE;
+ 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);
+
+ if (verbose) {
+ fprintf(stderr, " Read Long (10) cmd: ");
+ for (k = 0; k < READ_LONG_CMD_LEN; ++k)
+ fprintf(stderr, "%02x ", readLongCmdBlk[k]);
+ fprintf(stderr, "\n");
+ }
+
+ memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
+ io_hdr.interface_id = 'S';
+ io_hdr.cmd_len = sizeof(readLongCmdBlk);
+ io_hdr.mx_sb_len = sizeof(sense_buffer);
+ io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+ io_hdr.dxfer_len = xfer_len;
+ io_hdr.dxferp = data_out;
+ io_hdr.cmdp = readLongCmdBlk;
+ io_hdr.sbp = sense_buffer;
+ io_hdr.timeout = 60000; /* 60000 millisecs == 60 seconds */
+
+ if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+ perror(ME "SG_IO ioctl READ LONG(10) error");
+ return -1;
+ }
+
+ /* now for the error processing */
+ res = sg_err_category3(&io_hdr);
+ switch (res) {
+ case SG_LIB_CAT_RECOVERED:
+ sg_chk_n_print3("READ LONG(10), continuing", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
+ return 0;
+ case SG_LIB_CAT_INVALID_OP:
+ if (verbose > 1)
+ sg_chk_n_print3("READ LONG(10) command problem", &io_hdr);
+ return res;
+ default:
+ if (verbose > 1)
+ sg_chk_n_print3("READ LONG(10) sense", &io_hdr);
+ if ((sg_normalize_sense(&io_hdr, &ssh)) &&
+ (ssh.sense_key == ILLEGAL_REQUEST) &&
+ ((offset = info_offset(io_hdr.sbp, io_hdr.sb_len_wr)))) {
+ if (has_blk_ili(io_hdr.sbp, io_hdr.sb_len_wr)) {
+ if (offsetp)
+ *offsetp = offset;
+ return 1;
+ } else if (verbose)
+ fprintf(stderr, " info field [%d], but ILI clear ??\n",
+ offset);
+ }
+ if (SG_LIB_CAT_ILLEGAL_REQ == res)
+ return res;
+ return -1;
+ }
+}
+
+/* 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)
+{
+ int offset, res;
+
+ res = sg_ll_read_long10(sg_fd, correct, lba, data_out, xfer_len,
+ &offset, verbose);
+ switch (res) {
+ case 0:
+ return 0;
+ case 1:
+ fprintf(stderr, "<<< device indicates 'xfer_len' should be %d "
+ ">>>\n", xfer_len - offset);
+ return -1;
+ case SG_LIB_CAT_INVALID_OP:
+ fprintf(stderr, " SCSI READ LONG (10) command not supported\n");
+ return -1;
+ case SG_LIB_CAT_ILLEGAL_REQ:
+ fprintf(stderr, " SCSI READ LONG (10) command, bad field in cdb\n");
+ return -1;
+ default:
+ fprintf(stderr, " SCSI READ LONG (10) command error\n");
+ return -1;
+ }
+}
+
+
int main(int argc, char * argv[])
{
- int sg_fd, outfd, res, c, k, offset;
- unsigned char readLongCmdBlk [READ_LONG_CMD_LEN];
+ int sg_fd, outfd, res, c;
unsigned char * readLongBuff = NULL;
void * rawp = NULL;
- unsigned char sense_buffer[32];
int correct = 0;
int xfer_len = 520;
unsigned int lba = 0;
int verbose = 0;
int got_stdout;
char device_name[256];
- char file_name[256];
+ char out_fname[256];
char ebuff[EBUFF_SZ];
- struct sg_io_hdr io_hdr;
- struct sg_scsi_sense_hdr ssh;
int ret = 1;
memset(device_name, 0, sizeof device_name);
- memset(file_name, 0, sizeof file_name);
+ memset(out_fname, 0, sizeof out_fname);
while (1) {
int option_index = 0;
@@ -150,7 +259,7 @@ int main(int argc, char * argv[])
}
break;
case 'o':
- strncpy(file_name, optarg, sizeof(file_name));
+ strncpy(out_fname, optarg, sizeof(out_fname));
break;
case 'v':
++verbose;
@@ -191,9 +300,9 @@ int main(int argc, char * argv[])
usage();
return 1;
}
- if (xfer_len >= 1000){
- fprintf(stderr, "xfer_len (%d) is out of range ( < 1000)\n",
- xfer_len);
+ 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;
}
@@ -204,97 +313,40 @@ int main(int argc, char * argv[])
return 1;
}
- if (NULL == (rawp = malloc(1000))) {
+ if (NULL == (rawp = malloc(MAX_XFER_LEN))) {
fprintf(stderr, ME "out of memory (query)\n");
close(sg_fd);
return 1;
}
readLongBuff = rawp;
- memset(rawp, 0x0, 1000);
- memset(readLongCmdBlk, 0, READ_LONG_CMD_LEN);
- readLongCmdBlk[0] = READ_LONG_OPCODE;
- 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);
+ memset(rawp, 0x0, MAX_XFER_LEN);
fprintf(stderr, ME "issue read long 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);
- if (verbose) {
- fprintf(stderr, " Read Long (10) cmd: ");
- for (k = 0; k < READ_LONG_CMD_LEN; ++k)
- fprintf(stderr, "%02x ", readLongCmdBlk[k]);
- fprintf(stderr, "\n");
- }
-
- memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
- io_hdr.interface_id = 'S';
- io_hdr.cmd_len = sizeof(readLongCmdBlk);
- io_hdr.mx_sb_len = sizeof(sense_buffer);
- io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
- io_hdr.dxfer_len = xfer_len;
- io_hdr.dxferp = readLongBuff;
- io_hdr.cmdp = readLongCmdBlk;
- io_hdr.sbp = sense_buffer;
- io_hdr.timeout = 60000; /* 60000 millisecs == 60 seconds */
- /* do normal IO to find RB size (not dio or mmap-ed at this stage) */
-
- if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
- perror(ME "SG_IO ioctl READ LONG error");
+ if (process_read_long(sg_fd, correct, lba, readLongBuff, xfer_len,
+ verbose))
goto err_out;
- }
- /* now for the error processing */
- switch (sg_err_category3(&io_hdr)) {
- case SG_LIB_CAT_CLEAN:
- break;
- case SG_LIB_CAT_RECOVERED:
- fprintf(stderr, "Recovered error on READ LONG command, "
- "continuing\n");
- break;
- default: /* won't bother decoding other categories */
- if ((sg_normalize_sense(&io_hdr, &ssh)) &&
- (ssh.sense_key == ILLEGAL_REQUEST) &&
- ((offset = info_offset(io_hdr.sbp, io_hdr.sb_len_wr)))) {
- if (verbose)
- sg_chk_n_print3("READ LONG command problem", &io_hdr);
- fprintf(stderr, "<<< device indicates 'xfer_len' should be %d "
- ">>>\n", xfer_len - offset);
- if (! has_ili(io_hdr.sbp, io_hdr.sb_len_wr))
- fprintf(stderr, " [Invalid Length Indication (ILI) flag "
- "expected but not found]\n");
- goto err_out;
- }
- sg_chk_n_print3("READ LONG command problem", &io_hdr);
- goto err_out;
- }
- if ('\0' == file_name[0])
+ if ('\0' == out_fname[0])
dStrHex(rawp, xfer_len, 0);
else {
- got_stdout = (0 == strcmp(file_name, "-")) ? 1 : 0;
+ got_stdout = (0 == strcmp(out_fname, "-")) ? 1 : 0;
if (got_stdout)
outfd = 1;
else {
- if ((outfd = open(file_name, O_WRONLY | O_CREAT | O_TRUNC,
+ if ((outfd = open(out_fname, O_WRONLY | O_CREAT | O_TRUNC,
0666)) < 0) {
snprintf(ebuff, EBUFF_SZ,
- ME "could not open %s for writing", file_name);
+ ME "could not open %s for writing", out_fname);
perror(ebuff);
goto err_out;
}
}
res = write(outfd, readLongBuff, xfer_len);
if (res < 0) {
- snprintf(ebuff, EBUFF_SZ, ME "couldn't write to %s", file_name);
+ snprintf(ebuff, EBUFF_SZ, ME "couldn't write to %s", out_fname);
perror(ebuff);
goto err_out;
}
diff --git a/sg_readcap.c b/sg_readcap.c
index 0a696fa1..c7164307 100644
--- a/sg_readcap.c
+++ b/sg_readcap.c
@@ -13,7 +13,7 @@
/* This code is does a SCSI READ CAPACITY command on the given device
and outputs the result.
-* Copyright (C) 1999 - 2004 D. Gilbert
+* Copyright (C) 1999 - 2005 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)
@@ -27,7 +27,7 @@
*/
-static char * version_str = "3.68 20041106";
+static char * version_str = "3.69 20050211";
#define ME "sg_readcap: "
@@ -144,7 +144,7 @@ int main(int argc, char * argv[])
printf(" Last block address=%u (0x%x), Number of "
"blocks=%u\n", last_blk_addr, last_blk_addr,
last_blk_addr + 1);
- printf(" Block size = %u bytes\n", block_size);
+ printf(" Block size=%u bytes\n", block_size);
if (! pmi) {
unsigned long long total_sz = last_blk_addr + 1;
double sz_mb, sz_gb;
@@ -159,8 +159,7 @@ int main(int argc, char * argv[])
total_sz, sz_mb, sz_gb);
}
}
- }
- else if (SG_LIB_CAT_INVALID_OP == res) {
+ } else if (SG_LIB_CAT_INVALID_OP == res) {
do16 = 1;
close(sg_fd);
if ((sg_fd = open(file_name, O_RDWR | O_NONBLOCK)) < 0) {
@@ -172,7 +171,9 @@ int main(int argc, char * argv[])
if (verbose)
fprintf(stderr, "READ CAPACITY (10) not supported, trying "
"READ CAPACITY (16)\n");
- } else if (verbose)
+ } else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+ fprintf(stderr, "bad field in READ CAPACITY (10) cdb\n");
+ else if (verbose)
fprintf(stderr, "READ CAPACITY (10) failed [res=%d]\n", res);
}
if (do16) {
@@ -187,7 +188,7 @@ int main(int argc, char * argv[])
(resp_buff[10] << 8) | resp_buff[11]);
printf("Read Capacity results:\n");
printf(" Protection: prot_en=%d, rto_en=%d\n",
- (resp_buff[12] & 0x1), ((resp_buff[12] & 0x2) ? 1 : 0));
+ !!(resp_buff[12] & 0x1), !!(resp_buff[12] & 0x2));
if (pmi)
printf(" PMI mode: given lba=0x%llx, last block before "
"delay=0x%llx\n", llba, llast_blk_addr);
@@ -195,7 +196,7 @@ int main(int argc, char * argv[])
printf(" Last block address=%llu (0x%llx), Number of "
"blocks=%llu\n", llast_blk_addr, llast_blk_addr,
llast_blk_addr + 1);
- printf(" Block size = %u bytes\n", block_size);
+ printf(" Block size=%u bytes\n", block_size);
if (! pmi) {
unsigned long long total_sz = llast_blk_addr + 1;
double sz_mb, sz_gb;
@@ -212,6 +213,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_ILLEGAL_REQ == res)
+ fprintf(stderr, "bad field in READ CAPACITY (10) cdb\n");
else if (verbose)
fprintf(stderr, "READ CAPACITY (16) failed [res=%d]\n", res);
}
diff --git a/sg_reassign.c b/sg_reassign.c
index 4a7c1b22..43de70d4 100644
--- a/sg_reassign.c
+++ b/sg_reassign.c
@@ -50,7 +50,7 @@
* vendor specific data is written.
*/
-static char * version_str = "1.00 20050109";
+static char * version_str = "1.01 20050309";
#define ME "sg_reassign: "
@@ -107,7 +107,8 @@ static void usage()
}
/* Invokes a SCSI REASSIGN BLOCKS command. Return of 0 -> success,
- * SG_LIB_CAT_INVALID_OP -> invalid opcode, -1 -> other failure */
+ * SG_LIB_CAT_INVALID_OP -> invalid opcode,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */
int sg_ll_reassign_blocks(int sg_fd, int longlba, int longlist, void * paramp,
int param_len, int noisy, int verbose, int dummy)
{
@@ -153,10 +154,13 @@ int sg_ll_reassign_blocks(int sg_fd, int longlba, int longlist, void * paramp,
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ sg_chk_n_print3("Reassign blocks, continuing", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
return 0;
case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_ILLEGAL_REQ:
if (verbose > 1)
sg_chk_n_print3("Reassign blocks error", &io_hdr);
return res;
@@ -442,6 +446,9 @@ int main(int argc, char * argv[])
if (SG_LIB_CAT_INVALID_OP == res) {
fprintf(stderr, "REASSIGN BLOCKS not supported\n");
goto err_out;
+ } else if (SG_LIB_CAT_INVALID_OP == 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;
diff --git a/sg_requests.c b/sg_requests.c
index 803e6ba0..8fa2acb1 100644
--- a/sg_requests.c
+++ b/sg_requests.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004 Douglas Gilbert.
+ * Copyright (c) 2004-2005 Douglas Gilbert.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -47,7 +47,7 @@
* This program issues the SCSI command REQUEST SENSE to the given SCSI device.
*/
-static char * version_str = "1.05 20041229";
+static char * version_str = "1.06 20050210";
#define REQUEST_SENSE_BUFF_LEN 252
@@ -156,6 +156,10 @@ int main(int argc, char * argv[])
ret = 0;
} 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");
res = close(sg_fd);
if (res < 0) {
diff --git a/sg_rtpg.c b/sg_rtpg.c
index ac1786bc..3c00cb6f 100644
--- a/sg_rtpg.c
+++ b/sg_rtpg.c
@@ -48,7 +48,7 @@
* to the given SCSI device.
*/
-static char * version_str = "1.02 20050117";
+static char * version_str = "1.03 20050309";
#define REPORT_TGT_GRP_BUFF_LEN 1024
@@ -64,8 +64,8 @@ static char * version_str = "1.02 20050117";
#define STATUS_CODE_CHANGED_BY_SET 0x1
#define STATUS_CODE_CHANGED_BY_IMPLICIT 0x2
-// <<<<<<<<<<<<<<< start of test code
-// #define TEST_CODE
+/* <<<<<<<<<<<<<<< start of test code */
+/* #define TEST_CODE */
#ifdef TEST_CODE
@@ -83,7 +83,7 @@ unsigned char dummy_resp[32] = {
};
#endif
-// <<<<<<<<<<<<<<< end of test code
+/* <<<<<<<<<<<<<<< end of test code */
static struct option long_options[] = {
{"decode", 0, 0, 'd'},
@@ -224,7 +224,7 @@ int main(int argc, char * argv[])
trunc = 0;
#ifndef TEST_CODE
- res = sg_ll_report_tgt_grp(sg_fd, reportTgtGrpBuff,
+ res = sg_ll_report_tgt_prt_grp(sg_fd, reportTgtGrpBuff,
sizeof(reportTgtGrpBuff), 1, verbose);
#else
memcpy(reportTgtGrpBuff, dummy_resp, sizeof(dummy_resp));
@@ -289,6 +289,8 @@ int main(int argc, char * argv[])
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");
err_out:
res = close(sg_fd);
diff --git a/sg_scan.c b/sg_scan.c
index 3d53fefb..eced8fa6 100644
--- a/sg_scan.c
+++ b/sg_scan.c
@@ -14,7 +14,7 @@
/* Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg")
device driver.
-* Copyright (C) 1999 - 2004 D. Gilbert
+* Copyright (C) 1999 - 2005 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)
@@ -38,7 +38,7 @@
F. Jansen - modification to extend beyond 26 sg devices.
*/
-static char * version_str = "4.00 20041130";
+static char * version_str = "4.02 20050312";
#define ME "sg_scan: "
@@ -87,7 +87,7 @@ void usage()
printf(" -w force open with read/write flag\n");
printf(" -x extra information output about queuing\n");
printf(" <sam_dev> name of device that understands SAM command"
- " set\n");
+ " set\n");
}
void make_dev_name(char * fname, int k, int do_numeric)
@@ -146,7 +146,7 @@ int main(int argc, char * argv[])
int * argv_index_arr;
if ((argv_index_arr = malloc(max_file_args * sizeof(int))))
- memset(argv_index_arr, 0, max_file_args * sizeof(int));
+ memset(argv_index_arr, 0, max_file_args * sizeof(int));
else {
printf(ME "Out of memory\n");
return 1;
@@ -178,35 +178,35 @@ int main(int argc, char * argv[])
return 1;
}
else if (*argv[k] != '-') {
- if (j < max_file_args) {
- has_file_args = 1;
- argv_index_arr[j++] = k;
- } else {
- printf("Too many command line arguments\n");
- return 1;
- }
+ if (j < max_file_args) {
+ has_file_args = 1;
+ argv_index_arr[j++] = k;
+ } else {
+ printf("Too many command line arguments\n");
+ return 1;
+ }
}
}
flags = writeable ? O_RDWR : O_RDONLY;
for (k = 0, res = 0, j = 0;
- (k < max_file_args) && (has_file_args || (num_errors < MAX_ERRORS));
+ (k < max_file_args) && (has_file_args || (num_errors < MAX_ERRORS));
++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) {
if (res < 0) {
snprintf(ebuff, EBUFF_SZ, ME "Error closing %s ", fname);
perror(ME "close error");
return 1;
}
- if (has_file_args) {
- if (argv_index_arr[j])
- file_namep = argv[argv_index_arr[j++]];
- else
- break;
- } else {
+ if (has_file_args) {
+ if (argv_index_arr[j])
+ file_namep = argv[argv_index_arr[j++]];
+ else
+ break;
+ } else {
make_dev_name(fname, k, do_numeric);
- file_namep = fname;
- }
+ file_namep = fname;
+ }
sg_fd = open(file_namep, flags | O_NONBLOCK);
if (sg_fd < 0) {
@@ -231,9 +231,9 @@ int main(int argc, char * argv[])
}
res = ioctl(sg_fd, SCSI_IOCTL_GET_IDLUN, &my_idlun);
if (res < 0) {
- res = try_ata_identity(file_namep, sg_fd, do_inquiry);
+ res = try_ata_identity(file_namep, sg_fd, do_inquiry);
if (res == 0)
- continue;
+ continue;
snprintf(ebuff, EBUFF_SZ,
ME "device %s failed on scsi ioctl, skip", file_namep);
perror(ebuff);
@@ -250,7 +250,7 @@ int main(int argc, char * argv[])
}
res = ioctl(sg_fd, SG_EMULATED_HOST, &emul);
if (res < 0)
- emul = -1;
+ emul = -1;
printf("%s: scsi%d channel=%d id=%d lun=%d", file_namep, host_no,
(my_idlun.dev_id >> 16) & 0xff, my_idlun.dev_id & 0xff,
(my_idlun.dev_id >> 8) & 0xff);
@@ -277,8 +277,8 @@ int main(int argc, char * argv[])
else
printf("\n");
}
- else
- printf("\n");
+ else
+ printf("\n");
if (do_inquiry) {
if (-1 == sg_ver3) {
sg_ver3 = 0;
@@ -291,7 +291,7 @@ int main(int argc, char * argv[])
}
}
if ((num_errors >= MAX_ERRORS) && (num_silent < num_errors) &&
- (! has_file_args)) {
+ (! has_file_args)) {
printf("Stopping because there are too many error\n");
if (eacces_err)
printf(" root access may be required\n");
@@ -322,16 +322,18 @@ int sg3_inq(int sg_fd, unsigned char * inqBuff, int do_extra)
if ((err = scsi_inq(sg_fd, inqBuff)) < 0) {
perror(ME "Inquiry SG_IO + SCSI_IOCTL_SEND_COMMAND ioctl error");
return 1;
- } else if (err) {
+ } else if (err) {
printf(ME "SCSI_IOCTL_SEND_COMMAND ioctl error=0x%x\n", err);
return 1;
- }
- sg_io = 1;
+ }
} else {
+ sg_io = 1;
/* now for the error processing */
switch (sg_err_category3(&io_hdr)) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ sg_chk_n_print3("Inquiry, continuing", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
break;
default: /* won't bother decoding other categories */
ok = 0;
@@ -346,7 +348,7 @@ int sg3_inq(int sg_fd, unsigned char * inqBuff, int do_extra)
printf(" %.8s %.16s %.4s ", p + 8, p + 16, p + 32);
printf("[rmb=%d cmdq=%d pqual=%d pdev=0x%x] ",
!!(p[1] & 0x80), !!(p[7] & 2), (p[0] & 0xe0) >> 5,
- (p[0] & 0x1f));
+ (p[0] & 0x1f));
if (do_extra && sg_io)
printf("dur=%ums\n", io_hdr.duration);
else
@@ -374,7 +376,7 @@ int scsi_inq(int sg_fd, unsigned char * inqBuff)
memcpy(sicp->data, inqCmdBlk, INQ_CMD_LEN);
res = ioctl(sg_fd, SCSI_IOCTL_SEND_COMMAND, sicp);
if (0 == res)
- memcpy(inqBuff, sicp->data, INQ_REPLY_LEN);
+ memcpy(inqBuff, sicp->data, INQ_REPLY_LEN);
return res;
}
@@ -423,7 +425,7 @@ void swapbytes(char *out, const char *in, size_t n)
for (k = 0; k < (n - 1); k += 2) {
out[k] = in[k + 1];
out[k + 1] = in[k];
- }
+ }
}
}
@@ -480,10 +482,10 @@ void printswap(char *output, char *in, unsigned int n)
}
#define ATA_IDENTIFY_BUFF_SZ sizeof(struct ata_identify_device)
+#define HDIO_DRIVE_CMD_OFFSET 4
int ata_command_interface(int device, char *data)
{
- const int HDIO_DRIVE_CMD_OFFSET = 4;
unsigned char buff[ATA_IDENTIFY_BUFF_SZ + HDIO_DRIVE_CMD_OFFSET];
int retval;
@@ -508,14 +510,14 @@ int try_ata_identity(const char * file_namep, int ata_fd, int do_inq)
res = ata_command_interface(ata_fd, (char *)&ata_ident);
if (res)
- return res;
+ return res;
printf("%s: ATA device\n", file_namep);
if (do_inq) {
- printf(" ");
+ 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("\n");
}
return res;
}
diff --git a/sg_senddiag.8 b/sg_senddiag.8
index b686a5d8..2d76760c 100644
--- a/sg_senddiag.8
+++ b/sg_senddiag.8
@@ -1,4 +1,4 @@
-.TH SG_SENDDIAG "8" "October 2004" "sg3_utils-1.09" SG3_UTILS
+.TH SG_SENDDIAG "8" "March 2005" "sg3_utils-1.13" SG3_UTILS
.SH NAME
sg_senddiag \- performs a SCSI SEND DIAGNOSTIC command
.SH SYNOPSIS
@@ -16,9 +16,10 @@ diagnostic pages). This utility is mainly used to make the device do
self tests.
.TP
-doff
-set the Device Offline (DevOfl) bit (default is clear). Only significant
-when '-t' option is set for the default self test. In this sense the 'Device'
-is a target (with all contained logical units potentially impacted).
+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.
.TP
-e
outputs the expected extended self test duration. The duration
@@ -49,9 +50,11 @@ The '-s=<num>' option should not be active together with this option.
Both the '-doff' and/or '-uoff' options can be used with this option.
.TP
-uoff
-set the Unit Offline (DevOfl) bit (default is clear). Only significant
-when '-t' option is set for the default self test. In this sense the 'Unit'
-is a logical units on a target.
+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
+is underway. Some devices (e.g. Fujitsu disks) do more tests when this
+bit is set.
.TP
-v
increase level of verbosity: print out SCSI commands in hex prior to
diff --git a/sg_senddiag.c b/sg_senddiag.c
index 2599c369..88c02e3b 100644
--- a/sg_senddiag.c
+++ b/sg_senddiag.c
@@ -22,7 +22,7 @@
command.
*/
-static char * version_str = "0.21 20050120";
+static char * version_str = "0.22 20050309";
#define ME "sg_senddiag: "
@@ -78,8 +78,10 @@ static int do_senddiag(int sg_fd, int sf_code, int pf_bit, int sf_bit,
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ sg_chk_n_print3("Send diagnostic, continuing", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
return 0;
default:
if (noisy) {
@@ -130,8 +132,10 @@ static int do_rcvdiag(int sg_fd, int pcv, int pg_code, void * resp,
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ sg_chk_n_print3("Receive diagnostic, continuing", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
return 0;
default:
if (noisy) {
@@ -159,6 +163,9 @@ static int do_modes_0a(int sg_fd, void * resp, int mx_resp_len, int noisy,
if (SG_LIB_CAT_INVALID_OP == res)
fprintf(stderr, "Mode sense (%s) command not supported\n",
(mode6 ? "6" : "10"));
+ else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+ fprintf(stderr, "bad field in Mode sense (%s) command\n",
+ (mode6 ? "6" : "10"));
return res;
}
diff --git a/sg_ses.8 b/sg_ses.8
index c3bf80af..0cd8ae15 100644
--- a/sg_ses.8
+++ b/sg_ses.8
@@ -1,6 +1,7 @@
-.TH SG_SES "8" "September 2004" "sg3_utils-1.09" SG3_UTILS
+.TH SG_SES "8" "February 2005" "sg3_utils-1.13" SG3_UTILS
.SH NAME
-sg_ses \- send controls and fetch status from SES a (enclosure) device
+sg_ses \- send controls and fetch status from a SCSI Enclosure
+Services (SES) device
.SH SYNOPSIS
.B sg_ses
[\fI--byte1=<n>\fR] [\fI--control\fR] [\fI--data=<h>,<h>...\fR]
@@ -11,19 +12,20 @@ sg_ses \- send controls and fetch status from SES a (enclosure) device
.\" Add any additional description here
.PP
Send controls to a SES device (via a SCSI SEND DIAGNOSTIC command)
-or fetch status (via a SCSI RECEIVE DIAGNOSTIC command). The SES
-device is a <scsi_device> which may be dedicated enclosure
-services processor (INQUIRY peripheral device type 0xd) or attached
-to another type of SCSI device (e.g. a disk) which will have the
-EncServ bit set in its INQUIRY.
+or fetches status (via a SCSI RECEIVE DIAGNOSTIC RESULTS command).
+The given <scsi_device> should be a SES device which may be a dedicated
+enclosure services processor (INQUIRY peripheral device type 0xd) or
+attached to another type of SCSI device (e.g. a disk) in which case
+the EncServ bit set in its INQUIRY response.
.TP
--byte=<n> | -b <n>
some control pages need byte 1 (i.e. the second byte) of the cdb set.
+Only required in rare cases when the '--control' option is also set.
Default is 0; <n> is in decimal unless it is prefixed by 0x.
.TP
--control | -c
will send control information to the given device via a SCSI SEND
-DIAGNOSTIC command. Cannot both give this option and '--status'.
+DIAGNOSTIC command. Cannot give both this option and '--status'.
The Enclosure control, String Out, Threshold Out, Array control (obsolete
in SES-2) and Subenclosure String Out diagnostic pages can be set currently.
.TP
@@ -39,7 +41,7 @@ are permitted as separators.
.TP
--filter | -f
cuts down on the amount of output from the enclosure status diagnostic
-page. When this option is given any line which has all its binary flags
+page. When this option is given, any line which has all its binary flags
cleared (i.e. 0) is filtered out (i.e. ignored). If a line has some other
value on it (e.g. a temperature) then it is output.
.TP
@@ -50,8 +52,9 @@ output the usage message then exit.
output the response in hexadecimal.
.TP
--inner-hex | -i
-in outer level of a status page are decoded and printed out but the
-innermost level (e.g. element status descriptor) is output in hex.
+the outer levels of a status diagnostic page are decoded and printed out
+but the innermost level (e.g. the element status descriptor) is output in
+hex.
.TP
--list | -l
list all known diagnostic page names and SES elements. <scsi_device>
@@ -64,13 +67,13 @@ page_code 0 (i.e. "Supported diagnostic pages").
.TP
--raw | -r
outputs the chosen status page in (ASCII) hex in a format suitable for
-a later invocation using the '--data=" option. The status page less its
-first 4 bytes (page code and length) is output.
+a later invocation using the '--data=" option. A status diagnostic page
+less its first 4 bytes (page code and length) is output.
.TP
--status | -s
-will fetch status information from the given device via a SCSI RECEIVE
-DIAGNOSTIC command. If this option is not given and '--control' is not
-given then fetch status mode is assumed.
+will fetch status diagnostic page from the given device via a SCSI RECEIVE
+DIAGNOSTIC RESULTS command. If this option is not given and '--control' is
+not given then '--status' is assumed.
.TP
--verbose | -v
increase the level of verbosity, (i.e. debug output).
@@ -79,7 +82,7 @@ increase the level of verbosity, (i.e. debug output).
print the version string and then exit.
.PP
Currently all status pages, control pages and element types defined in
-SES-2 revision 9 (22nd July 2004) are decoded.
+SES-2 revision 10 (29th January 2005) are decoded.
.SH EXAMPLE
Changing a temperature threshold is possible, if a little awkward. The
current thresholds can be shown with:
@@ -108,7 +111,7 @@ 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-2005 Douglas Gilbert
.br
This software is distributed under a FreeBSD liense. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/sg_ses.c b/sg_ses.c
index e1feb50b..29653c08 100644
--- a/sg_ses.c
+++ b/sg_ses.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004 Douglas Gilbert.
+ * Copyright (c) 2004-2005 Douglas Gilbert.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -47,7 +47,7 @@
* tailored for SES (enclosure) devices.
*/
-static char * version_str = "1.10 20041229";
+static char * version_str = "1.14 20050309";
#define SEND_DIAGNOSTIC_CMD 0x1d
#define SEND_DIAGNOSTIC_CMDLEN 6
@@ -59,6 +59,9 @@ static char * version_str = "1.10 20041229";
#define MX_ALLOC_LEN 4096
#define MX_ELEM_HDR 512
+#define TEMPERATURE_OFFSET 20 /* 8 bits represents -19 C to +235 C */
+ /* value of 0 (would imply -20 C) reserved */
+
#define ME "sg_ses: "
#define EBUFF_SZ 256
@@ -155,8 +158,10 @@ static int do_senddiag(int sg_fd, int pf_bit, void * outgoing_pg,
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ sg_chk_n_print3("Send diagnostic, continuing", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
return 0;
default:
if (noisy) {
@@ -206,8 +211,10 @@ static int do_rcvdiag(int sg_fd, int pcv, int pg_code, void * resp,
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ sg_chk_n_print3("Receive diagnostic, continuing", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
return 0;
default:
if (noisy) {
@@ -242,18 +249,15 @@ static const char * scsi_ptype_strs[] = {
"automation/driver interface",
"0x13", "0x14", "0x15", "0x16", "0x17", "0x18",
"0x19", "0x1a", "0x1b", "0x1c", "0x1d",
+ "well known logical unit",
+ "no physical device on this lu",
};
static const char * get_ptype_str(int scsi_ptype)
{
int num = sizeof(scsi_ptype_strs) / sizeof(scsi_ptype_strs[0]);
- if (0x1f == scsi_ptype)
- return "no physical device on this lu";
- else if (0x1e == scsi_ptype)
- return "well known logical unit";
- else
- return (scsi_ptype < num) ? scsi_ptype_strs[scsi_ptype] : "";
+ return (scsi_ptype < num) ? scsi_ptype_strs[scsi_ptype] : "";
}
struct page_code_desc {
@@ -271,11 +275,12 @@ static struct page_code_desc pc_desc_arr[] = {
{0x7, "Element descriptor (SES)"},
{0x8, "Short enclosure status (SES)"},
{0x9, "Enclosure busy (SES-2)"},
- {0xa, "Device element status (SES-2)"},
+ {0xa, "Additional (device) element status (SES-2)"},
{0xb, "Subenclosure help text (SES-2)"},
{0xc, "Subenclosure string In/Out (SES-2)"},
{0xd, "Supported SES diagnostic pages (SES-2)"},
{0xe, "Download microcode (SES-2)"},
+ {0xf, "Subenclosure nickname (SES-2)"},
{0x3f, "Protocol specific SAS (SAS-1)"},
{0x40, "Translate address (SBC)"},
{0x41, "Device status (SBC)"},
@@ -326,6 +331,7 @@ static struct element_desc element_desc_arr[] = {
{0x16, "Simple subenclosure"},
{0x17, "Array device"},
{0x18, "SAS expander"},
+ {0x19, "SAS connector"},
};
static const char * find_element_desc(int type_code)
@@ -374,7 +380,7 @@ static void ses_configuration_sdg(const unsigned char * resp, int resp_len)
printf(" generation code: 0x%x\n", gen_code);
ucp = resp + 8;
for (k = 0; k < num_subs; ++k, ucp += el) {
- if ((ucp + 4) > last_ucp)
+ if ((ucp + 3) > last_ucp)
goto truncated;
el = ucp[3] + 4;
sum_elem_types += ucp[2];
@@ -399,7 +405,7 @@ static void ses_configuration_sdg(const unsigned char * resp, int resp_len)
printf("\n");
text_ucp = ucp + (sum_elem_types * 4);
for (k = 0; k < sum_elem_types; ++k, ucp += 4) {
- if ((ucp + 4) > last_ucp)
+ if ((ucp + 3) > last_ucp)
goto truncated;
cp = find_element_desc(ucp[0]);
if (cp)
@@ -461,7 +467,7 @@ static int populate_element_hdr_arr(int fd, struct element_hdr * ehp,
*generationp = gen_code;
ucp = resp + 8;
for (k = 0; k < num_subs; ++k, ucp += el) {
- if ((ucp + 4) > last_ucp)
+ if ((ucp + 3) > last_ucp)
goto p_truncated;
el = ucp[3] + 4;
sum_elem_types += ucp[2];
@@ -472,7 +478,7 @@ static int populate_element_hdr_arr(int fd, struct element_hdr * ehp,
}
}
for (k = 0; k < sum_elem_types; ++k, ucp += 4) {
- if ((ucp + 4) > last_ucp)
+ if ((ucp + 3) > last_ucp)
goto p_truncated;
if (k >= MX_ELEM_HDR) {
fprintf(stderr, "populate: too many elements\n");
@@ -493,6 +499,54 @@ p_truncated:
return -1;
}
+static char * find_sas_connector_type(int conn_type, char * buff,
+ int buff_len)
+{
+ switch (conn_type) {
+ case 0x0:
+ snprintf(buff, buff_len, "No information");
+ break;
+ case 0x1:
+ snprintf(buff, buff_len, "SAS external receptacle (SFF-8470) "
+ "[max 4 phys]");
+ break;
+ case 0x10:
+ snprintf(buff, buff_len, "SAS internal wide plug (SFF-8484) "
+ "[max 4 phys]");
+ break;
+ case 0x20:
+ snprintf(buff, buff_len, "SAS backplane receptacle (SFF-8482) "
+ "[max 2 phys]");
+ break;
+ case 0x21:
+ snprintf(buff, buff_len, "SATA style host plug [max 1 phy]");
+ break;
+ case 0x22:
+ snprintf(buff, buff_len, "SAS plug (SFF-8482) [max 2 phys]");
+ break;
+ case 0x23:
+ snprintf(buff, buff_len, "SATA device plug [max 1 phy]");
+ break;
+ default:
+ if (conn_type < 0x10)
+ snprintf(buff, buff_len, "unknown external connector type: 0x%x",
+ conn_type);
+ else if (conn_type < 0x20)
+ snprintf(buff, buff_len, "unknown internal wide connector type: "
+ "0x%x", conn_type);
+ else if (conn_type < 0x30)
+ snprintf(buff, buff_len, "unknown internal connector to end "
+ "device, type: 0x%x", conn_type);
+ else if (conn_type < 0xf0)
+ snprintf(buff, buff_len, "reserved connector type: 0x%x",
+ conn_type);
+ else
+ snprintf(buff, buff_len, "vendor specific connector type: 0x%x",
+ conn_type);
+ break;
+ }
+ return buff;
+}
static const char * element_status_desc[] = {
"Unsupported", "OK", "Critical", "Non-critical",
@@ -516,11 +570,12 @@ static const char * invop_type_desc[] = {
"Reserved", "Vendor specific error"
};
-static void print_over_elem_status(const char * pad,
- const unsigned char * statp, int etype,
- int filter)
+static void print_element_status(const char * pad,
+ const unsigned char * statp, int etype,
+ int filter)
{
int res;
+ char buff[128];
printf("%sPredicted failure=%d, swap=%d, status: %s\n",
pad, !!(statp[0] & 0x40), !!(statp[0] & 0x10),
@@ -582,7 +637,8 @@ static void print_over_elem_status(const char * pad,
!!(statp[3] & 0x8), !!(statp[3] & 0x4), !!(statp[3] & 0x2),
!!(statp[3] & 0x1));
if (statp[2])
- printf("%sTemperature=%d C\n", pad, (int)statp[2] - 20);
+ printf("%sTemperature=%d C\n", pad,
+ (int)statp[2] - TEMPERATURE_OFFSET);
else
printf("%sTemperature: <reserved>\n", pad);
break;
@@ -759,6 +815,12 @@ static void print_over_elem_status(const char * pad,
case 0x18: /* SAS expander */
printf("%sIdent=%d\n", pad, !!(statp[1] & 0x80));
break;
+ case 0x19: /* SAS connector */
+ printf("%sIdent=%d, %s, Connector physical "
+ "link=0x%x\n", pad, !!(statp[1] & 0x80),
+ find_sas_connector_type((statp[1] & 0x7f), buff, sizeof(buff)),
+ statp[2]);
+ break;
default:
printf("%sUnknown element type, status in hex: %02x %02x %02x %02x\n",
pad, statp[0], statp[1], statp[2], statp[3]);
@@ -796,7 +858,7 @@ static void ses_enclosure_sdg(const struct element_hdr * ehp, int num_telems,
}
ucp = resp + 8;
for (k = 0; k < num_telems; ++k) {
- if ((ucp + 4) > last_ucp)
+ if ((ucp + 3) > last_ucp)
goto truncated;
cp = find_element_desc(ehp[k].etype);
if (cp)
@@ -810,7 +872,7 @@ static void ses_enclosure_sdg(const struct element_hdr * ehp, int num_telems,
ucp[1], ucp[2], ucp[3]);
else {
printf(" Overall status:\n");
- print_over_elem_status(" ", ucp, ehp[k].etype, filter);
+ print_element_status(" ", ucp, ehp[k].etype, filter);
}
for (ucp += 4, j = 0; j < ehp[k].num_elements; ++j, ucp += 4) {
if (inner_hex)
@@ -818,7 +880,7 @@ static void ses_enclosure_sdg(const struct element_hdr * ehp, int num_telems,
j + 1, ucp[0], ucp[1], ucp[2], ucp[3]);
else {
printf(" Element %d status:\n", j + 1);
- print_over_elem_status(" ", ucp, ehp[k].etype, filter);
+ print_element_status(" ", ucp, ehp[k].etype, filter);
}
}
}
@@ -841,7 +903,8 @@ static char * reserved_or_num(char * buff, int buff_len, int num,
}
static void ses_threshold_helper(const char * pad, const unsigned char *tp,
- int etype, int p_num, int inner_hex)
+ int etype, int p_num, int inner_hex,
+ int verbose)
{
char buff[128];
char b[128];
@@ -858,12 +921,16 @@ static void ses_threshold_helper(const char * pad, const unsigned char *tp,
}
switch (etype) {
case 0x4: /*temperature */
- printf("%s%s: high critical=%s, high warning=%s\n",
- pad, buff, reserved_or_num(b, 128, tp[0] - 20, -20),
- reserved_or_num(b2, 128, tp[1] - 20, -20));
+ printf("%s%s: high critical=%s, high warning=%s\n", pad,
+ buff, reserved_or_num(b, 128, tp[0] - TEMPERATURE_OFFSET,
+ -TEMPERATURE_OFFSET),
+ reserved_or_num(b2, 128, tp[1] - TEMPERATURE_OFFSET,
+ -TEMPERATURE_OFFSET));
printf("%s low warning=%s, low critical=%s (in degrees Celsius)\n", pad,
- reserved_or_num(b, 128, tp[2] - 20, -20),
- reserved_or_num(b2, 128, tp[3] - 20, -20));
+ reserved_or_num(b, 128, tp[2] - TEMPERATURE_OFFSET,
+ -TEMPERATURE_OFFSET),
+ reserved_or_num(b2, 128, tp[3] - TEMPERATURE_OFFSET,
+ -TEMPERATURE_OFFSET));
break;
case 0xb: /* UPS */
if (0 == tp[2])
@@ -889,6 +956,8 @@ static void ses_threshold_helper(const char * pad, const unsigned char *tp,
printf("%s (above nominal current)\n", pad);
break;
default:
+ if (verbose)
+ printf("%s<< no thresholds for this element type >>\n", pad);
break;
}
}
@@ -896,7 +965,7 @@ static void ses_threshold_helper(const char * pad, const unsigned char *tp,
static void ses_threshold_sdg(const struct element_hdr * ehp, int num_telems,
unsigned int ref_gen_code,
const unsigned char * resp, int resp_len,
- int inner_hex)
+ int inner_hex, int verbose)
{
int j, k;
unsigned int gen_code;
@@ -921,7 +990,7 @@ static void ses_threshold_sdg(const struct element_hdr * ehp, int num_telems,
}
ucp = resp + 8;
for (k = 0; k < num_telems; ++k) {
- if ((ucp + 4) > last_ucp)
+ if ((ucp + 3) > last_ucp)
goto truncated;
cp = find_element_desc(ehp[k].etype);
if (cp)
@@ -930,9 +999,11 @@ static void ses_threshold_sdg(const struct element_hdr * ehp, int num_telems,
else
printf(" Element type: [0x%x], subenclosure id: %d\n",
ehp[k].etype, ehp[k].se_id);
- ses_threshold_helper(" ", ucp, ehp[k].etype, -1, inner_hex);
+ ses_threshold_helper(" ", ucp, ehp[k].etype, -1, inner_hex,
+ verbose);
for (ucp += 4, j = 0; j < ehp[k].num_elements; ++j, ucp += 4) {
- ses_threshold_helper(" ", ucp, ehp[k].etype, j, inner_hex);
+ ses_threshold_helper(" ", ucp, ehp[k].etype, j, inner_hex,
+ verbose);
}
}
return;
@@ -967,7 +1038,7 @@ static void ses_element_desc_sdg(const struct element_hdr * ehp,
}
ucp = resp + 8;
for (k = 0; k < num_telems; ++k) {
- if ((ucp + 4) > last_ucp)
+ if ((ucp + 3) > last_ucp)
goto truncated;
cp = find_element_desc(ehp[k].etype);
if (cp)
@@ -1021,7 +1092,7 @@ static char * sas_device_type[] = {
static void ses_transport_proto(const unsigned char * ucp, int len,
int elem_num)
{
- int ports, phys, j, m;
+ int ports, phys, j, m, desc_type;
const unsigned char * per_ucp;
switch (0xf & ucp[0]) {
@@ -1047,29 +1118,46 @@ static void ses_transport_proto(const unsigned char * ucp, int len,
break;
case 6:
phys = ucp[2];
- printf(" [%d] Transport protocol: SAS, number of phys: %d\n",
- elem_num + 1, phys);
- printf(" not all phys: %d\n", ucp[3] & 1);
- per_ucp = ucp + 4;
- for (j = 0; j < phys; ++j, per_ucp += 28) {
- printf(" [%d] device type: %s\n", phys + 1,
- sas_device_type[(0x70 & per_ucp[4]) >> 4]);
- printf(" initiator port for: %s %s %s\n",
- ((per_ucp[6] & 8) ? "SSP" : ""),
- ((per_ucp[6] & 4) ? "STP" : ""),
- ((per_ucp[6] & 2) ? "SMP" : ""));
- printf(" target port for: %s %s %s\n",
- ((per_ucp[7] & 8) ? "SSP" : ""),
- ((per_ucp[7] & 4) ? "STP" : ""),
- ((per_ucp[7] & 2) ? "SMP" : ""));
- printf(" attached SAS address: ");
- for (m = 0; m < 8; ++m)
- printf("%02x", per_ucp[8 + m]);
- printf("\n SAS address: ");
+ desc_type = (ucp[3] >> 6) & 0x3;
+ printf(" [%d] Transport protocol: SAS, number of%s phys: %d\n",
+ elem_num + 1, (desc_type ? " expander" : ""), phys);
+ printf(" desc_type: %d, not all phys: %d\n", desc_type,
+ ucp[3] & 1);
+ if (0 == desc_type) {
+ per_ucp = ucp + 4;
+ for (j = 0; j < phys; ++j, per_ucp += 28) {
+ printf(" [%d] device type: %s\n", phys + 1,
+ sas_device_type[(0x70 & per_ucp[0]) >> 4]);
+ printf(" initiator port for: %s %s %s\n",
+ ((per_ucp[2] & 8) ? "SSP" : ""),
+ ((per_ucp[2] & 4) ? "STP" : ""),
+ ((per_ucp[2] & 2) ? "SMP" : ""));
+ printf(" target port for: %s %s %s %s %s\n",
+ ((per_ucp[3] & 0x80) ? "SATA_port_selector" : ""),
+ ((per_ucp[3] & 8) ? "SSP" : ""),
+ ((per_ucp[3] & 4) ? "STP" : ""),
+ ((per_ucp[3] & 2) ? "SMP" : ""),
+ ((per_ucp[3] & 1) ? "SATA_device" : ""));
+ printf(" attached SAS address: ");
+ for (m = 0; m < 8; ++m)
+ printf("%02x", per_ucp[4 + m]);
+ printf("\n SAS address: ");
+ for (m = 0; m < 8; ++m)
+ printf("%02x", per_ucp[12 + m]);
+ printf("\n phy identifier: 0x%x\n", per_ucp[20]);
+ }
+ } else if (1 == desc_type) {
+ printf(" SAS address: ");
for (m = 0; m < 8; ++m)
- printf("%02x", per_ucp[16 + m]);
- printf("\n phy identifier: 0x%x\n", per_ucp[24]);
- }
+ printf("%02x", ucp[4 + m]);
+ printf("\n");
+ per_ucp = ucp + 12;
+ for (j = 0; j < phys; ++j, per_ucp += 2) {
+ printf(" [%d] connector element index: %d, other element "
+ "index: %d\n", phys + 1, per_ucp[0], per_ucp[1]);
+ }
+ } else
+ printf(" unrecognised descriptor type\n");
break;
default:
printf(" [%d] Transport protocol: %s not decoded, in hex:\n",
@@ -1079,17 +1167,19 @@ static void ses_transport_proto(const unsigned char * ucp, int len,
}
}
-static void ses_device_elem_sdg(const struct element_hdr * ehp,
+/* Previously called "Device element status descriptor". Changed "device"
+ to "additional" to allow for SAS descriptors and SATA devices */
+static void ses_additional_elem_sdg(const struct element_hdr * ehp,
int num_telems, unsigned int ref_gen_code,
const unsigned char * resp, int resp_len)
{
- int j, k, desc_len;
+ int j, k, desc_len, elem_type;
unsigned int gen_code;
const unsigned char * ucp;
const unsigned char * last_ucp;
const char * cp;
- printf("Device element status diagnostic page:\n");
+ printf("Additional (device) element status diagnostic page:\n");
if (resp_len < 4)
goto truncated;
last_ucp = resp + resp_len - 1;
@@ -1103,13 +1193,13 @@ static void ses_device_elem_sdg(const struct element_hdr * ehp,
}
ucp = resp + 8;
for (k = 0; k < num_telems; ++k) {
- if ((ucp + 2) > last_ucp)
+ if ((ucp + 1) > last_ucp)
goto truncated;
- if ((1 != ehp[k].etype) && (0x17 != ehp[k].etype) &&
- (0x18 != ehp[k].etype))
+ elem_type = ehp[k].etype;
+ if (! ((1 == elem_type) || (0x17 == elem_type) || (0x18 != elem_type)))
continue;
/* only interested in device, array and SAS expander elements */
- cp = find_element_desc(ehp[k].etype);
+ cp = find_element_desc(elem_type);
if (cp)
printf(" Element type: %s, subenclosure id: %d\n",
cp, ehp[k].se_id);
@@ -1146,7 +1236,7 @@ static void ses_subenc_help_sdg(const unsigned char * resp, int resp_len)
printf(" generation code: 0x%x\n", gen_code);
ucp = resp + 8;
for (k = 0; k < num_subs; ++k, ucp += el) {
- if ((ucp + 4) > last_ucp)
+ if ((ucp + 3) > last_ucp)
goto truncated;
el = (ucp[2] << 8) + ucp[3] + 4;
printf(" subenclosure identifier: %d\n", ucp[1]);
@@ -1180,7 +1270,7 @@ static void ses_subenc_string_sdg(const unsigned char * resp, int resp_len)
printf(" generation code: 0x%x\n", gen_code);
ucp = resp + 8;
for (k = 0; k < num_subs; ++k, ucp += el) {
- if ((ucp + 4) > last_ucp)
+ if ((ucp + 3) > last_ucp)
goto truncated;
el = (ucp[2] << 8) + ucp[3] + 4;
printf(" subenclosure identifier: %d\n", ucp[1]);
@@ -1211,6 +1301,43 @@ static void ses_supported_pages_sdg(const char * leadin,
}
}
+static void ses_download_code_sdg(const unsigned char * resp, int resp_len)
+{
+ int k, num_subs;
+ unsigned int gen_code;
+ const unsigned char * ucp;
+ const unsigned char * last_ucp;
+
+ printf("Download microcode status diagnostic page:\n");
+ if (resp_len < 4)
+ goto truncated;
+ num_subs = resp[1] + 1; /* number of subenclosures (add 1 for primary) */
+ last_ucp = resp + resp_len - 1;
+ printf(" number of subenclosures (other than primary): %d\n",
+ num_subs - 1);
+ gen_code = (resp[4] << 24) | (resp[5] << 16) |
+ (resp[6] << 8) | resp[7];
+ printf(" generation code: 0x%x\n", gen_code);
+ ucp = resp + 8;
+ for (k = 0; k < num_subs; ++k, ucp += 16) {
+ if ((ucp + 3) > last_ucp)
+ goto truncated;
+ printf(" subenclosure identifier: %d\n", ucp[1]);
+ printf(" download microcode status: 0x%x [additional status: "
+ "0x%x]\n", ucp[2], ucp[3]);
+ printf(" download microcode maximum size: %d bytes\n",
+ (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7]);
+ printf(" download microcode expected buffer id: 0x%x\n", ucp[11]);
+ printf(" download microcode expected buffer id offset: %d\n",
+ (ucp[12] << 24) + (ucp[13] << 16) + (ucp[14] << 8) + ucp[15]);
+ }
+ return;
+truncated:
+ fprintf(stderr, " <<<response too short>>>\n");
+ return;
+}
+
+
static int read_hex(const char * inp, unsigned char * arr, int * arr_len)
{
int in_len, k, j, m, off;
@@ -1319,6 +1446,7 @@ static void ses_process_status(int sg_fd, int page_code, int do_raw,
const char * cp;
memset(rsp_buff, 0, rsp_buff_size);
+ cp = find_page_code_desc(page_code);
if (0 == do_rcvdiag(sg_fd, 1, page_code, rsp_buff, rsp_buff_size, 1,
verbose)) {
rsp_len = (rsp_buff[2] << 8) + rsp_buff[3] + 4;
@@ -1327,7 +1455,6 @@ static void ses_process_status(int sg_fd, int page_code, int do_raw,
"[%d but need %d]>>>\n", rsp_buff_size, rsp_len);
rsp_len = rsp_buff_size;
}
- cp = find_page_code_desc(page_code);
if (page_code != rsp_buff[0]) {
if ((0x9 == rsp_buff[0]) && (1 & rsp_buff[1])) {
fprintf(stderr, "Enclosure busy, try again later\n");
@@ -1389,7 +1516,7 @@ static void ses_process_status(int sg_fd, int page_code, int do_raw,
if (res < 0)
break;
ses_threshold_sdg(element_hdr_arr, res, ref_gen_code,
- rsp_buff, rsp_len, inner_hex);
+ rsp_buff, rsp_len, inner_hex, verbose);
break;
case 7:
res = populate_element_hdr_arr(sg_fd, element_hdr_arr,
@@ -1413,8 +1540,8 @@ static void ses_process_status(int sg_fd, int page_code, int do_raw,
&ref_gen_code, verbose);
if (res < 0)
break;
- ses_device_elem_sdg(element_hdr_arr, res, ref_gen_code,
- rsp_buff, rsp_len);
+ ses_additional_elem_sdg(element_hdr_arr, res, ref_gen_code,
+ rsp_buff, rsp_len);
break;
case 0xb:
ses_subenc_help_sdg(rsp_buff, rsp_len);
@@ -1426,14 +1553,25 @@ static void ses_process_status(int sg_fd, int page_code, int do_raw,
ses_supported_pages_sdg("Supported SES diagnostic pages",
rsp_buff, rsp_len);
break;
+ case 0xe:
+ ses_download_code_sdg(rsp_buff, rsp_len);
+ break;
+ case 0xf:
+ /* subenclosure nickname: place holder */
+ break;
default:
printf("Cannot decode response from diagnostic "
"page: %s\n", (cp ? cp : "<unknown>"));
dStrHex((const char *)rsp_buff, rsp_len, 0);
}
}
- } else
- printf("Attempt to fetch status diagnostic page failed\n");
+ } else {
+ if (cp)
+ printf("Attempt to fetch %s diagnostic page failed\n", cp);
+ else
+ printf("Attempt to fetch status diagnostic page [0x%x] "
+ "failed\n", page_code);
+ }
}
diff --git a/sg_sync.8 b/sg_sync.8
index 60bf3efa..a64228d2 100644
--- a/sg_sync.8
+++ b/sg_sync.8
@@ -1,4 +1,4 @@
-.TH SG_SYNC "8" "November 2004" "sg3_utils-1.11" SG3_UTILS
+.TH SG_SYNC "8" "February 2005" "sg3_utils-1.13" SG3_UTILS
.SH NAME
sg_sync \- send the scsi command synchronize cache
.SH SYNOPSIS
@@ -64,10 +64,10 @@ print the version string and then exit.
.PP
The arguments to --count, --group and --lba may be followed by one of these
multiplicative suffixes:
-c C *1; b B *512; k *1,024; K *1,000; m *1,048,576; M *1,000,000;
-g *1,073,741,824; G *1,000,000,000; t *1,099,511,627,776 and
-T *1,000,000,000,000 (the latter two can only be used for --count
-and --lba).
+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". Also a suffix of
+the form "x<n>" multiplies the leading number by <n>. The "T" and "P"
+suffixes can only be used for --count and --lba.
.PP
Alternatively numerical values can be given in hexadecimal preceded by
either "0x" or "0X". When hex numbers are given multipliers cannot be
@@ -77,7 +77,7 @@ 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-2005 Douglas Gilbert
.br
This software is distributed under a FreeBSD license. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/sg_sync.c b/sg_sync.c
index 440038e8..ce3a2109 100644
--- a/sg_sync.c
+++ b/sg_sync.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004 Douglas Gilbert.
+ * Copyright (c) 2004-2005 Douglas Gilbert.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -49,7 +49,7 @@
* (e.g. disks)
*/
-static char * version_str = "1.01 20041229";
+static char * version_str = "1.02 20050210";
#define ME "sg_sync: "
@@ -194,6 +194,8 @@ int main(int argc, char * argv[])
ret = 0;
else if (SG_LIB_CAT_INVALID_OP == res)
fprintf(stderr, "Synchronize cache command not supported\n");
+ else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+ fprintf(stderr, "bad field in Synchronize cache command\n");
else
fprintf(stderr, "Synchronize cache failed\n");
diff --git a/sg_test_rwbuf.8 b/sg_test_rwbuf.8
index cd3906a1..37d9630f 100644
--- a/sg_test_rwbuf.8
+++ b/sg_test_rwbuf.8
@@ -1,44 +1,81 @@
-.TH SG_TEST_RWBUF "8" "November 2003" "sg3_utils-1.05" SG3_UTILS
+.TH SG_TEST_RWBUF "8" "March 2005" "sg3_utils-1.13" SG3_UTILS
.SH NAME
sg_test_rwbuf \- Tests the SCSI host adapter by issueing write and read
operations on a device's buffer and calculating checksums.
.SH SYNOPSIS
.B sg_test_rwbuf
-<\fIgeneric device\fR>
-<\fIsz\fR>
-[\fIaddwr\fR]
-[\fIaddrd\fR]
+[\fI--addrd=<n>\fR] [\fI--addwr=<n>\fR] [\fI--help\fR]
+[\fI--quick\fR] \fI--size=<sz>\fR [\fI--times=<n>\fR] [\fI--verbose\fR]
+[\fI--version\fR] \fI<scsi_device>\fR
+.br
+or (an older deprecated formst)
+.br
+.B sg_test_rwbuf
+\fI<scsi_device>\fR \fI<sz>\fR [\fI<addwr>\fR] [\fI<addrd>\fR]
.SH DESCRIPTION
.\" Add any additional description here
.PP
sg_test_rwbuf writes and reads back sz bytes to the internal buffer of
-device <\fIgeneric device\fR> (e.g. /dev/sg0). A pseudo random pattern is
+<\fIscsi_device\fR> (e.g. /dev/sda or /dev/sg0). A pseudo random pattern is
written to the data buffer on the device then read back. If the same pattern
is found 'Success' is reported. If they do not match (checksums unequal) then
this is reported and up to 24 bytes from the first point of mismatch are
reported; the first line shows what was written and the second line shows
-what was received. For testing purposes, you can ask it to write (addwr) or
-read (addrd) additional bytes.
+what was received. For testing purposes, you can ask it to write <addwr> or
+read <addrd> additional bytes.
+.TP
+--addrd=<n> | -r <n>
+Read an additional <n> bytes (than indicated by <sz>) from the data buffer.
+Checksum is performed over the first <sz> bytes.
+.TP
+--addwr=<n> | -w <n>
+Write an additional <n> bytes (than indicated by <sz>) of zeroes into the
+data buffer. Checksum is generated over the first <sz> bytes.
+.TP
+--help | -h
+Print out a usage message the exit.
+.TP
+--quick | -q
+Perform a READ BUFFER descriptor command to find out the available data
+buffer length and offset, print them out then exit (without testing
+with write/read sequences).
+.TP
+--size=<sz> | -s <sz>
+Size of buffer in bytes to be written then read and checked. This number
+needs to be less than on equal to the size of the device's data buffer
+which can be seen from the '--quick' option. Either this option or
+the '--quick' option should be given.
+.TP
+--times=<n> | -t <n>
+Number of times to repeat the write/read to buffer test. Default value
+is 1 .
+.TP
+--verbose | -v
+increase verbosity of output.
+.TP
+--version | -V
+print version number (and data of last change) then exit.
.PP
The microcode in a SCSI device is _not_ modified by doing a WRITE BUFFER
command with its mode set to "data" (0x2) as done by this utility. Therefore
this utility is safe in that respect. [Mode values 0x4, 0x5, 0x6 and 0x7
are the dangerous ones :-)]
.PP
-\fBWARNING\fR: If you access the device at the same time, e.g. because it's a
-mounted hard disk, the device's buffer may be used by the device
-itself for other data at the same time, and overwriting it may or may
-not cause data corruption! \fBHOWEVER\fR the SPC-3 draft standard does state
-in its WRITE BUFFER command: "This command shall not alter any medium of the
-logical unit when data mode ... is specified". This implies that it _is_ safe
-to use this utility with devices that have mounted file systems on them.
+\fBWARNING\fR: If you access the device at the same time (e.g. because it's
+a hard disk with a mounted file system on it) the device's buffer may be
+used by the device itself for other data at the same time, and overwriting
+it may or may not cause data corruption! \fBHOWEVER\fR the SPC-3 draft
+standard does state in its WRITE BUFFER command: "This command shall not
+alter any medium of the logical unit when data mode ... is specified". This
+implies that it _is_ safe to use this utility with devices that have mounted
+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 AUTHORS
Written by D. Gilbert and K. Garloff
.SH COPYRIGHT
-Copyright \(co 2000-2003 Douglas Gilbert, Kurt Garloff
+Copyright \(co 2000-2005 Douglas Gilbert, Kurt Garloff
.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_test_rwbuf.c b/sg_test_rwbuf.c
index c7abcd0b..ed4903c6 100644
--- a/sg_test_rwbuf.c
+++ b/sg_test_rwbuf.c
@@ -9,7 +9,7 @@
* somebody else in the meantime.
* (c) 2000 Kurt Garloff <garloff at suse dot de>
* heavily based on Doug Gilbert's sg_rbuf program.
- * (c) 1999-2004 Doug Gilbert
+ * (c) 1999-2005 Doug 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
@@ -27,12 +27,16 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <getopt.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <time.h>
#include "sg_include.h"
#include "sg_lib.h"
+
+static char * version_str = "1.01 20050310";
+
#define BPI (signed)(sizeof(int))
#define RB_MODE_DESC 3
@@ -47,18 +51,30 @@
#define ME "sg_test_rwbuf: "
-int base = 0x12345678;
-int buf_capacity = 0;
-int buf_granul = 255;
-int ln;
-char *file_name = 0;
-unsigned char *cmpbuf = 0;
+static int base = 0x12345678;
+static int buf_capacity = 0;
+static int buf_granul = 255;
+static unsigned char *cmpbuf = NULL;
/* Options */
-char do_quick = 0;
-int addwrite = 0;
-int addread = 0;
+static int size = -1;
+static char do_quick = 0;
+static int addwrite = 0;
+static int addread = 0;
+static int verbose = 0;
+
+static struct option long_options[] = {
+ {"help", 0, 0, 'h'},
+ {"quick", 0, 0, 'q'},
+ {"addrd", 1, 0, 'r'},
+ {"size", 1, 0, 's'},
+ {"times", 1, 0, 't'},
+ {"verbose", 0, 0, 'v'},
+ {"version", 0, 0, 'V'},
+ {"addwr", 1, 0, 'w'},
+ {0, 0, 0, 0},
+};
int find_out_about_buffer (int sg_fd)
{
@@ -66,6 +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;
rbCmdBlk[1] = RB_MODE_DESC;
rbCmdBlk[8] = RB_DESC_LEN;
@@ -80,17 +97,22 @@ int find_out_about_buffer (int sg_fd)
io_hdr.sbp = sense_buffer;
io_hdr.timeout = 60000; /* 60000 millisecs == 60 seconds */
+ if (verbose) {
+ fprintf(stderr, " read buffer [mode desc] cdb: ");
+ for (k = 0; k < (int)sizeof(rbCmdBlk); ++k)
+ fprintf(stderr, "%02x ", rbCmdBlk[k]);
+ fprintf(stderr, "\n");
+ }
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
perror(ME "SG_IO READ BUFFER descriptor error");
return 1;
}
/* now for the error processing */
switch (sg_err_category3(&io_hdr)) {
- case SG_LIB_CAT_CLEAN:
- break;
case SG_LIB_CAT_RECOVERED:
- printf("Recovered error on READ BUFFER descriptor, "
- "continuing\n");
+ sg_chk_n_print3("READ BUFFER descriptor, continuing", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
break;
default: /* won't bother decoding other categories */
sg_chk_n_print3("READ BUFFER descriptor error", &io_hdr);
@@ -103,8 +125,9 @@ int find_out_about_buffer (int sg_fd)
printf("READ BUFFER reports: %02x %02x %02x %02x\n",
rbBuff[0], rbBuff[1], rbBuff[2], rbBuff[3]);
#endif
- printf("READ BUFFER reports: buffer capacity=%d, offset boundary=%d\n",
- buf_capacity, buf_granul);
+ if (verbose)
+ printf("READ BUFFER reports: buffer capacity=%d, offset "
+ "boundary=%d\n", buf_capacity, buf_granul);
return 0;
}
@@ -127,18 +150,24 @@ int do_checksum (int *buf, int len, int quiet)
if (!quiet) printf ("sg_test_rwbuf: Checksum error (sz=%i):"
" %08x\n", len, sum);
if (cmpbuf && !quiet) {
- int diff = mymemcmp (cmpbuf, (unsigned char*)buf, len);
+ int diff = mymemcmp (cmpbuf, (unsigned char*)buf,
+ len);
printf ("Differ at pos %i/%i:\n", diff, len);
for (i = 0; i < 24 && i+diff < len; i++)
printf (" %02x", cmpbuf[i+diff]);
printf ("\n");
for (i = 0; i < 24 && i+diff < len; i++)
- printf (" %02x", ((unsigned char*)buf)[i+diff]);
+ printf (" %02x",
+ ((unsigned char*)buf)[i+diff]);
printf ("\n");
}
return 2;
}
- else return 0;
+ else {
+ if (verbose > 1)
+ printf("Checksum value: 0x%x\n", sum);
+ return 0;
+ }
}
void do_fill_buffer (int *buf, int len)
@@ -176,7 +205,7 @@ void do_fill_buffer (int *buf, int len)
int read_buffer (int sg_fd, unsigned size)
{
- int res;
+ int res, k;
unsigned char rbCmdBlk[] = {READ_BUFFER, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int bufSize = size + addread;
unsigned char * rbBuff = malloc(bufSize);
@@ -200,22 +229,27 @@ int read_buffer (int sg_fd, unsigned size)
io_hdr.sbp = sense_buffer;
io_hdr.pack_id = 2;
io_hdr.timeout = 60000; /* 60000 millisecs == 60 seconds */
+ if (verbose) {
+ fprintf(stderr, " read buffer [mode data] cdb: ");
+ for (k = 0; k < (int)sizeof(rbCmdBlk); ++k)
+ fprintf(stderr, "%02x ", rbCmdBlk[k]);
+ fprintf(stderr, "\n");
+ }
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
- perror(ME "SG_IO READ BUFFER read data error");
+ perror(ME "SG_IO READ BUFFER data error");
free(rbBuff);
return 1;
}
/* now for the error processing */
switch (sg_err_category3(&io_hdr)) {
- case SG_LIB_CAT_CLEAN:
- break;
case SG_LIB_CAT_RECOVERED:
- printf("Recovered error in READ BUFFER read data, "
- "continuing\n");
+ sg_chk_n_print3("READ BUFFER data, continuing", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
break;
default: /* won't bother decoding other categories */
- sg_chk_n_print3("READ BUFFER read data error", &io_hdr);
+ sg_chk_n_print3("READ BUFFER data error", &io_hdr);
free(rbBuff);
return 1;
}
@@ -232,6 +266,7 @@ 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;
if (NULL == wbBuff)
return 1;
@@ -252,21 +287,27 @@ int write_buffer (int sg_fd, unsigned size)
io_hdr.sbp = sense_buffer;
io_hdr.pack_id = 1;
io_hdr.timeout = 60000; /* 60000 millisecs == 60 seconds */
+ if (verbose) {
+ fprintf(stderr, " write buffer [mode data] cdb: ");
+ for (k = 0; k < (int)sizeof(wbCmdBlk); ++k)
+ fprintf(stderr, "%02x ", wbCmdBlk[k]);
+ fprintf(stderr, "\n");
+ }
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
- perror(ME "SG_IO READ BUFFER write data error");
+ perror(ME "SG_IO WRITE BUFFER data error");
free(wbBuff);
return 1;
}
/* now for the error processing */
switch (sg_err_category3(&io_hdr)) {
- case SG_LIB_CAT_CLEAN:
- break;
case SG_LIB_CAT_RECOVERED:
- printf("Recovered error in READ BUFFER write data, continuing\n");
+ sg_chk_n_print3("WRITE BUFFER data, continuing", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
break;
default: /* won't bother decoding other categories */
- sg_chk_n_print3("READ BUFFER write data error", &io_hdr);
+ sg_chk_n_print3("WRITE BUFFER data error", &io_hdr);
free(wbBuff);
return 1;
}
@@ -276,56 +317,203 @@ int write_buffer (int sg_fd, unsigned size)
void usage ()
{
- printf ("Usage: sg_test_rwbuf /dev/sgX sz [addwr] [addrd]\n");
- printf ("sg_test_rwbuf writes and reads back sz bytes to the internal buffer of\n");
- printf (" device /dev/sgX. For testing purposes, you can ask it to write\n");
- printf (" (addwr) or read (addrd) some more bytes.\n");
- printf ("WARNING: If you access the device at the same time, e.g. because it's a\n");
- printf (" mounted hard disk, the device's buffer may be used by the device itself\n");
- printf (" for other data at the same time, and overwriting it may or may not\n");
+ printf ("Usage: sg_test_rwbuf [--addrd=<n>] [--addwr=<n>] [--help] "
+ "[--quick]\n");
+ printf (" --size=<sz> [--times=<n>] [--verbose] "
+ "[--version]\n"
+ " <scsi_device>\n"
+ " or\n"
+ " sg_test_rwbuf <scsi_device> <sz> [<addwr>] "
+ "[<addrd>]\n");
+ printf (" where:\n"
+ " --addrd=<n> extra bytes to fetch during READ "
+ "BUFFER\n"
+ " --addwr=<n> extra bytes to send to WRITE BUFFER\n"
+ " --help output this usage message then exit\n"
+ " --quick output read buffer size then exit\n"
+ " --size=<sz> size of buffer (in bytes) to write "
+ "then read back\n"
+ " --times=<n> number of times to run test "
+ "(default 1)\n"
+ " --verbose increase verbosity of output\n"
+ " --verbose output version then exit\n");
+ printf ("\nWARNING: If you access the device at the same time, e.g. "
+ "because it's a\n");
+ printf (" mounted hard disk, the device's buffer may be used by the "
+ "device itself\n");
+ printf (" for other data at the same time, and overwriting it may or "
+ "may not\n");
printf (" cause data corruption!\n");
- printf ("(c) Douglas Gilbert, Kurt Garloff, 2000-2004, GNU GPL\n");
- exit (1);
-}
-
-void parseargs (int argc, char *argv[])
-{
- if (argc < 3) usage ();
- file_name = argv[1];
- ln = atol (argv[2]);
- if (argc > 3) addwrite = atol (argv[3]);
- if (argc > 4) addread = atol (argv[4]);
+ printf ("(c) Douglas Gilbert, Kurt Garloff, 2000-2005, GNU GPL\n");
}
int main (int argc, char * argv[])
{
int sg_fd, res;
+ char device_name[256];
+ int times = 1;
+ int ret = 0;
+ int k = 0;
+
+ device_name[0] = '\0';
+ while (1) {
+ int option_index = 0;
+ char c;
+
+ c = getopt_long(argc, argv, "hqr:s:t:w:vV",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ usage();
+ return 0;
+ case 'q':
+ do_quick = 1;
+ break;
+ case 'r':
+ addread = sg_get_num(optarg);
+ if (-1 == addread) {
+ fprintf(stderr, "bad argument to '--addrd'\n");
+ return 1;
+ }
+ break;
+ case 's':
+ size = sg_get_num(optarg);
+ if (-1 == size) {
+ fprintf(stderr, "bad argument to '--size'\n");
+ return 1;
+ }
+ break;
+ case 't':
+ times = sg_get_num(optarg);
+ if (-1 == times) {
+ fprintf(stderr, "bad argument to '--times'\n");
+ return 1;
+ }
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'V':
+ fprintf(stderr, ME "version: %s\n",
+ version_str);
+ return 0;
+ case 'w':
+ addwrite = sg_get_num(optarg);
+ if (-1 == addwrite) {
+ fprintf(stderr, "bad argument to '--addwr'\n");
+ return 1;
+ }
+ break;
+ default:
+ usage();
+ return 1;
+ }
+ }
+ if (optind < argc) {
+ if ('\0' == device_name[0]) {
+ strncpy(device_name, argv[optind],
+ sizeof(device_name) - 1);
+ device_name[sizeof(device_name) - 1] = '\0';
+ ++optind;
+ }
+ }
+ if (optind < argc) {
+ if (-1 == size) {
+ size = sg_get_num(argv[optind]);
+ if (-1 == size) {
+ fprintf(stderr, "bad <sz>\n");
+ usage();
+ return 1;
+ }
+ if (++optind < argc) {
+ addwrite = sg_get_num(argv[optind]);
+ if (-1 == addwrite) {
+ fprintf(stderr, "bad [addwr]\n");
+ usage();
+ return 1;
+ }
+ if (++optind < argc) {
+ addread = sg_get_num(argv[optind]);
+ if (-1 == addread) {
+ fprintf(stderr,
+ "bad [addrd]\n");
+ usage();
+ return 1;
+ }
+ }
+ }
+
+ }
+ if (optind < argc) {
+ for (; optind < argc; ++optind)
+ fprintf(stderr, "Unexpected extra argument"
+ ": %s\n", argv[optind]);
+ usage();
+ return 1;
+ }
+ }
+ if ('\0' == device_name[0]) {
+ fprintf(stderr, "no device name given\n");
+ usage();
+ return 1;
+ }
+ if ((size <= 0) && (! do_quick)) {
+ fprintf(stderr, "must give '--size' or '--quick' options "
+ "or <sz> argument\n");
+ usage();
+ return 1;
+ }
- parseargs (argc, argv);
- sg_fd = open(file_name, O_RDWR);
+ sg_fd = open(device_name, O_RDWR);
if (sg_fd < 0) {
perror("sg_test_rwbuf: open error");
return 1;
}
- if (find_out_about_buffer (sg_fd)) return 1;
- if (ln > buf_capacity) {
- printf ("sg_test_rwbuf: sz=%i > buf_capacity=%i!\n",
- ln, buf_capacity);
- exit (2);
+ if (find_out_about_buffer (sg_fd)) {
+ ret = 1;
+ goto err_out;
+ }
+ if (do_quick) {
+ printf ("READ BUFFER read descriptor reports a buffer "
+ "of %d bytes [%d KiB]\n", buf_capacity,
+ buf_capacity / 1024);
+ goto err_out;
+ }
+ if (size > buf_capacity) {
+ fprintf (stderr, ME "sz=%i > buf_capacity=%i\n",
+ size, buf_capacity);
+ ret = 2;
+ goto err_out;
}
- cmpbuf = malloc (ln);
- if (write_buffer (sg_fd, ln)) return 3;
- res = read_buffer (sg_fd, ln);
- if (res) return (res + 4);
+ cmpbuf = malloc (size);
+ for (k = 0; k < times; ++k) {
+ if (write_buffer (sg_fd, size)) {
+ ret = 3;
+ goto err_out;
+ }
+ res = read_buffer (sg_fd, size);
+ if (res) {
+ ret = res + 4;
+ goto err_out;
+ }
+ }
+err_out:
+ if (cmpbuf)
+ free(cmpbuf);
res = close(sg_fd);
if (res < 0) {
- perror("sg_test_rwbuf: close error");
- return 6;
+ perror(ME "close error");
+ ret = 6;
}
- printf ("Success\n");
- return 0;
+ if ((0 == ret) && (! do_quick))
+ printf ("Success\n");
+ else if (times > 1)
+ printf ("Failed after %d succesful cycles\n", k);
+ return ret;
}
-
diff --git a/sg_turs.8 b/sg_turs.8
index 9cde97a6..8e4061cb 100644
--- a/sg_turs.8
+++ b/sg_turs.8
@@ -1,4 +1,4 @@
-.TH SG_TURS "8" "SEptember 2004" "sg3_utils-1.09" SG3_UTILS
+.TH SG_TURS "8" "February 2005" "sg3_utils-1.13" SG3_UTILS
.SH NAME
sg_turs \- executes a user specified number of TEST UNIT READY commands on
the given device
@@ -16,9 +16,11 @@ returned SCSI status value.
.TP
-n=<num>
performs TEST UNIT READY "<num>" times. If not given defaults to 1.
-Postfix multipliers "k" (1024), "K" (1000), "m" (1048576) or "M" (1000000)
-may be applied to "<num>". Alternatively a hex number may
-be given, prefixed by either 0x or 0X.
+These suffix multipliers are permitted: 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;
+g G GiB *1,073,741,824; and GB *1,000,000,000 . Also a suffix of the
+form "x<n>" multiplies the leading number by <n>. Alternatively a hex
+number may be given, prefixed by either 0x or 0X.
.TP
-t
after completing the requested number of TEST UNIT READY commands, outputs
@@ -26,7 +28,7 @@ the total duration and the average number of commands executed per second.
.SH AUTHORS
Written by D. Gilbert
.SH COPYRIGHT
-Copyright \(co 2000-2003 Douglas Gilbert
+Copyright \(co 2000-2005 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_verify.8 b/sg_verify.8
index 5f143311..96a649af 100644
--- a/sg_verify.8
+++ b/sg_verify.8
@@ -1,4 +1,4 @@
-.TH SG_VERIFY "8" "October 2004" "sg3_utils-1.09" SG3_UTILS
+.TH SG_VERIFY "8" "February 2005" "sg3_utils-1.13" SG3_UTILS
.SH NAME
sg_verify \- invoke SCSI VERIFY command(s) on a block device
.SH SYNOPSIS
@@ -56,10 +56,11 @@ print the version string and then exit.
.PP
The arguments to --bpc, --count and --lba may be followed by one of these
multiplicative suffixes:
-c C *1; b B *512; k *1,024; K *1,000; m *1,048,576; M *1,000,000;
-g *1,073,741,824; G *1,000,000,000; t *1,099,511,627,776 and
-T *1,000,000,000,000 (the latter two can only be used for --count
-and --lba).
+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; g G GiB *1,073,741,824; GB *1,000,000,000; t T TiB *(2**40);
+TB *(10**12); p P PiB *(2**50) and PB *(10**15). The "T" and "P" based
+suffixes can only be used for --count and --lba. Also a suffix of the
+form "x<n>" multiplies the leading number by <n>.
.PP
Alternatively numerical values can be given in hexadecimal preceded by
either "0x" or "0X". When hex numbers are given multipliers cannot be
@@ -80,7 +81,7 @@ 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-2005 Douglas Gilbert
.br
This software is distributed under a FreeBSD license. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/sg_verify.c b/sg_verify.c
index eefa4b7c..73c7483c 100644
--- a/sg_verify.c
+++ b/sg_verify.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004 Douglas Gilbert.
+ * Copyright (c) 2004-2005 Douglas Gilbert.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -45,7 +45,7 @@
* This program issues the SCSI VERIFY command to the given SCSI block device.
*/
-static char * version_str = "1.01 20041229";
+static char * version_str = "1.02 20050309";
#define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */
#define DEF_TIMEOUT 60000 /* 60,000 millisecs == 60 seconds */
@@ -86,14 +86,14 @@ static void usage()
}
-/* Invokes a SCSI VERIFY (10) command */
-/* Return of 0 -> success, -1 -> failure, SG_LIB_CAT_INVALID_OP ->
- Verify(10) not supported */
+/* Invokes a SCSI VERIFY (10) command. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> Verify(10) not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -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,
int verbose)
{
- int k;
+ int k, res;
unsigned char vCmdBlk[VERIFY10_CMDLEN] =
{VERIFY10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
unsigned char sense_b[SENSE_BUFF_LEN];
@@ -143,17 +143,18 @@ int sg_ll_verify10(int sg_fd, int dpo, int bytechk, unsigned long lba,
safe_strerror(errno));
return -1;
}
- switch (sg_err_category3(&io_hdr)) {
- case SG_LIB_CAT_CLEAN:
- return 0;
+ res = sg_err_category3(&io_hdr);
+ switch (res) {
case SG_LIB_CAT_RECOVERED:
- fprintf(sg_warnings_str, "Recovered error on VERIFY(10) command, "
- "continuing\n");
+ sg_chk_n_print3("VERIFY(10), continuing", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
return 0;
case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_ILLEGAL_REQ:
if (verbose > 1)
sg_chk_n_print3("VERIFY(10) command problem", &io_hdr);
- return SG_LIB_CAT_INVALID_OP;
+ return res;
default:
sg_chk_n_print3("VERIFY(10) command problem", &io_hdr);
return -1;
@@ -268,6 +269,8 @@ int main(int argc, char * argv[])
if (0 != res) {
if (SG_LIB_CAT_INVALID_OP == res)
fprintf(stderr, "Verify(10) command not supported\n");
+ else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+ fprintf(stderr, "bad field in Verify(10) cdb\n");
else
fprintf(stderr, "Verify(10) failed near lba=%llu [0x%llx]\n",
lba, lba);
diff --git a/sg_wr_mode.8 b/sg_wr_mode.8
index d4c8bfe1..6b111f48 100644
--- a/sg_wr_mode.8
+++ b/sg_wr_mode.8
@@ -19,8 +19,9 @@ SENSE command then the same block descriptors are written back with the
following MODE SELECT command.
.PP
If a contents argument is not given then the various components (i.e.
-header, block descriptor(s) and mode page) of the existing mode page
-are printed out. In this case the mode page is not altered on the device.
+header, block descriptor(s) and mode page) of the "current" values of
+the existing mode page are printed out. In this case the mode page is
+not altered on the device.
.PP
If the contents are specified, and a mask is not specified, then the contents
must match the existing mode page in various aspects unless the '--force'
@@ -58,12 +59,12 @@ contents of the mode page to be written to the device, potentially filtered
by the mask string.
.TP
--dbd | -d
-disable block descriptors. Some device types include block descriptors
-in the mode data returned by a MODE SENSE command. If so the same
-block descriptors are written by the MODE SELECT command. This option
-instructs the MODE SENSE command not to return any block descriptors.
-This would be a sensible default for this utility apart from the fact
-that not all SCSI devices support this flag.
+disable block descriptors (DBD flag in cdb). Some device types include
+block descriptors in the mode data returned by a MODE SENSE command. If
+so the same block descriptors are written by the MODE SELECT command.
+This option instructs the MODE SENSE command not to return any block
+descriptors. This would be a sensible default for this utility apart
+from the fact that not all SCSI devices support the DBD bit in the cdb.
.TP
--force | -f
force the contents string to be taken as the new mode page, or at least
diff --git a/sg_wr_mode.c b/sg_wr_mode.c
index 0745eba6..2f454abc 100644
--- a/sg_wr_mode.c
+++ b/sg_wr_mode.c
@@ -46,7 +46,7 @@
* mode page on the given device.
*/
-static char * version_str = "1.02 20050120";
+static char * version_str = "1.04 20050306";
#define ME "sg_wr_mode: "
@@ -83,7 +83,8 @@ static void usage()
" to write\n"
" --contents=- | -c - read stdin for mode page contents"
" to write\n"
- " --dbd | -d disable block descriptors\n"
+ " --dbd | -d disable block descriptors (DBD bit"
+ " in cdb)\n"
" --force | -f force the contents to be written\n"
" --help | -h print out usage message\n"
" --len=<10|6> | -l <10|6> use 10 byte (def) or 6 byte "
@@ -424,15 +425,20 @@ int main(int argc, char * argv[])
memset(ref_md, 0, MX_ALLOC_LEN);
alloc_len = mode_6 ? SHORT_ALLOC_LEN : MX_ALLOC_LEN;
if (mode_6)
- res = sg_ll_mode_sense6(sg_fd, dbd, 0, pg_code, sub_pg_code, ref_md,
- alloc_len, 1, verbose);
+ res = sg_ll_mode_sense6(sg_fd, dbd, 0 /*current */, pg_code,
+ sub_pg_code, ref_md, alloc_len, 1, verbose);
else
- res = sg_ll_mode_sense10(sg_fd, 0 /* llbaa */, dbd, 0, pg_code,
- sub_pg_code, ref_md, alloc_len, 1, verbose);
+ res = sg_ll_mode_sense10(sg_fd, 0 /* llbaa */, dbd, 0 /* current */,
+ pg_code, sub_pg_code, ref_md, alloc_len, 1,
+ verbose);
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_ILLEGAL_REQ == res) {
+ fprintf(stderr, "bad field in MODE SENSE (%d) command\n",
+ (mode_6 ? 6 : 10));
+ goto err_out;
} else if (0 != res) {
fprintf(stderr, "MODE SENSE (%d) failed\n", (mode_6 ? 6 : 10));
goto err_out;
@@ -458,7 +464,7 @@ int main(int argc, char * argv[])
}
ref_md[0] = 0;
if (! mode_6)
- ref_md[0] = 0;
+ ref_md[1] = 0;
if (md_len > alloc_len) {
fprintf(stderr, "mode data length=%d exceeds allocation "
"length=%d\n", md_len, alloc_len);
@@ -511,6 +517,10 @@ int main(int argc, char * argv[])
fprintf(stderr, "MODE SELECT (%d) not supported\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));
+ goto err_out;
} else if (0 != res) {
fprintf(stderr, "MODE SELECT (%d) failed\n", (mode_6 ? 6 : 10));
goto err_out;
@@ -522,7 +532,8 @@ int main(int argc, char * argv[])
if (bd_len) {
printf(" block descriptor(s):\n");
dStrHex((const char *)(ref_md + hdr_len), bd_len, -1);
- }
+ } else
+ printf(" << no block descriptors >>\n");
printf(" mode page:\n");
dStrHex((const char *)(ref_md + off), md_len - off, -1);
}
diff --git a/sg_write_long.8 b/sg_write_long.8
index 235ce5e3..1ed85d84 100644
--- a/sg_write_long.8
+++ b/sg_write_long.8
@@ -1,4 +1,4 @@
-.TH SG_WRITE_LONG "8" "January 2005" "sg3_utils-1.12" SG3_UTILS
+.TH SG_WRITE_LONG "8" "February 2005" "sg3_utils-1.13" SG3_UTILS
.SH NAME
sg_write_long \- send the SCSI command write long
.SH SYNOPSIS
@@ -9,10 +9,10 @@ sg_write_long \- send the SCSI command write long
.\" Add any additional description here
.PP
Send WRITE LONG command to a Linux SCSI device. The
-buffer to be written to the device is filled with byte
+buffer to be written to the device is filled with
.B 0xff
-or read from the given file. This buffer includes the sector data and
-the ECC bytes.
+bytes or read from the given file. This buffer includes the sector data
+and the ECC bytes.
.PP
This utility can be used generate a MEDIUM ERROR at a specific logical
block address. This can be useful for testing. Prior to testing, the
@@ -54,8 +54,9 @@ error response and printed (to stderr).
.PP
The lba and xfer_len numerical arguments may be followed by the following
multiplicative suffixes:
-c C *1; b B *512; k *1,024; K *1,000; m *1,048,576; M *1,000,000;
-g *1,073,741,824; and G *1,000,000,000
+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; g G GiB *1,073,741,824; and GB *1,000,000,000 . Also a suffix
+of the form "x<n>" multiplies the leading number by <n>.
.PP
Alternatively numerical values can be given in hexadecimal preceded by
either "0x" or "0X". When hex numbers are given multipliers cannot be
@@ -76,14 +77,25 @@ 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.
+.PP
+If recoverable errors are being injected (e.g. only one or a few bits
+changed so that the ECC is able to correct the data) then care should
+be taken with the settings in the "read write error recovery" mode page.
+Specifically if the ARRE (for reads) and/or AWRE (for writes) all set
+then recovered errors will cause the lba to be reassigned (and the old
+location to be added to the grown defect list). This is not easily
+reversed and uses the (finite number) of spare blocks sets aside for
+this purpose. If in doubt it is probably safest to clear the ARRE and
+AWRE bits. These bits can be checked and modified with sginfo, sg_modes
+and sg_wr_mode.
.SH AUTHORS
Written by Saeed Bishara.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2004 Douglas Gilbert
+Copyright \(co 2004-2005 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_read_long, sg_dd
+.B sg_read_long, sg_dd, sginfo, sg_modes, sg_wr_mode(all in sg3_utils)
diff --git a/sg_write_long.c b/sg_write_long.c
index 2c1ba1e1..ed8531b8 100644
--- a/sg_write_long.c
+++ b/sg_write_long.c
@@ -27,11 +27,14 @@
This code was contributed by Saeed Bishara
*/
-static char * version_str = "1.04 20050118";
+static char * version_str = "1.05 20050309";
#define WRITE_LONG_OPCODE 0x3F
#define WRITE_LONG_CMD_LEN 10
+#define MAX_XFER_LEN 10000
+#define SENSE_BUFF_LEN 64
+
/* #define SG_DEBUG */
#define ME "sg_write_long: "
@@ -59,7 +62,7 @@ static void usage()
" --lba=<num>|-l <num> logical block address (default 0)\n"
" --verbose|-v increase verbosity\n"
" --version|-V print version string then exit\n"
- " --xfer_len=<num>|-x <num> transfer length (<1000) "
+ " --xfer_len=<num>|-x <num> transfer length (< 10000) "
"default 520\n"
"\n To read from a defected sector use:\n"
" sg_dd if=<scsi_device> skip=<lba> of=/dev/null bs=512 "
@@ -92,7 +95,7 @@ static int info_offset(unsigned char * sensep, int sb_len)
return 0;
}
-static int has_ili(unsigned char * sensep, int sb_len)
+static int has_blk_ili(unsigned char * sensep, int sb_len)
{
int resp_code;
const unsigned char * cup;
@@ -115,7 +118,7 @@ int main(int argc, char * argv[])
unsigned char writeLongCmdBlk [WRITE_LONG_CMD_LEN];
unsigned char * writeLongBuff = NULL;
void * rawp = NULL;
- unsigned char sense_buffer[64];
+ unsigned char sense_buffer[SENSE_BUFF_LEN];
int xfer_len = 520;
unsigned int lba = 0;
int verbose = 0;
@@ -190,9 +193,9 @@ int main(int argc, char * argv[])
usage();
return 1;
}
- if (xfer_len >= 1000){
- fprintf(stderr, "xfer_len (%d) is out of range ( < 1000)\n",
- xfer_len);
+ 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;
}
@@ -203,13 +206,13 @@ int main(int argc, char * argv[])
return 1;
}
- if (NULL == (rawp = malloc(1000))) {
+ if (NULL == (rawp = malloc(MAX_XFER_LEN))) {
fprintf(stderr, ME "out of memory (query)\n");
close(sg_fd);
return 1;
}
writeLongBuff = rawp;
- memset(rawp, 0xff, 1000);
+ memset(rawp, 0xff, MAX_XFER_LEN);
if (file_name[0]) {
got_stdin = (0 == strcmp(file_name, "-")) ? 1 : 0;
if (got_stdin)
@@ -279,11 +282,10 @@ int main(int argc, char * argv[])
sb_len = io_hdr.sb_len_wr;
/* now for the error processing */
switch (sg_err_category3(&io_hdr)) {
- case SG_LIB_CAT_CLEAN:
- break;
case SG_LIB_CAT_RECOVERED:
- fprintf(stderr, "Recovered error on WRITE LONG command, "
- "continuing\n");
+ sg_chk_n_print3("WRITE LONG, continuing", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
break;
default: /* won't bother decoding other categories */
if ((sg_normalize_sense(&io_hdr, &ssh)) &&
@@ -294,7 +296,7 @@ int main(int argc, char * argv[])
fprintf(stderr, "<<< nothing written to device >>>\n");
fprintf(stderr, "<<< device indicates 'xfer_len' should be %d "
">>>\n", xfer_len - offset);
- if (! has_ili(io_hdr.sbp, io_hdr.sb_len_wr))
+ if (! has_blk_ili(io_hdr.sbp, io_hdr.sb_len_wr))
fprintf(stderr, " [Invalid Length Indication (ILI) flag "
"expected but not found]\n");
goto err_out;
diff --git a/sginfo.8 b/sginfo.8
index 8dec4aa9..907ad9d1 100644
--- a/sginfo.8
+++ b/sginfo.8
@@ -1,4 +1,4 @@
-.TH SGINFO "8" "October 2004" "sg3_utils-1.09" SG3_UTILS
+.TH SGINFO "8" "February 2005" "sg3_utils-1.13" SG3_UTILS
.SH NAME
sginfo \- access mode page information for a SCSI (or ATAPI) device
.SH SYNOPSIS
@@ -82,7 +82,8 @@ Access information in the Format Device mode page.
.TP
-Farg
Format of the defect lists:
- -Flogical - logical blocks
+ -Flogical - logical block addresses (32 bit)
+ -Flba64 - logical block addresses (64 bit)
-Fphysical - physical blocks
-Findex - defect bytes from index
-Fhead - sort by head
diff --git a/sginfo.c b/sginfo.c
index e25a0196..2fb3f88f 100644
--- a/sginfo.c
+++ b/sginfo.c
@@ -7,7 +7,7 @@
* Options are:
* -6 do 6 byte mode sense + select (deafult: 10 byte)
* -a display all mode pages reported by the device: equivalent to '-t 63'.
- * -A display all mode pages and subpages reported by the device: equivalent
+ * -A display all mode pages and subpages reported by the device: equivalent
* to '-t 63,255'.
* -c access Cache control page.
* -C access Control Page.
@@ -16,7 +16,7 @@
* -e access Read-Write error recovery page.
* -E access Control Extension page.
* -f access Format Device Page.
- * -Farg defect list format (-Flogical, -Fphysical, -Findex, -Fhead)
+ * -Farg defect list format (-Flogical, -flba64, -Fphysical, -Findex, -Fhead)
* -g access rigid disk geometry page.
* -G display only "grown" defect list (default format: index)
* -i display information from Inquiry command.
@@ -106,7 +106,7 @@
#define _XOPEN_SOURCE 500
#define _GNU_SOURCE
-static const char * sginfo_version_str = "sginfo version 2.09 [20050119]";
+static const char * sginfo_version_str = "sginfo version 2.11 [20050309]";
#include <stdio.h>
#include <string.h>
@@ -201,7 +201,7 @@ static int spi4_training_config(struct mpage_info * mpi, const char * prefix);
static int spi4_negotiated(struct mpage_info * mpi, const char * prefix);
static int spi4_report_xfer(struct mpage_info * mpi, const char * prefix);
-enum page_class {PC_COMMON, PC_DISK, PC_TAPE, PC_CDVD, PC_SES};
+enum page_class {PC_COMMON, PC_DISK, PC_TAPE, PC_CDVD, PC_SES, PC_SMC};
struct mpage_name_func {
int page;
@@ -251,6 +251,7 @@ static struct mpage_name_func mpage_disk[] =
{ 0xb, 0, PC_DISK, "Medium Types Supported", NULL},
{ 0xc, 0, PC_DISK, "Notch and Partition", disk_notch_parameters},
{ 0x10, 0, PC_DISK, "XOR control", disk_xor_control},
+ { 0x1c, 1, PC_DISK, "Background control", NULL},
};
static const int mpage_disk_len = sizeof(mpage_disk) / sizeof(mpage_disk[0]);
@@ -282,6 +283,7 @@ static struct mpage_name_func mpage_tape[] =
{ 0x13, 0, PC_TAPE, "Medium partition(3)", tape_medium_part2_4},
{ 0x14, 0, PC_TAPE, "Medium partition(4)", tape_medium_part2_4},
{ 0x1c, 0, PC_TAPE, "Informational Exceptions", common_informational},
+ { 0x1d, 0, PC_TAPE, "Medium configuration", NULL},
};
static const int mpage_tape_len = sizeof(mpage_tape) / sizeof(mpage_tape[0]);
@@ -291,6 +293,15 @@ static struct mpage_name_func mpage_ses[] =
};
static const int mpage_ses_len = sizeof(mpage_ses) / sizeof(mpage_ses[0]);
+static struct mpage_name_func mpage_smc[] =
+{
+ { 0x1d, 0, PC_SMC, "Element address assignment", NULL},
+ { 0x1e, 0, PC_SMC, "Transport geometry parameters", NULL},
+ { 0x1f, 0, PC_SMC, "Device capabilities", NULL},
+ { 0x1f, 1, PC_SMC, "Extended device capabilities", NULL},
+};
+static const int mpage_smc_len = sizeof(mpage_smc) / sizeof(mpage_smc[0]);
+
static char * transport_proto_arr[] =
{
@@ -396,8 +407,10 @@ static int do_scsi_io(struct scsi_cmnd_io * sio)
}
res = sg_err_category3(&io_hdr);
switch (res) {
- case SG_LIB_CAT_CLEAN:
case SG_LIB_CAT_RECOVERED:
+ sg_chk_n_print3("do_scsi_cmd, continuing", &io_hdr);
+ /* fall through */
+ case SG_LIB_CAT_CLEAN:
return 0;
default:
if (trace_cmd) {
@@ -448,8 +461,9 @@ enum page_class get_page_class(struct mpage_info * mpi)
return PC_DISK;
case 1:
case 2:
- case 8: /* should be SMC */
return PC_TAPE;
+ case 8: /* should be SMC */
+ return PC_SMC;
case 5:
return PC_CDVD;
case 0xd:
@@ -481,6 +495,10 @@ struct mpage_name_func * get_mpage_name_func(struct mpage_info * mpi)
mpf = get_mpage_info(mpi->page, mpi->subpage, mpage_ses,
mpage_ses_len);
break;
+ case PC_SMC:
+ mpf = get_mpage_info(mpi->page, mpi->subpage, mpage_smc,
+ mpage_smc_len);
+ break;
case PC_COMMON:
/* picked up it catch all next */
break;
@@ -538,16 +556,33 @@ static void dump(void *buffer, unsigned int length)
}
-static int getnbyte(unsigned char *pnt, int nbyte)
+static int getnbyte(const unsigned char *pnt, int nbyte)
{
unsigned int result;
int i;
+
+ if (nbyte > 4)
+ fprintf(stderr, "getnbyte() limited to 32 bits, nbyte=%d\n", nbyte);
result = 0;
for (i = 0; i < nbyte; i++)
result = (result << 8) | (pnt[i] & 0xff);
return result;
}
+static long long getnbyte_ll(const unsigned char *pnt, int nbyte)
+{
+ long long result;
+ int i;
+
+ if (nbyte > 8)
+ fprintf(stderr, "getnbyte_ll() limited to 64 bits, nbyte=%d\n",
+ nbyte);
+ result = 0;
+ for (i = 0; i < nbyte; i++)
+ result = (result << 8) + (pnt[i] & 0xff);
+ return result;
+}
+
static int putnbyte(unsigned char *pnt, unsigned int value,
unsigned int nbyte)
{
@@ -1115,9 +1150,9 @@ static int common_disconnect_reconnect(struct mpage_info * mpi,
};
intfield(pagestart + 2, 1, "Buffer full ratio");
intfield(pagestart + 3, 1, "Buffer empty ratio");
- intfield(pagestart + 4, 2, "Bus Inactivity Limit");
+ intfield(pagestart + 4, 2, "Bus Inactivity Limit (SAS: 100us)");
intfield(pagestart + 6, 2, "Disconnect Time Limit");
- intfield(pagestart + 8, 2, "Connect Time Limit");
+ intfield(pagestart + 8, 2, "Connect Time Limit (SAS: 100us)");
intfield(pagestart + 10, 2, "Maximum Burst Size");
bitfield(pagestart + 12, "EMDP", 1, 7);
bitfield(pagestart + 12, "Fair Arbitration (fcp:faa,fab,fac)", 0x7, 4);
@@ -1179,7 +1214,7 @@ static int common_control_extension(struct mpage_info * mpi, const char * prefix
int status;
unsigned char *pagestart;
- status = setup_mode_page(mpi, 2, cbuffer, &pagestart);
+ status = setup_mode_page(mpi, 4, cbuffer, &pagestart);
if (status)
return status;
@@ -1190,6 +1225,8 @@ static int common_control_extension(struct mpage_info * mpi, const char * prefix
mpi->subpage);
printf("--------------------------------------------\n");
}
+ bitfield(pagestart + 4, "TCMOS", 1, 2);
+ bitfield(pagestart + 4, "SCSIP", 1, 1);
bitfield(pagestart + 4, "IALUAE", 1, 0);
bitfield(pagestart + 5, "Initial Priority", 0xf, 0);
@@ -1259,7 +1296,7 @@ static int disk_error_recovery(struct mpage_info * mpi, const char * prefix)
intfield(pagestart + 5, 1, "Head Offset Count");
intfield(pagestart + 6, 1, "Data Strobe Offset Count");
intfield(pagestart + 8, 1, "Write Retry Count");
- intfield(pagestart + 10, 2, "Recovery Time Limit");
+ intfield(pagestart + 10, 2, "Recovery Time Limit (ms)");
if (x_interface && replace)
return put_mode_page(mpi, cbuffer);
else
@@ -1375,18 +1412,19 @@ static int disk_notch_parameters(struct mpage_info * mpi, const char * prefix)
static char *formatname(int format) {
switch(format) {
- case 0x0: return "logical blocks";
+ case 0x0: return "logical block addresses (32 bit)";
+ case 0x3: return "logical block addresses (64 bit)";
case 0x4: return "bytes from index [Cyl:Head:Off]\n"
- "Offset -1 marks whole track as bad.\n";
+ "Offset -1 marks whole track as bad.\n";
case 0x5: return "physical blocks [Cyl:Head:Sect]\n"
- "Sector -1 marks whole track as bad.\n";
+ "Sector -1 marks whole track as bad.\n";
}
return "Weird, unknown format";
}
static int read_defect_list(int grown_only)
{
- int i, len, reallen, table, k;
+ int i, len, reallen, table, k, defect_format;
int status = 0;
int header = 1;
int sorthead = 0;
@@ -1524,15 +1562,16 @@ trytenbyte:
else {
if (table && !status && !sorthead)
printf("\n");
+ defect_format = (bp[1] & 0x7);
printf("%d entries (%d bytes) in %s table.\n",
- reallen / ((bp[1] & 7) ? 8 : 4), reallen,
+ reallen / ((0 == defect_format) ? 4 : 8), reallen,
table ? "grown" : "manufacturer");
if (!sorthead)
- printf("Format (%x) is: %s\n",
- bp[1] & 7,
- formatname(bp[1] & 7));
+ printf("Format (%x) is: %s\n", defect_format,
+ formatname(defect_format));
i = 0;
- if ((bp[1] & 7) == 4) {
+ switch (defect_format) {
+ case 4: /* bytes from index */
while (len > 0) {
snprintf((char *)cbuffer1, 40, "%6d:%3u:%8d", getnbyte(df, 3),
df[3], getnbyte(df + 4, 4));
@@ -1549,7 +1588,7 @@ trytenbyte:
}
else if (!sorthead) printf("|");
}
- } else if ((bp[1] & 7) == 5) {
+ case 5: /* physical sector */
while (len > 0) {
snprintf((char *)cbuffer1, 40, "%6d:%2u:%5d", getnbyte(df, 3),
df[3], getnbyte(df + 4, 4));
@@ -1566,8 +1605,7 @@ trytenbyte:
}
else if (!sorthead) printf("|");
}
- }
- else {
+ case 0: /* lba (32 bit) */
while (len > 0) {
printf("%10d", getnbyte(df, 4));
len -= 4;
@@ -1580,6 +1618,23 @@ trytenbyte:
else
printf("|");
}
+ case 3: /* lba (64 bit) */
+ while (len > 0) {
+ printf("%15lld", getnbyte_ll(df, 8));
+ len -= 8;
+ df += 8;
+ i++;
+ if (i >= 5) {
+ printf("\n");
+ i = 0;
+ }
+ else
+ printf("|");
+ }
+ break;
+ default:
+ printf("unknown defect list format: %d\n", defect_format);
+ break;
}
if (i && !sorthead)
printf("\n");
@@ -1869,7 +1924,7 @@ static int cdvd_write_param(struct mpage_info * mpi, const char * prefix)
int status;
unsigned char *pagestart;
- status = setup_mode_page(mpi, 19, cbuffer, &pagestart);
+ status = setup_mode_page(mpi, 20, cbuffer, &pagestart);
if (status)
return status;
@@ -1888,6 +1943,7 @@ static int cdvd_write_param(struct mpage_info * mpi, const char * prefix)
bitfield(pagestart + 3, "Copy", 1, 4);
bitfield(pagestart + 3, "Track Mode", 0xf, 0);
bitfield(pagestart + 4, "Data Block type", 0xf, 0);
+ intfield(pagestart + 5, 1, "Link size");
bitfield(pagestart + 7, "Initiator app. code", 0x3f, 0);
intfield(pagestart + 8, 1, "Session Format");
intfield(pagestart + 10, 4, "Packet size");
@@ -2304,7 +2360,7 @@ static int ses_services_manag(struct mpage_info * mpi, const char * prefix)
printf("----------------------------------------------------\n");
}
bitfield(pagestart + 5, "ENBLTC", 1, 0);
- intfield(pagestart + 6, 2, "Maximum time to completion");
+ intfield(pagestart + 6, 2, "Maximum time to completion (100 ms units)");
if (x_interface && replace)
return put_mode_page(mpi, cbuffer);
@@ -3090,7 +3146,7 @@ static void show_devices()
printf("%s ", devices[k]);
close(fd);
};
- printf("\n"); // <<<<<<<<<<<<<<<<<<<<<
+ printf("\n"); /* <<<<<<<<<<<<<<<<<<<<< */
for (k = 0; k < MAX_SG_DEVS; k++) {
make_dev_name(name, k, do_numeric);
fd = open(name, O_RDWR | O_NONBLOCK);
@@ -3276,7 +3332,8 @@ static void usage(char *errtext)
"\t-E Access Control Extension page.\n"
"\t-f Access Format Device Page.\n"
"\t-Farg Format of the defect list:\n"
- "\t\t-Flogical - logical blocks\n"
+ "\t\t-Flogical - logical block addresses (32 bit)\n"
+ "\t\t-Flba64 - logical block addresses (64 bit)\n"
"\t\t-Fphysical - physical blocks\n"
"\t\t-Findex - defect bytes from index\n"
"\t\t-Fhead - sort by head\n", stdout);
@@ -3369,6 +3426,8 @@ int main(int argc, char *argv[])
case 'F':
if (!strcasecmp(optarg, "logical"))
defectformat = 0x0;
+ else if (!strcasecmp(optarg, "lba64"))
+ defectformat = 0x3;
else if (!strcasecmp(optarg, "physical"))
defectformat = 0x5;
else if (!strcasecmp(optarg, "index"))
diff --git a/sgm_dd.8 b/sgm_dd.8
index 64fb4932..6aefe31b 100644
--- a/sgm_dd.8
+++ b/sgm_dd.8
@@ -1,4 +1,4 @@
-.TH SGM_DD "8" "August 2004" "sg3_utils-1.08" SG3_UTILS
+.TH SGM_DD "8" "February 2005" "sg3_utils-1.13" SG3_UTILS
.SH NAME
sgm_dd \- copies data to and from files and devices. Specialised for
devices that understand the SCSI command set.
@@ -110,10 +110,12 @@ Raw device partition information can often be found with
[the "-ul" argument is useful in this respect].
.PP
BYTES and BLOCKS may be followed by one of these multiplicative suffixes:
-c C *1; b B *512; k *1,024; K *1,000; m *1,048,576; M *1,000,000;
-g *1,073,741,824; G *1,000,000,000; t *1,099,511,627,776 and
-T *1,000,000,000,000 (the latter two can only be used for count, skip
-and seek values).
+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
+suffixes can only be used for count, skip and seek values). Also a suffix of
+the form "x<n>" multiplies the leading number by <n>. These multiplicative
+suffixes are compatible with GNU's dd command (since 2002) which claims
+compliance with SI and with IEC 60027-2.
.PP
Alternatively numerical values can be given in hexadecimal preceded by
either "0x" or "0X". When hex numbers are given multipliers cannot be
@@ -157,7 +159,7 @@ Written by Doug Gilbert and Peter Allworth.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2000-2004 Douglas Gilbert
+Copyright \(co 2000-2005 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/sgm_dd.c b/sgm_dd.c
index 127050de..b2beaf99 100644
--- a/sgm_dd.c
+++ b/sgm_dd.c
@@ -38,10 +38,7 @@
This program complains if 'ibs' or 'obs' are given with a value
that differs from 'bs' (or the default 512).
If 'if' is not given or 'if=-' then stdin is assumed. If 'of' is
- not given or 'of=-' then stdout assumed. Multipliers:
- 'c','C' *1 'b','B' *512 'k' *1024 'K' *1000
- 'm' *(1024^2) 'M' *(1000^2) 'g' *(1024^3) 'G' *(1000^3)
- 't' *(1024^4) 'T' *(1000^4)
+ not given or 'of=-' then stdout assumed.
A non-standard argument "bpt" (blocks per transfer) is added to control
the maximum number of blocks in each transfer. The default value is 128.
@@ -57,7 +54,7 @@
This version is designed for the linux kernel 2.4 and 2.6 series.
*/
-static char * version_str = "1.17 20050107";
+static char * version_str = "1.19 20050309";
#define DEF_BLOCK_SIZE 512
#define DEF_BLOCKS_PER_TRANSFER 128
@@ -192,7 +189,9 @@ void usage()
" (only when read side is sg device (using mmap))\n");
}
-/* Return of 0 -> success, -1 -> failure, 2 -> try again */
+/* 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 */
int scsi_read_capacity(int sg_fd, long long * num_sect, int * sect_sz)
{
int k, res;
@@ -404,8 +403,7 @@ int sg_read(int sg_fd, unsigned char * buff, int blocks, long long from_block,
case SG_LIB_CAT_CLEAN:
break;
case SG_LIB_CAT_RECOVERED:
- fprintf(stderr, "Recovered error while reading block=%lld, num=%d\n",
- from_block, blocks);
+ sg_chk_n_print3("Reading, continuing", &io_hdr);
break;
case SG_LIB_CAT_MEDIA_CHANGED:
return 2;
@@ -474,8 +472,7 @@ int sg_write(int sg_fd, unsigned char * buff, int blocks, long long to_block,
case SG_LIB_CAT_CLEAN:
break;
case SG_LIB_CAT_RECOVERED:
- fprintf(stderr, "Recovered error while writing block=%lld, num=%d\n",
- to_block, blocks);
+ sg_chk_n_print3("Writing, continuing", &io_hdr);
break;
case SG_LIB_CAT_MEDIA_CHANGED:
return 2;
@@ -560,21 +557,49 @@ int main(int argc, char * argv[])
return 1;
} else
strncpy(outf, buf, INOUTF_SZ);
- } else if (0 == strcmp(key,"ibs"))
+ } else if (0 == strcmp(key,"ibs")) {
ibs = sg_get_num(buf);
- else if (0 == strcmp(key,"obs"))
+ if (-1 == ibs) {
+ fprintf(stderr, ME "bad argument to 'ibs'\n");
+ return 1;
+ }
+ } else if (0 == strcmp(key,"obs")) {
obs = sg_get_num(buf);
- else if (0 == strcmp(key,"bs"))
+ if (-1 == obs) {
+ fprintf(stderr, ME "bad argument to 'obs'\n");
+ return 1;
+ }
+ } else if (0 == strcmp(key,"bs")) {
bs = sg_get_num(buf);
- else if (0 == strcmp(key,"bpt"))
+ if (-1 == bs) {
+ fprintf(stderr, ME "bad argument to 'bs'\n");
+ return 1;
+ }
+ } else if (0 == strcmp(key,"bpt")) {
bpt = sg_get_num(buf);
- else if (0 == strcmp(key,"skip"))
+ if (-1 == bpt) {
+ fprintf(stderr, ME "bad argument to 'bpt'\n");
+ return 1;
+ }
+ } else if (0 == strcmp(key,"skip")) {
skip = sg_get_llnum(buf);
- else if (0 == strcmp(key,"seek"))
+ if (-1LL == skip) {
+ fprintf(stderr, ME "bad argument to 'skip'\n");
+ return 1;
+ }
+ } else if (0 == strcmp(key,"seek")) {
seek = sg_get_llnum(buf);
- else if (0 == strcmp(key,"count"))
+ if (-1LL == seek) {
+ fprintf(stderr, ME "bad argument to 'seek'\n");
+ return 1;
+ }
+ } else if (0 == strcmp(key,"count")) {
dd_count = sg_get_llnum(buf);
- else if (0 == strcmp(key,"time"))
+ if (-1LL == dd_count) {
+ fprintf(stderr, ME "bad argument to 'count'\n");
+ return 1;
+ }
+ } else if (0 == strcmp(key,"time"))
do_time = sg_get_num(buf);
else if (0 == strcmp(key,"cdbsz")) {
scsi_cdbsz_in = sg_get_num(buf);
@@ -768,13 +793,17 @@ int main(int argc, char * argv[])
in_num_sect = -1;
if (FT_SG == in_type) {
res = scsi_read_capacity(infd, &in_num_sect, &in_sect_sz);
- if (2 == res) {
+ if (SG_LIB_CAT_MEDIA_CHANGED == res) {
fprintf(stderr,
"Unit attention, media changed(in), continuing\n");
res = scsi_read_capacity(infd, &in_num_sect, &in_sect_sz);
}
if (0 != res) {
- fprintf(stderr, "Unable to read capacity on %s\n", inf);
+ if (res == SG_LIB_CAT_INVALID_OP)
+ fprintf(stderr, "read capacity not supported on %s\n",
+ inf);
+ else
+ fprintf(stderr, "Unable to read capacity on %s\n", inf);
in_num_sect = -1;
}
} else if (FT_BLOCK == in_type) {
@@ -794,13 +823,17 @@ 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 (2 == res) {
+ if (SG_LIB_CAT_MEDIA_CHANGED == res) {
fprintf(stderr,
"Unit attention, media changed(out), continuing\n");
res = scsi_read_capacity(outfd, &out_num_sect, &out_sect_sz);
}
if (0 != res) {
- fprintf(stderr, "Unable to read capacity on %s\n", outf);
+ if (res == SG_LIB_CAT_INVALID_OP)
+ fprintf(stderr, "read capacity not supported on %s\n",
+ outf);
+ else
+ fprintf(stderr, "Unable to read capacity on %s\n", outf);
out_num_sect = -1;
}
} else if (FT_BLOCK == out_type) {
diff --git a/sgp_dd.8 b/sgp_dd.8
index a88748e2..2cae422d 100644
--- a/sgp_dd.8
+++ b/sgp_dd.8
@@ -1,4 +1,4 @@
-.TH SGP_DD "8" "October 2004" "sg3_utils-1.10" SG3_UTILS
+.TH SGP_DD "8" "February 2005" "sg3_utils-1.13" SG3_UTILS
.SH NAME
sgp_dd \- copies data to and from files and devices. Specialised for
devices that understand the SCSI command set.
@@ -120,11 +120,13 @@ Raw device partition information can often be found with
.B fdisk(8)
[the "-ul" argument is useful in this respect].
.PP
-BYTES and BLOCKS may be followed by the following multiplicative suffixes:
-c C *1; b B *512; k *1,024; K *1,000; m *1,048,576; M *1,000,000;
-g *1,073,741,824; G *1,000,000,000; t *1,099,511,627,776 and
-T *1,000,000,000,000 (the latter two can only be used for count, skip
-and seek values).
+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
+suffixes can only be used for count, skip and seek values). Also a suffix of
+the form "x<n>" multiplies the leading number by <n>. These multiplicative
+suffixes are compatible with GNU's dd command (since 2002) which claims
+compliance with SI and with IEC 60027-2.
.PP
Alternatively numerical values can be given in hexadecimal preceded by
either "0x" or "0X". When hex numbers are given multipliers cannot be
@@ -154,7 +156,7 @@ which has a negative impact on other machine activity.
.PP
Looks quite similar in usage to dd:
.PP
- sgp_dd if=/dev/sg0 of=t bs=512 count=1M
+ sgp_dd if=/dev/sg0 of=t bs=512 count=1MB
.PP
This will copy 1 million 512 byte blocks from the device associated with
/dev/sg0 (which should have 512 byte blocks) to a file called t.
@@ -168,7 +170,7 @@ reduced. Using a raw device to do something similar on a IDE disk:
.PP
raw /dev/raw/raw1 /dev/hda
.br
- sgp_dd if=/dev/raw/raw1 of=t bs=512 count=1M
+ sgp_dd if=/dev/raw/raw1 of=t bs=512 count=1MB
.PP
To copy a SCSI disk partition to an IDE disk partition:
.PP
@@ -190,7 +192,7 @@ Written by Doug Gilbert and Peter Allworth.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2000-2004 Douglas Gilbert
+Copyright \(co 2000-2005 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/sgp_dd.c b/sgp_dd.c
index 7a0f20c2..5593a2a6 100644
--- a/sgp_dd.c
+++ b/sgp_dd.c
@@ -37,10 +37,7 @@
device. A block size ('bs') is assumed to be 512 if not given. This
program complains if 'ibs' or 'obs' are given with some other value
than 'bs'. If 'if' is not given or 'if=-' then stdin is assumed. If
- 'of' is not given or 'of=-' then stdout assumed. Multipliers:
- 'c','C' *1 'b','B' *512 'k' *1024 'K' *1000
- 'm' *(1024^2) 'M' *(1000^2) 'g' *(1024^3) 'G' *(1000^3)
- 't' *(1024^4) 'T' *(1000^4)
+ 'of' is not given or 'of=-' then stdout assumed.
A non-standard argument "bpt" (blocks per transfer) is added to control
the maximum number of blocks in each transfer. The default value is 128.
@@ -52,7 +49,7 @@
*/
-static char * version_str = "5.21 20050113";
+static char * version_str = "5.23 20050309";
#define DEF_BLOCK_SIZE 512
#define DEF_BLOCKS_PER_TRANSFER 128
@@ -261,7 +258,9 @@ static void guarded_stop_both(Rq_coll * clp)
guarded_stop_out(clp);
}
-/* Return of 0 -> success, -1 -> failure, 2 -> try again */
+/* 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 */
int scsi_read_capacity(int sg_fd, long long * num_sect, int * sect_sz)
{
int k, res;
@@ -858,8 +857,8 @@ int sg_finish_io(int wr, Rq_elem * rep, pthread_mutex_t * a_mutp)
case SG_LIB_CAT_CLEAN:
break;
case SG_LIB_CAT_RECOVERED:
- fprintf(stderr, "Recovered error on block=%d, num=%d\n",
- rep->blk, rep->num_blks);
+ sg_chk_n_print3((rep->wr ? "writing continuing":
+ "reading continuing"), hp);
break;
case SG_LIB_CAT_MEDIA_CHANGED:
return 1;
@@ -986,21 +985,49 @@ int main(int argc, char * argv[])
return 1;
} else
strncpy(outf, buf, INOUTF_SZ);
- } else if (0 == strcmp(key,"ibs"))
+ } else if (0 == strcmp(key,"ibs")) {
ibs = sg_get_num(buf);
- else if (0 == strcmp(key,"obs"))
+ if (-1 == ibs) {
+ fprintf(stderr, ME "bad argument to 'ibs'\n");
+ return 1;
+ }
+ } else if (0 == strcmp(key,"obs")) {
obs = sg_get_num(buf);
- else if (0 == strcmp(key,"bs"))
+ if (-1 == obs) {
+ fprintf(stderr, ME "bad argument to 'obs'\n");
+ return 1;
+ }
+ } else if (0 == strcmp(key,"bs")) {
rcoll.bs = sg_get_num(buf);
- else if (0 == strcmp(key,"bpt"))
+ if (-1 == rcoll.bs) {
+ fprintf(stderr, ME "bad argument to 'bs'\n");
+ return 1;
+ }
+ } else if (0 == strcmp(key,"bpt")) {
rcoll.bpt = sg_get_num(buf);
- else if (0 == strcmp(key,"skip"))
+ if (-1 == rcoll.bpt) {
+ fprintf(stderr, ME "bad argument to 'bpt'\n");
+ return 1;
+ }
+ } else if (0 == strcmp(key,"skip")) {
skip = sg_get_llnum(buf);
- else if (0 == strcmp(key,"seek"))
+ if (-1LL == skip) {
+ fprintf(stderr, ME "bad argument to 'skip'\n");
+ return 1;
+ }
+ } else if (0 == strcmp(key,"seek")) {
seek = sg_get_llnum(buf);
- else if (0 == strcmp(key,"count"))
+ if (-1LL == seek) {
+ fprintf(stderr, ME "bad argument to 'seek'\n");
+ return 1;
+ }
+ } else if (0 == strcmp(key,"count")) {
count = sg_get_llnum(buf);
- else if (0 == strcmp(key,"dio"))
+ if (-1LL == count) {
+ fprintf(stderr, ME "bad argument to 'count'\n");
+ return 1;
+ }
+ } else if (0 == strcmp(key,"dio"))
rcoll.dio = sg_get_num(buf);
else if (0 == strcmp(key,"thr"))
num_threads = sg_get_num(buf);
@@ -1160,7 +1187,11 @@ int main(int argc, char * argv[])
&in_sect_sz);
}
if (0 != res) {
- fprintf(stderr, "Unable to read capacity on %s\n", inf);
+ if (res == SG_LIB_CAT_INVALID_OP)
+ fprintf(stderr, "read capacity not supported on %s\n",
+ inf);
+ else
+ fprintf(stderr, "Unable to read capacity on %s\n", inf);
in_num_sect = -1;
}
} else if (FT_BLOCK == rcoll.in_type) {
@@ -1188,7 +1219,11 @@ int main(int argc, char * argv[])
&out_sect_sz);
}
if (0 != res) {
- fprintf(stderr, "Unable to read capacity on %s\n", outf);
+ if (res == SG_LIB_CAT_INVALID_OP)
+ fprintf(stderr, "read capacity not supported on %s\n",
+ outf);
+ else
+ fprintf(stderr, "Unable to read capacity on %s\n", outf);
out_num_sect = -1;
}
} else if (FT_BLOCK == rcoll.out_type) {