aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2017-12-07 13:32:05 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2017-12-07 13:32:05 +0000
commitefe114304fdf1c20afd491eacfd1aade4b136ca5 (patch)
treeb1672761995949e914d9821fa33b157ed1ce80ab
parent0693a6de283a5802c480ff352b2933b87020cd42 (diff)
downloadsg3_utils-efe114304fdf1c20afd491eacfd1aade4b136ca5.tar.gz
add include/sg_pt_linux.h lib/sg_pt_linux_nvme.c and .gitignore; sg_write_x: almost finished; more NVMe work (for sg_ses)
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@733 6180dd3e-e324-4e3e-922d-17de1ae2f315
-rw-r--r--.gitignore102
-rw-r--r--COVERAGE24
-rw-r--r--ChangeLog10
-rw-r--r--config.h.in2
-rwxr-xr-xconfigure2
-rw-r--r--configure.ac4
-rw-r--r--debian/changelog2
-rw-r--r--doc/sg_write_same.834
-rw-r--r--doc/sg_write_x.8311
-rw-r--r--include/Makefile.am6
-rw-r--r--include/Makefile.in10
-rw-r--r--include/sg_cmds_basic.h2
-rw-r--r--include/sg_cmds_extra.h4
-rw-r--r--include/sg_lib.h12
-rw-r--r--include/sg_pt.h8
-rw-r--r--include/sg_pt_linux.h158
-rw-r--r--include/sg_pt_nvme.h37
-rw-r--r--lib/Makefile.am3
-rw-r--r--lib/Makefile.in11
-rw-r--r--lib/sg_lib.c97
-rw-r--r--lib/sg_lib_data.c6
-rw-r--r--lib/sg_pt_linux.c861
-rw-r--r--lib/sg_pt_linux_nvme.c387
-rw-r--r--sg3_utils.spec2
-rw-r--r--src/sg_dd.c25
-rw-r--r--src/sg_inq.c83
-rw-r--r--src/sg_raw.c65
-rw-r--r--src/sg_ses.c83
-rw-r--r--src/sg_vpd.c10
-rw-r--r--src/sg_write_x.c574
30 files changed, 1656 insertions, 1279 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..d194ced1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,102 @@
+# Please keep the entries in this file sorted with the following vi command:
+# :3,$!LC_ALL=C sort -fu
+
+*.exe
+*.la
+*.lo
+*.o
+*~
+.deps
+.libs
+aclocal.m4
+ar-lib
+autom4te.cache/
+compile
+config.guess
+config.h
+config.log
+config.status
+config.sub
+configure
+depcomp
+doc/Makefile
+doc/sg_scan.8
+include/Makefile
+INSTALL
+install-sh
+lib/Makefile
+libtool
+ltmain.sh
+Makefile
+Makefile.in
+missing
+src/Makefile
+src/sginfo
+src/sgm_dd
+src/sgp_dd
+src/sg_bg_ctl
+src/sg_compare_and_write
+src/sg_copy_results
+src/sg_dd
+src/sg_decode_sense
+src/sg_emc_trespass
+src/sg_format
+src/sg_get_config
+src/sg_get_lba_status
+src/sg_ident
+src/sg_inq
+src/sg_logs
+src/sg_luns
+src/sg_map
+src/sg_map26
+src/sg_modes
+src/sg_opcodes
+src/sg_persist
+src/sg_prevent
+src/sg_raw
+src/sg_rbuf
+src/sg_rdac
+src/sg_read
+src/sg_readcap
+src/sg_read_attr
+src/sg_read_block_limits
+src/sg_read_buffer
+src/sg_read_long
+src/sg_reassign
+src/sg_referrals
+src/sg_rep_zones
+src/sg_requests
+src/sg_reset
+src/sg_reset_wp
+src/sg_rmsn
+src/sg_rtpg
+src/sg_safte
+src/sg_sanitize
+src/sg_sat_identify
+src/sg_sat_phy_event
+src/sg_sat_read_gplog
+src/sg_sat_set_features
+src/sg_scan
+src/sg_scan.c
+src/sg_senddiag
+src/sg_ses
+src/sg_ses_microcode
+src/sg_start
+src/sg_stpg
+src/sg_sync
+src/sg_test_rwbuf
+src/sg_timestamp
+src/sg_turs
+src/sg_unmap
+src/sg_verify
+src/sg_vpd
+src/sg_write_atomic
+src/sg_write_buffer
+src/sg_write_long
+src/sg_write_same
+src/sg_write_verify
+src/sg_write_x
+src/sg_wr_mode
+src/sg_xcopy
+src/sg_zone
+stamp-h1
diff --git a/COVERAGE b/COVERAGE
index 313a4c9a..83c8105f 100644
--- a/COVERAGE
+++ b/COVERAGE
@@ -37,6 +37,8 @@ MODE SENSE(6) sdparm, sg_modes, sg_wr_mode, sginfo, sg_format,
MODE SENSE(10) sdparm, sg_modes, sg_wr_mode, sginfo, sg_format,
sg_senddiag('-e'), sg_rdac, ++
OPEN ZONE sg_zone
+ORWRITE(16) sg_write_x
+ORWRITE(32) sg_write_x
PERSISTENT RESERVE IN sg_persist, ++
PERSISTENT RESERVE OUT sg_persist, ++
POPULATE TOKEN ddpt, ddptctl, ++
@@ -99,15 +101,18 @@ WRITE BUFFER sg_test_rwbuf, sg_write_buffer, ++
WRITE LONG(10) sg_write_long, ++
WRITE LONG(16) sg_write_long, ++
WRITE SAME(10) sg_write_same
-WRITE SAME(16) sg_write_same
-WRITE SAME(32) sg_write_same
+WRITE SAME(16) sg_write_same, sg_write_x
+WRITE SAME(32) sg_write_same, sg_write_x
+WRITE SCATTERED(16) sg_write_x
+WRITE SCATTERED(32) sg_write_x
+WRITE STREAM(16) sg_write_x
+WRITE STREAM(32) sg_write_x
WRITE USING TOKEN ddpt, ddptctl, ++
<most commands> sg_raw
-
-ATA command sg3_utils utilities that use this SCSI command
------------ ----------------------------------------------
+ATA command sg3_utils utilities that use this (S)ATA command
+----------- ------------------------------------------------
CHECK POWER MODE examples/sg_sat_chk_power
IDENTIFY DEVICE sg_inq, sg_scan, sg_sat_identify,
examples/sg__sat_identify
@@ -121,6 +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
+------------ ------------------------------------------------
+IDENTIFY sg_inq
+SES READ sg_senddiag, sg_ses (NVME-MI command)
+SES WRITE sg_senddiag, sg_ses (NVME-MI command)
+
+
++ command wrapper found in sg_cmds_basic.c, sg_cmds_mmc.c or
sg_cmds_extra.c for this command
(2) this command was known as REPORT DEVICE IDENTIFIER prior to spc4r07
@@ -137,4 +149,4 @@ THIRD PARTY COPY IN (0x83).
Douglas Gilbert
-15th November 2017
+30th November 2017
diff --git a/ChangeLog b/ChangeLog
index 4209f57a..2ea98dd3 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 [20171127] [svn: r732]
+Changelog for sg3_utils-1.43 [20171206] [svn: r733]
- 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
@@ -92,6 +92,7 @@ Changelog for sg3_utils-1.43 [20171127] [svn: r732]
- add sg_decode_transportid_str()
- add sg_msense_calc_length()
- add sg_all_zeros(), sg_all_ffs()
+ - add sg_memalign() and sg_get_page_size()
- implement 'format' argument in dStrHexStr()
- add read buffer(16) command mode names
- add Microcode activation sense descriptor spc5r10
@@ -112,14 +113,17 @@ Changelog for sg3_utils-1.43 [20171127] [svn: r732]
- add to install list in Makefile, hope it does
not clash with other package providing it
- 55-scsi-sg3_id.rules: fixes from Suse
- - https://github.com/hreinecke/sg3_utils
- branch sles15 synced 20170914
+ - https://github.com/hreinecke/sg3_utils branch
+ sles15 synced 20170914
- move some testing utilities out of the
'examples' and 'utils' directories into the new
'testing' directory
- gcc 7.2 cleanups (sysmacros.h etc)
- clang --analyze static checker clean ups
- shellcheck cleanup on scripts
+ - --disable-linuxbsg to ./configure still accepted
+ but now ignored, Linux sg v3 or v4 interface
+ decision made at runtime
- automake: add AM_PROG_AR to configure.ac
- upgrade to version 1.15
diff --git a/config.h.in b/config.h.in
index 5bab057b..12379ef9 100644
--- a/config.h.in
+++ b/config.h.in
@@ -60,7 +60,7 @@
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
-/* ignore linux bsg */
+/* option ignored */
#undef IGNORE_LINUX_BSG
/* Define to the sub-directory where libtool stores uninstalled libraries. */
diff --git a/configure b/configure
index f1970e93..4ab0fc69 100755
--- a/configure
+++ b/configure
@@ -1438,7 +1438,7 @@ Optional Features:
--enable-fast-install[=PKGS]
optimize for fast installation [default=yes]
--disable-libtool-lock avoid locking (might break parallel builds)
- --disable-linuxbsg ignore linux bsg (sgv4) if present
+ --disable-linuxbsg option ignored, this is placeholder
--enable-win32-spt-direct
enable Win32 SPT Direct
--disable-scsistrings Disable full SCSI sense strings
diff --git a/configure.ac b/configure.ac
index 135a5797..c32e0585 100644
--- a/configure.ac
+++ b/configure.ac
@@ -95,8 +95,8 @@ AM_CONDITIONAL(OS_WIN32_MINGW, [echo $host_os | grep '^mingw' > /dev/null])
AM_CONDITIONAL(OS_WIN32_CYGWIN, [echo $host_os | grep '^cygwin' > /dev/null])
AC_ARG_ENABLE([linuxbsg],
- AC_HELP_STRING([--disable-linuxbsg], [ignore linux bsg (sgv4) if present]),
- [AC_DEFINE_UNQUOTED(IGNORE_LINUX_BSG, 1, [ignore linux bsg], )], [])
+ AC_HELP_STRING([--disable-linuxbsg], [option ignored, this is placeholder]),
+ [AC_DEFINE_UNQUOTED(IGNORE_LINUX_BSG, 1, [option ignored], )], [])
AC_ARG_ENABLE([win32-spt-direct],
AC_HELP_STRING([--enable-win32-spt-direct], [enable Win32 SPT Direct]),
diff --git a/debian/changelog b/debian/changelog
index 763093bb..64d72ec5 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> Fri, 03 Nov 2017 12:00:00 -0400
+ -- Douglas Gilbert <dgilbert@interlog.com> Mon, 04 Dec 2017 15:00:00 -0500
sg3-utils (1.42-0.1) unstable; urgency=low
diff --git a/doc/sg_write_same.8 b/doc/sg_write_same.8
index b8da6903..55e739d9 100644
--- a/doc/sg_write_same.8
+++ b/doc/sg_write_same.8
@@ -1,4 +1,4 @@
-.TH SG_WRITE_SAME "8" "September 2017" "sg3_utils\-1.43" SG3_UTILS
+.TH SG_WRITE_SAME "8" "November 2017" "sg3_utils\-1.43" SG3_UTILS
.SH NAME
sg_write_same \- send SCSI WRITE SAME command
.SH SYNOPSIS
@@ -31,7 +31,7 @@ or the \fI\-\-unmap\fR option is given then WRITE SAME(16) is sent.
The \fI\-\-10\fR, \fI\-\-16\fR and \fI\-\-32\fR options are mutually
exclusive.
.PP
-SBC\-3 revision 35d introduced a "no data\-out buffer" (NDOB) bit which, if
+SBC\-3 revision 35d introduced a "No Data\-Out Buffer" (NDOB) bit which, if
set, bypasses the requirement to send a single block of data to the
\fIDEVICE\fR together with the command. Only WRITE SAME (16 and 32 byte)
support the NDOB bit. If given, a user block of zeros is assumed; if
@@ -94,13 +94,13 @@ sets the 'Group number' field to \fIGN\fR. Defaults to a value of zero.
output the usage message then exit.
.TP
\fB\-i\fR, \fB\-\-in\fR=\fIIF\fR
-read data (binary) from file named \fIIF\fR and use it as the data out
-buffer for the SCSI WRITE SAME command. The length of the data out buffer
+read data (binary) from file named \fIIF\fR and use it as the data\-out
+buffer for the SCSI WRITE SAME command. The length of the data\-out buffer
is \fI\-\-xferlen=LEN\fR or, if that is not given, the length of the \fIIF\fR
file. If \fIIF\fR is "\-" then stdin is read. If this option is not given
-then 0x00 bytes are used as fill with the length of the data out buffer
+then 0x00 bytes are used as fill with the length of the data\-out buffer
obtained from \fI\-\-xferlen=LEN\fR or by calling READ CAPACITY(16 or 10).
-If the response to READ CAPACITY(16) has the PROT_EN bit set then data
+If the response to READ CAPACITY(16) has the PROT_EN bit set then data\-
out buffer size is modified accordingly with the last 8 bytes set to 0xff.
.TP
\fB\-l\fR, \fB\-\-lba\fR=\fILBA\fR
@@ -121,10 +121,10 @@ only be given if \fILEN\fR is 0 .
.TP
\fB\-n\fR, \fB\-\-num\fR=\fINUM\fR
where \fINUM\fR is the number of blocks, starting at \fILBA\fR, to write the
-data out buffer to. The default value for \fINUM\fR is 1. The value corresponds
-to the 'Number of logical blocks' field in the WRITE SAME cdb.
+data\-out buffer to. The default value for \fINUM\fR is 1. The value
+corresponds to the 'Number of logical blocks' field in the WRITE SAME cdb.
.br
-Note that a value of 0 in \fINUM\fR may be interpreted as write the data out
+Note that a value of 0 in \fINUM\fR may be interpreted as write the data\-out
buffer on every block starting at \fILBA\fR to the end of the \fIDEVICE\fR.
If the WSNZ bit (introduced in sbc3r26, January 2011) in the Block Limits VPD
page is set then the value of 0 is disallowed, yielding an Invalid request
@@ -154,17 +154,17 @@ sets the "Write protect" field in the WRITE SAME cdb to \fIWPR\fR. The
default value is zero. \fIWPR\fR should be a value between 0 and 7.
When \fIWPR\fR is 1 or greater, and the disk's protection type is 1 or
greater, then 8 extra bytes of protection information are expected or
-generated (to place in the command's data out buffer).
+generated (to place in the command's data\-out buffer).
.TP
\fB\-x\fR, \fB\-\-xferlen\fR=\fILEN\fR
-where \fILEN\fR is the data out buffer length. Defaults to the length of
+where \fILEN\fR is the data\-out buffer length. Defaults to the length of
the \fIIF\fR file or, if that is not given, then the READ CAPACITY(16 or 10)
command is used to find the 'Logical block length in bytes'. That figure
may be increased by 8 bytes if the \fIDEVICE\fR's protection type is 1 or
greater and the WRPROTECT field (see \fI\-\-wrprotect=WPR\fR) is 1 or
greater. If both this option and the \fIIF\fR option are given and
\fILEN\fR exceeds the length of the \fIIF\fR file then \fILEN\fR is the
-data out buffer length with zeros used as pad bytes.
+data\-out buffer length with zeros used as pad bytes.
.SH UNMAP
Logical block provisioning is a new term introduced in SBC\-3 revision 25
for the ability to mark blocks as unused. For large storage arrays, it is a
@@ -194,13 +194,13 @@ Logical Block Provisioning VPD page. When LBPU is set it indicates that
the device supports the UNMAP command (see the sg_unmap utility). When the
ANC_SUP bit is set it indicates the device supports anchored LBAs.
.PP
-When the UNMAP bit is set in the cdb then the data out buffer is also sent.
-Additionally the data section of that data out buffer should be full of 0x0
+When the UNMAP bit is set in the cdb then the data\-out buffer is also sent.
+Additionally the data section of that data\-out buffer should be full of 0x0
bytes while the data protection block, 8 bytes at the end if present, should
be set to 0xff bytes. If these conditions are not met and the LBPRZ bit is
-set then the UNMAP bit is ignored and the data out buffer is written to the
+set then the UNMAP bit is ignored and the data\-out buffer is written to the
\fIDEVICE\fR as if the UNMAP bit was zero. In the absence of the
-\fI\-\-in=IF\fR option, this utility will attempt build a data out buffer
+\fI\-\-in=IF\fR option, this utility will attempt build a data\-out buffer
that meets the requirements for the UNMAP bit in the cdb to be acted on by
the \fIDEVICE\fR.
.PP
@@ -239,7 +239,7 @@ of bytes in a logical block. Let us assume that is 512 bytes. Since
\fI\-\-in=IF\fR is not given a block of zeros is assumed. So 63 blocks
of zeros (each block containing 512 bytes) will be written from (and
including) LBA 0x1234 . Note that only one block of zeros is passed
-to the SCSI WRITE SAME command in the data out buffer (as required by
+to the SCSI WRITE SAME command in the data\-out buffer (as required by
SBC\-3).
.PP
A similar example follows but in this case the blocks
diff --git a/doc/sg_write_x.8 b/doc/sg_write_x.8
index 44fe5dda..760c0d6f 100644
--- a/doc/sg_write_x.8
+++ b/doc/sg_write_x.8
@@ -1,4 +1,4 @@
-.TH SG_WRITE_X "8" "November 2017" "sg3_utils\-1.43" SG3_UTILS
+.TH SG_WRITE_X "8" "December 2017" "sg3_utils\-1.43" SG3_UTILS
.SH NAME
sg_write_x \- SCSI WRITE normal/ATOMIC/SAME/SCATTERED/STREAM, ORWRITE commands
.SH SYNOPSIS
@@ -9,8 +9,8 @@ sg_write_x \- SCSI WRITE normal/ATOMIC/SAME/SCATTERED/STREAM, ORWRITE commands
[\fI\-\-generation=EOG,NOG\fR] [\fI\-\-grpnum=GN\fR] [\fI\-\-help\fR]
\fI\-\-in=IF\fR [\fI\-\-lba=LBA[,LBA...]\fR] [\fI\-\-normal\fR]
[\fI\-\-num=NUM[,NUM...]\fR] [\fI\-\-offset=OFF[,DLEN]\fR] [\fI\-\-or\fR]
-[\fI\-\-raw\fR] [\fI\-\-ref\-tag=RT\fR] [\fI\-\-same=NDOB\fR]
-[\fI\-\-scat\-file=SF\fR] [\fI\-\-scattered=RD\fR] [\fI\-\-stream=ID\fR]
+[\fI\-\-ref\-tag=RT\fR] [\fI\-\-same=NDOB\fR] [\fI\-\-scat\-file=SF\fR]
+[\fI\-\-scat\-raw\fR] [\fI\-\-scattered=RD\fR] [\fI\-\-stream=ID\fR]
[\fI\-\-strict\fR] [\fI\-\-tag\-mask=TM\fR] [\fI\-\-timeout=TO\fR]
[\fI\-\-unmap=U_A\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR]
[\fI\-\-wrprotect=WPR\fR] \fIDEVICE\fR
@@ -49,10 +49,10 @@ sg_write_x \- SCSI WRITE normal/ATOMIC/SAME/SCATTERED/STREAM, ORWRITE commands
\fI\-\-scattered=RD\fR [\fI\-\-16\fR] [\fI\-\-32\fR] [\fI\-\-app-tag=AT\fR]
[\fI\-\-bs=LBS\fR] [\fI\-\-dld=DLD\fR] [\fI\-\-dpo\fR] [\fI\-\-fua\fR]
[\fI\-\-grpnum=GN\fR] \fI\-\-in=IF\fR [\fI\-\-lba=LBA[,LBA...]\fR]
-[\fI\-\-num=NUM[,NUM...]\fR] [\fI\-\-offset=OFF[,DLEN]\fR] [\fI\-\-raw\fR]
-[\fI\-\-ref\-tag=RT\fR] [\fI\-\-scat\-file=SF\fR] [\fI\-\-strict\fR]
-[\fI\-\-tag\-mask=TM\fR] [\fI\-\-timeout=TO\fR] [\fI\-\-wrprotect=WPR\fR]
-\fIDEVICE\fR
+[\fI\-\-num=NUM[,NUM...]\fR] [\fI\-\-offset=OFF[,DLEN]\fR]
+[\fI\-\-ref\-tag=RT\fR] [\fI\-\-scat\-file=SF\fR] [\fI\-\-scat\-raw\fR]
+[\fI\-\-strict\fR] [\fI\-\-tag\-mask=TM\fR] [\fI\-\-timeout=TO\fR]
+[\fI\-\-wrprotect=WPR\fR] \fIDEVICE\fR
.PP
.B sg_write_x
\fI\-\-stream=ID\fR [\fI\-\-16\fR] [\fI\-\-32\fR] [\fI\-\-app-tag=AT\fR]
@@ -100,12 +100,12 @@ CAPACITY(16) fails then READ CAPACITY(10) is tried and if that works then
the logical block size in the response is used as the actual block size.
.PP
The number of bytes this utility will attempt to read from the file named by
-\fIIF\fR is the product of the actual block size and the number of
-blocks (\fINUM\fR or the sum of \fINUM\fR arguments). If less bytes are read
-from the file \fIIF\fR and the \fI\-\-strict\fR option is given then this
-utility exits at this point with an exit status of SG_LIB_FILE_ERROR. If less
-bytes are read from the file \fIIF\fR and the \fI\-\-strict\fR option is not
-given then zero bytes are substituted for the "missing" bytes and this
+\fIIF\fR is the product of the actual block size and the
+number_of_blocks (\fINUM\fR or the sum of \fINUM\fR arguments). If less bytes
+are read from the file \fIIF\fR and the \fI\-\-strict\fR option is given then
+this utility exits with an exit status of SG_LIB_FILE_ERROR. If less bytes
+are read from the file \fIIF\fR and the \fI\-\-strict\fR option is not
+given then bytes of zero are substituted for the "missing" bytes and this
utility continues.
.PP
Attempts to write multi megabyte data with a single command are likely to fail
@@ -162,7 +162,7 @@ is 0xffff .
selects the WRITE ATOMIC command and \fIAB\fR is placed in the Atomic
Boundary field of its cdb. It is a 16 bit field so the maximum value
is 0xffff. If unsure what value to set, try 0 which will attempt to
-write the whole data-out buffer in a single atomic operation.
+write the whole data\-out buffer in a single atomic operation.
.TP
\fB\-B\fR, \fB\-\-bmop\fR=\fIOP,PGP\fR
where \fIOP\fR and \fIPGP\fR are the values to be placed in ORWRITE(32)'s
@@ -179,34 +179,39 @@ CAPACITY(16) command is sent to \fIDEVICE\fR. If that fails then the READ
CAPACITY(10) command is sent.
.TP
\fB\-c\fR, \fB\-\-combined\fR=\fIDOF\fR
-This option only applies to WRITE SCATTERED and assumes the whole data-out
-buffer (including the scatter list) can be read from \fIIF\fR given by
-the \fI\-\-in=IF\fR option. If the \fI\-\-lba=LBA[,LBA...]\fR,
+This option only applies to WRITE SCATTERED and assumes the whole data\-out
+buffer can be read from \fIIF\fR given by the \fI\-\-in=IF\fR option. The
+whole data\-out buffer is the parameter list header, followed by one or more
+LBA range descriptors, optional followed by some pad bytes and then the data
+to be written to the media. If the \fI\-\-lba=LBA[,LBA...]\fR,
\fI\-\-num=NUM[,NUM...]\fR or \fI\-\-scat\-file=SF\fR options are given
-then they are ignored (or an error occurs if the \fI\-\-strict\fR option
-is present). The \fIDOF\fR argument should be the value suitable for
-the 'Logical Block Data Offset' field in the WRITE SCATTERED cdb. This is
-the offset in the data-out buffer where the data to write commences. The
-unit of that field is the actual block size which is the logical block
-size plus 8, if protection information (PI) is being sent. When \fIWPR\fR
-(from \fI\-\-wrprotect=WPR\fR) is greater than zero then PI is expected.
-SBC\-4 revision 15 does not state it but it would appear that a \fIDOF\fR
-value of 0 is invalid.
+then an error is generated. The \fIDOF\fR argument should be the value
+suitable for the 'Logical Block Data Offset' field in the WRITE SCATTERED
+cdb. This is the offset in the data\-out buffer where the data to write
+to the media commences. The unit of that field is the actual block size
+which is the logical block size plus 8, if protection information (PI) is
+being sent. When \fIWPR\fR (from \fI\-\-wrprotect=WPR\fR) is greater than
+zero then PI is expected. SBC\-4 revision 15 does not state it but it would
+appear that a \fIDOF\fR value of 0 is invalid. It is suggested that this
+option be used with the \fI\-\-strict\fR option while experimenting as
+random or incorrect data fed in via the \fI\-\-in=IF\fR option could write
+a lot of "interesting" data on the \fIDEVICE\fR. If \fIDOF\fR is given as 0
+the utility will scan the data in \fIIF\fR until \fIRD\fR LBA range
+descriptors are found; or if \fIRD\fR is also 0 until a degenerate LBA range
+descriptor is found.
.TP
\fB\-D\fR, \fB\-\-dld\fR=\fIDLD\fR
where \fIDLD\fR is the duration limits descriptor spread across 3 bits in
-the SCSI WRITE(16) cdb. \fIDLD\fR is between 0 to 7 inclusive with a default
-of zero. The DLD0 field in WRITE(16) is set if (0x1 & \fIDLD\fR) is non\-zero.
-The DLD1 field in WRITE(16) is (0x2 & \fIDLD\fR) is non\-zero. The DLD2 field
-in WRITE(16) is (0x4 & \fIDLD\fR) is non\-zero.
+the SCSI WRITE(16) and the WRITE SCATTERED(16) cdbs. \fIDLD\fR is between 0
+to 7 inclusive with a default of zero. The DLD0 field in WRITE(16) and WRITE
+SCATTERED(16) is set if (0x1 & \fIDLD\fR) is non\-zero. The DLD1 field in
+both cdbs is set if (0x2 & \fIDLD\fR) is non\-zero. The DLD2 field in both
+cdbs is set if (0x4 & \fIDLD\fR) is non\-zero.
.TP
\fB\-d\fR, \fB\-\-dpo\fR
if this option is given then the DPO (disable page out) bit field in the
-cdb is set. The default is to clear this bit field.
-.TP
-\fB\-f\fR, \fB\-\-fua\fR
-if this option is given then the FUA (force unit access) bit field in the
-cdb is set. The default is to clear this bit field.
+cdb is set. The default is to clear this bit field. Applies to all write
+commands except WRITE SAME.
.TP
\fB\-x\fR, \fB\-\-dry\-run\fR
this option exits (with a status of 0) just before it would otherwise send
@@ -217,70 +222,168 @@ the data in and process it if the \fI\-\-in=IF\fR and/or the
and sanity checks (e.g. if the \fI\-\-strict\fR option is given) will be
performed and if there is an error then there will be a non zero exit
status value.
-xxxxxxxxxxxxxxxxxxxxxxxxxxx
+.TP
+\fB\-f\fR, \fB\-\-fua\fR
+if this option is given then the FUA (force unit access) bit field in the
+cdb is set. The default is to clear this bit field. Applies to all write
+commands except WRITE SAME.
+.TP
+\fB\-G\fR, \fB\-\-generation\fR=\fIEOG,NOG\fR
+the arguments for this option are used by the ORWITE(32) command only.
+\fIEOG\fR is placed in the "Expected ORWgeneration" field while \fINOG\fR
+is placed in the "New ORWgeneration" field. Both are 32 bits long and
+default to zero.
.TP
\fB\-g\fR, \fB\-\-grpnum\fR=\fIGN\fR
sets the 'Group number' field to \fIGN\fR. Defaults to a value of zero.
\fIGN\fR should be a value between 0 and 63.
.TP
\fB\-h\fR, \fB\-\-help\fR
-output the usage message then exit.
+output the usage message then exit. Use multiple times for more help.
+Currently '\-h' to '\-hhhh' provide different output.
.TP
\fB\-i\fR, \fB\-\-in\fR=\fIIF\fR
-read data (binary) from a file named \fIIF\fR in a single OS system
-call (read(2)). That data is placed in a continuous buffer and then used as
-the data out buffer for the SCSI WRITE ATOMIC(16 or 32) or the normal SCSI
-WRITE(16 or 32) command. The data read from \fIIF\fR starts from byte offset
-\fIOFF\fR which defaults to zero and that is the start of \fIIF\fR. The
-number of bytes read from \fIIF\fR is basically the product of \fINUM\fR and
-\fIBS\fR (i.e. the number_of_blocks multiplied by block_size). This option
-is mandatory. In Unix based OSes, any number of zeros can produced by
-using the /dev/zero device file.
-.TP
-\fB\-l\fR, \fB\-\-lba\fR=\fILBA\fR
-where \fILBA\fR is the logical block address (lba) of the first block written
-by the SCSI WRITE ATOMIC(16 or 32) or SCSI WRITE(16 or 32) command. Defaults
-to lba 0 which is a dangerous block to overwrite on a disk that is in use.
-\fILBA\fR is assumed to be in decimal unless prefixed with '0x' or has a
-trailing 'h'.
-.TP
-\fB\-N\fR, \fB\-\-non\-atomic\fR
-when this option is given either a SCSI WRITE(16) or SCSI WRITE(32) command
-is sent. The default (i.e. in the absence of this option) is to send
-either SCSI WRITE ATOMIC(16) or SCSI WRITE ATOMIC(32) command.
-.TP
-\fB\-n\fR, \fB\-\-num\fR=\fINUM\fR
-where \fINUM\fR is the number of blocks, to read from the file named \fIIF\fR.
-It is also the number of blocks written using a SCSI WRITE ATOMIC(16 or 32)
-or a SCSI WRITE(16 or 32). The default is 0 which is the degenerate case
-that will not modify the \fIDEVICE\fR but is still valid.
-.TP
-\fB\-o\fR, \fB\-\-offset\fR=\fIOFF\fR
+read data (in binary) from a file named \fIIF\fR in a single OS system
+call (in Unix: read(2)). That data is placed in a continuous buffer and then
+used as the data\-out buffer for all SCSI write commands apart from WRITE
+SCATTERED(16 or 32) which may include other data in the data\-out buffer.
+For WRITE SCATTERED (16 or 32) the data\-out buffer is made up of 3 or 4
+components in this order: a parameter list header (32 zero bytes); zero or
+more LBA range descriptors, optionally some pad bytes (zeros) and then data
+to write to the media. For WRITE
+SCATTERED \fIIF\fR only provides the data to write to the media unless
+\fI\-\-combined=DOF\fR is given. When the \fI\-\-combined=DOF\fR option is
+given \fIIF\fR contains all 3 components of the WRITE SCATTERED data\-out
+buffer in binary. The data read from \fIIF\fR starts from byte offset
+\fIOFF\fR which defaults to zero and no more than \fIDLEN\fR bytes are read
+from that point (i.e. the file byte offset \fIOFF\fR). If \fIDLEN\fR is
+zero or not given the rest of the file \fIIF\fR is read. This option is
+mandatory apart from when \-\-same=1 is given (that sets the NDOB bit which
+stands for "No Data Out Buffer"). In Unix based OSes, any number of zeros
+can produced by using the /dev/zero device file.
+.TP
+\fB\-l\fR, \fB\-\-lba\fR=\fILBA[,LBA...]\fR
+where the argument is a single Logical Block Address (LBA) or a comma
+separated list of \fILBA\fRs each of which is the address of the first block
+written by the selected write command. Only the WRITE SCATTERED command
+can usefully take more than one \fILBA\fR. Whatever number of \fILBA\fRs is
+given, there needs to be an equal number of \fINUM\fRs given to the
+\fI\-\-num=NUM[,NUM...]\fR option. The first given \fILBA\fR joins with the
+first given \fINUM\fR to form the first LBA range descriptor (which T10
+number from zero in SBC\-4). The second \fILBA\ffR joins with the second
+\fILBA\fR to form the second LBA range descriptor, etc. A more convenient
+way to define a large number of LBA range descriptors is the \fISF\fR
+name of the file given with the \fI\-\-scat\-file=SF\fR option. Defaults
+to logical block 0 (which could be dangerous) while \fINUM\fR defaults to
+0 which makes the combination harmless. \fILBA\fR is assumed to be in decimal
+unless prefixed with '0x' or has a trailing 'h'.
+.TP
+\fB\-N\fR, \fB\-\-normal\fR
+as well as implicitly selecting a "normal" WRITE (16 or 32) in the absence
+of selecting any other command, the choice of a "normal" WRITE can be made
+.TP
+\fB\-n\fR, \fB\-\-num\fR=\fINUM[,NUM...]\fR
+where the argument is a single NUMber of blocks (NUM) or a comma separated
+list of \fINUM\fRs that pair with the corresponding entries in the
+\fI\-\-lba=LBA[,LBA...]\fR option. If a \fINUM\fR is given and is not
+provided by another method (e.g. by using the \fI\-\-scat\-file=SF\fR option)
+then it defaults to the number of blocks derived from the size of the file
+named by \fIIF\fR (starting at byte offset \fIOFF\fR to the end or the file
+or \fIDLEN\fR). Apart from the \fI\-\-combined=DOF\fR option, an LBA must
+be explicitly given (either with \fII\-\-lba=LBA\fR or via
+\fI\-\-scat\-file=SF\fR), if not \fINUM\fR defaults to 0 as a safety measure.
+.TP
+\fB\-o\fR, \fB\-\-offset\fR=\fIOFF[,DLEN]\fR
where \fIOFF\fR is the byte offset within the file named \fIIF\fR to start
-reading from. The default value is zero which is the beginning \fIIF\fR.
+reading from. The default value of \fIOFF\fR is zero which is the beginning
+of file named \fIIF\fR. \fIDLEN\fR is the maximum number of bytes to read,
+starting at byte offset \fIOFF\fR, from the file named \fIIF\fR. Less bytes
+will be read if an end of file occurs before \fIDLEN\fR is exhausted. If
+\fIDLEN\fR is zer or not given then reading from byte offset \fIOFF\fR to
+the end of the file named \fIIF\fR is assumed.
+.TP
+\fB\-O\fR, \fB\-\-or\fR
+selects the ORWRITE command. ORWRITE(16) has similar fields to WRITE(16)
+apart from the WRPROTECT field being named ORPROTECT with slightly different
+semantics and the absence of the 3 DLD bit fields. ORWRITE(32) has four
+extra fields that are set with the \fI\-\-bmop=OP,PGP\fR and
+\fI\-\-generation=EOG,NOG\fR options. ORWRITE(32) is the only 32 byte cdb
+command in this utility that does not require a \fIDEVICE\fR formatted with
+type 1, 2 or 3 PI (although it will still work if it is formatted with PI).
.TP
\fB\-r\fR, \fB\-\-ref\-tag\fR=\fIRT\fR
-where \fIRT\fR is the expected logical block reference tag field in the
-WRITE ATOMIC(32) and WRITE(32) cdbs. It is 32 bit field which means the
-maximum value is 0xffffffff. The default value is zero.
+where \fIRT\fR is the expected logical block reference tag field found in
+the 32 byte cdb variants of WRITE, WRITE ATOMIC, WRITE SAME and WRITE STREAM.
+The field is also found in the WRITE SCATTERED(32) LBA range descriptors.
+It is 32 bit field which means the maximum value is 0xffffffff. The default
+value is 0xffffffff.
+.TP
+\fB\-S\fR, \fB\-\-same\fR=\fINDOB\fR
+selects the WRITE SAME command with the NDOB field set to \fINDOB\fR which
+stands for No Data\-Out Buffer. \fINDOB\fR can take values 0 or 1 (i.e. i
+is a single bit field). When \-\-same=1 are options associated with the
+data\-out buffer are ignored.
+.TP
+\fB\-q\fR, \fB\-\-scat\-file\fR=\fISF\fR
+where \fISF\fR is the name of an auxiliary file containing the scatter list
+for the WRITE SCATTERED command. If the \fI\-\-scat\-raw\fR option is also
+given then \fISF\fR is assumed to contain both the parameter list header (32
+bytes of zeros) followed by one or more LBA range descriptors which are
+also 32 bytes long each. These components are as defined by SBC\-4 (i.e.
+in binary with integers in big endian format). If the \fI\-\-scat\-raw\fR
+option is not given then a file of ACSII hexadecimal is expected as described
+in the SCATTERED FILE ASCII FORMAT section below.
+.br
+If this option is given with the \fI\-\-combined=DOF\fR option then this
+utility will exit with a syntax error.
+.TP
+\fB\-R\fR, \fB\-\-scat\-raw\fR
+this option only effects the way that the file named \fISF\fR from the
+\fI\-\-scat\-file=SF\fR option for WRITE SCATTERED is interpreted. By
+default (i.e. without this option), \fISF\fR is parsed as ASCII hexadecimal
+with blank lines and lines contents from and including '#' to the end of
+line ignored. Hence it can contain comments and other indications. When
+this option is given, the file named \fISF\fR is interpreted as binary.
+It is assumed to contain 32 bytes of zeros (the WRITE SCATTERED parameter
+list header) followed by one or more LBA range descriptors (which are 32
+bytes each). If the \fI\-\-strict\fR option is given the reserved field
+in those two items are checked with any non zero bytes causing an error.
+.TP
+\fB\-S\fR, \fB\-\-scattered\fR=\fIRD\fR
+selects the WRITE SCATTERED command with \fIRD\fR being the number of LBA
+range descriptors that will be placed in the data\-out buffer. If \fIRD\fR
+is zero then the logic will try and determine the number of range descriptors
+by other means (e.g. by parsing the file named by \fISF\fR, if there is one).
+The LBA range descriptors differ between the 16 and 32 byte cdb variants of
+WRITE SCATTERED. In the 16 byte cdb variant the 32 byte LBA range descriptor
+is made up of an 8 byte LBA, followed by a 4 byte number_of_blocks followed
+by 20 bytes of zeros. In the 32 byte variant the LBA and number_of_blocks
+are followed by a RT (4 bytes), an AT (2 bytes) and a TM (2 bytes) then
+12 bytes of zeros.
+.TP
+\fB\-T\fR, \fB\-\-stream\fR=\fIID\fR
+selects the WRITE STREAM command with the STR_ID field set to \fIID\fR.
+\fIID\fR can take values from 0 to 0xffff (i.e. it is a 16 bit field).
.TP
\fB\-s\fR, \fB\-\-strict\fR
-when this option is present, if the read of the file named \fIIF\fR yields
-less bytes than requested then this utility will exit at this point
-with an exit status of SG_LIB_FILE_ERROR. The default is to fill the
-remaining part of the buffer with zeros and attempt to write the
-full buffer to the \fIDEVICE\fR.
+when this option is present, more things (e.g. that reserved fields contain
+zeros) and any irregularities will terminate the utility with a message to
+stderr and an indicative exit status. While experimenting with these commands,
+especially WRITE SCATTERED, it is recommended to use this option.
.TP
-\fB\-T\fR, \fB\-\-tag\-mask\fR=\fITM\fR
+\fB\-t\fR, \fB\-\-tag\-mask\fR=\fITM\fR
where \fITM\fR is the logical block application tag mask field in the
WRITE ATOMIC(32) and WRITE(32) cdbs. It is 16 bit field which means the
-maximum value is 0xffff. The default value is zero.
+maximum value is 0xffff. The default value is 0xffff.
.TP
-\fB\-t\fR, \fB\-\-timeout\fR=\fITO\fR
+\fB\-I\fR, \fB\-\-timeout\fR=\fITO\fR
where \fITO\fR is the command timeout value in seconds. The default value is
120 seconds. If \fINUM\fR is large a WRITE ATOMIC command may require
considerably more time than 120 seconds to complete.
.TP
+\fB\-u\fR, \fB\-\-unmap\fR=\fIU_A\fR
+where \fITO\fR is the command timeout value in seconds. The default value is
+.TP
\fB\-v\fR, \fB\-\-verbose\fR
increase the degree of verbosity (debug messages). These messages are usually
written to stderr.
@@ -289,11 +392,49 @@ written to stderr.
output version string then exit.
.TP
\fB\-w\fR, \fB\-\-wrprotect\fR=\fIWPR\fR
-sets the "Write protect" field in the WRITE SAME cdb to \fIWPR\fR. The
-default value is zero. \fIWPR\fR should be a value between 0 and 7.
-When \fIWPR\fR is 1 or greater, and the disk's protection type is 1 or
-greater, then 8 extra bytes of protection information are expected or
-generated (to place in the command's data out buffer).
+sets the WRPROTECT field (3 bits) in all sg_write_x commands apart from
+ORWRITE which has a 3 bit ORPROTECT field. In all cases \fIWPR\fR is placed
+in that 3 bit field. The default value is zero which does not send any PI
+in the data\-out buffer. \fIWPR\fR should be a value between 0 and 7.
+.SH SCATTERED FILE ASCII FORMAT
+This file named with the \fI\-\-scat\-file=SF\fR option only applies to
+the WRITE SCATTERED (16 and 32) command. If the \fI\-\-scat\-raw\fR option
+is also given then the file named \fISF\fR is expected to be binary and
+contain the parameter list header (32 bytes of zeros for both the 16 and 32
+byte variants) followed by "n" LBA range descriptors, each of 32 bytes each.
+This section describes what is expected in \fISF\fR when the
+\fI\-\-scat\-raw\fR option is not given.
+.PP
+The ASCII hexadecimal "scatter file" (named by \fISF\fR) can contain
+comments, empty lines and numbers. If multiple numbers appear on one line
+they can be separated by spaces, tabs or a single comma. Numbers are parsed
+as decimal unless prefixed by "0x" (or "0X") or have a suffix of "h". Ox is
+the prefix of hexadecimal number is the C language while T10 uses the "h"
+suffix for the same purpose. Anything from and including a "#" character
+to the end\-of\-line is ignored, so comments can be placed there.
+.PP
+For the WRITE SCATTERED (16) command, its LBA range descriptors contain two
+items per descriptor: an 8 byte LBA followed by a 4 byte number_of_blocks.
+The remaining 20 bytes of the descriptor are zeros. The format accepted
+is relatively loose with each decoded value being placed in an LBA and
+then a number_of_blocks until the end\-of\-file is reached. The pattern
+starts with a LBA and if it doesn't finish with a number_of_blocks (i.e.
+an odd number of values are parsed) an error occurs. So the number of
+LBA range descriptors generated will be half the number of values parsed
+in \fISF\fR.
+.PP
+For the WRITE SCATTERED (32) command, its LBA range descriptors contain five
+items per descriptor: an 8 byte LBA followed by a 4 byte number_of_blocks,
+then a 4 byte RT, a 2 byte AT, and a 2 byte TM. The last three items are
+associated with protection information (PI). The accepted format in the
+\fISF\fR file is more constrained than the 16 byte cdb variant. The items
+for each LBA range descriptor must be found on one line with adjacent items
+being comma separated. The first two items (LBA and number_of_blocks) must be
+given, and if no more items are on the line then RT, AT and TM are given
+their default values (all "ff" bytes). Spaces and tabs may appear between
+items but commas are the separators. Two commas with no value between them
+will cause the "missing" item to receive its default value.
+xxxxx
.SH NOTES
Various numeric arguments (e.g. \fILBA\fR) may include multiplicative
suffixes or be given in hexadecimal. See the "NUMERIC ARGUMENTS" section
@@ -306,13 +447,13 @@ given (or implied by other options). The bsg driver with device nodes like
/dev/bsg/6:0:0:1 does support cdb sizes greater than 16 bytes since its
introduction in lk 2.6.28 .
.SH EXIT STATUS
-The exit status of sg_write_atomic is 0 when it is successful. Otherwise see
+The exit status of sg_write_x is 0 when it is successful. Otherwise see
the sg3_utils(8) man page.
.SH EXAMPLES
One simple usage is to write 4 blocks of zeros from (and including) a given
LBA:
.PP
- sg_write_atomic \-\-in=/dev/zero \-\-lba=0x1234 \-\-num=4 /dev/sdc
+ sg_write_x \-\-in=/dev/zero \-\-lba=0x1234 \-\-num=4 /dev/sdc
.PP
Since \fI\-\-bs=BS\fR has not been given, then this utility will call the
READ CAPACITY(16) command on /dev/sdc to determine the number of bytes in a
diff --git a/include/Makefile.am b/include/Makefile.am
index ba921855..3dc1ef3b 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -8,12 +8,14 @@ scsiinclude_HEADERS = \
sg_cmds_basic.h \
sg_cmds_extra.h \
sg_cmds_mmc.h \
- sg_pt.h
+ sg_pt.h \
+ sg_pt_nvme.h
if OS_LINUX
scsiinclude_HEADERS += \
sg_linux_inc.h \
- sg_io_linux.h
+ sg_io_linux.h \
+ sg_pt_linux.h
noinst_HEADERS = \
sg_pt_win32.h
diff --git a/include/Makefile.in b/include/Makefile.in
index 08495eb2..6d403862 100644
--- a/include/Makefile.in
+++ b/include/Makefile.in
@@ -90,7 +90,8 @@ build_triplet = @build@
host_triplet = @host@
@OS_LINUX_TRUE@am__append_1 = \
@OS_LINUX_TRUE@ sg_linux_inc.h \
-@OS_LINUX_TRUE@ sg_io_linux.h
+@OS_LINUX_TRUE@ sg_io_linux.h \
+@OS_LINUX_TRUE@ sg_pt_linux.h
@OS_WIN32_MINGW_TRUE@am__append_2 = sg_pt_win32.h
@OS_WIN32_CYGWIN_TRUE@am__append_3 = sg_pt_win32.h
@@ -127,7 +128,8 @@ am__can_run_installinfo = \
am__noinst_HEADERS_DIST = sg_linux_inc.h sg_io_linux.h sg_pt_win32.h
am__scsiinclude_HEADERS_DIST = sg_lib.h sg_lib_data.h sg_cmds.h \
sg_cmds_basic.h sg_cmds_extra.h sg_cmds_mmc.h sg_pt.h \
- sg_linux_inc.h sg_io_linux.h sg_pt_win32.h
+ sg_pt_nvme.h sg_linux_inc.h sg_io_linux.h sg_pt_linux.h \
+ sg_pt_win32.h
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
@@ -300,8 +302,8 @@ top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
scsiincludedir = $(includedir)/scsi
scsiinclude_HEADERS = sg_lib.h sg_lib_data.h sg_cmds.h sg_cmds_basic.h \
- sg_cmds_extra.h sg_cmds_mmc.h sg_pt.h $(am__append_1) \
- $(am__append_2) $(am__append_3)
+ sg_cmds_extra.h sg_cmds_mmc.h sg_pt.h sg_pt_nvme.h \
+ $(am__append_1) $(am__append_2) $(am__append_3)
@OS_FREEBSD_TRUE@noinst_HEADERS = \
@OS_FREEBSD_TRUE@ sg_linux_inc.h \
@OS_FREEBSD_TRUE@ sg_io_linux.h \
diff --git a/include/sg_cmds_basic.h b/include/sg_cmds_basic.h
index 499d8473..507effad 100644
--- a/include/sg_cmds_basic.h
+++ b/include/sg_cmds_basic.h
@@ -42,7 +42,7 @@ int sg_ll_inquiry(int sg_fd, bool cmddt, bool evpd, int pg_op, void * resp,
* an argument to set it has been removed (use the REPORT SUPPORTED OPERATION
* CODES command instead). Adds the ability to set the command abort timeout
* and the ability to report the residual count. If timeout_secs is zero
- * or less the the default command abort timeout (60 seconds) is used.
+ * or less the default command abort timeout (60 seconds) is used.
* If residp is non-NULL then the residual value is written where residp
* points. A residual value of 0 implies mx_resp_len bytes have be written
* where resp points. If the residual value equals mx_resp_len then no
diff --git a/include/sg_cmds_extra.h b/include/sg_cmds_extra.h
index 9869ae9a..32405068 100644
--- a/include/sg_cmds_extra.h
+++ b/include/sg_cmds_extra.h
@@ -167,7 +167,7 @@ int sg_ll_receive_diag(int sg_fd, bool pcv, int pg_code, void * resp,
/* Same as sg_ll_receive_diag() but with added timeout_secs and residp
* arguments. Adds the ability to set the command abort timeout
* and the ability to report the residual count. If timeout_secs is zero
- * or less the the default command abort timeout (60 seconds) is used.
+ * or less the default command abort timeout (60 seconds) is used.
* If residp is non-NULL then the residual value is written where residp
* points. A residual value of 0 implies mx_resp_len bytes have be written
* where resp points. If the residual value equals mx_resp_len then no
@@ -210,7 +210,7 @@ int sg_ll_report_referrals(int sg_fd, uint64_t start_llba, bool one_seg,
int verbose);
/* Invokes a SCSI SEND DIAGNOSTIC command. Foreground, extended self tests can
- * take a long time, if so set long_duration flag in which case the timout
+ * take a long time, if so set long_duration flag in which case the timeout
* is set to 7200 seconds; if the value of long_duration is > 7200 then that
* value is taken as the timeout value in seconds. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Send diagnostic not supported,
diff --git a/include/sg_lib.h b/include/sg_lib.h
index fd64e31e..9c457350 100644
--- a/include/sg_lib.h
+++ b/include/sg_lib.h
@@ -504,6 +504,18 @@ int64_t sg_get_llnum(const char * buf);
* negative numbers and '-1' must be treated separately. */
int64_t sg_get_llnum_nomult(const char * buf);
+/* Returns pointer to heap (or NULL) that is aligned to a align_to byte
+ * boundary. Sends back *buff_to_free pointer in third argument that may be
+ * different from the return value. If it is different then the *buff_to_free
+ * pointer should be freed (rather than the returned value) when the heap is
+ * no longer needed. If align_to is 0 then aligns to OS's page size. Sets all
+ * returned heap to zeros. If num_bytes is 0 then set to page size. */
+uint8_t * sg_memalign(uint32_t num_bytes, uint32_t align_to,
+ uint8_t ** buff_to_free, bool vb);
+
+/* Returns OS page size in bytes. If uncertain returns 4096. */
+uint32_t sg_get_page_size(void);
+
/* <<< Architectural support functions [is there a better place?] >>> */
diff --git a/include/sg_pt.h b/include/sg_pt.h
index cf185ccd..d11bae26 100644
--- a/include/sg_pt.h
+++ b/include/sg_pt.h
@@ -71,7 +71,7 @@ struct sg_pt_base *
/* Forget any previous dev_fd and install the one given. May attempt to
* find file type (e.g. if pass-though) from OS so there could be an error.
- * Returns 0 for success or the the same value as get_scsi_pt_os_err()
+ * Returns 0 for success or the same value as get_scsi_pt_os_err()
* will return. dev_fd should be >= 0 for a valid file handle or -1 . */
int set_pt_file_handle(struct sg_pt_base * objp, int dev_fd, int verbose);
@@ -99,7 +99,7 @@ void set_scsi_pt_data_out(struct sg_pt_base * objp, /* to device */
/* Set a pointer and length to be used for metadata transferred to
* (out_true=true) or from (out_true-false) device */
void set_pt_metadata_xfer(struct sg_pt_base * objp, unsigned char * mdxferp,
- uint32_t mdxfer_len, bool out_true);
+ uint32_t mdxfer_len, bool out_true);
/* The following "set_"s implementations may be dummies */
void set_scsi_pt_packet_id(struct sg_pt_base * objp, int pack_id);
void set_scsi_pt_tag(struct sg_pt_base * objp, uint64_t tag);
@@ -114,7 +114,7 @@ void set_scsi_pt_task_attr(struct sg_pt_base * objp, int attribute,
* are given, use the pass-through default. */
#define SCSI_PT_FLAGS_QUEUE_AT_TAIL 0x10
#define SCSI_PT_FLAGS_QUEUE_AT_HEAD 0x20
-/* Set (potentially OS dependant) flags for pass-through mechanism.
+/* Set (potentially OS dependent) flags for pass-through mechanism.
* Apart from contradictions, flags can be OR-ed together. */
void set_scsi_pt_flags(struct sg_pt_base * objp, int flags);
@@ -185,7 +185,7 @@ uint32_t get_pt_nvme_nsid(const struct sg_pt_base * objp);
/* Should be invoked once per objp after other processing is complete in
* order to clean up resources. For ever successful construct_scsi_pt_obj()
* call there should be one destruct_scsi_pt_obj(). If the
- * construct_scsi_pt_obj_with_fd() function was used to create thsi object
+ * construct_scsi_pt_obj_with_fd() function was used to create this object
* then the dev_fd provided to that constructor is not altered by this
* destructor. So the user should still close dev_fd (perhaps with
* scsi_pt_close_device() ). */
diff --git a/include/sg_pt_linux.h b/include/sg_pt_linux.h
new file mode 100644
index 00000000..4e68f286
--- /dev/null
+++ b/include/sg_pt_linux.h
@@ -0,0 +1,158 @@
+#ifndef SG_PT_LINUX_H
+#define SG_PT_LINUX_H
+
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <linux/types.h>
+
+#include "sg_pt_nvme.h"
+
+/* This header is for internal use by the sg3_utils library (libsgutils)
+ * and is Linux specific. Best not to include it directly in code that
+ * is meant to be OS independent. */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef HAVE_LINUX_BSG_H
+
+#define BSG_PROTOCOL_SCSI 0
+
+#define BSG_SUB_PROTOCOL_SCSI_CMD 0
+#define BSG_SUB_PROTOCOL_SCSI_TMF 1
+#define BSG_SUB_PROTOCOL_SCSI_TRANSPORT 2
+
+/*
+ * For flag constants below:
+ * sg.h sg_io_hdr also has bits defined for it's flags member. These
+ * two flag values (0x10 and 0x20) have the same meaning in sg.h . For
+ * bsg the BSG_FLAG_Q_AT_HEAD flag is ignored since it is the default.
+ */
+#define BSG_FLAG_Q_AT_TAIL 0x10 /* default is Q_AT_HEAD */
+#define BSG_FLAG_Q_AT_HEAD 0x20
+
+struct sg_io_v4 {
+ __s32 guard; /* [i] 'Q' to differentiate from v3 */
+ __u32 protocol; /* [i] 0 -> SCSI , .... */
+ __u32 subprotocol; /* [i] 0 -> SCSI command, 1 -> SCSI task
+ management function, .... */
+
+ __u32 request_len; /* [i] in bytes */
+ __u64 request; /* [i], [*i] {SCSI: cdb} */
+ __u64 request_tag; /* [i] {SCSI: task tag (only if flagged)} */
+ __u32 request_attr; /* [i] {SCSI: task attribute} */
+ __u32 request_priority; /* [i] {SCSI: task priority} */
+ __u32 request_extra; /* [i] {spare, for padding} */
+ __u32 max_response_len; /* [i] in bytes */
+ __u64 response; /* [i], [*o] {SCSI: (auto)sense data} */
+
+ /* "dout_": data out (to device); "din_": data in (from device) */
+ __u32 dout_iovec_count; /* [i] 0 -> "flat" dout transfer else
+ dout_xfer points to array of iovec */
+ __u32 dout_xfer_len; /* [i] bytes to be transferred to device */
+ __u32 din_iovec_count; /* [i] 0 -> "flat" din transfer */
+ __u32 din_xfer_len; /* [i] bytes to be transferred from device */
+ __u64 dout_xferp; /* [i], [*i] */
+ __u64 din_xferp; /* [i], [*o] */
+
+ __u32 timeout; /* [i] units: millisecond */
+ __u32 flags; /* [i] bit mask */
+ __u64 usr_ptr; /* [i->o] unused internally */
+ __u32 spare_in; /* [i] */
+
+ __u32 driver_status; /* [o] 0 -> ok */
+ __u32 transport_status; /* [o] 0 -> ok */
+ __u32 device_status; /* [o] {SCSI: command completion status} */
+ __u32 retry_delay; /* [o] {SCSI: status auxiliary information} */
+ __u32 info; /* [o] additional information */
+ __u32 duration; /* [o] time to complete, in milliseconds */
+ __u32 response_len; /* [o] bytes of response actually written */
+ __s32 din_resid; /* [o] din_xfer_len - actual_din_xfer_len */
+ __s32 dout_resid; /* [o] dout_xfer_len - actual_dout_xfer_len */
+ __u64 generated_tag; /* [o] {SCSI: transport generated task tag} */
+ __u32 spare_out; /* [o] */
+
+ __u32 padding;
+};
+
+#else
+
+#include <linux/bsg.h>
+
+#endif
+
+
+struct sg_pt_linux_scsi {
+ struct sg_io_v4 io_hdr; /* use v4 header as it is more general */
+ int dev_fd; /* -1 if not given (yet) */
+ int in_err;
+ int os_err;
+ unsigned char tmf_request[4];
+ bool is_sg;
+ bool is_bsg;
+ bool is_nvme;
+ bool mdxfer_out; /* direction of metadata xfer, true->data-out */
+ bool scsi_dsense; /* SCSI descriptor sense active when true */
+ uint32_t nvme_nsid; /* 1 to 0xfffffffe are possibly valid, 0
+ * implies dev_fd is not a NVMe device
+ * (is_nvme=false) or it is a NVMe char
+ * device (e.g. /dev/nvme0 ) */
+ uint32_t nvme_result;
+ uint32_t mdxfer_len;
+ void * mdxferp;
+ uint8_t * nvme_id_ctlp; /* cached response to controller IDENTIFY */
+ uint8_t * free_nvme_id_ctlp;
+};
+
+struct sg_pt_base {
+ struct sg_pt_linux_scsi impl;
+};
+
+
+#ifndef sg_nvme_admin_cmd
+#define sg_nvme_admin_cmd sg_nvme_passthru_cmd
+#endif
+
+/* Linux NVMe related ioctls */
+#ifndef NVME_IOCTL_ID
+#define NVME_IOCTL_ID _IO('N', 0x40)
+#endif
+#ifndef NVME_IOCTL_ADMIN_CMD
+#define NVME_IOCTL_ADMIN_CMD _IOWR('N', 0x41, struct sg_nvme_admin_cmd)
+#endif
+#ifndef NVME_IOCTL_SUBMIT_IO
+#define NVME_IOCTL_SUBMIT_IO _IOW('N', 0x42, struct sg_nvme_user_io)
+#endif
+#ifndef NVME_IOCTL_IO_CMD
+#define NVME_IOCTL_IO_CMD _IOWR('N', 0x43, struct sg_nvme_passthru_cmd)
+#endif
+#ifndef NVME_IOCTL_RESET
+#define NVME_IOCTL_RESET _IO('N', 0x44)
+#endif
+#ifndef NVME_IOCTL_SUBSYS_RESET
+#define NVME_IOCTL_SUBSYS_RESET _IO('N', 0x45)
+#endif
+
+extern bool sg_bsg_nvme_char_major_checked;
+extern int sg_bsg_major;
+extern volatile int sg_nvme_char_major;
+extern long sg_lin_page_size;
+
+void sg_find_bsg_nvme_char_major(int verbose);
+int sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* end of SG_PT_LINUX_H */
diff --git a/include/sg_pt_nvme.h b/include/sg_pt_nvme.h
index 89cee29f..05862100 100644
--- a/include/sg_pt_nvme.h
+++ b/include/sg_pt_nvme.h
@@ -47,6 +47,21 @@ __packed;
;
#endif
+/* Using byte offsets and unaligned be/le copies safer than packed
+ * structures. These are for sg_nvme_user_io . */
+#define SG_NVME_IO_OPCODE 0
+#define SG_NVME_IO_FLAGS 1
+#define SG_NVME_IO_CONTROL 2
+#define SG_NVME_IO_NBLOCKS 4
+#define SG_NVME_IO_RSVD 6
+#define SG_NVME_IO_METADATA 8
+#define SG_NVME_IO_ADDR 16
+#define SG_NVME_IO_SLBA 24
+#define SG_NVME_IO_DSMGMT 32
+#define SG_NVME_IO_REFTAG 36
+#define SG_NVME_IO_APPTAG 40
+#define SG_NVME_IO_APPMASK 42
+
#ifdef __GNUC__
#ifndef __clang__
struct __attribute__((__packed__)) sg_nvme_passthru_cmd
@@ -83,6 +98,28 @@ __packed;
;
#endif
+/* Using byte offsets and unaligned be/le copies safer than packed
+ * structures. These are for sg_nvme_passthru_cmd . */
+#define SG_NVME_PT_OPCODE 0
+#define SG_NVME_PT_FLAGS 1
+#define SG_NVME_PT_RSVD1 2
+#define SG_NVME_PT_NSID 4
+#define SG_NVME_PT_CDW2 8
+#define SG_NVME_PT_CDW3 12
+#define SG_NVME_PT_METADATA 16
+#define SG_NVME_PT_ADDR 24
+#define SG_NVME_PT_METADATA_LEN 32
+#define SG_NVME_PT_DATA_LEN 36
+#define SG_NVME_PT_CDW10 40
+#define SG_NVME_PT_CDW11 44
+#define SG_NVME_PT_CDW12 48
+#define SG_NVME_PT_CDW13 52
+#define SG_NVME_PT_CDW14 56
+#define SG_NVME_PT_CDW15 60
+
+#define SG_NVME_PT_TIMEOUT_MS 64
+#define SG_NVME_PT_RESULT 68
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 84202dc5..24d3c959 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -10,7 +10,8 @@ libsgutils2_la_SOURCES = \
if OS_LINUX
libsgutils2_la_SOURCES += \
sg_pt_linux.c \
- sg_io_linux.c
+ sg_io_linux.c \
+ sg_pt_linux_nvme.c
endif
if OS_WIN32_MINGW
diff --git a/lib/Makefile.in b/lib/Makefile.in
index fa695ceb..a06aa388 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -90,7 +90,8 @@ build_triplet = @build@
host_triplet = @host@
@OS_LINUX_TRUE@am__append_1 = \
@OS_LINUX_TRUE@ sg_pt_linux.c \
-@OS_LINUX_TRUE@ sg_io_linux.c
+@OS_LINUX_TRUE@ sg_io_linux.c \
+@OS_LINUX_TRUE@ sg_pt_linux_nvme.c
@OS_WIN32_MINGW_TRUE@am__append_2 = sg_pt_win32.c
@OS_WIN32_CYGWIN_TRUE@am__append_3 = sg_pt_win32.c
@@ -138,9 +139,10 @@ am__installdirs = "$(DESTDIR)$(libdir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
am__libsgutils2_la_SOURCES_DIST = sg_lib.c sg_lib_data.c \
sg_cmds_basic.c sg_cmds_basic2.c sg_cmds_extra.c sg_cmds_mmc.c \
- sg_pt_common.c sg_pt_linux.c sg_io_linux.c sg_pt_win32.c \
- sg_pt_freebsd.c sg_pt_solaris.c sg_pt_osf1.c
-@OS_LINUX_TRUE@am__objects_1 = sg_pt_linux.lo sg_io_linux.lo
+ sg_pt_common.c sg_pt_linux.c sg_io_linux.c sg_pt_linux_nvme.c \
+ sg_pt_win32.c sg_pt_freebsd.c sg_pt_solaris.c sg_pt_osf1.c
+@OS_LINUX_TRUE@am__objects_1 = sg_pt_linux.lo sg_io_linux.lo \
+@OS_LINUX_TRUE@ sg_pt_linux_nvme.lo
@OS_WIN32_MINGW_TRUE@am__objects_2 = sg_pt_win32.lo
@OS_WIN32_CYGWIN_TRUE@am__objects_3 = sg_pt_win32.lo
@OS_FREEBSD_TRUE@am__objects_4 = sg_pt_freebsd.lo
@@ -448,6 +450,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_common.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_freebsd.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_linux.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_linux_nvme.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_osf1.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_solaris.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_win32.Plo@am__quote@
diff --git a/lib/sg_lib.c b/lib/sg_lib.c
index d6c011a8..d4549b75 100644
--- a/lib/sg_lib.c
+++ b/lib/sg_lib.c
@@ -3025,6 +3025,91 @@ sg_ata_get_chars(const uint16_t * word_arr, int start_word,
return op - ochars;
}
+int
+pr2serr(const char * fmt, ...)
+{
+ va_list args;
+ int n;
+
+ va_start(args, fmt);
+ n = vfprintf(stderr, fmt, args);
+ va_end(args);
+ return n;
+}
+
+uint32_t
+sg_get_page_size(void)
+{
+#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
+ return sysconf(_SC_PAGESIZE); /* POSIX.1 (was getpagesize()) */
+#elif defined(SG_LIB_WIN32)
+ return win_pagesize();
+#elif defined(SG_LIB_FREEBSD)
+#include <sys/param.h>
+ return PAGE_SIZE;
+#else
+ return 4096; /* give up, pick likely figure */
+#endif
+}
+
+/* Returns pointer to heap (or NULL) that is aligned to a align_to byte
+ * boundary. Sends back *buff_to_free pointer in third argument that may be
+ * different from the return value. If it is different then the *buff_to_free
+ * pointer should be freed (rather than the returned value) when the heap is
+ * no longer needed. If align_to is 0 then aligns to OS's page size. Sets all
+ * returned heap to zeros. If num_bytes is 0 then set to page size. */
+uint8_t *
+sg_memalign(uint32_t num_bytes, uint32_t align_to, uint8_t ** buff_to_free,
+ bool vb)
+{
+ size_t psz;
+ uint8_t * res;
+
+ psz = (align_to > 0) ? align_to : sg_get_page_size();
+ if (0 == num_bytes)
+ num_bytes = psz; /* ugly to handle otherwise */
+
+#ifdef HAVE_POSIX_MEMALIGN
+ {
+ int err;
+ void * wp = NULL;
+
+ err = posix_memalign(&wp, psz, num_bytes);
+ if (err || (NULL == wp)) {
+ pr2ws("%s: posix_memalign: error [%d], out of memory?\n",
+ __func__, err);
+ return NULL;
+ }
+ memset(wp, 0, num_bytes);
+ if (buff_to_free)
+ *buff_to_free = (uint8_t *)wp;
+ res = (uint8_t *)wp;
+ if (vb)
+ pr2ws("%s: posix, len=%d, wrkBuffp=%p, psz=%d, rp=%p\n",
+ __func__, num_bytes, (void *)*buff_to_free, (int)psz,
+ (void *)res);
+ return res;
+ }
+#else
+ {
+ uint8_t * wrkBuff;
+
+ wrkBuff = (uint8_t)calloc(length + psz, 1);
+ if (NULL == wrkBuff) {
+ if (buff_to_free)
+ *buff_to_free = NULL;
+ return NULL;
+ } else if (buff_to_free)
+ *buff_to_free = wrkBuff;
+ res = (uint8_t *)(((uintptr_t)wrkBuff + psz - 1) & (~(psz - 1)));
+ if (vb)
+ pr2ws("%s: hack, len=%d, wrkBuffp=%p, psz=%d, rp=%p\n", __func__,
+ length, (void *)*wrkBuffp, (int)psz, (void *)res);
+ return res;
+ }
+#endif
+}
+
const char *
sg_lib_version()
{
@@ -3069,15 +3154,3 @@ sg_set_binary_mode(int fd)
}
#endif
-
-int
-pr2serr(const char * fmt, ...)
-{
- va_list args;
- int n;
-
- va_start(args, fmt);
- n = vfprintf(stderr, fmt, args);
- va_end(args);
- return n;
-}
diff --git a/lib/sg_lib_data.c b/lib/sg_lib_data.c
index 535d3a00..ce2b9f07 100644
--- a/lib/sg_lib_data.c
+++ b/lib/sg_lib_data.c
@@ -17,7 +17,7 @@
#endif
-const char * sg_lib_version_str = "2.32 20171127";/* spc5r17, sbc4r15 */
+const char * sg_lib_version_str = "2.33 20171205";/* spc5r17, sbc4r15 */
/* indexed by pdt; those that map to own index do not decay */
@@ -432,8 +432,8 @@ struct sg_lib_value_name_t sg_lib_rec_copy_sa_arr[] = {
struct sg_lib_value_name_t sg_lib_variable_length_arr[] = {
{0x1, 0, "Rebuild(32)"},
{0x2, 0, "Regenerate(32)"},
- {0x3, 0, "Xdread(32)"}, /* obsolete in SBC-3 r31 */
- {0x4, 0, "Xdwrite(32)"}, /* obsolete in SBC-3 r31 */
+ {0x3, 0, "Xdread(32)"}, /* obsolete in SBC-3 r31 */
+ {0x4, 0, "Xdwrite(32)"}, /* obsolete in SBC-3 r31 */
{0x5, 0, "Xdwrite extended(32)"}, /* obsolete in SBC-4 r15 */
{0x6, 0, "Xpwrite(32)"}, /* obsolete in SBC-4 r15 */
{0x7, 0, "Xdwriteread(32)"}, /* obsolete in SBC-4 r15 */
diff --git a/lib/sg_pt_linux.c b/lib/sg_pt_linux.c
index 7433b860..4ddf8990 100644
--- a/lib/sg_pt_linux.c
+++ b/lib/sg_pt_linux.c
@@ -5,7 +5,7 @@
* license that can be found in the BSD_LICENSE file.
*/
-/* sg_pt_linux version 1.30 20171113 */
+/* sg_pt_linux version 1.31 20171203 */
#include <stdio.h>
@@ -34,88 +34,28 @@
#include "sg_pt.h"
#include "sg_lib.h"
#include "sg_linux_inc.h"
-#include "sg_pt_nvme.h"
+#include "sg_pt_linux.h"
#if (__STDC_VERSION__ >= 199901L) /* C99 or later */
-typedef intptr_t sg_intptr_t;
+typedef uintptr_t sg_uintptr_t;
#else
-typedef long sg_intptr_t;
+typedef unsigned long sg_uintptr_t;
#endif
+/* Checking CDB for whether it is a SCSI or NVME command: all NVME coomands
+ * are 64 bytes long. There are very few standardized SCSI commands with
+ * a cdb of 64 bytes. If one does then byte 0 (in the CDB) must contain
+ * 7Fh and at byte offset 7 the value must be 56 (Additional CDB length)a */
+#if 0
// xxxxxxxxxxxxxxxx testing <<<<<<<<<<<<<<<<<<<<<<<<
-// #undef HAVE_LINUX_NVME_IOCTL_H
+#undef HAVE_LINUX_NVME_IOCTL_H
#ifdef HAVE_LINUX_NVME_IOCTL_H
#include <linux/nvme_ioctl.h>
#else
-
-/*
- * Definitions for the NVM Express ioctl interface
- * Copyright (c) 2011-2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- */
-
-#if 0
-#include <linux/types.h>
-
-struct nvme_user_io {
- __u8 opcode;
- __u8 flags;
- __u16 control;
- __u16 nblocks;
- __u16 rsvd;
- __u64 metadata;
- __u64 addr;
- __u64 slba;
- __u32 dsmgmt;
- __u32 reftag;
- __u16 apptag;
- __u16 appmask;
-};
-
-struct nvme_passthru_cmd {
- __u8 opcode;
- __u8 flags;
- __u16 rsvd1;
- __u32 nsid;
- __u32 cdw2;
- __u32 cdw3;
- __u64 metadata;
- __u64 addr;
- __u32 metadata_len;
- __u32 data_len;
- __u32 cdw10;
- __u32 cdw11;
- __u32 cdw12;
- __u32 cdw13;
- __u32 cdw14;
- __u32 cdw15;
- __u32 timeout_ms;
- __u32 result;
-};
+#include "sg_pt_linux.h"
+#endif
#endif
-
-#define nvme_admin_cmd nvme_passthru_cmd
-
-#define NVME_IOCTL_ID _IO('N', 0x40)
-#define NVME_IOCTL_ADMIN_CMD _IOWR('N', 0x41, struct nvme_admin_cmd)
-#define NVME_IOCTL_SUBMIT_IO _IOW('N', 0x42, struct nvme_user_io)
-#define NVME_IOCTL_IO_CMD _IOWR('N', 0x43, struct nvme_passthru_cmd)
-#define NVME_IOCTL_RESET _IO('N', 0x44)
-#define NVME_IOCTL_SUBSYS_RESET _IO('N', 0x45)
-
-#endif /* end of HAVE_LINUX_NVME_IOCTL_H */
-
-#include <linux/types.h>
-#include <linux/bsg.h>
#ifdef major
#define SG_DEV_MAJOR major
@@ -188,9 +128,11 @@ static const char * linux_driver_suggests[] = {
#define SG_LIB_SUGGEST_MASK SUGGEST_MASK
#define SG_LIB_DRIVER_SENSE DRIVER_SENSE
-static bool bsg_nvme_char_major_checked = false;
-static int bsg_major = 0;
-static volatile int nvme_char_major = 0;
+bool sg_bsg_nvme_char_major_checked = false;
+int sg_bsg_major = 0;
+volatile int sg_nvme_char_major = 0;
+
+long sg_lin_page_size = 4096; /* default, overridden with correct value */
#if defined(__GNUC__) || defined(__clang__)
@@ -216,8 +158,8 @@ pr2ws(const char * fmt, ...)
/* This function only needs to be called once (unless a NVMe controller
* can be hot-plugged into system in which case it should be called
* (again) after that event). */
-static void
-find_bsg_nvme_char_major(int verbose)
+void
+sg_find_bsg_nvme_char_major(int verbose)
{
bool got_one = false;
int n;
@@ -227,6 +169,7 @@ find_bsg_nvme_char_major(int verbose)
char a[128];
char b[128];
+ sg_lin_page_size = sysconf(_SC_PAGESIZE);
if (NULL == (fp = fopen(proc_devices, "r"))) {
if (verbose)
pr2ws("fopen %s failed: %s\n", proc_devices, strerror(errno));
@@ -240,12 +183,12 @@ find_bsg_nvme_char_major(int verbose)
while (cp && (cp = fgets(b, sizeof(b), fp))) {
if (2 == sscanf(b, "%d %126s", &n, a)) {
if (0 == strcmp("bsg", a)) {
- bsg_major = n;
+ sg_bsg_major = n;
if (got_one)
break;
got_one = true;
} else if (0 == strcmp("nvme", a)) {
- nvme_char_major = n;
+ sg_nvme_char_major = n;
if (got_one)
break;
got_one = true;
@@ -255,17 +198,17 @@ find_bsg_nvme_char_major(int verbose)
}
if (verbose > 3) {
if (cp) {
- if (bsg_major > 0)
- pr2ws("found bsg_major=%d\n", bsg_major);
- if (nvme_char_major > 0)
- pr2ws("found nvme_char_major=%d\n", nvme_char_major);
+ if (sg_bsg_major > 0)
+ pr2ws("found sg_bsg_major=%d\n", sg_bsg_major);
+ if (sg_nvme_char_major > 0)
+ pr2ws("found sg_nvme_char_major=%d\n", sg_nvme_char_major);
} else
pr2ws("found no bsg not nvme char device in %s\n", proc_devices);
}
fclose(fp);
}
-/* Assumes that find_bsg_nvme_char_major() has already been called. Returns
+/* Assumes that sg_find_bsg_nvme_char_major() has already been called. Returns
* true if dev_fd is a scsi generic pass-through device. If yields
* *is_nvme_p = true with *nsid_p = 0 then dev_fd is a NVMe char device.
* If yields *nsid_p > 0 then dev_fd is a NVMe block device. */
@@ -294,9 +237,9 @@ check_file_type(int dev_fd, struct stat * dev_statp, bool * is_bsg_p,
if (S_ISCHR(dev_statp->st_mode)) {
if (SCSI_GENERIC_MAJOR == major_num)
is_sg = true;
- else if (bsg_major == major_num)
+ else if (sg_bsg_major == major_num)
is_bsg = true;
- else if (nvme_char_major == major_num)
+ else if (sg_nvme_char_major == major_num)
is_nvme = true;
} else if (S_ISBLK(dev_statp->st_mode)) {
is_block = true;
@@ -361,9 +304,9 @@ check_pt_file_handle(int dev_fd, const char * device_name, int verbose)
pr2ws("%s: dev_fd=%d, device_name: %s\n", __func__, dev_fd,
device_name);
/* Linux doesn't need device_name to determine which pass-through */
- if (! bsg_nvme_char_major_checked) {
- bsg_nvme_char_major_checked = true;
- find_bsg_nvme_char_major(verbose);
+ if (! sg_bsg_nvme_char_major_checked) {
+ sg_bsg_nvme_char_major_checked = true;
+ sg_find_bsg_nvme_char_major(verbose);
}
if (dev_fd >= 0) {
bool is_sg, is_bsg, is_nvme;
@@ -389,614 +332,11 @@ check_pt_file_handle(int dev_fd, const char * device_name, int verbose)
return 0;
}
-// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-#if defined(IGNORE_LINUX_BSG) || ! defined(HAVE_LINUX_BSG_H)
/*
- * sg(v3) via SG_IO ioctl on a sg node or other node that accepts that ioctl.
- * Decision has been made at compile time because either:
- * a) no /usr/include/linux/bsg.h header file was found, or
- * b) the builder gave the '--enable-no-linux-bsg' option to ./configure
- */
-
-
-struct sg_pt_linux_scsi {
- struct sg_io_hdr io_hdr;
- int dev_fd; /* -1 if not given */
- int in_err;
- int os_err;
- bool is_sg; /* is_sg,is_nvme: (F,F)-->unknown; (T,F)-->sg; */
- bool is_nvme; /* (F,T)-->nvme; (T,T)-->illegal */
- bool mdxfer_out; /* direction of metadata xfer, true->data-out */
- uint32_t nvme_nsid;
- uint32_t nvme_result;
- uint32_t dxfer_ilen;
- uint32_t dxfer_olen;
- uint32_t mdxfer_len;
- void * dxferip;
- void * dxferop;
- void * mdxferp;
-};
-
-struct sg_pt_base {
- struct sg_pt_linux_scsi impl;
-};
-
-
-
-/* Returns >= 0 if successful. If error in Unix returns negated errno. */
-int
-scsi_pt_open_device(const char * device_name, bool read_only, int verbose)
-{
- int oflags = O_NONBLOCK;
-
- oflags |= (read_only ? O_RDONLY : O_RDWR);
- return scsi_pt_open_flags(device_name, oflags, verbose);
-}
-
-/* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed */
-/* together. The 'flags' argument is advisory and may be ignored. */
-/* Returns >= 0 if successful, otherwise returns negated errno. */
-int
-scsi_pt_open_flags(const char * device_name, int flags, int verbose)
-{
- int fd;
-
- if (verbose > 1) {
- pr2ws("open %s with flags=0x%x\n", device_name, flags);
- }
- if (! bsg_nvme_char_major_checked) {
- bsg_nvme_char_major_checked = true;
- find_bsg_nvme_char_major(verbose);
- }
- fd = open(device_name, flags);
- if (fd < 0)
- fd = -errno;
- return fd;
-}
-
-/* Returns 0 if successful. If error in Unix returns negated errno. */
-int
-scsi_pt_close_device(int device_fd)
-{
- int res;
-
- res = close(device_fd);
- if (res < 0)
- res = -errno;
- return res;
-}
-
-/* Caller should additionally call get_scsi_pt_os_err() after this call */
-struct sg_pt_base *
-construct_scsi_pt_obj_with_fd(int dev_fd, int verbose)
-{
- int err;
- struct sg_pt_linux_scsi * ptp;
-
- /* The following 2 lines are temporary. It is to avoid a NULL pointer
- * crash when an old utility is used with a newer library built after
- * the sg_warnings_strm cleanup */
- if (NULL == sg_warnings_strm)
- sg_warnings_strm = stderr;
-
- ptp = (struct sg_pt_linux_scsi *)
- calloc(1, sizeof(struct sg_pt_linux_scsi));
- if (ptp) {
- err = set_pt_file_handle((struct sg_pt_base *)ptp, dev_fd, verbose);
- if ((0 == err) && (! ptp->is_nvme)) {
- ptp->io_hdr.interface_id = 'S';
- ptp->io_hdr.dxfer_direction = SG_DXFER_NONE;
- }
- } else if (verbose)
- pr2ws("%s: calloc() failed, out of memory?\n", __func__);
-
- return (struct sg_pt_base *)ptp;
-}
-
-struct sg_pt_base *
-construct_scsi_pt_obj()
-{
- return construct_scsi_pt_obj_with_fd(-1 /* dev_fd */, 0 /* verbose */);
-}
-
-void
-destruct_scsi_pt_obj(struct sg_pt_base * vp)
-{
- struct sg_pt_linux_scsi * ptp = &vp->impl;
-
- if (ptp)
- free(ptp);
-}
-
-/* Remembers previous device file descriptor */
-void
-clear_scsi_pt_obj(struct sg_pt_base * vp)
-{
- bool is_sg, is_nvme;
- int fd, nvme_nsid;
- struct sg_pt_linux_scsi * ptp = &vp->impl;
-
- if (ptp) {
- fd = ptp->dev_fd;
- is_sg = ptp->is_sg;
- is_nvme = ptp->is_nvme;
- nvme_nsid = ptp->nvme_nsid;
- memset(ptp, 0, sizeof(struct sg_pt_linux_scsi));
- ptp->io_hdr.interface_id = 'S';
- ptp->io_hdr.dxfer_direction = SG_DXFER_NONE;
- ptp->dev_fd = fd;
- ptp->is_sg = is_sg;
- ptp->is_nvme = is_nvme;
- ptp->nvme_nsid = nvme_nsid;
- }
-}
-
-/* Forget any previous dev_fd and install the one given. May attempt to
- * find file type (e.g. if pass-though) from OS so there could be an error.
- * Returns 0 for success or the the same value as get_scsi_pt_os_err()
- * will return. dev_fd should be >= 0 for a valid file handle or -1 . */
-int
-set_pt_file_handle(struct sg_pt_base * vp, int dev_fd, int verbose)
-{
- struct sg_pt_linux_scsi * ptp = &vp->impl;
- struct stat a_stat;
-
- if (! bsg_nvme_char_major_checked) {
- bsg_nvme_char_major_checked = true;
- find_bsg_nvme_char_major(verbose);
- }
- ptp->dev_fd = dev_fd;
- if (dev_fd >= 0)
- ptp->is_sg = check_file_type(dev_fd, &a_stat, NULL, &ptp->is_nvme,
- &ptp->nvme_nsid, &ptp->os_err, verbose);
- else {
- ptp->is_sg = false;
- ptp->is_nvme = false;
- ptp->nvme_nsid = 0;
- ptp->os_err = 0;
- }
- return ptp->os_err;
-}
-
-/* Valid file handles (which is the return value) are >= 0 . Returns -1
- * if there is no valid file handle. */
-int
-get_pt_file_handle(const struct sg_pt_base * vp)
-{
- const struct sg_pt_linux_scsi * ptp = &vp->impl;
-
- return ptp->dev_fd;
-}
-
-void
-set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb,
- int cdb_len)
-{
- struct sg_pt_linux_scsi * ptp = &vp->impl;
-
- if (ptp->io_hdr.cmdp)
- ++ptp->in_err;
- ptp->io_hdr.cmdp = (unsigned char *)cdb;
- ptp->io_hdr.cmd_len = cdb_len;
-}
-
-void
-set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense,
- int max_sense_len)
-{
- struct sg_pt_linux_scsi * ptp = &vp->impl;
-
- if (ptp->io_hdr.sbp)
- ++ptp->in_err;
- memset(sense, 0, max_sense_len);
- ptp->io_hdr.sbp = sense;
- ptp->io_hdr.mx_sb_len = max_sense_len;
-}
-
-/* Setup for data transfer from device */
-void
-set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp,
- int dxfer_ilen)
-{
- struct sg_pt_linux_scsi * ptp = &vp->impl;
-
- if (ptp->dxferip)
- ++ptp->in_err;
- if (dxfer_ilen > 0) {
- ptp->io_hdr.dxferp = dxferp;
- ptp->dxferip = dxferp;
- ptp->io_hdr.dxfer_len = dxfer_ilen;
- ptp->dxfer_ilen = dxfer_ilen;
- ptp->io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
- }
-}
-
-/* Setup for data transfer toward device */
-void
-set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp,
- int dxfer_olen)
-{
- struct sg_pt_linux_scsi * ptp = &vp->impl;
-
- if (ptp->dxferop)
- ++ptp->in_err;
- if (dxfer_olen > 0) {
- ptp->io_hdr.dxferp = (unsigned char *)dxferp;
- ptp->dxferop = (void *)dxferp;
- ptp->io_hdr.dxfer_len = dxfer_olen;
- ptp->dxfer_olen = dxfer_olen;
- ptp->io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
- }
-}
-
-void
-set_pt_metadata_xfer(struct sg_pt_base * vp, unsigned char * dxferp,
- uint32_t dxfer_len, bool out_true)
-{
- struct sg_pt_linux_scsi * ptp = &vp->impl;
-
- if (ptp->mdxferp)
- ++ptp->in_err;
- if (dxfer_len > 0) {
- ptp->mdxferp = dxferp;
- ptp->mdxfer_len = dxfer_len;
- ptp->mdxfer_out = out_true;
- }
-}
-
-void
-set_scsi_pt_packet_id(struct sg_pt_base * vp, int pack_id)
-{
- struct sg_pt_linux_scsi * ptp = &vp->impl;
-
- ptp->io_hdr.pack_id = pack_id;
-}
-
-void
-set_scsi_pt_tag(struct sg_pt_base * vp, uint64_t tag)
-{
- struct sg_pt_linux_scsi * ptp = &vp->impl;
-
- ++ptp->in_err;
- if (tag) { ; } /* unused, suppress warning */
-}
-
-/* Note that task management function codes are transport specific */
-void
-set_scsi_pt_task_management(struct sg_pt_base * vp, int tmf_code)
-{
- struct sg_pt_linux_scsi * ptp = &vp->impl;
-
- ++ptp->in_err;
- if (tmf_code) { ; } /* unused, suppress warning */
-}
-
-void
-set_scsi_pt_task_attr(struct sg_pt_base * vp, int attribute, int priority)
-{
- struct sg_pt_linux_scsi * ptp = &vp->impl;
-
- ++ptp->in_err;
- if (attribute) { ; } /* unused, suppress warning */
- if (priority) { ; } /* unused, suppress warning */
-}
-
-#ifndef SG_FLAG_Q_AT_TAIL
-#define SG_FLAG_Q_AT_TAIL 0x10
-#endif
-#ifndef SG_FLAG_Q_AT_HEAD
-#define SG_FLAG_Q_AT_HEAD 0x20
-#endif
-
-void
-set_scsi_pt_flags(struct sg_pt_base * vp, int flags)
-{
- struct sg_pt_linux_scsi * ptp = &vp->impl;
-
- /* default action of sg driver [sg v3 interface] is QUEUE_AT_HEAD */
- /* default action of block layer SG_IO ioctl is QUEUE_AT_TAIL */
- if (SCSI_PT_FLAGS_QUEUE_AT_HEAD & flags) { /* favour AT_HEAD */
- ptp->io_hdr.flags |= SG_FLAG_Q_AT_HEAD;
- ptp->io_hdr.flags &= ~SG_FLAG_Q_AT_TAIL;
- } else if (SCSI_PT_FLAGS_QUEUE_AT_TAIL & flags) {
- ptp->io_hdr.flags |= SG_FLAG_Q_AT_TAIL;
- ptp->io_hdr.flags &= ~SG_FLAG_Q_AT_HEAD;
- }
-}
-
-/* Executes NVMe Admin command (or at least forwards it to lower layers).
- * Returns 0 for success, negative numbers are negated 'errno' values from
- * OS system calls. Positive return values are errors from this package.
- * When time_secs is 0 the Linux NVMe Admin command default of 60 seconds
- * is used. */
-int
-do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
-{
- int n, len;
- struct sg_pt_linux_scsi * ptp = &vp->impl;
- struct sg_nvme_passthru_cmd cmd;
-
- if (vb > 3)
- pr2ws("%s: fd=%d, time_secs=%d\n", __func__, fd, time_secs);
- if (! ptp->io_hdr.cmdp) {
- if (vb)
- pr2ws("No NVMe command given (set_scsi_pt_cdb())\n");
- return SCSI_PT_DO_BAD_PARAMS;
- }
- n = ptp->io_hdr.cmd_len;
- len = (int)sizeof(cmd);
- n = (n < len) ? n : len;
- if (n < 8) {
- if (vb)
- pr2ws("%s: command length of %d bytes is too short\n", __func__,
- n);
- return SCSI_PT_DO_BAD_PARAMS;
- }
- memcpy(&cmd, (unsigned char *)ptp->io_hdr.cmdp, n);
- if (n < len) /* zero out rest of 'cmd' */
- memset((unsigned char *)&cmd + n, 0, len - n);
- if (ptp->io_hdr.dxfer_len > 0) {
- cmd.data_len = ptp->io_hdr.dxfer_len;
- cmd.addr = (__u64)(sg_intptr_t)ptp->io_hdr.dxferp;
- }
- if (time_secs < 0)
- cmd.timeout_ms = 0;
- else
- cmd.timeout_ms = 1000 * cmd.timeout_ms;
- if (vb > 2) {
- pr2ws("NVMe command:\n");
- dStrHex((const char *)&cmd, len, 1);
- }
- if (ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd) < 0) {
- ptp->os_err = errno;
- if (vb > 2)
- pr2ws("%s: ioctl(NVME_IOCTL_ADMIN_CMD) failed: %s (errno=%d)\n",
- __func__, strerror(ptp->os_err), ptp->os_err);
- return -ptp->os_err;
- } else
- ptp->os_err = 0;
- n = ptp->io_hdr.mx_sb_len;
- if ((n > 0) && ptp->io_hdr.sbp) {
- n = (n < len) ? n : len;
- memcpy(ptp->io_hdr.sbp, &cmd, n);
- ptp->io_hdr.sb_len_wr = n;
- } else
- ptp->io_hdr.sb_len_wr = 0;
- ptp->nvme_result = cmd.result;
- if (vb > 2)
- pr2ws("%s: timeout_ms=%u, result=%u\n", __func__, cmd.timeout_ms,
- ptp->nvme_result);
- return 0;
-}
-
-/* Executes SCSI command (or at least forwards it to lower layers).
- * Returns 0 for success, negative numbers are negated 'errno' values from
- * OS system calls. Positive return values are errors from this package. */
-int
-do_scsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose)
-{
- int err;
- struct sg_pt_linux_scsi * ptp = &vp->impl;
- bool have_checked_for_type = (ptp->dev_fd >= 0);
-
- ptp->os_err = 0;
- if (ptp->in_err) {
- if (verbose)
- pr2ws("Replicated or unused set_scsi_pt... functions\n");
- return SCSI_PT_DO_BAD_PARAMS;
- }
- if (fd >= 0) {
- if ((ptp->dev_fd >= 0) && (fd != ptp->dev_fd)) {
- if (verbose)
- pr2ws("%s: file descriptor given to create() and here "
- "differ\n", __func__);
- return SCSI_PT_DO_BAD_PARAMS;
- }
- ptp->dev_fd = fd;
- } else if (ptp->dev_fd < 0) {
- if (verbose)
- pr2ws("%s: invalid file descriptors\n", __func__);
- return SCSI_PT_DO_BAD_PARAMS;
- }
- if (! have_checked_for_type) {
- err = set_pt_file_handle(vp, ptp->dev_fd, verbose);
- if (err)
- return -ptp->os_err;
- }
- if (ptp->is_nvme)
- return do_nvme_pt(vp, ptp->dev_fd, time_secs, verbose);
- if (NULL == ptp->io_hdr.cmdp) {
- if (verbose)
- pr2ws("No SCSI command (cdb) given\n");
- return SCSI_PT_DO_BAD_PARAMS;
- }
- /* io_hdr.timeout is in milliseconds */
- ptp->io_hdr.timeout = ((time_secs > 0) ? (time_secs * 1000) :
- DEF_TIMEOUT);
- if (ptp->io_hdr.sbp && (ptp->io_hdr.mx_sb_len > 0))
- memset(ptp->io_hdr.sbp, 0, ptp->io_hdr.mx_sb_len);
- if (ioctl(ptp->dev_fd, SG_IO, &ptp->io_hdr) < 0) {
- ptp->os_err = errno;
- if (verbose > 1)
- pr2ws("ioctl(SG_IO) failed: %s (errno=%d)\n",
- strerror(ptp->os_err), ptp->os_err);
- return -ptp->os_err;
- }
- return 0;
-}
-
-int
-get_scsi_pt_result_category(const struct sg_pt_base * vp)
-{
- const struct sg_pt_linux_scsi * ptp = &vp->impl;
- int dr_st = ptp->io_hdr.driver_status & SG_LIB_DRIVER_MASK;
- int scsi_st = ptp->io_hdr.status & 0x7e;
-
- if (ptp->os_err)
- return SCSI_PT_RESULT_OS_ERR;
- else if (ptp->io_hdr.host_status)
- return SCSI_PT_RESULT_TRANSPORT_ERR;
- else if (dr_st && (SG_LIB_DRIVER_SENSE != dr_st))
- return SCSI_PT_RESULT_TRANSPORT_ERR;
- else if ((SG_LIB_DRIVER_SENSE == dr_st) ||
- (SAM_STAT_CHECK_CONDITION == scsi_st) ||
- (SAM_STAT_COMMAND_TERMINATED == scsi_st))
- return SCSI_PT_RESULT_SENSE;
- else if (scsi_st)
- return SCSI_PT_RESULT_STATUS;
- else
- return SCSI_PT_RESULT_GOOD;
-}
-
-int
-get_scsi_pt_resid(const struct sg_pt_base * vp)
-{
- const struct sg_pt_linux_scsi * ptp = &vp->impl;
-
- return ((NULL == ptp) || ptp->is_nvme) ? 0 : ptp->io_hdr.resid;
-}
-
-int
-get_scsi_pt_status_response(const struct sg_pt_base * vp)
-{
- const struct sg_pt_linux_scsi * ptp = &vp->impl;
-
- if (NULL == ptp)
- return 0;
- return ptp->is_nvme ? (int)ptp->nvme_result : ptp->io_hdr.status;
-}
-
-uint32_t
-get_pt_result(const struct sg_pt_base * vp)
-{
- const struct sg_pt_linux_scsi * ptp = &vp->impl;
-
- if (NULL == ptp)
- return 0;
- return ptp->is_nvme ? ptp->nvme_result : (uint32_t)ptp->io_hdr.status;
-}
-
-int
-get_scsi_pt_sense_len(const struct sg_pt_base * vp)
-{
- const struct sg_pt_linux_scsi * ptp = &vp->impl;
-
- if (NULL == ptp)
- return 0;
- return ptp->io_hdr.sb_len_wr; /* NVMe stuffs that variable */
-}
-
-int
-get_scsi_pt_duration_ms(const struct sg_pt_base * vp)
-{
- const struct sg_pt_linux_scsi * ptp = &vp->impl;
-
- return ptp->io_hdr.duration;
-}
-
-int
-get_scsi_pt_transport_err(const struct sg_pt_base * vp)
-{
- const struct sg_pt_linux_scsi * ptp = &vp->impl;
-
- return (ptp->io_hdr.host_status << 8) + ptp->io_hdr.driver_status;
-}
-
-int
-get_scsi_pt_os_err(const struct sg_pt_base * vp)
-{
- const struct sg_pt_linux_scsi * ptp = &vp->impl;
-
- return ptp->os_err;
-}
-
-bool
-pt_device_is_nvme(const struct sg_pt_base * vp)
-{
- const struct sg_pt_linux_scsi * ptp = &vp->impl;
-
- return ptp->is_nvme;
-}
-
-/* If a NVMe block device (which includes the NSID) handle is associated
- * with 'vp', then its NSID is returned (values range from 0x1 to
- * 0xffffffe). Otherwise 0 is returned. */
-uint32_t
-get_pt_nvme_nsid(const struct sg_pt_base * vp)
-{
- const struct sg_pt_linux_scsi * ptp = &vp->impl;
-
- return ptp->nvme_nsid;
-}
-
-/* Returns b which will contain a null char terminated string (if
- * max_b_len > 0). That string should decode Linux driver and host
- * status values. */
-char *
-get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len,
- char * b)
-{
- const struct sg_pt_linux_scsi * ptp = &vp->impl;
- int ds = ptp->io_hdr.driver_status;
- int hs = ptp->io_hdr.host_status;
- int n, m;
- char * cp = b;
- int driv;
- const char * driv_cp = "unknown";
-
- if (max_b_len < 1)
- return b;
- m = max_b_len;
- n = 0;
- if (hs) {
- if ((hs < 0) || (hs >= LINUX_HOST_BYTES_SZ))
- n = snprintf(cp, m, "Host_status=0x%02x is unknown\n", hs);
- else
- n = snprintf(cp, m, "Host_status=0x%02x [%s]\n", hs,
- linux_host_bytes[hs]);
- }
- m -= n;
- if (m < 1) {
- b[max_b_len - 1] = '\0';
- return b;
- }
- cp += n;
- driv = ds & SG_LIB_DRIVER_MASK;
- if (driv < LINUX_DRIVER_BYTES_SZ)
- driv_cp = linux_driver_bytes[driv];
-#if 0
- sugg = (ds & SG_LIB_SUGGEST_MASK) >> 4;
- if (sugg < LINUX_DRIVER_SUGGESTS_SZ)
- sugg_cp = linux_driver_suggests[sugg];
-#endif
- n = snprintf(cp, m, "Driver_status=0x%02x [%s]\n", ds, driv_cp);
- m -= n;
- if (m < 1)
- b[max_b_len - 1] = '\0';
- return b;
-}
-
-char *
-get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b)
-{
- const struct sg_pt_linux_scsi * ptp = &vp->impl;
- const char * cp;
-
- cp = safe_strerror(ptp->os_err);
- strncpy(b, cp, max_b_len);
- if ((int)strlen(cp) >= max_b_len)
- b[max_b_len - 1] = '\0';
- return b;
-}
-
-
-// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-#else /* allow for runtime selection of sg v3 or v4 (via bsg) */
-// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-/*
- * So bsg is an option. Thus we make a runtime decision. If all the following
- * are true we use sg v4 which is only currently supported on bsg device
- * nodes:
+ * We make a runtime decision whether to use the sg v3 interface or the sg
+ * v4 interface (currently exclusively used by the bsg driver). If all the
+ * following are true we use sg v4 which is only currently supported on bsg
+ * device nodes:
* a) there is a bsg entry in the /proc/devices file
* b) the device node given to scsi_pt_open() is a char device
* c) the char major number of the device node given to scsi_pt_open()
@@ -1014,9 +354,6 @@ get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b)
*/
-#include <linux/types.h>
-#include <linux/bsg.h>
-
#ifdef major
#define SG_DEV_MAJOR major
#else
@@ -1027,30 +364,6 @@ get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b)
#endif
-struct sg_pt_linux_scsi {
- struct sg_io_v4 io_hdr; /* use v4 header as it is more general */
- int dev_fd; /* -1 if not given */
- int in_err;
- int os_err;
- unsigned char tmf_request[4];
- bool is_sg;
- bool is_bsg;
- bool is_nvme;
- bool mdxfer_out; /* direction of metadata xfer, true->data-out */
- uint32_t nvme_nsid; /* 1 to 0xfffffffe are possibly valid, 0
- * implies dev_fd is not a NVMe device
- * (is_nvme=false) or it is a NVMe char
- * device (e.g. /dev/nvme0 ) */
- uint32_t nvme_result;
- uint32_t mdxfer_len;
- void * mdxferp;
-};
-
-struct sg_pt_base {
- struct sg_pt_linux_scsi impl;
-};
-
-
/* Returns >= 0 if successful. If error in Unix returns negated errno. */
int
scsi_pt_open_device(const char * device_name, bool read_only, int verbose)
@@ -1069,9 +382,9 @@ scsi_pt_open_flags(const char * device_name, int flags, int verbose)
{
int fd;
- if (! bsg_nvme_char_major_checked) {
- bsg_nvme_char_major_checked = true;
- find_bsg_nvme_char_major(verbose);
+ if (! sg_bsg_nvme_char_major_checked) {
+ sg_bsg_nvme_char_major_checked = true;
+ sg_find_bsg_nvme_char_major(verbose);
}
if (verbose > 1) {
pr2ws("open %s with flags=0x%x\n", device_name, flags);
@@ -1186,9 +499,9 @@ set_pt_file_handle(struct sg_pt_base * vp, int dev_fd, int verbose)
struct sg_pt_linux_scsi * ptp = &vp->impl;
struct stat a_stat;
- if (! bsg_nvme_char_major_checked) {
- bsg_nvme_char_major_checked = true;
- find_bsg_nvme_char_major(verbose);
+ if (! sg_bsg_nvme_char_major_checked) {
+ sg_bsg_nvme_char_major_checked = true;
+ sg_find_bsg_nvme_char_major(verbose);
}
ptp->dev_fd = dev_fd;
if (dev_fd >= 0)
@@ -1223,7 +536,7 @@ set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb,
if (ptp->io_hdr.request)
++ptp->in_err;
- ptp->io_hdr.request = (__u64)(sg_intptr_t)cdb;
+ ptp->io_hdr.request = (__u64)(sg_uintptr_t)cdb;
ptp->io_hdr.request_len = cdb_len;
}
@@ -1236,7 +549,7 @@ set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense,
if (ptp->io_hdr.response)
++ptp->in_err;
memset(sense, 0, max_sense_len);
- ptp->io_hdr.response = (__u64)(sg_intptr_t)sense;
+ ptp->io_hdr.response = (__u64)(sg_uintptr_t)sense;
ptp->io_hdr.max_response_len = max_sense_len;
}
@@ -1250,7 +563,7 @@ set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp,
if (ptp->io_hdr.din_xferp)
++ptp->in_err;
if (dxfer_ilen > 0) {
- ptp->io_hdr.din_xferp = (__u64)(sg_intptr_t)dxferp;
+ ptp->io_hdr.din_xferp = (__u64)(sg_uintptr_t)dxferp;
ptp->io_hdr.din_xfer_len = dxfer_ilen;
}
}
@@ -1265,7 +578,7 @@ set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp,
if (ptp->io_hdr.dout_xferp)
++ptp->in_err;
if (dxfer_olen > 0) {
- ptp->io_hdr.dout_xferp = (__u64)(sg_intptr_t)dxferp;
+ ptp->io_hdr.dout_xferp = (__u64)(sg_uintptr_t)dxferp;
ptp->io_hdr.dout_xfer_len = dxfer_olen;
}
}
@@ -1307,7 +620,7 @@ set_scsi_pt_task_management(struct sg_pt_base * vp, int tmf_code)
ptp->io_hdr.subprotocol = 1; /* SCSI task management function */
ptp->tmf_request[0] = (unsigned char)tmf_code; /* assume it fits */
- ptp->io_hdr.request = (__u64)(sg_intptr_t)(&(ptp->tmf_request[0]));
+ ptp->io_hdr.request = (__u64)(sg_uintptr_t)(&(ptp->tmf_request[0]));
ptp->io_hdr.request_len = 1;
}
@@ -1581,73 +894,6 @@ do_scsi_pt_v3(struct sg_pt_linux_scsi * ptp, int fd, int time_secs,
return 0;
}
-/* Executes NVMe Admin command (or at least forwards it to lower layers).
- * Returns 0 for success, negative numbers are negated 'errno' values from
- * OS system calls. Positive return values are errors from this package.
- * When time_secs is 0 the Linux NVMe Admin command default of 60 seconds
- * is used. */
-static int
-do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
-{
- int n, len;
- struct sg_pt_linux_scsi * ptp = &vp->impl;
- struct sg_nvme_passthru_cmd cmd;
-
- if (vb > 3)
- pr2ws("%s: fd=%d, time_secs=%d\n", __func__, fd, time_secs);
- if (! ptp->io_hdr.request) {
- if (vb)
- pr2ws("No NVMe command given (set_scsi_pt_cdb())\n");
- return SCSI_PT_DO_BAD_PARAMS;
- }
- n = ptp->io_hdr.request_len;
- len = (int)sizeof(cmd);
- n = (n < len) ? n : len;
- if (n < 64) {
- if (vb)
- pr2ws("%s: command length of %d bytes is too short\n", __func__,
- n);
- return SCSI_PT_DO_BAD_PARAMS;
- }
- memcpy(&cmd, (unsigned char *)ptp->io_hdr.request, n);
- if (n < len) /* zero out rest of 'cmd' */
- memset((unsigned char *)&cmd + n, 0, len - n);
- if (ptp->io_hdr.din_xfer_len > 0) {
- cmd.data_len = ptp->io_hdr.din_xfer_len;
- cmd.addr = (__u64)(sg_intptr_t)ptp->io_hdr.din_xferp;
- } else if (ptp->io_hdr.dout_xfer_len > 0) {
- cmd.data_len = ptp->io_hdr.dout_xfer_len;
- cmd.addr = (__u64)(sg_intptr_t)ptp->io_hdr.dout_xferp;
- }
- if (time_secs < 0)
- cmd.timeout_ms = 0;
- else
- cmd.timeout_ms = 1000 * cmd.timeout_ms;
- if (vb > 2) {
- pr2ws("NVMe command:\n");
- dStrHex((const char *)&cmd, len, 1);
- }
- if (ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd) < 0) {
- ptp->os_err = errno;
- if (vb > 2)
- pr2ws("%s: ioctl(NVME_IOCTL_ADMIN_CMD) failed: %s (errno=%d)\n",
- __func__, strerror(ptp->os_err), ptp->os_err);
- return -ptp->os_err;
- } else
- ptp->os_err = 0;
- ptp->nvme_result = cmd.result;
- n = ptp->io_hdr.max_response_len;
- if ((n > 0) && ptp->io_hdr.response) {
- n = (n < len) ? n : len;
- memcpy((uint8_t *)ptp->io_hdr.response, &cmd, n);
- ptp->io_hdr.response_len = n;
- }
- if (vb > 2)
- pr2ws("%s: timeout_ms=%u, result=%u\n", __func__, cmd.timeout_ms,
- cmd.result);
- return 0;
-}
-
/* Executes SCSI command (or at least forwards it to lower layers).
* Returns 0 for success, negative numbers are negated 'errno' values from
* OS system calls. Positive return values are errors from this package. */
@@ -1658,9 +904,9 @@ do_scsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose)
struct sg_pt_linux_scsi * ptp = &vp->impl;
bool have_checked_for_type = (ptp->dev_fd >= 0);
- if (! bsg_nvme_char_major_checked) {
- bsg_nvme_char_major_checked = true;
- find_bsg_nvme_char_major(verbose);
+ if (! sg_bsg_nvme_char_major_checked) {
+ sg_bsg_nvme_char_major_checked = true;
+ sg_find_bsg_nvme_char_major(verbose);
}
if (ptp->in_err) {
if (verbose)
@@ -1688,8 +934,8 @@ do_scsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose)
if (ptp->os_err)
return -ptp->os_err;
if (ptp->is_nvme)
- return do_nvme_pt(vp, ptp->dev_fd, time_secs, verbose);
- else if (bsg_major <= 0)
+ return sg_do_nvme_pt(vp, ptp->dev_fd, time_secs, verbose);
+ else if (sg_bsg_major <= 0)
return do_scsi_pt_v3(ptp, fd, time_secs, verbose);
else if (ptp->is_bsg)
; /* drop through to sg v4 implementation */
@@ -1722,6 +968,3 @@ do_scsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose)
}
return 0;
}
-
-#endif
-// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
diff --git a/lib/sg_pt_linux_nvme.c b/lib/sg_pt_linux_nvme.c
new file mode 100644
index 00000000..89e5df4a
--- /dev/null
+++ b/lib/sg_pt_linux_nvme.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+/* sg_pt_linux_nvme version 1.00 20171206 */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h> /* to define 'major' */
+#ifndef major
+#include <sys/types.h>
+#endif
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <linux/major.h>
+
+#include "sg_pt.h"
+#include "sg_lib.h"
+#include "sg_linux_inc.h"
+#include "sg_pt_linux.h"
+#include "sg_unaligned.h"
+
+#define SCSI_INQUIRY_OPC 0x12
+#define SCSI_TEST_UNIT_READY_OPC 0x0
+
+/* Additional Sense Code (ASC) */
+#define NO_ADDITIONAL_SENSE 0x0
+#define LOGICAL_UNIT_NOT_READY 0x4
+#define LOGICAL_UNIT_COMMUNICATION_FAILURE 0x8
+#define UNRECOVERED_READ_ERR 0x11
+#define PARAMETER_LIST_LENGTH_ERR 0x1a
+#define INVALID_OPCODE 0x20
+#define LBA_OUT_OF_RANGE 0x21
+#define INVALID_FIELD_IN_CDB 0x24
+#define INVALID_FIELD_IN_PARAM_LIST 0x26
+#define UA_RESET_ASC 0x29
+#define UA_CHANGED_ASC 0x2a
+#define TARGET_CHANGED_ASC 0x3f
+#define LUNS_CHANGED_ASCQ 0x0e
+#define INSUFF_RES_ASC 0x55
+#define INSUFF_RES_ASCQ 0x3
+#define POWER_ON_RESET_ASCQ 0x0
+#define BUS_RESET_ASCQ 0x2 /* scsi bus reset occurred */
+#define MODE_CHANGED_ASCQ 0x1 /* mode parameters changed */
+#define CAPACITY_CHANGED_ASCQ 0x9
+#define SAVING_PARAMS_UNSUP 0x39
+#define TRANSPORT_PROBLEM 0x4b
+#define THRESHOLD_EXCEEDED 0x5d
+#define LOW_POWER_COND_ON 0x5e
+#define MISCOMPARE_VERIFY_ASC 0x1d
+#define MICROCODE_CHANGED_ASCQ 0x1 /* with TARGET_CHANGED_ASC */
+#define MICROCODE_CHANGED_WO_RESET_ASCQ 0x16
+
+#if (__STDC_VERSION__ >= 199901L) /* C99 or later */
+typedef intptr_t sg_uintptr_t;
+#else
+typedef long sg_uintptr_t;
+#endif
+
+
+static inline bool is_aligned(const void *restrict pointer,
+ size_t byte_count)
+{
+ return (sg_uintptr_t)pointer % byte_count == 0;
+}
+
+
+#if defined(__GNUC__) || defined(__clang__)
+static int pr2ws(const char * fmt, ...)
+ __attribute__ ((format (printf, 1, 2)));
+#else
+static int pr2ws(const char * fmt, ...);
+#endif
+
+
+static int
+pr2ws(const char * fmt, ...)
+{
+ va_list args;
+ int n;
+
+ va_start(args, fmt);
+ n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
+ va_end(args);
+ return n;
+}
+
+/* The web claims that all NVMe commands are 64 bytes long. Believe it until
+ * contradicted. The only SCSI commands that can be longer than 16 bytes are
+ * the Variable Length Commands (opcode 0x7f) and the XCDB wrapped commands
+ * (opcode 0x7e). Both have an inbuilt length field which can be cross
+ * checked with clen. */
+static bool
+is_scsi_command(const uint8_t * cdbp, int clen)
+{
+ int ilen, sa;
+
+ if (clen <= 16)
+ return true;
+ if (0 == (clen % 4)) {
+ if (0x7f == cdbp[0]) {
+ ilen = 8 + cdbp[7];
+ sa = sg_get_unaligned_be16(cdbp + 8);
+ if ((ilen == clen) && sa)
+ return true;
+ } else if (0x7e == cdbp[0]) {
+ ilen = 4 + sg_get_unaligned_be16(cdbp + 2);
+ if (ilen == clen)
+ return true;
+ }
+ }
+ if ((clen >= 64) && (clen <= 72))
+ return false;
+ pr2ws("%s: irregular command, assume NVMe:\n", __func__);
+ dStrHexErr((const char *)cdbp, clen, 1);
+ return false;
+}
+
+static void
+build_sense_buffer(bool desc, uint8_t *buf, uint8_t key, uint8_t asc,
+ uint8_t ascq)
+{
+ if (desc) {
+ buf[0] = 0x72; /* descriptor, current */
+ buf[1] = key;
+ buf[2] = asc;
+ buf[3] = ascq;
+ buf[7] = 0;
+ } else {
+ buf[0] = 0x70; /* fixed, current */
+ buf[2] = key;
+ buf[7] = 0xa;
+ buf[12] = asc;
+ buf[13] = ascq;
+ }
+}
+
+/* Set in_bit to -1 to indicate no bit position of invalid field */
+static void
+mk_sense_invalid_fld(struct sg_pt_linux_scsi * ptp, bool in_cdb, int in_byte,
+ int in_bit, int vb)
+{
+ bool dsense = ptp->scsi_dsense;
+ int sl, asc, n;
+ uint8_t * sbp = (uint8_t *)ptp->io_hdr.response;
+ uint8_t sks[4];
+
+ ptp->io_hdr.device_status = SAM_STAT_CHECK_CONDITION;
+ asc = in_cdb ? INVALID_FIELD_IN_CDB : INVALID_FIELD_IN_PARAM_LIST;
+ n = ptp->io_hdr.max_response_len;
+ if ((n < 8) || ((! dsense) && (n < 14))) {
+ pr2ws("%s: max_response_len=%d too short, want 14 or more\n",
+ __func__, n);
+ return;
+ } else
+ ptp->io_hdr.response_len = dsense ? 8 : ((n < 18) ? n : 18);
+ memset(sbp, 0, n);
+ build_sense_buffer(dsense, sbp, SPC_SK_ILLEGAL_REQUEST, asc, 0);
+ memset(sks, 0, sizeof(sks));
+ sks[0] = 0x80;
+ if (in_cdb)
+ sks[0] |= 0x40;
+ if (in_bit >= 0) {
+ sks[0] |= 0x8;
+ sks[0] |= (0x7 & in_bit);
+ }
+ sg_put_unaligned_be16(in_byte, sks + 1);
+ if (dsense) {
+ sl = sbp[7] + 8;
+ sbp[7] = sl;
+ sbp[sl] = 0x2;
+ sbp[sl + 1] = 0x6;
+ memcpy(sbp + sl + 4, sks, 3);
+ } else
+ memcpy(sbp + 15, sks, 3);
+ if (vb > 1)
+ pr2ws("%s: [sense_key,asc,ascq]: [0x5,0x%x,0x0] %c byte=%d, bit=%d\n",
+ __func__, asc, in_cdb ? 'C' : 'D', in_byte, in_bit);
+}
+
+static const char * nvme_scsi_vendor_str = "NVMe ";
+static const uint16_t inq_resp_len = 36;
+
+static int
+sntl_inq(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int fd,
+ int time_secs, int vb)
+{
+ bool evpd;
+ int err;
+ uint16_t k, n, alloc_len, pg_cd;
+ uint8_t inq_dout[128];
+
+ if (vb > 3)
+ pr2ws("%s: fd=%d, time_secs=%d\n", __func__, fd, time_secs);
+
+ if (0x2 & cdbp[1]) {
+ mk_sense_invalid_fld(ptp, true, 1, 1, vb);
+ return 0;
+ }
+ if (NULL == ptp->nvme_id_ctlp) {
+ struct sg_nvme_passthru_cmd cmd;
+ uint32_t pg_sz = sg_get_page_size();
+
+ ptp->nvme_id_ctlp = sg_memalign(pg_sz, pg_sz, &ptp->free_nvme_id_ctlp,
+ vb > 3);
+ if (NULL == ptp->nvme_id_ctlp) {
+ pr2ws("%s: sg_memalign() failed to get memory\n", __func__);
+ return SG_LIB_OS_BASE_ERR + ENOMEM;
+ }
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = 0x6;
+ cmd.cdw10 = 0x1; /* CNS=0x1 Identify controller */
+ cmd.addr = (uint64_t)ptp->nvme_id_ctlp;
+ cmd.data_len = pg_sz;
+ if (ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd) < 0) {
+ err = errno;
+ if (vb > 2)
+ pr2ws("%s: ioctl(NVME_IOCTL_ADMIN_CMD) failed: %s (errno=%d)"
+ "\n", __func__, strerror(err), err);
+ ptp->os_err = err;
+ return -err;
+ }
+ }
+ memset(inq_dout, 0, sizeof(inq_dout));
+ alloc_len = sg_get_unaligned_be16(cdbp + 3);
+ evpd = !!(0x1 & cdbp[1]);
+ pg_cd = cdbp[2];
+ if (evpd) { /* VPD page responses */
+ inq_dout[1] = pg_cd;
+ n = 0;
+ switch (pg_cd) {
+ case 0:
+ /* inq_dout[0] = (PQ=0)<<5 | (PDT=0); prefer pdt=0xd --> SES */
+ sg_put_unaligned_be16(3, inq_dout + 2);
+ inq_dout[4] = 0x0;
+ inq_dout[5] = 0x80;
+ inq_dout[6] = 0x83;
+ n = 7;
+ break;
+ case 0x80:
+ /* inq_dout[0] = (PQ=0)<<5 | (PDT=0); prefer pdt=0xd --> SES */
+ sg_put_unaligned_be16(20, inq_dout + 2);
+ memcpy(inq_dout + 4, ptp->nvme_id_ctlp + 4, 20); /* SN */
+ n = 24;
+ break;
+ case 0x83:
+ /* inq_dout[0] = (PQ=0)<<5 | (PDT=0); prefer pdt=0xd --> SES */
+ inq_dout[4] = 0x2; /* Prococol id=0, code_set=2 (ASCII) */
+ inq_dout[5] = 0x1; /* PIV=0, ASSOC=0 (LU ??), desig_id=1 */
+ /* Building T10 Vendor ID base designator, SNTL document 1.5
+ * dated 20150624 confuses this with SCSI name string
+ * descriptor, desig_id=8 */
+ memcpy(inq_dout + 8, nvme_scsi_vendor_str, 8);
+ memcpy(inq_dout + 16, ptp->nvme_id_ctlp + 24, 40); /* MN */
+ for (k = 40; k > 0; --k) {
+ if (' ' == inq_dout[16 + k - 1])
+ inq_dout[16 + k - 1] = '_'; /* convert trailing spaces */
+ else
+ break;
+ }
+ memcpy(inq_dout + 16 + k + 1, ptp->nvme_id_ctlp + 4, 20); /* SN */
+ n = 16 + k + 1 + 20;
+ inq_dout[7] = 8 + k + 1 + 20;
+ sg_put_unaligned_be16(n - 4, inq_dout + 2);
+ break;
+ default: /* Point to page_code field in cdb */
+ mk_sense_invalid_fld(ptp, true, 2, 7, vb);
+ return 0;
+ }
+ if (alloc_len > 0) {
+ n = (alloc_len < n) ? alloc_len : n;
+ n = (n < ptp->io_hdr.din_xfer_len) ? n : ptp->io_hdr.din_xfer_len;
+ if (n > 0)
+ memcpy((uint8_t *)ptp->io_hdr.din_xferp, inq_dout, n);
+ }
+ } else { /* Standard INQUIRY response */
+ /* inq_dout[0] = (PQ=0)<<5 | (PDT=0); pdt=0 --> SBC; 0xd --> SES */
+ inq_dout[2] = 6; /* version: SPC-4 */
+ inq_dout[3] = 2; /* NORMACA=0, HISUP=0, response data format: 2 */
+ inq_dout[4] = 31; /* so response length is (or could be) 36 bytes */
+ inq_dout[6] = 0x40; /* ENCSERV=1 */
+ inq_dout[7] = 0x2; /* CMDQUE=1 */
+ memcpy(inq_dout + 8, nvme_scsi_vendor_str, 8); /* NVMe not Intel */
+ memcpy(inq_dout + 16, ptp->nvme_id_ctlp + 24, 16); /* Prod <-- MN */
+ memcpy(inq_dout + 32, ptp->nvme_id_ctlp + 64, 4); /* Rev <-- FR */
+ if (alloc_len > 0) {
+ n = (alloc_len < inq_resp_len) ? alloc_len : inq_resp_len;
+ n = (n < ptp->io_hdr.din_xfer_len) ? n : ptp->io_hdr.din_xfer_len;
+ if (n > 0)
+ memcpy((uint8_t *)ptp->io_hdr.din_xferp, inq_dout, n);
+ }
+ }
+ return 0;
+}
+
+/* Executes NVMe Admin command (or at least forwards it to lower layers).
+ * Returns 0 for success, negative numbers are negated 'errno' values from
+ * OS system calls. Positive return values are errors from this package.
+ * When time_secs is 0 the Linux NVMe Admin command default of 60 seconds
+ * is used. */
+int
+sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
+{
+ bool scsi_cmd;
+ int n, len;
+ struct sg_pt_linux_scsi * ptp = &vp->impl;
+ struct sg_nvme_passthru_cmd cmd;
+ const uint8_t * cdbp;
+
+ if (vb > 4)
+ pr2ws("%s: fd=%d, time_secs=%d\n", __func__, fd, time_secs);
+ if (! ptp->io_hdr.request) {
+ if (vb)
+ pr2ws("No NVMe command given (set_scsi_pt_cdb())\n");
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ n = ptp->io_hdr.request_len;
+ cdbp = (const uint8_t *)ptp->io_hdr.request;
+ scsi_cmd = is_scsi_command(cdbp, n);
+ if (scsi_cmd) {
+ if (SCSI_INQUIRY_OPC == cdbp[0])
+ return sntl_inq(ptp, cdbp, fd, time_secs, vb);
+
+ }
+ len = (int)sizeof(cmd);
+ n = (n < len) ? n : len;
+ if (n < 64) {
+ if (vb)
+ pr2ws("%s: command length of %d bytes is too short\n", __func__,
+ n);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ memcpy(&cmd, (unsigned char *)ptp->io_hdr.request, n);
+ if (n < len) /* zero out rest of 'cmd' */
+ memset((unsigned char *)&cmd + n, 0, len - n);
+ if (ptp->io_hdr.din_xfer_len > 0) {
+ cmd.data_len = ptp->io_hdr.din_xfer_len;
+ cmd.addr = (__u64)(sg_uintptr_t)ptp->io_hdr.din_xferp;
+ } else if (ptp->io_hdr.dout_xfer_len > 0) {
+ cmd.data_len = ptp->io_hdr.dout_xfer_len;
+ cmd.addr = (__u64)(sg_uintptr_t)ptp->io_hdr.dout_xferp;
+ }
+ if (time_secs < 0)
+ cmd.timeout_ms = 0;
+ else
+ cmd.timeout_ms = 1000 * cmd.timeout_ms;
+ if (vb > 2) {
+ pr2ws("NVMe command:\n");
+ dStrHex((const char *)&cmd, len, 1);
+ }
+ if (ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd) < 0) {
+ ptp->os_err = errno;
+ if (vb > 2)
+ pr2ws("%s: ioctl(NVME_IOCTL_ADMIN_CMD) failed: %s (errno=%d)\n",
+ __func__, strerror(ptp->os_err), ptp->os_err);
+ return -ptp->os_err;
+ } else
+ ptp->os_err = 0;
+ ptp->nvme_result = cmd.result;
+ n = ptp->io_hdr.max_response_len;
+ if ((n > 0) && ptp->io_hdr.response) {
+ n = (n < len) ? n : len;
+ memcpy((uint8_t *)ptp->io_hdr.response, &cmd, n);
+ ptp->io_hdr.response_len = n;
+ }
+ if (vb > 2)
+ pr2ws("%s: timeout_ms=%u, result=%u\n", __func__, cmd.timeout_ms,
+ cmd.result);
+ return 0;
+}
diff --git a/sg3_utils.spec b/sg3_utils.spec
index 2a24fcb6..6a60354f 100644
--- a/sg3_utils.spec
+++ b/sg3_utils.spec
@@ -79,7 +79,7 @@ fi
%{_libdir}/*.la
%changelog
-* Fri Nov 03 2017 - dgilbert at interlog dot com
+* Mon Dec 04 2017 - dgilbert at interlog dot com
- track t10 changes
* sg3_utils-1.43
diff --git a/src/sg_dd.c b/src/sg_dd.c
index 736d1e05..74b0547e 100644
--- a/src/sg_dd.c
+++ b/src/sg_dd.c
@@ -62,7 +62,7 @@
#include "sg_unaligned.h"
#include "sg_pr2serr.h"
-static const char * version_str = "5.92 20171023";
+static const char * version_str = "5.93 20171206";
#define ME "sg_dd: "
@@ -1838,7 +1838,7 @@ main(int argc, char * argv[])
}
if (iflag.dio || iflag.direct || oflag.direct || (FT_RAW & in_type) ||
- (FT_RAW & out_type)) {
+ (FT_RAW & out_type)) { /* want heap buffer aligned to page_size */
size_t psz;
#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
@@ -1847,26 +1847,11 @@ main(int argc, char * argv[])
psz = 4096; /* give up, pick likely figure */
#endif
-#ifdef HAVE_POSIX_MEMALIGN
- {
- int err;
-
- err = posix_memalign((void **)&wrkBuff, psz, blk_sz * bpt);
- if (err) {
- pr2serr("posix_memalign: error [%d] out of memory?\n", err);
- return SG_LIB_CAT_OTHER;
- }
- wrkPos = wrkBuff;
- }
-#else
- wrkBuff = (unsigned char*)malloc(blk_sz * bpt + psz);
- if (0 == wrkBuff) {
- pr2serr("Not enough user memory for work buffer\n");
+ wrkPos = sg_memalign(blk_sz * bpt, psz, &wrkBuff, verbose > 3);
+ if (NULL == wrkPos) {
+ pr2serr("sg_memalign: error, out of memory?\n");
return SG_LIB_CAT_OTHER;
}
- wrkPos = (unsigned char *)(((uintptr_t)wrkBuff + psz - 1) &
- (~(psz - 1)));
-#endif
} else {
wrkBuff = (unsigned char*)malloc(blk_sz * bpt);
if (0 == wrkBuff) {
diff --git a/src/sg_inq.c b/src/sg_inq.c
index 6cdaf757..29dcc7aa 100644
--- a/src/sg_inq.c
+++ b/src/sg_inq.c
@@ -46,7 +46,7 @@
#include "sg_pt_nvme.h"
#endif
-static const char * version_str = "1.73 20171115"; /* SPC-5 rev 17 */
+static const char * version_str = "1.74 20171206"; /* SPC-5 rev 17 */
/* INQUIRY notes:
* It is recommended that the initial allocation length given to a
@@ -300,7 +300,8 @@ usage()
" only supported for VPD pages 0x80 and 0x83\n"
" --extended|-E|-x decode extended INQUIRY data VPD page "
"(0x86)\n"
- " --force|-f skip VPD page 0 checking\n"
+ " --force|-f skip VPD page 0 checking; provide more "
+ "NVMe info\n"
" --help|-h print usage message then exit\n"
" --hex|-H output response in hex\n"
" --id|-i decode device identification VPD page "
@@ -1748,7 +1749,7 @@ decode_dev_ids(const char * leadin, unsigned char * buff, int len, int do_hex,
"identifier\n",
sg_get_trans_proto_str(p_id, sizeof(b), b));
break;
- case 0xa: /* UUID identifier [spc5r08] */
+ case 0xa: /* UUID identifier [spc5r08] RFC 4122 */
if (1 != c_set) {
pr2serr(" << expected binary code_set >>\n");
dStrHexErr((const char *)ip, i_len, 0);
@@ -3793,7 +3794,7 @@ const char * rperf[] = {"Best", "Better", "Good", "Degraded"};
static int
do_nvme_id_ns(struct sg_pt_base * ptvp, uint32_t nsid,
- struct sg_nvme_passthru_cmd * id_cmdp, uint8_t * id_din,
+ 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;
@@ -3806,7 +3807,7 @@ do_nvme_id_ns(struct sg_pt_base * ptvp, uint32_t nsid,
clear_scsi_pt_obj(ptvp);
id_cmdp->nsid = nsid;
id_cmdp->cdw10 = 0x0; /* CNS=0x0 Identify NS */
- set_scsi_pt_data_in(ptvp, id_din, id_din_len);
+ set_scsi_pt_data_in(ptvp, id_dinp, id_din_len);
set_scsi_pt_sense(ptvp, (unsigned char *)&cmd_back, sizeof(cmd_back));
set_scsi_pt_cdb(ptvp, (const uint8_t *)id_cmdp, sizeof(*id_cmdp));
ret = do_scsi_pt(ptvp, -1, 0 /* timeout (def: 1 min) */, vb);
@@ -3817,24 +3818,24 @@ do_nvme_id_ns(struct sg_pt_base * ptvp, uint32_t nsid,
}
if (ret)
return ret;
- num_lbaf = id_din[25] + 1; /* spec says this is "0's based value" */
- flbas = id_din[26] & 0xf; /* index of active LBA format (for this ns) */
+ 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_din, sizeof(id_din), op);
+ do_nvme_identify_hex_raw(id_dinp, id_din_len, op);
return ret;
}
- ns_sz = sg_get_unaligned_le64(id_din + 0);
- eui_64 = sg_get_unaligned_be64(id_din + 120); /* N.B. big endian */
- if (! sg_all_zeros(id_din + 104, 16))
+ ns_sz = sg_get_unaligned_le64(id_dinp + 0);
+ eui_64 = sg_get_unaligned_be64(id_dinp + 120); /* N.B. big endian */
+ if (! sg_all_zeros(id_dinp + 104, 16))
got_eui_128 = true;
printf(" Namespace size/capacity: %" PRIu64 "/%" PRIu64
- " blocks\n", ns_sz, sg_get_unaligned_le64(id_din + 8));
+ " blocks\n", ns_sz, sg_get_unaligned_le64(id_dinp + 8));
printf(" Namespace utilization: %" PRIu64 " blocks\n",
- sg_get_unaligned_le64(id_din + 16));
+ sg_get_unaligned_le64(id_dinp + 16));
if (got_eui_128) { /* N.B. big endian */
- printf(" NGUID: 0x%02x", id_din[104]);
+ printf(" NGUID: 0x%02x", id_dinp[104]);
for (k = 1; k < 16; ++k)
- printf("%02x", id_din[104 + k]);
+ printf("%02x", id_dinp[104 + k]);
printf("\n");
} else if (op->do_force)
printf(" NGUID: 0x0\n");
@@ -3848,7 +3849,7 @@ do_nvme_id_ns(struct sg_pt_base * ptvp, uint32_t nsid,
printf(" <-- active\n");
else
printf("\n");
- flba_info = sg_get_unaligned_le32(id_din + off);
+ flba_info = sg_get_unaligned_le32(id_dinp + off);
md_size = flba_info & 0xffff;
lb_size = flba_info >> 16 & 0xff;
if (lb_size > 31) {
@@ -3889,7 +3890,9 @@ do_nvme_identify(int pt_fd, const struct opts_t * op)
struct sg_nvme_passthru_cmd identify_cmd;
struct sg_nvme_passthru_cmd cmd_back;
struct sg_nvme_passthru_cmd * id_cmdp = &identify_cmd;
- uint8_t id_din[4096];
+ uint8_t * id_dinp = NULL;
+ uint8_t * free_id_dinp = NULL;
+ const uint32_t pg_sz = sg_get_page_size();
if (op->do_raw) {
if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
@@ -3906,7 +3909,8 @@ do_nvme_identify(int pt_fd, const struct opts_t * op)
id_cmdp->opcode = 0x6;
nsid = get_pt_nvme_nsid(ptvp);
id_cmdp->cdw10 = 0x1; /* CNS=0x1 Identify controller */
- set_scsi_pt_data_in(ptvp, id_din, sizeof(id_din));
+ id_dinp = sg_memalign(pg_sz, pg_sz, &free_id_dinp, vb > 3);
+ set_scsi_pt_data_in(ptvp, id_dinp, pg_sz);
set_scsi_pt_cdb(ptvp, (const uint8_t *)id_cmdp, sizeof(*id_cmdp));
set_scsi_pt_sense(ptvp, (unsigned char *)&cmd_back, sizeof(cmd_back));
ret = do_scsi_pt(ptvp, -1, 0 /* timeout (def: 1 min) */, vb);
@@ -3917,16 +3921,16 @@ do_nvme_identify(int pt_fd, const struct opts_t * op)
}
if (ret)
goto err_out;
- max_nsid = sg_get_unaligned_le16(id_din + 516);
+ max_nsid = sg_get_unaligned_le16(id_dinp + 516);
if (op->do_raw || op->do_hex) {
- do_nvme_identify_hex_raw(id_din, sizeof(id_din), op);
+ do_nvme_identify_hex_raw(id_dinp, pg_sz, op);
goto skip1;
}
printf("Identify controller for %s:\n", op->device_name);
- printf(" Model number: %.40s\n", (const char *)(id_din + 24));
- printf(" Serial number: %.20s\n", (const char *)(id_din + 4));
- printf(" Firmware revision: %.8s\n", (const char *)(id_din + 64));
- ver = sg_get_unaligned_le32(id_din + 80);
+ printf(" Model number: %.40s\n", (const char *)(id_dinp + 24));
+ printf(" Serial number: %.20s\n", (const char *)(id_dinp + 4));
+ printf(" Firmware revision: %.8s\n", (const char *)(id_dinp + 64));
+ ver = sg_get_unaligned_le32(id_dinp + 80);
ver_maj = (ver >> 16);
ver_min = (ver >> 8) & 0xff;
ver_ter = (ver & 0xff);
@@ -3937,28 +3941,28 @@ do_nvme_identify(int pt_fd, const struct opts_t * op)
else
printf("\n");
printf(" PCI vendor ID VID/SSVID: 0x%x/0x%x\n",
- sg_get_unaligned_le16(id_din + 0),
- sg_get_unaligned_le16(id_din + 2));
+ sg_get_unaligned_le16(id_dinp + 0),
+ sg_get_unaligned_le16(id_dinp + 2));
printf(" IEEE OUI Identifier: 0x%x\n",
- sg_get_unaligned_le24(id_din + 73));
- got_fguid = ! sg_all_zeros(id_din + 112, 16);
+ sg_get_unaligned_le24(id_dinp + 73));
+ got_fguid = ! sg_all_zeros(id_dinp + 112, 16);
if (got_fguid) {
- printf(" FGUID: 0x%02x", id_din[112]);
+ printf(" FGUID: 0x%02x", id_dinp[112]);
for (k = 1; k < 16; ++k)
- printf("%02x", id_din[112 + k]);
+ printf("%02x", id_dinp[112 + k]);
printf("\n");
} else if (op->do_force)
printf(" FGUID: 0x0\n");
- printf(" Controller ID: 0x%x\n", sg_get_unaligned_le16(id_din + 78));
+ printf(" Controller ID: 0x%x\n", sg_get_unaligned_le16(id_dinp + 78));
if (op->do_force) {
printf(" Management endpoint capabilities, over a PCIe port: %d\n",
- !! (0x2 & id_din[255]));
+ !! (0x2 & id_dinp[255]));
printf(" Management endpoint capabilities, over a SMBus/I2C port: "
- "%d\n", !! (0x1 & id_din[255]));
+ "%d\n", !! (0x1 & id_dinp[255]));
}
printf(" Number of namespaces: %u\n", max_nsid);
- sz1 = sg_get_unaligned_le64(id_din + 280); /* lower 64 bits */
- sz2 = sg_get_unaligned_le64(id_din + 288); /* upper 64 bits */
+ sz1 = sg_get_unaligned_le64(id_dinp + 280); /* lower 64 bits */
+ sz2 = sg_get_unaligned_le64(id_dinp + 288); /* upper 64 bits */
if (sz2)
printf(" Total NVM capacity: huge ...\n");
else if (sz1)
@@ -3969,8 +3973,8 @@ do_nvme_identify(int pt_fd, const struct opts_t * op)
const char * cp;
printf(" Total NVM capacity: 0 bytes\n");
- npss = id_din[263] + 1;
- up = id_din + 2048;
+ npss = id_dinp[263] + 1;
+ up = id_dinp + 2048;
for (k = 0; k < npss; ++k, up += 32) {
n = sg_get_unaligned_le16(up + 0);
n *= (0x1 & up[3]) ? 1 : 100; /* unit: 100 microWatts */
@@ -4013,7 +4017,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_din, sizeof(id_din), op);
+ ret = do_nvme_id_ns(ptvp, nsid, id_cmdp, id_dinp, pg_sz, op);
if (ret)
goto err_out;
@@ -4021,13 +4025,14 @@ 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_din, sizeof(id_din), op);
+ ret = do_nvme_id_ns(ptvp, k, id_cmdp, id_dinp, pg_sz, op);
if (ret)
goto err_out;
}
}
err_out:
destruct_scsi_pt_obj(ptvp);
+ free(free_id_dinp);
return ret;
}
diff --git a/src/sg_raw.c b/src/sg_raw.c
index 0f513e4c..537b9b1d 100644
--- a/src/sg_raw.c
+++ b/src/sg_raw.c
@@ -32,7 +32,7 @@
#include "sg_pr2serr.h"
#include "sg_unaligned.h"
-#define SG_RAW_VERSION "0.4.18 (2017-10-09)"
+#define SG_RAW_VERSION "0.4.19 (2017-12-06)"
#ifdef SG_LIB_WIN32
#ifndef HAVE_SYSCONF
@@ -275,63 +275,6 @@ process_cl(struct opts_t * op, int argc, char *argv[])
return 0;
}
-/* Allocate aligned memory (heap) starting on page boundary */
-static unsigned char *
-my_memalign(int length, unsigned char ** wrkBuffp, const struct opts_t * op)
-{
- size_t psz;
- unsigned char * res;
-
-#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
- psz = sysconf(_SC_PAGESIZE); /* POSIX.1 (was getpagesize()) */
-#elif defined(SG_LIB_WIN32)
- psz = win_pagesize();
-#else
- psz = 4096; /* give up, pick likely figure */
-#endif
-
-#ifdef HAVE_POSIX_MEMALIGN
- {
- int err;
- void * wp = NULL;
-
- err = posix_memalign(&wp, psz, length);
- if (err || (NULL == wp)) {
- pr2serr("posix_memalign: error [%d], out of memory?\n", err);
- return NULL;
- }
- memset(wp, 0, length);
- if (wrkBuffp)
- *wrkBuffp = (unsigned char *)wp;
- res = (unsigned char *)wp;
- if (op->verbose > 3)
- pr2serr("%s: posix, len=%d, wrkBuffp=%p, psz=%d, rp=%p\n",
- __func__, length, (void *)*wrkBuffp, (int)psz,
- (void *)res);
- return res;
- }
-#else
- {
- unsigned char * wrkBuff;
-
- wrkBuff = (unsigned char*)calloc(length + psz, 1);
- if (NULL == wrkBuff) {
- if (wrkBuffp)
- *wrkBuffp = NULL;
- return NULL;
- } else if (wrkBuffp)
- *wrkBuffp = wrkBuff;
- res = (unsigned char *)(((uintptr_t)wrkBuff + psz - 1) &
- (~(psz - 1)));
- if (op->verbose > 3)
- pr2serr("%s: hack, len=%d, wrkBuffp=%p, psz=%d, rp=%p\n",
- __func__, length, (void *)*wrkBuffp, (int)psz,
- (void *)res);
- return res;
- }
-#endif
-}
-
static int
skip(int fd, off_t offset)
{
@@ -392,7 +335,8 @@ fetch_dataout(struct opts_t * op)
}
}
- buf = my_memalign(op->dataout_len, &wrkBuf, op);
+ buf = sg_memalign(op->dataout_len, 0 /* page_size */, &wrkBuf,
+ op->verbose > 3);
if (buf == NULL) {
perror("malloc");
goto bail;
@@ -528,7 +472,8 @@ main(int argc, char *argv[])
set_scsi_pt_data_out(ptvp, dxfer_buffer_out, op->dataout_len);
}
if (op->do_datain) {
- dxfer_buffer_in = my_memalign(op->datain_len, &wrkBuf, op);
+ dxfer_buffer_in = sg_memalign(op->datain_len, 0 /* page_size */,
+ &wrkBuf, op->verbose > 3);
if (dxfer_buffer_in == NULL) {
perror("malloc");
ret = SG_LIB_CAT_OTHER;
diff --git a/src/sg_ses.c b/src/sg_ses.c
index ce977ecd..80153b3f 100644
--- a/src/sg_ses.c
+++ b/src/sg_ses.c
@@ -314,27 +314,11 @@ static struct join_row_t join_arr[MX_JOIN_ROWS];
static struct join_row_t * join_arr_lastp = join_arr + MX_JOIN_ROWS - 1;
static bool join_done = false;
-#ifdef SG_LIB_FREEBSD
-
-#include <sys/param.h> /* contains PAGE_SIZE */
-
-static uint8_t enc_stat_rsp[MX_ALLOC_LEN]
- __attribute__ ((aligned (PAGE_SIZE)));
-static uint8_t elem_desc_rsp[MX_ALLOC_LEN]
- __attribute__ ((aligned (PAGE_SIZE)));
-static uint8_t add_elem_rsp[MX_ALLOC_LEN]
- __attribute__ ((aligned (PAGE_SIZE)));
-static uint8_t threshold_rsp[MX_ALLOC_LEN]
- __attribute__ ((aligned (PAGE_SIZE)));
-
-#else
-
-static uint8_t enc_stat_rsp[MX_ALLOC_LEN];
-static uint8_t elem_desc_rsp[MX_ALLOC_LEN];
-static uint8_t add_elem_rsp[MX_ALLOC_LEN];
-static uint8_t threshold_rsp[MX_ALLOC_LEN];
-
-#endif
+/* Large buffers on heap, aligned to page size and zeroed */
+static uint8_t * enc_stat_rsp;
+static uint8_t * elem_desc_rsp;
+static uint8_t * add_elem_rsp;
+static uint8_t * threshold_rsp;
static int enc_stat_rsp_len;
static int elem_desc_rsp_len;
@@ -5096,24 +5080,31 @@ enumerate_work(const struct opts_t * op)
int
main(int argc, char * argv[])
{
- int sg_fd, k, res;
- int pd_type = 0;
bool have_cgs = false;
+ int k, res;
+ int sg_fd = -1;
+ int pd_type = 0;
int ret = 0;
- struct sg_simple_inquiry_resp inq_resp;
- char buff[128];
- char b[80];
- struct tuple_acronym_val tav_arr[CGS_CL_ARR_MAX_SZ];
+ uint32_t pg_sz;
const char * cp;
struct opts_t opts;
struct opts_t * op;
struct tuple_acronym_val * tavp;
struct cgs_cl_t * cgs_clp;
+ uint8_t * free_enc_stat_rsp = NULL;
+ uint8_t * free_elem_desc_rsp = NULL;
+ uint8_t * free_add_elem_rsp = NULL;
+ uint8_t * free_threshold_rsp = NULL;
+ struct tuple_acronym_val tav_arr[CGS_CL_ARR_MAX_SZ];
+ struct sg_simple_inquiry_resp inq_resp;
+ char buff[128];
+ char b[80];
op = &opts;
memset(op, 0, sizeof(*op));
op->dev_slot_num = -1;
op->ind_indiv_last = -1;
+ pg_sz = sg_get_page_size();
res = cl_process(op, argc, argv);
if (res)
return SG_LIB_SYNTAX_ERROR;
@@ -5129,6 +5120,31 @@ main(int argc, char * argv[])
enumerate_work(op);
return 0;
}
+ enc_stat_rsp = sg_memalign(MX_ALLOC_LEN, 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;
+ }
+ elem_desc_rsp = sg_memalign(MX_ALLOC_LEN, 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;
+ }
+ add_elem_rsp = sg_memalign(MX_ALLOC_LEN, 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;
+ }
+ threshold_rsp = sg_memalign(MX_ALLOC_LEN, pg_sz, &free_threshold_rsp,
+ op->verbose > 3);
+ if (NULL == threshold_rsp) {
+ pr2serr("Unable to get heap for threshold_rsp\n");
+ goto err_out;
+ }
+
if (op->num_cgs) {
have_cgs = true;
if (op->page_code_given &&
@@ -5332,7 +5348,18 @@ err_out:
if (ret && (0 == op->verbose))
pr2serr("Problem detected, try again with --verbose option for more "
"information\n");
- res = sg_cmds_close_device(sg_fd);
+ if (sg_fd >= 0)
+ res = sg_cmds_close_device(sg_fd);
+ else
+ res = 0;
+ if (free_enc_stat_rsp)
+ free(free_enc_stat_rsp);
+ if (free_elem_desc_rsp)
+ free(free_elem_desc_rsp);
+ if (free_add_elem_rsp)
+ free(free_add_elem_rsp);
+ if (free_threshold_rsp)
+ free(free_threshold_rsp);
if (res < 0) {
pr2serr("close error: %s\n", safe_strerror(-res));
if (0 == ret)
diff --git a/src/sg_vpd.c b/src/sg_vpd.c
index 728c7f9b..a3cf9fe9 100644
--- a/src/sg_vpd.c
+++ b/src/sg_vpd.c
@@ -38,7 +38,7 @@
*/
-static const char * version_str = "1.31 20171104"; /* spc5r17 + sbc4r14 */
+static const char * version_str = "1.32 20171206"; /* spc5r17 + sbc4r14 */
/* standard VPD pages, in ascending page number order */
#define VPD_SUPPORTED_VPDS 0x0
@@ -1086,7 +1086,7 @@ decode_dev_ids_quiet(unsigned char * buff, int len, int m_assoc,
break;
case 9: /* Protocol specific port identifier */
break;
- case 0xa: /* UUID identifier */
+ case 0xa: /* UUID identifier [spc5r08] RFC 4122 */
if ((1 != c_set) || (18 != i_len) || (1 != ((ip[0] >> 4) & 0xf)))
break;
for (m = 0; m < 16; ++m) {
@@ -2261,10 +2261,10 @@ decode_b1_vpd(unsigned char * buff, int len, int do_hex, int pdt)
printf(" Manufacturer-assigned serial number: %.*s\n",
len - 4, buff + 4);
break;
- case PDT_SES: /* T10/17-142r1 -> ses4r02 ?? */
+ case PDT_SES: /* T10/17-142r1 -> ses4r02 ?? */
if (len < 8) {
pr2serr("Enclosure service device characteristics VPD page "
- "length too short=%d\n", len);
+ "length too short=%d\n", len);
return;
}
printf(" SESDNLD=%d\n", !! (0x2 & buff[4]));
@@ -2276,7 +2276,7 @@ decode_b1_vpd(unsigned char * buff, int len, int do_hex, int pdt)
printf(" DMOSASDS=%d\n", !! (0x8 & buff[6]));
printf(" DMOSDS=%d\n", !! (0x4 & buff[6]));
printf(" ADMS=%d\n", !! (0x1 & buff[6]));
- break;
+ break;
default:
pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt);
dStrHexErr((const char *)buff, len, 0);
diff --git a/src/sg_write_x.c b/src/sg_write_x.c
index 4c4207c3..12d0b630 100644
--- a/src/sg_write_x.c
+++ b/src/sg_write_x.c
@@ -37,10 +37,17 @@
#include "sg_unaligned.h"
#include "sg_pr2serr.h"
-static const char * version_str = "1.05 20171127";
+static const char * version_str = "1.05 20171202";
-
-#define ME "sg_write_x: "
+/* Protection Information refers to 8 bytes of extra information usually
+ * associated with each logical block and is often abbreviated to PI while
+ * its fields: reference-tag (4 bytes), application-tag (2 bytes) and
+ * tag-mask (2 bytes) are often abbreviated to RT, AT and TM respectively.
+ * And the LBA Range Descriptor associated with the WRITE SCATTERED command
+ * is abbreviated to RD. A degenerate RD is one where both the LBA and length
+ * components are zero; they are not illegal according to T10 but are a
+ * little tricky to handle when scanning and little extra information
+ * is provided. */
#define ORWRITE16_OP 0x8b
#define WRITE_16_OP 0x8a
@@ -102,12 +109,13 @@ static struct option long_options[] = {
{"num", required_argument, 0, 'n'},
{"offset", required_argument, 0, 'o'},
{"or", no_argument, 0, 'O'},
- {"raw", no_argument, 0, 'r'},
- {"ref-tag", required_argument, 0, 'R'},
- {"ref_tag", required_argument, 0, 'R'},
+ {"ref-tag", required_argument, 0, 'r'},
+ {"ref_tag", required_argument, 0, 'r'},
{"same", required_argument, 0, 'M'},
{"scat-file", required_argument, 0, 'q'},
{"scat_file", required_argument, 0, 'q'},
+ {"scat-raw", no_argument, 0, 'R'},
+ {"scat_raw", no_argument, 0, 'R'},
{"scattered", required_argument, 0, 'S'},
{"stream", required_argument, 0, 'T'},
{"strict", no_argument, 0, 's'},
@@ -130,11 +138,11 @@ struct opts_t {
bool do_combined; /* -c DOF --> .scat_lbdof */
bool do_dry_run;
bool do_or; /* -O ORWRITE(16 or 32) */
- bool do_raw;
+ bool do_scat_raw;
bool do_same; /* -M WRITE SAME(16 or 32) */
/* --same=NDOB NDOB --> .ndob */
bool do_scattered; /* -S WRITE SCATTERED(16 or 32) */
- /* --scattered=RD RD --> .scat_num_lbard */
+ /* --scattered=RD RD --> .scat_num_lbrd */
bool do_stream; /* -T WRITE STREAM(16 or 32) */
/* --stream=ID ID --> .str_id */
bool do_unmap;
@@ -143,6 +151,7 @@ struct opts_t {
* is 8 bytes long following each logical
* block in the data out buffer. */
bool dpo;
+ bool explicit_lba; /* .numblocks defaults to 0 when false */
bool fua;
bool ndob;
bool strict;
@@ -158,19 +167,21 @@ struct opts_t {
uint16_t app_tag; /* part of protection information (def: 0xffff) */
uint16_t atomic_boundary;
uint16_t scat_lbdof;
- uint16_t scat_num_lbard;
+ uint16_t scat_num_lbrd;
uint16_t str_id; /* (stream ID) is for WRITE STREAM */
uint16_t tag_mask; /* part of protection information (def: 0xffff) */
uint32_t bs; /* logical block size (def: 0). 0 implies use READ
* CAPACITY(10 or 16) to determine */
uint32_t bs_pi_do; /* logical block size plus PI, if any */
- uint32_t numblocks;
+ uint32_t if_dlen; /* bytes to read after .if_offset from .if_name,
+ * if 0 given, read rest of .if_name */
+ uint32_t numblocks; /* defaults to 0 if .explicit_lba is false, else
+ * derives from IF and/or sum of NUMs */
uint32_t orw_eog;
uint32_t orw_nog;
uint32_t ref_tag; /* part of protection information (def: 0xffffffff) */
uint64_t lba;
- uint64_t offset; /* byte offset in if_name to start reading */
- uint64_t dlen; /* bytes to read after offset from if_name, 0->rest */
+ uint64_t if_offset; /* byte offset in .if_name to start reading */
uint64_t tot_lbs; /* from READ CAPACITY */
ssize_t xfer_bytes; /* derived value: bs_pi_do * numblocks */
const char * device_name;
@@ -193,24 +204,24 @@ usage(int do_help)
" [--fua] [--generation=EOG,NOG] [--grpnum=GN] "
"[--help] --in=IF\n"
" [--lba=LBA,LBA...] [--normal] [--num=NUM,NUM...]\n"
- " [--offset=OFF[,DLEN]] [--or] [--raw] [--ref-tag=RT] "
+ " [--offset=OFF[,DLEN]] [--or] [--ref-tag=RT] "
"[--same=NDOB]\n"
- " [--scat-file=SF] [--scattered=RD] [--stream=ID] "
- "[--strict]\n"
- " [--tag-mask=TM] [--timeout=TO] [--unmap=U_A] "
- "[--verbose]\n"
- " [--version] [--wrprotect=WRP] DEVICE\n");
+ " [--scat-file=SF] [--scat-raw] [--scattered=RD] "
+ "[--stream=ID]\n"
+ " [--strict] [--tag-mask=TM] [--timeout=TO] "
+ "[--unmap=U_A]\n"
+ " [--verbose] [--version] [--wrprotect=WRP] DEVICE\n");
if (1 != do_help) {
pr2serr("\nOr the corresponding short option usage:\n"
"sg_write_x [-6] [-3] [-a AT] [-A AB] [-B OP,PGP] [-b LBS] "
"[-c DOF] [-D DLD]\n"
" [-d] [-x] [-f] [-G EOG,NOG] [-g GN] [-h] -i IF "
"[-l LBA,LBA...]\n"
- " [-N] [-n NUM,NUM...] [-o OFF[,DLEN]] [-O] [-r] "
- "[-R RT] [-M NDOB]\n"
- " [-q SF] [-S RD] [-T ID] [-s] [-t TM] [-I TO] "
- "[-u U_A] [-v] [-V]\n"
- " [-w WPR] DEVICE\n"
+ " [-N] [-n NUM,NUM...] [-o OFF[,DLEN]] [-O] "
+ "[-r RT] [-M NDOB]\n"
+ " [-q SF] [-R] [-S RD] [-T ID] [-s] [-t TM] [-I TO] "
+ "[-u U_A] [-v]\n"
+ " [-V] [-w WPR] DEVICE\n"
);
pr2serr("\nUse '-h' or '--help' for more help\n");
return;
@@ -274,9 +285,7 @@ usage(int do_help)
" |-o OFF[,DLEN] (def: 0), then read DLEN bytes(def: "
"rest of IF)\n"
" --or|-O send ORWRITE command\n"
- " --raw|-r read --scat_file=SF as binary (def: "
- "ASCII hex)\n"
- " --ref-tag=RT|-R RT expected reference tag field (def: "
+ " --ref-tag=RT|-r RT expected reference tag field (def: "
"0xffffffff)\n"
" --same=NDOB|-M NDOB send WRITE SAME command. NDOB (no "
"data out buffer)\n"
@@ -284,6 +293,8 @@ usage(int do_help)
"1 (don't)\n"
" --scat-file=SF|-q SF file containing LBA, NUM pairs, "
"see manpage\n"
+ " --scat-raw|-R read --scat_file=SF as binary (def: "
+ "ASCII hex)\n"
" --scattered=RD|-S RD send WRITE SCATTERED command with "
"RD range\n"
" descriptors (RD can be 0 when "
@@ -376,7 +387,7 @@ usage(int do_help)
" [--combined=DOF] [--dpo] [--fua] [--grpnum=GN]\n"
" [--lba=LBA,LBA...] [--num=NUM,NUM...] "
"[--offset=OFF[,DLEN]]\n"
- " [--raw] [--ref-tag=RT] [--scat-file=SF] "
+ " [--ref-tag=RT] [--scat-file=SF] [--scat-raw] "
"[--strict]\n"
" [--tag-mask=TM] [--timeout=TO] [--wrprotect=WRP] "
"DEVICE\n"
@@ -385,10 +396,11 @@ usage(int do_help)
" sg_write_x --scattered --in=IF [--bs=LBS] [--combined=DOF] "
"[--dld=DLD]\n"
" [--dpo] [--fua] [--grpnum=GN] [--lba=LBA,LBA...]\n"
- " [--num=NUM,NUM...] [--offset=OFF[,DLEN]] [--raw] "
- "[--scat-file=SF]\n"
- " [--strict] [--timeout=TO] [--wrprotect=WRP] "
- "DEVICE\n"
+ " [--num=NUM,NUM...] [--offset=OFF[,DLEN]] "
+ "[--scat-raw]\n"
+ " [--scat-file=SF] [--strict] [--timeout=TO] "
+ "[--wrprotect=WRP]\n"
+ " DEVICE\n"
"\n"
"WRITE STREAM (32) applicable options:\n"
" sg_write_x --stream=ID --in=IF --32 [--app-tag=AT] "
@@ -417,10 +429,10 @@ usage(int do_help)
"and/or the\n"
" --dry-run option\n"
" - all WRITE X commands will accept --scat-file=SF and "
- "optionally --raw\n"
+ "optionally --scat-raw\n"
" options but only the first addr,num pair is used (any "
"more are ignored)\n"
- " - when '--raw --scat-file=SF' are used then the binary "
+ " - when '--rscat-aw --scat-file=SF' are used then the binary "
"format expected in\n"
" SF is as defined for the WRITE SCATTERED commands. "
"That is 32 bytes\n"
@@ -456,7 +468,7 @@ usage(int do_help)
/* Returns true if num_of_f_chars of ASCII 'f' or 'F' characters are found
* in sequence. Any leading "0x" or "0X" is ignored; otherwise false is
- * returned (and the comparsion stops when the first mismatch is found).
+ * returned (and the comparison stops when the first mismatch is found).
* For example a sequence of 'f' characters in a null terminated C string
* that is two characters shorter than the requested num_of_f_chars will
* compare the null character in the string with 'f', find them unequal,
@@ -601,13 +613,13 @@ build_num_arr(const char * inp, uint32_t * num_arr, uint32_t * num_arr_len,
* SG_LIB_SYNTAX_ERROR). If protection information fields not given, then
* default values are given (i.e. all 0xff bytes). Ignores all spaces and
* tabs and everything after '#' on lcp (assumed to be an ASCII line that
- * is null terminated. If successful writes a LBA range descriptor starting
- * at 'up'. */
+ * is null terminated). If successful writes a LBA range descriptor starting
+ * at 'up'. The array starting at 'up' should be at least 20 bytes long. */
static int
parse_scat_pi_line(const char * lcp, uint8_t * up, uint32_t * sum_num)
{
bool ok;
- int n;
+ int k;
int64_t ll;
const char * cp;
const char * bp;
@@ -615,13 +627,12 @@ parse_scat_pi_line(const char * lcp, uint8_t * up, uint32_t * sum_num)
bp = c;
cp = strchr(lcp, '#');
- n = strspn(lcp, " \t");
- lcp = lcp + n;
+ lcp += strspn(lcp, " \t");
if (('\0' == *lcp) || (cp && (lcp >= cp)))
return 999; /* blank line or blank prior to first '#' */
if (cp) { /* copy from first non whitespace ... */
- memcpy(c, lcp, cp - lcp);
- c[cp - lcp] = '\0'; /* ... to just before first '#' */
+ memcpy(c, lcp, cp - lcp); /* ... to just prior to first '#' */
+ c[cp - lcp] = '\0';
} else
strcpy(c, lcp); /* ... to end of line, including null */
ll = sg_get_llnum(bp);
@@ -635,7 +646,7 @@ parse_scat_pi_line(const char * lcp, uint8_t * up, uint32_t * sum_num)
cp = strchr(bp, ',');
if (cp) {
bp = cp + 1;
- if (*cp) {
+ if (*bp) {
ll = sg_get_llnum(bp);
if (-1 != ll)
ok = true;
@@ -647,64 +658,92 @@ parse_scat_pi_line(const char * lcp, uint8_t * up, uint32_t * sum_num)
}
sg_put_unaligned_be32((uint32_t)ll, up + 8);
*sum_num += (uint32_t)ll;
- cp = strchr(bp, ',');
- if (NULL == cp) {
- sg_put_unaligned_be32((uint32_t)DEF_RT, up + 12);
- sg_put_unaligned_be16((uint16_t)DEF_AT, up + 16);
- sg_put_unaligned_be16((uint16_t)DEF_TM, up + 18);
- return 0;
- }
- ok = false;
- bp = cp + 1;
- if (*cp) {
- ll = sg_get_llnum(bp);
- if (-1 != ll)
- ok = true;
- }
- if ((! ok) || (ll > UINT32_MAX)) {
- pr2serr("%s: error reading RT (third) item on ", __func__);
- return SG_LIB_SYNTAX_ERROR;
- }
- sg_put_unaligned_be32((uint32_t)ll, up + 12);
- ok = false;
- cp = strchr(bp, ',');
- if (cp) {
+ /* now for 3 PI items */
+ for (k = 0; k < 3; ++k) {
+ ok = true;
+ cp = strchr(bp, ',');
+ if (NULL == cp)
+ break;
bp = cp + 1;
- if (*cp) {
- n = sg_get_num(bp);
- if (-1 != ll)
- ok = true;
+ if (*bp) {
+ cp += strspn(bp, " \t");
+ if ('\0' == *cp)
+ break;
+ else if (',' == *cp) {
+ if (0 == k)
+ ll = DEF_RT;
+ else
+ ll = DEF_AT; /* DEF_AT and DEF_TM have same value */
+ } else {
+ ll = sg_get_llnum(bp);
+ if (-1 == ll)
+ ok = false;
+ }
}
- }
- if ((! ok) || (n > UINT16_MAX)) {
- pr2serr("%s: error reading AT (fourth) item on ", __func__);
- return SG_LIB_SYNTAX_ERROR;
- }
- sg_put_unaligned_be32((uint16_t)n, up + 16);
- ok = false;
- cp = strchr(bp, ',');
- if (cp) {
- bp = cp + 1;
- if (*cp) {
- n = sg_get_num(bp);
- if (-1 != ll)
- ok = true;
+ if (! ok) {
+ pr2serr("%s: error reading item %d NUM item on ", __func__,
+ k + 3);
+ break;
}
+ switch (k) {
+ case 0:
+ if (ll > UINT32_MAX) {
+ pr2serr("%s: error with item 3, >0xffffffff; on ", __func__);
+ ok = false;
+ } else
+ sg_put_unaligned_be32((uint32_t)ll, up + 12);
+ break;
+ case 1:
+ if (ll > UINT16_MAX) {
+ pr2serr("%s: error with item 4, >0xffff; on ", __func__);
+ ok = false;
+ } else
+ sg_put_unaligned_be16((uint16_t)ll, up + 16);
+ break;
+ case 2:
+ if (ll > UINT16_MAX) {
+ pr2serr("%s: error with item 5, >0xffff; on ", __func__);
+ ok = false;
+ } else
+ sg_put_unaligned_be16((uint16_t)ll, up + 18);
+ break;
+ default:
+ pr2serr("%s: k=%d should not be >= 3\n", __func__, k);
+ ok = false;
+ break;
+ }
+ if (! ok)
+ break;
}
- if ((! ok) || (n > UINT16_MAX)) {
- pr2serr("%s: error reading TM (fifth) item on ", __func__);
+ if (! ok)
return SG_LIB_SYNTAX_ERROR;
+ for ( ; k < 3; ++k) {
+ switch (k) {
+ case 0:
+ sg_put_unaligned_be32((uint32_t)DEF_RT, up + 12);
+ break;
+ case 1:
+ sg_put_unaligned_be16((uint16_t)DEF_AT, up + 16);
+ break;
+ case 2:
+ sg_put_unaligned_be16((uint16_t)DEF_TM, up + 18);
+ break;
+ default:
+ pr2serr("%s: k=%d should not be >= 3\n", __func__, k);
+ ok = false;
+ break;
+ }
}
- return 0;
+ return ok ? 0 : SG_LIB_SYNTAX_ERROR;
}
/* Read pairs or LBAs and NUMs from a scat_file. A T10 scatter list array is
* built at t10_scat_list_out (e.g. as per T10 the first 32 bytes are zeros
* followed by the first LBA range descriptor (also 32 bytes long) then the
- * second LBA range descriptor, etc. If pi_as_well is false then only LBA,NUM
+ * second LBA range descriptor, etc. If do_16 is true then only LBA,NUM
* pairs are expected, loosely formatted if they are in the scat_file (e.g.
* single line entries alternating LBA and NUM, with an even number of
- * elements. If pa_as_well is true then a stricter format for quintets is
+ * elements. If do_16 is false then a stricter format for quintets is
* expected: on each non comment line should contain: LBA,NUM[,RT,AT,TM] . If
* RT,AT,TM are not given then they assume their defaults (i.e. 0xffffffff,
* 0xffff, 0xffff). Each number (up to 64 bits in size) from command line or
@@ -714,7 +753,7 @@ parse_scat_pi_line(const char * lcp, uint8_t * up, uint32_t * sum_num)
* actual byte length of t10_scat_list_out written into act_list_blen and the
* number of LBA range descriptors written in num_scat_elems . */
static int
-build_t10_scat(const char * scat_fname, bool pi_as_well,
+build_t10_scat(const char * scat_fname, bool do_16,
uint8_t * t10_scat_list_out, uint32_t * act_list_blen,
uint32_t * num_scat_elems, uint32_t * sum_num,
int max_list_blen)
@@ -776,7 +815,7 @@ build_t10_scat(const char * scat_fname, bool pi_as_well,
__func__, scat_fname, j + 1, m + k + 1);
goto bad_exit;
}
- if (pi_as_well) {
+ if (! do_16) {
res = parse_scat_pi_line(lcp, up + n, sum_num);
if (999 == res)
;
@@ -829,7 +868,7 @@ build_t10_scat(const char * scat_fname, bool pi_as_well,
} /* inner for loop(k) over line elements */
off += (k + 1);
} /* outer for loop(j) over lines */
- if ((! pi_as_well) && (0x1 & off)) {
+ if (do_16 && (0x1 & off)) {
pr2serr("%s: expect LBA,NUM pairs but decoded odd number\n from "
"%s\n", __func__, scat_fname);
goto bad_exit;
@@ -852,6 +891,99 @@ is_pi_default(const struct opts_t * op)
(DEF_TM == op->tag_mask));
}
+/* Given a t10 parameter list header (32 zero bytes) for WRITE SCATTERED
+ * (16 or 32) followed by n RDs with a total length of at least
+ * max_lbrds_blen bytes, find "n" and increment where num_lbrds points
+ * n times. Further get the LBA length component from each RD and add each
+ * length into where sum_num points. Note: the caller probably wants to zero
+ * where num_lbrds and sum_num point before invoking this function. If all
+ * goes well return true, else false. If a degenerate RD is detected then
+ * if 'RD' (from --scattered=RD) is 0 then stop looking for further RDs;
+ * otherwise keep going. Currently overlapping LBA range descriptors are no
+ * checked for. If op->strict > 0 then the first 32 bytes are checked for
+ * zeros; any non-zero bytes will report to stderr, stop the check and
+ * return false. If op->strict > 0 then the trailing 20 or 12 bytes (only
+ * 12 if RT, AT and TM fields (for PI) are present) are checked for zeros;
+ * any non-zero bytes cause the same action as the previous check. If
+ * the number of RDs (when 'RD' from --scattered=RD > 0) is greater than
+ * the number of RDs found then a report is sent to stderr and if op->strict
+ * > 0 then returns false, else returns true. */
+static bool
+check_lbrds(const uint8_t * up, int max_lbrds_blen, const struct opts_t * op,
+ uint32_t * num_lbrds, uint32_t * sum_num)
+{
+ bool ok;
+ int k, j, n;
+ const int max_lbrd_start = max_lbrds_blen - 32;
+ int vb = op->verbose;
+
+ if (op->strict) {
+ if (max_lbrds_blen < 32) {
+ pr2serr("%s: logical block range descriptors too short "
+ "(%d < 32)\n", __func__, max_lbrds_blen);
+ return false;
+ }
+ if (! sg_all_zeros(up, 32)) {
+ pr2serr("%s: first 32 bytes of WRITE SCATTERED data-out buffer "
+ "should be zero.\nFound non-zero byte.\n", __func__);
+ return false;
+ }
+ }
+ if (max_lbrds_blen < 64) {
+ *num_lbrds = 0;
+ return true;
+ }
+ n = op->scat_num_lbrd ? -1 : (int)op->scat_num_lbrd;
+ for (k = 32, j = 0; k < max_lbrd_start; k += 32) {
+ if ((n < 0) && sg_all_zeros(up + k + 0, 12)) { /* degenerate LBA */
+ if (vb) /* ... range descriptor terminator if --scattered=0 */
+ pr2serr("%s: degenerate LBA range descriptor stops scan at "
+ "k=%d (RD=0)\n", __func__, k);
+ break;
+ }
+ *sum_num += sg_get_unaligned_be32(up + k + 8);
+ *num_lbrds += 1;
+ if (op->strict) {
+ ok = true;
+ if (op->wrprotect) {
+ if (! sg_all_zeros(up + k + 20, 12))
+ ok = false;
+ } else if (! sg_all_zeros(up + k + 12, 20))
+ ok = false;
+ if (! ok) {
+ pr2serr("%s: LB range descriptor %d non zero in reserved "
+ "fields\n", __func__, (k / 32) - 1);
+ return false;
+ }
+ }
+ ++j;
+ if (n >= 0) {
+ if (--n <= 0)
+ break;
+ }
+ }
+ if ((k < max_lbrd_start) && op->strict) { /* check pad all zeros */
+ k += 32;
+ n = max_lbrds_blen - k;
+ if (! sg_all_zeros(up + k, n)) {
+ pr2serr("%s: pad (%d bytes) following LB range descriptors is "
+ "non zero\n", __func__, n);
+ return false;
+ }
+ }
+ if (vb > 2)
+ pr2serr("%s: about to return true, num_lbrds=%u, sum_num=%u "
+ "[k=%d, n=%d]\n", __func__, *num_lbrds, *sum_num, k, n);
+ if (n > 0) {
+ pr2serr("%s: number of range descriptors found (%d) less than RD "
+ "(%u) given to --scattered=\n", __func__, j,
+ op->scat_num_lbrd);
+ if (op->strict)
+ return false;
+ }
+ return true;
+}
+
static int
do_write_x(int sg_fd, const void * dataoutp, int dout_len,
const struct opts_t * op)
@@ -996,7 +1128,7 @@ do_write_x(int sg_fd, const void * dataoutp, int dout_len,
x_cdb[2] |= 0x1;
}
sg_put_unaligned_be16(op->scat_lbdof, x_cdb + 4);
- sg_put_unaligned_be16(op->scat_num_lbard, x_cdb + 8);
+ sg_put_unaligned_be16(op->scat_num_lbrd, x_cdb + 8);
sg_put_unaligned_be32(op->numblocks, x_cdb + 10);
} else {
@@ -1007,8 +1139,9 @@ do_write_x(int sg_fd, const void * dataoutp, int dout_len,
if (op->fua)
x_cdb[10] |= 0x8;
sg_put_unaligned_be16(op->scat_lbdof, x_cdb + 12);
- sg_put_unaligned_be16(op->scat_num_lbard, x_cdb + 16);
+ sg_put_unaligned_be16(op->scat_num_lbrd, x_cdb + 16);
sg_put_unaligned_be32(op->numblocks, x_cdb + 28);
+ /* ref_tag, app_tag and tag_mask placed in scatter list */
}
} else if (op->do_stream) {
if (16 == cdb_len) {
@@ -1044,6 +1177,29 @@ do_write_x(int sg_fd, const void * dataoutp, int dout_len,
pr2serr("%02x ", x_cdb[k]);
pr2serr("\n");
}
+ if (op->do_scattered && (op->verbose > 2) && (dout_len > 63)) {
+ uint32_t sod_off = op->bs_pi_do * op->scat_lbdof;
+ const uint8_t * up = dataoutp;
+
+ pr2serr(" %s scatter list, number of LBA range descriptors: %u\n",
+ op->cdb_name, op->scat_num_lbrd);
+ pr2serr(" byte offset of data_to_write: %u, dout_len: %d\n",
+ sod_off, dout_len);
+ up += 32; /* step over parameter list header */
+ for (k = 0; k < (int)op->scat_num_lbrd; ++k, up += 32) {
+ pr2serr(" desc %d: LBA=0x%" PRIx64 " numblocks=%" PRIu32
+ "%s", k, sg_get_unaligned_be64(up + 0),
+ sg_get_unaligned_be32(up + 8), (op->do_16 ? "\n" : " "));
+ if (! op->do_16)
+ pr2serr("rt=0x%x at=0x%x tm=0x%x\n",
+ sg_get_unaligned_be32(up + 12),
+ sg_get_unaligned_be16(up + 16),
+ sg_get_unaligned_be16(up + 18));
+ if ((uint32_t)(((k + 2) * 32) + 20) > sod_off)
+ pr2serr("Warning: possible clash of descriptor %u with "
+ "data_to_write\n", k);
+ }
+ }
if ((op->verbose > 3) && (dout_len > 0)) {
pr2serr(" Data-out buffer contents:\n");
dStrHexErr((const char *)dataoutp, op->xfer_bytes, 1);
@@ -1205,7 +1361,7 @@ do_read_capacity(int sg_fd, struct opts_t *op)
#define WANT_ZERO_EXIT 9999
static const char * const opt_long_ctl_str =
- "36a:A:b:B:c:dD:Efg:G:hi:I:l:M:n:No:Oq:rR:sS:t:T:u:vVw:x";
+ "36a:A:b:B:c:dD:Efg:G:hi:I:l:M:n:No:Oq:r:RsS:t:T:u:vVw:x";
/* command line processing, options and arguments. Returns 0 if ok,
* returns WANT_ZERO_EXIT so upper level yields an exist status of zero.
@@ -1379,14 +1535,19 @@ cl_process(struct opts_t *op, int argc, char *argv[], const char ** lba_opp,
pr2serr("bad first argument to '--offset='\n");
return SG_LIB_SYNTAX_ERROR;
}
- op->offset = (uint64_t)ll;
+ op->if_offset = (uint64_t)ll;
if ((cp = strchr(optarg, ','))) {
ll = sg_get_llnum(cp + 1);
if (-1 == ll) {
pr2serr("bad second argument to '--offset='\n");
return SG_LIB_SYNTAX_ERROR;
}
- op->dlen = (uint64_t)ll;
+ if (ll > UINT32_MAX) {
+ pr2serr("bad second argument to '--offset=', cannot "
+ "exceed 32 bits\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ op->if_dlen = (uint32_t)ll;
}
break;
case 'O':
@@ -1396,7 +1557,10 @@ cl_process(struct opts_t *op, int argc, char *argv[], const char ** lba_opp,
case 'q':
op->scat_filename = optarg;
break;
- case 'r':
+ case 'R':
+ op->do_scat_raw = true;
+ break;
+ case 'r': /* same as --ref-tag= */
ll = sg_get_llnum(optarg);
if ((ll < 0) || (ll > UINT32_MAX)) {
pr2serr("bad argument to '--ref-tag='. Expect 0 to "
@@ -1405,15 +1569,6 @@ cl_process(struct opts_t *op, int argc, char *argv[], const char ** lba_opp,
}
op->ref_tag = (uint32_t)ll;
break;
- case 'R': /* same as --ref-tag= */
- ll = sg_get_llnum(optarg);
- if ((ll < 0) || (ll > UINT32_MAX)) {
- pr2serr("bad argument to '--ref-tag='. Expect 0 to 0xffffffff "
- "inclusive\n");
- return SG_LIB_SYNTAX_ERROR;
- }
- op->ref_tag = (uint32_t)ll;
- break;
case 's':
op->strict = true;
break;
@@ -1424,7 +1579,7 @@ cl_process(struct opts_t *op, int argc, char *argv[], const char ** lba_opp,
"inclusive\n");
return SG_LIB_SYNTAX_ERROR;
}
- op->scat_num_lbard = (uint16_t)j;
+ op->scat_num_lbrd = (uint16_t)j;
op->do_scattered = true;
op->cmd_name = "Write scattered";
break;
@@ -1461,7 +1616,7 @@ cl_process(struct opts_t *op, int argc, char *argv[], const char ** lba_opp,
++op->verbose;
break;
case 'V':
- pr2serr(ME "version: %s\n", version_str);
+ pr2serr("sg_write_x version: %s\n", version_str);
return WANT_ZERO_EXIT;
case 'w': /* WRPROTECT field (or ORPROTECT for ORWRITE) */
op->wrprotect = sg_get_num(optarg);
@@ -1506,9 +1661,11 @@ main(int argc, char * argv[])
int sg_fd = -1;
int rsl_fd = -1;
int ret = -1;
- uint32_t addr_arr_len, num_arr_len, num_lbard, do_len;
+ uint32_t addr_arr_len, num_arr_len, do_len, s;
+ uint32_t num_lbrd = 0;
+ uint32_t if_len = 0;
ssize_t res;
- off_t if_len = 0;
+ off_t if_tot_len = 0;
struct opts_t * op;
unsigned char * wBuff = NULL;
const char * lba_op = NULL;
@@ -1528,10 +1685,11 @@ main(int argc, char * argv[])
op->numblocks = DEF_WR_NUMBLOCKS;
op->pi_type = -1; /* Protection information type unknown */
op->ref_tag = DEF_RT; /* first 4 bytes of 8 byte protection info */
- op->app_tag = DEF_AT; /* part of protection information */
- op->tag_mask = DEF_TM; /* part of protection information */
+ op->app_tag = DEF_AT; /* 2 bytes of protection information */
+ op->tag_mask = DEF_TM; /* final 2 bytes of protection information */
op->timeout = DEF_TIMEOUT_SECS;
+ /* Process command line */
ret = cl_process(op, argc, argv, &lba_op, &num_op);
if (ret) {
if (WANT_ZERO_EXIT == ret)
@@ -1543,6 +1701,7 @@ main(int argc, char * argv[])
return 0;
}
vb = op->verbose;
+ /* sanity checks */
if ((! op->do_16) && (! op->do_32)) {
op->do_16 = true;
if (vb > 1)
@@ -1566,8 +1725,23 @@ main(int argc, char * argv[])
}
snprintf(op->cdb_name, sizeof(op->cdb_name), "%s(%d)", op->cmd_name,
(op->do_16 ? 16 : 32));
+ if (op->do_combined) {
+ if (! op->do_scattered) {
+ pr2serr("--combined=DOF only allowed with --scattered=RD (i.e. "
+ "only with\nWRITE SCATTERED command)\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ if (lba_op || num_op) {
+ pr2serr("--scattered=RD --combined=DOF does not use --lba= or "
+ "--num=\nPlease remove.\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+ /* examine .if_name . Open, move to .if_offset, calculate length that we
+ * want to read. */
if (! op->ndob) {
+ if_len = op->if_dlen;
if (NULL == op->if_name) {
pr2serr("Need --if=FN option to be given, exiting.\n");
if (vb > 1)
@@ -1599,16 +1773,17 @@ main(int argc, char * argv[])
}
got_stat = true;
if (S_ISREG(if_stat.st_mode))
- if_len = if_stat.st_size;
+ if_tot_len = if_stat.st_size;
}
- if (got_stat && if_len && ((int64_t)op->offset >= (if_len - 1))) {
+ if (got_stat && if_tot_len &&
+ ((int64_t)op->if_offset >= (if_tot_len - 1))) {
pr2serr("Offset (%" PRIu64 ") is at or beyond IF byte length (%"
- PRIu64 ")\n", op->offset, if_len);
+ PRIu64 ")\n", op->if_offset, if_tot_len);
ret = SG_LIB_FILE_ERROR;
goto err_out;
}
- if (op->offset > 0) {
- off_t off = op->offset;
+ if (op->if_offset > 0) {
+ off_t off = op->if_offset;
if (got_stdin) {
if (vb)
@@ -1623,17 +1798,27 @@ main(int argc, char * argv[])
ret = SG_LIB_FILE_ERROR;
goto err_out;
}
- if_len -= op->offset;
- if (if_len <= 0) {
+ if_tot_len -= op->if_offset;
+ if (if_tot_len <= 0) {
pr2serr("--offset [0x%" PRIx64 "] at or beyond file "
- "length[0x%" PRIx64 "]\n", (uint64_t)op->offset,
- (uint64_t)if_len);
+ "length[0x%" PRIx64 "]\n",
+ (uint64_t)op->if_offset, (uint64_t)if_tot_len);
ret = SG_LIB_FILE_ERROR;
goto err_out;
}
+ if_len = (uint32_t)((if_tot_len < (off_t)op->if_dlen) ?
+ if_tot_len : (off_t)op->if_dlen);
}
}
if (0 != (if_len % op->bs_pi_do)) {
+ if (op->strict) {
+ pr2serr("Error: number of bytes to read from IF [%u] is "
+ "not a multiple\nblock size %u (including"
+ "protection information\n", (unsigned int)if_len,
+ op->bs_pi_do);
+ ret = SG_LIB_FILE_ERROR;
+ goto err_out;
+ }
pr2serr("Warning: number of bytes to read from IF [%u] is not a "
"multiple\nblock size %u (including protection "
"information, if any);\npad with zeros",
@@ -1642,32 +1827,26 @@ main(int argc, char * argv[])
op->bs_pi_do; /* round up */
}
}
+ /* A bit more sanity */
if (NULL == op->device_name) {
pr2serr("missing device name!\n");
usage((op->help > 0) ? op->help : 0);
ret = SG_LIB_SYNTAX_ERROR;
goto err_out;
}
- if (op->scat_filename && (lba_op || num_op)) {
- pr2serr("expect '--scat-file=' by itself, or both '--lba=' and "
- "'--num='\n");
- ret = SG_LIB_SYNTAX_ERROR;
- goto err_out;
- } else if (op->scat_filename || (lba_op && num_op))
- ; /* we want this path */
- else {
- if (lba_op)
- pr2serr("since '--lba=' is given, also need '--num='\n");
- else
- pr2serr("expect either both '--lba=' and '--num=', or "
- "'--scat-file=' by itself\n");
+ n = (!! op->scat_filename) + (!! (lba_op || num_op)) +
+ (!! op->do_combined);
+ if (1 != n) {
+ pr2serr("want one and only one of: (--lba=LBA or --num=NUM), or "
+ "--scat-file=SF,\nor --combined=DOF\n");
ret = SG_LIB_SYNTAX_ERROR;
goto err_out;
}
+ /* Open device file, do READ CAPACITY(16, maybe 10) if no BS */
sg_fd = sg_cmds_open_device(op->device_name, false /* rw */, vb);
if (sg_fd < 0) {
- pr2serr(ME "open error: %s: %s\n", op->device_name,
+ pr2serr("open error: %s: %s\n", op->device_name,
safe_strerror(-sg_fd));
return SG_LIB_FILE_ERROR;
}
@@ -1677,6 +1856,7 @@ main(int argc, char * argv[])
goto err_out;
}
+ /* decode --lba= and --num= options */
memset(addr_arr, 0, sizeof(addr_arr));
memset(num_arr, 0, sizeof(num_arr));
addr_arr_len = 0;
@@ -1687,6 +1867,8 @@ main(int argc, char * argv[])
ret = SG_LIB_SYNTAX_ERROR;
goto err_out;
}
+ if (addr_arr_len > 0)
+ op->explicit_lba = true;
if (0 != build_num_arr(num_op, num_arr, &num_arr_len,
MAX_NUM_ADDR)) {
pr2serr("bad argument to '--num'\n");
@@ -1705,6 +1887,7 @@ main(int argc, char * argv[])
uint32_t sum_num = 0;
do_len = 0;
+ /* if WRITE SCATTERED check for --scat-file=SF, if so state(SF) */
if (op->scat_filename) {
if (op->do_combined) {
pr2serr("Ambiguous: got --combined=DOF and --scat-file=SF "
@@ -1720,31 +1903,34 @@ main(int argc, char * argv[])
goto err_out;
}
}
- if (op->do_combined && op->do_raw) {
- pr2serr("Ambiguous: do expect --combined=DOF and --raw\n"
+ /* some WRITE SCATTERED sanity checks */
+ if (op->do_combined && op->do_scat_raw) {
+ pr2serr("Ambiguous: do expect --combined=DOF and --scat-raw\n"
"Give one or the other\n");
ret = SG_LIB_SYNTAX_ERROR;
goto err_out;
}
- if ((NULL == op->scat_filename) && op->do_raw) {
- pr2serr("--raw only applies to the --scat-file=SF option\n"
- "Give both or neither\n");
+ if ((NULL == op->scat_filename) && op->do_scat_raw) {
+ pr2serr("--scat-raw only applies to the --scat-file=SF option\n"
+ "--scat-raw without the --scat-file=SF option is an "
+ "error\n");
ret = SG_LIB_SYNTAX_ERROR;
goto err_out;
}
- if ((addr_arr_len > 0) && (op->scat_num_lbard > 0) &&
- (op->scat_num_lbard < addr_arr_len)) {
+ if ((addr_arr_len > 0) && (op->scat_num_lbrd > 0) &&
+ (op->scat_num_lbrd < addr_arr_len)) {
pr2serr("less LBA,NUM pairs (%d )than --scattered=%d\n",
- addr_arr_len, op->scat_num_lbard);
+ addr_arr_len, op->scat_num_lbrd);
ret = SG_LIB_SYNTAX_ERROR;
goto err_out;
}
- num_lbard = (addr_arr_len > 0) ? addr_arr_len : op->scat_num_lbard;
- if (num_lbard < 15)
- num_lbard = 15; /* 32 byte leadin, 15 32 byte LRD = 512 bytes */
- if (op->do_combined)
+ num_lbrd = (addr_arr_len > 0) ? addr_arr_len : op->scat_num_lbrd;
+ if (num_lbrd < 15)
+ num_lbrd = 15; /* 32 byte leadin, 15 32 byte LRD = 512 bytes */
+ if (op->do_combined) {
goto skip_scat_build;
- if (op->do_raw) {
+ }
+ if (op->do_scat_raw) {
if (S_ISREG(sf_stat.st_mode)) {
do_len = sf_stat.st_size;
d = sf_stat.st_size / 32;
@@ -1756,13 +1942,13 @@ main(int argc, char * argv[])
}
if (sf_stat.st_size % 32)
d += 1; /* round up, will zero pad unfinished RD */
- if (op->scat_num_lbard) {
- if (op->scat_num_lbard != (d - 1)) {
+ if (op->scat_num_lbrd) {
+ if (op->scat_num_lbrd != (d - 1)) {
pr2serr("Command line RD (%u) contradicts value "
"calculated from raw SF (%u)\n",
- op->scat_num_lbard, d - 1);
- if (op->scat_num_lbard < (d - 1))
- d = op->scat_num_lbard + 1;
+ op->scat_num_lbrd, d - 1);
+ if (op->scat_num_lbrd < (d - 1))
+ d = op->scat_num_lbrd + 1;
else {
pr2serr("Command line RD greater than raw SF "
"file length implies, exit\n");
@@ -1772,14 +1958,16 @@ main(int argc, char * argv[])
}
}
} else {
- pr2serr("--scat-file= --raw wants regular file for length\n");
+ pr2serr("--scat-file= --scat-raw wants regular file for "
+ "length\n");
ret = SG_LIB_FILE_ERROR;
goto err_out;
}
- num_lbard = d;
+ num_lbrd = d;
}
- do_len = (1 + num_lbard) * 32;
+ /* Calculations to work out initial dout length */
+ do_len = (1 + num_lbrd) * 32;
op->scat_lbdof = do_len / op->bs_pi_do;
if (0 != (do_len % op->bs_pi_do)) { /* if not multiple, round up */
op->scat_lbdof += 1;
@@ -1789,13 +1977,13 @@ main(int argc, char * argv[])
do_len += (uint32_t)if_len;
} else { /* IF is stdin, a pipe or a device (special) ... */
op->xfer_bytes = _SC_PAGE_SIZE; /* ... so need length */
- if (op->bs_pi_do > (op->xfer_bytes / 2))
+ if (op->bs_pi_do > ((uint32_t)op->xfer_bytes / 2))
op->xfer_bytes = op->bs_pi_do * 3;
- else if (do_len >= (op->xfer_bytes / 2)) {
+ else if (do_len >= ((uint32_t)op->xfer_bytes / 2)) {
op->xfer_bytes *= 4;
- if (do_len >= (op->xfer_bytes / 2)) {
+ if (do_len >= ((uint32_t)op->xfer_bytes / 2)) {
op->xfer_bytes *= 4;
- if (do_len >= (op->xfer_bytes / 2)) {
+ if (do_len >= ((uint32_t)op->xfer_bytes / 2)) {
pr2serr("Giving up guessing big enough buffers, "
"please use --offset=OFF,DLEN\n");
ret = SG_LIB_SYNTAX_ERROR;
@@ -1805,8 +1993,8 @@ main(int argc, char * argv[])
}
do_len = op->xfer_bytes;
}
- if (0 != (do_len % op->bs_pi_do)) /* round up */
- do_len = ((do_len / op->bs_pi_do) + 1) * op->bs_pi_do;
+ if (0 != (do_len % op->bs_pi_do)) /* round up */
+ do_len = ((do_len / op->bs_pi_do) + 1) * op->bs_pi_do;
if (do_len < op->bs_pi_do) {
pr2serr("failed calculating data-out buffer size (%u)\n",
do_len);
@@ -1825,7 +2013,8 @@ main(int argc, char * argv[])
ret = SG_LIB_OS_BASE_ERR + ENOMEM;
goto err_out;
}
- if (op->do_raw) {
+
+ if (op->do_scat_raw) {
rsl_fd = open(op->scat_filename, O_RDONLY);
if (rsl_fd < 0) {
err = errno;
@@ -1861,12 +2050,17 @@ main(int argc, char * argv[])
close(rsl_fd);
rsl_fd = -1;
} else if (op->scat_filename) {
- ret = build_t10_scat(op->scat_filename, op->expect_pi_do, up, &d,
- &num_lbard, &sum_num,
+ ret = build_t10_scat(op->scat_filename, op->do_16, up, &d,
+ &num_lbrd, &sum_num,
op->scat_lbdof * op->bs_pi_do);
if (ret)
goto err_out;
+ if (num_lbrd > 0)
+ op->explicit_lba = true;
op->numblocks = sum_num;
+ if (vb > 1)
+ pr2serr("After build_t10_scat(): num_lbrd=%u sum_num=%u\n",
+ num_lbrd, sum_num);
} else if (addr_arr_len > 0) { /* build RDs for --addr= --num= */
for (n = 32, k = 0; k < (int)addr_arr_len; ++k, n += 32) {
sg_put_unaligned_be64(addr_arr[k], up + n + 0);
@@ -1886,14 +2080,30 @@ main(int argc, char * argv[])
}
op->numblocks = sum_num;
}
- /* now read data to write component into up */
+ /* now read data to write component into 'up' */
d = op->scat_lbdof * op->bs_pi_do;
- if (d > op->xfer_bytes) {
+ if (d > (uint32_t)op->xfer_bytes) {
pr2serr("Logic error in scattered, read data into buffer "
"(d=%u)\n", d);
ret = SG_LIB_FILE_ERROR;
goto err_out;
}
+ s = op->scat_lbdof + op->numblocks;
+ if ((uint32_t)op->xfer_bytes < (s * op->bs_pi_do)) {
+ uint8_t * u2p;
+
+ u2p = calloc(s, op->bs_pi_do);
+ if (NULL == u2p) {
+ pr2serr("unable to allocate memory for final "
+ "scatterlist+data\n");
+ ret = SG_LIB_OS_BASE_ERR + ENOMEM;
+ goto err_out;
+ }
+ memcpy(u2p, up, d);
+ free(up);
+ up = u2p;
+ op->xfer_bytes = s * op->bs_pi_do;
+ }
res = read(infd, up + d, op->xfer_bytes - d);
d = op->xfer_bytes - d;
if (res < 0) {
@@ -1902,7 +2112,7 @@ main(int argc, char * argv[])
ret = SG_LIB_FILE_ERROR;
goto err_out;
}
- if (res < d) {
+ if ((uint32_t)res < d) {
pr2serr("Short (%u) read of IF file, wanted %u\n",
(unsigned int)res, d);
ret = SG_LIB_FILE_ERROR;
@@ -1919,7 +2129,35 @@ skip_scat_build: // needs more XXXXXXXXX xxxxxx ???
if (op->do_same)
op->xfer_bytes = 1 * op->bs_pi_do;
else if (op->do_scattered) {
- ; /* already done, scatter_list+data waiting in 'up' */
+ if (op->do_combined) {
+ int up_len;
+ uint32_t sum_num;
+
+ if ((if_len < 32) || (op->bs_pi_do < 32)) {
+ pr2serr("Logic error combined calloc should be > %u, "
+ "bs_pi_do=%u\n", (uint32_t)if_len, op->bs_pi_do);
+ ret = SG_LIB_FILE_ERROR;
+ goto err_out;
+ }
+ /* assume if_len % op->bs_pi_do is zero (i.e. no remainder) */
+ up = calloc(if_len / op->bs_pi_do, op->bs_pi_do);
+ if (NULL == up) {
+ pr2serr("unable to allocate memory for combined\n");
+ ret = SG_LIB_OS_BASE_ERR + ENOMEM;
+ goto err_out;
+ }
+ up_len = (op->scat_lbdof > 0) ? (op->scat_lbdof * op->bs_pi_do) :
+ if_len;
+ num_lbrd = 0;
+ sum_num = 0;
+ if (! check_lbrds(up, up_len, op, &num_lbrd, &sum_num)) {
+ ret = SG_LIB_FILE_ERROR;
+ goto err_out;
+ }
+ op->numblocks = sum_num;
+ } else {
+ ; /* already done, scatter_list+data waiting in 'up' */
+ }
} else
op->xfer_bytes = op->numblocks * op->bs_pi_do;