aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog16
-rw-r--r--config.h.in14
-rwxr-xr-xconfigure191
-rw-r--r--configure.ac22
-rw-r--r--debian/changelog2
-rw-r--r--doc/sg_format.84
-rw-r--r--doc/sg_inq.827
-rw-r--r--doc/sg_ses.810
-rw-r--r--doc/sg_write_x.872
-rw-r--r--doc/sgp_dd.89
-rw-r--r--include/sg_lib.h41
-rw-r--r--include/sg_lib_data.h10
-rw-r--r--include/sg_pt_linux.h7
-rw-r--r--include/sg_pt_nvme.h72
-rw-r--r--include/sg_unaligned.h4
-rw-r--r--lib/sg_lib.c189
-rw-r--r--lib/sg_lib_data.c177
-rw-r--r--lib/sg_pt_freebsd.c955
-rw-r--r--lib/sg_pt_linux.c13
-rw-r--r--lib/sg_pt_linux_nvme.c328
-rw-r--r--sg3_utils.spec2
-rw-r--r--src/sg_inq.c132
-rw-r--r--src/sg_logs.c3
-rw-r--r--src/sg_ses.c148
-rw-r--r--src/sg_write_x.c136
-rw-r--r--src/sgp_dd.c26
26 files changed, 1903 insertions, 707 deletions
diff --git a/ChangeLog b/ChangeLog
index 91dfb821..38d3aa8a 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 [20171219] [svn: r737]
+Changelog for sg3_utils-1.43 [20171229] [svn: r738]
- 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
@@ -23,6 +23,9 @@ Changelog for sg3_utils-1.43 [20171219] [svn: r737]
sensor element type
- interpret '--join --page=aes' to only display join
rows that have a corresponding AES dpage element
+ - support NVMe attached enclosure via NVME-MI Send and
+ Receive SES commands
+ - decode array status diagnostic page (obsolete)
- sync to ses4r01
- sg_ses, sg_ses_microcode, sg_senddiag: make all access
buffer page size aligned (typically page_size=4096)
@@ -62,6 +65,8 @@ Changelog for sg3_utils-1.43 [20171219] [svn: r737]
- sg_turs+sg_requests: make both accept '--num=NUM'
and '--number=NUM' for mutual compatibility
- sg_zone: fix debug cdb naming
+ - sgp_dd: if SG_LIB_ANDROID defined invoke
+ pthread_kill() rather than pthread_cancel() [Linux]
- sg_opcode: add '--enumerate' and '--pdt=' options
- support CDLP (command duration limit page)
- check resid and trim response if necessary
@@ -94,6 +99,10 @@ Changelog for sg3_utils-1.43 [20171219] [svn: r737]
- add sg_decode_transportid_str()
- add sg_msense_calc_length()
- add sg_all_zeros(), sg_all_ffs()
+ - add sg_get_sense_cmd_spec_fld()
+ - add sg_is_scsi_cdb()
+ - add sg_get_nvme_cmd_status_str()
+ - add sg_nvme_status2scsi()
- add sg_memalign() and sg_get_page_size()
- implement 'format' argument in dStrHexStr()
- add read buffer(16) command mode names
@@ -103,7 +112,7 @@ Changelog for sg3_utils-1.43 [20171219] [svn: r737]
- sg_pt: add construct_scsi_pt_obj_with_fd()
- add pt_device_is_nvme(), get_pt_nvme_nsid()
- add check_pt_file_handle()
- - add get_pt_file_handle(), get_pt_file_handle()
+ - add get_pt_file_handle(), set_pt_file_handle()
- sg_lib_data: sync asc/ascq codes with T10 20170114
- add write scattered (16+32) cdb names sbc4r11
- sg_cmds_extra: expand sg_ll_ata_pt() to send new
@@ -128,6 +137,9 @@ Changelog for sg3_utils-1.43 [20171219] [svn: r737]
decision made at runtime
- automake: add AM_PROG_AR to configure.ac
- upgrade to version 1.15
+ - add SG_LIB_ANDROID build 'define'. If defined then
+ SG_LIB_LINUX is also defined, so test for Android
+ before Linux if need to differentiate
Changelog for sg3_utils-1.42 [20160217] [svn: r663]
- sg_timestamp: new, to report or set timestamp
diff --git a/config.h.in b/config.h.in
index 12379ef9..b33212f4 100644
--- a/config.h.in
+++ b/config.h.in
@@ -36,6 +36,12 @@
/* Define to 1 if you have the `posix_memalign' function. */
#undef HAVE_POSIX_MEMALIGN
+/* Found pthread_cancel */
+#undef HAVE_PTHREAD_CANCEL
+
+/* Found pthread_kill */
+#undef HAVE_PTHREAD_KILL
+
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
@@ -63,6 +69,9 @@
/* option ignored */
#undef IGNORE_LINUX_BSG
+/* compile out NVMe support */
+#undef IGNORE_NVME
+
/* Define to the sub-directory where libtool stores uninstalled libraries. */
#undef LT_OBJDIR
@@ -87,6 +96,9 @@
/* Define to the version of this package. */
#undef PACKAGE_VERSION
+/* sg3_utils on android */
+#undef SG_LIB_ANDROID
+
/* sg3_utils Build Host */
#undef SG_LIB_BUILD_HOST
@@ -108,7 +120,7 @@
/* sg3_utils on Win32 */
#undef SG_LIB_WIN32
-/* full SCSI sense strings */
+/* full SCSI sense strings and NVMe status strings */
#undef SG_SCSI_STRINGS
/* Define to 1 if you have the ANSI C header files. */
diff --git a/configure b/configure
index 4ab0fc69..79815dd9 100755
--- a/configure
+++ b/configure
@@ -635,6 +635,8 @@ ac_subst_vars='am__EXEEXT_FALSE
am__EXEEXT_TRUE
LTLIBOBJS
LIBOBJS
+OS_ANDROID_FALSE
+OS_ANDROID_TRUE
OS_WIN32_CYGWIN_FALSE
OS_WIN32_CYGWIN_TRUE
OS_WIN32_MINGW_FALSE
@@ -784,6 +786,7 @@ enable_libtool_lock
enable_linuxbsg
enable_win32_spt_direct
enable_scsistrings
+enable_nvme_supp
'
ac_precious_vars='build_alias
host_alias
@@ -1441,7 +1444,9 @@ Optional Features:
--disable-linuxbsg option ignored, this is placeholder
--enable-win32-spt-direct
enable Win32 SPT Direct
- --disable-scsistrings Disable full SCSI sense strings
+ --disable-scsistrings Disable full SCSI sense strings and NVMe status
+ strings
+ --disable-nvme-supp remove all or most NVMe code
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
@@ -12355,6 +12360,122 @@ _ACEOF
fi
done
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing pthread_cancel" >&5
+$as_echo_n "checking for library containing pthread_cancel... " >&6; }
+if ${ac_cv_search_pthread_cancel+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_cancel ();
+int
+main ()
+{
+return pthread_cancel ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' pthread; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_pthread_cancel=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_pthread_cancel+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_pthread_cancel+:} false; then :
+
+else
+ ac_cv_search_pthread_cancel=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_pthread_cancel" >&5
+$as_echo "$ac_cv_search_pthread_cancel" >&6; }
+ac_res=$ac_cv_search_pthread_cancel
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+$as_echo "#define HAVE_PTHREAD_CANCEL 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing pthread_kill" >&5
+$as_echo_n "checking for library containing pthread_kill... " >&6; }
+if ${ac_cv_search_pthread_kill+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_kill ();
+int
+main ()
+{
+return pthread_kill ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' pthread; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_pthread_kill=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_pthread_kill+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_pthread_kill+:} false; then :
+
+else
+ ac_cv_search_pthread_kill=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_pthread_kill" >&5
+$as_echo "$ac_cv_search_pthread_kill" >&6; }
+ac_res=$ac_cv_search_pthread_kill
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+$as_echo "#define HAVE_PTHREAD_KILL 1" >>confdefs.h
+
+fi
+
@@ -12448,6 +12569,52 @@ fi
done
;;
+ *-*-android*)
+
+cat >>confdefs.h <<_ACEOF
+#define SG_LIB_ANDROID 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define SG_LIB_LINUX 1
+_ACEOF
+
+ os_cflags=''
+
+ os_libs=''
+
+ for ac_header in linux/nvme_ioctl.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "linux/nvme_ioctl.h" "ac_cv_header_linux_nvme_ioctl_h" "$ac_includes_default"
+if test "x$ac_cv_header_linux_nvme_ioctl_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LINUX_NVME_IOCTL_H 1
+_ACEOF
+
+$as_echo "#define HAVE_NVME 1" >>confdefs.h
+
+fi
+
+done
+
+ for ac_header in linux/types.h linux/bsg.h linux/kdev_t.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "#ifdef HAVE_LINUX_TYPES_H
+ # include <linux/types.h>
+ #endif
+
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+ ;;
*-*-freebsd*|*-*-kfreebsd*-gnu*)
cat >>confdefs.h <<_ACEOF
@@ -12598,6 +12765,14 @@ else
OS_WIN32_CYGWIN_FALSE=
fi
+ if echo $host_os | grep 'android' > /dev/null; then
+ OS_ANDROID_TRUE=
+ OS_ANDROID_FALSE='#'
+else
+ OS_ANDROID_TRUE='#'
+ OS_ANDROID_FALSE=
+fi
+
# Check whether --enable-linuxbsg was given.
if test "${enable_linuxbsg+set}" = set; then :
@@ -12632,6 +12807,16 @@ _ACEOF
fi
+# Check whether --enable-nvme-supp was given.
+if test "${enable_nvme_supp+set}" = set; then :
+ enableval=$enable_nvme_supp;
+cat >>confdefs.h <<_ACEOF
+#define IGNORE_NVME 1
+_ACEOF
+
+fi
+
+
ac_config_files="$ac_config_files Makefile include/Makefile lib/Makefile src/Makefile doc/Makefile scripts/Makefile"
cat >confcache <<\_ACEOF
@@ -12794,6 +12979,10 @@ if test -z "${OS_WIN32_CYGWIN_TRUE}" && test -z "${OS_WIN32_CYGWIN_FALSE}"; then
as_fn_error $? "conditional \"OS_WIN32_CYGWIN\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
+if test -z "${OS_ANDROID_TRUE}" && test -z "${OS_ANDROID_FALSE}"; then
+ as_fn_error $? "conditional \"OS_ANDROID\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
: "${CONFIG_STATUS=./config.status}"
ac_write_fail=0
diff --git a/configure.ac b/configure.ac
index c32e0585..1cce0b25 100644
--- a/configure.ac
+++ b/configure.ac
@@ -25,6 +25,8 @@ AC_CHECK_FUNCS(posix_fadvise)
AC_CHECK_FUNCS(posix_memalign)
AC_CHECK_FUNCS(sysconf)
AC_CHECK_FUNCS(lseek64)
+AC_SEARCH_LIBS([pthread_cancel], [pthread], [AC_DEFINE(HAVE_PTHREAD_CANCEL, 1, [Found pthread_cancel])], [])
+AC_SEARCH_LIBS([pthread_kill], [pthread], [AC_DEFINE(HAVE_PTHREAD_KILL, 1, [Found pthread_kill])], [])
AC_SUBST(GETOPT_O_FILES)
AC_CANONICAL_HOST
@@ -52,6 +54,17 @@ case "${host}" in
# include <linux/types.h>
#endif
]]) ;;
+ *-*-android*)
+ AC_DEFINE_UNQUOTED(SG_LIB_ANDROID, 1, [sg3_utils on android])
+ AC_DEFINE_UNQUOTED(SG_LIB_LINUX, 1, [sg3_utils on linux])
+ AC_SUBST([os_cflags], [''])
+ AC_SUBST([os_libs], [''])
+ AC_CHECK_HEADERS([linux/nvme_ioctl.h], [AC_DEFINE(HAVE_NVME, 1, [Found NVMe])], [], [])
+ AC_CHECK_HEADERS([linux/types.h linux/bsg.h linux/kdev_t.h], [], [],
+ [[#ifdef HAVE_LINUX_TYPES_H
+ # include <linux/types.h>
+ #endif
+ ]]) ;;
*-*-freebsd*|*-*-kfreebsd*-gnu*)
AC_DEFINE_UNQUOTED(SG_LIB_FREEBSD, 1, [sg3_utils on FreeBSD])
AC_DEFINE(HAVE_NVME, 1, ['Found NVMe'])
@@ -93,6 +106,7 @@ AM_CONDITIONAL(OS_OSF, [echo $host_os | grep '^osf' > /dev/null])
AM_CONDITIONAL(OS_SOLARIS, [echo $host_os | grep '^solaris' > /dev/null])
AM_CONDITIONAL(OS_WIN32_MINGW, [echo $host_os | grep '^mingw' > /dev/null])
AM_CONDITIONAL(OS_WIN32_CYGWIN, [echo $host_os | grep '^cygwin' > /dev/null])
+AM_CONDITIONAL(OS_ANDROID, [echo $host_os | grep 'android' > /dev/null])
AC_ARG_ENABLE([linuxbsg],
AC_HELP_STRING([--disable-linuxbsg], [option ignored, this is placeholder]),
@@ -105,7 +119,11 @@ AC_ARG_ENABLE([win32-spt-direct],
AC_ARG_ENABLE([scsistrings],
[AS_HELP_STRING([--disable-scsistrings],
- [Disable full SCSI sense strings])],
- [], [AC_DEFINE_UNQUOTED(SG_SCSI_STRINGS, 1, [full SCSI sense strings], )])
+ [Disable full SCSI sense strings and NVMe status strings])],
+ [], [AC_DEFINE_UNQUOTED(SG_SCSI_STRINGS, 1, [full SCSI sense strings and NVMe status strings], )])
+
+AC_ARG_ENABLE([nvme-supp],
+ AC_HELP_STRING([--disable-nvme-supp], [remove all or most NVMe code]),
+ [AC_DEFINE_UNQUOTED(IGNORE_NVME, 1, [compile out NVMe support], )], [])
AC_OUTPUT(Makefile include/Makefile lib/Makefile src/Makefile doc/Makefile scripts/Makefile)
diff --git a/debian/changelog b/debian/changelog
index 5dbcd649..3f948278 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,7 +2,7 @@ sg3-utils (1.43-0.1) unstable; urgency=low
* New upstream version
- -- Douglas Gilbert <dgilbert@interlog.com> Mon, 18 Dec 2017 14:00:00 -0500
+ -- Douglas Gilbert <dgilbert@interlog.com> Fri, 29 Dec 2017 10:00:00 -0500
sg3-utils (1.42-0.1) unstable; urgency=low
diff --git a/doc/sg_format.8 b/doc/sg_format.8
index 8733f797..449fa605 100644
--- a/doc/sg_format.8
+++ b/doc/sg_format.8
@@ -428,8 +428,8 @@ The SBC\-2 standard states that the block count can be set back to the
manufacturer's maximum recommended value in a format or resize operation.
This can be done by placing an address of 0xffffffff (or the 64 bit
equivalent) in the appropriate block descriptor field to a MODE SELECT
-command. In signed (two's complement) arithmetic that value corresponds to
-'\-1'. So a \fI\-\-count=\fR\-1 causes the block count to be set back to
+command. In signed (two's complement) arithmetic that value corresponds
+to '\-1'. So a \-\-count=\-1 causes the block count to be set back to
the manufacturer's maximum recommended value. To see exactly which SCSI
commands are being executed and parameters passed add the "\-vvv" option to
the sg_format command line.
diff --git a/doc/sg_inq.8 b/doc/sg_inq.8
index 8a4c84bd..ef8b77f1 100644
--- a/doc/sg_inq.8
+++ b/doc/sg_inq.8
@@ -1,4 +1,4 @@
-.TH SG_INQ "8" "September 2017" "sg3_utils\-1.43" SG3_UTILS
+.TH SG_INQ "8" "December 2017" "sg3_utils\-1.43" SG3_UTILS
.SH NAME
sg_inq \- issue SCSI INQUIRY command and/or decode its response
.SH SYNOPSIS
@@ -6,15 +6,15 @@ sg_inq \- issue SCSI INQUIRY command and/or decode its response
[\fI\-\-ata\fR] [\fI\-\-block=0|1\fR] [\fI\-\-cmddt\fR]
[\fI\-\-descriptors\fR] [\fI\-\-export\fR] [\fI\-\-extended\fR]
[\fI\-\-force\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-id\fR]
-[\fI\-\-inhex=FN\fR] [\fI\-\-len=LEN\fR] [\fI\-\-maxlen=LEN\fR]
-[\fI\-\-page=PG\fR] [\fI\-\-raw\fR] [\fI\-\-vendor\fR] [\fI\-\-verbose\fR]
-[\fI\-\-version\fR] [\fI\-\-vpd\fR] \fIDEVICE\fR
+[\fI\-\-inhex=FN\fR] [\fI\-\-len=LEN\fR] [\fI\-\-long\fR]
+[\fI\-\-maxlen=LEN\fR] [\fI\-\-page=PG\fR] [\fI\-\-raw\fR] [\fI\-\-vendor\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] [\fI\-\-vpd\fR] \fIDEVICE\fR
.PP
.B sg_inq
[\fI\-36\fR] [\fI\-a\fR] [\fI\-A\fR] [\fI\-b\fR] [\fI\-\-B=0|1\fR]
[\fI\-c\fR] [\fI\-cl\fR] [\fI\-d\fR] [\fI\-e\fR] [\fI\-f\fR]
[\fI\-h\fR] [\fI\-H\fR] [\fI\-i\fR] [\fI\-I=FN\fR] [\fI\-l=LEN\fR]
-[\fI\-m\fR] [\fI\-M\fR] [\fI\-o=OPCODE_PG\fR] [\fI\-p=VPD_PG\fR]
+[\fI\-L\fR] [\fI\-m\fR] [\fI\-M\fR] [\fI\-o=OPCODE_PG\fR] [\fI\-p=VPD_PG\fR]
[\fI\-P\fR] [\fI\-r\fR] [\fI\-s\fR] [\fI\-u\fR] [\fI\-v\fR]
[\fI\-V\fR] [\fI\-x\fR] [\fI\-36\fR] [\fI\-?\fR] \fIDEVICE\fR
.SH DESCRIPTION
@@ -48,6 +48,13 @@ tried. If it succeeds then device identification strings are output. The
If the \fI\-\-ata\fR option is given then the SCSI INQUIRY is not performed
and the \fIDEVICE\fR is assumed to be ATA (or ATAPI).
.PP
+In some operating systems a NVMe device (e.g. SSD) may be given as the
+\fIDEVICE\fR. An Identify command is sent to the controller followed
+by an namespace if \fIDEVICE\fR is associated with a namespace. If not,
+for example if \fIDEVICE\fR corresponds to a controller, then an Identify
+is sent to the controller and then an Identify coomand is sent to all
+attached namespaces.
+.PP
The reference document used for interpreting an INQUIRY is T10/BSR INCITS
502 Revision 07 which is draft SPC\-5 revision 07, 26 November 2015). It can
be found at http://www.t10.org . Obsolete and reserved items in the standard
@@ -164,6 +171,10 @@ length" field in the response indicates that more than 36 bytes is available.
If \fILEN\fR is greater than 0 then only one INQUIRY command is performed.
See paragraph below about "36 byte INQUIRYs".
.TP
+\fB\-L\fR, \fB\-\-long\fR
+this option causes more information to be decoded from the Identify command
+sent to a NVMe \fIDEVICE\fR.
+.TP
\fB\-m\fR, \fB\-\-maxlen\fR=\fILEN\fR
this option has the same action as the \fI\-\-len=LEN\fR option. It has
been added for compatibility with the sg_vpd, sg_modes and sg_logs
@@ -352,6 +363,12 @@ response in hex use '\-p=83 \-h'.
\fB\-I\fR=\fIFN\fR
equivalent to \fI\-\-inhex=FN\fR in the OPTIONS section.
.TP
+\fB\-l\fR=\fILEN\fR
+equivalent to \fI\-\-len=LEN\fR in the OPTIONS section.
+.TP
+\fB\-L\fR
+equivalent to \fI\-\-long\fR in the OPTIONS section.
+.TP
\fB\-m\fR
decodes the Management network addresses VPD page [0x85]. Equivalent
to '\-\-page=mna' in the OPTIONS section.
diff --git a/doc/sg_ses.8 b/doc/sg_ses.8
index 4d015c8c..8042b059 100644
--- a/doc/sg_ses.8
+++ b/doc/sg_ses.8
@@ -1,4 +1,4 @@
-.TH SG_SES "8" "November 2017" "sg3_utils\-1.43" SG3_UTILS
+.TH SG_SES "8" "December 2017" "sg3_utils\-1.43" SG3_UTILS
.SH NAME
sg_ses \- access a SCSI Enclosure Services (SES) device
.SH SYNOPSIS
@@ -142,7 +142,7 @@ index field is 1 (in other words a heuristic to guess whether the EIIOE field
should be set to 1 or 0).
.br
If the enclosure sets the actual EIIOE field to 1 or more then this option has
-no effect. It is recommended that HP JBOD users set --eiioe=auto .
+no effect. It is recommended that HP JBOD users set \-\-eiioe=auto .
.TP
\fB\-e\fR, \fB\-\-enumerate\fR
enumerate all known page names and SES elements when this option is given
@@ -347,6 +347,10 @@ Enclosure Status page has only one "overall" element corresponding to that
type header. The Element Descriptor page and the Threshold (In and Out)
pages follow the same pattern as the Enclosure Status page.
.PP
+The numeric index corresponding to the overall element is "\-1". If the
+Configuration page indicates a particular element type has "n" elements
+and n is greater than 0 then its indexes range from 0 to n-\1 .
+.PP
The Additional Element Status page is a bit more complicated. It has
entries for "Number of possible elements" of certain Element types. It
does not have entries corresponding to the "overall" elements. To make
@@ -377,7 +381,7 @@ individual index then the option is equivalent to \fI\-\-index=0,II\fR. When
.PP
Wherever an individual index is applicable, it can be replaced by an
individual index range. It has the form: <first_ii>-<last_ii>. For
-example: '3-5' will select individial indexes 3, 4 and 5 .
+example: '3\-5' will select individial indexes 3, 4 and 5 .
.PP
To cope with vendor specific Element types (which should be in the range 128
to 255) the Element type can be given as a number with a leading underscore.
diff --git a/doc/sg_write_x.8 b/doc/sg_write_x.8
index 73fd8c30..294096d4 100644
--- a/doc/sg_write_x.8
+++ b/doc/sg_write_x.8
@@ -231,12 +231,22 @@ commands supported by thus utility except WRITE SAME.
\fB\-x\fR, \fB\-\-dry\-run\fR
this option exits (with a status of 0) just before it would otherwise send
the selected SCSI write command. It may still send a SCSI READ CAPACITY
-command (16 byte variant and perhaps 10 byte variant as well) and read
-the data in and process it if the \fI\-\-in=IF\fR and/or the
-\fI\-\-scat\-file=SF\fR options are given. All command line processing
-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.
+command (16 byte variant and perhaps 10 byte variant as well) so the
+\fIDEVICE\fR is still required. It reads the data in and processes it if the
+\fI\-\-in=IF\fR and/or the \fI\-\-scat\-file=SF\fR options are given. All
+command line processing 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.
+.br
+If this option is given twice (e.g. \-xx) then instead of performing the
+selected write SCSI command, the data\-out buffer is written to a file
+called sg_write_x.bin . If it doesn't exist then that file is created in
+the current directory and is truncated if it previously did exist with
+longer contents. The data\-out buffer is written in binary with some
+information about it written to stdout. For writes other than scattered
+the filename and its length in bytes is output to stdout. For write
+scattered additionally its number of LBA range descriptors and its
+logical block data offset written to stdout.
.TP
\fB\-f\fR, \fB\-\-fua\fR
if this option is given then the FUA (force unit access) bit field in the
@@ -491,7 +501,9 @@ 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:
+LBA according to the rules of WRITE ATOMIC with an atomic boundary of 0.
+Since no cdb size option is given, the 16 byte cdb will be assumed (i.e.
+WRITE ATOMIC(16)):
.PP
sg_write_x \-\-atomic=0 \-\-in=/dev/zero \-\-lba=0x1234 \-\-num=4 /dev/sdc
.PP
@@ -507,8 +519,10 @@ LBA 0x1234 . Now to bypass the need for the READ CAPACITY command(s) the
sg_write_x \-\-atomic=0 \-\-bs=512 \-\-in=/dev/zero \-\-lba=0x1234 \-\-num=4
/dev/sdc
.PP
-Both of the examples above issue a SCSI WRITE ATOMIC(16) command. To send the
-32 byte variant add \-\-32 as in:
+Since \-\-bs= is given and its value (512) is a power of 2, then the actual
+block size is also 512. If instead 520 was given then the logical block size
+would be 512 (the highest power of 2 less than 520) and the actual block size
+would be 520 bytes. To send the 32 byte variant add \-\-32 as in:
.PP
sg_write_x \-\-atomic=0 \-\-32 \-\-bs=512 \-\-in=/dev/zero \-\-lba=0x1234
\-\-num=4 /dev/sdc
@@ -518,11 +532,47 @@ To send a WRITE STREAM(32) with a STR_ID of 1 use the following:
sg_write_x \-\-stream=1 \-\-32 \-\-bs=512 \-\-in=/dev/zero \-\-lba=0x1234
\-\-num=4 /dev/sdc
.PP
+Next is a WRITE SCATTERED(16) command with the scatter list, split between
+the \-\-lba= and \-\-num= options, on the command line:
+.PP
+ sg_write_x \-\-scattered=2 \-\-lba=2,0x33 \-\-num=4,1 -i /dev/zero /dev/sg1
+.PP
+Example of a WRITE SCATTERED(16) command with a degenerate LBA range
+descriptor (first element to \-\-lba= and \-\-num=):
+.PP
+ sg_write_x \-\-scattered=2 \-\-lba=0,0x33 \-\-num=0,1 -i /dev/zero /dev/sg1
+.PP
Example of a WRITE SCATTERED(16) command with the scatter list in
scat_file.txt
- sg_write_x \-\-scattered=3 \-q scat_file.txt \-i /dev/zero /dev/sg
.PP
-xxxxxx More examples ...
+ sg_write_x \-\-scattered=3 \-q scat_file.txt \-i /dev/zero /dev/sg1
+.PP
+Next a WRITE SCATTERED(16) command with its scatter list and data in a
+single file. Note that the argument to \-\-scattered= is 0 so the number of
+LBA range descriptors is calculated by analyzing the first two blocks of
+scat_data.bin (because the argument to \-\-combined= is 2) :
+.PP
+ sg_write_x \-\-scattered=0 \-\-combined=2 \-i scat_data.bin /dev/sg1
+.PP
+When the \-xx option is used, a WRITE SCATTERED command is not executed
+but instead the contents of the data\-out buffer are written to a file
+called sg_write_x.bin . In the case of WRITE SCATTERED that binary file
+is suitable for supplying to a later invocation to do the actual write
+to media. For example:
+.PP
+ sg_write_x \-\-scattered=3 \-q scat_file.txt \-xx \-i /dev/zero /dev/sg1
+.br
+Wrote 8192 bytes to sg_write_x.bin, LB data offset: 1
+.br
+Number of LBA range descriptors: 3
+.br
+ sg_write_x \-\-scattered=0 \-\-combined=1 \-i sg_write_x.bin /dev/sg1
+.PP
+Notice when the sg_write_x.bin is written (and nothing is written to the
+media), a summary of what has happened is sent to stdout. The value shown
+for "LB data offset:" (1) should be given to the \-\-combined= option
+when the write to media actually occurs (i.e. the second invocation shown
+directly above).
.SH AUTHORS
Written by Douglas Gilbert.
.SH "REPORTING BUGS"
diff --git a/doc/sgp_dd.8 b/doc/sgp_dd.8
index 6d148f52..9a0ead5c 100644
--- a/doc/sgp_dd.8
+++ b/doc/sgp_dd.8
@@ -1,4 +1,4 @@
-.TH SGP_DD "8" "November 2012" "sg3_utils\-1.35" SG3_UTILS
+.TH SGP_DD "8" "December 2017" "sg3_utils\-1.43" SG3_UTILS
.SH NAME
sgp_dd \- copy data to and from files and devices, especially SCSI
devices
@@ -17,8 +17,9 @@ devices
Copy data to and from any files. Specialised for "files" that are
Linux SCSI generic (sg) and raw devices. Similar syntax and semantics to
.B dd(1)
-but does not perform any conversions. Uses POSIX threads to increase
-the amount of parallelism. This improves speed in some cases.
+but does not perform any conversions. Uses POSIX threads (often
+called "pthreads") to increase the amount of parallelism. This improves
+speed in some cases.
.PP
The first group in the synopsis above are "standard" Unix
.B dd(1)
@@ -302,7 +303,7 @@ Written by Douglas Gilbert and Peter Allworth.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2000\-2012 Douglas Gilbert
+Copyright \(co 2000\-2017 Douglas Gilbert
.br
This software is distributed under the GPL version 2. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/include/sg_lib.h b/include/sg_lib.h
index c5230974..28a5feb7 100644
--- a/include/sg_lib.h
+++ b/include/sg_lib.h
@@ -206,6 +206,14 @@ char * sg_get_asc_ascq_str(int asc, int ascq, int buff_len, char * buff);
bool sg_get_sense_info_fld(const unsigned char * sensep, int sb_len,
uint64_t * info_outp);
+/* Returns true if fixed format or command specific information descriptor
+ * is found in the descriptor sense; else false. If available the command
+ * specific information field (4 byte integer in fixed format, 8 byte
+ * integer in descriptor format) is written out via 'cmd_spec_outp'.
+ * Handles both fixed and descriptor sense formats. */
+bool sg_get_sense_cmd_spec_fld(const unsigned char * sensep, int sb_len,
+ uint64_t * cmd_spec_outp);
+
/* Returns true if any of the 3 bits (i.e. FILEMARK, EOM or ILI) are set.
* In descriptor format if the stream commands descriptor not found
* then returns false. Writes true or false corresponding to these bits to
@@ -302,6 +310,37 @@ const char * sg_get_desig_assoc_str(int val);
const char * sg_get_sfs_str(uint16_t sfs_code, int peri_type, int buff_len,
char * buff, bool * foundp, int verbose);
+/* This is a heuristic that takes into account the command bytes and length
+ * to decide whether the presented unstructured sequence of bytes could be
+ * a SCSI command. If so it returns true otherwise false. Vendor specific
+ * SCSI commands (i.e. opcodes from 0xc0 to 0xff), if presented, are assumed
+ * to follow SCSI conventions (i.e. length of 6, 10, 12 or 16 bytes). The
+ * only SCSI commands considered above 16 bytes of length 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.
+ * No NVMe commands (64 bytes long plus some extra added by some OSes) have
+ * opcodes 0x7e or 0x7f yet. ATA is register based but SATA has FIS
+ * structures that are sent across the wire. The 'FIS register' structure is
+ * used to move a command from a SATA host to device, but the ATA 'command'
+ * is not the first byte. So it is harder to say what will happen if a
+ * FIS structure is presented as a SCSI command, hopfully there is a low
+ * probability this function will yield true in that case. */
+bool sg_is_scsi_cdb(const uint8_t * cdbp, int clen);
+
+/* Yield string associated with NVMe command status value in sct_sc. It
+ * expects to decode DW3 bits 27:17 from the completion queue. Bits 27:25
+ * are the Status Code Type (SCT) and bits 24:17 are the Status Code (SC).
+ * Bit 17 in DW3 should be bit 0 in sct_sc. If no status string is found
+ * a string of the form "Reserved [0x<sct_sc_in_hex>]" is generated.
+ * Returns 'buff'. Does nothing if buff_len<=0 or if buff is NULL.*/
+char * sg_get_nvme_cmd_status_str(uint16_t sct_sc, int buff_len, char * buff);
+
+/* Attempts to map NVMe status value (SCT and SC) to SCSI status, sense_key,
+ * asc and ascq tuple. If successful returns true and writes to non-NULL
+ * pointer arguments; otherwise returns false. */
+bool sg_nvme_status2scsi(uint16_t sct_sc, uint8_t * status_p, uint8_t * sk_p,
+ uint8_t * asc_p, uint8_t * ascq_p);
+
extern FILE * sg_warnings_strm;
void sg_set_warnings_strm(FILE * warnings_strm);
@@ -539,4 +578,4 @@ int sg_set_binary_mode(int fd);
}
#endif
-#endif
+#endif /* SG_LIB_H */
diff --git a/include/sg_lib_data.h b/include/sg_lib_data.h
index 86afd6e4..82822002 100644
--- a/include/sg_lib_data.h
+++ b/include/sg_lib_data.h
@@ -67,6 +67,14 @@ struct sg_lib_asc_ascq_range_t {
const char * text;
};
+/* First use: SCSI status, sense_key, asc, ascq tuple */
+struct sg_lib_4tuple_u8 {
+ uint8_t t1;
+ uint8_t t2;
+ uint8_t t3;
+ uint8_t t4;
+};
+
extern const char * sg_lib_version_str;
@@ -98,6 +106,8 @@ extern const char * sg_lib_pdt_strs[];
extern const char * sg_lib_transport_proto_strs[];
extern int sg_lib_pdt_decay_arr[];
+extern struct sg_lib_value_name_t sg_lib_nvme_cmd_status_arr[];
+extern struct sg_lib_4tuple_u8 sg_lib_scsi_status_sense_arr[];
#ifdef __cplusplus
}
diff --git a/include/sg_pt_linux.h b/include/sg_pt_linux.h
index 4b59cfbf..c7447726 100644
--- a/include/sg_pt_linux.h
+++ b/include/sg_pt_linux.h
@@ -154,6 +154,13 @@ 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);
+/* This trims given NVMe block device name in Linux (e.g. /dev/nvme0n1p5)
+ * to the name of its associated char device (e.g. /dev/nvme0). If this
+ * occurs true is returned and the char device name is placed in 'b' (as
+ * long as b_len is sufficient). Otherwise false is returned. */
+bool sg_get_nvme_char_devname(const char * nvme_block_devname, uint32_t b_len,
+ char * b);
+
#ifdef __cplusplus
}
diff --git a/include/sg_pt_nvme.h b/include/sg_pt_nvme.h
index 25d52021..ef90d6b2 100644
--- a/include/sg_pt_nvme.h
+++ b/include/sg_pt_nvme.h
@@ -104,7 +104,7 @@ struct sg_nvme_passthru_cmd
uint32_t cdw15;
#ifdef SG_LIB_LINUX
uint32_t timeout_ms;
- uint32_t result; /* Dword(0) of completion queue entry */
+ uint32_t result; /* DWord(0) of completion queue entry */
#endif
}
#ifdef SG_LIB_FREEBSD
@@ -116,66 +116,38 @@ __packed;
/* 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_OPCODE 0 /* length: 1 byte */
+#define SG_NVME_PT_FLAGS 1 /* length: 1 byte */
+#define SG_NVME_PT_RSVD1 2 /* length: 2 bytes */
+#define SG_NVME_PT_NSID 4 /* length: 4 bytes */
+#define SG_NVME_PT_CDW2 8 /* length: 4 bytes */
+#define SG_NVME_PT_CDW3 12 /* length: 4 bytes */
+#define SG_NVME_PT_METADATA 16 /* length: 8 bytes */
+#define SG_NVME_PT_ADDR 24 /* length: 8 bytes */
+#define SG_NVME_PT_METADATA_LEN 32 /* length: 4 bytes */
+#define SG_NVME_PT_DATA_LEN 36 /* length: 4 bytes */
+#define SG_NVME_PT_CDW10 40 /* length: 4 bytes */
+#define SG_NVME_PT_CDW11 44 /* length: 4 bytes */
+#define SG_NVME_PT_CDW12 48 /* length: 4 bytes */
+#define SG_NVME_PT_CDW13 52 /* length: 4 bytes */
+#define SG_NVME_PT_CDW14 56 /* length: 4 bytes */
+#define SG_NVME_PT_CDW15 60 /* length: 4 bytes */
#ifdef SG_LIB_LINUX
/* General references state that "all NVMe commands are 64 bytes long". If
* so then the following are add-ons by Linux, go to the OS and not the
- * the NVMe device. And Linux doesn't seem to use the TIMEOUT_MS field on
- * output to yield the "time taken" by the command. */
-#define SG_NVME_PT_TIMEOUT_MS 64
-#define SG_NVME_PT_RESULT 68
+ * the NVMe device. */
+#define SG_NVME_PT_TIMEOUT_MS 64 /* length: 4 bytes */
+#define SG_NVME_PT_RESULT 68 /* length: 4 bytes */
#endif
-#ifdef __GNUC__
-#ifndef __clang__
- struct __attribute__((__packed__)) sg_nvme_passthru_result
-#else
- struct sg_nvme_passthru_result
-#endif
-#else
-struct sg_nvme_passthru_result
-#endif
-{
- uint8_t status;
- uint16_t transferred;
- uint8_t reserved;
-}
-#ifdef SG_LIB_FREEBSD
-__packed;
-#else
-;
-#endif
-
-/* Using byte offsets and unaligned be/le copies safer than packed
- * structures. These are for sg_nvme_passthru_result . */
-#define SG_NVME_PT_RES_STATUS 0
-#define SG_NVME_PT_RES_TRANSFERRED 1
-#define SG_NVME_PT_RES_RESERVED 3
-
-
/* Valid namespace IDs (nsid_s) range from 1 to 0xfffffffe, leaving: */
-#define SG_NVME_BROADCAST_NSID 0xffffffff
+#define SG_NVME_BROADCAST_NSID 0xffffffff /* all namespaces */
#define SG_NVME_CTL_NSID 0x0 /* the "controller's" namespace */
#ifdef __cplusplus
}
#endif
-#endif
+#endif /* SG_PT_NVME_H */
diff --git a/include/sg_unaligned.h b/include/sg_unaligned.h
index 6a7b10e8..1ca74d55 100644
--- a/include/sg_unaligned.h
+++ b/include/sg_unaligned.h
@@ -97,7 +97,7 @@ static inline uint64_t sg_get_unaligned_be64(const void *p)
/* Returns 0 if 'num_bytes' is less than or equal to 0 or greater than
* 8 (i.e. sizeof(uint64_t)). Else returns result in uint64_t which is
- * an 8 bytes unsigned integer. */
+ * an 8 byte unsigned integer. */
static inline uint64_t sg_get_unaligned_be(int num_bytes, const void *p)
{
if ((num_bytes <= 0) || (num_bytes > (int)sizeof(uint64_t)))
@@ -239,7 +239,7 @@ static inline uint64_t sg_get_unaligned_le64(const void *p)
/* Returns 0 if 'num_bytes' is less than or equal to 0 or greater than
* 8 (i.e. sizeof(uint64_t)). Else returns result in uint64_t which is
- * an 8 bytes unsigned integer. */
+ * an 8 byte unsigned integer. */
static inline uint64_t sg_get_unaligned_le(int num_bytes, const void *p)
{
if ((num_bytes <= 0) || (num_bytes > (int)sizeof(uint64_t)))
diff --git a/lib/sg_lib.c b/lib/sg_lib.c
index 003f1547..5055c414 100644
--- a/lib/sg_lib.c
+++ b/lib/sg_lib.c
@@ -353,6 +353,42 @@ sg_get_sense_info_fld(const unsigned char * sbp, int sb_len,
}
}
+/* Returns true if fixed format or command specific information descriptor
+ * is found in the descriptor sense; else false. If available the command
+ * specific information field (4 byte integer in fixed format, 8 byte
+ * integer in descriptor format) is written out via 'cmd_spec_outp'.
+ * Handles both fixed and descriptor sense formats. */
+bool
+sg_get_sense_cmd_spec_fld(const unsigned char * sbp, int sb_len,
+ uint64_t * cmd_spec_outp)
+{
+ const unsigned char * bp;
+
+ if (cmd_spec_outp)
+ *cmd_spec_outp = 0;
+ if (sb_len < 7)
+ return false;
+ switch (sbp[0] & 0x7f) {
+ case 0x70:
+ case 0x71:
+ if (cmd_spec_outp)
+ *cmd_spec_outp = sg_get_unaligned_be32(sbp + 8);
+ return true;
+ case 0x72:
+ case 0x73:
+ bp = sg_scsi_sense_desc_find(sbp, sb_len,
+ 1 /* command specific info desc */);
+ if (bp && (0xa == bp[1])) {
+ if (cmd_spec_outp)
+ *cmd_spec_outp = sg_get_unaligned_be64(bp + 4);
+ return true;
+ } else
+ return false;
+ default:
+ return false;
+ }
+}
+
/* Returns true if any of the 3 bits (i.e. FILEMARK, EOM or ILI) are set.
* In descriptor format if the stream commands descriptor not found
* then returns false. Writes true or false corresponding to these bits to
@@ -2342,6 +2378,153 @@ sg_get_sfs_str(uint16_t sfs_code, int peri_type, int buff_len, char * buff,
return buff;
}
+/* This is a heuristic that takes into account the command bytes and length
+ * to decide whether the presented unstructured sequence of bytes could be
+ * a SCSI command. If so it returns true otherwise false. Vendor specific
+ * SCSI commands (i.e. opcodes from 0xc0 to 0xff), if presented, are assumed
+ * to follow SCSI conventions (i.e. length of 6, 10, 12 or 16 bytes). The
+ * only SCSI commands considered above 16 bytes of length 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.
+ * No NVMe commands (64 bytes long plus some extra added by some OSes) have
+ * opcodes 0x7e or 0x7f yet. ATA is register based but SATA has FIS
+ * structures that are sent across the wire. The FIS register structure is
+ * used to move a command from a SATA host to device, but the ATA 'command'
+ * is not the first byte. So it is harder to say what will happen if a
+ * FIS structure is presented as a SCSI command, hopfully there is a low
+ * probability this function will yield true in that case. */
+bool
+sg_is_scsi_cdb(const uint8_t * cdbp, int clen)
+{
+ int ilen, sa;
+ uint8_t opcode;
+ uint8_t top3bits;
+
+ if (clen < 6)
+ return false;
+ opcode = cdbp[0];
+ top3bits = opcode >> 5;
+ if (0x3 == top3bits) {
+ if ((clen < 12) || (clen % 4))
+ return false; /* must be modulo 4 and 12 or more bytes */
+ switch (opcode) {
+ case 0x7e: /* Extended cdb (XCDB) */
+ ilen = 4 + sg_get_unaligned_be16(cdbp + 2);
+ return (ilen == clen);
+ case 0x7f: /* Variable Length cdb */
+ ilen = 8 + cdbp[7];
+ sa = sg_get_unaligned_be16(cdbp + 8);
+ /* service action (sa) 0x0 is reserved */
+ return ((ilen == clen) && sa);
+ default:
+ return false;
+ }
+ } else if (clen <= 16) {
+ switch (clen) {
+ case 6:
+ if (top3bits > 0x5) /* vendor */
+ return true;
+ return (0x0 == top3bits); /* 6 byte cdb */
+ case 10:
+ if (top3bits > 0x5) /* vendor */
+ return true;
+ return ((0x1 == top3bits) || (0x2 == top3bits)); /* 10 byte cdb */
+ case 16:
+ if (top3bits > 0x5) /* vendor */
+ return true;
+ return (0x4 == top3bits); /* 16 byte cdb */
+ case 12:
+ if (top3bits > 0x5) /* vendor */
+ return true;
+ return (0x5 == top3bits); /* 12 byte cdb */
+ default:
+ return false;
+ }
+ }
+ /* NVMe probably falls out here, clen > 16 and (opcode < 0x60 or
+ * opcode > 0x7f). */
+ return false;
+}
+
+/* Yield string associated with NVMe command status value in sct_sc. It
+ * expects to decode DW3 bits 27:17 from the completion queue. Bits 27:25
+ * are the Status Code Type (SCT) and bits 24:17 are the Status Code (SC).
+ * Bit 17 in DW3 should be bit 0 in sct_sc. If no status string is found
+ * a string of the form "Reserved [0x<sct_sc_in_hex>]" is generated.
+ * Returns 'buff'. Does nothing if buff_len<=0 or if buff is NULL.*/
+char *
+sg_get_nvme_cmd_status_str(uint16_t sct_sc, int b_len, char * b)
+{
+ int k;
+ uint16_t s = 0x3ff & sct_sc;
+ const struct sg_lib_value_name_t * vp = sg_lib_nvme_cmd_status_arr;
+
+ if ((b_len <= 0) || (NULL == b))
+ return b;
+ else if (1 == b_len) {
+ b[0] = '\0';
+ return b;
+ }
+ for (k = 0; (vp->name && (k < 1000)); ++k, ++vp) {
+ if (s == (uint16_t)vp->value) {
+ strncpy(b, vp->name, b_len);
+ b[b_len - 1] = '\0';
+ return b;
+ }
+ }
+ if (k >= 1000)
+ pr2ws("%s: where is sentinel for sg_lib_nvme_cmd_status_arr ??\n",
+ __func__);
+ snprintf(b, b_len, "Reserved [0x%x]", sct_sc);
+ return b;
+}
+
+/* Attempts to map NVMe status value (SCT and SC) to SCSI status, sense_key,
+ * asc and ascq tuple. If successful returns true and writes to non-NULL
+ * pointer arguments; otherwise returns false. */
+bool
+sg_nvme_status2scsi(uint16_t sct_sc, uint8_t * status_p, uint8_t * sk_p,
+ uint8_t * asc_p, uint8_t * ascq_p)
+{
+ int k, ind;
+ uint16_t s = 0x3ff & sct_sc;
+ struct sg_lib_value_name_t * vp = sg_lib_nvme_cmd_status_arr;
+ struct sg_lib_4tuple_u8 * mp = sg_lib_scsi_status_sense_arr;
+
+ for (k = 0; (vp->name && (k < 1000)); ++k, ++vp) {
+ if (s == (uint16_t)vp->value)
+ break;
+ }
+ if (k >= 1000) {
+ pr2ws("%s: where is sentinel for sg_lib_nvme_cmd_status_arr ??\n",
+ __func__);
+ return false;
+ }
+ if (NULL == vp->name)
+ return false;
+ ind = vp->peri_dev_type;
+
+
+ for (k = 0; (0xff != mp->t2) && k < 1000; ++k, ++mp)
+ ; /* count entries for valid index range */
+ if (k >= 1000) {
+ pr2ws("%s: where is sentinel for sg_lib_scsi_status_sense_arr ??\n",
+ __func__);
+ return false;
+ } else if (ind >= k)
+ return false;
+ mp = sg_lib_scsi_status_sense_arr + ind;
+ if (status_p)
+ *status_p = mp->t1;
+ if (sk_p)
+ *sk_p = mp->t2;
+ if (asc_p)
+ *asc_p = mp->t3;
+ if (ascq_p)
+ *ascq_p = mp->t4;
+ return true;
+}
+
/* safe_strerror() contributed by Clayton Weaver <cgweav at email dot com>
* Allows for situation in which strerror() is given a wild value (or the
* C library is incomplete) and returns NULL. Still not thread safe.
@@ -2586,7 +2769,8 @@ sg_is_big_endian()
the most significant byte */
}
-bool sg_all_zeros(const uint8_t * bp, int b_len)
+bool
+sg_all_zeros(const uint8_t * bp, int b_len)
{
if ((NULL == bp) || (b_len <= 0))
return false;
@@ -2597,7 +2781,8 @@ bool sg_all_zeros(const uint8_t * bp, int b_len)
return true;
}
-bool sg_all_ffs(const uint8_t * bp, int b_len)
+bool
+sg_all_ffs(const uint8_t * bp, int b_len)
{
if ((NULL == bp) || (b_len <= 0))
return false;
diff --git a/lib/sg_lib_data.c b/lib/sg_lib_data.c
index 76964d8c..b6362a50 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.34 20171219";/* spc5r17, sbc4r15 */
+const char * sg_lib_version_str = "2.35 20171227";/* spc5r17, sbc4r15 */
/* indexed by pdt; those that map to own index do not decay */
@@ -1506,3 +1506,178 @@ struct sg_lib_value_name_t sg_lib_scsi_feature_sets[] =
{SCSI_FS_SBC_DRIVE_MAINT_2016, PDT_DISK, "Drive maintenance 2016"},
{0x0, 0, NULL}, /* 0x0 is reserved sfs; trailing sentinel */
};
+
+/* .value is completion queue's DW3 as follows: ((DW3 >> 17) & 0x3ff)
+ * .peri_dev_type is an index for the sg_lib_scsi_status_sense_arr[]
+ * .name is taken from NVMe 1.3a document, section 4.6.1.2.1 with less
+ * capitalization.
+ * NVMe term bits 31:17 of DW3 in the completion field as the "Status
+ * Field" (SF). Bit 31 is "Do not retry" (DNR) and bit 30 is "More" (M).
+ * Bits 29:28 are reserved, bit 27:25 are the "Status Code Type" (SCT)
+ * and bits 24:17 are the Status Code (SC). This table is in ascending
+ * order of its .value field so a binary search could be done on it. */
+#ifdef SG_SCSI_STRINGS
+struct sg_lib_value_name_t sg_lib_nvme_cmd_status_arr[] =
+{
+ /* Generic command status values, Status Code Type (SCT): 0h
+ * Lowest 8 bits are the Status Code (SC), in this case:
+ * 00h - 7Fh: Applicable to Admin Command Set, or across multiple
+ * command sets
+ * 80h - BFh: I/O Command Set Specific status codes
+ * c0h - FFh: I/O Vendor Specific status codes */
+ {0x0, 0, "Successful completion"},
+ {0x1, 1, "Invalid command opcode"},
+ {0x2, 2, "Invalid field in command"},
+ {0x3, 2, "Command id conflict"},
+ {0x4, 3, "Data transfer error"},
+ {0x5, 4, "Command aborted due to power loss notication"},
+ {0x6, 5, "Internal error"},
+ {0x7, 6, "Command abort requested"},
+ {0x8, 6, "Command aborted due to SQ deletion"},
+ {0x9, 6, "Command aborted due to failed fused command"},
+ {0xa, 6, "Command aborted due to missing fused command"},
+ {0xb, 7, "Invalid namespace or format"},
+ {0xc, 5, "Command sequence error"},
+ {0xd, 5, "Invalid SGL segment descriptor"},
+ {0xe, 5, "Invalid number of SGL descriptors"},
+ {0xf, 5, "Data SGL length invalid"},
+ {0x10, 5, "Matadata SGL length invalid"},
+ {0x11, 5, "SGL descriptor type invalid"},
+ {0x12, 5, "Invalid use of controller memory buffer"},
+ {0x13, 5, "PRP offset invalid"},
+ {0x14, 2, "Atomic write unit exceeded"},
+ {0x15, 8, "Operation denied"},
+ {0x16, 5, "SGL offset invalid"},
+ {0x17, 5, "Reserved [0x17]"},
+ {0x18, 5, "Host identifier inconsistent format"},
+ {0x19, 5, "Keep alive timeout expired"},
+ {0x1a, 5, "Keep alive timeout invalid"},
+ {0x1b, 6, "Command aborted due to Preempt and Abort"},
+ {0x1c, 10, "Sanitize failed"},
+ {0x1d, 11, "Sanitize in progress"},
+ {0x1e, 5, "SGL data block granularity invalid"},
+ {0x1f, 5, "Command not supported for queue in CMB"},
+
+ /* Generic command status values, NVM (I/O) Command Set */
+ {0x80, 12, "LBA out of range"},
+ {0x81, 3, "Capacity exceeded"},
+ {0x82, 13, "Namespace not ready"},
+ {0x83, 14, "Reservation conflict"},
+ {0x84, 15, "Format in progress"},
+ /* 0xc0 - 0xff: vendor specific */
+
+ /* Command specific status values, Status Code Type (SCT): 1h */
+ {0x100, 5, "Completion queue invalid"},
+ {0x101, 5, "Invalid queue identifier"},
+ {0x102, 5, "Invalid queue size"},
+ {0x103, 5, "Abort command limit exceeded"},
+ {0x104, 5, "Reserved [0x104]"},
+ {0x105, 5, "Asynchronous event request limit exceeded"},
+ {0x106, 5, "Invalid firmware slot"},
+ {0x107, 5, "Invalid firmware image"},
+ {0x108, 5, "Invalid interrupt vector"},
+ {0x109, 5, "Invalid log page"},
+ {0x10a,16, "Invalid format"},
+ {0x10b, 5, "Firmware activation requires conventional reset"},
+ {0x10c, 5, "Invalid queue deletion"},
+ {0x10d, 5, "Feature identifier not saveable"},
+ {0x10e, 5, "Feature not changeable"},
+ {0x10f, 5, "Feature not namespace specific"},
+ {0x110, 5, "Firmware activation requires NVM subsystem reset"},
+ {0x111, 5, "Firmware activation requires reset"},
+ {0x112, 5, "Firmware activation requires maximum time violation"},
+ {0x113, 5, "Firmware activation prohibited"},
+ {0x114, 5, "Overlapping range"},
+ {0x115, 5, "Namespace insufficient capacity"},
+ {0x116, 5, "Namespace identifier unavailable"},
+ {0x117, 5, "Reserved [0x107]"},
+ {0x118, 5, "Namespace already attached"},
+ {0x119, 5, "Namespace is private"},
+ {0x11a, 5, "Namespace not attached"},
+ {0x11b, 3, "Thin provisioning not supported"},
+ {0x11c, 3, "Controller list invalid"},
+ {0x11d,17, "Device self-test in progress"},
+ {0x11e,18, "Boot partition write prohibited"},
+ {0x11f, 5, "Invalid controller identifier"},
+ {0x120, 5, "Invalid secondary controller state"},
+ {0x121, 5, "Invalid number of controller resorces"},
+ {0x122, 5, "Invalid resorce identifier"},
+
+ /* Command specific status values, Status Code Type (SCT): 1h
+ * for NVM (I/O) Command Set */
+ {0x180, 2, "Conflicting attributes"},
+ {0x181,19, "Invalid protection information"},
+ {0x182,18, "Attempted write to read only range"},
+ /* 0x1c0 - 0x1ff: vendor specific */
+
+ /* Media and Data Integrity error values, Status Code Type (SCT): 2h */
+ {0x280,20, "Write fault"},
+ {0x281,21, "Unrecovered read error"},
+ {0x282,22, "End-to-end guard check error"},
+ {0x283,23, "End-to-end application tag check error"},
+ {0x284,24, "End-to-end reference tag check error"},
+ {0x285,25, "Compare failure"},
+ {0x286, 8, "Access denied"},
+ {0x287,26, "Deallocated or unwritten logical block"},
+ /* 0x2c0 - 0x2ff: vendor specific */
+
+ /* Leave this Sentinel value at end of this array */
+ {0x3ff, 0, NULL},
+};
+
+/* The sg_lib_nvme_cmd_status_arr[n].peri_dev_type field is an index
+ * to this array. It allows an NVMe status (error) value to be mapped
+ * to this SCSI tuple: status, sense_key, additional sense code (asc) and
+ * asc qualifier (ascq). For brevity SAM_STAT_CHECK_CONDITION is written
+ * as 0x2. */
+struct sg_lib_4tuple_u8 sg_lib_scsi_status_sense_arr[] =
+{
+ {SAM_STAT_GOOD, SPC_SK_NO_SENSE, 0, 0}, /* it's all good */ /* 0 */
+ {SAM_STAT_CHECK_CONDITION, SPC_SK_ILLEGAL_REQUEST, 0x20, 0x0},/* opcode */
+ {0x2, SPC_SK_ILLEGAL_REQUEST, 0x24, 0x0}, /* field in cdb */
+ {0x2, SPC_SK_MEDIUM_ERROR, 0x0, 0x0},
+ {SAM_STAT_TASK_ABORTED, SPC_SK_ABORTED_COMMAND, 0xb, 0x8},
+ {0x2, SPC_SK_HARDWARE_ERROR, 0x44, 0x0}, /* internal error */ /* 5 */
+ {SAM_STAT_TASK_ABORTED, SPC_SK_ABORTED_COMMAND, 0x0, 0x0},
+ {0x2, SPC_SK_ILLEGAL_REQUEST, 0x20, 0x9}, /* invalid LU */
+ {0x2, SPC_SK_ILLEGAL_REQUEST, 0x20, 0x2}, /* access denied */
+ {0x2, SPC_SK_ILLEGAL_REQUEST, 0x2c, 0x0}, /* cmd sequence error */
+ {0x2, SPC_SK_MEDIUM_ERROR, 0x31, 0x3}, /* sanitize failed */ /* 10 */
+ {0x2, SPC_SK_NOT_READY, 0x4, 0x1b}, /* sanitize in progress */
+ {0x2, SPC_SK_ILLEGAL_REQUEST, 0x21, 0x0}, /* LBA out of range */
+ {0x2, SPC_SK_NOT_READY, 0x4, 0x0}, /* not reportable; 0x1: becoming */
+ {SAM_STAT_RESERVATION_CONFLICT, 0x0, 0x0, 0x0},
+ {0x2, SPC_SK_NOT_READY, 0x4, 0x4}, /* format in progress */ /* 15 */
+ {0x2, SPC_SK_ILLEGAL_REQUEST, 0x31, 0x1}, /* format failed */
+ {0x2, SPC_SK_NOT_READY, 0x4, 0x9}, /* self-test in progress */
+ {0x2, SPC_SK_DATA_PROTECT, 0x27, 0x0}, /* write prohibited */
+ {0x2, SPC_SK_ILLEGAL_REQUEST, 0x10, 0x5}, /* protection info */
+ {0x2, SPC_SK_MEDIUM_ERROR, 0x3, 0x0}, /* periph dev w fault */ /* 20 */
+ {0x2, SPC_SK_MEDIUM_ERROR, 0x11, 0x0}, /* unrecoc rd */
+ {0x2, SPC_SK_MEDIUM_ERROR, 0x10, 0x1}, /* PI guard */
+ {0x2, SPC_SK_MEDIUM_ERROR, 0x10, 0x2}, /* PI app tag */
+ {0x2, SPC_SK_MEDIUM_ERROR, 0x10, 0x2}, /* PI app tag */
+ {0x2, SPC_SK_MISCOMPARE, 0x1d, 0x0}, /* during verify */ /* 25 */
+ {0x2, SPC_SK_MEDIUM_ERROR, 0x21, 0x6}, /* read invalid data */
+
+ /* Leave this Sentinel value at end of this array */
+ {0xff, 0xff, 0xff, 0xff},
+};
+
+
+#else /* no SG_SCSI_STRINGS define in config.sys */
+struct sg_lib_value_name_t sg_lib_nvme_cmd_status_arr[] =
+{
+
+ /* Leave this Sentinel value at end of this array */
+ {0x3ff, 0, NULL},
+};
+
+struct sg_lib_4tuple_u8 sg_lib_scsi_status_sense_arr[] =
+{
+
+ /* Leave this Sentinel value at end of this array */
+ {0xff, 0xff, 0xff, 0xff},
+};
+
+#endif /* SG_SCSI_STRINGS */
diff --git a/lib/sg_pt_freebsd.c b/lib/sg_pt_freebsd.c
index 6fa3f310..0d2e38f9 100644
--- a/lib/sg_pt_freebsd.c
+++ b/lib/sg_pt_freebsd.c
@@ -5,7 +5,7 @@
* license that can be found in the BSD_LICENSE file.
*/
-/* sg_pt_freebsd version 1.19 20171218 */
+/* sg_pt_freebsd version 1.20 20171227 */
#include <stdio.h>
#include <stdlib.h>
@@ -18,6 +18,8 @@
#include <libgen.h> /* for basename */
#include <fcntl.h>
#include <errno.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h> /* from PRIx macros */
#include <err.h>
#include <camlib.h>
#include <cam/scsi/scsi_message.h>
@@ -51,8 +53,12 @@ struct freebsd_dev_channel {
uint32_t nsid;
uint32_t nv_ctrlid;
int dev_fd; // for NVMe, use -1 to indicate not provided
+ uint32_t nvme_result; // cdw0 from completion
+ uint16_t nvme_status; // from completion: ((sct << 8) | sc)
char* devname; // the device name
struct cam_device* cam_dev;
+ uint8_t * nvme_id_ctlp;
+ uint8_t * free_nvme_id_ctlp;
};
// Private table of open devices: guaranteed zero on startup since
@@ -79,14 +85,17 @@ struct sg_pt_freebsd_scsi {
uint32_t mdxfer_len;
bool mdxfer_out;
bool scsi_dsense;
+ int timeout_ms;
int scsi_status;
int resid;
int sense_resid;
int in_err;
int os_err;
int transport_err;
- int dev_han; // -1 if not provided
- uint32_t nvme_result; // from completion
+ int dev_han; // should be >= FREEBSD_FDOFFSET then
+ // (dev_han - FREEBSD_FDOFFSET) is the
+ // index into devicetable[]
+ bool is_nvme; // copy of same field in fdc object
};
struct sg_pt_base {
@@ -102,6 +111,10 @@ static int pr2ws(const char * fmt, ...)
static int pr2ws(const char * fmt, ...);
#endif
+static int sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb);
+static struct freebsd_dev_channel *
+ get_fdc_p(struct sg_pt_freebsd_scsi * ptp);
+
static int
pr2ws(const char * fmt, ...)
@@ -120,38 +133,6 @@ static inline bool is_aligned(const void * pointer, size_t byte_count)
return (sg_uintptr_t)pointer % byte_count == 0;
}
-/* 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;
-}
-
-
/* 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)
@@ -174,7 +155,7 @@ scsi_pt_open_flags(const char * device_name, int oflags, int verbose)
int k, err, dev_fd, ret;
uint32_t nsid, nv_ctrlid;
ssize_t s;
- struct freebsd_dev_channel *fdchan = NULL;
+ struct freebsd_dev_channel *fdc_p = NULL;
struct cam_device* cam_dev;
struct stat a_stat;
char b[PATH_MAX];
@@ -233,24 +214,24 @@ scsi_pt_open_flags(const char * device_name, int oflags, int verbose)
break;
}
- fdchan = (struct freebsd_dev_channel *)
+ fdc_p = (struct freebsd_dev_channel *)
calloc(1,sizeof(struct freebsd_dev_channel));
- if (fdchan == NULL) {
+ if (fdc_p == NULL) {
// errno already set by call to calloc()
ret = -ENOMEM;
goto err_out;
}
- fdchan->dev_fd = -1;
- if (! (fdchan->devname = (char *)calloc(1, DEV_IDLEN+1))) {
+ fdc_p->dev_fd = -1;
+ if (! (fdc_p->devname = (char *)calloc(1, DEV_IDLEN+1))) {
ret = -ENOMEM;
goto err_out;
}
if (possible_nvme) {
// we should always open controller, not namespace device
- snprintf(fdchan->devname, DEV_IDLEN, NVME_CTRLR_PREFIX"%d",
+ snprintf(fdc_p->devname, DEV_IDLEN, NVME_CTRLR_PREFIX"%d",
nv_ctrlid);
- dev_fd = open(fdchan->devname, oflags);
+ dev_fd = open(fdc_p->devname, oflags);
if (dev_fd < 0) {
err = errno;
if (verbose)
@@ -258,19 +239,19 @@ scsi_pt_open_flags(const char * device_name, int oflags, int verbose)
__func__, full_path, strerror(err), err);
goto scsi_ata_try;
}
- fdchan->is_nvme = true;
- fdchan->is_char = is_char;
- fdchan->nsid = (broadcast_nsid == nsid) ? 0 : nsid;
- fdchan->nv_ctrlid = nv_ctrlid;
- fdchan->dev_fd = dev_fd;
- devicetable[k] = fdchan;
+ fdc_p->is_nvme = true;
+ fdc_p->is_char = is_char;
+ fdc_p->nsid = (broadcast_nsid == nsid) ? 0 : nsid;
+ fdc_p->nv_ctrlid = nv_ctrlid;
+ fdc_p->dev_fd = dev_fd;
+ devicetable[k] = fdc_p;
return k + FREEBSD_FDOFFSET;
}
scsi_ata_try:
- fdchan->is_char = is_char;
- if (cam_get_device(device_name, fdchan->devname, DEV_IDLEN,
- &(fdchan->unitnum)) == -1) {
+ fdc_p->is_char = is_char;
+ if (cam_get_device(device_name, fdc_p->devname, DEV_IDLEN,
+ &(fdc_p->unitnum)) == -1) {
if (verbose)
pr2ws("bad device name structure\n");
errno = EINVAL;
@@ -279,27 +260,27 @@ scsi_ata_try:
}
if (verbose > 4)
pr2ws("%s: cam_get_device, f->devname: %s, f->unitnum=%d\n", __func__,
- fdchan->devname, fdchan->unitnum);
+ fdc_p->devname, fdc_p->unitnum);
- if (! (cam_dev = cam_open_spec_device(fdchan->devname,
- fdchan->unitnum, O_RDWR, NULL))) {
+ if (! (cam_dev = cam_open_spec_device(fdc_p->devname,
+ fdc_p->unitnum, O_RDWR, NULL))) {
if (verbose)
pr2ws("cam_open_spec_device: %s\n", cam_errbuf);
errno = EPERM; /* permissions or not CAM device (NVMe ?) */
ret = -errno;
goto err_out;
}
- fdchan->cam_dev = cam_dev;
+ fdc_p->cam_dev = cam_dev;
// return pointer to "file descriptor" table entry, properly offset.
- devicetable[k] = fdchan;
+ devicetable[k] = fdc_p;
return k + FREEBSD_FDOFFSET;
err_out: /* ret should be negative value (negated errno) */
- if (fdchan) {
- if (fdchan->devname)
- free(fdchan->devname);
- free(fdchan);
- fdchan = NULL;
+ if (fdc_p) {
+ if (fdc_p->devname)
+ free(fdc_p->devname);
+ free(fdc_p);
+ fdc_p = NULL;
}
return ret;
}
@@ -308,27 +289,32 @@ err_out: /* ret should be negative value (negated errno) */
int
scsi_pt_close_device(int device_han)
{
- struct freebsd_dev_channel *fdchan;
+ struct freebsd_dev_channel *fdc_p;
int han = device_han - FREEBSD_FDOFFSET;
if ((han < 0) || (han >= FREEBSD_MAXDEV)) {
errno = ENODEV;
return -errno;
}
- fdchan = devicetable[han];
- if (NULL == fdchan) {
+ fdc_p = devicetable[han];
+ if (NULL == fdc_p) {
errno = ENODEV;
return -errno;
}
- if (fdchan->devname)
- free(fdchan->devname);
- if (fdchan->cam_dev)
- cam_close_device(fdchan->cam_dev);
- if (fdchan->is_nvme) {
- if (fdchan->dev_fd >= 0)
- close(fdchan->dev_fd);
+ if (fdc_p->devname)
+ free(fdc_p->devname);
+ if (fdc_p->cam_dev)
+ cam_close_device(fdc_p->cam_dev);
+ if (fdc_p->is_nvme) {
+ if (fdc_p->dev_fd >= 0)
+ close(fdc_p->dev_fd);
+ if (fdc_p->free_nvme_id_ctlp) {
+ free(fdc_p->free_nvme_id_ctlp);
+ fdc_p->nvme_id_ctlp = NULL;
+ fdc_p->free_nvme_id_ctlp = NULL;
+ }
}
- free(fdchan);
+ free(fdc_p);
devicetable[han] = NULL;
errno = 0;
return 0;
@@ -343,22 +329,22 @@ scsi_pt_close_device(int device_han)
int
check_pt_file_handle(int device_han, const char * device_name, int verbose)
{
- struct freebsd_dev_channel *fdchan;
+ struct freebsd_dev_channel *fdc_p;
int han = device_han - FREEBSD_FDOFFSET;
if ((han < 0) || (han >= FREEBSD_MAXDEV)) {
errno = ENODEV;
return -errno;
}
- fdchan = devicetable[han];
- if (NULL == fdchan) {
+ fdc_p = devicetable[han];
+ if (NULL == fdc_p) {
errno = ENODEV;
return -errno;
}
- if (fdchan->is_nvme)
- return 4 - (int)fdchan->is_char;
- else if (fdchan->cam_dev)
- return 2 - (int)fdchan->is_char;
+ if (fdc_p->is_nvme)
+ return 4 - (int)fdc_p->is_char;
+ else if (fdc_p->cam_dev)
+ return 2 - (int)fdc_p->is_char;
else {
if (device_name) { }
if (verbose) { }
@@ -380,7 +366,12 @@ construct_scsi_pt_obj_with_fd(int dev_han, int verbose)
ptp = (struct sg_pt_freebsd_scsi *)
calloc(1, sizeof(struct sg_pt_freebsd_scsi));
if (ptp) {
+ struct freebsd_dev_channel *fdc_p;
+
memset(ptp, 0, sizeof(struct sg_pt_freebsd_scsi));
+ fdc_p = get_fdc_p(ptp);
+ if (fdc_p)
+ ptp->is_nvme = fdc_p->is_nvme;
ptp->dxfer_dir = CAM_DIR_NONE;
ptp->dev_han = (dev_han < 0) ? -1 : dev_han;
} else if (verbose)
@@ -407,19 +398,43 @@ destruct_scsi_pt_obj(struct sg_pt_base * vp)
}
}
+static struct freebsd_dev_channel *
+get_fdc_p(struct sg_pt_freebsd_scsi * ptp)
+{
+ int han = ptp->dev_han - FREEBSD_FDOFFSET;
+
+ if ((han < 0) || (han >= FREEBSD_MAXDEV))
+ return NULL;
+ return devicetable[han];
+}
+
+static const struct freebsd_dev_channel *
+get_fdc_cp(const struct sg_pt_freebsd_scsi * ptp)
+{
+ int han = ptp->dev_han - FREEBSD_FDOFFSET;
+
+ if ((han < 0) || (han >= FREEBSD_MAXDEV))
+ return NULL;
+ return devicetable[han];
+}
+
+
void
clear_scsi_pt_obj(struct sg_pt_base * vp)
{
+ bool is_nvme;
int dev_han;
struct sg_pt_freebsd_scsi * ptp = &vp->impl;
if (ptp) {
if (ptp->ccb)
cam_freeccb(ptp->ccb);
+ is_nvme = ptp->is_nvme;
dev_han = ptp->dev_han;
memset(ptp, 0, sizeof(struct sg_pt_freebsd_scsi));
ptp->dxfer_dir = CAM_DIR_NONE;
ptp->dev_han = dev_han;
+ ptp->is_nvme = is_nvme;
}
}
@@ -481,11 +496,11 @@ set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp,
if (ptp->dxferip)
++ptp->in_err;
+ ptp->dxferip = dxferp;
+ ptp->dxfer_ilen = dxfer_len;
if (dxfer_len > 0) {
ptp->dxferp = dxferp;
- ptp->dxferip = dxferp;
ptp->dxfer_len = dxfer_len;
- ptp->dxfer_ilen = dxfer_len;
ptp->dxfer_dir = CAM_DIR_IN;
}
}
@@ -499,11 +514,11 @@ set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp,
if (ptp->dxferop)
++ptp->in_err;
+ ptp->dxferop = (unsigned char *)dxferp;
+ ptp->dxfer_olen = dxfer_len;
if (dxfer_len > 0) {
ptp->dxferp = (unsigned char *)dxferp;
- ptp->dxferop = (unsigned char *)dxferp;
ptp->dxfer_len = dxfer_len;
- ptp->dxfer_olen = dxfer_len;
ptp->dxfer_dir = CAM_DIR_OUT;
}
}
@@ -516,11 +531,10 @@ set_pt_metadata_xfer(struct sg_pt_base * vp, unsigned char * mdxferp,
if (ptp->mdxferp)
++ptp->in_err;
- if (mdxfer_len > 0) {
- ptp->mdxferp = mdxferp;
- ptp->mdxfer_len = mdxfer_len;
+ ptp->mdxferp = mdxferp;
+ ptp->mdxfer_len = mdxfer_len;
+ if (mdxfer_len > 0)
ptp->mdxfer_out = out_true;
- }
}
void
@@ -563,16 +577,43 @@ set_scsi_pt_flags(struct sg_pt_base * objp, int flags)
if (flags) { ; } /* unused, suppress warning */
}
+static int
+nvme_pt_low(struct freebsd_dev_channel *fdc_p, void * dxferp, uint32_t len,
+ bool is_read, struct nvme_pt_command * npcp, int vb)
+{
+ int err, status;
+ uint8_t opcode;
+ char b[80];
+
+ if (fdc_p->dev_fd < 0) {
+ if (vb)
+ pr2ws("%s: is_nvme is true but dev_fd<0, inconsistent\n",
+ __func__);
+ return -EINVAL;
+ }
+ npcp->buf = dxferp;
+ npcp->len = len;
+ npcp->is_read = (uint32_t)is_read;
+ opcode = npcp->cmd.opc;
+ err = ioctl(fdc_p->dev_fd, NVME_PASSTHROUGH_CMD, npcp);
+ if (err < 0)
+ return -errno;
+ status = ((npcp->cpl.status.sct << 8) | npcp->cpl.status.sc);
+ if (status && vb)
+ pr2ws("%s: opcode=0x%x, status: %s\n", __func__, opcode,
+ sg_get_nvme_cmd_status_str(status, sizeof(b), b));
+ return status;
+}
+
/* Executes SCSI command (or at least forwards it to lower layers).
* Clears os_err field prior to active call (whose result may set it
* again). */
int
do_scsi_pt(struct sg_pt_base * vp, int dev_han, int time_secs, int verbose)
{
- int n, len, timout_ms;
- int han;
+ int len;
struct sg_pt_freebsd_scsi * ptp = &vp->impl;
- struct freebsd_dev_channel *fdchan;
+ struct freebsd_dev_channel *fdc_p;
union ccb *ccb;
ptp->os_err = 0;
@@ -598,78 +639,34 @@ do_scsi_pt(struct sg_pt_base * vp, int dev_han, int time_secs, int verbose)
} else
ptp->dev_han = dev_han;
}
- han = ptp->dev_han - FREEBSD_FDOFFSET;
if (NULL == ptp->cdb) {
if (verbose)
pr2ws("No command (cdb) given\n");
return SCSI_PT_DO_BAD_PARAMS;
}
+ if (ptp->is_nvme)
+ return sg_do_nvme_pt(vp, -1, verbose);
- if ((han < 0) || (han >= FREEBSD_MAXDEV)) {
- if (verbose)
- pr2ws("Bad file handle\n");
- ptp->os_err = ENODEV;
- return -ptp->os_err;
- }
- fdchan = devicetable[han];
- if (NULL == fdchan) {
+ fdc_p = get_fdc_p(ptp);
+ if (NULL == fdc_p) {
if (verbose)
- pr2ws("File descriptor closed??\n");
+ pr2ws("File descriptor bad or closed??\n");
ptp->os_err = ENODEV;
return -ptp->os_err;
}
- if (fdchan->is_nvme) {
- int err;
- struct nvme_pt_command npc;
+ ptp->is_nvme = fdc_p->is_nvme;
+ if (fdc_p->is_nvme)
+ return sg_do_nvme_pt(vp, -1, verbose);
- if (fdchan->dev_fd < 0) {
- if (verbose)
- pr2ws("%s: is_nvme is true but dev_fd<0, inconsistent\n",
- __func__);
- ptp->os_err = EINVAL;
- return -ptp->os_err;
- }
- memset(&npc, 0, sizeof(npc));
- n = ptp->cdb_len;
- len = (int)sizeof(npc.cmd);
- n = (len < n) ? len : n;
- if (n < 8) {
- if (verbose)
- pr2ws("%s: cdb_len=%d too short\n", __func__, n);
- return SCSI_PT_DO_BAD_PARAMS;
- }
- memcpy(&npc.cmd, ptp->cdb, ptp->cdb_len);
- npc.buf = ptp->dxferp;
- npc.len = ptp->dxfer_len;
- npc.is_read = (CAM_DIR_IN == ptp->dxfer_dir);
- if ((0 == npc.is_read) && (CAM_DIR_OUT == ptp->dxfer_dir))
- npc.len = 0; /* don't want write by accident */
- err = ioctl(fdchan->dev_fd, NVME_PASSTHROUGH_CMD, &npc);
- if (err < 0) {
- ptp->os_err = errno;
- if (verbose > 3)
- pr2ws("%s: ioctl(NVME_PASSTHROUGH_CMD) failed: %s "
- "(errno=%d)\n", __func__, strerror(ptp->os_err),
- ptp->os_err);
- return -ptp->os_err;
- }
- ptp->nvme_result = npc.cpl.cdw0;
- if (ptp->sense_len > 0) {
- n = (int)sizeof(npc.cpl);
- n = ptp->sense_len < n ? ptp->sense_len : n;
- memcpy(ptp->sense, &npc.cpl, n);
- }
- return 0;
- }
- if (NULL == fdchan->cam_dev) {
+ if (NULL == fdc_p->cam_dev) {
if (verbose)
pr2ws("No open CAM device\n");
return SCSI_PT_DO_BAD_PARAMS;
}
if (NULL == ptp->ccb) { /* re-use if we have one already */
- if (! (ccb = cam_getccb(fdchan->cam_dev))) {
+ if (! (ccb = cam_getccb(fdc_p->cam_dev))) {
if (verbose)
pr2ws("cam_getccb: failed\n");
ptp->os_err = ENOMEM;
@@ -683,7 +680,7 @@ do_scsi_pt(struct sg_pt_base * vp, int dev_han, int time_secs, int verbose)
bzero(&(&ccb->ccb_h)[1],
sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
- timout_ms = (time_secs > 0) ? (time_secs * 1000) : DEF_TIMEOUT;
+ ptp->timeout_ms = (time_secs > 0) ? (time_secs * 1000) : DEF_TIMEOUT;
cam_fill_csio(&ccb->csio,
/* retries */ 1,
/* cbfcnp */ NULL,
@@ -693,14 +690,14 @@ do_scsi_pt(struct sg_pt_base * vp, int dev_han, int time_secs, int verbose)
/* datalen */ ptp->dxfer_len,
/* senselen */ ptp->sense_len,
/* cdblen */ ptp->cdb_len,
- /* timeout (millisecs) */ timout_ms);
+ /* timeout (millisecs) */ ptp->timeout_ms);
memcpy(ccb->csio.cdb_io.cdb_bytes, ptp->cdb, ptp->cdb_len);
- if (cam_send_ccb(fdchan->cam_dev, ccb) < 0) {
+ if (cam_send_ccb(fdc_p->cam_dev, ccb) < 0) {
if (verbose) {
warn("error sending SCSI ccb");
#if __FreeBSD_version > 500000
- cam_error_print(fdchan->cam_dev, ccb, CAM_ESF_ALL,
+ cam_error_print(fdc_p->cam_dev, ccb, CAM_ESF_ALL,
CAM_EPF_ALL, stderr);
#endif
}
@@ -728,7 +725,7 @@ do_scsi_pt(struct sg_pt_base * vp, int dev_han, int time_secs, int verbose)
} else
ptp->transport_err = 1;
- ptp->cam_dev = fdchan->cam_dev; // for error processing
+ ptp->cam_dev = fdc_p->cam_dev; // for error processing
return 0;
}
@@ -755,7 +752,7 @@ get_scsi_pt_resid(const struct sg_pt_base * vp)
{
const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
- return ptp->resid;
+ return ptp->is_nvme ? 0 : ptp->resid;
}
int
@@ -764,35 +761,35 @@ get_scsi_pt_status_response(const struct sg_pt_base * vp)
const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
if (ptp) {
- int han = ptp->dev_han - FREEBSD_FDOFFSET;
- struct freebsd_dev_channel *fdchan;
+ if (ptp->is_nvme) {
+ const struct freebsd_dev_channel *fdc_p;
- if ((han < 0) || (han >= FREEBSD_MAXDEV))
- return -1;
- fdchan = devicetable[han];
- if (NULL == fdchan)
- return -1;
- return fdchan->is_nvme ? (int)ptp->nvme_result : ptp->scsi_status;
+ fdc_p = get_fdc_cp(ptp);
+ if (NULL == fdc_p)
+ return -1;
+ return (int)fdc_p->nvme_status;
+ } else
+ return ptp->scsi_status;
}
return -1;
}
+/* For NVMe, CDW0 from completion (32 bits), for SCSI the status */
uint32_t
get_pt_result(const struct sg_pt_base * vp)
{
const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
if (ptp) {
- int han = ptp->dev_han - FREEBSD_FDOFFSET;
- struct freebsd_dev_channel *fdchan;
+ if (ptp->is_nvme) {
+ const struct freebsd_dev_channel *fdc_p;
- if ((han < 0) || (han >= FREEBSD_MAXDEV))
- return -1;
- fdchan = devicetable[han];
- if (NULL == fdchan)
- return -1;
- return fdchan->is_nvme ? ptp->nvme_result :
- (uint32_t)ptp->scsi_status;
+ fdc_p = get_fdc_cp(ptp);
+ if (NULL == fdc_p)
+ return -1;
+ return fdc_p->nvme_result;
+ } else
+ return (uint32_t)ptp->scsi_status;
}
return -1;
}
@@ -843,6 +840,11 @@ get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len,
b[max_b_len - 1] = '\0';
return b;
}
+ if (ptp->is_nvme) {
+ snprintf(b, max_b_len, "NVMe has no transport errors at present "
+ "but tranport_err=%d ??\n", ptp->transport_err);
+ return b;
+ }
#if __FreeBSD_version > 500000
if (ptp->cam_dev)
cam_error_string(ptp->cam_dev, ptp->ccb, b, max_b_len, CAM_ESF_ALL,
@@ -864,19 +866,17 @@ pt_device_is_nvme(const struct sg_pt_base * vp)
const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
if (ptp && (ptp->dev_han >= 0)) {
- int han = ptp->dev_han - FREEBSD_FDOFFSET;
- struct freebsd_dev_channel *fdchan;
+ const struct freebsd_dev_channel *fdc_p;
- if ((han < 0) || (han >= FREEBSD_MAXDEV)) {
+ fdc_p = get_fdc_cp(ptp);
+ if (NULL == fdc_p) {
errno = ENODEV;
return false;
}
- fdchan = devicetable[han];
- if (NULL == fdchan) {
- errno = ENODEV;
- return false;
- }
- return fdchan->is_nvme;
+ /* if unequal, cast away const and drive fdc_p value into ptp */
+ if (ptp->is_nvme != fdc_p->is_nvme)
+ ((struct sg_pt_freebsd_scsi *)ptp)->is_nvme = fdc_p->is_nvme;
+ return fdc_p->is_nvme;
}
return false;
}
@@ -890,15 +890,12 @@ get_pt_nvme_nsid(const struct sg_pt_base * vp)
const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
if (ptp && (ptp->dev_han >= 0)) {
- int han = ptp->dev_han - FREEBSD_FDOFFSET;
- struct freebsd_dev_channel *fdchan;
+ const struct freebsd_dev_channel *fdc_p;
- if ((han < 0) || (han >= FREEBSD_MAXDEV))
- return 0;
- fdchan = devicetable[han];
- if (NULL == fdchan)
+ fdc_p = get_fdc_cp(ptp);
+ if (NULL == fdc_p)
return 0;
- return fdchan->nsid;
+ return fdc_p->nsid;
}
return 0;
}
@@ -953,7 +950,6 @@ get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b)
#define MICROCODE_CHANGED_ASCQ 0x1 /* with TARGET_CHANGED_ASC */
#define MICROCODE_CHANGED_WO_RESET_ASCQ 0x16
-
static void
build_sense_buffer(bool desc, uint8_t *buf, uint8_t skey, uint8_t asc,
uint8_t ascq)
@@ -993,8 +989,41 @@ mk_sense_asc_ascq(struct sg_pt_freebsd_scsi * ptp, int sk, int asc, int ascq,
memset(sbp, 0, n);
build_sense_buffer(dsense, sbp, sk, asc, ascq);
if (vb > 3)
- pr2ws("%s: [sense_key,asc,ascq]: [0x5,0x%x,0x%x]\n", __func__, asc,
- ascq);
+ pr2ws("%s: [sense_key,asc,ascq]: [0x%x,0x%x,0x%x]\n", __func__,
+ sk, asc, ascq);
+}
+
+static void
+mk_sense_from_nvme_status(struct sg_pt_freebsd_scsi * ptp, uint16_t sct_sc,
+ int vb)
+{
+ bool ok;
+ bool dsense = ptp->scsi_dsense;
+ int n;
+ uint8_t sstatus, sk, asc, ascq;
+ uint8_t * sbp = ptp->sense;
+
+ ok = sg_nvme_status2scsi(sct_sc, &sstatus, &sk, &asc, &ascq);
+ if (! ok) { /* can't find a mapping to a SCSI error, so ... */
+ sstatus = SAM_STAT_CHECK_CONDITION;
+ sk = SPC_SK_ILLEGAL_REQUEST;
+ asc = 0xb;
+ ascq = 0x0; /* asc: "WARNING" purposely vague */
+ }
+
+ ptp->scsi_status = sstatus;
+ n = ptp->sense_len;
+ if ((n < 8) || ((! dsense) && (n < 14))) {
+ pr2ws("%s: sense_len=%d too short, want 14 or more\n", __func__, n);
+ return;
+ } else
+ ptp->sense_resid = ptp->sense_len -
+ (dsense ? 8 : ((n < 18) ? n : 18));
+ memset(sbp, 0, n);
+ build_sense_buffer(dsense, sbp, sk, asc, ascq);
+ if (vb > 3)
+ pr2ws("%s: [sense_key,asc,ascq]: [0x%x,0x%x,0x%x]\n", __func__,
+ sk, asc, ascq);
}
/* Set in_bit to -1 to indicate no bit position of invalid field */
@@ -1040,73 +1069,40 @@ mk_sense_invalid_fld(struct sg_pt_freebsd_scsi * ptp, bool in_cdb,
__func__, asc, in_cdb ? 'C' : 'D', in_byte, in_bit);
}
-#if 0
static int
-do_nvme_admin_cmd(struct sg_pt_linux_scsi * ptp,
- struct sg_nvme_passthru_cmd *cmdp, int time_secs,
- bool cp_cmd_out2resp, int vb)
-{
- const uint32_t cmd_len = sizeof(struct sg_nvme_passthru_cmd);
- uint32_t n;
-
- cmdp->timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
- if (vb > 2) {
- pr2ws("NVMe command:\n");
- dStrHex((const char *)cmdp, cmd_len, 1);
- }
- if (ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, cmdp) < 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 = cmdp->result;
- if (cp_cmd_out2resp) {
- n = ptp->io_hdr.max_response_len;
- if ((n > 0) && ptp->io_hdr.response) {
- n = (n < cmd_len) ? n : cmd_len;
- memcpy((uint8_t *)ptp->io_hdr.response, cmdp, n);
- ptp->io_hdr.response_len = n;
- } else
- ptp->io_hdr.response_len = 0;
- } else
- ptp->io_hdr.response_len = 0;
-
- if (vb > 2)
- pr2ws("%s: timeout_ms=%u, result=%u\n", __func__, cmdp->timeout_ms,
- cmdp->result);
- return 0;
-}
-#endif
-
-static int
-sntl_cache_identity(struct sg_pt_freebsd_scsi * ptp, int time_secs, int vb)
+sntl_cache_identity(struct freebsd_dev_channel * fdc_p, int vb)
{
int err;
- struct sg_nvme_passthru_cmd cmd;
+ struct nvme_pt_command npc;
+ uint8_t * npc_up = (uint8_t *)&npc;
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) {
+ fdc_p->nvme_id_ctlp = sg_memalign(pg_sz, pg_sz,
+ &fdc_p->free_nvme_id_ctlp, vb > 3);
+ if (NULL == fdc_p->nvme_id_ctlp) {
pr2ws("%s: sg_memalign() failed to get memory\n", __func__);
return SG_LIB_OS_BASE_ERR + ENOMEM;
}
- memset(&cmd, 0, sizeof(cmd));
- cmd.opcode = 0x6; /* Identify */
- cmd.cdw10 = 0x1; /* CNS=0x1 Identify controller */
- cmd.addr = (uint64_t)(sg_uintptr_t)ptp->nvme_id_ctlp;
- cmd.data_len = pg_sz;
- cmd.timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
- 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(npc_up, 0, sizeof(npc));
+ npc_up[SG_NVME_PT_OPCODE] = 0x6; /* Identify */
+ sg_put_unaligned_le32(0x0, npc_up + SG_NVME_PT_NSID);
+ /* CNS=0x1 Identify: controller */
+ sg_put_unaligned_le32(0x1, npc_up + SG_NVME_PT_CDW10);
+ sg_put_unaligned_le64((sg_uintptr_t)fdc_p->nvme_id_ctlp,
+ npc_up + SG_NVME_PT_ADDR);
+ sg_put_unaligned_le32(pg_sz, npc_up + SG_NVME_PT_DATA_LEN);
+ err = nvme_pt_low(fdc_p, fdc_p->nvme_id_ctlp, pg_sz, true, &npc, vb);
+ if (err) {
+ if (err < 0) {
+ err = -err;
+ if (vb > 1)
+ pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", __func__,
+ strerror(err), err);
+ return SG_LIB_OS_BASE_ERR + err;
+ } else {
+ fdc_p->nvme_status = err;
+ return SG_LIB_NVME_STATUS;
+ }
}
return 0;
}
@@ -1115,24 +1111,32 @@ static const char * nvme_scsi_vendor_str = "NVMe ";
static const uint16_t inq_resp_len = 36;
static int
-sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int time_secs,
- int vb)
+sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
{
bool evpd;
int res;
uint16_t k, n, alloc_len, pg_cd;
+ struct freebsd_dev_channel * fdc_p;
uint8_t inq_dout[128];
if (vb > 3)
- pr2ws("%s: time_secs=%d\n", __func__, time_secs);
+ pr2ws("%s: starting\n", __func__);
if (0x2 & cdbp[1]) {
mk_sense_invalid_fld(ptp, true, 1, 1, vb);
return 0;
}
- if (NULL == ptp->nvme_id_ctlp) {
- res = sntl_cache_identity(ptp, time_secs, vb);
- if (res)
+ fdc_p = get_fdc_p(ptp);
+ if (NULL == fdc_p) {
+ pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__);
+ return SG_LIB_OS_BASE_ERR + EINVAL;
+ }
+ if (NULL == fdc_p->nvme_id_ctlp) {
+ res = sntl_cache_identity(fdc_p, vb);
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb);
+ return 0;
+ } else if (res)
return res;
}
memset(inq_dout, 0, sizeof(inq_dout));
@@ -1154,7 +1158,7 @@ sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int time_secs,
/* inq_dout[0] = (PQ=0)<<5 | (PDT=0); prefer pdt=0xd --> SES */
inq_dout[1] = pg_cd;
sg_put_unaligned_be16(20, inq_dout + 2);
- memcpy(inq_dout + 4, ptp->nvme_id_ctlp + 4, 20); /* SN */
+ memcpy(inq_dout + 4, fdc_p->nvme_id_ctlp + 4, 20); /* SN */
n = 24;
break;
case 0x83:
@@ -1166,14 +1170,15 @@ sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int time_secs,
* 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 */
+ memcpy(inq_dout + 16, fdc_p->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 */
+ /* SN */
+ memcpy(inq_dout + 16 + k + 1, fdc_p->nvme_id_ctlp + 4, 20);
n = 16 + k + 1 + 20;
inq_dout[7] = 8 + k + 1 + 20;
sg_put_unaligned_be16(n - 4, inq_dout + 2);
@@ -1184,9 +1189,9 @@ sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int time_secs,
}
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;
+ n = (n < ptp->dxfer_len) ? n : ptp->dxfer_len;
if (n > 0)
- memcpy((uint8_t *)ptp->io_hdr.din_xferp, inq_dout, n);
+ memcpy((uint8_t *)ptp->dxferp, inq_dout, n);
}
} else { /* Standard INQUIRY response */
/* inq_dout[0] = (PQ=0)<<5 | (PDT=0); pdt=0 --> SBC; 0xd --> SES */
@@ -1196,39 +1201,46 @@ sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int time_secs,
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 */
+ memcpy(inq_dout + 16, fdc_p->nvme_id_ctlp + 24, 16);/* Prod <-- MN */
+ memcpy(inq_dout + 32, fdc_p->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;
+ n = (n < ptp->dxfer_len) ? n : ptp->dxfer_len;
if (n > 0)
- memcpy((uint8_t *)ptp->io_hdr.din_xferp, inq_dout, n);
+ memcpy((uint8_t *)ptp->dxferp, inq_dout, n);
}
}
return 0;
}
static int
-sntl_rluns(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs,
- int vb)
+sntl_rluns(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
{
int res;
uint16_t sel_report;
uint32_t alloc_len, k, n, num, max_nsid;
+ struct freebsd_dev_channel * fdc_p;
uint8_t * rl_doutp;
uint8_t * up;
if (vb > 3)
- pr2ws("%s: time_secs=%d\n", __func__, time_secs);
-
+ pr2ws("%s: starting\n", __func__);
+ fdc_p = get_fdc_p(ptp);
+ if (NULL == fdc_p) {
+ pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__);
+ return SG_LIB_OS_BASE_ERR + EINVAL;
+ }
sel_report = cdbp[2];
alloc_len = sg_get_unaligned_be32(cdbp + 6);
- if (NULL == ptp->nvme_id_ctlp) {
- res = sntl_cache_identity(ptp, time_secs, vb);
- if (res)
+ if (NULL == fdc_p->nvme_id_ctlp) {
+ res = sntl_cache_identity(fdc_p, vb);
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb);
+ return 0;
+ } else if (res)
return res;
}
- max_nsid = sg_get_unaligned_le32(ptp->nvme_id_ctlp + 516);
+ max_nsid = sg_get_unaligned_le32(fdc_p->nvme_id_ctlp + 516);
switch (sel_report) {
case 0:
case 2:
@@ -1240,7 +1252,7 @@ sntl_rluns(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs,
num = 0;
break;
case 0x11:
- num = (1 == ptp->nvme_nsid) ? max_nsid : 0;
+ num = (1 == fdc_p->nsid) ? max_nsid : 0;
break;
default:
if (vb > 1)
@@ -1261,10 +1273,10 @@ sntl_rluns(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs,
n+= 8;
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;
+ n = (n < (uint32_t)ptp->dxfer_len) ? n : (uint32_t)ptp->dxfer_len;
if (n > 0) {
- memcpy((uint8_t *)ptp->io_hdr.din_xferp, rl_doutp, n);
- ptp->io_hdr.din_resid = ptp->io_hdr.din_xfer_len - n;
+ memcpy((uint8_t *)ptp->dxferp, rl_doutp, n);
+ ptp->resid = ptp->dxfer_len - (int)n;
}
}
res = 0;
@@ -1273,33 +1285,49 @@ sntl_rluns(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs,
}
static int
-sntl_tur(struct sg_pt_linux_scsi * ptp, int time_secs, int vb)
+sntl_tur(struct sg_pt_freebsd_scsi * ptp, int vb)
{
int res, err;
uint32_t pow_state;
- struct sg_nvme_passthru_cmd cmd;
+ struct nvme_pt_command npc;
+ uint8_t * npc_up = (uint8_t *)&npc;
+ struct freebsd_dev_channel * fdc_p;
if (vb > 3)
- pr2ws("%s: time_secs=%d\n", __func__, time_secs);
- if (NULL == ptp->nvme_id_ctlp) {
- res = sntl_cache_identity(ptp, time_secs, vb);
- if (res)
+ pr2ws("%s: starting\n", __func__);
+ fdc_p = get_fdc_p(ptp);
+ if (NULL == fdc_p) {
+ pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__);
+ return SG_LIB_OS_BASE_ERR + EINVAL;
+ }
+ if (NULL == fdc_p->nvme_id_ctlp) {
+ res = sntl_cache_identity(fdc_p, vb);
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb);
+ return 0;
+ } else if (res)
return res;
}
- memset(&cmd, 0, sizeof(cmd));
- cmd.opcode = 0xa; /* Get feature */
- cmd.nsid = SG_NVME_BROADCAST_NSID;
- cmd.cdw10 = 0x2; /* SEL=0 (current), Feature=2 Power Management */
- cmd.timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
- if (ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd) < 0) {
- err = errno;
- if (vb > 2)
- pr2ws("%s: ioctl(NVME_ADMIN(Get feature)) failed: %s (errno=%d)"
- "\n", __func__, strerror(err), err);
- ptp->os_err = err;
- return -err;
+ memset(npc_up, 0, sizeof(npc));
+ npc_up[SG_NVME_PT_OPCODE] = 0xa; /* Get feature */
+ sg_put_unaligned_le32(SG_NVME_BROADCAST_NSID, npc_up + SG_NVME_PT_NSID);
+ /* SEL=0 (current), Feature=2 Power Management */
+ sg_put_unaligned_le32(0x2, npc_up + SG_NVME_PT_CDW10);
+ err = nvme_pt_low(fdc_p, NULL, 0, false, &npc, vb);
+ if (err) {
+ if (err < 0) {
+ err = -err;
+ if (vb > 1)
+ pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", __func__,
+ strerror(err), err);
+ return SG_LIB_OS_BASE_ERR + err;
+ } else {
+ fdc_p->nvme_status = err;
+ mk_sense_from_nvme_status(ptp, err, vb);
+ return 0;
+ }
}
- pow_state = (0x1f & cmd.result);
+ pow_state = (0x1f & fdc_p->nvme_result);
if (vb > 3)
pr2ws("%s: pow_state=%u\n", __func__, pow_state);
#if 0 /* pow_state bounces around too much on laptop */
@@ -1311,38 +1339,53 @@ sntl_tur(struct sg_pt_linux_scsi * ptp, int time_secs, int vb)
}
static int
-sntl_req_sense(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
- int time_secs, int vb)
+sntl_req_sense(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
{
bool desc;
int res, err;
uint32_t pow_state, alloc_len, n;
- struct sg_nvme_passthru_cmd cmd;
+ struct nvme_pt_command npc;
+ uint8_t * npc_up = (uint8_t *)&npc;
+ struct freebsd_dev_channel * fdc_p;
uint8_t rs_dout[64];
if (vb > 3)
- pr2ws("%s: time_secs=%d\n", __func__, time_secs);
- if (NULL == ptp->nvme_id_ctlp) {
- res = sntl_cache_identity(ptp, time_secs, vb);
- if (res)
+ pr2ws("%s: starting\n", __func__);
+ fdc_p = get_fdc_p(ptp);
+ if (NULL == fdc_p) {
+ pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__);
+ return SG_LIB_OS_BASE_ERR + EINVAL;
+ }
+ if (NULL == fdc_p->nvme_id_ctlp) {
+ res = sntl_cache_identity(fdc_p, vb);
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb);
+ return 0;
+ } else if (res)
return res;
}
desc = !!(0x1 & cdbp[1]);
alloc_len = cdbp[4];
- memset(&cmd, 0, sizeof(cmd));
- cmd.opcode = 0xa; /* Get feature */
- cmd.nsid = SG_NVME_BROADCAST_NSID;
- cmd.cdw10 = 0x2; /* SEL=0 (current), Feature=2 Power Management */
- cmd.timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
- if (ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd) < 0) {
- err = errno;
- if (vb > 2)
- pr2ws("%s: ioctl(NVME_ADMIN(Get feature)) failed: %s (errno=%d)"
- "\n", __func__, strerror(err), err);
- ptp->os_err = err;
- return -err;
+ memset(npc_up, 0, sizeof(npc));
+ npc_up[SG_NVME_PT_OPCODE] = 0xa; /* Get feature */
+ sg_put_unaligned_le32(SG_NVME_BROADCAST_NSID, npc_up + SG_NVME_PT_NSID);
+ /* SEL=0 (current), Feature=2 Power Management */
+ sg_put_unaligned_le32(0x2, npc_up + SG_NVME_PT_CDW10);
+ err = nvme_pt_low(fdc_p, NULL, 0, false, &npc, vb);
+ if (err) {
+ if (err < 0) {
+ err = -err;
+ if (vb > 1)
+ pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", __func__,
+ strerror(err), err);
+ return SG_LIB_OS_BASE_ERR + err;
+ } else {
+ fdc_p->nvme_status = err;
+ mk_sense_from_nvme_status(ptp, err, vb);
+ return 0;
+ }
}
- pow_state = (0x1f & cmd.result);
+ pow_state = (0x1f & fdc_p->nvme_result);
if (vb > 3)
pr2ws("%s: pow_state=%u\n", __func__, pow_state);
memset(rs_dout, 0, sizeof(rs_dout));
@@ -1354,10 +1397,10 @@ sntl_req_sense(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
NO_ADDITIONAL_SENSE, 0);
n = desc ? 8 : 18;
n = (n < alloc_len) ? n : alloc_len;
- n = (n < ptp->io_hdr.din_xfer_len) ? n : ptp->io_hdr.din_xfer_len;
+ n = (n < (uint32_t)ptp->dxfer_len) ? n : (uint32_t)ptp->dxfer_len;
if (n > 0) {
- memcpy((uint8_t *)ptp->io_hdr.din_xferp, rs_dout, n);
- ptp->io_hdr.din_resid = ptp->io_hdr.din_xfer_len - n;
+ memcpy((uint8_t *)ptp->dxferp, rs_dout, n);
+ ptp->resid = ptp->dxfer_len - (int)n;
}
return 0;
}
@@ -1368,26 +1411,57 @@ sntl_req_sense(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
* (SCSI Enclosure Services) use of diagnostics pages that are
* related to SES. */
static int
-sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
- int time_secs, int vb)
+sntl_senddiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
{
bool pf, self_test;
+ int err;
uint8_t st_cd, dpg_cd;
- uint32_t alloc_len, n, dout_len, dpg_len;
+ uint32_t alloc_len, n, dout_len, dpg_len, nvme_dst;
uint32_t pg_sz = sg_get_page_size();
const uint8_t * dop;
- struct sg_nvme_passthru_cmd cmd;
+ struct nvme_pt_command npc;
+ uint8_t * npc_up = (uint8_t *)&npc;
+ struct freebsd_dev_channel * fdc_p;
st_cd = 0x7 & (cdbp[1] >> 5);
pf = !! (0x4 & cdbp[1]);
self_test = !! (0x10 & cdbp[1]);
if (vb > 3)
- pr2ws("%s: pf=%d, self_test=%d (st_code=%d)\n", __func__, (int)pf,
+ pr2ws("%s: pf=%d, self_test=%d, st_code=%d\n", __func__, (int)pf,
(int)self_test, (int)st_cd);
- if (self_test)
- return 0; /* NVMe has no self-test, just say OK */
+ fdc_p = get_fdc_p(ptp);
+ if (NULL == fdc_p) {
+ pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__);
+ return SG_LIB_OS_BASE_ERR + EINVAL;
+ }
+ if (self_test || st_cd) {
+ memset(npc_up, 0, sizeof(npc));
+ npc_up[SG_NVME_PT_OPCODE] = 0x14; /* Device self-test */
+ /* just this namespace (if there is one) and controller */
+ sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID);
+ switch (st_cd) {
+ case 0: /* Here if self_test is set, do short self-test */
+ case 1: /* Background short */
+ case 5: /* Foreground short */
+ nvme_dst = 1;
+ break;
+ case 2: /* Background extended */
+ case 6: /* Foreground extended */
+ nvme_dst = 2;
+ break;
+ case 4: /* Abort self-test */
+ nvme_dst = 0xf;
+ break;
+ default:
+ pr2ws("%s: bad self-test code [0x%x]\n", __func__, st_cd);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ sg_put_unaligned_le32(nvme_dst, npc_up + SG_NVME_PT_CDW10);
+ err = nvme_pt_low(fdc_p, NULL, 0x0, false, &npc, vb);
+ goto do_low;
+ }
alloc_len = sg_get_unaligned_be16(cdbp + 3); /* parameter list length */
- dout_len = ptp->io_hdr.dout_xfer_len;
+ dout_len = ptp->dxfer_len;
if (pf) {
if (0 == alloc_len) {
mk_sense_invalid_fld(ptp, true, 3, 7, vb);
@@ -1417,11 +1491,11 @@ sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
}
n = dout_len;
n = (n < alloc_len) ? n : alloc_len;
- dop = (const uint8_t *)ptp->io_hdr.dout_xferp;
+ dop = (const uint8_t *)ptp->dxferp;
if (! is_aligned(dop, pg_sz)) { /* caller best use sg_memalign(,pg_sz) */
if (vb)
pr2ws("%s: dout [0x%" PRIx64 "] not page aligned\n", __func__,
- (uint64_t)ptp->io_hdr.dout_xferp);
+ (uint64_t)ptp->dxferp);
return SCSI_PT_DO_BAD_PARAMS;
}
dpg_cd = dop[0];
@@ -1432,15 +1506,35 @@ sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
if (vb)
pr2ws("%s: passing through d_pg=0x%x, len=%u to NVME_MI SES send\n",
__func__, dpg_cd, dpg_len);
- memset(&cmd, 0, sizeof(cmd));
- cmd.opcode = 0x1d; /* MI send; hmmm same opcode as SEND DIAG */
- cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.dout_xferp;
- cmd.data_len = 0x1000; /* NVMe 4k page size. Maybe determine this? */
- /* dout_len > 0x1000, is this a problem?? */
- cmd.cdw10 = 0x0804; /* NVMe Message Header */
- cmd.cdw11 = 0x9; /* nvme_mi_ses_send; (0x8 -> mi_ses_recv) */
- cmd.cdw13 = n;
- return do_nvme_admin_cmd(ptp, &cmd, time_secs, false, vb);
+ memset(npc_up, 0, sizeof(npc));
+ npc_up[SG_NVME_PT_OPCODE] = 0x1d; /* MI send; same opcode as SEND DIAG */
+ sg_put_unaligned_le64((sg_uintptr_t)ptp->dxferp,
+ npc_up + SG_NVME_PT_ADDR);
+ /* NVMe 4k page size. Maybe determine this? */
+ /* dout_len > 0x1000, is this a problem?? */
+ sg_put_unaligned_le32(0x1000, npc_up + SG_NVME_PT_DATA_LEN);
+ /* NVMe Message Header */
+ sg_put_unaligned_le32(0x0804, npc_up + SG_NVME_PT_CDW10);
+ /* nvme_mi_ses_send; (0x8 -> mi_ses_recv) */
+ sg_put_unaligned_le32(0x9, npc_up + SG_NVME_PT_CDW11);
+ /* data-out length I hope */
+ sg_put_unaligned_le32(n, npc_up + SG_NVME_PT_CDW13);
+ err = nvme_pt_low(fdc_p, ptp->dxferp, 0x1000, false, &npc, vb);
+do_low:
+ if (err) {
+ if (err < 0) {
+ err = -err;
+ if (vb > 1)
+ pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n",
+ __func__, strerror(err), err);
+ return SG_LIB_OS_BASE_ERR + err;
+ } else {
+ fdc_p->nvme_status = err;
+ mk_sense_from_nvme_status(ptp, err, vb);
+ return 0;
+ }
+ }
+ return 0;
}
/* This is not really a SNTL. For SCSI RECEIVE DIAGNOSTIC RESULTS(PCV=1)
@@ -1449,16 +1543,17 @@ sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
* SES (SCSI Enclosure Services) use of diagnostics pages that are
* related to SES. */
static int
-sntl_recvdiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
- int time_secs, int vb)
+sntl_recvdiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
{
bool pcv;
- int res;
+ int err;
uint8_t dpg_cd;
uint32_t alloc_len, n, din_len;
uint32_t pg_sz = sg_get_page_size();
const uint8_t * dip;
- struct sg_nvme_passthru_cmd cmd;
+ struct nvme_pt_command npc;
+ uint8_t * npc_up = (uint8_t *)&npc;
+ struct freebsd_dev_channel * fdc_p;
pcv = !! (0x1 & cdbp[1]);
dpg_cd = cdbp[2];
@@ -1466,7 +1561,12 @@ sntl_recvdiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
if (vb > 3)
pr2ws("%s: dpg_cd=0x%x, pcv=%d, alloc_len=0x%x\n", __func__,
dpg_cd, (int)pcv, alloc_len);
- din_len = ptp->io_hdr.din_xfer_len;
+ fdc_p = get_fdc_p(ptp);
+ if (NULL == fdc_p) {
+ pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__);
+ return SG_LIB_OS_BASE_ERR + EINVAL;
+ }
+ din_len = ptp->dxfer_len;
if (pcv) {
if (0 == alloc_len) {
/* T10 says not an error, hmmm */
@@ -1491,91 +1591,127 @@ sntl_recvdiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
}
n = din_len;
n = (n < alloc_len) ? n : alloc_len;
- dip = (const uint8_t *)ptp->io_hdr.din_xferp;
+ dip = (const uint8_t *)ptp->dxferp;
if (! is_aligned(dip, pg_sz)) { /* caller best use sg_memalign(,pg_sz) */
if (vb)
pr2ws("%s: din [0x%" PRIx64 "] not page aligned\n", __func__,
- (uint64_t)ptp->io_hdr.din_xferp);
+ (uint64_t)ptp->dxferp);
return SCSI_PT_DO_BAD_PARAMS;
}
if (vb)
pr2ws("%s: expecting d_pg=0x%x from NVME_MI SES receive\n", __func__,
dpg_cd);
- memset(&cmd, 0, sizeof(cmd));
- cmd.opcode = 0x1e; /* MI receive */
- cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.din_xferp;
- cmd.data_len = 0x1000; /* NVMe 4k page size. Maybe determine this? */
- /* din_len > 0x1000, is this a problem?? */
- cmd.cdw10 = 0x0804; /* NVMe Message Header */
- cmd.cdw11 = 0x8; /* nvme_mi_ses_receive */
- cmd.cdw12 = dpg_cd;
- cmd.cdw13 = n;
- res = do_nvme_admin_cmd(ptp, &cmd, time_secs, false, vb);
- ptp->io_hdr.din_resid = din_len - n;
- return res;
+ memset(npc_up, 0, sizeof(npc));
+ npc_up[SG_NVME_PT_OPCODE] = 0x1e; /* MI receive */
+ sg_put_unaligned_le64((sg_uintptr_t)ptp->dxferp,
+ npc_up + SG_NVME_PT_ADDR);
+ /* NVMe 4k page size. Maybe determine this? */
+ /* dout_len > 0x1000, is this a problem?? */
+ sg_put_unaligned_le32(0x1000, npc_up + SG_NVME_PT_DATA_LEN);
+ /* NVMe Message Header */
+ sg_put_unaligned_le32(0x0804, npc_up + SG_NVME_PT_CDW10);
+ /* nvme_mi_ses_receive */
+ sg_put_unaligned_le32(0x8, npc_up + SG_NVME_PT_CDW11);
+ sg_put_unaligned_le32(dpg_cd, npc_up + SG_NVME_PT_CDW12);
+ /* data-in length I hope */
+ sg_put_unaligned_le32(n, npc_up + SG_NVME_PT_CDW13);
+ err = nvme_pt_low(fdc_p, ptp->dxferp, 0x1000, true, &npc, vb);
+ if (err) {
+ if (err < 0) {
+ err = -err;
+ if (vb > 1)
+ pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n",
+ __func__, strerror(err), err);
+ return SG_LIB_OS_BASE_ERR + err;
+ } else {
+ fdc_p->nvme_status = err;
+ mk_sense_from_nvme_status(ptp, err, vb);
+ return 0;
+ }
+ }
+ ptp->resid = din_len - 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)
+ * The time_secs argument is ignored. */
+static int
+sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb)
{
- bool scsi_cmd;
- int n, len;
- struct sg_pt_linux_scsi * ptp = &vp->impl;
- struct sg_nvme_passthru_cmd cmd;
+ bool scsi_cmd, in_xfer;
+ int n, err, len, io_len;
+ struct nvme_pt_command npc;
+ uint8_t * dxferp;
+ uint8_t * npc_up = (uint8_t *)&npc;
+ struct freebsd_dev_channel * fdc_p;
+ struct sg_pt_freebsd_scsi * ptp = &vp->impl;
const uint8_t * cdbp;
- if (! ptp->io_hdr.request) {
+ if (vb > 3)
+ pr2ws("%s: fd=%d\n", __func__, fd);
+ if (! ptp->cdb) {
if (vb)
- pr2ws("No NVMe command given (set_scsi_pt_cdb())\n");
+ pr2ws("%s: No NVMe command given (set_scsi_pt_cdb())\n",
+ __func__);
return SCSI_PT_DO_BAD_PARAMS;
}
- if (fd >= 0) {
- if ((ptp->dev_fd >= 0) && (fd != ptp->dev_fd)) {
- if (vb)
- pr2ws("%s: file descriptor given to create() and here "
- "differ\n", __func__);
+ fdc_p = get_fdc_p(ptp);
+ if (fd < 0) {
+ if (NULL == fdc_p) {
+ pr2ws("%s: no device handle in object or fd ?\n", __func__);
+ return SG_LIB_FILE_ERROR;
+ }
+ } else {
+ int han = fd - FREEBSD_FDOFFSET;
+
+ if ((han < 0) || (han >= FREEBSD_MAXDEV)) {
+ pr2ws("%s: argument 'fd' is bad\n", __func__);
return SCSI_PT_DO_BAD_PARAMS;
}
- ptp->dev_fd = fd;
- } else if (ptp->dev_fd < 0) {
- if (vb)
- pr2ws("%s: invalid file descriptors\n", __func__);
- return SCSI_PT_DO_BAD_PARAMS;
+ if (NULL == devicetable[han]) {
+ pr2ws("%s: argument 'fd' is bad (2)\n", __func__);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ if (fdc_p && (fdc_p != devicetable[han])) {
+ pr2ws("%s: different device handle in object and fd ?\n",
+ __func__);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ if (NULL == fdc_p) {
+ ptp->dev_han = fd;
+ fdc_p = devicetable[han];
+ }
}
- n = ptp->io_hdr.request_len;
- cdbp = (const uint8_t *)ptp->io_hdr.request;
+
+ n = ptp->cdb_len;
+ cdbp = (const uint8_t *)ptp->cdb;
if (vb > 3)
- pr2ws("%s: opcode=0x%x, fd=%d, time_secs=%d\n", __func__, cdbp[0],
- fd, time_secs);
- scsi_cmd = is_scsi_command(cdbp, n);
+ pr2ws("%s: opcode=0x%x, fd=%d\n", __func__, cdbp[0], fd);
+ scsi_cmd = sg_is_scsi_cdb(cdbp, n);
if (scsi_cmd) {
switch (cdbp[0]) {
case SCSI_INQUIRY_OPC:
- return sntl_inq(ptp, cdbp, time_secs, vb);
+ return sntl_inq(ptp, cdbp, vb);
case SCSI_REPORT_LUNS_OPC:
- return sntl_rluns(ptp, cdbp, time_secs, vb);
+ return sntl_rluns(ptp, cdbp, vb);
case SCSI_TEST_UNIT_READY_OPC:
- return sntl_tur(ptp, time_secs, vb);
+ return sntl_tur(ptp, vb);
case SCSI_REQUEST_SENSE_OPC:
- return sntl_req_sense(ptp, cdbp, time_secs, vb);
+ return sntl_req_sense(ptp, cdbp, vb);
case SCSI_SEND_DIAGNOSTIC_OPC:
- return sntl_senddiag(ptp, cdbp, time_secs, vb);
+ return sntl_senddiag(ptp, cdbp, vb);
case SCSI_RECEIVE_DIAGNOSTIC_OPC:
- return sntl_recvdiag(ptp, cdbp, time_secs, vb);
-// xxxxxxxxxx
+ return sntl_recvdiag(ptp, cdbp, vb);
default:
mk_sense_asc_ascq(ptp, SPC_SK_ILLEGAL_REQUEST, INVALID_OPCODE,
0, vb);
return 0;
}
}
- len = (int)sizeof(cmd);
+ len = (int)sizeof(npc.cmd);
n = (n < len) ? n : len;
if (n < 64) {
if (vb)
@@ -1583,15 +1719,42 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
n);
return SCSI_PT_DO_BAD_PARAMS;
}
- memcpy(&cmd, (const uint8_t *)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 = (uint64_t)(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 = (uint64_t)(sg_uintptr_t)ptp->io_hdr.dout_xferp;
- }
- return do_nvme_admin_cmd(ptp, &cmd, time_secs, true, vb);
+ memcpy(npc_up, (const uint8_t *)ptp->cdb, n);
+ if (n < len) /* zero out rest of 'npc' */
+ memset(npc_up + n, 0, len - n);
+ in_xfer = false;
+ io_len = 0;
+ dxferp = NULL;
+ if (ptp->dxfer_ilen > 0) {
+ in_xfer = true;
+ io_len = ptp->dxfer_ilen;
+ dxferp = ptp->dxferip;
+ sg_put_unaligned_le32(ptp->dxfer_ilen, npc_up + SG_NVME_PT_DATA_LEN);
+ sg_put_unaligned_le64((sg_uintptr_t)ptp->dxferip,
+ npc_up + SG_NVME_PT_ADDR);
+ } else if (ptp->dxfer_olen > 0) {
+ in_xfer = false;
+ io_len = ptp->dxfer_olen;
+ dxferp = ptp->dxferop;
+ sg_put_unaligned_le32(ptp->dxfer_olen, npc_up + SG_NVME_PT_DATA_LEN);
+ sg_put_unaligned_le64((sg_uintptr_t)ptp->dxferop,
+ npc_up + SG_NVME_PT_ADDR);
+ }
+ err = nvme_pt_low(fdc_p, dxferp, io_len, in_xfer, &npc, vb);
+ if (err) {
+ if (err < 0) {
+ err = -err;
+ if (vb > 1)
+ pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n",
+ __func__, strerror(err), err);
+ return SG_LIB_OS_BASE_ERR + err;
+ } else {
+ fdc_p->nvme_status = err;
+ mk_sense_from_nvme_status(ptp, err, vb);
+ return 0;
+ }
+ }
+ if (in_xfer)
+ ptp->resid = 0; /* Just hoping ... */
+ return 0;
}
diff --git a/lib/sg_pt_linux.c b/lib/sg_pt_linux.c
index 91c4c250..3512ea1e 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.32 20171207 */
+/* sg_pt_linux version 1.33 20171227 */
#include <stdio.h>
@@ -360,6 +360,12 @@ scsi_pt_open_flags(const char * device_name, int flags, int verbose)
{
int fd;
+bool ok;
+char b[512];
+ok = sg_get_nvme_char_devname(device_name, sizeof(b), b);
+pr2ws("%s: sg_get_nvme_char_devname() --> ok=%s\n", __func__, ok ? "true" : "false");
+if (ok)
+pr2ws("\t trimmed devname: %s\n", b);
if (! sg_bsg_nvme_char_major_checked) {
sg_bsg_nvme_char_major_checked = true;
sg_find_bsg_nvme_char_major(verbose);
@@ -433,6 +439,11 @@ destruct_scsi_pt_obj(struct sg_pt_base * vp)
{
struct sg_pt_linux_scsi * ptp = &vp->impl;
+ if (ptp->free_nvme_id_ctlp) {
+ free(ptp->free_nvme_id_ctlp);
+ ptp->free_nvme_id_ctlp = NULL;
+ ptp->nvme_id_ctlp = NULL;
+ }
if (ptp)
free(ptp);
}
diff --git a/lib/sg_pt_linux_nvme.c b/lib/sg_pt_linux_nvme.c
index c18a854a..a3d092d6 100644
--- a/lib/sg_pt_linux_nvme.c
+++ b/lib/sg_pt_linux_nvme.c
@@ -136,35 +136,32 @@ pr2ws(const char * fmt, ...)
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)
+/* This trims given NVMe block device name in Linux (e.g. /dev/nvme0n1p5)
+ * to the name of its associated char device (e.g. /dev/nvme0). If this
+ * occurs true is returned and the char device name is placed in 'b' (as
+ * long as b_len is sufficient). Otherwise false is returned. */
+ bool
+sg_get_nvme_char_devname(const char * nvme_block_devname, uint32_t b_len,
+ char * b)
{
- 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;
+ uint32_t n, tlen;
+ const char * cp;
+ char buff[8];
+
+ if ((NULL == b) || (b_len < 5))
+ return false; /* degenerate cases */
+ cp = strstr(nvme_block_devname, "nvme");
+ if (NULL == cp)
+ return false; /* expected to find "nvme" in given name */
+ if (1 != sscanf(cp, "nvme%u", &n))
+ return false; /* didn't find valid "nvme<number>" */
+ snprintf(buff, sizeof(buff), "%u", n);
+ tlen = (cp - nvme_block_devname) + 4 + strlen(buff);
+ if ((tlen + 1) > b_len)
+ return false; /* b isn't long enough to fit output */
+ memcpy(b, nvme_block_devname, tlen);
+ b[tlen] = '\0';
+ return true;
}
static void
@@ -210,6 +207,37 @@ mk_sense_asc_ascq(struct sg_pt_linux_scsi * ptp, int sk, int asc, int ascq,
ascq);
}
+static void
+mk_sense_from_nvme_status(struct sg_pt_linux_scsi * ptp, int vb)
+{
+ bool ok;
+ bool dsense = ptp->scsi_dsense;
+ int n;
+ uint8_t sstatus, sk, asc, ascq;
+ uint8_t * sbp = (uint8_t *)ptp->io_hdr.response;
+
+ ok = sg_nvme_status2scsi(ptp->nvme_status, &sstatus, &sk, &asc, &ascq);
+ if (! ok) { /* can't find a mapping to a SCSI error, so ... */
+ sstatus = SAM_STAT_CHECK_CONDITION;
+ sk = SPC_SK_ILLEGAL_REQUEST;
+ asc = 0xb;
+ ascq = 0x0; /* asc: "WARNING" purposely vague */
+ }
+
+ ptp->io_hdr.device_status = sstatus;
+ n = ptp->io_hdr.max_response_len;
+ if ((n < 8) || ((! dsense) && (n < 14))) {
+ pr2ws("%s: sense_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, sk, asc, ascq);
+ if (vb > 3)
+ pr2ws("%s: [status, sense_key,asc,ascq]: [0x%x, 0x%x,0x%x,0x%x]\n",
+ __func__, sstatus, sk, asc, 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,
@@ -253,53 +281,94 @@ mk_sense_invalid_fld(struct sg_pt_linux_scsi * ptp, bool in_cdb, int in_byte,
__func__, asc, in_cdb ? 'C' : 'D', in_byte, in_bit);
}
+/* Returns 0 for success. Returns SG_LIB_NVME_STATUS if there is non-zero
+ * NVMe status (from the completion queue) with the value in
+ * ptp->nvme_status. If Unix error from ioctl add equivalent errno value to
+ * SG_LIB_OS_BASE_ERR. Should not return negative values. CDW0 from
+ * the completion queue is placed in ptp->nvme_result on success. */
static int
do_nvme_admin_cmd(struct sg_pt_linux_scsi * ptp,
- struct sg_nvme_passthru_cmd *cmdp, int time_secs, int vb)
+ struct sg_nvme_passthru_cmd *cmdp, const void * dp,
+ bool is_read, int time_secs, int vb)
{
const uint32_t cmd_len = sizeof(struct sg_nvme_passthru_cmd);
int res;
+ uint32_t n;
+ const uint8_t * up = ((const uint8_t *)cmdp) + SG_NVME_PT_OPCODE;
cmdp->timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
if (vb > 2) {
pr2ws("NVMe command:\n");
- dStrHex((const char *)cmdp, cmd_len, 1);
+ dStrHexErr((const char *)cmdp, cmd_len, 1);
+ if ((vb > 3) && (! is_read) && dp) {
+ uint32_t len = sg_get_unaligned_le32(up + SG_NVME_PT_DATA_LEN);
+
+ if (len > 0) {
+ n = len;
+ if ((len < 512) || (vb > 5))
+ pr2ws("\nData-out buffer (%u bytes):\n", n);
+ else {
+ pr2ws("\nData-out buffer (first 512 of %u bytes):\n", n);
+ n = 512;
+ }
+ dStrHexErr((const char *)dp, n, 0);
+ }
+ }
}
res = ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, cmdp);
if (0 != res) {
if (res < 0) { /* OS error (errno negated) */
- ptp->os_err = -res;
- if (vb > 2)
- pr2ws("%s: ioctl(ADMIN_CMD) failed: %s "
- "(errno=%d)\n", __func__, strerror(ptp->os_err),
- ptp->os_err);
- return -res;
+ res = (-res & 0x3ff); /* clear DNR and More, if present */
+ ptp->os_err = res;
+ if (vb > 3) {
+ pr2ws("%s: ioctl opcode=0x%x failed: %s "
+ "(errno=%d)\n", __func__, *up, strerror(res), res);
+ }
+ return SG_LIB_OS_BASE_ERR + res;
} else { /* NVMe errors are positive return values */
ptp->nvme_status = res;
- if (vb > 2)
- pr2ws("%s: ioctl(ADMIN_CMD) failed: NVMe status "
- "(SF) 0x%x\n", __func__, res);
+ if (vb > 2) {
+ char b[80];
+
+ pr2ws("%s: ioctl opcode=0x%x failed: NVMe status: %s "
+ "[0x%x]\n", __func__, *up,
+ sg_get_nvme_cmd_status_str(res, sizeof(b), b), res);
+ }
return SG_LIB_NVME_STATUS;
}
} else {
ptp->os_err = 0;
ptp->nvme_status = 0;
+ if ((vb > 3) && is_read && dp) {
+ uint32_t len = sg_get_unaligned_le32(up + SG_NVME_PT_DATA_LEN);
+
+ if (len > 0) {
+ n = len;
+ if ((len < 1024) || (vb > 5))
+ pr2ws("\nData-in buffer (%u bytes):\n", n);
+ else {
+ pr2ws("\nData-in buffer (first 1024 of %u bytes):\n", n);
+ n = 1024;
+ }
+ dStrHexErr((const char *)dp, n, 0);
+ }
+ }
}
ptp->nvme_result = cmdp->result;
- ptp->io_hdr.response_len = 0;
return 0;
}
+/* Returns 0 on success; otherwise a positive value is returned */
static int
sntl_cache_identity(struct sg_pt_linux_scsi * ptp, int time_secs, int vb)
{
- int res;
struct sg_nvme_passthru_cmd cmd;
uint32_t pg_sz = sg_get_page_size();
+ void * vp;
- ptp->nvme_id_ctlp = sg_memalign(pg_sz, pg_sz, &ptp->free_nvme_id_ctlp,
- vb > 3);
- if (NULL == ptp->nvme_id_ctlp) {
+ vp = sg_memalign(pg_sz, pg_sz, &ptp->free_nvme_id_ctlp, vb > 3);
+ ptp->nvme_id_ctlp = vp;
+ if (NULL == vp) {
pr2ws("%s: sg_memalign() failed to get memory\n", __func__);
return SG_LIB_OS_BASE_ERR + ENOMEM;
}
@@ -308,30 +377,7 @@ sntl_cache_identity(struct sg_pt_linux_scsi * ptp, int time_secs, int vb)
cmd.cdw10 = 0x1; /* CNS=0x1 Identify controller */
cmd.addr = (uint64_t)(sg_uintptr_t)ptp->nvme_id_ctlp;
cmd.data_len = pg_sz;
- cmd.timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
- res = ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd);
- if (0 != res) {
- if (res < 0) { /* OS error (errno negated) */
- ptp->os_err = -res;
- if (vb > 2)
- pr2ws("%s: ioctl(ADMIN_CMD(Identify)) failed: %s "
- "(errno=%d)\n", __func__, strerror(ptp->os_err),
- ptp->os_err);
- return -res;
- } else { /* NVMe errors are positive return values */
- ptp->nvme_status = res;
- if (vb > 2)
- pr2ws("%s: ioctl(NVME_IOCTL_ADMIN_CMD) failed: NVMe status "
- "(SF) 0x%x\n", __func__, res);
- return SG_LIB_NVME_STATUS;
- }
- } else {
- ptp->os_err = 0;
- ptp->nvme_status = 0;
- }
- ptp->nvme_result = cmd.result;
- ptp->io_hdr.response_len = 0;
- return 0;
+ return do_nvme_admin_cmd(ptp, &cmd, vp, true, time_secs, vb);
}
static const char * nvme_scsi_vendor_str = "NVMe ";
@@ -355,7 +401,10 @@ sntl_inq(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs,
}
if (NULL == ptp->nvme_id_ctlp) {
res = sntl_cache_identity(ptp, time_secs, vb);
- if (res)
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, vb);
+ return 0;
+ } else if (res)
return res;
}
memset(inq_dout, 0, sizeof(inq_dout));
@@ -448,7 +497,10 @@ sntl_rluns(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs,
alloc_len = sg_get_unaligned_be32(cdbp + 6);
if (NULL == ptp->nvme_id_ctlp) {
res = sntl_cache_identity(ptp, time_secs, vb);
- if (res)
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, vb);
+ return 0;
+ } else if (res)
return res;
}
max_nsid = sg_get_unaligned_le32(ptp->nvme_id_ctlp + 516);
@@ -502,11 +554,14 @@ sntl_tur(struct sg_pt_linux_scsi * ptp, int time_secs, int vb)
uint32_t pow_state;
struct sg_nvme_passthru_cmd cmd;
- if (vb > 3)
+ if (vb > 4)
pr2ws("%s: time_secs=%d\n", __func__, time_secs);
if (NULL == ptp->nvme_id_ctlp) {
res = sntl_cache_identity(ptp, time_secs, vb);
- if (res)
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, vb);
+ return 0;
+ } else if (res)
return res;
}
memset(&cmd, 0, sizeof(cmd));
@@ -514,28 +569,17 @@ sntl_tur(struct sg_pt_linux_scsi * ptp, int time_secs, int vb)
cmd.nsid = SG_NVME_BROADCAST_NSID;
cmd.cdw10 = 0x2; /* SEL=0 (current), Feature=2 Power Management */
cmd.timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
- res = ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd);
+ res = do_nvme_admin_cmd(ptp, &cmd, NULL, false, time_secs, vb);
if (0 != res) {
- if (res < 0) { /* OS error (errno negated) */
- ptp->os_err = -res;
- if (vb > 2)
- pr2ws("%s: ioctl(ADMIN_CMD(Get feature)) failed: %s "
- "(errno=%d)\n", __func__, strerror(ptp->os_err),
- ptp->os_err);
- return -res;
- } else { /* NVMe errors are positive return values */
- ptp->nvme_status = res;
- if (vb > 2)
- pr2ws("%s: ioctl(ADMIN_CMD(Get feature)) failed: NVMe "
- "status (SF) 0x%x\n", __func__, res);
- return SG_LIB_NVME_STATUS;
- }
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, vb);
+ return 0;
+ } else
+ return res;
} else {
ptp->os_err = 0;
ptp->nvme_status = 0;
}
- ptp->nvme_result = cmd.result;
- ptp->io_hdr.response_len = 0;
pow_state = (0x1f & ptp->nvme_result);
if (vb > 3)
pr2ws("%s: pow_state=%u\n", __func__, pow_state);
@@ -561,7 +605,10 @@ sntl_req_sense(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
pr2ws("%s: time_secs=%d\n", __func__, time_secs);
if (NULL == ptp->nvme_id_ctlp) {
res = sntl_cache_identity(ptp, time_secs, vb);
- if (res)
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, vb);
+ return 0;
+ } else if (res)
return res;
}
desc = !!(0x1 & cdbp[1]);
@@ -571,38 +618,28 @@ sntl_req_sense(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
cmd.nsid = SG_NVME_BROADCAST_NSID;
cmd.cdw10 = 0x2; /* SEL=0 (current), Feature=2 Power Management */
cmd.timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
- res = ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd);
+ res = do_nvme_admin_cmd(ptp, &cmd, NULL, false, time_secs, vb);
if (0 != res) {
- if (res < 0) { /* OS error (errno negated) */
- ptp->os_err = -res;
- if (vb > 2)
- pr2ws("%s: ioctl(ADMIN(Get feature)) failed: %s "
- "(errno=%d)\n", __func__, strerror(ptp->os_err),
- ptp->os_err);
- return -res;
- } else { /* NVMe errors are positive return values */
- ptp->nvme_status = res;
- if (vb > 2)
- pr2ws("%s: ioctl(ADMIN(Get feature)) failed: NVMe "
- "status (SF) 0x%x\n", __func__, res);
- return SG_LIB_NVME_STATUS;
- }
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, vb);
+ return 0;
+ } else
+ return res;
} else {
ptp->os_err = 0;
ptp->nvme_status = 0;
}
- ptp->nvme_result = cmd.result;
ptp->io_hdr.response_len = 0;
pow_state = (0x1f & ptp->nvme_result);
if (vb > 3)
pr2ws("%s: pow_state=%u\n", __func__, pow_state);
memset(rs_dout, 0, sizeof(rs_dout));
if (pow_state)
- build_sense_buffer(desc, rs_dout, SPC_SK_NO_SENSE,
- LOW_POWER_COND_ON_ASC, 0);
+ build_sense_buffer(desc, rs_dout, SPC_SK_NO_SENSE,
+ LOW_POWER_COND_ON_ASC, 0);
else
- build_sense_buffer(desc, rs_dout, SPC_SK_NO_SENSE,
- NO_ADDITIONAL_SENSE, 0);
+ build_sense_buffer(desc, rs_dout, SPC_SK_NO_SENSE,
+ NO_ADDITIONAL_SENSE, 0);
n = desc ? 8 : 18;
n = (n < alloc_len) ? n : alloc_len;
n = (n < ptp->io_hdr.din_xfer_len) ? n : ptp->io_hdr.din_xfer_len;
@@ -623,11 +660,13 @@ sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
int time_secs, int vb)
{
bool pf, self_test;
+ int res;
uint8_t st_cd, dpg_cd;
- uint32_t alloc_len, n, dout_len, dpg_len;
+ uint32_t alloc_len, n, dout_len, dpg_len, nvme_dst;
uint32_t pg_sz = sg_get_page_size();
const uint8_t * dop;
struct sg_nvme_passthru_cmd cmd;
+ uint8_t * cmd_up = (uint8_t *)&cmd;
st_cd = 0x7 & (cdbp[1] >> 5);
self_test = !! (0x4 & cdbp[1]);
@@ -635,8 +674,38 @@ sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
if (vb > 3)
pr2ws("%s: pf=%d, self_test=%d (st_code=%d)\n", __func__, (int)pf,
(int)self_test, (int)st_cd);
- if (self_test)
- return 0; /* NVMe has no self-test, just say OK */
+ if (self_test || st_cd) {
+ memset(cmd_up, 0, sizeof(cmd));
+ cmd_up[SG_NVME_PT_OPCODE] = 0x14; /* Device self-test */
+ /* just this namespace (if there is one) and controller */
+ sg_put_unaligned_le32(ptp->nvme_nsid, cmd_up + SG_NVME_PT_NSID);
+ switch (st_cd) {
+ case 0: /* Here if self_test is set, do short self-test */
+ case 1: /* Background short */
+ case 5: /* Foreground short */
+ nvme_dst = 1;
+ break;
+ case 2: /* Background extended */
+ case 6: /* Foreground extended */
+ nvme_dst = 2;
+ break;
+ case 4: /* Abort self-test */
+ nvme_dst = 0xf;
+ break;
+ default:
+ pr2ws("%s: bad self-test code [0x%x]\n", __func__, st_cd);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ sg_put_unaligned_le32(nvme_dst, cmd_up + SG_NVME_PT_CDW10);
+ res = do_nvme_admin_cmd(ptp, &cmd, NULL, false, time_secs, vb);
+ if (0 != res) {
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, vb);
+ return 0;
+ } else
+ return res;
+ }
+ }
alloc_len = sg_get_unaligned_be16(cdbp + 3); /* parameter list length */
dout_len = ptp->io_hdr.dout_xfer_len;
if (pf) {
@@ -685,13 +754,20 @@ sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
__func__, dpg_cd, dpg_len);
memset(&cmd, 0, sizeof(cmd));
cmd.opcode = 0x1d; /* MI send; hmmm same opcode as SEND DIAG */
- cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.dout_xferp;
+ cmd.addr = (uint64_t)(sg_uintptr_t)dop;
cmd.data_len = 0x1000; /* NVMe 4k page size. Maybe determine this? */
/* dout_len > 0x1000, is this a problem?? */
cmd.cdw10 = 0x0804; /* NVMe Message Header */
cmd.cdw11 = 0x9; /* nvme_mi_ses_send; (0x8 -> mi_ses_recv) */
cmd.cdw13 = n;
- return do_nvme_admin_cmd(ptp, &cmd, time_secs, vb);
+ res = do_nvme_admin_cmd(ptp, &cmd, dop, false, time_secs, vb);
+ if (0 != res) {
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, vb);
+ return 0;
+ }
+ }
+ return res;
}
/* This is not really a SNTL. For SCSI RECEIVE DIAGNOSTIC RESULTS(PCV=1)
@@ -733,14 +809,21 @@ sntl_recvdiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
dpg_cd);
memset(&cmd, 0, sizeof(cmd));
cmd.opcode = 0x1e; /* MI receive */
- cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.din_xferp;
+ cmd.addr = (uint64_t)(sg_uintptr_t)dip;
cmd.data_len = 0x1000; /* NVMe 4k page size. Maybe determine this? */
/* din_len > 0x1000, is this a problem?? */
cmd.cdw10 = 0x0804; /* NVMe Message Header */
cmd.cdw11 = 0x8; /* nvme_mi_ses_receive */
cmd.cdw12 = dpg_cd;
cmd.cdw13 = n;
- res = do_nvme_admin_cmd(ptp, &cmd, time_secs, vb);
+ res = do_nvme_admin_cmd(ptp, &cmd, dip, true, time_secs, vb);
+ if (0 != res) {
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, vb);
+ return 0;
+ } else
+ return res;
+ }
ptp->io_hdr.din_resid = din_len - n;
return res;
}
@@ -754,10 +837,12 @@ int
sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
{
bool scsi_cmd;
+ bool is_read = false;
int n, len;
struct sg_pt_linux_scsi * ptp = &vp->impl;
struct sg_nvme_passthru_cmd cmd;
const uint8_t * cdbp;
+ void * dp = NULL;
if (! ptp->io_hdr.request) {
if (vb)
@@ -782,7 +867,7 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
if (vb > 3)
pr2ws("%s: opcode=0x%x, fd=%d, time_secs=%d\n", __func__, cdbp[0],
fd, time_secs);
- scsi_cmd = is_scsi_command(cdbp, n);
+ scsi_cmd = sg_is_scsi_cdb(cdbp, n);
if (scsi_cmd) {
switch (cdbp[0]) {
case SCSI_INQUIRY_OPC:
@@ -797,7 +882,6 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
return sntl_senddiag(ptp, cdbp, time_secs, vb);
case SCSI_RECEIVE_DIAGNOSTIC_OPC:
return sntl_recvdiag(ptp, cdbp, time_secs, vb);
-// xxxxxxxxxx
default:
mk_sense_asc_ascq(ptp, SPC_SK_ILLEGAL_REQUEST, INVALID_OPCODE,
0, vb);
@@ -817,10 +901,14 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
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;
+ dp = (void *)ptp->io_hdr.din_xferp;
cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.din_xferp;
+ is_read = true;
} else if (ptp->io_hdr.dout_xfer_len > 0) {
cmd.data_len = ptp->io_hdr.dout_xfer_len;
+ dp = (void *)ptp->io_hdr.dout_xferp;
cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.dout_xferp;
+ is_read = false;
}
- return do_nvme_admin_cmd(ptp, &cmd, time_secs, vb);
+ return do_nvme_admin_cmd(ptp, &cmd, dp, is_read, time_secs, vb);
}
diff --git a/sg3_utils.spec b/sg3_utils.spec
index 0d53d0e9..7ba2b2b4 100644
--- a/sg3_utils.spec
+++ b/sg3_utils.spec
@@ -79,7 +79,7 @@ fi
%{_libdir}/*.la
%changelog
-* Mon Dec 18 2017 - dgilbert at interlog dot com
+* Fri Dec 29 2017 - dgilbert at interlog dot com
- track t10 changes
* sg3_utils-1.43
diff --git a/src/sg_inq.c b/src/sg_inq.c
index d294406b..0c5b9677 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.76 20171219"; /* SPC-5 rev 17 */
+static const char * version_str = "1.79 20171227"; /* SPC-5 rev 17 */
/* INQUIRY notes:
* It is recommended that the initial allocation length given to a
@@ -217,6 +217,7 @@ static struct option long_options[] = {
{"id", no_argument, 0, 'i'},
{"inhex", required_argument, 0, 'I'},
{"len", required_argument, 0, 'l'},
+ {"long", no_argument, 0, 'L'},
{"maxlen", required_argument, 0, 'm'},
#ifdef SG_SCSI_STRINGS
{"new", no_argument, 0, 'N'},
@@ -244,6 +245,7 @@ struct opts_t {
int do_cmddt;
int do_help;
int do_hex;
+ int do_long;
int do_raw;
int do_vendor;
int do_verbose;
@@ -269,9 +271,9 @@ usage()
"[--export]\n"
" [--extended] [--help] [--hex] [--id] [--inhex=FN] "
"[--len=LEN]\n"
- " [--maxlen=LEN] [--page=PG] [--raw] [--vendor] "
- "[--verbose]\n"
- " [--version] [--vpd] DEVICE\n"
+ " [--long] [--maxlen=LEN] [--page=PG] [--raw] "
+ "[--vendor]\n"
+ " [--verbose] [--version] [--vpd] DEVICE\n"
" where:\n"
" --ata|-a treat DEVICE as (directly attached) ATA "
"device\n");
@@ -300,8 +302,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; provide more "
- "NVMe info\n"
+ " --force|-f skip VPD page 0 checking; direct fetch "
+ "requested page\n"
" --help|-h print usage message then exit\n"
" --hex|-H output response in hex\n"
" --id|-i decode device identification VPD page "
@@ -314,6 +316,7 @@ usage()
"-> fetch 36\n"
" bytes first, then fetch again as "
"indicated)\n"
+ " --long|-L supply extra information on NVMe devices\n"
" --maxlen=LEN|-m LEN same as '--len='\n"
" --page=PG|-p PG Vital Product Data (VPD) page number "
"or\n"
@@ -341,7 +344,7 @@ usage_old()
#ifdef SG_LIB_LINUX
pr2serr("Usage: sg_inq [-a] [-A] [-b] [-B=0|1] [-c] [-cl] [-d] [-e] "
"[-h]\n"
- " [-H] [-i] [I=FN] [-l=LEN] [-m] [-M] "
+ " [-H] [-i] [I=FN] [-l=LEN] [-L] [-m] [-M] "
"[-o=OPCODE_PG]\n"
" [-p=VPD_PG] [-P] [-r] [-s] [-u] [-U] [-v] [-V] "
"[-x]\n"
@@ -352,7 +355,7 @@ usage_old()
#else
pr2serr("Usage: sg_inq [-a] [-b] [-B 0|1] [-c] [-cl] [-d] [-e] [-h] "
"[-H]\n"
- " [-i] [-l=LEN] [-m] [-M] [-o=OPCODE_PG] "
+ " [-i] [-l=LEN] [-L] [-m] [-M] [-o=OPCODE_PG] "
"[-p=VPD_PG]\n"
" [-P] [-r] [-s] [-u] [-v] [-V] [-x] [-36] "
"[-?]\n"
@@ -375,6 +378,7 @@ usage_old()
"-> fetch 36\n"
" bytes first, then fetch again as "
"indicated)\n"
+ " -L supply extra information on NVMe devices\n"
" -m decode management network addresses VPD page "
"(0x85)\n"
" -M decode mode page policy VPD page (0x87)\n"
@@ -425,18 +429,18 @@ new_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
#ifdef SG_LIB_LINUX
#ifdef SG_SCSI_STRINGS
- c = getopt_long(argc, argv, "aB:cdeEfhHiI:l:m:NOp:rsuvVx",
+ c = getopt_long(argc, argv, "aB:cdeEfhHiI:l:Lm:NOp:rsuvVx",
long_options, &option_index);
#else
- c = getopt_long(argc, argv, "B:cdeEfhHiI:l:m:p:rsuvVx", long_options,
+ c = getopt_long(argc, argv, "B:cdeEfhHiI:l:Lm:p:rsuvVx", long_options,
&option_index);
#endif /* SG_SCSI_STRINGS */
#else /* SG_LIB_LINUX */
#ifdef SG_SCSI_STRINGS
- c = getopt_long(argc, argv, "B:cdeEfhHiI:l:m:NOp:rsuvVx", long_options,
- &option_index);
+ c = getopt_long(argc, argv, "B:cdeEfhHiI:l:Lm:NOp:rsuvVx",
+ long_options, &option_index);
#else
- c = getopt_long(argc, argv, "B:cdeEfhHiI:l:m:p:rsuvVx", long_options,
+ c = getopt_long(argc, argv, "B:cdeEfhHiI:l:Lm:p:rsuvVx", long_options,
&option_index);
#endif /* SG_SCSI_STRINGS */
#endif /* SG_LIB_LINUX */
@@ -508,6 +512,9 @@ new_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
}
op->resp_len = n;
break;
+ case 'L':
+ ++op->do_long;
+ break;
#ifdef SG_SCSI_STRINGS
case 'N':
break; /* ignore */
@@ -625,6 +632,9 @@ old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
op->do_vpd = true;
++op->num_pages;
break;
+ case 'L':
+ ++op->do_long;
+ break;
case 'm':
op->page_num = VPD_MAN_NET_ADDR;
op->do_vpd = true;
@@ -3792,6 +3802,7 @@ do_nvme_identify_hex_raw(const unsigned char * b, int b_len,
const char * rperf[] = {"Best", "Better", "Good", "Degraded"};
+/* Send Identify(CNS=0, nsid) and decode the Identify namespace response */
static int
do_nvme_id_ns(struct sg_pt_base * ptvp, uint32_t nsid,
struct sg_nvme_passthru_cmd * id_cmdp, uint8_t * id_dinp,
@@ -3811,16 +3822,8 @@ do_nvme_id_ns(struct sg_pt_base * ptvp, uint32_t nsid,
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);
- if (vb > 2) {
- int rlen;
-
- pr2serr("do_scsi_pt() result is %d\n", ret);
- rlen = get_scsi_pt_sense_len(ptvp);
- if (rlen > 0) {
- pr2serr("do_scsi_pt() result via sense buffer:\n");
- dStrHex((const char *)&cmd_back, rlen, 0);
- }
- }
+ if (vb > 2)
+ pr2serr("%s: do_scsi_pt() result is %d\n", __func__, ret);
if (ret)
return ret;
num_lbaf = id_dinp[25] + 1; /* spec says this is "0's based value" */
@@ -3842,7 +3845,7 @@ do_nvme_id_ns(struct sg_pt_base * ptvp, uint32_t nsid,
for (k = 1; k < 16; ++k)
printf("%02x", id_dinp[104 + k]);
printf("\n");
- } else if (op->do_force)
+ } else if (op->do_long)
printf(" NGUID: 0x0\n");
if (eui_64)
printf(" EUI-64: 0x%" PRIx64 "\n", eui_64); /* N.B. big endian */
@@ -3879,15 +3882,16 @@ do_nvme_id_ns(struct sg_pt_base * ptvp, uint32_t nsid,
return ret;
}
-/* Send a NVMe Identify(CNS=1) and decode Controller info */
+/* Send a NVMe Identify(CNS=1, nsid=0) and decode Controller info. For each
+ * namespace found call do_nvme_id_ns(). */
static int
do_nvme_identify(int pt_fd, const struct opts_t * op)
{
bool got_fguid;
int ret = 0;
int vb = op->do_verbose;
- uint8_t ver_min, ver_ter;
- uint16_t ver_maj;
+ uint8_t ver_min, ver_ter, mtds;
+ uint16_t ver_maj, oacs, oncs;
uint32_t k, ver, nsid, max_nsid, npss, j, n, m;
uint64_t sz1, sz2;
uint8_t * up;
@@ -3919,19 +3923,11 @@ do_nvme_identify(int pt_fd, const struct opts_t * op)
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);
- if (vb > 2) {
- int rlen;
-
- pr2serr("do_scsi_pt result is %d\n", ret);
- rlen = get_scsi_pt_sense_len(ptvp);
- if (rlen > 0) {
- pr2serr("do_scsi_pt result via sense buffer:\n");
- dStrHex((const char *)&cmd_back, rlen, 0);
- }
- }
+ if (vb > 2)
+ pr2serr("%s: do_scsi_pt result is %d\n", __func__, ret);
if (ret)
goto err_out;
- max_nsid = sg_get_unaligned_le32(id_dinp + 516);
+ max_nsid = sg_get_unaligned_le32(id_dinp + 516); /* NN */
if (op->do_raw || op->do_hex) {
do_nvme_identify_hex_raw(id_dinp, pg_sz, op);
goto skip1;
@@ -3950,6 +3946,48 @@ do_nvme_identify(int pt_fd, const struct opts_t * op)
printf(".%u\n", ver_ter);
else
printf("\n");
+ oacs = sg_get_unaligned_le16(id_dinp + 256);
+ if (0x1ff & oacs) {
+ printf(" Optional admin command support:\n");
+ if (0x100 & oacs)
+ printf(" Doorbell buffer config\n");
+ if (0x80 & oacs)
+ printf(" Virtualization management\n");
+ if (0x40 & oacs)
+ printf(" NVMe-MI send and NVMe-MI receive\n");
+ if (0x20 & oacs)
+ printf(" Directive send and directive receive\n");
+ if (0x10 & oacs)
+ printf(" Device self-test\n");
+ if (0x8 & oacs)
+ printf(" Namespace management and attachment\n");
+ if (0x4 & oacs)
+ printf(" Firmware download and commit\n");
+ if (0x2 & oacs)
+ printf(" Format NVM\n");
+ if (0x1 & oacs)
+ printf(" Security send and receive\n");
+ } else
+ printf(" No optional admin command support\n");
+ oncs = sg_get_unaligned_le16(id_dinp + 256);
+ if (0x7f & oncs) {
+ printf(" Optional NVM command support:\n");
+ if (0x40 & oncs)
+ printf(" Timestamp feature\n");
+ if (0x20 & oncs)
+ printf(" Reservations\n");
+ if (0x10 & oncs)
+ printf(" Save and Select fields non-zero\n");
+ if (0x8 & oncs)
+ printf(" Write zeroes\n");
+ if (0x4 & oncs)
+ printf(" Dataset management\n");
+ if (0x2 & oncs)
+ printf(" Write uncorrectable\n");
+ if (0x1 & oncs)
+ printf(" Compare\n");
+ } else
+ printf(" No optional NVM command support\n");
printf(" PCI vendor ID VID/SSVID: 0x%x/0x%x\n",
sg_get_unaligned_le16(id_dinp + 0),
sg_get_unaligned_le16(id_dinp + 2));
@@ -3961,10 +3999,10 @@ do_nvme_identify(int pt_fd, const struct opts_t * op)
for (k = 1; k < 16; ++k)
printf("%02x", id_dinp[112 + k]);
printf("\n");
- } else if (op->do_force)
+ } else if (op->do_long)
printf(" FGUID: 0x0\n");
printf(" Controller ID: 0x%x\n", sg_get_unaligned_le16(id_dinp + 78));
- if (op->do_force) {
+ if (op->do_long) {
printf(" Management endpoint capabilities, over a PCIe port: %d\n",
!! (0x2 & id_dinp[255]));
printf(" Management endpoint capabilities, over a SMBus/I2C port: "
@@ -3977,7 +4015,14 @@ do_nvme_identify(int pt_fd, const struct opts_t * op)
printf(" Total NVM capacity: huge ...\n");
else if (sz1)
printf(" Total NVM capacity: %" PRIu64 " bytes\n", sz1);
- else if (op->do_force) {
+ mtds = id_dinp[77];
+ printf(" Maximum data transfer size: ");
+ if (mtds)
+ printf("%u pages\n", 1U << mtds);
+ else
+ printf("<unlimited>\n");
+
+ if (op->do_long) {
const char * const non_op = "does not process I/O";
const char * const operat = "processes I/O";
const char * cp;
@@ -4059,11 +4104,6 @@ do_nvme_identify(int pt_fd, const struct opts_t * op)
}
#endif
-// <<<<<<<<<<<<<<<<<<<<<<<< ******************
-#include <linux/nvme_ioctl.h>
-#include "sg_pt_linux.h"
-// <<<<<<<<<<<<<<<<<<<<<<<< ******************
-
int
main(int argc, char * argv[])
diff --git a/src/sg_logs.c b/src/sg_logs.c
index e4d4bf97..36182af9 100644
--- a/src/sg_logs.c
+++ b/src/sg_logs.c
@@ -5232,7 +5232,8 @@ skip:
return true;
}
-/* LPS misalignment page [0x15,0x3] introduced: SBC-4 rev 10 */
+/* LPS misalignment page [0x15,0x3] introduced: SBC-4 rev 10
+ LPS: "Long Physical Sector" a term from an ATA feature set */
static bool
show_lps_misalignment_page(const uint8_t * resp, int len,
const struct opts_t * op)
diff --git a/src/sg_ses.c b/src/sg_ses.c
index 7b25d23b..31736fda 100644
--- a/src/sg_ses.c
+++ b/src/sg_ses.c
@@ -32,7 +32,7 @@
* commands tailored for SES (enclosure) devices.
*/
-static const char * version_str = "2.25 20171217"; /* ses4r01 */
+static const char * version_str = "2.27 20171228"; /* ses4r01 */
#define MX_ALLOC_LEN ((64 * 1024) - 4) /* max allowable for big enclosures */
#define MX_ELEM_HDR 1024
@@ -54,7 +54,7 @@ static const char * version_str = "2.25 20171217"; /* ses4r01 */
#define HELP_TEXT_DPC 0x3
#define STRING_DPC 0x4
#define THRESHOLD_DPC 0x5
-#define ARRAY_CONTROL_DPC 0x6 /* obsolete */
+#define ARRAY_CONTROL_DPC 0x6 /* obsolete, last seen ses-r08b.pdf */
#define ARRAY_STATUS_DPC 0x6 /* obsolete */
#define ELEM_DESC_DPC 0x7
#define SHORT_ENC_STATUS_DPC 0x8
@@ -320,6 +320,11 @@ static uint8_t * elem_desc_rsp;
static uint8_t * add_elem_rsp;
static uint8_t * threshold_rsp;
+static unsigned enc_stat_rsp_sz;
+static unsigned elem_desc_rsp_sz;
+static unsigned add_elem_rsp_sz;
+static unsigned threshold_rsp_sz;
+
static int enc_stat_rsp_len;
static int elem_desc_rsp_len;
static int add_elem_rsp_len;
@@ -2297,6 +2302,27 @@ enc_status_helper(const char * pad, const uint8_t * statp, int etype,
pad, statp[0], statp[1], statp[2], statp[3]);
break;
case DEVICE_ETC:
+ if (ARRAY_STATUS_DPC == op->page_code) { /* obsolete after SES-1 */
+ if (nofilter || (0xf0 & statp[1]))
+ printf("%sOK=%d, Reserved device=%d, Hot spare=%d, Cons "
+ "check=%d\n", pad, !!(statp[1] & 0x80),
+ !!(statp[1] & 0x40), !!(statp[1] & 0x20),
+ !!(statp[1] & 0x10));
+ if (nofilter || (0xf & statp[1]))
+ printf("%sIn crit array=%d, In failed array=%d, Rebuild/"
+ "remap=%d, R/R abort=%d\n", pad, !!(statp[1] & 0x8),
+ !!(statp[1] & 0x4), !!(statp[1] & 0x2),
+ !!(statp[1] & 0x1));
+ if (nofilter || ((0x46 & statp[2]) || (0x8 & statp[3])))
+ printf("%sDo not remove=%d, RMV=%d, Ident=%d, Enable bypass "
+ "A=%d\n", pad, !!(statp[2] & 0x40), !!(statp[2] & 0x4),
+ !!(statp[2] & 0x2), !!(statp[3] & 0x8));
+ if (nofilter || (0x7 & statp[3]))
+ printf("%sEnable bypass B=%d, Bypass A enabled=%d, Bypass B "
+ "enabled=%d\n", pad, !!(statp[3] & 0x4),
+ !!(statp[3] & 0x2), !!(statp[3] & 0x1));
+ break;
+ }
printf("%sSlot address: %d\n", pad, statp[1]);
if (nofilter || (0xe0 & statp[2]))
printf("%sApp client bypassed A=%d, Do not remove=%d, Enc "
@@ -2319,21 +2345,21 @@ enc_status_helper(const char * pad, const uint8_t * statp, int etype,
printf("%sDevice bypassed B=%d\n", pad, !!(statp[3] & 0x1));
break;
case POWER_SUPPLY_ETC:
- if (nofilter || ((0xc0 & statp[1]) || (0xe & statp[2]))) {
+ if (nofilter || ((0xc0 & statp[1]) || (0xc & statp[2]))) {
printf("%sIdent=%d, Do not remove=%d, DC overvoltage=%d, "
"DC undervoltage=%d\n", pad, !!(statp[1] & 0x80),
!!(statp[1] & 0x40), !!(statp[2] & 0x8),
!!(statp[2] & 0x4));
- printf("%s DC overcurrent=%d\n", pad, !!(statp[2] & 0x2));
}
- if (nofilter || (0xf8 & statp[3]))
- printf("%sHot swap=%d, Fail=%d, Requested on=%d, Off=%d, "
- "Overtmp fail=%d\n", pad, !!(statp[3] & 0x80),
- !!(statp[3] & 0x40), !!(statp[3] & 0x20),
- !!(statp[3] & 0x10), !!(statp[3] & 0x8));
- if (nofilter || (0x7 & statp[3]))
- printf("%sTemperature warn=%d, AC fail=%d, DC fail=%d\n",
- pad, !!(statp[3] & 0x4), !!(statp[3] & 0x2),
+ if (nofilter || ((0x2 & statp[2]) || (0xf0 & statp[3])))
+ printf("%sDC overcurrent=%d, Hot swap=%d, Fail=%d, Requested "
+ "on=%d, Off=%d\n", pad, !!(statp[2] & 0x2),
+ !!(statp[3] & 0x80), !!(statp[3] & 0x40),
+ !!(statp[3] & 0x20), !!(statp[3] & 0x10));
+ if (nofilter || (0xf & statp[3]))
+ printf("%sOvertmp fail=%d, Temperature warn=%d, AC fail=%d, "
+ "DC fail=%d\n", pad, !!(statp[3] & 0x8),
+ !!(statp[3] & 0x4), !!(statp[3] & 0x2),
!!(statp[3] & 0x1));
break;
case COOLING_ETC:
@@ -2671,6 +2697,74 @@ truncated:
return;
}
+/* ARRAY_STATUS_DPC [0x6]
+ * Display array status diagnostic page. */
+static void
+array_status_dp(const struct th_es_t * tesp, uint32_t ref_gen_code,
+ const uint8_t * resp, int resp_len,
+ const struct opts_t * op)
+{
+ int j, k;
+ uint32_t gen_code;
+ bool got1, match_ind_th;
+ const uint8_t * bp;
+ const uint8_t * last_bp;
+ const struct type_desc_hdr_t * tdhp = tesp->th_base;
+ char b[64];
+
+ printf("Array Status diagnostic page:\n");
+ if (resp_len < 4)
+ goto truncated;
+ printf(" INVOP=%d, INFO=%d, NON-CRIT=%d, CRIT=%d, UNRECOV=%d\n",
+ !!(resp[1] & 0x10), !!(resp[1] & 0x8), !!(resp[1] & 0x4),
+ !!(resp[1] & 0x2), !!(resp[1] & 0x1));
+ last_bp = resp + resp_len - 1;
+ if (resp_len < 8)
+ goto truncated;
+ gen_code = sg_get_unaligned_be32(resp + 4);
+ printf(" generation code: 0x%x\n", gen_code);
+ if (ref_gen_code != gen_code) {
+ pr2serr(" <<state of enclosure changed, please try again>>\n");
+ return;
+ }
+ printf(" status descriptor list\n");
+ bp = resp + 8;
+ for (k = 0, got1 = false; k < tesp->num_ths; ++k, ++tdhp) {
+ if ((bp + 3) > last_bp)
+ goto truncated;
+ match_ind_th = (op->ind_given && (k == op->ind_th));
+ if ((! op->ind_given) || (match_ind_th && (-1 == op->ind_indiv))) {
+ printf(" Element type: %s, subenclosure id: %d [ti=%d]\n",
+ etype_str(tdhp->etype, b, sizeof(b)), tdhp->se_id, k);
+ printf(" Overall descriptor:\n");
+ enc_status_helper(" ", bp, tdhp->etype, false, op);
+ got1 = true;
+ }
+ for (bp += 4, j = 0; j < tdhp->num_elements; ++j, bp += 4) {
+ if (op->ind_given) {
+ if ((! match_ind_th) || (-1 == op->ind_indiv) ||
+ (! match_ind_indiv(j, op)))
+ continue;
+ }
+ printf(" Element %d descriptor:\n", j);
+ enc_status_helper(" ", bp, tdhp->etype, false, op);
+ got1 = true;
+ }
+ }
+ if (op->ind_given && (! got1)) {
+ printf(" >>> no match on --index=%d,%d", op->ind_th,
+ op->ind_indiv);
+ if (op->ind_indiv_last > op->ind_indiv)
+ printf("-%d\n", op->ind_indiv_last);
+ else
+ printf("\n");
+ }
+ return;
+truncated:
+ pr2serr(" <<<arr: response too short>>>\n");
+ return;
+}
+
static char *
reserved_or_num(char * buff, int buff_len, int num, int reserve_num)
{
@@ -3800,6 +3894,24 @@ process_status_page(int sg_fd, struct opts_t * op)
tesp->num_ths = num_ths;
enc_status_dp(tesp, ref_gen_code, resp, resp_len, op);
break;
+ case ARRAY_STATUS_DPC:
+ num_ths = build_type_desc_hdr_arr(sg_fd, type_desc_hdr_arr,
+ MX_ELEM_HDR, &ref_gen_code,
+ &primary_info, op);
+ if (num_ths < 0) {
+ ret = num_ths;
+ goto fini;
+ }
+ if (primary_info.have_info) {
+ printf(" Primary enclosure logical identifier (hex): ");
+ for (j = 0; j < 8; ++j)
+ printf("%02x", primary_info.enc_log_id[j]);
+ printf("\n");
+ }
+ tesp->th_base = type_desc_hdr_arr;
+ tesp->num_ths = num_ths;
+ array_status_dp(tesp, ref_gen_code, resp, resp_len, op);
+ break;
case HELP_TEXT_DPC:
printf("Help text diagnostic page (for primary "
"subenclosure):\n");
@@ -4405,7 +4517,7 @@ join_work(int sg_fd, struct opts_t * op, bool display)
printf("%02x", primary_info.enc_log_id[j]);
printf("\n");
}
- mlen = sizeof(enc_stat_rsp);
+ mlen = enc_stat_rsp_sz;
if (mlen > op->maxlen)
mlen = op->maxlen;
res = do_rec_diag(sg_fd, ENC_STATUS_DPC, enc_stat_rsp, mlen, op,
@@ -4424,7 +4536,7 @@ join_work(int sg_fd, struct opts_t * op, bool display)
es_bp = enc_stat_rsp + 8;
/* es_last_bp = enc_stat_rsp + enc_stat_rsp_len - 1; */
- mlen = sizeof(elem_desc_rsp);
+ mlen = elem_desc_rsp_sz;
if (mlen > op->maxlen)
mlen = op->maxlen;
res = do_rec_diag(sg_fd, ELEM_DESC_DPC, elem_desc_rsp, mlen, op,
@@ -4452,7 +4564,7 @@ join_work(int sg_fd, struct opts_t * op, bool display)
/* check if we want to add the AES page to the join */
if (display || (ADD_ELEM_STATUS_DPC == op->page_code) ||
(op->dev_slot_num >= 0) || saddr_non_zero(op->sas_addr)) {
- mlen = sizeof(add_elem_rsp);
+ mlen = add_elem_rsp_sz;
if (mlen > op->maxlen)
mlen = op->maxlen;
res = do_rec_diag(sg_fd, ADD_ELEM_STATUS_DPC, add_elem_rsp, mlen, op,
@@ -4490,7 +4602,7 @@ join_work(int sg_fd, struct opts_t * op, bool display)
if ((op->do_join > 1) ||
((! display) && (THRESHOLD_DPC == op->page_code))) {
- mlen = sizeof(threshold_rsp);
+ mlen = threshold_rsp_sz;
if (mlen > op->maxlen)
mlen = op->maxlen;
res = do_rec_diag(sg_fd, THRESHOLD_DPC, threshold_rsp, mlen, op,
@@ -5133,24 +5245,28 @@ main(int argc, char * argv[])
pr2serr("Unable to get heap for enc_stat_rsp\n");
goto err_out;
}
+ enc_stat_rsp_sz = MX_ALLOC_LEN;
elem_desc_rsp = sg_memalign(MX_ALLOC_LEN, pg_sz, &free_elem_desc_rsp,
op->verbose > 3);
if (NULL == elem_desc_rsp) {
pr2serr("Unable to get heap for elem_desc_rsp\n");
goto err_out;
}
+ elem_desc_rsp_sz = MX_ALLOC_LEN;
add_elem_rsp = sg_memalign(MX_ALLOC_LEN, pg_sz, &free_add_elem_rsp,
op->verbose > 3);
if (NULL == add_elem_rsp) {
pr2serr("Unable to get heap for add_elem_rsp\n");
goto err_out;
}
+ add_elem_rsp_sz = MX_ALLOC_LEN;
threshold_rsp = sg_memalign(MX_ALLOC_LEN, pg_sz, &free_threshold_rsp,
op->verbose > 3);
if (NULL == threshold_rsp) {
pr2serr("Unable to get heap for threshold_rsp\n");
goto err_out;
}
+ threshold_rsp_sz = MX_ALLOC_LEN;
if (op->num_cgs) {
have_cgs = true;
diff --git a/src/sg_write_x.c b/src/sg_write_x.c
index 79afe1f3..4effbde1 100644
--- a/src/sg_write_x.c
+++ b/src/sg_write_x.c
@@ -36,7 +36,7 @@
#include "sg_unaligned.h"
#include "sg_pr2serr.h"
-static const char * version_str = "1.08 20171218";
+static const char * version_str = "1.09 20171222";
/* Protection Information refers to 8 bytes of extra information usually
* associated with each logical block and is often abbreviated to PI while
@@ -193,6 +193,7 @@ struct opts_t {
char cdb_name[24]; /* e.g. 'Write atomic(16)' */
};
+static const char * xx_wr_fname = "sg_write_x.bin";
static const uint32_t lbard_sz = 32;
static const char * lbard_str = "LBA range descriptor";
@@ -480,7 +481,6 @@ bin_read(int fd, uint8_t * up, uint32_t len, const char * fname)
{
int res, err;
-pr2serr("%s: len=%u, fname: %s\n", __func__, len, fname);
res = read(fd, up, len);
if (res < 0) {
err = errno;
@@ -808,7 +808,6 @@ build_t10_scat(const char * scat_fname, bool do_16, bool parse_one,
FILE * fp = NULL;
char line[1024];
-pr2serr("%s: max_list_blen=%u, have t10_scat_list_out pointer=%u\n", __func__, max_list_blen, (t10_scat_list_out ? 1 : 0));
if (up) {
if (max_list_blen < 64) {
pr2serr("%s: t10_scat_list_out is too short\n", __func__);
@@ -922,7 +921,6 @@ pr2serr("%s: max_list_blen=%u, have t10_scat_list_out pointer=%u\n", __func__, m
}
fini:
*num_scat_elems = (n / lbard_sz) - 1;
-pr2serr("%s: num_scat_elems=%u\n", __func__, *num_scat_elems);
if (fp && (stdin != fp))
fclose(fp);
return 0;
@@ -970,7 +968,6 @@ check_lbrds(const uint8_t * up, uint32_t max_lbrds_blen,
const int max_lbrd_start = max_lbrds_blen - lbard_sz;
int vb = op->verbose;
-pr2serr("%s: max_lbrds_blen=%u\n", __func__, max_lbrds_blen);
if (op->strict) {
if (max_lbrds_blen < lbard_sz) {
pr2serr("%s: %ss too short (%d < 32)\n", __func__, lbard_str,
@@ -1256,9 +1253,12 @@ do_write_x(int sg_fd, const void * dataoutp, int dout_len,
sg_get_unaligned_be32(up + 12),
sg_get_unaligned_be16(up + 16),
sg_get_unaligned_be16(up + 18));
- if ((uint32_t)(((k + 2) * lbard_sz) + 20) > sod_off)
+ if ((uint32_t)(((k + 2) * lbard_sz) + 20) > sod_off) {
pr2serr("Warning: possible clash of descriptor %u with "
"data_to_write\n", k);
+ if (op->strict > 1)
+ return SG_LIB_FILE_ERROR;
+ }
}
}
if ((vb > 3) && (dout_len > 0)) {
@@ -1279,22 +1279,25 @@ do_write_x(int sg_fd, const void * dataoutp, int dout_len,
op->cdb_name);
if (op->dry_run > 1) {
int w_fd;
- const char * w_fname = "sg_write_x.bin";
- w_fd = open(w_fname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ w_fd = open(xx_wr_fname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (w_fd < 0) {
- perror(w_fname);
+ perror(xx_wr_fname);
return SG_LIB_FILE_ERROR;
}
res = write(w_fd, dataoutp, dout_len);
if (res < 0) {
- perror(w_fname);
+ perror(xx_wr_fname);
close(w_fd);
return SG_LIB_FILE_ERROR;
}
- if (vb)
- pr2serr("Wrote data-out buffer to %s\n", w_fname);
close(w_fd);
+ printf("Wrote %u bytes to %s", dout_len, xx_wr_fname);
+ if (op->do_scattered)
+ printf(", LB data offset: %u\nNumber of %ss: %u\n",
+ op->scat_lbdof, lbard_str, op->scat_num_lbard);
+ else
+ printf("\n");
}
return 0;
}
@@ -1307,8 +1310,8 @@ do_write_x(int sg_fd, const void * dataoutp, int dout_len,
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
if (dout_len > 0)
set_scsi_pt_data_out(ptvp, (uint8_t *)dataoutp, dout_len);
- else if (vb)
- pr2serr("%s thinks dout_len==0, so empty dout buffer\n",
+ else if (vb && (! op->ndob))
+ pr2serr("%s: dout_len==0, so empty dout buffer\n",
op->cdb_name);
res = do_scsi_pt(ptvp, sg_fd, op->timeout, vb);
ret = sg_cmds_process_resp(ptvp, op->cdb_name, res, SG_NO_DATA_IN,
@@ -1335,8 +1338,18 @@ do_write_x(int sg_fd, const void * dataoutp, int dout_len,
if (0 == ull)
pr2serr("%s=<not reported>\n", lbard_str);
else
- pr2serr("%s=%" PRIu64 "] (origin 0)\n", lbard_str,
+ pr2serr("%s=%" PRIu64 " (origin 0)\n", lbard_str,
ull - 1);
+ if (sg_get_sense_cmd_spec_fld(sense_b, slen, &ull)) {
+ if (0 == ull)
+ pr2serr(" Number of successfully written "
+ "%ss is 0 or not reported\n",
+ lbard_str);
+ else
+ pr2serr(" Number of successfully written "
+ "%ss is %u\n", lbard_str,
+ (uint32_t)ull);
+ }
} else
pr2serr("lba=%" PRIu64 " [0x%" PRIx64 "]\n", ull,
ull);
@@ -1829,7 +1842,7 @@ process_scattered(int sg_fd, int infd, uint32_t if_len, uint32_t if_rlen,
bool rd_gt = (op->scat_num_lbard > num_lbard);
if (rd_gt || op->strict || vb) {
- pr2serr("RD (%u) %s number of %ss (%u) found in SF\n",
+ pr2serr("RD (%u) %s number of %ss (%u) found in IF\n",
op->scat_num_lbard, (rd_gt ? ">" : "<"), lbard_str,
num_lbard);
if (rd_gt)
@@ -1839,15 +1852,48 @@ process_scattered(int sg_fd, int infd, uint32_t if_len, uint32_t if_rlen,
}
num_lbard = op->scat_num_lbard;
sum_num = sum_num_lbards(up, op->scat_num_lbard);
- }
+ } else
+ op->scat_num_lbard = num_lbard;
dd = lbard_sz * (num_lbard + 1);
if (0 != (dd % op->bs_pi_do))
dd = ((dd / op->bs_pi_do) + 1) * op->bs_pi_do; /* round up */
+ nn = op->scat_lbdof * op->bs_pi_do;
+ if (dd != nn) {
+ bool dd_gt = (dd > nn);
+
+ if (dd_gt) {
+ pr2serr("%s: Cannot fit %ss (%u) in given LB data offset "
+ "(%u)\n", __func__, lbard_str, num_lbard,
+ op->scat_lbdof);
+ goto file_err_outt;
+ }
+ if (vb || op->strict)
+ pr2serr("%s: empty blocks before LB data offset (%u), could "
+ "be okay\n", __func__, op->scat_lbdof);
+ if (op->strict) {
+ pr2serr("Exiting due to --strict; perhaps try again with "
+ "--combined=%u\n", dd / op->bs_pi_do);
+ goto file_err_outt;
+ }
+ dd = nn;
+ }
dd += (sum_num * op->bs_pi_do);
if (dd > d) {
uint8_t * u2p;
uint8_t * free_u2p;
+ if (dd != if_len) {
+ bool dd_gt = (dd > if_len);
+
+ if (dd_gt || op->strict || vb) {
+ pr2serr("Calculated dout length (%u) %s bytes available "
+ "in IF (%u)\n", dd, (dd_gt ? ">" : "<"), if_len);
+ if (dd_gt)
+ goto file_err_outt;
+ else if (op->strict)
+ goto file_err_outt;
+ }
+ }
u2p = (uint8_t *)sg_memalign(dd, sg_get_page_size(), &free_u2p,
vb > 4);
if (NULL == u2p) {
@@ -1941,7 +1987,7 @@ process_scattered(int sg_fd, int infd, uint32_t if_len, uint32_t if_rlen,
}
}
ret = bin_read(infd, up + (op->scat_lbdof * op->bs_pi_do), d,
- "IF");
+ "IF 3");
if (ret)
goto finii;
do_len = ((op->scat_lbdof + sum_num) * op->bs_pi_do);
@@ -2019,13 +2065,19 @@ process_scattered(int sg_fd, int infd, uint32_t if_len, uint32_t if_rlen,
} else
pr2serr("continuing ...\n");
}
- ret = bin_read(infd, up + d, (if_len_gt ? nn - d : if_len), "IF");
+ ret = bin_read(infd, up + d, (if_len_gt ? nn - d : if_len), "IF 4");
if (ret)
goto finii;
do_len = (num_lbard + sum_num) * op->bs_pi_do;
op->numblocks = sum_num;
op->xfer_bytes = sum_num * op->bs_pi_do;
} else if (addr_arr_len > 0) { /* build RDs for --lba= --num= */
+ if ((op->scat_num_lbard > 0) && (op->scat_num_lbard > addr_arr_len)) {
+ pr2serr("%s: number given to --scattered= (%u) exceeds number of "
+ "--lba= elements (%u)\n", __func__, op->scat_num_lbard,
+ addr_arr_len);
+ return SG_LIB_SYNTAX_ERROR;
+ }
d = lbard_sz * (num_lbard + 1);
op->scat_lbdof = d / op->bs_pi_do;
if (0 != (d % op->bs_pi_do)) /* if not multiple, round up */
@@ -2098,11 +2150,13 @@ main(int argc, char * argv[])
uint32_t nn, addr_arr_len, num_arr_len; /* --lba= */
uint32_t do_len = 0;
uint16_t num_lbard = 0;
- uint32_t if_len = 0;
+ uint32_t if_len = 0; /* after accounting for OFF,DLEN and moving file
+ * file pointer to OFF, is bytes available in IF */
uint32_t sf_len = 0;
uint32_t sum_num = 0;
ssize_t res;
- off_t if_readable_len = 0;
+ off_t if_readable_len = 0; /* similar to if_len but doesn't take DLEN
+ * into account */
struct opts_t * op;
const char * lba_op = NULL;
const char * num_op = NULL;
@@ -2155,10 +2209,16 @@ main(int argc, char * argv[])
"--normal, --or,\n--same=, --scattered= or --stream=\n") ;
return SG_LIB_SYNTAX_ERROR;
} else if (n < 1) {
- op->do_write_normal = true;
- op->cmd_name = "Write";
- if (vb)
- pr2serr("No command selected so choose 'normal' WRITE\n");
+ if (op->strict) {
+ pr2serr("With --strict won't default to a normal WRITE, add "
+ "--normal\n");
+ return SG_LIB_SYNTAX_ERROR;
+ } else {
+ op->do_write_normal = true;
+ op->cmd_name = "Write";
+ if (vb)
+ pr2serr("No command selected so choose 'normal' WRITE\n");
+ }
}
snprintf(op->cdb_name, sizeof(op->cdb_name), "%s(%d)", op->cmd_name,
(op->do_16 ? 16 : 32));
@@ -2491,17 +2551,21 @@ main(int argc, char * argv[])
op->xfer_bytes = op->numblocks * op->bs_pi_do;
do_len = op->xfer_bytes;
- /* fill allocated buffer with zeros */
- up = (uint8_t *)sg_memalign(do_len, sg_get_page_size(), &free_up,
- vb > 4);
- if (NULL == up) {
- pr2serr("unable to allocate %u bytes of memory\n", do_len);
- ret = SG_LIB_OS_BASE_ERR + ENOMEM;
- goto err_out;
- }
- ret = bin_read(infd, up, ((if_len < do_len) ? if_len : do_len), "IF");
- if (ret)
- goto fini;
+ if (do_len > 0) {
+ /* fill allocated buffer with zeros */
+ up = (uint8_t *)sg_memalign(do_len, sg_get_page_size(), &free_up,
+ vb > 4);
+ if (NULL == up) {
+ pr2serr("unable to allocate %u bytes of memory\n", do_len);
+ ret = SG_LIB_OS_BASE_ERR + ENOMEM;
+ goto err_out;
+ }
+ ret = bin_read(infd, up, ((if_len < do_len) ? if_len : do_len),
+ "IF 5");
+ if (ret)
+ goto fini;
+ } else
+ up = NULL;
ret = do_write_x(sg_fd, up, do_len, op);
if (ret) {
diff --git a/src/sgp_dd.c b/src/sgp_dd.c
index f6cf2a6b..bb515c82 100644
--- a/src/sgp_dd.c
+++ b/src/sgp_dd.c
@@ -60,7 +60,7 @@
#include "sg_pr2serr.h"
-static const char * version_str = "5.58 20171209";
+static const char * version_str = "5.59 20171222";
#define DEF_BLOCK_SIZE 512
#define DEF_BLOCKS_PER_TRANSFER 128
@@ -272,6 +272,14 @@ install_handler(int sig_num, void (*sig_handler) (int sig))
}
}
+#ifdef SG_LIB_ANDROID
+static void
+thread_exit_handler(int sig)
+{
+ pthread_exit(0);
+}
+#endif
+
/* Make safe_strerror() thread safe */
static char *
tsafe_strerror(int code, char * ebp)
@@ -1147,7 +1155,15 @@ main(int argc, char * argv[])
int in_sect_sz, out_sect_sz, status, n, flags;
void * vp;
char ebuff[EBUFF_SZ];
-
+#if SG_LIB_ANDROID
+ struct sigaction actions;
+
+ memset(&actions, 0, sizeof(actions));
+ sigemptyset(&actions.sa_mask);
+ actions.sa_flags = 0;
+ actions.sa_handler = thread_exit_handler;
+ sigaction(SIGUSR1, &actions, NULL);
+#endif
memset(&rcoll, 0, sizeof(Rq_coll));
rcoll.bpt = DEF_BLOCKS_PER_TRANSFER;
rcoll.in_type = FT_OTHER;
@@ -1629,7 +1645,13 @@ main(int argc, char * argv[])
}
}
+#if SG_LIB_ANDROID
+ /* Android doesn't have pthread_cancel() so use pthread_kill() instead.
+ * Also there is no need to link with -lpthread in Android */
+ status = pthread_kill(sig_listen_thread_id, SIGUSR1);
+#else
status = pthread_cancel(sig_listen_thread_id);
+#endif
if (0 != status) err_exit(status, "pthread_cancel");
if (STDIN_FILENO != rcoll.infd)
close(rcoll.infd);