aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2018-01-13 06:38:53 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2018-01-13 06:38:53 +0000
commit239d147229acac3d3504360a38cd5ec8505d0d5e (patch)
tree308272c943a93228ac732eff81e7f94840d6e76e
parent605b21190ce9af77ee3533e12b2e59a7f883ffee (diff)
downloadsg3_utils-239d147229acac3d3504360a38cd5ec8505d0d5e.tar.gz
sg_ses_microcode: add --dry-run and --ealsd; sg_write_buffer: add --dry-run
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@744 6180dd3e-e324-4e3e-922d-17de1ae2f315
-rw-r--r--COVERAGE6
-rw-r--r--ChangeLog5
-rw-r--r--debian/changelog2
-rw-r--r--doc/sg3_utils.84
-rw-r--r--doc/sg_format.82
-rw-r--r--doc/sg_logs.82
-rw-r--r--doc/sg_persist.88
-rw-r--r--doc/sg_sat_identify.855
-rw-r--r--doc/sg_ses.88
-rw-r--r--doc/sg_ses_microcode.865
-rw-r--r--doc/sg_test_rwbuf.819
-rw-r--r--doc/sg_verify.86
-rw-r--r--doc/sg_write_buffer.823
-rw-r--r--doc/sg_write_x.82
-rw-r--r--include/sg_cmds_extra.h11
-rw-r--r--lib/sg_cmds_basic.c2
-rw-r--r--lib/sg_cmds_extra.c31
-rw-r--r--lib/sg_lib.c5
-rw-r--r--lib/sg_lib_data.c9
-rw-r--r--lib/sg_pt_freebsd.c139
-rw-r--r--lib/sg_pt_linux_nvme.c51
-rw-r--r--sg3_utils.spec2
-rw-r--r--src/sg_format.c15
-rw-r--r--src/sg_inq.c166
-rw-r--r--src/sg_ses.c37
-rw-r--r--src/sg_ses_microcode.c363
-rw-r--r--src/sg_write_buffer.c74
27 files changed, 742 insertions, 370 deletions
diff --git a/COVERAGE b/COVERAGE
index 83c8105f..7e12d29d 100644
--- a/COVERAGE
+++ b/COVERAGE
@@ -126,11 +126,13 @@ SET FEATURES sg_sat_set_features
SMART READ DATA examples/sg_sat_smart_rd_data
-NVMe command sg3_utild utilities that use this NVMe command
+NVMe command sg3_utils utilities that use this NVMe command
------------ ------------------------------------------------
IDENTIFY sg_inq
SES READ sg_senddiag, sg_ses (NVME-MI command)
SES WRITE sg_senddiag, sg_ses (NVME-MI command)
+Device self-test [SNTL of SEND DIAGNOSTIC] sg_senddiag
+Get features(power management) [SNTL of REQUEST SENSE] sg_requests
++ command wrapper found in sg_cmds_basic.c, sg_cmds_mmc.c or
@@ -149,4 +151,4 @@ THIRD PARTY COPY IN (0x83).
Douglas Gilbert
-30th November 2017
+8th January 2018
diff --git a/ChangeLog b/ChangeLog
index 914472c3..28a8b043 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,7 +2,7 @@ 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. There is also a sg3_utils man page.
-Changelog for sg3_utils-1.43 [20180108] [svn: r743]
+Changelog for sg3_utils-1.43 [20180112] [svn: r744]
- sg_bg_ctl: new Background control command (sbc4r08)
- sg_write_x: where x can be normal, atomic, orwrite,
same, scattered, or stream writes with 16 or 32 byte
@@ -27,8 +27,10 @@ Changelog for sg3_utils-1.43 [20180108] [svn: r743]
Receive SES commands
- decode array status diagnostic page (obsolete)
- sync to ses4r01
+ - sg_ses_microcode: add --dry-run and -ealsd options
- sg_ses, sg_ses_microcode, sg_senddiag: make all access
buffer page size aligned (typically page_size=4096)
+ - sg_write_buffer: add --dry-run option
- sg_luns: resync with drafts (sam6r02+spc5r10)
- remove undocumented test "W" format
- accept and output on request "quad dashed" format
@@ -104,6 +106,7 @@ Changelog for sg3_utils-1.43 [20180108] [svn: r743]
- add sg_get_llnum_nomult()
- add sg_ll_get_lba_status16()
- add sg_ll_get_lba_status32()
+ - add sg_ll_format_unit_v2()
- add sg_get_sfs_name() for spc5r11 (Feature sets)
- add sg_decode_transportid_str()
- add sg_msense_calc_length()
diff --git a/debian/changelog b/debian/changelog
index f001f885..da81096c 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,7 +2,7 @@ sg3-utils (1.43-0.1) unstable; urgency=low
* New upstream version
- -- Douglas Gilbert <dgilbert@interlog.com> Mon, 08 Jan 2018 01:00:00 -0500
+ -- Douglas Gilbert <dgilbert@interlog.com> Sat, 13 Jan 2018 01:00:00 -0500
sg3-utils (1.42-0.1) unstable; urgency=low
diff --git a/doc/sg3_utils.8 b/doc/sg3_utils.8
index 93b4e1bb..7238303b 100644
--- a/doc/sg3_utils.8
+++ b/doc/sg3_utils.8
@@ -329,7 +329,7 @@ OS system calls that fail often return a small integer number to help
indicate what the error is. For example in Unix the inability of a system
call to allocate memory returns (in 'errno') ENOMEM which often is
associated with the integer 12. So 62 (i.e. '50 + 12') may be returned
-by a utility in this case. It is also possible that a utiity in this
+by a utility in this case. It is also possible that a utility in this
package reports 50+ENOMEM when it can't allocate memory, not necessarily
from an OS system call. In recent versions of Linux the file showing the
mapping between symbolic constants (e.g. ENOMEM) and the corresponding
@@ -448,7 +448,7 @@ usually sent to stderr so as to not interfere with the output from this
option.
.br
Some utilities that consume data to send to the \fIDEVICE\fR along with the
-SCSI command, use this option. Alernatively the \fI\-\-in=FN\fR option causes
+SCSI command, use this option. Alternatively the \fI\-\-in=FN\fR option causes
\fIDEVICE\fR to be ignored and the response data (to be decoded) fetched
from a file named \fIFN\fR. In these cases this option may indicate that
binary data can be read from stdin or from a nominated file (e.g. \fIFN\fR).
diff --git a/doc/sg_format.8 b/doc/sg_format.8
index 449fa605..5d0d22e5 100644
--- a/doc/sg_format.8
+++ b/doc/sg_format.8
@@ -138,7 +138,7 @@ which no data has been written to, since the fast format. When \fIFFMT\fR
is 1 the read operation should return "unspecified logical block data" and
complete without error. When \fIFFMT\fR is 2 the read operation should
yield check condition status with a sense key set to hardware error, medium
-error or command aborted. See SBC\-4 revsion 10 section 4.35 for more
+error or command aborted. See SBC\-4 revision 10 section 4.35 for more
details.
.TP
\fB\-f\fR, \fB\-\-fmtpinfo\fR=\fIFPI\fR
diff --git a/doc/sg_logs.8 b/doc/sg_logs.8
index 72212829..04bb4f56 100644
--- a/doc/sg_logs.8
+++ b/doc/sg_logs.8
@@ -50,7 +50,7 @@ file (named \fIFN\fR) and decodes it as if it were a response from a LOG
SENSE command. The third form shows the options that can be used to send a
LOG SELECT command. The fourth form groups various management options.
The last form shows the older, deprecated command line interface which is
-maintaimed for backward compatibility.
+maintained for backward compatibility.
.SH OPTIONS
Arguments to long options are mandatory for short options as well.
The options are arranged in alphabetical order based on the long
diff --git a/doc/sg_persist.8 b/doc/sg_persist.8
index ca0eb5db..167d2665 100644
--- a/doc/sg_persist.8
+++ b/doc/sg_persist.8
@@ -1,4 +1,4 @@
-.TH SG_PERSIST "8" "November 2017" "sg3_utils\-1.43" SG3_UTILS
+.TH SG_PERSIST "8" "January 2018" "sg3_utils\-1.43" SG3_UTILS
.SH NAME
sg_persist \- use SCSI PERSISTENT RESERVE command to access registrations
and reservations
@@ -237,7 +237,7 @@ The \fITIDS\fR argument can take one of several forms. It can be a
comma (or single space) separated list of ASCII hex bytes representing
a single TransportID as defined in SPC\-4. They are usually 24 bytes
long apart from in iSCSI. The \fITIDS\fR argument may be a transport
-specific form (e.g. "sas,5000c50005b32001" is clearer than and equivalent
+specific form (e.g. "sas,5000c50005b32001" is clearer than an equivalent
to the hex byte form: "6,0,0,0,5,0,c5,0,5,b3,20,1"). The \fITIDS\fR argument
may be "\-" in which case one or more TransportIDs can be read from stdin.
The \fITIDS\fR argument may be of the form "file=<name>" in which case
@@ -305,7 +305,7 @@ command line invocation of sg_persist with "LANG=en_US.utf\-8" for example.
Alternatively the \fITIDS\fR argument may specify a file (or pipe) from which
one or more TransportIDs may be read. If the \fITIDS\fR argument is "\-"
then stdin (standard input) is read. If the \fITIDS\fR argument is of the
-form "file=<name>" than a file called <name> is read.
+form "file=<name>" then a file called <name> is read.
A valid SPC\-4 TransportID is built from the transport specific string
outlined in the previous paragraphs. The parsing of the data read is
relatively simple. Empty lines are ignored. Everything from and including
@@ -423,7 +423,7 @@ Written by Douglas Gilbert
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2004\-2017 Douglas Gilbert
+Copyright \(co 2004\-2018 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/doc/sg_sat_identify.8 b/doc/sg_sat_identify.8
index db05d0f2..aee34674 100644
--- a/doc/sg_sat_identify.8
+++ b/doc/sg_sat_identify.8
@@ -1,4 +1,4 @@
-.TH SG_SAT_IDENTIFY "8" "October 2017" "sg3_utils\-1.43" SG3_UTILS
+.TH SG_SAT_IDENTIFY "8" "January 2018" "sg3_utils\-1.43" SG3_UTILS
.SH NAME
sg_sat_identify \- send ATA IDENTIFY DEVICE command via SCSI to ATA
Translation (SAT) layer
@@ -24,7 +24,7 @@ byte "cdb" and the other with a 12 byte cdb. This utility defaults to using
the 16 byte cdb variant. SAT\-4 revision 5 added a SCSI "ATA
PASS\-THROUGH(32)" command. SAT\-2 and SAT\-3 are now also standards: SAT\-2
ANSI INCITS 465\-2010 and SAT\3 ANSI INCITS 517-2015 . The SAT\-4 project
-is ongiong and the most recent draft is sat4r05c.pdf .
+is near standardization and the most recent draft is sat4r06.pdf .
.SH OPTIONS
Arguments to long options are mandatory for short options as well.
.TP
@@ -102,6 +102,55 @@ does not clash with anything so it is a better choice.
.PP
Prior to Linux kernel 2.6.29 USB mass storage limited sense data to 18 bytes
which made the \fB\-\-ck_cond\fR option yield strange (truncated) results.
+.SH EXAMPLES
+These examples use Linux device names and a Linux utility called hdparm. For
+suitable device names in other supported Operating Systems see the
+sg3_utils(8) man page.
+.PP
+In this example /dev/sdb is a SATA 2.5" disk connected via a USB (type C
+connector) dongle that implements the UAS (USB attached SCSI) protocol (also
+known as UASP). UAS is a vast improvement over the USB mass storage class.
+.PP
+ # sg_sat_identify /dev/sdb
+.br
+Response for IDENTIFY DEVICE ATA command:
+.br
+ 00 0c5a 3fff c837 0010 0000 0000 003f 0000 .Z ?. .7 .. .. .. .? ..
+.br
+ ....
+.PP
+The hexadecimal ASCII (with plain ASCII to the right) output is abridged
+to a single line (i.e. the first 16 bytes (or 8 words)). Now to decode
+some of that ATA Identify response. First sg_inq can decode a few strings:
+.PP
+ # sg_sat_identify \-HHHH /dev/sdb | sg_inq \-\-ata \-I \-
+.br
+ATA device: model, serial number and firmware revision:
+.br
+ ST9500420AS 5VJCE6R7 0002SDM1
+.PP
+For a lot more details, the hdparm utility is a good choice:
+.PP
+ # sg_sat_identify \-HHH /dev/sdb | hdparm \-\-Istdin
+.br
+ATA device, with non\-removable media
+.br
+ Model Number: ST9500420AS
+.br
+ Serial Number: 5VJCE6R7
+.br
+ Firmware Revision: 0002SDM1
+.br
+ Transport: Serial
+.br
+Standards:
+.br
+ ....
+.PP
+There are about 80 more lines of details decoded by hdparm in this case.
+Notice the difference in the number of "H" options: three give an unadorned
+hex output arranged in (little endian) words (i.e. 16 bits each) while
+four "H" options give an unadorned hex output in bytes (i.e. 8 bits each).
.SH EXIT STATUS
The exit status of sg_sat_identify is 0 when it is successful. Otherwise
see the sg3_utils(8) man page.
@@ -110,7 +159,7 @@ Written by Douglas Gilbert
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2006\-2017 Douglas Gilbert
+Copyright \(co 2006\-2018 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/doc/sg_ses.8 b/doc/sg_ses.8
index bef92cfe..56234047 100644
--- a/doc/sg_ses.8
+++ b/doc/sg_ses.8
@@ -248,8 +248,10 @@ the read\-mask\-modify\-write sequence.
DIAGNOSTIC RESULTS commands sent by the utility. It represents the maximum
size of data the SES device can return (in bytes). It cannot exceed 65535
and defaults to 65532 (bytes). Some systems may not permit such large sizes
-hence the need for this option. If \fILEN\fR is set to 0 then the default
-size is used.
+hence the need for this option. If \fILEN\fR is less than 0 or greater than
+65535 then an error is generated. If \fILEN\fR is 0 then the default value
+is used, otherwise if it is less than 4 then it is ignored (and a warning is
+sent to stderr).
.TP
\fB\-n\fR, \fB\-\-nickname\fR=\fISEN\fR
where \fISEN\fR is the new Subenclosure Nickname. Only the first 32
@@ -381,7 +383,7 @@ individual index then the option is equivalent to \fI\-\-index=0,II\fR. When
.PP
Wherever an individual index is applicable, it can be replaced by an
individual index range. It has the form: <first_ii>-<last_ii>. For
-example: '3\-5' will select individial indexes 3, 4 and 5 .
+example: '3\-5' will select individual indexes 3, 4 and 5 .
.PP
To cope with vendor specific Element types (which should be in the range 128
to 255) the Element type can be given as a number with a leading underscore.
diff --git a/doc/sg_ses_microcode.8 b/doc/sg_ses_microcode.8
index 3e4a9d27..0f486b03 100644
--- a/doc/sg_ses_microcode.8
+++ b/doc/sg_ses_microcode.8
@@ -1,12 +1,13 @@
-.TH SG_SES_MICROCODE "8" "October 2014" "sg3_utils\-1.40" SG3_UTILS
+.TH SG_SES_MICROCODE "8" "January 2018" "sg3_utils\-1.43" SG3_UTILS
.SH NAME
sg_ses_microcode \- send microcode to a SCSI enclosure
.SH SYNOPSIS
.B sg_ses_microcode
-[\fI\-\-bpw=CS\fR] [\fI\-\-help\fR] [\fI\-\-id=ID\fR] [\fI\-\-in=FILE\fR]
-[\fI\-\-length=LEN\fR] [\fI\-\-mode=MO\fR] [\fI\-\-non\fR]
-[\fI\-\-offset=OFF\fR] [\fI\-\-skip=SKIP\fR] [\fI\-\-subenc=MS\fR]
-[\fI\-\-tlength=TLEN\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+[\fI\-\-bpw=CS\fR] [\fI\-\-dry\-run\fR] [\fI\-\-ealsd\fR] [\fI\-\-help\fR]
+[\fI\-\-id=ID\fR] [\fI\-\-in=FILE\fR] [\fI\-\-length=LEN\fR]
+[\fI\-\-mode=MO\fR] [\fI\-\-non\fR] [\fI\-\-offset=OFF\fR]
+[\fI\-\-skip=SKIP\fR] [\fI\-\-subenc=MS\fR] [\fI\-\-tlength=TLEN\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
.SH DESCRIPTION
.\" Add any additional description here
.PP
@@ -53,6 +54,33 @@ In this case after the microcode has been successfully sent to the
\fIDEVICE\fR, an additional Download microcode control dpage with its mode
set to "Activate deferred microcode" [0xf] is sent.
.TP
+\fB\-d\fR, \fB\-\-dry\-run\fR
+the actual calls to perform SEND DIAGNOSTIC and RECEIVE DIAGNOSTIC RESULTS
+commands are skipped when this option is given. No SCSI commands are sent
+to the \fIDEVICE\fR but it is still opened and is required to be given.
+A dummy device such as /dev/null (in Unix) can be used.
+.br
+This utility expects a "sensible" response to the RECEIVE DIAGNOSTIC RESULTS
+command it sends (and will abort if it doesn't receive one). So this option
+supplies dummy responses with one primary enclosure and three
+sub\-enclosures. The dummy responses include good status values.
+.TP
+\fB\-e\fR, \fB\-\-ealsd\fR
+exit after last SEND DIAGNOSTIC command. A SES device should not start its
+firmware update immediately after the last received "chunk" of its firmware.
+Rather it should wait till at least one RECEIVE DIAGNOSTIC RESULTS command
+is sent to give the device a chance to report any error. However some
+devices do start the firmware update immediately which causes the trailing
+RECEIVE DIAGNOSTIC RESULTS command to be held up and often be aborted with
+a "target reset" error.
+.br
+This option causes the trailing RECEIVE DIAGNOSTIC RESULTS command to be
+skipped. This option would be typically used with the \fI\-\-bpw=CS\fR
+option.
+.br
+Prior to version 1.10 of this utility [20180112] this (i.e. skipping
+the last RECEIVE DIAGNOSTIC RESULTS command) was the default action.
+.TP
\fB\-h\fR, \fB\-\-help\fR
output the usage message then exit. If used multiple times also prints
the mode names and their acronyms.
@@ -91,7 +119,8 @@ dpage combination is avoided unless an error has already occurred.
\fB\-o\fR, \fB\-\-offset\fR=\fIOFF\fR
this option sets the BUFFER OFFSET field in the Download microcode control
dpage. \fIOFF\fR is a value between 0 (default) and 2**32\-1 . It is a
-byte offset.
+byte offset. This option is ignored (and a warning sent to stderr) if the
+\fI\-\-bpw=CS\fR option is also given.
.TP
\fB\-s\fR, \fB\-\-skip\fR=\fISKIP\fR
this option is only active when \fI\-\-in=FILE\fR is given and \fIFILE\fR is
@@ -100,14 +129,18 @@ a regular file, rather than stdin. Data is read starting at byte offset
If not given the byte offset defaults to 0 (i.e. the start of the file).
.TP
\fB\-S\fR, \fB\-\-subenc\fR=\fISEID\fR
-\fISEID\fR is the subenclosure identify. It defaults to 0 which is the
+\fISEID\fR is the sub\-enclosure identify. It defaults to 0 which is the
primary enclosure identifier.
.TP
\fB\-t\fR, \fB\-\-tlength\fR=\fITLEN\fR
\fITLEN\fR is the total length in bytes of the microcode to be (or being)
-downloaded. It defaults to 0 which is okay in most cases. This option is
-only needed when sections of microcode and being sent in separate invocations
-of this utility.
+downloaded. It defaults to 0 which is okay in most cases. This option only
+comes into play when \fITLEN\fR is greater than \fILEN\fR. In this case
+\fITLEN\fR is sent to the SES \fIDEVICE\fR so that it knows when it only
+receives \fILEN\fR bytes from this invocation, that it should expect more
+to be sent in the near future (e.g. by another invocation). This option
+is only needed when sections of microcode are being sent in separate
+invocations of this utility (e.g. the microcode is spread across two files).
.TP
\fB\-v\fR, \fB\-\-verbose\fR
increase the level of verbosity, (i.e. debug output).
@@ -139,7 +172,7 @@ resetting.
.PP
Apart from dmc_status, these are placed in the Download microcode mode
field in the Download microcode control dpage. In the case of dmc_status
-the Download microcode status dpage is fetch with the RECEIVE DIAGNOSTIC
+the Download microcode status dpage is fetched with the RECEIVE DIAGNOSTIC
RESULTS command and decoded.
.SH WHEN THE DOWNLOAD FAILS
Firstly, if it succeeds, this utility should stay silent and return.
@@ -154,7 +187,7 @@ be needed.
If something goes wrong, there will typically be messages printed out
by this utility. The first thing to check is the microcode (firmware)
file itself. Is it designed for the device model; has it been corrupted,
-and if downgrading (i.e. trying to re-instate older firmware), does
+and if downgrading (i.e. trying to reinstate older firmware), does
the vendor allow that?
.PP
Getting new firmware on a device is a delicate operation that is not
@@ -204,11 +237,11 @@ happening, fetching the status page should not cause any problems:
.br
Download microcode status diagnostic page:
.br
- number of secondary subenclosures: 0
+ number of secondary sub\-enclosures: 0
.br
generation code: 0x0
.br
- subenclosure identifier: 0 [primary]
+ sub\-enclosure identifier: 0 [primary]
.br
download microcode status: No download microcode operation in progress [0x0]
.br
@@ -238,9 +271,9 @@ Written by Douglas Gilbert.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2014 Douglas Gilbert
+Copyright \(co 2014\-2018 Douglas Gilbert
.br
This software is distributed under a FreeBSD license. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
.SH "SEE ALSO"
-.B sg_ses, sg_write_buffer(sg3_utils)
+.B sg_ses, sg_write_buffer, sg_inq(sg3_utils)
diff --git a/doc/sg_test_rwbuf.8 b/doc/sg_test_rwbuf.8
index f30c3517..d610531e 100644
--- a/doc/sg_test_rwbuf.8
+++ b/doc/sg_test_rwbuf.8
@@ -1,4 +1,4 @@
-.TH SG_TEST_RWBUF "8" "November 2012" "sg3_utils\-1.35" SG3_UTILS
+.TH SG_TEST_RWBUF "8" "January 2018" "sg3_utils\-1.43" SG3_UTILS
.SH NAME
sg_test_rwbuf \- test a SCSI host adapter by issuing dummy writes
and reads
@@ -26,12 +26,13 @@ read \fIAR\fR additional bytes.
Arguments to long options are mandatory for short options as well.
.TP
\fB\-r\fR, \fB\-\-addrd\fR=\fIAR\fR
-Read an additional \fIAR\fR bytes (than indicated by \fISZ\fR) from the data
-buffer. Checksum is performed over the first \fISZ\fR bytes.
+Read an additional \fIAR\fR bytes (more than indicated by \fISZ\fR) from the
+data buffer. Checksum is performed over the first \fISZ\fR bytes.
.TP
\fB\-w\fR, \fB\-\-addwr\fR=\fIAW\fR
-Write an additional \fIAW\fR bytes (than indicated by \fISZ\fR) of zeros
-into the data buffer. Checksum is generated over the first \fISZ\fR bytes.
+Write an additional \fIAW\fR bytes (more than indicated by \fISZ\fR) of
+zeros into the data buffer. Checksum is generated over the first \fISZ\fR
+bytes.
.TP
\fB\-h\fR, \fB\-\-help\fR
Print out a usage message the exit.
@@ -70,16 +71,16 @@ 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.
+Following this theme further, a disk with active mounted file systems may
+cause the data read back to be different (due to caching activity) to what
+was written and hence a checksum error.
.SH EXIT STATUS
The exit status of sg_test_rwbuf is 0 when it is successful. Otherwise see
the sg3_utils(8) man page.
.SH AUTHORS
Written by D. Gilbert and K. Garloff
.SH COPYRIGHT
-Copyright \(co 2000\-2012 Douglas Gilbert, Kurt Garloff
+Copyright \(co 2000\-2018 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/doc/sg_verify.8 b/doc/sg_verify.8
index 4e7ddf3a..51c8f880 100644
--- a/doc/sg_verify.8
+++ b/doc/sg_verify.8
@@ -1,4 +1,4 @@
-.TH SG_VERIFY "8" "November 2017" "sg3_utils\-1.43" SG3_UTILS
+.TH SG_VERIFY "8" "January 2018" "sg3_utils\-1.43" SG3_UTILS
.SH NAME
sg_verify \- invoke SCSI VERIFY command(s) on a block device
.SH SYNOPSIS
@@ -64,7 +64,7 @@ field in the block limits VPD page.
\fB\-c\fR, \fB\-\-count\fR=\fICOUNT\fR
where \fICOUNT\fR specifies the number of blocks to verify. The default value
is 1 . If \fICOUNT\fR is greater than \fIBPC\fR (or its default value of 128)
-and \fINDO\fR is not given, 0 or less then multiple SCSI VERIFY commands are
+and \fINDO\fR is not given, 0 or less than multiple SCSI VERIFY commands are
sent to the device. Otherwise \fICOUNT\fR becomes the contents of the
verification length field of the SCSI VERIFY command issued. The
.B sg_readcap
@@ -198,7 +198,7 @@ Written by Douglas Gilbert.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2004\-2017 Douglas Gilbert
+Copyright \(co 2004\-2018 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/doc/sg_write_buffer.8 b/doc/sg_write_buffer.8
index 888288db..cde60deb 100644
--- a/doc/sg_write_buffer.8
+++ b/doc/sg_write_buffer.8
@@ -1,12 +1,13 @@
-.TH SG_WRITE_BUFFER "8" "October 2017" "sg3_utils\-1.43" SG3_UTILS
+.TH SG_WRITE_BUFFER "8" "January 2018" "sg3_utils\-1.43" SG3_UTILS
.SH NAME
sg_write_buffer \- send SCSI WRITE BUFFER commands
.SH SYNOPSIS
.B sg_write_buffer
-[\fI\-\-bpw=CS\fR] [\fI\-\-help\fR] [\fI\-\-id=ID\fR] [\fI\-\-in=FILE\fR]
-[\fI\-\-length=LEN\fR] [\fI\-\-mode=MO\fR] [\fI\-\-offset=OFF\fR]
-[\fI\-\-read\-stdin\fR] [\fI\-\-skip=SKIP\fR] [\fI\-\-specific=MS\fR]
-[\fI\-\-timeout=TO\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+[\fI\-\-bpw=CS\fR] [\fI\-\-dry\-run\fR] [\fI\-\-help\fR] [\fI\-\-id=ID\fR]
+[\fI\-\-in=FILE\fR] [\fI\-\-length=LEN\fR] [\fI\-\-mode=MO\fR]
+[\fI\-\-offset=OFF\fR] [\fI\-\-read\-stdin\fR] [\fI\-\-skip=SKIP\fR]
+[\fI\-\-specific=MS\fR] [\fI\-\-timeout=TO\fR] [\fI\-\-verbose\fR]
+[\fI\-\-version\fR] \fIDEVICE\fR
.SH DESCRIPTION
.\" Add any additional description here
.PP
@@ -40,6 +41,14 @@ In this case after WRITE BUFFER commands have been sent until the
effective length is exhausted another WRITE BUFFER command with its mode
set to "Activate deferred microcode mode" [mode 0xf] is sent.
.TP
+\fB\-d\fR, \fB\-\-dry\-run\fR
+Do all the command line processing and sanity checks including reading
+the input file. However at the point where a WRITE BUFFER SCSI command(s)
+would be sent, step over that call and assume it completed without errors
+and continue. \fIDEVICE\fR is still opened but can be /dev/null (in Unix).
+It is recommended to use \fI\-\-verbose\fR with this option to get an
+overview of what would have happened.
+.TP
\fB\-h\fR, \fB\-\-help\fR
output the usage message then exit. If used multiple times also prints
the mode names and their acronyms.
@@ -75,7 +84,7 @@ between 0 (default) and 2**24\-1 . It is a byte offset.
\fB\-r\fR, \fB\-\-read\-stdin\fR
read data from stdin until an EOF is detected. This data is sent with
the WRITE BUFFER command to \fIDEVICE\fR. The action of this option is the
-same as using '\-\-in=\-'. Previousy this option's long name was
+same as using '\-\-in=\-'. Previously this option's long name was
\fI\-\-raw\fR and it may still be used for backward compatibility.
.TP
\fB\-s\fR, \fB\-\-skip\fR=\fISKIP\fR
@@ -210,7 +219,7 @@ Written by Luben Tuikov and Douglas Gilbert.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2006\-2017 Luben Tuikov and Douglas Gilbert
+Copyright \(co 2006\-2018 Luben Tuikov and Douglas Gilbert
.br
This software is distributed under a FreeBSD license. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/doc/sg_write_x.8 b/doc/sg_write_x.8
index 294096d4..9d965a5f 100644
--- a/doc/sg_write_x.8
+++ b/doc/sg_write_x.8
@@ -15,6 +15,8 @@ sg_write_x \- SCSI WRITE normal/ATOMIC/SAME/SCATTERED/STREAM, ORWRITE commands
[\fI\-\-unmap=U_A\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR]
[\fI\-\-wrprotect=WPR\fR] \fIDEVICE\fR
.PP
+Synopsis per supported command:
+.PP
.B sg_write_x
\fI\-\-normal\fR \fI\-\-in=IF\fR [\fI\-\-16\fR] [\fI\-\-32\fR]
[\fI\-\-app\-tag=AT\fR] [\fI\-\-bs=BS\fR] [\fI\-\-dld=DLD\fR] [\fI\-\-dpo\fR]
diff --git a/include/sg_cmds_extra.h b/include/sg_cmds_extra.h
index 32405068..2dedf21e 100644
--- a/include/sg_cmds_extra.h
+++ b/include/sg_cmds_extra.h
@@ -2,7 +2,7 @@
#define SG_CMDS_EXTRA_H
/*
- * Copyright (c) 2004-2017 Douglas Gilbert.
+ * Copyright (c) 2004-2018 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
@@ -52,7 +52,8 @@ int sg_ll_ata_pt(int sg_fd, const unsigned char * cdbp, int cdb_len,
* SG_LIB_CAT_INVALID_OP -> Format unit not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
- * -1 -> other failure */
+ * -1 -> other failure. Note that sg_ll_format_unit2() and
+ * sg_ll_format_unit_v2() are the same, both add the ffmt argument. */
int sg_ll_format_unit(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata,
bool cmplist, int dlist_format, int timeout_secs,
void * paramp, int param_len, bool noisy, int verbose);
@@ -60,6 +61,10 @@ int sg_ll_format_unit2(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata,
bool cmplist, int dlist_format, int ffmt,
int timeout_secs, void * paramp, int param_len,
bool noisy, int verbose);
+int sg_ll_format_unit_v2(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata,
+ bool cmplist, int dlist_format, int ffmt,
+ int timeout_secs, void * paramp, int param_len,
+ bool noisy, int verbose);
/* Invokes a SCSI GET LBA STATUS(16) or GET LBA STATUS(32) command (SBC).
* Returns 0 -> success,
@@ -217,7 +222,7 @@ int sg_ll_report_referrals(int sg_fd, uint64_t start_llba, bool one_seg,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
-int sg_ll_send_diag(int sg_fd, int sf_code, bool pf_bit, bool sf_bit,
+int sg_ll_send_diag(int sg_fd, int st_code, bool pf_bit, bool st_bit,
bool devofl_bit, bool unitofl_bit, int long_duration,
void * paramp, int param_len, bool noisy, int verbose);
diff --git a/lib/sg_cmds_basic.c b/lib/sg_cmds_basic.c
index 479d45b3..2f0cbac0 100644
--- a/lib/sg_cmds_basic.c
+++ b/lib/sg_cmds_basic.c
@@ -36,7 +36,7 @@
#endif
-static const char * const version_str = "1.78 20180104";
+static const char * const version_str = "1.79 20180112";
#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
diff --git a/lib/sg_cmds_extra.c b/lib/sg_cmds_extra.c
index cbd8e626..a6072c95 100644
--- a/lib/sg_cmds_extra.c
+++ b/lib/sg_cmds_extra.c
@@ -452,7 +452,7 @@ sg_ll_report_referrals(int sg_fd, uint64_t start_llba, bool one_seg,
* value is taken as the timeout value in seconds. Return of 0 -> success,
* various SG_LIB_CAT_* positive values or -1 -> other errors */
int
-sg_ll_send_diag(int sg_fd, int sf_code, bool pf_bit, bool sf_bit,
+sg_ll_send_diag(int sg_fd, int st_code, bool pf_bit, bool st_bit,
bool devofl_bit, bool unitofl_bit, int long_duration,
void * paramp, int param_len, bool noisy, int verbose)
{
@@ -463,10 +463,10 @@ sg_ll_send_diag(int sg_fd, int sf_code, bool pf_bit, bool sf_bit,
unsigned char sense_b[SENSE_BUFF_LEN];
struct sg_pt_base * ptvp;
- senddiag_cdb[1] = (unsigned char)(sf_code << 5);
+ senddiag_cdb[1] = (unsigned char)(st_code << 5);
if (pf_bit)
senddiag_cdb[1] |= 0x10;
- if (sf_bit)
+ if (st_bit)
senddiag_cdb[1] |= 0x4;
if (devofl_bit)
senddiag_cdb[1] |= 0x2;
@@ -851,19 +851,32 @@ sg_ll_format_unit(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata,
bool cmplst, int dlist_format, int timeout_secs,
void * paramp, int param_len, bool noisy, int verbose)
{
- return sg_ll_format_unit2(sg_fd, fmtpinfo, longlist, fmtdata, cmplst,
- dlist_format, 0, timeout_secs, paramp,
- param_len, noisy, verbose);
+ return sg_ll_format_unit_v2(sg_fd, fmtpinfo, longlist, fmtdata, cmplst,
+ dlist_format, 0, timeout_secs, paramp,
+ param_len, noisy, verbose);
}
-/* Invokes a FORMAT UNIT (SBC-4) command. Return of 0 -> success,
- * various SG_LIB_CAT_* positive values or -1 -> other errors.
- * FFMT field added in sbc4r10 [20160121] */
+/* Invokes a FORMAT UNIT (SBC-3) command. Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
int
sg_ll_format_unit2(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata,
bool cmplst, int dlist_format, int ffmt, int timeout_secs,
void * paramp, int param_len, bool noisy, int verbose)
{
+ return sg_ll_format_unit_v2(sg_fd, fmtpinfo, longlist, fmtdata, cmplst,
+ dlist_format, ffmt, timeout_secs, paramp,
+ param_len, noisy, verbose);
+}
+
+/* Invokes a FORMAT UNIT (SBC-4) command. Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors.
+ * FFMT field added in sbc4r10 [20160121] */
+int
+sg_ll_format_unit_v2(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata,
+ bool cmplst, int dlist_format, int ffmt,
+ int timeout_secs, void * paramp, int param_len,
+ bool noisy, int verbose)
+{
static const char * const cdb_name_s = "format unit";
int k, res, ret, sense_cat, tmout;
unsigned char fu_cdb[FORMAT_UNIT_CMDLEN] =
diff --git a/lib/sg_lib.c b/lib/sg_lib.c
index f6bafb52..314a5508 100644
--- a/lib/sg_lib.c
+++ b/lib/sg_lib.c
@@ -27,13 +27,14 @@
*
*/
+#define _POSIX_C_SOURCE 200809L /* for posix_memalign() */
+#define __STDC_FORMAT_MACROS 1
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
-#define __STDC_FORMAT_MACROS 1
#include <inttypes.h>
#ifdef HAVE_CONFIG_H
@@ -3265,6 +3266,8 @@ sg_memalign(uint32_t num_bytes, uint32_t align_to, uint8_t ** buff_to_free,
size_t psz;
uint8_t * res;
+ if (buff_to_free) /* make sure buff_to_free is NULL if alloc fails */
+ *buff_to_free = NULL;
psz = (align_to > 0) ? align_to : sg_get_page_size();
if (0 == num_bytes)
num_bytes = psz; /* ugly to handle otherwise */
diff --git a/lib/sg_lib_data.c b/lib/sg_lib_data.c
index b3842632..5ac4a9bb 100644
--- a/lib/sg_lib_data.c
+++ b/lib/sg_lib_data.c
@@ -17,7 +17,7 @@
#include "sg_lib_data.h"
-const char * sg_lib_version_str = "2.36 20180104";/* spc5r17, sbc4r15 */
+const char * sg_lib_version_str = "2.37 20180109";/* spc5r17, sbc4r15 */
/* indexed by pdt; those that map to own index do not decay */
@@ -1507,6 +1507,8 @@ struct sg_lib_value_name_t sg_lib_scsi_feature_sets[] =
{0x0, 0, NULL}, /* 0x0 is reserved sfs; trailing sentinel */
};
+#if (SG_SCSI_STRINGS && HAVE_NVME && (! IGNORE_NVME))
+
/* .value is completion queue's DW3 as follows: ((DW3 >> 17) & 0x3ff)
* .peri_dev_type is an index for the sg_lib_scsi_status_sense_arr[]
* .name is taken from NVMe 1.3a document, section 4.6.1.2.1 with less
@@ -1516,7 +1518,6 @@ struct sg_lib_value_name_t sg_lib_scsi_feature_sets[] =
* Bits 29:28 are reserved, bit 27:25 are the "Status Code Type" (SCT)
* and bits 24:17 are the Status Code (SC). This table is in ascending
* order of its .value field so a binary search could be done on it. */
-#ifdef SG_SCSI_STRINGS
struct sg_lib_value_name_t sg_lib_nvme_cmd_status_arr[] =
{
/* Generic command status values, Status Code Type (SCT): 0h
@@ -1665,7 +1666,7 @@ struct sg_lib_4tuple_u8 sg_lib_scsi_status_sense_arr[] =
};
-#else /* no SG_SCSI_STRINGS define in config.sys */
+#else /* (SG_SCSI_STRINGS && HAVE_NVME && (! IGNORE_NVME)) */
struct sg_lib_value_name_t sg_lib_nvme_cmd_status_arr[] =
{
@@ -1680,4 +1681,4 @@ struct sg_lib_4tuple_u8 sg_lib_scsi_status_sense_arr[] =
{0xff, 0xff, 0xff, 0xff},
};
-#endif /* SG_SCSI_STRINGS */
+#endif /* (SG_SCSI_STRINGS && HAVE_NVME && (! IGNORE_NVME)) */
diff --git a/lib/sg_pt_freebsd.c b/lib/sg_pt_freebsd.c
index 9ded65ca..efad6d12 100644
--- a/lib/sg_pt_freebsd.c
+++ b/lib/sg_pt_freebsd.c
@@ -5,7 +5,7 @@
* license that can be found in the BSD_LICENSE file.
*/
-/* sg_pt_freebsd version 1.21 20180104 */
+/* sg_pt_freebsd version 1.22 20180112 */
#include <stdio.h>
#include <stdlib.h>
@@ -39,7 +39,13 @@
#include "sg_lib.h"
#include "sg_unaligned.h"
#include "sg_pt_nvme.h"
+
+#if (HAVE_NVME && (! IGNORE_NVME))
#include "freebsd_nvme_ioctl.h"
+#else
+#define NVME_CTRLR_PREFIX "/dev/nvme"
+#define NVME_NS_PREFIX "ns"
+#endif
#define FREEBSD_MAXDEV 64
@@ -128,10 +134,12 @@ pr2ws(const char * fmt, ...)
return n;
}
+#if (HAVE_NVME && (! IGNORE_NVME))
static inline bool is_aligned(const void * pointer, size_t byte_count)
{
- return (sg_uintptr_t)pointer % byte_count == 0;
+ return ((sg_uintptr_t)pointer % byte_count) == 0;
}
+#endif
/* Returns >= 0 if successful. If error in Unix returns negated errno. */
int
@@ -577,34 +585,6 @@ set_scsi_pt_flags(struct sg_pt_base * objp, int flags)
if (flags) { ; } /* unused, suppress warning */
}
-static int
-nvme_pt_low(struct freebsd_dev_channel *fdc_p, void * dxferp, uint32_t len,
- bool is_read, struct nvme_pt_command * npcp, int vb)
-{
- int err, status;
- uint8_t opcode;
- char b[80];
-
- if (fdc_p->dev_fd < 0) {
- if (vb)
- pr2ws("%s: is_nvme is true but dev_fd<0, inconsistent\n",
- __func__);
- return -EINVAL;
- }
- npcp->buf = dxferp;
- npcp->len = len;
- npcp->is_read = (uint32_t)is_read;
- opcode = npcp->cmd.opc;
- err = ioctl(fdc_p->dev_fd, NVME_PASSTHROUGH_CMD, npcp);
- if (err < 0)
- return -errno;
- status = ((npcp->cpl.status.sct << 8) | npcp->cpl.status.sc);
- if (status && vb)
- pr2ws("%s: opcode=0x%x, status: %s\n", __func__, opcode,
- sg_get_nvme_cmd_status_str(status, sizeof(b), b));
- return status;
-}
-
/* Executes SCSI command (or at least forwards it to lower layers).
* Clears os_err field prior to active call (whose result may set it
* again). */
@@ -950,6 +930,8 @@ get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b)
#define MICROCODE_CHANGED_ASCQ 0x1 /* with TARGET_CHANGED_ASC */
#define MICROCODE_CHANGED_WO_RESET_ASCQ 0x16
+#if (HAVE_NVME && (! IGNORE_NVME))
+
static void
build_sense_buffer(bool desc, uint8_t *buf, uint8_t skey, uint8_t asc,
uint8_t ascq)
@@ -1069,6 +1051,36 @@ mk_sense_invalid_fld(struct sg_pt_freebsd_scsi * ptp, bool in_cdb,
__func__, asc, in_cdb ? 'C' : 'D', in_byte, in_bit);
}
+/* Does actual ioctl(NVME_PASSTHROUGH_CMD). Returns 0 on success; negative
+ * values are Unix negated errno values; positive values are NVMe status. */
+static int
+nvme_pt_low(struct freebsd_dev_channel *fdc_p, void * dxferp, uint32_t len,
+ bool is_read, struct nvme_pt_command * npcp, int vb)
+{
+ int err, status;
+ uint8_t opcode;
+ char b[80];
+
+ if (fdc_p->dev_fd < 0) {
+ if (vb)
+ pr2ws("%s: is_nvme is true but dev_fd<0, inconsistent\n",
+ __func__);
+ return -EINVAL;
+ }
+ npcp->buf = dxferp;
+ npcp->len = len;
+ npcp->is_read = (uint32_t)is_read;
+ opcode = npcp->cmd.opc;
+ err = ioctl(fdc_p->dev_fd, NVME_PASSTHROUGH_CMD, npcp);
+ if (err < 0)
+ return -errno; /* Assume Unix error in normal place */
+ status = ((npcp->cpl.status.sct << 8) | npcp->cpl.status.sc);
+ if (status && vb)
+ pr2ws("%s: opcode=0x%x, status: %s\n", __func__, opcode,
+ sg_get_nvme_cmd_status_str(status, sizeof(b), b));
+ return status;
+}
+
static int
sntl_cache_identity(struct freebsd_dev_channel * fdc_p, int vb)
{
@@ -1081,7 +1093,7 @@ sntl_cache_identity(struct freebsd_dev_channel * fdc_p, int vb)
&fdc_p->free_nvme_id_ctlp, vb > 3);
if (NULL == fdc_p->nvme_id_ctlp) {
pr2ws("%s: sg_memalign() failed to get memory\n", __func__);
- return SG_LIB_OS_BASE_ERR + ENOMEM;
+ return -ENOMEM;
}
memset(npc_up, 0, sizeof(npc));
npc_up[SG_NVME_PT_OPCODE] = 0x6; /* Identify */
@@ -1094,12 +1106,11 @@ sntl_cache_identity(struct freebsd_dev_channel * fdc_p, int vb)
err = nvme_pt_low(fdc_p, fdc_p->nvme_id_ctlp, pg_sz, true, &npc, vb);
if (err) {
if (err < 0) {
- err = -err;
if (vb > 1)
pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", __func__,
- strerror(err), err);
- return SG_LIB_OS_BASE_ERR + err;
- } else {
+ strerror(-err), -err);
+ return err;
+ } else { /* non-zero NVMe command status */
fdc_p->nvme_status = err;
return SG_LIB_NVME_STATUS;
}
@@ -1132,7 +1143,7 @@ sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
fdc_p = get_fdc_p(ptp);
if (NULL == fdc_p) {
pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__);
- return SG_LIB_OS_BASE_ERR + EINVAL;
+ return -EINVAL;
}
if (NULL == fdc_p->nvme_id_ctlp) {
res = sntl_cache_identity(fdc_p, vb);
@@ -1246,7 +1257,7 @@ sntl_rluns(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
fdc_p = get_fdc_p(ptp);
if (NULL == fdc_p) {
pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__);
- return SG_LIB_OS_BASE_ERR + EINVAL;
+ return -EINVAL;
}
sel_report = cdbp[2];
alloc_len = sg_get_unaligned_be32(cdbp + 6);
@@ -1282,7 +1293,7 @@ sntl_rluns(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
rl_doutp = (uint8_t *)calloc(num + 1, 8);
if (NULL == rl_doutp) {
pr2ws("%s: calloc() failed to get memory\n", __func__);
- return SG_LIB_OS_BASE_ERR + ENOMEM;
+ return -ENOMEM;
}
for (k = 0, up = rl_doutp + 8; k < num; ++k, up += 8)
sg_put_unaligned_be16(k, up);
@@ -1316,7 +1327,7 @@ sntl_tur(struct sg_pt_freebsd_scsi * ptp, int vb)
fdc_p = get_fdc_p(ptp);
if (NULL == fdc_p) {
pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__);
- return SG_LIB_OS_BASE_ERR + EINVAL;
+ return -EINVAL;
}
if (NULL == fdc_p->nvme_id_ctlp) {
res = sntl_cache_identity(fdc_p, vb);
@@ -1334,11 +1345,10 @@ sntl_tur(struct sg_pt_freebsd_scsi * ptp, int vb)
err = nvme_pt_low(fdc_p, NULL, 0, false, &npc, vb);
if (err) {
if (err < 0) {
- err = -err;
if (vb > 1)
pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", __func__,
- strerror(err), err);
- return SG_LIB_OS_BASE_ERR + err;
+ strerror(-err), -err);
+ return err;
} else {
fdc_p->nvme_status = err;
mk_sense_from_nvme_status(ptp, err, vb);
@@ -1372,7 +1382,7 @@ sntl_req_sense(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
fdc_p = get_fdc_p(ptp);
if (NULL == fdc_p) {
pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__);
- return SG_LIB_OS_BASE_ERR + EINVAL;
+ return -EINVAL;
}
if (NULL == fdc_p->nvme_id_ctlp) {
res = sntl_cache_identity(fdc_p, vb);
@@ -1392,11 +1402,10 @@ sntl_req_sense(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
err = nvme_pt_low(fdc_p, NULL, 0, false, &npc, vb);
if (err) {
if (err < 0) {
- err = -err;
if (vb > 1)
pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", __func__,
- strerror(err), err);
- return SG_LIB_OS_BASE_ERR + err;
+ strerror(-err), -err);
+ return err;
} else {
fdc_p->nvme_status = err;
mk_sense_from_nvme_status(ptp, err, vb);
@@ -1450,7 +1459,7 @@ sntl_senddiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
fdc_p = get_fdc_p(ptp);
if (NULL == fdc_p) {
pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__);
- return SG_LIB_OS_BASE_ERR + EINVAL;
+ return -EINVAL;
}
if (self_test || st_cd) {
memset(npc_up, 0, sizeof(npc));
@@ -1472,7 +1481,8 @@ sntl_senddiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
break;
default:
pr2ws("%s: bad self-test code [0x%x]\n", __func__, st_cd);
- return SG_LIB_SYNTAX_ERROR;
+ mk_sense_invalid_fld(ptp, true, 1, 7, vb);
+ return 0;
}
sg_put_unaligned_le32(nvme_dst, npc_up + SG_NVME_PT_CDW10);
err = nvme_pt_low(fdc_p, NULL, 0x0, false, &npc, vb);
@@ -1541,11 +1551,10 @@ sntl_senddiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
do_low:
if (err) {
if (err < 0) {
- err = -err;
if (vb > 1)
pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n",
- __func__, strerror(err), err);
- return SG_LIB_OS_BASE_ERR + err;
+ __func__, strerror(-err), -err);
+ return err;
} else {
fdc_p->nvme_status = err;
mk_sense_from_nvme_status(ptp, err, vb);
@@ -1582,7 +1591,7 @@ sntl_recvdiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
fdc_p = get_fdc_p(ptp);
if (NULL == fdc_p) {
pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__);
- return SG_LIB_OS_BASE_ERR + EINVAL;
+ return -EINVAL;
}
din_len = ptp->dxfer_len;
if (pcv) {
@@ -1637,11 +1646,10 @@ sntl_recvdiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
err = nvme_pt_low(fdc_p, ptp->dxferp, 0x1000, true, &npc, vb);
if (err) {
if (err < 0) {
- err = -err;
if (vb > 1)
pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n",
- __func__, strerror(err), err);
- return SG_LIB_OS_BASE_ERR + err;
+ __func__, strerror(-err), -err);
+ return err;
} else {
fdc_p->nvme_status = err;
mk_sense_from_nvme_status(ptp, err, vb);
@@ -1680,7 +1688,7 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb)
if (fd < 0) {
if (NULL == fdc_p) {
pr2ws("%s: no device handle in object or fd ?\n", __func__);
- return SG_LIB_FILE_ERROR;
+ return -EINVAL;
}
} else {
int han = fd - FREEBSD_FDOFFSET;
@@ -1761,11 +1769,10 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb)
err = nvme_pt_low(fdc_p, dxferp, io_len, in_xfer, &npc, vb);
if (err) {
if (err < 0) {
- err = -err;
if (vb > 1)
pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n",
- __func__, strerror(err), err);
- return SG_LIB_OS_BASE_ERR + err;
+ __func__, strerror(-err), -err);
+ return err;
} else {
fdc_p->nvme_status = err;
mk_sense_from_nvme_status(ptp, err, vb);
@@ -1776,3 +1783,17 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb)
ptp->resid = 0; /* Just hoping ... */
return 0;
}
+
+#else /* if not(HAVE_NVME && (! IGNORE_NVME)) */
+
+static int
+sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb)
+{
+ if (vb)
+ pr2ws("%s: not supported\n", __func__);
+ if (vp) { ; } /* suppress warning */
+ if (fd) { ; } /* suppress warning */
+ return -ENOTTY; /* inappropriate ioctl error */
+}
+
+#endif /* (HAVE_NVME && (! IGNORE_NVME)) */
diff --git a/lib/sg_pt_linux_nvme.c b/lib/sg_pt_linux_nvme.c
index 9c2d8124..f62366f7 100644
--- a/lib/sg_pt_linux_nvme.c
+++ b/lib/sg_pt_linux_nvme.c
@@ -112,7 +112,7 @@
static inline bool is_aligned(const void * pointer, size_t byte_count)
{
- return (sg_uintptr_t)pointer % byte_count == 0;
+ return ((sg_uintptr_t)pointer % byte_count) == 0;
}
@@ -136,6 +136,8 @@ pr2ws(const char * fmt, ...)
return n;
}
+#if (HAVE_NVME && (! IGNORE_NVME))
+
/* This trims given NVMe block device name in Linux (e.g. /dev/nvme0n1p5)
* to the name of its associated char device (e.g. /dev/nvme0). If this
* occurs true is returned and the char device name is placed in 'b' (as
@@ -283,9 +285,10 @@ mk_sense_invalid_fld(struct sg_pt_linux_scsi * ptp, bool in_cdb, int in_byte,
/* Returns 0 for success. Returns SG_LIB_NVME_STATUS if there is non-zero
* NVMe status (from the completion queue) with the value placed in
- * ptp->nvme_status. If Unix error from ioctl add equivalent errno value to
- * SG_LIB_OS_BASE_ERR. Should not return negative values. CDW0 from
- * the completion queue is placed in ptp->nvme_result on success. */
+ * ptp->nvme_status. If Unix error from ioctl then return negated value
+ * (equivalent -errno from basic Unix system functions like open()).
+ * CDW0 from the completion queue is placed in ptp->nvme_result in the
+ * absence of a Unix error. */
static int
do_nvme_admin_cmd(struct sg_pt_linux_scsi * ptp,
struct sg_nvme_passthru_cmd *cmdp, const void * dp,
@@ -318,14 +321,14 @@ do_nvme_admin_cmd(struct sg_pt_linux_scsi * ptp,
res = ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, cmdp);
if (0 != res) {
if (res < 0) { /* OS error (errno negated) */
- res = (-res & 0x3ff); /* clear DNR and More, if present */
- ptp->os_err = res;
+ ptp->os_err = -res;
if (vb > 3) {
pr2ws("%s: ioctl opcode=0x%x failed: %s "
- "(errno=%d)\n", __func__, *up, strerror(res), res);
+ "(errno=%d)\n", __func__, *up, strerror(-res), -res);
}
- return SG_LIB_OS_BASE_ERR + res;
+ return res;
} else { /* NVMe errors are positive return values */
+ res &= 0x3ff; /* clear DNR and More bits */
ptp->nvme_status = res;
if (vb > 2) {
char b[80];
@@ -364,20 +367,20 @@ sntl_cache_identity(struct sg_pt_linux_scsi * ptp, int time_secs, int vb)
{
struct sg_nvme_passthru_cmd cmd;
uint32_t pg_sz = sg_get_page_size();
- void * vp;
+ uint8_t * up;
- vp = sg_memalign(pg_sz, pg_sz, &ptp->free_nvme_id_ctlp, vb > 3);
- ptp->nvme_id_ctlp = vp;
- if (NULL == vp) {
+ up = sg_memalign(pg_sz, pg_sz, &ptp->free_nvme_id_ctlp, vb > 3);
+ ptp->nvme_id_ctlp = up;
+ if (NULL == up) {
pr2ws("%s: sg_memalign() failed to get memory\n", __func__);
- return SG_LIB_OS_BASE_ERR + ENOMEM;
+ return -ENOMEM;
}
memset(&cmd, 0, sizeof(cmd));
cmd.opcode = 0x6; /* Identify */
cmd.cdw10 = 0x1; /* CNS=0x1 Identify controller */
cmd.addr = (uint64_t)(sg_uintptr_t)ptp->nvme_id_ctlp;
cmd.data_len = pg_sz;
- return do_nvme_admin_cmd(ptp, &cmd, vp, true, time_secs, vb);
+ return do_nvme_admin_cmd(ptp, &cmd, up, true, time_secs, vb);
}
static const char * nvme_scsi_vendor_str = "NVMe ";
@@ -543,7 +546,7 @@ sntl_rluns(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs,
rl_doutp = (uint8_t *)calloc(num + 1, 8);
if (NULL == rl_doutp) {
pr2ws("%s: calloc() failed to get memory\n", __func__);
- return SG_LIB_OS_BASE_ERR + ENOMEM;
+ return -ENOMEM;
}
for (k = 0, up = rl_doutp + 8; k < num; ++k, up += 8)
sg_put_unaligned_be16(k, up);
@@ -710,7 +713,8 @@ sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
break;
default:
pr2ws("%s: bad self-test code [0x%x]\n", __func__, st_cd);
- return SG_LIB_SYNTAX_ERROR;
+ mk_sense_invalid_fld(ptp, true, 1, 7, vb);
+ return 0;
}
sg_put_unaligned_le32(nvme_dst, cmd_up + SG_NVME_PT_CDW10);
res = do_nvme_admin_cmd(ptp, &cmd, NULL, false, time_secs, vb);
@@ -928,3 +932,18 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
}
return do_nvme_admin_cmd(ptp, &cmd, dp, is_read, time_secs, vb);
}
+
+#else /* (HAVE_NVME && (! IGNORE_NVME)) */
+
+int
+sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
+{
+ if (vb)
+ pr2ws("%s: not supported\n", __func__);
+ if (vp) { ; } /* suppress warning */
+ if (fd) { ; } /* suppress warning */
+ if (time_secs) { ; } /* suppress warning */
+ return -ENOTTY; /* inappropriate ioctl error */
+}
+
+#endif /* (HAVE_NVME && (! IGNORE_NVME)) */
diff --git a/sg3_utils.spec b/sg3_utils.spec
index ddf74ec2..05a33b70 100644
--- a/sg3_utils.spec
+++ b/sg3_utils.spec
@@ -79,7 +79,7 @@ fi
%{_libdir}/*.la
%changelog
-* Mon Jan 08 2018 - dgilbert at interlog dot com
+* Sat Jan 13 2018 - dgilbert at interlog dot com
- track t10 changes
* sg3_utils-1.43
diff --git a/src/sg_format.c b/src/sg_format.c
index f403e0cd..4da7923b 100644
--- a/src/sg_format.c
+++ b/src/sg_format.c
@@ -6,7 +6,7 @@
*
* 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-2017 Douglas Gilbert dgilbert at interlog dot com
+ * Copyright (C) 2005-2018 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
@@ -37,7 +37,7 @@
#include "sg_pr2serr.h"
#include "sg_pt.h"
-static const char * version_str = "1.41 20171107";
+static const char * version_str = "1.42 20180112";
#define RW_ERROR_RECOVERY_PAGE 1 /* can give alternate with --mode=MP */
@@ -281,7 +281,7 @@ sg_ll_format_medium(int sg_fd, bool verify, bool immed, int format,
return ret;
}
-/* Return 0 on success, else see sg_ll_format_unit2() */
+/* Return 0 on success, else see sg_ll_format_unit_v2() */
static int
scsi_format_unit(int fd, const struct opts_t * op)
{
@@ -323,10 +323,11 @@ scsi_format_unit(int fd, const struct opts_t * op)
if (need_hdr)
fmt_pl_sz = off + (ip_desc ? INIT_PATTERN_DESC_SZ : 0);
- res = sg_ll_format_unit2(fd, op->fmtpinfo, longlist,
- need_hdr/* FMTDATA*/, op->cmplst,
- 0 /* DEFECT_LIST_FORMAT */, op->ffmt,
- timeout, fmt_pl, fmt_pl_sz, 1, op->verbose);
+ res = sg_ll_format_unit_v2(fd, op->fmtpinfo, longlist,
+ need_hdr/* FMTDATA*/, op->cmplst,
+ 0 /* DEFECT_LIST_FORMAT */, op->ffmt,
+ timeout, fmt_pl, fmt_pl_sz, true,
+ op->verbose);
if (res) {
sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
pr2serr("Format unit command: %s\n", b);
diff --git a/src/sg_inq.c b/src/sg_inq.c
index 14f2fc0f..ca0d4c51 100644
--- a/src/sg_inq.c
+++ b/src/sg_inq.c
@@ -42,11 +42,11 @@
#include "sg_pt.h"
#include "sg_unaligned.h"
#include "sg_pr2serr.h"
-#ifdef HAVE_NVME
+#if (HAVE_NVME && (! IGNORE_NVME))
#include "sg_pt_nvme.h"
#endif
-static const char * version_str = "1.81 20180106"; /* SPC-5 rev 18 */
+static const char * version_str = "1.83 20180112"; /* SPC-5 rev 18 */
/* INQUIRY notes:
* It is recommended that the initial allocation length given to a
@@ -139,6 +139,8 @@ static void decode_dev_ids(const char * leadin, unsigned char * buff,
#if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS)
static int try_ata_identify(int ata_fd, int do_hex, int do_raw,
int verbose);
+struct opts_t;
+static void prepare_ata_identify(const struct opts_t * op, int inhex_len);
#endif
/* This structure is a duplicate of one of the same name in sg_vpd_vendor.c .
@@ -235,14 +237,15 @@ static struct option long_options[] = {
struct opts_t {
bool do_ata;
+ bool do_decode;
bool do_descriptors;
bool do_export;
bool do_force;
+ bool do_only; /* --only after standard inq don't fetch VPD page 0x80 */
bool do_version;
- bool do_decode;
bool do_vpd;
- bool p_given;
- bool do_only; /* --only after standard inq don't fetch VPD page 0x80 */
+ bool page_given;
+ bool possible_nvme;
int do_block;
int do_cmddt;
int do_help;
@@ -487,6 +490,7 @@ new_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
op->do_decode = true;
op->do_vpd = true;
op->page_num = VPD_EXT_INQ;
+ op->page_given = true;
break;
case 'f':
op->do_force = true;
@@ -508,6 +512,7 @@ new_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
op->do_decode = true;
op->do_vpd = true;
op->page_num = VPD_DEVICE_ID;
+ op->page_given = true;
break;
case 'I':
op->inhex_fn = optarg;
@@ -534,7 +539,7 @@ new_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
#endif
case 'p':
op->page_arg = optarg;
- op->p_given = true;
+ op->page_given = true;
break;
case 'r':
++op->do_raw;
@@ -603,6 +608,7 @@ old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
case 'a':
op->page_num = VPD_ATA_INFO;
op->do_vpd = true;
+ op->page_given = true;
++op->num_pages;
break;
#ifdef SG_LIB_LINUX
@@ -613,6 +619,7 @@ old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
case 'b':
op->page_num = VPD_BLOCK_LIMITS;
op->do_vpd = true;
+ op->page_given = true;
++op->num_pages;
break;
case 'c':
@@ -640,6 +647,7 @@ old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
case 'i':
op->page_num = VPD_DEVICE_ID;
op->do_vpd = true;
+ op->page_given = true;
++op->num_pages;
break;
case 'L':
@@ -649,10 +657,12 @@ old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
op->page_num = VPD_MAN_NET_ADDR;
op->do_vpd = true;
++op->num_pages;
+ op->page_given = true;
break;
case 'M':
op->page_num = VPD_MODE_PG_POLICY;
op->do_vpd = true;
+ op->page_given = true;
++op->num_pages;
break;
case 'N':
@@ -666,6 +676,7 @@ old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
case 'P':
op->page_num = VPD_UPR_EMC;
op->do_vpd = true;
+ op->page_given = true;
++op->num_pages;
break;
case 'r':
@@ -674,6 +685,7 @@ old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
case 's':
op->page_num = VPD_SCSI_PORTS;
op->do_vpd = true;
+ op->page_given = true;
++op->num_pages;
break;
case 'u':
@@ -688,6 +700,7 @@ old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
case 'x':
op->page_num = VPD_EXT_INQ;
op->do_vpd = true;
+ op->page_given = true;
++op->num_pages;
break;
case '?':
@@ -726,7 +739,7 @@ old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
op->resp_len = n;
} else if (0 == strncmp("p=", cp, 2)) {
op->page_arg = cp + 2;
- op->p_given = true;
+ op->page_given = true;
} else if (0 == strncmp("-old", cp, 4))
;
else if (jmp_out) {
@@ -3805,11 +3818,10 @@ out:
return res;
}
-#ifdef HAVE_NVME
+#if (HAVE_NVME && (! IGNORE_NVME))
static void
-do_nvme_identify_hex_raw(const unsigned char * b, int b_len,
- const struct opts_t * op)
+nvme_hex_raw(const unsigned char * b, int b_len, const struct opts_t * op)
{
if (op->do_raw)
dStrRaw((const char *)b, b_len);
@@ -3826,9 +3838,9 @@ const char * rperf[] = {"Best", "Better", "Good", "Degraded"};
/* Send Identify(CNS=0, nsid) and decode the Identify namespace response */
static int
-do_nvme_id_ns(struct sg_pt_base * ptvp, uint32_t nsid,
- struct sg_nvme_passthru_cmd * id_cmdp, uint8_t * id_dinp,
- int id_din_len, const struct opts_t * op)
+nvme_id_namespace(struct sg_pt_base * ptvp, uint32_t nsid,
+ struct sg_nvme_passthru_cmd * id_cmdp, uint8_t * id_dinp,
+ int id_din_len, const struct opts_t * op)
{
bool got_eui_128 = false;
int ret = 0;
@@ -3851,7 +3863,7 @@ do_nvme_id_ns(struct sg_pt_base * ptvp, uint32_t nsid,
num_lbaf = id_dinp[25] + 1; /* spec says this is "0's based value" */
flbas = id_dinp[26] & 0xf; /* index of active LBA format (for this ns) */
if (op->do_hex || op->do_raw) {
- do_nvme_identify_hex_raw(id_dinp, id_din_len, op);
+ nvme_hex_raw(id_dinp, id_din_len, op);
return 0;
}
ns_sz = sg_get_unaligned_le64(id_dinp + 0);
@@ -3904,10 +3916,14 @@ do_nvme_id_ns(struct sg_pt_base * ptvp, uint32_t nsid,
return ret;
}
-/* Send a NVMe Identify(CNS=1, nsid=0) and decode Controller info. For each
- * namespace found call do_nvme_id_ns(). CNS (Controller or Namespace
- * Structure) field is CDW10 7:0, was only bit 0 in NVMe 1.0 and bits 1:0 in
- * NVMe 1.1, thereafter 8 bits. */
+/* Send a NVMe Identify(CNS=1, nsid=0) and decode Controller info. If the
+ * device name includes a namespace indication (e.g. /dev/nvme0ns1) then
+ * an Identify namespace command is sent to that namespace (e.g. 1). If the
+ * device name does not contain a namespace indication (e.g. /dev/nvme0)
+ * and --only is not given then nvme_id_namespace() is sent for each
+ * namespace in the controller. Namespaces number sequentially starting at
+ * 1 . The CNS (Controller or Namespace Structure) field is CDW10 7:0, was
+ * only bit 0 in NVMe 1.0 and bits 1:0 in NVMe 1.1, thereafter 8 bits. */
static int
do_nvme_identify(int pt_fd, const struct opts_t * op)
{
@@ -3955,8 +3971,8 @@ do_nvme_identify(int pt_fd, const struct opts_t * op)
max_nsid = sg_get_unaligned_le32(id_dinp + 516); /* NN */
if (op->do_raw || op->do_hex) {
if (op->do_only || (SG_NVME_CTL_NSID == nsid ) ||
- (SG_NVME_BROADCAST_NSID == nsid)) {
- do_nvme_identify_hex_raw(id_dinp, pg_sz, op);
+ (SG_NVME_BROADCAST_NSID == nsid)) {
+ nvme_hex_raw(id_dinp, pg_sz, op);
goto fini;
}
goto skip1;
@@ -4103,7 +4119,7 @@ skip1:
pr2serr("NSID from device (%u) should not exceed number of "
"namespaces (%u)\n", nsid, max_nsid);
}
- ret = do_nvme_id_ns(ptvp, nsid, id_cmdp, id_dinp, pg_sz, op);
+ ret = nvme_id_namespace(ptvp, nsid, id_cmdp, id_dinp, pg_sz, op);
if (ret)
goto err_out;
@@ -4111,7 +4127,7 @@ skip1:
for (k = 1; k <= max_nsid; ++k) {
if ((! op->do_raw) || (op->do_hex < 3))
printf(" Namespace %u (of %u):\n", k, max_nsid);
- ret = do_nvme_id_ns(ptvp, k, id_cmdp, id_dinp, pg_sz, op);
+ ret = nvme_id_namespace(ptvp, k, id_cmdp, id_dinp, pg_sz, op);
if (ret)
goto err_out;
if (op->do_raw || op->do_hex)
@@ -4125,7 +4141,7 @@ err_out:
free(free_id_dinp);
return ret;
}
-#endif /* HAVE_NVME */
+#endif /* (HAVE_NVME && (! IGNORE_NVME)) */
int
@@ -4300,7 +4316,7 @@ main(int argc, char * argv[])
}
}
- if ((0 == op->do_cmddt) && (op->page_num >= 0) && op->p_given)
+ if ((0 == op->do_cmddt) && (op->page_num >= 0) && op->page_given)
op->do_vpd = true;
if (op->do_raw && op->do_hex) {
@@ -4355,7 +4371,14 @@ main(int argc, char * argv[])
return vpd_decode(-1, op, inhex_len);
else
return vpd_mainly_hex(-1, op, inhex_len);
- } else
+ }
+#if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS)
+ else if (op->do_ata) {
+ prepare_ata_identify(op, inhex_len);
+ return 0;
+ }
+#endif
+ else
return std_inq_process(-1, op, inhex_len);
}
@@ -4387,11 +4410,17 @@ main(int argc, char * argv[])
#endif
memset(rsp_buff, 0, sizeof(rsp_buff));
-#ifdef HAVE_NVME
+#if (HAVE_NVME && (! IGNORE_NVME))
n = check_pt_file_handle(sg_fd, op->device_name, op->do_verbose);
+ if (op->do_verbose > 1)
+ pr2serr("check_pt_file_handle()-->%d, page_given=%d\n", n,
+ op->page_given);
if ((3 == n) || (4 == n)) { /* NVMe char or NVMe block */
- ret = do_nvme_identify(sg_fd, op);
- goto fini2;
+ op->possible_nvme = true;
+ if (! op->page_given) {
+ ret = do_nvme_identify(sg_fd, op);
+ goto fini2;
+ }
}
#endif
@@ -4432,7 +4461,7 @@ main(int argc, char * argv[])
}
}
-#ifdef HAVE_NVME
+#if (HAVE_NVME && (! IGNORE_NVME))
fini2:
#endif
#if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS)
@@ -4563,15 +4592,63 @@ ata_command_interface(int device, char *data, bool * atapi_flag, int verbose)
return 0;
}
+static void
+show_ata_identify(const struct ata_identify_device * aidp, bool atapi,
+ int vb)
+{
+ int res;
+ char model[64];
+ char serial[64];
+ char firm[64];
+
+ printf("%s device: model, serial number and firmware revision:\n",
+ (atapi ? "ATAPI" : "ATA"));
+ res = sg_ata_get_chars((const unsigned short *)aidp->model,
+ 0, 20, sg_is_big_endian(), model);
+ model[res] = '\0';
+ res = sg_ata_get_chars((const unsigned short *)aidp->serial_no,
+ 0, 10, sg_is_big_endian(), serial);
+ serial[res] = '\0';
+ res = sg_ata_get_chars((const unsigned short *)aidp->fw_rev,
+ 0, 4, sg_is_big_endian(), firm);
+ firm[res] = '\0';
+ printf(" %s %s %s\n", model, serial, firm);
+ if (vb) {
+ if (atapi)
+ printf("ATA IDENTIFY PACKET DEVICE response "
+ "(256 words):\n");
+ else
+ printf("ATA IDENTIFY DEVICE response (256 words):\n");
+ dWordHex((const unsigned short *)aidp, 256, 0,
+ sg_is_big_endian());
+ }
+}
+
+static void
+prepare_ata_identify(const struct opts_t * op, int inhex_len)
+{
+ int n = inhex_len;
+ struct ata_identify_device ata_ident;
+
+ if (n < 16) {
+ pr2serr("%s: got only %d bytes, give up\n", __func__, n);
+ return;
+ } else if (n < 512)
+ pr2serr("%s: expect 512 bytes or more, got %d, continue\n", __func__,
+ n);
+ else if (n > 512)
+ n = 512;
+ memset(&ata_ident, 0, sizeof(ata_ident));
+ memcpy(&ata_ident, rsp_buff, n);
+ show_ata_identify(&ata_ident, false, op->do_verbose);
+}
+
/* Returns 0 if successful, else errno of error */
static int
try_ata_identify(int ata_fd, int do_hex, int do_raw, int verbose)
{
bool atapi;
int res;
- char model[64];
- char serial[64];
- char firm[64];
struct ata_identify_device ata_ident;
memset(&ata_ident, 0, sizeof(ata_ident));
@@ -4597,29 +4674,8 @@ try_ata_identify(int ata_fd, int do_hex, int do_raw, int verbose)
dWordHex((const unsigned short *)&ata_ident, 256, 0,
sg_is_big_endian());
}
- } else {
- printf("%s device: model, serial number and firmware revision:\n",
- (atapi ? "ATAPI" : "ATA"));
- res = sg_ata_get_chars((const unsigned short *)ata_ident.model,
- 0, 20, sg_is_big_endian(), model);
- model[res] = '\0';
- res = sg_ata_get_chars((const unsigned short *)ata_ident.serial_no,
- 0, 10, sg_is_big_endian(), serial);
- serial[res] = '\0';
- res = sg_ata_get_chars((const unsigned short *)ata_ident.fw_rev,
- 0, 4, sg_is_big_endian(), firm);
- firm[res] = '\0';
- printf(" %s %s %s\n", model, serial, firm);
- if (verbose) {
- if (atapi)
- printf("ATA IDENTIFY PACKET DEVICE response "
- "(256 words):\n");
- else
- printf("ATA IDENTIFY DEVICE response (256 words):\n");
- dWordHex((const unsigned short *)&ata_ident, 256, 0,
- sg_is_big_endian());
- }
- }
+ } else
+ show_ata_identify(&ata_ident, atapi, verbose);
}
return 0;
}
diff --git a/src/sg_ses.c b/src/sg_ses.c
index d5df8517..f14f495f 100644
--- a/src/sg_ses.c
+++ b/src/sg_ses.c
@@ -32,7 +32,7 @@
* commands tailored for SES (enclosure) devices.
*/
-static const char * version_str = "2.28 20180107"; /* ses4r01 */
+static const char * version_str = "2.29 20180111"; /* ses4r01 */
#define MX_ALLOC_LEN ((64 * 1024) - 4) /* max allowable for big enclosures */
#define MX_ELEM_HDR 1024
@@ -1075,7 +1075,7 @@ parse_index(struct opts_t *op)
static int
parse_cmd_line(struct opts_t *op, int argc, char *argv[])
{
- int c, j, ret;
+ int c, j, n, ret;
const char * data_arg = NULL;
uint64_t saddr;
const char * cp;
@@ -1210,12 +1210,18 @@ parse_cmd_line(struct opts_t *op, int argc, char *argv[])
op->seid_given = true;
break;
case 'm':
- op->maxlen = sg_get_num(optarg);
- if ((op->maxlen < 0) || (op->maxlen > 65535)) {
- pr2serr("bad argument to '--maxlen' (0 to 65535 "
- "inclusive expected)\n");
+ n = sg_get_num(optarg);
+ if ((n < 0) || (n > 65535)) {
+ pr2serr("bad argument to '--maxlen' (0 to 65535 inclusive "
+ "expected)\n");
return SG_LIB_SYNTAX_ERROR;
}
+ if (0 == n)
+ op->maxlen = MX_ALLOC_LEN;
+ else if (n < 4)
+ pr2serr("Warning: --maxlen= less than 4 ignored\n");
+ else
+ op->maxlen = n;
break;
case 'M':
op->mask_ign = true;
@@ -1313,8 +1319,6 @@ parse_cmd_line(struct opts_t *op, int argc, char *argv[])
return SG_LIB_SYNTAX_ERROR;
}
}
- if (op->maxlen <= 0)
- op->maxlen = MX_ALLOC_LEN;
if (op->do_join && op->do_control) {
pr2serr("cannot have '--join' and '--control'\n");
goto err_help;
@@ -5223,6 +5227,7 @@ main(int argc, char * argv[])
memset(op, 0, sizeof(*op));
op->dev_slot_num = -1;
op->ind_indiv_last = -1;
+ op->maxlen = MX_ALLOC_LEN;
pg_sz = sg_get_page_size();
res = parse_cmd_line(op, argc, argv);
if (res)
@@ -5239,34 +5244,34 @@ main(int argc, char * argv[])
enumerate_work(op);
return 0;
}
- enc_stat_rsp = sg_memalign(MX_ALLOC_LEN, pg_sz, &free_enc_stat_rsp,
+ enc_stat_rsp = sg_memalign(op->maxlen, pg_sz, &free_enc_stat_rsp,
op->verbose > 3);
if (NULL == enc_stat_rsp) {
pr2serr("Unable to get heap for enc_stat_rsp\n");
goto err_out;
}
- enc_stat_rsp_sz = MX_ALLOC_LEN;
- elem_desc_rsp = sg_memalign(MX_ALLOC_LEN, pg_sz, &free_elem_desc_rsp,
+ enc_stat_rsp_sz = op->maxlen;
+ elem_desc_rsp = sg_memalign(op->maxlen, pg_sz, &free_elem_desc_rsp,
op->verbose > 3);
if (NULL == elem_desc_rsp) {
pr2serr("Unable to get heap for elem_desc_rsp\n");
goto err_out;
}
- elem_desc_rsp_sz = MX_ALLOC_LEN;
- add_elem_rsp = sg_memalign(MX_ALLOC_LEN, pg_sz, &free_add_elem_rsp,
+ elem_desc_rsp_sz = op->maxlen;
+ add_elem_rsp = sg_memalign(op->maxlen, pg_sz, &free_add_elem_rsp,
op->verbose > 3);
if (NULL == add_elem_rsp) {
pr2serr("Unable to get heap for add_elem_rsp\n");
goto err_out;
}
- add_elem_rsp_sz = MX_ALLOC_LEN;
- threshold_rsp = sg_memalign(MX_ALLOC_LEN, pg_sz, &free_threshold_rsp,
+ add_elem_rsp_sz = op->maxlen;
+ threshold_rsp = sg_memalign(op->maxlen, pg_sz, &free_threshold_rsp,
op->verbose > 3);
if (NULL == threshold_rsp) {
pr2serr("Unable to get heap for threshold_rsp\n");
goto err_out;
}
- threshold_rsp_sz = MX_ALLOC_LEN;
+ threshold_rsp_sz = op->maxlen;
if (op->num_cgs) {
have_cgs = true;
diff --git a/src/sg_ses_microcode.c b/src/sg_ses_microcode.c
index 4a6b8678..626a4297 100644
--- a/src/sg_ses_microcode.c
+++ b/src/sg_ses_microcode.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2017 Douglas Gilbert.
+ * Copyright (c) 2014-2018 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
@@ -11,6 +11,7 @@
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
+#include <stdint.h>
#include <ctype.h>
#include <string.h>
#include <getopt.h>
@@ -25,46 +26,52 @@
#include "sg_lib.h"
#include "sg_cmds_basic.h"
#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
#ifdef SG_LIB_WIN32
#ifdef SG_LIB_WIN32_DIRECT
#include "sg_pt.h" /* needed for scsi_pt_win32_direct() */
#endif
#endif
-#include "sg_unaligned.h"
-#include "sg_pr2serr.h"
/*
* This utility issues the SCSI SEND DIAGNOSTIC and RECEIVE DIAGNOSTIC
* RESULTS commands in order to send microcode to the given SES device.
*/
-static const char * version_str = "1.09 20171208"; /* ses4r01 */
+static const char * version_str = "1.10 20180112"; /* ses4r01 */
#define ME "sg_ses_microcode: "
#define MAX_XFER_LEN (128 * 1024 * 1024)
#define DEF_XFER_LEN (8 * 1024 * 1024)
-#define DEF_DI_LEN (8 * 1024)
+#define DEF_DIN_LEN (8 * 1024)
#define EBUFF_SZ 256
#define DPC_DOWNLOAD_MICROCODE 0xe
struct opts_t {
+ bool dry_run;
+ bool ealsd;
bool mc_non;
bool bpw_then_activate;
bool mc_len_given;
- int bpw;
+ int bpw; /* bytes per write, chunk size */
int mc_id;
- int mc_len;
+ int mc_len; /* --length=LEN */
int mc_mode;
- int mc_offset;
- int mc_skip;
+ int mc_offset; /* Buffer offset in SCSI commands */
+ int mc_skip; /* on FILE */
int mc_subenc;
- int mc_tlen;
+ int mc_tlen; /* --tlength=TLEN */
int verbose;
};
static struct option long_options[] = {
{"bpw", required_argument, 0, 'b'},
+ {"dry-run", no_argument, 0, 'd'},
+ {"dry_run", no_argument, 0, 'd'},
+ {"ealsd", no_argument, 0, 'e'},
{"help", no_argument, 0, 'h'},
{"id", required_argument, 0, 'i'},
{"in", required_argument, 0, 'I'},
@@ -80,13 +87,85 @@ static struct option long_options[] = {
{0, 0, 0, 0},
};
+#define MODE_DNLD_STATUS 0
+#define MODE_DNLD_MC_OFFS 6
+#define MODE_DNLD_MC_OFFS_SAVE 7
+#define MODE_DNLD_MC_OFFS_DEFER 0x0E
+#define MODE_ACTIVATE_MC 0x0F
+#define MODE_ABORT_MC 0xFF /* actually reserved; any reserved
+ * value aborts a microcode download
+ * in progress */
+
+struct mode_s {
+ const char *mode_string;
+ int mode;
+ const char *comment;
+};
+
+static struct mode_s mode_arr[] = {
+ {"dmc_status", MODE_DNLD_STATUS, "report status of microcode "
+ "download"},
+ {"dmc_offs", MODE_DNLD_MC_OFFS, "download microcode with offsets "
+ "and activate"},
+ {"dmc_offs_save", MODE_DNLD_MC_OFFS_SAVE, "download microcode with "
+ "offsets, save and\n\t\t\t\tactivate"},
+ {"dmc_offs_defer", MODE_DNLD_MC_OFFS_DEFER, "download microcode "
+ "with offsets, save and\n\t\t\t\tdefer activation"},
+ {"activate_mc", MODE_ACTIVATE_MC, "activate deferred microcode"},
+ {"dmc_abort", MODE_ABORT_MC, "abort download microcode in progress"},
+ {NULL, 0, NULL},
+};
+
+struct diag_page_code {
+ int page_code;
+ const char * desc;
+};
+
+/* An array of Download microcode status field values and descriptions */
+static struct diag_page_code mc_status_arr[] = {
+ {0x0, "No download microcode operation in progress"},
+ {0x1, "Download in progress, awaiting more"},
+ {0x2, "Download complete, updating storage"},
+ {0x3, "Updating storage with deferred microcode"},
+ {0x10, "Complete, no error, starting now"},
+ {0x11, "Complete, no error, start after hard reset or power cycle"},
+ {0x12, "Complete, no error, start after power cycle"},
+ {0x13, "Complete, no error, start after activate_mc, hard reset or "
+ "power cycle"},
+ {0x80, "Error, discarded, see additional status"},
+ {0x81, "Error, discarded, image error"},
+ {0x82, "Timeout, discarded"},
+ {0x83, "Internal error, need new microcode before reset"},
+ {0x84, "Internal error, need new microcode, reset safe"},
+ {0x85, "Unexpected activate_mc received"},
+ {0x1000, NULL},
+};
+
+struct dout_buff_t {
+ unsigned char * doutp;
+ unsigned char * free_doutp;
+ int dout_len;
+};
+
+/* This dummy response is used when --dry-run skips the RECEIVE DIAGNOSTICS
+ * RESULTS command. Say maximum download MC size is 4 MB. Set generation
+ * code to 0 . */
+uint8_t dummy_rd_resp[] = {
+ 0xe, 3, 0, 68, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0x0, 0x40, 0x0, 0x0, 0, 0, 0, 0, 0x0, 0x0, 0x0, 0x0,
+ 0, 1, 0, 0, 0x0, 0x40, 0x0, 0x0, 0, 0, 0, 0, 0x0, 0x0, 0x0, 0x0,
+ 0, 2, 0, 0, 0x0, 0x40, 0x0, 0x0, 0, 0, 0, 0, 0x0, 0x0, 0x0, 0x0,
+ 0, 3, 0, 0, 0x0, 0x40, 0x0, 0x0, 0, 0, 0, 0, 0x0, 0x0, 0x0, 0x0,
+};
+
static void
usage()
{
pr2serr("Usage: "
- "sg_ses_microcode [--bpw=CS] [--help] [--id=ID] [--in=FILE]\n"
- " [--length=LEN] [--mode=MO] "
+ "sg_ses_microcode [--bpw=CS] [--dry-run] [--ealsd] [--help] "
+ "[--id=ID]\n"
+ " [--in=FILE] [--length=LEN] [--mode=MO] "
"[--non]\n"
" [--offset=OFF] [--skip=SKIP] "
"[--subenc=SEID]\n"
@@ -98,14 +177,20 @@ usage()
"diagnostic\n"
" command (def: 0 -> as many as "
"possible)\n"
+ " can append ',act' to do activate "
+ "after last\n"
+ " --dry-run|-d skip SCSI commands, do everything "
+ "else\n"
+ " --ealsd|-e exit after last Send Diagnostic "
+ "command\n"
" --help|-h print out usage message then exit\n"
" --id=ID|-i ID buffer identifier (0 (default) to "
"255)\n"
" --in=FILE|-I FILE read from FILE ('-I -' read "
"from stdin)\n"
- " --length=LEN|-l LEN length in bytes to send; may be "
+ " --length=LEN|-l LEN length in bytes to send (def: "
"deduced from\n"
- " FILE\n"
+ " FILE taking SKIP into account)\n"
" --mode=MO|-m MO download microcode mode, MO is "
"number or\n"
" acronym (def: 0 -> 'dmc_status')\n"
@@ -133,32 +218,6 @@ usage()
);
}
-#define MODE_DNLD_STATUS 0
-#define MODE_DNLD_MC_OFFS 6
-#define MODE_DNLD_MC_OFFS_SAVE 7
-#define MODE_DNLD_MC_OFFS_DEFER 0x0E
-#define MODE_ACTIVATE_MC 0x0F
-
-struct mode_s {
- const char *mode_string;
- int mode;
- const char *comment;
-};
-
-static struct mode_s mode_arr[] = {
- {"dmc_status", MODE_DNLD_STATUS, "report status of microcode "
- "download"},
- {"dmc_offs", MODE_DNLD_MC_OFFS, "download microcode with offsets "
- "and activate"},
- {"dmc_offs_save", MODE_DNLD_MC_OFFS_SAVE, "download microcode with "
- "offsets, save and\n\t\t\t\tactivate"},
- {"dmc_offs_defer", MODE_DNLD_MC_OFFS_DEFER, "download microcode "
- "with offsets, save and\n\t\t\t\tdefer activation"},
- {"activate_mc", MODE_ACTIVATE_MC, "activate deferred microcode"},
- {NULL, 0, NULL},
-};
-
-
static void
print_modes(void)
{
@@ -167,7 +226,7 @@ print_modes(void)
pr2serr("The modes parameter argument can be numeric (hex or decimal)\n"
"or symbolic:\n");
for (mp = mode_arr; mp->mode_string; ++mp) {
- pr2serr(" %2d (0x%02x) %-18s%s\n", mp->mode, mp->mode,
+ pr2serr(" %3d [0x%02x] %-18s%s\n", mp->mode, mp->mode,
mp->mode_string, mp->comment);
}
pr2serr("\nAdditionally '--bpw=<val>,act' does a activate deferred "
@@ -175,31 +234,6 @@ print_modes(void)
"download.\n");
}
-struct diag_page_code {
- int page_code;
- const char * desc;
-};
-
-/* An array of Download microcode status field values and descriptions */
-static struct diag_page_code mc_status_arr[] = {
- {0x0, "No download microcode operation in progress"},
- {0x1, "Download in progress, awaiting more"},
- {0x2, "Download complete, updating storage"},
- {0x3, "Updating storage with deferred microcode"},
- {0x10, "Complete, no error, starting now"},
- {0x11, "Complete, no error, start after hard reset or power cycle"},
- {0x12, "Complete, no error, start after power cycle"},
- {0x13, "Complete, no error, start after activate_mc, hard reset or "
- "power cycle"},
- {0x80, "Error, discarded, see additional status"},
- {0x81, "Error, discarded, image error"},
- {0x82, "Timeout, discarded"},
- {0x83, "Internal error, need new microcode before reset"},
- {0x84, "Internal error, need new microcode, reset safe"},
- {0x85, "Unexpected activate_mc received"},
- {0x1000, NULL},
-};
-
static const char *
get_mc_status_str(unsigned char status_val)
{
@@ -212,10 +246,10 @@ get_mc_status_str(unsigned char status_val)
return "";
}
-/* DPC_DOWNLOAD_MICROCODE [0xe] */
+/* display DPC_DOWNLOAD_MICROCODE status dpage [0xe] */
static void
-ses_download_code_sdg(const unsigned char * resp, int resp_len,
- uint32_t gen_code)
+show_download_mc_sdg(const unsigned char * resp, int resp_len,
+ uint32_t gen_code)
{
int k, num_subs, num;
const unsigned char * bp;
@@ -255,20 +289,14 @@ truncated:
return;
}
-struct dout_buff_t {
- unsigned char * doutp;
- unsigned char * free_doutp;
- int dout_len;
-};
-
static int
send_then_receive(int sg_fd, uint32_t gen_code, int off_off,
const unsigned char * dmp, int dmp_len,
struct dout_buff_t * wp, unsigned char * dip,
- bool last, const struct opts_t * op)
+ int din_len, bool last, const struct opts_t * op)
{
bool send_data = false;
- int do_len, rem, res, rsp_len, k, num, mc_status, resid, act_len, verb;
+ int do_len, rem, res, rsp_len, k, n, num, mc_status, resid, act_len, verb;
int ret = 0;
uint32_t rec_gen_code;
const unsigned char * bp;
@@ -286,10 +314,11 @@ send_then_receive(int sg_fd, uint32_t gen_code, int off_off,
do_len += (4 - rem);
break;
case MODE_ACTIVATE_MC:
+ case MODE_ABORT_MC:
do_len = 24;
break;
default:
- pr2serr("send_then_receive: unexpected mc_mode=0x%x\n", op->mc_mode);
+ pr2serr("%s: unexpected mc_mode=0x%x\n", __func__, op->mc_mode);
return SG_LIB_SYNTAX_ERROR;
}
if (do_len > wp->dout_len) {
@@ -298,12 +327,12 @@ send_then_receive(int sg_fd, uint32_t gen_code, int off_off,
wp->doutp = (unsigned char *)sg_memalign(do_len, sg_get_page_size(),
&wp->free_doutp, op->verbose > 3);
if (! wp->doutp) {
- pr2serr("send_then_receive: unable to alloc %d bytes\n", do_len);
+ pr2serr("%s: unable to alloc %d bytes\n", __func__, do_len);
return SG_LIB_CAT_OTHER;
}
wp->dout_len = do_len;
- }
- memset(wp->doutp, 0, do_len);
+ } else
+ memset(wp->doutp, 0, do_len);
wp->doutp[0] = DPC_DOWNLOAD_MICROCODE;
wp->doutp[1] = op->mc_subenc;
sg_put_unaligned_be16(do_len - 4, wp->doutp + 2);
@@ -316,11 +345,36 @@ send_then_receive(int sg_fd, uint32_t gen_code, int off_off,
sg_put_unaligned_be32(dmp_len, wp->doutp + 20);
if (send_data && (dmp_len > 0))
memcpy(wp->doutp + 24, dmp, dmp_len);
+ if ((op->verbose > 2) || (op->dry_run && op->verbose)) {
+ pr2serr("send diag: sub-enc id=%u exp_gen=%u download_mc_code=%u "
+ "buff_id=%u\n", op->mc_subenc, gen_code, op->mc_mode,
+ op->mc_id);
+ pr2serr(" buff_off=%u image_len=%u this_mc_data_len=%u "
+ "dout_len=%u\n", op->mc_offset + off_off, op->mc_tlen,
+ dmp_len, do_len);
+ }
/* select long duration timeout (7200 seconds) */
- res = sg_ll_send_diag(sg_fd, 0 /* sf_code */, true /* pf */,
- false /* sf */, false /* devofl */,
- false /* unitofl */, 1 /* long_duration */,
- wp->doutp, do_len, true /* noisy */, verb);
+ if (op->dry_run) {
+ if (op->mc_subenc < 4) {
+ int s = op->mc_offset + off_off + dmp_len;
+
+ n = 8 + (op->mc_subenc * 16);
+ dummy_rd_resp[n + 11] = op->mc_id;
+ sg_put_unaligned_be32(((send_data && (! last)) ? s : 0),
+ dummy_rd_resp + n + 12);
+ if (MODE_ABORT_MC == op->mc_mode)
+ dummy_rd_resp[n + 2] = 0x80;
+ else if (MODE_ACTIVATE_MC == op->mc_mode)
+ dummy_rd_resp[n + 2] = 0x0; /* done */
+ else
+ dummy_rd_resp[n + 2] = (s >= op->mc_tlen) ? 0x13 : 0x1;
+ }
+ res = 0;
+ } else
+ res = sg_ll_send_diag(sg_fd, 0 /* st_code */, true /* pf */,
+ false /* st */, false /* devofl */,
+ false /* unitofl */, 1 /* long_duration */,
+ wp->doutp, do_len, true /* noisy */, verb);
if (op->mc_non) {
/* If non-standard, only call RDR after failed SD */
if (0 == res)
@@ -333,36 +387,49 @@ send_then_receive(int sg_fd, uint32_t gen_code, int off_off,
case MODE_DNLD_MC_OFFS_SAVE:
if (res)
return res;
- else if (last)
- return 0; /* RDR after last may hit a device reset */
+ else if (last) {
+ if (op->ealsd)
+ return 0; /* RDR after last may hit a device reset */
+ }
break;
case MODE_DNLD_MC_OFFS_DEFER:
if (res)
return res;
break;
case MODE_ACTIVATE_MC:
- if (0 == res)
- return 0; /* RDR after ACTIVATE_MC may hit a device reset */
+ case MODE_ABORT_MC:
+ if (0 == res) {
+ if (op->ealsd)
+ return 0; /* RDR after this may hit a device reset */
+ }
/* SD has failed, so do a RDR but return SD's error */
ret = res;
break;
default:
- pr2serr("send_then_receive: mc_mode=0x%x\n", op->mc_mode);
+ pr2serr("%s: mc_mode=0x%x\n", __func__, op->mc_mode);
return SG_LIB_SYNTAX_ERROR;
}
}
- res = sg_ll_receive_diag_v2(sg_fd, true /* pcv */, DPC_DOWNLOAD_MICROCODE,
- dip, DEF_DI_LEN, 0 /* default timeout */,
- &resid, true, verb);
+ if (op->dry_run) {
+ n = sizeof(dummy_rd_resp);
+ n = (n < din_len) ? n : din_len;
+ memcpy(dip, dummy_rd_resp, n);
+ resid = din_len - n;
+ res = 0;
+ } else
+ res = sg_ll_receive_diag_v2(sg_fd, true /* pcv */,
+ DPC_DOWNLOAD_MICROCODE, dip, din_len,
+ 0 /* default timeout */, &resid, true,
+ verb);
if (res)
return ret ? ret : res;
rsp_len = sg_get_unaligned_be16(dip + 2) + 4;
- act_len = DEF_DI_LEN - resid;
- if (rsp_len > DEF_DI_LEN) {
+ act_len = din_len - resid;
+ if (rsp_len > din_len) {
pr2serr("<<< warning response buffer too small [%d but need "
- "%d]>>>\n", DEF_DI_LEN, rsp_len);
- rsp_len = DEF_DI_LEN;
+ "%d]>>>\n", din_len, rsp_len);
+ rsp_len = din_len;
}
if (rsp_len > act_len) {
pr2serr("<<< warning response too short [actually got %d but need "
@@ -370,10 +437,17 @@ send_then_receive(int sg_fd, uint32_t gen_code, int off_off,
rsp_len = act_len;
}
if (rsp_len < 8) {
- pr2serr("Download microcode status dpage too short\n");
+ pr2serr("Download microcode status dpage too short [%d]\n", rsp_len);
return ret ? ret : SG_LIB_CAT_OTHER;
}
rec_gen_code = sg_get_unaligned_be32(dip + 4);
+ if ((op->verbose > 2) || (op->dry_run && op->verbose)) {
+ n = 8 + (op->mc_subenc * 16);
+ pr2serr("rec diag: rsp_len=%d, num_sub-enc=%u rec_gen_code=%u "
+ "exp_buff_off=%u\n", rsp_len, dip[1],
+ sg_get_unaligned_be32(dip + 4),
+ sg_get_unaligned_be32(dip + n + 12));
+ }
if (rec_gen_code != gen_code)
pr2serr("gen_code changed from %" PRIu32 " to %" PRIu32
", continuing but may fail\n", gen_code, rec_gen_code);
@@ -387,8 +461,8 @@ send_then_receive(int sg_fd, uint32_t gen_code, int off_off,
mc_status = bp[2];
cp = get_mc_status_str(mc_status);
if ((mc_status >= 0x80) || op->verbose)
- pr2serr("mc offset=%d: status: %s [0x%x, additional=0x%x]\n",
- off_off, cp, mc_status, bp[3]);
+ pr2serr("mc offset=%u: status: %s [0x%x, additional=0x%x]\n",
+ sg_get_unaligned_be32(bp + 12), cp, mc_status, bp[3]);
if (op->verbose > 1)
pr2serr(" subenc_id=%d, expected_buffer_id=%d, "
"expected_offset=0x%" PRIx32 "\n", bp[1], bp[11],
@@ -405,7 +479,8 @@ int
main(int argc, char * argv[])
{
bool last, got_stdin, is_reg;
- int sg_fd, res, c, len, k, n, rsp_len, resid, act_len, verb;
+ bool want_file = false;
+ int sg_fd, res, c, len, k, n, rsp_len, resid, act_len, din_len, verb;
int infd = -1;
int do_help = 0;
int ret = 0;
@@ -426,10 +501,11 @@ main(int argc, char * argv[])
op = &opts;
memset(op, 0, sizeof(opts));
memset(&dout, 0, sizeof(dout));
+ din_len = DEF_DIN_LEN;
while (1) {
int option_index = 0;
- c = getopt_long(argc, argv, "b:hi:I:l:m:No:s:S:t:vV", long_options,
+ c = getopt_long(argc, argv, "b:dehi:I:l:m:No:s:S:t:vV", long_options,
&option_index);
if (c == -1)
break;
@@ -447,6 +523,12 @@ main(int argc, char * argv[])
op->bpw_then_activate = true;
}
break;
+ case 'd':
+ op->dry_run = true;
+ break;
+ case 'e':
+ op->ealsd = true;
+ break;
case 'h':
case '?':
++do_help;
@@ -562,15 +644,36 @@ main(int argc, char * argv[])
}
if (NULL == device_name) {
- pr2serr("missing device name!\n");
+ pr2serr("missing device name!\n\n");
usage();
return SG_LIB_SYNTAX_ERROR;
}
+ switch (op->mc_mode) {
+ case MODE_DNLD_MC_OFFS:
+ case MODE_DNLD_MC_OFFS_SAVE:
+ case MODE_DNLD_MC_OFFS_DEFER:
+ want_file = true;
+ break;
+ case MODE_DNLD_STATUS:
+ case MODE_ACTIVATE_MC:
+ case MODE_ABORT_MC:
+ want_file = false;
+ break;
+ default:
+ pr2serr("%s: mc_mode=0x%x, continue for now\n", __func__,
+ op->mc_mode);
+ break;
+ }
if ((op->mc_len > 0) && (op->bpw > op->mc_len)) {
pr2serr("trim chunk size (CS) to be the same as LEN\n");
op->bpw = op->mc_len;
}
+ if ((op->mc_offset > 0) && (op->bpw > 0)) {
+ op->mc_offset = 0;
+ pr2serr("WARNING: --offset= ignored (set back to 0) when --bpw= "
+ "argument given (and > 0)\n");
+ }
#ifdef SG_LIB_WIN32
#ifdef SG_LIB_WIN32_DIRECT
@@ -588,8 +691,7 @@ main(int argc, char * argv[])
return SG_LIB_FILE_ERROR;
}
- if (file_name && ((MODE_DNLD_STATUS == op->mc_mode) ||
- (MODE_ACTIVATE_MC == op->mc_mode)))
+ if (file_name && (! want_file))
pr2serr("ignoring --in=FILE option\n");
else if (file_name) {
got_stdin = (0 == strcmp(file_name, "-"));
@@ -629,7 +731,7 @@ main(int argc, char * argv[])
goto fini;
}
if (NULL == (dmp = (unsigned char *)malloc(op->mc_len))) {
- pr2serr(ME "out of memory (to hold microcode)\n");
+ pr2serr(ME "out of memory to hold microcode read from FILE\n");
ret = SG_LIB_CAT_OTHER;
goto fini;
}
@@ -682,8 +784,7 @@ main(int argc, char * argv[])
if (! got_stdin)
close(infd);
infd = -1;
- } else if (! ((MODE_DNLD_STATUS == op->mc_mode) ||
- (MODE_ACTIVATE_MC == op->mc_mode))) {
+ } else if (want_file) {
pr2serr("need --in=FILE option with given mode\n");
ret = SG_LIB_SYNTAX_ERROR;
goto fini;
@@ -696,25 +797,33 @@ main(int argc, char * argv[])
goto fini;
}
- if (NULL == (dip = (unsigned char *)sg_memalign(DEF_DI_LEN,
- sg_get_page_size(), &free_dip, op->verbose > 3))) {
+ dip = (unsigned char *)sg_memalign(din_len, sg_get_page_size(),
+ &free_dip, op->verbose > 3);
+ if (NULL == dip) {
pr2serr(ME "out of memory (data-in buffer)\n");
ret = SG_LIB_CAT_OTHER;
goto fini;
}
- memset(dip, 0, DEF_DI_LEN);
verb = (op->verbose > 1) ? op->verbose - 1 : 0;
/* Fetch Download microcode status dpage for generation code ++ */
- res = sg_ll_receive_diag_v2(sg_fd, true /* pcv */, DPC_DOWNLOAD_MICROCODE,
- dip, DEF_DI_LEN, 0 /*default timeout */,
- &resid, true, verb);
+ if (op->dry_run) {
+ n = sizeof(dummy_rd_resp);
+ n = (n < din_len) ? n : din_len;
+ memcpy(dip, dummy_rd_resp, n);
+ resid = din_len - n;
+ res = 0;
+ } else
+ res = sg_ll_receive_diag_v2(sg_fd, true /* pcv */,
+ DPC_DOWNLOAD_MICROCODE, dip, din_len,
+ 0 /*default timeout */, &resid, true,
+ verb);
if (0 == res) {
rsp_len = sg_get_unaligned_be16(dip + 2) + 4;
- act_len = DEF_DI_LEN - resid;
- if (rsp_len > DEF_DI_LEN) {
+ act_len = din_len - resid;
+ if (rsp_len > din_len) {
pr2serr("<<< warning response buffer too small [%d but need "
- "%d]>>>\n", DEF_DI_LEN, rsp_len);
- rsp_len = DEF_DI_LEN;
+ "%d]>>>\n", din_len, rsp_len);
+ rsp_len = din_len;
}
if (rsp_len > act_len) {
pr2serr("<<< warning response too short [actually got %d but "
@@ -726,6 +835,10 @@ main(int argc, char * argv[])
ret = SG_LIB_CAT_OTHER;
goto fini;
}
+ if ((op->verbose > 2) || (op->dry_run && op->verbose))
+ pr2serr("rec diag(ini): rsp_len=%d, num_sub-enc=%u "
+ "rec_gen_code=%u\n", rsp_len, dip[1],
+ sg_get_unaligned_be32(dip + 4));
} else {
ret = res;
goto fini;
@@ -733,11 +846,11 @@ main(int argc, char * argv[])
gen_code = sg_get_unaligned_be32(dip + 4);
if (MODE_DNLD_STATUS == op->mc_mode) {
- ses_download_code_sdg(dip, rsp_len, gen_code);
+ show_download_mc_sdg(dip, rsp_len, gen_code);
goto fini;
- } else if (MODE_ACTIVATE_MC == op->mc_mode) {
- res = send_then_receive(sg_fd, gen_code, 0, NULL, 0, &dout, dip, 1,
- op);
+ } else if (! want_file) { /* ACTIVATE and ABORT */
+ res = send_then_receive(sg_fd, gen_code, 0, NULL, 0, &dout, dip,
+ din_len, true, op);
ret = res;
goto fini;
}
@@ -754,7 +867,7 @@ main(int argc, char * argv[])
pr2serr("bpw loop: mode=0x%x, id=%d, off_off=%d, len=%d, "
"last=%d\n", op->mc_mode, op->mc_id, k, n, last);
res = send_then_receive(sg_fd, gen_code, k, dmp + k, n, &dout,
- dip, (int)last, op);
+ dip, din_len, last, op);
if (res)
break;
}
@@ -763,14 +876,14 @@ main(int argc, char * argv[])
if (op->verbose)
pr2serr("sending Activate deferred microcode [0xf]\n");
res = send_then_receive(sg_fd, gen_code, 0, NULL, 0, &dout,
- dip, 1, op);
+ dip, din_len, true, op);
}
} else {
if (op->verbose)
pr2serr("single: mode=0x%x, id=%d, offset=%d, len=%d\n",
op->mc_mode, op->mc_id, op->mc_offset, op->mc_len);
res = send_then_receive(sg_fd, gen_code, 0, dmp, op->mc_len, &dout,
- dip, 1, op);
+ dip, din_len, true, op);
}
if (res)
ret = res;
diff --git a/src/sg_write_buffer.c b/src/sg_write_buffer.c
index 7560e7e8..18d8f6f8 100644
--- a/src/sg_write_buffer.c
+++ b/src/sg_write_buffer.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006-2017 Luben Tuikov and Douglas Gilbert.
+ * Copyright (c) 2006-2018 Luben Tuikov and Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
@@ -23,15 +23,20 @@
#include "sg_lib.h"
#include "sg_cmds_basic.h"
#include "sg_cmds_extra.h"
-#include "sg_pt.h" /* needed for scsi_pt_win32_direct() */
#include "sg_unaligned.h"
#include "sg_pr2serr.h"
+#ifdef SG_LIB_WIN32
+#ifdef SG_LIB_WIN32_DIRECT
+#include "sg_pt.h" /* needed for scsi_pt_win32_direct() */
+#endif
+#endif
+
/*
* This utility issues the SCSI WRITE BUFFER command to the given device.
*/
-static const char * version_str = "1.23 20171008"; /* spc5r10 */
+static const char * version_str = "1.24 20180111"; /* spc5r18 */
#define ME "sg_write_buffer: "
#define DEF_XFER_LEN (8 * 1024 * 1024)
@@ -44,6 +49,8 @@ static const char * version_str = "1.23 20171008"; /* spc5r10 */
static struct option long_options[] = {
{"bpw", required_argument, 0, 'b'},
+ {"dry-run", no_argument, 0, 'd'},
+ {"dry_run", no_argument, 0, 'd'},
{"help", no_argument, 0, 'h'},
{"id", required_argument, 0, 'i'},
{"in", required_argument, 0, 'I'},
@@ -66,17 +73,21 @@ static void
usage()
{
pr2serr("Usage: "
- "sg_write_buffer [--bpw=CS] [--help] [--id=ID] [--in=FILE]\n"
+ "sg_write_buffer [--bpw=CS] [--dry-run] [--help] [--id=ID] "
+ "[--in=FILE]\n"
" [--length=LEN] [--mode=MO] "
- "[--offset=OFF] [--read-stdin]\n"
- " [--skip=SKIP] [--specific=MS] "
- "[--timeout=TO]\n"
- " [--verbose] [--version] DEVICE\n"
+ "[--offset=OFF]\n"
+ " [--read-stdin] [--skip=SKIP] "
+ "[--specific=MS]\n"
+ " [--timeout=TO] [--verbose] [--version] "
+ "DEVICE\n"
" where:\n"
" --bpw=CS|-b CS CS is chunk size: bytes per write "
"buffer\n"
" command (def: 0 -> as many as "
"possible)\n"
+ " --dry-run|-d skip WRITE BUFFER commands, do "
+ "everything else\n"
" --help|-h print out usage message then exit\n"
" --id=ID|-i ID buffer identifier (0 (default) to "
"255)\n"
@@ -178,7 +189,8 @@ int
main(int argc, char * argv[])
{
bool bpw_then_activate = false;
- bool got_stdin;
+ bool dry_run = false;
+ bool got_stdin = false;
bool wb_len_given = false;
int sg_fd, infd, res, c, len, k, n;
int bpw = 0;
@@ -202,7 +214,7 @@ main(int argc, char * argv[])
while (1) {
int option_index = 0;
- c = getopt_long(argc, argv, "b:hi:I:l:m:o:rs:S:t:vV", long_options,
+ c = getopt_long(argc, argv, "b:dhi:I:l:m:o:rs:S:t:vV", long_options,
&option_index);
if (c == -1)
break;
@@ -220,6 +232,9 @@ main(int argc, char * argv[])
bpw_then_activate = true;
}
break;
+ case 'd':
+ dry_run = true;
+ break;
case 'h':
case '?':
++do_help;
@@ -438,28 +453,47 @@ main(int argc, char * argv[])
pr2serr("sending write buffer, mode=0x%x, mspec=%d, id=%d, "
" offset=%d, len=%d\n", wb_mode, wb_mspec, wb_id,
wb_offset + k, n);
- res = sg_ll_write_buffer_v2(sg_fd, wb_mode, wb_mspec, wb_id,
- wb_offset + k, dop + k, n,
- wb_timeout, true, verbose);
+ if (dry_run) {
+ if (verbose)
+ pr2serr("skipping WRITE BUFFER command due to "
+ "--dry-run\n");
+ res = 0;
+ } else
+ res = sg_ll_write_buffer_v2(sg_fd, wb_mode, wb_mspec, wb_id,
+ wb_offset + k, dop + k, n,
+ wb_timeout, true, verbose);
if (res)
break;
}
if (bpw_then_activate) {
if (verbose)
pr2serr("sending Activate deferred microcode [0xf]\n");
- res = sg_ll_write_buffer_v2(sg_fd, MODE_ACTIVATE_MC,
- 0 /* buffer_id */,
- 0 /* buffer_offset */, 0,
- NULL, 0, wb_timeout, true, verbose);
+ if (dry_run) {
+ if (verbose)
+ pr2serr("skipping WRITE BUFFER(ACTIVATE) command due to "
+ "--dry-run\n");
+ res = 0;
+ } else
+ res = sg_ll_write_buffer_v2(sg_fd, MODE_ACTIVATE_MC,
+ 0 /* buffer_id */,
+ 0 /* buffer_offset */, 0,
+ NULL, 0, wb_timeout, true,
+ verbose);
}
} else {
if (verbose)
pr2serr("sending single write buffer, mode=0x%x, mpsec=%d, "
"id=%d, offset=%d, len=%d\n", wb_mode, wb_mspec, wb_id,
wb_offset, wb_len);
- res = sg_ll_write_buffer_v2(sg_fd, wb_mode, wb_mspec, wb_id,
- wb_offset, dop, wb_len, wb_timeout, true,
- verbose);
+ if (dry_run) {
+ if (verbose)
+ pr2serr("skipping WRITE BUFFER(all in one) command due to "
+ "--dry-run\n");
+ res = 0;
+ } else
+ res = sg_ll_write_buffer_v2(sg_fd, wb_mode, wb_mspec, wb_id,
+ wb_offset, dop, wb_len, wb_timeout,
+ true, verbose);
}
if (0 != res) {
char b[80];