aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2017-11-15 06:21:21 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2017-11-15 06:21:21 +0000
commitcad489950e9fc29fe8786ddee2e163831abb3322 (patch)
treef5263f4d234ec6894c61a8561a4a77c06068c7bc
parent098b15e48f02ac4b501dc383456be94b25c1266b (diff)
downloadsg3_utils-cad489950e9fc29fe8786ddee2e163831abb3322.tar.gz
rename sg_write_atomic to sg_write_x; sg_inq: decode NVMe identify for Linux+FreeBSD
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@731 6180dd3e-e324-4e3e-922d-17de1ae2f315
-rw-r--r--COVERAGE10
-rw-r--r--ChangeLog16
-rw-r--r--README10
-rw-r--r--README.freebsd6
-rw-r--r--README.solaris6
-rw-r--r--README.win322
-rw-r--r--config.h.in6
-rwxr-xr-xconfigure204
-rw-r--r--configure.ac30
-rw-r--r--doc/Makefile.am4
-rw-r--r--doc/Makefile.in6
-rw-r--r--doc/sg_dd.814
-rw-r--r--doc/sg_luns.88
-rw-r--r--doc/sg_persist.84
-rw-r--r--doc/sg_rdac.86
-rw-r--r--doc/sg_read_attr.86
-rw-r--r--doc/sg_verify.86
-rw-r--r--doc/sg_write_x.8 (renamed from doc/sg_write_atomic.8)4
-rw-r--r--include/freebsd_nvme_ioctl.h155
-rw-r--r--include/sg_lib.h6
-rw-r--r--include/sg_pt.h70
-rw-r--r--include/sg_pt_nvme.h70
-rw-r--r--lib/sg_lib.c22
-rw-r--r--lib/sg_lib_data.c2
-rw-r--r--lib/sg_pt_common.c2
-rw-r--r--lib/sg_pt_freebsd.c348
-rw-r--r--lib/sg_pt_linux.c771
-rw-r--r--lib/sg_pt_osf1.c41
-rw-r--r--lib/sg_pt_solaris.c58
-rw-r--r--lib/sg_pt_win32.c38
-rw-r--r--src/Makefile.am8
-rw-r--r--src/Makefile.in38
-rw-r--r--src/sg_format.c4
-rw-r--r--src/sg_inq.c253
-rw-r--r--src/sg_logs.c46
-rw-r--r--src/sg_read_long.c2
-rw-r--r--src/sg_sat_identify.c2
-rw-r--r--src/sg_sat_phy_event.c4
-rw-r--r--src/sg_sat_read_gplog.c4
-rw-r--r--src/sg_sat_set_features.c4
-rw-r--r--src/sg_scan_win32.c4
-rw-r--r--src/sg_ses.c10
-rw-r--r--src/sg_test_rwbuf.c4
-rw-r--r--src/sg_write_long.c4
-rw-r--r--src/sg_write_x.c (renamed from src/sg_write_atomic.c)6
-rw-r--r--src/sginfo.c2
46 files changed, 1992 insertions, 334 deletions
diff --git a/COVERAGE b/COVERAGE
index b1648acc..313a4c9a 100644
--- a/COVERAGE
+++ b/COVERAGE
@@ -89,12 +89,12 @@ VERIFY(16) sg_verify, ++
WRITE(6) sg_dd, sgm_dd, sgp_dd
WRITE(10) sg_dd, sgm_dd, sgp_dd
WRITE(12) sg_dd, sgm_dd, sgp_dd
-WRITE(16) sg_dd, sgm_dd, sgp_dd, sg_write_atomic
-WRITE(32) sg_write_atomic
+WRITE(16) sg_dd, sgm_dd, sgp_dd, sg_write_x
+WRITE(32) sg_write_x
WRITE AND VERIFY(10) sg_write_verify
WRITE AND VERIFY(16) sg_write_verify
-WRITE ATOMIC(16) ddpt, sg_write_atomic
-WRITE ATOMIC(32) sg_write_atomic
+WRITE ATOMIC(16) ddpt, sg_write_x
+WRITE ATOMIC(32) sg_write_x
WRITE BUFFER sg_test_rwbuf, sg_write_buffer, ++
WRITE LONG(10) sg_write_long, ++
WRITE LONG(16) sg_write_long, ++
@@ -137,4 +137,4 @@ THIRD PARTY COPY IN (0x83).
Douglas Gilbert
-21st September 2017
+15th November 2017
diff --git a/ChangeLog b/ChangeLog
index c8195922..d1690578 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,10 +2,11 @@ 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 [20171104] [svn: r730]
+Changelog for sg3_utils-1.43 [20171115] [svn: r731]
- sg_bg_ctl: new Background control command (sbc4r08)
- - sg_write_atomic: new 16 and 32 byte cdbs (sbc4r04)
- - also can issue plain WRITE(16 or 32)
+ - sg_write_x: where X can be normal, atomic,
+ scattered, or stream writes with 16 or 32 byte
+ cdbs (sbc4r04 for atomic, sbc4r11 for scattered)
- sg_senddiag: add --timeout=SEC option
- sg_sanitize: add --timeout=SEC option
- sg_format: add --timeout=SEC option
@@ -41,6 +42,7 @@ Changelog for sg3_utils-1.43 [20171104] [svn: r730]
log page (from H. Reinecke, Suse)
- sg_inq: fix potential unbounded loop in --export
- update version descriptor list to 20170114
+ - decode NVMe Identify controller/nsid
- sg_inq+sg_vpd: update Extended inquiry data vpd
page (spc5r09)
- block limits and block limit extension VPD pages:
@@ -88,11 +90,16 @@ Changelog for sg3_utils-1.43 [20171104] [svn: r730]
- add sg_get_sfs_name() for spc5r11 (Feature sets)
- add sg_decode_transportid_str()
- add sg_msense_calc_length()
+ - add sg_all_zeros(), sg_all_ffs()
- implement 'format' argument in dStrHexStr()
- add read buffer(16) command mode names
- add Microcode activation sense descriptor spc5r10
- add SG_LIB_UNBOUNDED_32BIT (_16BIT and _64BIT)
defines to help with decoding corner cases
+ - 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()
- 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
@@ -107,9 +114,8 @@ Changelog for sg3_utils-1.43 [20171104] [svn: r730]
- https://github.com/hreinecke/sg3_utils
branch sles15 synced 20170914
- move some testing utilities out of the
- 'examples' and 'utils' directory into the new
+ 'examples' and 'utils' directories into the new
'testing' directory
- directory into the 'testing' directory
- gcc 7.2 cleanups (sysmacros.h etc)
- clang --analyze static checker clean ups
- shellcheck cleanup on scripts
diff --git a/README b/README
index 0925e374..f8d1a73f 100644
--- a/README
+++ b/README
@@ -238,8 +238,8 @@ subdirectory of the sg3_utils package:
sg_rtpg, sg_safte, sg_sanitize, sg_sat_identify, sg_sat_phy_event,
sg_sat_read_gplog, sg_sat_set_features, sg_scan, sg_senddiag, sg_ses,
sg_ses_microcode, sg_start, sg_stpg, sg_sync, sg_test_rwbuff,
- sg_timestamp, sg_turs, sg_unmap, sg_verify, sg_vpd, sg_write_atomic,
- sg_write_buffer, sg_write_long, sg_write_same, sg_write_verify,
+ sg_timestamp, sg_turs, sg_unmap, sg_verify, sg_vpd, sg_write_buffer,
+ sg_write_long, sg_write_same, sg_write_verify, sg_write_x,
sg_wr_mode, sg_xcopy, sg_zone
Each of the above utilities depends on header files found in the 'include'
@@ -403,8 +403,8 @@ The more recent utilities that use "getopt_long" only are:
sg_rmsn, sg_rtpg, sg_safte, sg_sanitize, sg_sat_identify,
sg_sat_phy_event, sg_sat_read_gplog, sg_sat_set_features, sg_scan(w),
sg_ses, sg_ses_microcode, sg_stpg, sg_sync, sg_test_rwbuf, sg_timestamp,
- sg_unmap, sg_verify, sg_vpd, sg_write_atomic, sg_write_buffer,
- sg_write_long, sg_write_same, sg_write_verify, sg_wr_mode, sg_zone
+ sg_unmap, sg_verify, sg_vpd, sg_write_buffer, sg_write_long,
+ sg_write_same, sg_write_verify, sg_write_x, sg_wr_mode, sg_zone
Dangerous code
@@ -476,4 +476,4 @@ See http://sg.danny.cz/sg/tools.html
Douglas Gilbert
-4th November 2017
+15th November 2017
diff --git a/README.freebsd b/README.freebsd
index 9ad4d787..f226b250 100644
--- a/README.freebsd
+++ b/README.freebsd
@@ -35,6 +35,7 @@ Here is a list of utilities that have been ported:
sg_readcap
sg_reassign
sg_referrals
+ sg_rep_zones
sg_requests
sg_rmsn
sg_rtpg
@@ -53,11 +54,12 @@ Here is a list of utilities that have been ported:
sg_unmap
sg_vpd
sg_wr_mode
- sg_write_atomic
sg_write_buffer
sg_write_long
sg_write_same
sg_write_verify
+ sg_write_x
+ sg_zone
Most utility names are indicative of the main SCSI command
that they execute. Some utilities are slightly higher level, for
@@ -138,4 +140,4 @@ utilities.
Douglas Gilbert
-21st September 2017
+15th November 2017
diff --git a/README.solaris b/README.solaris
index 9a4dc585..7191fa31 100644
--- a/README.solaris
+++ b/README.solaris
@@ -52,6 +52,7 @@ Here is a list of utilities that have been ported:
sg_readcap
sg_reassign
sg_referrals
+ sg_rep_zones
sg_requests
sg_rmsn
sg_rtpg
@@ -70,11 +71,12 @@ Here is a list of utilities that have been ported:
sg_verify
sg_vpd
sg_wr_mode
- sg_write_atomic
sg_write_buffer
sg_write_long
sg_write_same
sg_write_verify
+ sg_write_x
+ sg_zone
Most utility names are indicative of the main SCSI command
that they execute. Some utilities are slightly higher level, for
@@ -159,4 +161,4 @@ disks to accept SCSI commands including the SCSI ATA PASS THROUGH commands.
Douglas Gilbert
-21st September 2017
+15th November 2017
diff --git a/README.win32 b/README.win32
index 645f8e50..4db54200 100644
--- a/README.win32
+++ b/README.win32
@@ -80,11 +80,11 @@ Here is a list of utilities that have been ported:
sg_verify
sg_vpd
sg_wr_mode
- sg_write_atomic
sg_write_buffer
sg_write_long
sg_write_same
sg_write_verify
+ sg_write_x
sg_zone
Most utility names are indicative of the main SCSI command that they execute.
diff --git a/config.h.in b/config.h.in
index 781e9eb3..5bab057b 100644
--- a/config.h.in
+++ b/config.h.in
@@ -15,6 +15,9 @@
/* Define to 1 if you have the <linux/kdev_t.h> header file. */
#undef HAVE_LINUX_KDEV_T_H
+/* Define to 1 if you have the <linux/nvme_ioctl.h> header file. */
+#undef HAVE_LINUX_NVME_IOCTL_H
+
/* Define to 1 if you have the <linux/types.h> header file. */
#undef HAVE_LINUX_TYPES_H
@@ -24,6 +27,9 @@
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
+/* Found NVMe */
+#undef HAVE_NVME
+
/* Define to 1 if you have the `posix_fadvise' function. */
#undef HAVE_POSIX_FADVISE
diff --git a/configure b/configure
index 9958be24..f1970e93 100755
--- a/configure
+++ b/configure
@@ -1807,6 +1807,97 @@ $as_echo "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_func
+
+# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists, giving a warning if it cannot be compiled using
+# the include files in INCLUDES and setting the cache variable VAR
+# accordingly.
+ac_fn_c_check_header_mongrel ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if eval \${$3+:} false; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_header_compiler=yes
+else
+ ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <$2>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ ac_header_preproc=yes
+else
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
+ yes:no: )
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+( $as_echo "## ------------------------------------ ##
+## Report this to dgilbert@interlog.com ##
+## ------------------------------------ ##"
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_mongrel
cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
@@ -12205,23 +12296,6 @@ $as_echo "#define STDC_HEADERS 1" >>confdefs.h
fi
-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
-
# check for functions
for ac_func in getopt_long
@@ -12301,6 +12375,37 @@ _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
;;
*-*-linux*)
@@ -12311,6 +12416,37 @@ _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*)
@@ -12318,6 +12454,9 @@ cat >>confdefs.h <<_ACEOF
#define SG_LIB_FREEBSD 1
_ACEOF
+
+$as_echo "#define HAVE_NVME 1" >>confdefs.h
+
os_cflags=''
os_libs='-lcam'
@@ -12376,6 +12515,37 @@ _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
;;
esac
diff --git a/configure.ac b/configure.ac
index 6c35d1af..135a5797 100644
--- a/configure.ac
+++ b/configure.ac
@@ -16,11 +16,6 @@ ifdef([AM_PROG_AR], [AM_PROG_AR], [])
# check for headers
AC_HEADER_STDC
-AC_CHECK_HEADERS([linux/types.h linux/bsg.h linux/kdev_t.h], [], [],
- [[#ifdef HAVE_LINUX_TYPES_H
- # include <linux/types.h>
- #endif
- ]])
# check for functions
AC_CHECK_FUNCS(getopt_long,
@@ -40,13 +35,26 @@ case "${host}" in
*-*-linux-gnu*)
AC_DEFINE_UNQUOTED(SG_LIB_LINUX, 1, [sg3_utils on linux])
AC_SUBST([os_cflags], [''])
- AC_SUBST([os_libs], ['']) ;;
+ 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
+ ]]) ;;
*-*-linux*)
AC_DEFINE_UNQUOTED(SG_LIB_LINUX, 1, [sg3_utils on linux])
AC_SUBST([os_cflags], [''])
- AC_SUBST([os_libs], ['']) ;;
+ 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'])
AC_SUBST([os_cflags], [''])
AC_SUBST([os_libs], ['-lcam']);;
*-*-solaris*)
@@ -69,7 +77,13 @@ case "${host}" in
*)
AC_DEFINE_UNQUOTED(SG_LIB_LINUX, 1, [assume sg3_utils on linux])
AC_SUBST([os_cflags], [''])
- AC_SUBST([os_libs], ['']) ;;
+ 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
+ ]]) ;;
esac
# Define platform-specific symbol.
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 8910e4db..b1ffa079 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -12,8 +12,8 @@ man_MANS = \
sg_sat_read_gplog.8 sg_sat_set_features.8 sg_senddiag.8 sg_ses.8 \
sg_ses_microcode.8 sg_start.8 sg_stpg.8 sg_sync.8 sg_timestamp.8 \
sg_turs.8 sg_unmap.8 sg_verify.8 sg_vpd.8 sg_wr_mode.8 \
- sg_write_atomic.8 sg_write_buffer.8 sg_write_long.8 sg_write_same.8 \
- sg_write_verify.8 sg_zone.8
+ sg_write_buffer.8 sg_write_long.8 sg_write_same.8 sg_write_verify.8 \
+ sg_write_x.8 sg_zone.8
CLEANFILES =
if OS_LINUX
diff --git a/doc/Makefile.in b/doc/Makefile.in
index 707f049a..e71f9b01 100644
--- a/doc/Makefile.in
+++ b/doc/Makefile.in
@@ -293,9 +293,9 @@ man_MANS = scsi_mandat.8 scsi_readcap.8 scsi_ready.8 scsi_satl.8 \
sg_sat_read_gplog.8 sg_sat_set_features.8 sg_senddiag.8 \
sg_ses.8 sg_ses_microcode.8 sg_start.8 sg_stpg.8 sg_sync.8 \
sg_timestamp.8 sg_turs.8 sg_unmap.8 sg_verify.8 sg_vpd.8 \
- sg_wr_mode.8 sg_write_atomic.8 sg_write_buffer.8 \
- sg_write_long.8 sg_write_same.8 sg_write_verify.8 sg_zone.8 \
- $(am__append_1) $(am__append_3) $(am__append_5)
+ sg_wr_mode.8 sg_write_buffer.8 sg_write_long.8 sg_write_same.8 \
+ sg_write_verify.8 sg_write_x.8 sg_zone.8 $(am__append_1) \
+ $(am__append_3) $(am__append_5)
CLEANFILES = $(am__append_2) $(am__append_4) $(am__append_6)
all: all-am
diff --git a/doc/sg_dd.8 b/doc/sg_dd.8
index 8e616755..0aa7a386 100644
--- a/doc/sg_dd.8
+++ b/doc/sg_dd.8
@@ -1,4 +1,4 @@
-.TH SG_DD "8" "June 2013" "sg3_utils\-1.37" SG3_UTILS
+.TH SG_DD "8" "November 2017" "sg3_utils\-1.43" SG3_UTILS
.SH NAME
sg_dd \- copy data to and from files and devices, especially SCSI
devices
@@ -151,10 +151,10 @@ unless 'oflag=append' or \fISEEK\fR is given.
\fBof2\fR=\fIOFILE2\fR
write output to \fIOFILE2\fR. The default action is not to do this additional
write (i.e. when this option is not given). \fIOFILE2\fR is assumed to be
-a normal file or a fifo (i.e. a named pipe). \fIOFILE2\fR is opened for writing,
-created if necessary, and closed at the end of the transfer. If \fIOFILE2\fR
-is a fifo (named pipe) then some other command should be consuming that
-data (e.g. 'md5sum OFILE2'), otherwise this utility will block.
+a normal file or a fifo (i.e. a named pipe). \fIOFILE2\fR is opened for
+writing, created if necessary, and closed at the end of the transfer. If
+\fIOFILE2\fR is a fifo (named pipe) then some other command should be
+consuming that data (e.g. 'md5sum OFILE2'), otherwise this utility will block.
.TP
\fBoflag\fR=\fIFLAGS\fR
where \fIFLAGS\fR is a comma separated list of one or more flags outlined
@@ -214,7 +214,7 @@ this conversion is very close to "iflag=coe" and is treated as such. See
the "coe" flag. Note that an error on \fIOFILE\fR will stop the copy.
.TP
notrunc
-this conversion is accepted for compatibilty with dd and ignored since
+this conversion is accepted for compatibility with dd and ignored since
the default action of this utility is not to truncate \fIOFILE\fR.
.TP
null
@@ -483,7 +483,7 @@ Written by Douglas Gilbert and Peter Allworth.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2000\-2013 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/doc/sg_luns.8 b/doc/sg_luns.8
index d9a9aafe..eb79ddf3 100644
--- a/doc/sg_luns.8
+++ b/doc/sg_luns.8
@@ -1,4 +1,4 @@
-.TH SG_LUNS "8" "May" "sg3_utils\-1.43" SG3_UTILS
+.TH SG_LUNS "8" "November" "sg3_utils\-1.43" SG3_UTILS
.SH NAME
sg_luns \- send SCSI REPORT LUNS command or decode given LUN
.SH SYNOPSIS
@@ -65,8 +65,8 @@ Linux LUN integer is output in hexadecimal.
\fB\-L\fR, \fB\-\-lu_cong\fR
this option is only considered with \fI\-\-decode\fR. When given once
then the list of LUNs is decoded as if the LU_CONG bit was set in
-each LU's coresponding INQUIRY response. When given twice the list of
-LUNs is decoded as if the LU_CONG bit was clear in each LU's coresponding
+each LU's corresponding INQUIRY response. When given twice the list of
+LUNs is decoded as if the LU_CONG bit was clear in each LU's corresponding
INQUIRY response. When this option is not given and \fI\-\-decode\fR is
given then an INQUIRY is sent to the \fIDEVICE\fR and the setting of
its LU_CONG bit is used to decode the list of LUNs.
@@ -311,7 +311,7 @@ Written by Douglas Gilbert.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2004\-2016 Douglas Gilbert
+Copyright \(co 2004\-2017 Douglas Gilbert
.br
This software is distributed under a FreeBSD license. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/doc/sg_persist.8 b/doc/sg_persist.8
index b2cb602a..ca0eb5db 100644
--- a/doc/sg_persist.8
+++ b/doc/sg_persist.8
@@ -1,4 +1,4 @@
-.TH SG_PERSIST "8" "October 2017" "sg3_utils\-1.43" SG3_UTILS
+.TH SG_PERSIST "8" "November 2017" "sg3_utils\-1.43" SG3_UTILS
.SH NAME
sg_persist \- use SCSI PERSISTENT RESERVE command to access registrations
and reservations
@@ -414,7 +414,7 @@ The above sequence of commands was tested successfully on a Seagate Savvio
The exit status of sg_persist is 0 when it is successful. Otherwise see
the sg3_utils(8) man page.
.SH ENVIRONMENT VARIABLES
-If the SG_PERSIST_IN_RDONLY enviroment variable is present and only if a
+If the SG_PERSIST_IN_RDONLY environment variable is present and only if a
PRIN command has been selected then the given \fIDEVICE\fR is opened
read\-only (e.g. in Unix that is with the O_RDONLY flag) unless
overridden. See the \fI\-\-readonly\fR option.
diff --git a/doc/sg_rdac.8 b/doc/sg_rdac.8
index 76553d7e..ddbda949 100644
--- a/doc/sg_rdac.8
+++ b/doc/sg_rdac.8
@@ -1,4 +1,4 @@
-.TH SG_RDAC "8" "April 2016" "sg3_utils\-1.43" SG3_UTILS
+.TH SG_RDAC "8" "November 2017" "sg3_utils\-1.43" SG3_UTILS
.SH NAME
sg_rdac \- display or modify SCSI RDAC Redundant Controller mode page
.SH SYNOPSIS
@@ -9,7 +9,7 @@ sg_rdac \- display or modify SCSI RDAC Redundant Controller mode page
.PP
sg_rdac displays or modifies the RDAC controller settings via the
Redundant Controller mode page (0x2C). When modifying the settings it
-allows to transfer the ownership of individual drives to the
+allows one to transfer the ownership of individual drives to the
controller the command was received on.
.SH OPTIONS
.TP
@@ -40,7 +40,7 @@ Written by Hannes Reinecke <hare at suse dot com>, based on sg_emc_trespass.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2006\-2016 Hannes Reinecke, Douglas Gilbert.
+Copyright \(co 2006\-2017 Hannes Reinecke, Douglas Gilbert.
.br
This software is distributed under the GPL version 2. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/doc/sg_read_attr.8 b/doc/sg_read_attr.8
index 191a8d82..b7c4d09e 100644
--- a/doc/sg_read_attr.8
+++ b/doc/sg_read_attr.8
@@ -1,4 +1,4 @@
-.TH SG_READ_ATTR "8" "September 2017" "sg3_utils\-1.43" SG3_UTILS
+.TH SG_READ_ATTR "8" "November 2017" "sg3_utils\-1.43" SG3_UTILS
.SH NAME
sg_read_attr \- send SCSI READ ATTRIBUTE command
.SH SYNOPSIS
@@ -37,8 +37,8 @@ is set to zero.
.TP
\fB\-f\fR, \fB\-\-filter\fR=\fIFL\fR
where \fIFL\fR is an attribute identifier in the range 0 to 65535 or \-1.
-Attribute identifiers are typical given in hexadecimal in which case the
-hex number should be prefixed by "0x" ot has a trailing "h". "\-1" is
+Attribute identifiers are typically given in hexadecimal in which case the
+hex number should be prefixed by "0x" or has a trailing "h". "\-1" is
the default value and means 'match all'; for all other values of \fIFL\fR
on the matching attribute is output.
.TP
diff --git a/doc/sg_verify.8 b/doc/sg_verify.8
index 1618c292..4e7ddf3a 100644
--- a/doc/sg_verify.8
+++ b/doc/sg_verify.8
@@ -1,4 +1,4 @@
-.TH SG_VERIFY "8" "September 2017" "sg3_utils\-1.43" SG3_UTILS
+.TH SG_VERIFY "8" "November 2017" "sg3_utils\-1.43" SG3_UTILS
.SH NAME
sg_verify \- invoke SCSI VERIFY command(s) on a block device
.SH SYNOPSIS
@@ -82,7 +82,7 @@ sets the BYTCHK field to \fIBCH\fR overriding the value (1) set by the
however sbc3r34 reserves the value 2. If this option is given then
\fI\-\-ndo=NDO\fR must also be given. If \fIBCH\fR is 3 then \fICOUNT\fR
must be 1 and \fINDO\fR should be the size of one logical block (plus the
-size of some or all of the protection infomation if \fIVRP\fR is greater
+size of some or all of the protection information if \fIVRP\fR is greater
than 0).
.TP
\fB\-g\fR, \fB\-\-group\fR=\fIGN\fR
@@ -111,7 +111,7 @@ of the device).
\fI\-\-in=FN\fR is given) or from stdin. Those bytes are placed in the
data\-out buffer associated with the SCSI VERIFY command and \fINDO\fR
is placed in the verification length field in the cdb. The default value
-for \fINDO\fR is 0 and the maximum value is dependant on the OS. If the
+for \fINDO\fR is 0 and the maximum value is dependent on the OS. If the
\fI\-\-ebytchk=BCH\fR option is not given then the BYTCHK field in the cdb
is set to 1.
.TP
diff --git a/doc/sg_write_atomic.8 b/doc/sg_write_x.8
index 5eb58d0f..a01befe3 100644
--- a/doc/sg_write_atomic.8
+++ b/doc/sg_write_x.8
@@ -1,6 +1,6 @@
-.TH SG_WRITE_ATOMIC "8" "September 2017" "sg3_utils\-1.43" SG3_UTILS
+.TH SG_WRITE_X "8" "November 2017" "sg3_utils\-1.43" SG3_UTILS
.SH NAME
-sg_write_atomic \- send SCSI WRITE ATOMIC command
+sg_write_atomic \- send SCSI WRITE ATOMIC/SCATTERED/STREAM command
.SH SYNOPSIS
.B sg_write_atomic
[\fI\-\-16\fR] [\fI\-\-32\fR] [\fI\-\-app-tag=AT\fR] [\fI\-\-boundary=AB\fR]
diff --git a/include/freebsd_nvme_ioctl.h b/include/freebsd_nvme_ioctl.h
new file mode 100644
index 00000000..f5761430
--- /dev/null
+++ b/include/freebsd_nvme_ioctl.h
@@ -0,0 +1,155 @@
+/*-
+ * Copyright (C) 2012-2013 Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+
+#include <sys/param.h>
+
+#define NVME_PASSTHROUGH_CMD _IOWR('n', 0, struct nvme_pt_command)
+
+#if __FreeBSD_version < 1100110
+struct nvme_command
+{
+ /* dword 0 */
+ uint16_t opc : 8; /* opcode */
+ uint16_t fuse : 2; /* fused operation */
+ uint16_t rsvd1 : 6;
+ uint16_t cid; /* command identifier */
+
+ /* dword 1 */
+ uint32_t nsid; /* namespace identifier */
+
+ /* dword 2-3 */
+ uint32_t rsvd2;
+ uint32_t rsvd3;
+
+ /* dword 4-5 */
+ uint64_t mptr; /* metadata pointer */
+
+ /* dword 6-7 */
+ uint64_t prp1; /* prp entry 1 */
+
+ /* dword 8-9 */
+ uint64_t prp2; /* prp entry 2 */
+
+ /* dword 10-15 */
+ uint32_t cdw10; /* command-specific */
+ uint32_t cdw11; /* command-specific */
+ uint32_t cdw12; /* command-specific */
+ uint32_t cdw13; /* command-specific */
+ uint32_t cdw14; /* command-specific */
+ uint32_t cdw15; /* command-specific */
+} __packed;
+
+struct nvme_status {
+
+ uint16_t p : 1; /* phase tag */
+ uint16_t sc : 8; /* status code */
+ uint16_t sct : 3; /* status code type */
+ uint16_t rsvd2 : 2;
+ uint16_t m : 1; /* more */
+ uint16_t dnr : 1; /* do not retry */
+} __packed;
+
+struct nvme_completion {
+
+ /* dword 0 */
+ uint32_t cdw0; /* command-specific */
+
+ /* dword 1 */
+ uint32_t rsvd1;
+
+ /* dword 2 */
+ uint16_t sqhd; /* submission queue head pointer */
+ uint16_t sqid; /* submission queue identifier */
+
+ /* dword 3 */
+ uint16_t cid; /* command identifier */
+ struct nvme_status status;
+} __packed;
+
+struct nvme_pt_command {
+
+ /*
+ * cmd is used to specify a passthrough command to a controller or
+ * namespace.
+ *
+ * The following fields from cmd may be specified by the caller:
+ * * opc (opcode)
+ * * nsid (namespace id) - for admin commands only
+ * * cdw10-cdw15
+ *
+ * Remaining fields must be set to 0 by the caller.
+ */
+ struct nvme_command cmd;
+
+ /*
+ * cpl returns completion status for the passthrough command
+ * specified by cmd.
+ *
+ * The following fields will be filled out by the driver, for
+ * consumption by the caller:
+ * * cdw0
+ * * status (except for phase)
+ *
+ * Remaining fields will be set to 0 by the driver.
+ */
+ struct nvme_completion cpl;
+
+ /* buf is the data buffer associated with this passthrough command. */
+ void * buf;
+
+ /*
+ * len is the length of the data buffer associated with this
+ * passthrough command.
+ */
+ uint32_t len;
+
+ /*
+ * is_read = 1 if the passthrough command will read data into the
+ * supplied buffer from the controller.
+ *
+ * is_read = 0 if the passthrough command will write data from the
+ * supplied buffer to the controller.
+ */
+ uint32_t is_read;
+
+ /*
+ * driver_lock is used by the driver only. It must be set to 0
+ * by the caller.
+ */
+ struct mtx * driver_lock;
+};
+#else
+#include <dev/nvme/nvme.h>
+#endif
+
+#define nvme_completion_is_error(cpl) \
+ ((cpl)->status.sc != 0 || (cpl)->status.sct != 0)
+
+#define NVME_CTRLR_PREFIX "/dev/nvme"
+#define NVME_NS_PREFIX "ns"
diff --git a/include/sg_lib.h b/include/sg_lib.h
index 0c74eb5d..6ba1c22f 100644
--- a/include/sg_lib.h
+++ b/include/sg_lib.h
@@ -440,6 +440,12 @@ int dStrHexStr(const char * str, int len, const char * leadin, int format,
*/
bool sg_is_big_endian();
+/* Returns true if byte sequence starting at bp with a length of b_len is
+ * all zeros (for sg_all_zeros()) or all 0xff_s (for sg_all_ffs());
+ * otherwise returns false. If bp is NULL ir b_len <= 0 returns false. */
+bool sg_all_zeros(const uint8_t * bp, int b_len);
+bool sg_all_ffs(const uint8_t * bp, int b_len);
+
/* Extract character sequence from ATA words as in the model string
* in a IDENTIFY DEVICE response. Returns number of characters
* written to 'ochars' before 0 character is found or 'num' words
diff --git a/include/sg_pt.h b/include/sg_pt.h
index 16c0bfa6..60237062 100644
--- a/include/sg_pt.h
+++ b/include/sg_pt.h
@@ -41,6 +41,16 @@ int scsi_pt_open_flags(const char * device_name, int flags, int verbose);
/* Returns 0 if successful. If error in Unix returns negated errno. */
int scsi_pt_close_device(int device_fd);
+/* Assumes dev_fd is an "open" file handle associated with device_name. If
+ * the implementation (possibly for one OS) cannot determine from dev_fd if
+ * a SCSI or NVMe pass-through is referenced, then it might guess based on
+ * device_name. Returns 1 if SCSI generic pass-though device, returns 2 if
+ * secondary SCSI pass-through device (in Linux a bsg device); returns 3 is
+ * char NVMe device (i.e. no NSID); returns 4 if block NVMe device (includes
+ * NSID), or 0 if something else (e.g. ATA block device) or dev_fd < 0.
+ * If error, returns negated errno (operating system) value. */
+int check_pt_file_handle(int dev_fd, const char * device_name, int verbose);
+
/* Creates an object that can be used to issue one or more SCSI commands
* (or task management functions). Returns NULL if problem.
@@ -48,8 +58,30 @@ int scsi_pt_close_device(int device_fd);
* destruct_scsi_pt_obj() when it is no longer needed. */
struct sg_pt_base * construct_scsi_pt_obj(void);
+/* An alternate way to create an object that can be used to issue one or
+ * more SCSI commands (or task management functions). This variant
+ * associate a device file descriptor (handle) with the object and a
+ * verbose argument that causes error messages if errors occur. The
+ * reason for this is to optionally allow the detection of NVMe devices
+ * that will cause pt_device_is_nvme() to return true. Set dev_fd to
+ * -1 if no open device file descriptor is available. Caller should
+ * additionally call get_scsi_pt_os_err() after this call. */
+struct sg_pt_base *
+ construct_scsi_pt_obj_with_fd(int dev_fd, int verbose);
+
+/* Forget any previous dev_fd and install the one given. May attempt to
+ * find file type (e.g. if pass-though) from OS so there could be an error.
+ * Returns 0 for success or the the same value as get_scsi_pt_os_err()
+ * will return. dev_fd should be >= 0 for a valid file handle or -1 . */
+int set_pt_file_handle(struct sg_pt_base * objp, int dev_fd, int verbose);
+
+/* Valid file handles (which is the return value) are >= 0 . Returns -1
+ * if there is no valid file handle. */
+int get_pt_file_handle(const struct sg_pt_base * objp);
+
/* Clear state information held in *objp . This allows this object to be
- * used to issue more than one SCSI command. */
+ * used to issue more than one SCSI command. The dev_fd is remembered.
+ * Use set_pt_file_handle() to change dev_fd. */
void clear_scsi_pt_obj(struct sg_pt_base * objp);
/* Set the CDB (command descriptor block) */
@@ -60,10 +92,10 @@ void set_scsi_pt_sense(struct sg_pt_base * objp, unsigned char * sense,
int max_sense_len);
/* Set a pointer and length to be used for data transferred from device */
void set_scsi_pt_data_in(struct sg_pt_base * objp, /* from device */
- unsigned char * dxferp, int dxfer_len);
+ unsigned char * dxferp, int dxfer_ilen);
/* Set a pointer and length to be used for data transferred to device */
void set_scsi_pt_data_out(struct sg_pt_base * objp, /* to device */
- const unsigned char * dxferp, int dxfer_len);
+ const unsigned char * dxferp, int dxfer_olen);
/* The following "set_"s implementations may be dummies */
void set_scsi_pt_packet_id(struct sg_pt_base * objp, int pack_id);
void set_scsi_pt_tag(struct sg_pt_base * objp, uint64_t tag);
@@ -90,7 +122,9 @@ void set_scsi_pt_flags(struct sg_pt_base * objp, int flags);
* (e.g. by a signal) in which case -EINTR would be returned. Note that
* system call errors also can be fetched with get_scsi_pt_os_err().
* Return 0 if okay (i.e. at the very least: command sent). Positive
- * return values are errors (see SCSI_PT_DO_* defines). */
+ * return values are errors (see SCSI_PT_DO_* defines). If a file descriptor
+ * has already been provided by construct_scsi_pt_obj_with_fd() then the
+ * given 'fd' can be -1 or the same value as given to the constructor. */
int do_scsi_pt(struct sg_pt_base * objp, int fd, int timeout_secs,
int verbose);
@@ -102,19 +136,25 @@ int do_scsi_pt(struct sg_pt_base * objp, int fd, int timeout_secs,
/* highest numbered applicable category returned */
int get_scsi_pt_result_category(const struct sg_pt_base * objp);
-/* If not available return 0 */
+/* If not available return 0 which implies there is no residual
+ * value. If supported the number of bytes actually sent back by
+ * the device is 'dxfer_ilen - get_scsi_pt_len()' bytes. */
int get_scsi_pt_resid(const struct sg_pt_base * objp);
+
/* Returns SCSI status value (from device that received the
command). */
int get_scsi_pt_status_response(const struct sg_pt_base * objp);
+
/* Actual sense length returned. If sense data is present but
actual sense length is not known, return 'max_sense_len' */
int get_scsi_pt_sense_len(const struct sg_pt_base * objp);
-/* If not available return 0 */
+
+/* If not available return 0 (for success). */
int get_scsi_pt_os_err(const struct sg_pt_base * objp);
char * get_scsi_pt_os_err_str(const struct sg_pt_base * objp, int max_b_len,
char * b);
-/* If not available return 0 */
+
+/* If not available return 0 (for success) */
int get_scsi_pt_transport_err(const struct sg_pt_base * objp);
char * get_scsi_pt_transport_err_str(const struct sg_pt_base * objp,
int max_b_len, char * b);
@@ -122,13 +162,25 @@ char * get_scsi_pt_transport_err_str(const struct sg_pt_base * objp,
/* If not available return -1 */
int get_scsi_pt_duration_ms(const struct sg_pt_base * objp);
-/* Return true if device associated with 'objp' uses NVMe command set. */
+/* Return true if device associated with 'objp' uses NVMe command set. To
+ * be useful (in modifying the type of command sent (SCSI or NVMe) then
+ * construct_scsi_pt_obj_with_fd() should be used followed by an invocation
+ * of this function. */
bool pt_device_is_nvme(const struct sg_pt_base * objp);
+/* If a NVMe block device (which includes the NSID) handle is associated
+ * with 'objp', then its NSID is returned (values range from 0x1 to
+ * 0xffffffe). Otherwise 0 is returned. */
+uint32_t get_pt_nvme_nsid(const struct sg_pt_base * objp);
+
/* Should be invoked once per objp after other processing is complete in
* order to clean up resources. For ever successful construct_scsi_pt_obj()
- * call there should be one destruct_scsi_pt_obj(). */
+ * call there should be one destruct_scsi_pt_obj(). If the
+ * construct_scsi_pt_obj_with_fd() function was used to create thsi object
+ * then the dev_fd provided to that constructor is not altered by this
+ * destructor. So the user should still close dev_fd (perhaps with
+ * scsi_pt_close_device() ). */
void destruct_scsi_pt_obj(struct sg_pt_base * objp);
#ifdef SG_LIB_WIN32
diff --git a/include/sg_pt_nvme.h b/include/sg_pt_nvme.h
new file mode 100644
index 00000000..646a3a10
--- /dev/null
+++ b/include/sg_pt_nvme.h
@@ -0,0 +1,70 @@
+#ifndef SG_PT_NVME_H
+#define SG_PT_NVME_H
+
+/*
+ * Copyright (c) 2017 Douglas Gilbert.
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the BSD_LICENSE file.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* structures copied and slightly modified from <linux/nvme_ioctl.h> which
+ * is Copyright (c) 2011-2014, Intel Corporation. */
+
+struct sg_nvme_user_io {
+ uint8_t opcode;
+ uint8_t flags;
+ uint16_t control;
+ uint16_t nblocks;
+ uint16_t rsvd;
+ uint64_t metadata;
+ uint64_t addr;
+ uint64_t slba;
+ uint32_t dsmgmt;
+ uint32_t reftag;
+ uint16_t apptag;
+ uint16_t appmask;
+#ifdef SG_LIB_FREEBSD
+} __packed;
+#else
+};
+#endif
+
+struct sg_nvme_passthru_cmd {
+ uint8_t opcode;
+ uint8_t flags;
+ uint16_t rsvd1;
+ uint32_t nsid;
+ uint32_t cdw2;
+ uint32_t cdw3;
+ uint64_t metadata;
+ uint64_t addr;
+ uint32_t metadata_len;
+ uint32_t data_len;
+ uint32_t cdw10;
+ uint32_t cdw11;
+ uint32_t cdw12;
+ uint32_t cdw13;
+ uint32_t cdw14;
+ uint32_t cdw15;
+
+ uint32_t timeout_ms;
+ uint32_t result;
+#ifdef SG_LIB_FREEBSD
+} __packed;
+#else
+};
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/sg_lib.c b/lib/sg_lib.c
index 7014a0eb..81ac1ddb 100644
--- a/lib/sg_lib.c
+++ b/lib/sg_lib.c
@@ -2586,6 +2586,28 @@ sg_is_big_endian()
the most significant byte */
}
+bool sg_all_zeros(const uint8_t * bp, int b_len)
+{
+ if ((NULL == bp) || (b_len <= 0))
+ return false;
+ for (--b_len; b_len >= 0; --b_len) {
+ if (0x0 != bp[b_len])
+ return false;
+ }
+ return true;
+}
+
+bool sg_all_ffs(const uint8_t * bp, int b_len)
+{
+ if ((NULL == bp) || (b_len <= 0))
+ return false;
+ for (--b_len; b_len >= 0; --b_len) {
+ if (0xff != bp[b_len])
+ return false;
+ }
+ return true;
+}
+
static uint16_t
swapb_uint16(uint16_t u)
{
diff --git a/lib/sg_lib_data.c b/lib/sg_lib_data.c
index d7840bb2..a58c2369 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.30 20171003";/* spc5r17, sbc4r14 */
+const char * sg_lib_version_str = "2.31 20171112";/* spc5r17, sbc4r14 */
/* indexed by pdt; those that map to own index do not decay */
diff --git a/lib/sg_pt_common.c b/lib/sg_pt_common.c
index ab70e463..afff6479 100644
--- a/lib/sg_pt_common.c
+++ b/lib/sg_pt_common.c
@@ -14,7 +14,7 @@
#endif
-static const char * scsi_pt_version_str = "2.14 20171030";
+static const char * scsi_pt_version_str = "3.01 20171107";
const char *
scsi_pt_version()
diff --git a/lib/sg_pt_freebsd.c b/lib/sg_pt_freebsd.c
index 2828c34a..dc4f3c78 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.16 20171030 */
+/* sg_pt_freebsd version 1.18 20171114 */
#include <stdio.h>
#include <stdlib.h>
@@ -14,6 +14,8 @@
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
+#include <limits.h>
+#include <libgen.h> /* for basename */
#include <fcntl.h>
#include <errno.h>
#include <err.h>
@@ -29,6 +31,7 @@
#include "sg_pt.h"
#include "sg_lib.h"
+#include "freebsd_nvme_ioctl.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -40,9 +43,14 @@
struct freebsd_dev_channel {
- char* devname; // the SCSI device name
- int unitnum; // the SCSI unit number
- struct cam_device* cam_dev;
+ int unitnum; // the SCSI unit number
+ bool is_nvme;
+ bool is_char;
+ uint32_t nsid;
+ uint32_t nv_ctrlid;
+ int dev_fd; // for NVMe, use -1 to indicate not provided
+ char* devname; // the device name
+ struct cam_device* cam_dev;
};
// Private table of open devices: guaranteed zero on startup since
@@ -67,12 +75,16 @@ struct sg_pt_freebsd_scsi {
int in_err;
int os_err;
int transport_err;
+ int dev_han; // -1 if not provided
+ uint32_t result; // NVMe result from completion
};
struct sg_pt_base {
struct sg_pt_freebsd_scsi impl;
};
+static const uint32_t broadcast_nsid = 0xffffffff;
+
#if defined(__GNUC__) || defined(__clang__)
static int pr2ws(const char * fmt, ...)
__attribute__ ((format (printf, 1, 2)));
@@ -105,15 +117,22 @@ scsi_pt_open_device(const char * device_name, bool read_only, int verbose)
}
/* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed
- * together. The 'flags' argument is ignored in FreeBSD.
+ * together. The 'flags' is only used on NVMe devices. It is ignored on
+ * SCSI and ATA devices in FreeBSD.
* Returns >= 0 if successful, otherwise returns negated errno. */
int
-scsi_pt_open_flags(const char * device_name,
- int flags __attribute__ ((unused)), int verbose)
+scsi_pt_open_flags(const char * device_name, int oflags, int verbose)
{
- struct freebsd_dev_channel *fdchan;
+ bool is_char, is_block, possible_nvme;
+ char tmp;
+ int k, err, dev_fd, ret;
+ uint32_t nsid, nv_ctrlid;
+ ssize_t s;
+ struct freebsd_dev_channel *fdchan = NULL;
struct cam_device* cam_dev;
- int k;
+ struct stat a_stat;
+ char b[PATH_MAX];
+ char full_path[64];
// Search table for a free entry
for (k = 0; k < FREEBSD_MAXDEV; k++)
@@ -125,68 +144,184 @@ scsi_pt_open_flags(const char * device_name,
if (k == FREEBSD_MAXDEV) {
if (verbose)
pr2ws("too many open file descriptors (%d)\n", FREEBSD_MAXDEV);
- errno = EMFILE;
- return -1;
+ ret = -EMFILE;
+ goto err_out;
+ }
+ if (stat(device_name, &a_stat) < 0) {
+ err = errno;
+ pr2ws("%s: unable to stat(%s): %s\n", __func__, device_name,
+ strerror(err));
+ ret = -err;
+ goto err_out;
+ }
+ is_block = S_ISBLK(a_stat.st_mode);
+ is_char = S_ISCHR(a_stat.st_mode);
+ if (! (is_block || is_char)) {
+ if (verbose)
+ pr2ws("%s: %s is not char nor block device\n", __func__,
+ device_name);
+ ret = -ENODEV;
+ goto err_out;
+ }
+ s = readlink(device_name, b, sizeof(b));
+ if (s <= 0) {
+ strncpy(b, device_name, PATH_MAX - 1);
+ b[PATH_MAX - 1] = '\0';
+ }
+
+ /* Some code borrowed from smartmontools, Christian Franke */
+ nsid = broadcast_nsid;
+ nv_ctrlid = broadcast_nsid;
+ possible_nvme = false;
+ while (true) { /* dummy loop, so can 'break' out */
+ if(sscanf(b, NVME_CTRLR_PREFIX"%u%c", &nv_ctrlid, &tmp) == 1) {
+ if(nv_ctrlid == broadcast_nsid)
+ break;
+ } else if (sscanf(b, NVME_CTRLR_PREFIX"%d"NVME_NS_PREFIX"%d%c",
+ &nv_ctrlid, &nsid, &tmp) == 2) {
+ if((nv_ctrlid == broadcast_nsid) || (nsid == broadcast_nsid))
+ break;
+ } else
+ break;
+ possible_nvme = true;
+ break;
}
fdchan = (struct freebsd_dev_channel *)
calloc(1,sizeof(struct freebsd_dev_channel));
if (fdchan == NULL) {
// errno already set by call to calloc()
- return -1;
+ ret = -ENOMEM;
+ goto err_out;
+ }
+ fdchan->dev_fd = -1;
+ if (! (fdchan->devname = (char *)calloc(1, DEV_IDLEN+1))) {
+ ret = -ENOMEM;
+ goto err_out;
}
- if (! (fdchan->devname = (char *)calloc(1, DEV_IDLEN+1)))
- return -1;
+ if (possible_nvme) {
+ // we should always open controller, not namespace device
+ snprintf(fdchan->devname, DEV_IDLEN, NVME_CTRLR_PREFIX"%d",
+ nv_ctrlid);
+ dev_fd = open(fdchan->devname, oflags);
+ if (dev_fd < 0) {
+ err = errno;
+ if (verbose)
+ pr2ws("%s: open(%s) failed: %s (errno=%d), try SCSI/ATA\n",
+ __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;
+ 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) {
if (verbose)
pr2ws("bad device name structure\n");
errno = EINVAL;
- return -1;
+ ret = -errno;
+ goto err_out;
}
+ if (verbose > 4)
+ pr2ws("%s: cam_get_device, f->devname: %s, f->unitnum=%d\n", __func__,
+ fdchan->devname, fdchan->unitnum);
if (! (cam_dev = cam_open_spec_device(fdchan->devname,
fdchan->unitnum, O_RDWR, NULL))) {
if (verbose)
pr2ws("cam_open_spec_device: %s\n", cam_errbuf);
- errno = EPERM; /* permissions or no CAM */
- return -1;
+ errno = EPERM; /* permissions or not CAM device (NVMe ?) */
+ ret = -errno;
+ goto err_out;
}
fdchan->cam_dev = cam_dev;
// return pointer to "file descriptor" table entry, properly offset.
devicetable[k] = fdchan;
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;
+ }
+ return ret;
}
/* Returns 0 if successful. If error in Unix returns negated errno. */
int
-scsi_pt_close_device(int device_fd)
+scsi_pt_close_device(int device_han)
{
struct freebsd_dev_channel *fdchan;
- int fd = device_fd - FREEBSD_FDOFFSET;
+ int han = device_han - FREEBSD_FDOFFSET;
- if ((fd < 0) || (fd >= FREEBSD_MAXDEV)) {
+ if ((han < 0) || (han >= FREEBSD_MAXDEV)) {
errno = ENODEV;
- return -1;
+ return -errno;
}
- fdchan = devicetable[fd];
+ fdchan = devicetable[han];
if (NULL == fdchan) {
errno = ENODEV;
- return -1;
+ 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);
+ }
free(fdchan);
- devicetable[fd] = NULL;
+ devicetable[han] = NULL;
+ errno = 0;
return 0;
}
+/* Assumes dev_fd is an "open" file handle associated with some device.
+ * Returns 1 if SCSI generic pass-though device, returns 2 if secondary
+ * SCSI pass-through device (in Linux a bsg device); returns 3 is char
+ * NVMe device (i.e. no NSID); returns 4 if block NVMe device (includes
+ * NSID), or 0 if something else (e.g. ATA block device) or dev_fd < 0.
+ * If error, returns negated errno (operating system) value. */
+int
+check_pt_file_handle(int device_han, const char * device_name, int verbose)
+{
+ struct freebsd_dev_channel *fdchan;
+ int han = device_han - FREEBSD_FDOFFSET;
+
+ if ((han < 0) || (han >= FREEBSD_MAXDEV)) {
+ errno = ENODEV;
+ return -errno;
+ }
+ fdchan = devicetable[han];
+ if (NULL == fdchan) {
+ 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;
+ else {
+ if (device_name) { }
+ if (verbose) { }
+ return 0;
+ }
+}
+
struct sg_pt_base *
-construct_scsi_pt_obj()
+construct_scsi_pt_obj_with_fd(int dev_han, int verbose)
{
struct sg_pt_freebsd_scsi * ptp;
@@ -201,10 +336,19 @@ construct_scsi_pt_obj()
if (ptp) {
memset(ptp, 0, sizeof(struct sg_pt_freebsd_scsi));
ptp->dxfer_dir = CAM_DIR_NONE;
- }
+ ptp->dev_han = (dev_han < 0) ? -1 : dev_han;
+ } else if (verbose)
+ pr2ws("%s: calloc() out of memory\n", __func__);
return (struct sg_pt_base *)ptp;
}
+
+struct sg_pt_base *
+construct_scsi_pt_obj()
+{
+ return construct_scsi_pt_obj_with_fd(-1, 0);
+}
+
void
destruct_scsi_pt_obj(struct sg_pt_base * vp)
{
@@ -220,16 +364,44 @@ destruct_scsi_pt_obj(struct sg_pt_base * vp)
void
clear_scsi_pt_obj(struct sg_pt_base * vp)
{
+ int dev_han;
struct sg_pt_freebsd_scsi * ptp = &vp->impl;
if (ptp) {
if (ptp->ccb)
cam_freeccb(ptp->ccb);
+ 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;
}
}
+/* Forget any previous dev_han and install the one given. May attempt to
+ * find file type (e.g. if pass-though) from OS so there could be an error.
+ * Returns 0 for success or the the same value as get_scsi_pt_os_err()
+ * will return. dev_han should be >= 0 for a valid file handle or -1 . */
+int set_pt_file_handle(struct sg_pt_base * vp, int dev_han, int verbose)
+{
+ struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+ if (ptp)
+ ptp->dev_han = dev_han;
+ ptp->os_err = 0;
+ if (verbose) { }
+ return 0;
+
+}
+
+/* Valid file handles (which is the return value) are >= 0 . Returns -1
+ * if there is no valid file handle. */
+int get_pt_file_handle(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+ return ptp ? ptp->dev_han : -1;
+}
+
void
set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb, int cdb_len)
{
@@ -330,13 +502,13 @@ set_scsi_pt_flags(struct sg_pt_base * objp, int flags)
* Clears os_err field prior to active call (whose result may set it
* again). */
int
-do_scsi_pt(struct sg_pt_base * vp, int device_fd, int time_secs, int verbose)
+do_scsi_pt(struct sg_pt_base * vp, int dev_han, int time_secs, int verbose)
{
- int fd = device_fd - FREEBSD_FDOFFSET;
+ int n, len, timout_ms;
+ int han;
struct sg_pt_freebsd_scsi * ptp = &vp->impl;
struct freebsd_dev_channel *fdchan;
union ccb *ccb;
- int len, timout_ms;
ptp->os_err = 0;
if (ptp->in_err) {
@@ -344,25 +516,87 @@ do_scsi_pt(struct sg_pt_base * vp, int device_fd, int time_secs, int verbose)
pr2ws("Replicated or unused set_scsi_pt...\n");
return SCSI_PT_DO_BAD_PARAMS;
}
+ if (dev_han < 0) {
+ if (ptp->dev_han < 0) {
+ if (verbose)
+ pr2ws("%s: No device file handle given\n", __func__);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ } else {
+ if (ptp->dev_han >= 0) {
+ if (dev_han != ptp->dev_han) {
+ if (verbose)
+ pr2ws("%s: file handle given to create and this "
+ "differ\n", __func__);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ } 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 ((fd < 0) || (fd >= FREEBSD_MAXDEV)) {
+ if ((han < 0) || (han >= FREEBSD_MAXDEV)) {
if (verbose)
- pr2ws("Bad file descriptor\n");
+ pr2ws("Bad file handle\n");
ptp->os_err = ENODEV;
return -ptp->os_err;
}
- fdchan = devicetable[fd];
+ fdchan = devicetable[han];
if (NULL == fdchan) {
if (verbose)
pr2ws("File descriptor closed??\n");
ptp->os_err = ENODEV;
return -ptp->os_err;
}
+ if (fdchan->is_nvme) {
+ int err;
+ struct nvme_pt_command npc;
+
+ 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->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 (verbose)
pr2ws("No open CAM device\n");
@@ -464,7 +698,18 @@ get_scsi_pt_status_response(const struct sg_pt_base * vp)
{
const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
- return ptp->scsi_status;
+ if (ptp) {
+ int han = ptp->dev_han - FREEBSD_FDOFFSET;
+ struct freebsd_dev_channel *fdchan;
+
+ if ((han < 0) || (han >= FREEBSD_MAXDEV))
+ return -1;
+ fdchan = devicetable[han];
+ if (NULL == fdchan)
+ return -1;
+ return fdchan->is_nvme ? (int)ptp->result : ptp->scsi_status;
+ }
+ return -1;
}
int
@@ -533,10 +778,45 @@ pt_device_is_nvme(const struct sg_pt_base * vp)
{
const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
- if (ptp) { ; } /* suppress warning */
+ if (ptp && (ptp->dev_han >= 0)) {
+ int han = ptp->dev_han - FREEBSD_FDOFFSET;
+ struct freebsd_dev_channel *fdchan;
+
+ if ((han < 0) || (han >= FREEBSD_MAXDEV)) {
+ errno = ENODEV;
+ return false;
+ }
+ fdchan = devicetable[han];
+ if (NULL == fdchan) {
+ errno = ENODEV;
+ return false;
+ }
+ return fdchan->is_nvme ;
+ }
return false;
}
+/* If a NVMe block device (which includes the NSID) handle is associated
+ * with 'objp', then its NSID is returned (values range from 0x1 to
+ * 0xffffffe). Otherwise 0 is returned. */
+uint32_t
+get_pt_nvme_nsid(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+ if (ptp && (ptp->dev_han >= 0)) {
+ int han = ptp->dev_han - FREEBSD_FDOFFSET;
+ struct freebsd_dev_channel *fdchan;
+
+ if ((han < 0) || (han >= FREEBSD_MAXDEV))
+ return 0;
+ fdchan = devicetable[han];
+ if (NULL == fdchan)
+ return 0;
+ return fdchan->nsid ;
+ }
+ return 0;
+}
char *
get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b)
diff --git a/lib/sg_pt_linux.c b/lib/sg_pt_linux.c
index f1b6ffaf..cc0ad0a9 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.29 20171030 */
+/* sg_pt_linux version 1.30 20171113 */
#include <stdio.h>
@@ -29,10 +29,106 @@
#include "config.h"
#endif
+#include <linux/major.h>
+
#include "sg_pt.h"
#include "sg_lib.h"
#include "sg_linux_inc.h"
+#if (__STDC_VERSION__ >= 199901L) /* C99 or later */
+typedef intptr_t sg_intptr_t;
+#else
+typedef long sg_intptr_t;
+#endif
+
+// xxxxxxxxxxxxxxxx testing <<<<<<<<<<<<<<<<<<<<<<<<
+// #undef HAVE_LINUX_NVME_IOCTL_H
+
+#ifdef HAVE_LINUX_NVME_IOCTL_H
+#include <linux/nvme_ioctl.h>
+#else
+
+/*
+ * Definitions for the NVM Express ioctl interface
+ * Copyright (c) 2011-2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/types.h>
+
+struct nvme_user_io {
+ __u8 opcode;
+ __u8 flags;
+ __u16 control;
+ __u16 nblocks;
+ __u16 rsvd;
+ __u64 metadata;
+ __u64 addr;
+ __u64 slba;
+ __u32 dsmgmt;
+ __u32 reftag;
+ __u16 apptag;
+ __u16 appmask;
+};
+
+struct nvme_passthru_cmd {
+ __u8 opcode;
+ __u8 flags;
+ __u16 rsvd1;
+ __u32 nsid;
+ __u32 cdw2;
+ __u32 cdw3;
+ __u64 metadata;
+ __u64 addr;
+ __u32 metadata_len;
+ __u32 data_len;
+ __u32 cdw10;
+ __u32 cdw11;
+ __u32 cdw12;
+ __u32 cdw13;
+ __u32 cdw14;
+ __u32 cdw15;
+ __u32 timeout_ms;
+ __u32 result;
+};
+
+#define nvme_admin_cmd nvme_passthru_cmd
+
+#define NVME_IOCTL_ID _IO('N', 0x40)
+#define NVME_IOCTL_ADMIN_CMD _IOWR('N', 0x41, struct nvme_admin_cmd)
+#define NVME_IOCTL_SUBMIT_IO _IOW('N', 0x42, struct nvme_user_io)
+#define NVME_IOCTL_IO_CMD _IOWR('N', 0x43, struct nvme_passthru_cmd)
+#define NVME_IOCTL_RESET _IO('N', 0x44)
+#define NVME_IOCTL_SUBSYS_RESET _IO('N', 0x45)
+
+#endif /* end of HAVE_LINUX_NVME_IOCTL_H */
+
+#include <linux/types.h>
+#include <linux/bsg.h>
+
+#ifdef major
+#define SG_DEV_MAJOR major
+#else
+#ifdef HAVE_LINUX_KDEV_T_H
+#include <linux/kdev_t.h>
+#endif
+#define SG_DEV_MAJOR MAJOR /* MAJOR() macro faulty if > 255 minors */
+#endif
+
+#ifndef BLOCK_EXT_MAJOR
+#define BLOCK_EXT_MAJOR 259
+#endif
+
+#define SG_NVME_BROADCAST_NSID 0xffffffff
+
#define DEF_TIMEOUT 60000 /* 60,000 millisecs (60 seconds) */
static const char * linux_host_bytes[] = {
@@ -89,6 +185,10 @@ static const char * linux_driver_suggests[] = {
#define SG_LIB_SUGGEST_MASK SUGGEST_MASK
#define SG_LIB_DRIVER_SENSE DRIVER_SENSE
+static bool bsg_nvme_char_major_checked = false;
+static int bsg_major = 0;
+static volatile int nvme_char_major = 0;
+
#if defined(__GNUC__) || defined(__clang__)
static int pr2ws(const char * fmt, ...)
@@ -110,6 +210,181 @@ pr2ws(const char * fmt, ...)
return n;
}
+/* This function only needs to be called once (unless a NVMe controller
+ * can be hot-plugged into system in which case it should be called
+ * (again) after that event). */
+static void
+find_bsg_nvme_char_major(int verbose)
+{
+ bool got_one = false;
+ int n;
+ const char * proc_devices = "/proc/devices";
+ char * cp;
+ FILE *fp;
+ char a[128];
+ char b[128];
+
+ if (NULL == (fp = fopen(proc_devices, "r"))) {
+ if (verbose)
+ pr2ws("fopen %s failed: %s\n", proc_devices, strerror(errno));
+ return;
+ }
+ while ((cp = fgets(b, sizeof(b), fp))) {
+ if ((1 == sscanf(b, "%126s", a)) &&
+ (0 == memcmp(a, "Character", 9)))
+ break;
+ }
+ while (cp && (cp = fgets(b, sizeof(b), fp))) {
+ if (2 == sscanf(b, "%d %126s", &n, a)) {
+ if (0 == strcmp("bsg", a)) {
+ bsg_major = n;
+ if (got_one)
+ break;
+ got_one = true;
+ } else if (0 == strcmp("nvme", a)) {
+ nvme_char_major = n;
+ if (got_one)
+ break;
+ got_one = true;
+ }
+ } else
+ break;
+ }
+ if (verbose > 3) {
+ if (cp) {
+ if (bsg_major > 0)
+ pr2ws("found bsg_major=%d\n", bsg_major);
+ if (nvme_char_major > 0)
+ pr2ws("found nvme_char_major=%d\n", nvme_char_major);
+ } else
+ pr2ws("found no bsg not nvme char device in %s\n", proc_devices);
+ }
+ fclose(fp);
+}
+
+/* Assumes that find_bsg_nvme_char_major() has already been called. Returns
+ * true if dev_fd is a scsi generic pass-through device. If yields
+ * *is_nvme_p = true with *nsid_p = 0 then dev_fd is a NVMe char device.
+ * If yields *nsid_p > 0 then dev_fd is a NVMe block device. */
+static bool
+check_file_type(int dev_fd, struct stat * dev_statp, bool * is_bsg_p,
+ bool * is_nvme_p, uint32_t * nsid_p, int * os_err_p,
+ int verbose)
+{
+ bool is_nvme = false;
+ bool is_sg = false;
+ bool is_bsg = false;
+ bool is_block = false;
+ int os_err = 0;
+ int major_num;
+ uint32_t nsid = 0; /* invalid NSID */
+
+ if (dev_fd >= 0) {
+ if (fstat(dev_fd, dev_statp) < 0) {
+ os_err = errno;
+ if (verbose)
+ pr2ws("%s: fstat() failed: %s (errno=%d)\n", __func__,
+ safe_strerror(os_err), os_err);
+ goto skip_out;
+ }
+ major_num = (int)SG_DEV_MAJOR(dev_statp->st_rdev);
+ if (S_ISCHR(dev_statp->st_mode)) {
+ if (SCSI_GENERIC_MAJOR == major_num)
+ is_sg = true;
+ else if (bsg_major == major_num)
+ is_bsg = true;
+ else if (nvme_char_major == major_num)
+ is_nvme = true;
+ } else if (S_ISBLK(dev_statp->st_mode)) {
+ is_block = true;
+ if (BLOCK_EXT_MAJOR == major_num) {
+ is_nvme = true;
+ nsid = ioctl(dev_fd, NVME_IOCTL_ID, NULL);
+ if (SG_NVME_BROADCAST_NSID == nsid) { /* means ioctl error */
+ os_err = errno;
+ if (verbose)
+ pr2ws("%s: ioctl(NVME_IOCTL_ID) failed: %s "
+ "(errno=%d)\n", __func__, safe_strerror(os_err),
+ os_err);
+ } else
+ os_err = 0;
+ }
+ }
+ } else {
+ os_err = EBADF;
+ if (verbose)
+ pr2ws("%s: invalid file descriptor (%d)\n", __func__, dev_fd);
+ }
+skip_out:
+ if (verbose > 3) {
+ pr2ws("%s: file descriptor is ", __func__);
+ if (is_sg)
+ pr2ws("sg device\n");
+ else if (is_bsg)
+ pr2ws("bsg device\n");
+ else if (is_nvme && (0 == nsid))
+ pr2ws("NVMe char device\n");
+ else if (is_nvme)
+ pr2ws("NVMe block device, nsid=%lld\n",
+ ((uint32_t)-1 == nsid) ? -1LL : (long long)nsid);
+ else if (is_block)
+ pr2ws("block device\n");
+ else
+ pr2ws("undetermined device, could be regular file\n");
+ }
+ if (is_bsg_p)
+ *is_bsg_p = is_bsg;
+ if (is_nvme_p)
+ *is_nvme_p = is_nvme;
+ if (nsid_p)
+ *nsid_p = nsid;
+ if (os_err_p)
+ *os_err_p = os_err;
+ return is_sg;
+}
+
+/* Assumes dev_fd is an "open" file handle associated with device_name. If
+ * the implementation (possibly for one OS) cannot determine from dev_fd if
+ * a SCSI or NVMe pass-through is referenced, then it might guess based on
+ * device_name. Returns 1 if SCSI generic pass-though device, returns 2 if
+ * secondary SCSI pass-through device (in Linux a bsg device); returns 3 is
+ * char NVMe device (i.e. no NSID); returns 4 if block NVMe device (includes
+ * NSID), or 0 if something else (e.g. ATA block device) or dev_fd < 0.
+ * If error, returns negated errno (operating system) value. */
+int
+check_pt_file_handle(int dev_fd, const char * device_name, int verbose)
+{
+ if (verbose > 4)
+ pr2ws("%s: dev_fd=%d, device_name: %s\n", __func__, dev_fd,
+ device_name);
+ /* Linux doesn't need device_name to determine which pass-through */
+ if (! bsg_nvme_char_major_checked) {
+ bsg_nvme_char_major_checked = true;
+ find_bsg_nvme_char_major(verbose);
+ }
+ if (dev_fd >= 0) {
+ bool is_sg, is_bsg, is_nvme;
+ int err;
+ uint32_t nsid;
+ struct stat a_stat;
+
+ is_sg = check_file_type(dev_fd, &a_stat, &is_bsg, &is_nvme, &nsid,
+ &err, verbose);
+ if (err)
+ return -err;
+ else if (is_sg)
+ return 1;
+ else if (is_bsg)
+ return 2;
+ else if (is_nvme && (0 == nsid))
+ return 3;
+ else if (is_nvme)
+ return 4;
+ else
+ return 0;
+ } else
+ return 0;
+}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
#if defined(IGNORE_LINUX_BSG) || ! defined(HAVE_LINUX_BSG_H)
@@ -123,9 +398,12 @@ pr2ws(const char * fmt, ...)
struct sg_pt_linux_scsi {
struct sg_io_hdr io_hdr;
+ int dev_fd; /* -1 if not given */
int in_err;
int os_err;
+ bool is_sg;
bool is_nvme;
+ uint32_t nvme_nsid;
};
struct sg_pt_base {
@@ -133,6 +411,7 @@ struct sg_pt_base {
};
+
/* 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)
@@ -154,6 +433,10 @@ scsi_pt_open_flags(const char * device_name, int flags, int verbose)
if (verbose > 1) {
pr2ws("open %s with flags=0x%x\n", device_name, flags);
}
+ if (! bsg_nvme_char_major_checked) {
+ bsg_nvme_char_major_checked = true;
+ find_bsg_nvme_char_major(verbose);
+ }
fd = open(device_name, flags);
if (fd < 0)
fd = -errno;
@@ -172,10 +455,11 @@ scsi_pt_close_device(int device_fd)
return res;
}
-
+/* Caller should additionally call get_scsi_pt_os_err() after this call */
struct sg_pt_base *
-construct_scsi_pt_obj()
+construct_scsi_pt_obj_with_fd(int dev_fd, int verbose)
{
+ int err;
struct sg_pt_linux_scsi * ptp;
/* The following 2 lines are temporary. It is to avoid a NULL pointer
@@ -187,12 +471,23 @@ construct_scsi_pt_obj()
ptp = (struct sg_pt_linux_scsi *)
calloc(1, sizeof(struct sg_pt_linux_scsi));
if (ptp) {
- ptp->io_hdr.interface_id = 'S';
- ptp->io_hdr.dxfer_direction = SG_DXFER_NONE;
- }
+ err = set_pt_file_handle((struct sg_pt_base *)ptp, dev_fd, verbose);
+ if ((0 == err) && (! ptp->is_nvme)) {
+ ptp->io_hdr.interface_id = 'S';
+ ptp->io_hdr.dxfer_direction = SG_DXFER_NONE;
+ }
+ } else if (verbose)
+ pr2ws("%s: calloc() failed, out of memory?\n", __func__);
+
return (struct sg_pt_base *)ptp;
}
+struct sg_pt_base *
+construct_scsi_pt_obj()
+{
+ return construct_scsi_pt_obj_with_fd(-1 /* dev_fd */, 0 /* verbose */);
+}
+
void
destruct_scsi_pt_obj(struct sg_pt_base * vp)
{
@@ -202,16 +497,64 @@ destruct_scsi_pt_obj(struct sg_pt_base * vp)
free(ptp);
}
+/* Remembers previous device file descriptor */
void
clear_scsi_pt_obj(struct sg_pt_base * vp)
{
+ bool is_sg, is_nvme;
+ int fd, nvme_nsid;
struct sg_pt_linux_scsi * ptp = &vp->impl;
if (ptp) {
+ fd = ptp->dev_fd;
+ is_sg = ptp->is_sg;
+ is_nvme = ptp->is_nvme;
+ nvme_nsid = ptp->nvme_nsid;
memset(ptp, 0, sizeof(struct sg_pt_linux_scsi));
ptp->io_hdr.interface_id = 'S';
ptp->io_hdr.dxfer_direction = SG_DXFER_NONE;
+ ptp->dev_fd = fd;
+ ptp->is_sg = is_sg;
+ ptp->is_nvme = is_nvme;
+ ptp->nvme_nsid = nvme_nsid;
+ }
+}
+
+/* Forget any previous dev_fd and install the one given. May attempt to
+ * find file type (e.g. if pass-though) from OS so there could be an error.
+ * Returns 0 for success or the the same value as get_scsi_pt_os_err()
+ * will return. dev_fd should be >= 0 for a valid file handle or -1 . */
+int
+set_pt_file_handle(struct sg_pt_base * vp, int dev_fd, int verbose)
+{
+ struct sg_pt_linux_scsi * ptp = &vp->impl;
+ struct stat a_stat;
+
+ if (! bsg_nvme_char_major_checked) {
+ bsg_nvme_char_major_checked = true;
+ find_bsg_nvme_char_major(verbose);
}
+ ptp->dev_fd = dev_fd;
+ if (dev_fd >= 0)
+ ptp->is_sg = check_file_type(dev_fd, &a_stat, NULL, &ptp->is_nvme,
+ &ptp->nvme_nsid, &ptp->os_err, verbose);
+ else {
+ ptp->is_sg = false;
+ ptp->is_nvme = false;
+ ptp->nvme_nsid = 0;
+ ptp->os_err = 0;
+ }
+ return ptp->os_err;
+}
+
+/* Valid file handles (which is the return value) are >= 0 . Returns -1
+ * if there is no valid file handle. */
+int
+get_pt_file_handle(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+ return ptp->dev_fd;
}
void
@@ -242,15 +585,15 @@ set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense,
/* Setup for data transfer from device */
void
set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp,
- int dxfer_len)
+ int dxfer_ilen)
{
struct sg_pt_linux_scsi * ptp = &vp->impl;
if (ptp->io_hdr.dxferp)
++ptp->in_err;
- if (dxfer_len > 0) {
+ if (dxfer_ilen > 0) {
ptp->io_hdr.dxferp = dxferp;
- ptp->io_hdr.dxfer_len = dxfer_len;
+ ptp->io_hdr.dxfer_len = dxfer_ilen;
ptp->io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
}
}
@@ -258,15 +601,15 @@ set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp,
/* Setup for data transfer toward device */
void
set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp,
- int dxfer_len)
+ int dxfer_olen)
{
struct sg_pt_linux_scsi * ptp = &vp->impl;
if (ptp->io_hdr.dxferp)
++ptp->in_err;
- if (dxfer_len > 0) {
+ if (dxfer_olen > 0) {
ptp->io_hdr.dxferp = (unsigned char *)dxferp;
- ptp->io_hdr.dxfer_len = dxfer_len;
+ ptp->io_hdr.dxfer_len = dxfer_olen;
ptp->io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
}
}
@@ -331,13 +674,78 @@ set_scsi_pt_flags(struct sg_pt_base * vp, int flags)
}
}
+/* Executes NVMe Admin command (or at least forwards it to lower layers).
+ * Returns 0 for success, negative numbers are negated 'errno' values from
+ * OS system calls. Positive return values are errors from this package.
+ * When time_secs is 0 the Linux NVMe Admin command default of 60 seconds
+ * is used. */
+int
+do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
+{
+ int n, len;
+ struct sg_pt_linux_scsi * ptp = &vp->impl;
+ struct nvme_passthru_cmd cmd;
+
+ if (vb > 3)
+ pr2ws("%s: fd=%d, time_secs=%d\n", __func__, fd, time_secs);
+ if (! ptp->io_hdr.cmdp) {
+ if (vb)
+ pr2ws("No NVMe command given (set_scsi_pt_cdb())\n");
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ n = ptp->io_hdr.cmd_len;
+ len = (int)sizeof(cmd);
+ n = (n < len) ? n : len;
+ if (n < 8) {
+ if (vb)
+ pr2ws("%s: command length of %d bytes is too short\n", __func__,
+ n);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ memcpy(&cmd, (unsigned char *)ptp->io_hdr.cmdp, n);
+ if (n < len) /* zero out rest of 'cmd' */
+ memset((unsigned char *)&cmd + n, 0, len - n);
+ if (ptp->io_hdr.dxfer_len > 0) {
+ cmd.data_len = ptp->io_hdr.dxfer_len;
+ cmd.addr = (__u64)(sg_intptr_t)ptp->io_hdr.dxferp;
+ }
+ if (time_secs < 0)
+ cmd.timeout_ms = 0;
+ else
+ cmd.timeout_ms = 1000 * cmd.timeout_ms;
+ if (vb > 2) {
+ pr2ws("NVMe command:\n");
+ dStrHex((const char *)&cmd, len, 1);
+ }
+ if (ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd) < 0) {
+ ptp->os_err = errno;
+ if (vb > 2)
+ pr2ws("%s: ioctl(NVME_IOCTL_ADMIN_CMD) failed: %s (errno=%d)\n",
+ __func__, strerror(ptp->os_err), ptp->os_err);
+ return -ptp->os_err;
+ } else
+ ptp->os_err = 0;
+ n = ptp->io_hdr.mx_sb_len;
+ if ((n > 0) && ptp->io_hdr.sbp) {
+ n = (n < len) ? n : len;
+ memcpy(ptp->io_hdr.sbp, &cmd, n);
+ ptp->io_hdr.sb_len_wr = n;
+ }
+ if (vb > 2)
+ pr2ws("%s: timeout_ms=%u, result=%u\n", __func__, cmd.timeout_ms,
+ cmd.result);
+ return 0;
+}
+
/* Executes SCSI command (or at least forwards it to lower layers).
- * Clears os_err field prior to active call (whose result may set it
- * again). */
+ * Returns 0 for success, negative numbers are negated 'errno' values from
+ * OS system calls. Positive return values are errors from this package. */
int
do_scsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose)
{
+ int err;
struct sg_pt_linux_scsi * ptp = &vp->impl;
+ bool have_checked_for_type = (ptp->dev_fd >= 0);
ptp->os_err = 0;
if (ptp->in_err) {
@@ -345,6 +753,26 @@ do_scsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose)
pr2ws("Replicated or unused set_scsi_pt... functions\n");
return SCSI_PT_DO_BAD_PARAMS;
}
+ if (fd >= 0) {
+ if ((ptp->dev_fd >= 0) && (fd != ptp->dev_fd)) {
+ if (verbose)
+ pr2ws("%s: file descriptor given to create() and here "
+ "differ\n", __func__);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ ptp->dev_fd = fd;
+ } else if (ptp->dev_fd < 0) {
+ if (verbose)
+ pr2ws("%s: invalid file descriptors\n", __func__);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ if (! have_checked_for_type) {
+ err = set_pt_file_handle(vp, ptp->dev_fd, verbose);
+ if (err)
+ return -ptp->os_err;
+ }
+ if (ptp->is_nvme)
+ return do_nvme_pt(vp, ptp->dev_fd, time_secs, verbose);
if (NULL == ptp->io_hdr.cmdp) {
if (verbose)
pr2ws("No SCSI command (cdb) given\n");
@@ -355,7 +783,7 @@ do_scsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose)
DEF_TIMEOUT);
if (ptp->io_hdr.sbp && (ptp->io_hdr.mx_sb_len > 0))
memset(ptp->io_hdr.sbp, 0, ptp->io_hdr.mx_sb_len);
- if (ioctl(fd, SG_IO, &ptp->io_hdr) < 0) {
+ if (ioctl(ptp->dev_fd, SG_IO, &ptp->io_hdr) < 0) {
ptp->os_err = errno;
if (verbose > 1)
pr2ws("ioctl(SG_IO) failed: %s (errno=%d)\n",
@@ -444,6 +872,17 @@ pt_device_is_nvme(const struct sg_pt_base * vp)
return ptp->is_nvme;
}
+/* If a NVMe block device (which includes the NSID) handle is associated
+ * with 'objp', then its NSID is returned (values range from 0x1 to
+ * 0xffffffe). Otherwise 0 is returned. */
+uint32_t
+get_pt_nvme_nsid(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+ return ptp->nvme_nsid;
+}
+
/* Returns b which will contain a null char terminated string (if
* max_b_len > 0). That string should decode Linux driver and host
* status values. */
@@ -544,59 +983,23 @@ get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b)
struct sg_pt_linux_scsi {
struct sg_io_v4 io_hdr; /* use v4 header as it is more general */
+ int dev_fd; /* -1 if not given */
int in_err;
int os_err;
unsigned char tmf_request[4];
+ bool is_sg;
+ bool is_bsg;
bool is_nvme;
+ uint32_t nvme_nsid; /* 1 to 0xfffffffe are possibly valid, 0
+ * implies dev_fd is not a NVMe device
+ * (is_nvme=false) or it is a NVMe char
+ * device (e.g. /dev/nvme0 ) */
};
struct sg_pt_base {
struct sg_pt_linux_scsi impl;
};
-static bool bsg_major_checked = false;
-static int bsg_major = 0;
-
-
-
-static void
-find_bsg_major(int verbose)
-{
- const char * proc_devices = "/proc/devices";
- FILE *fp;
- char a[128];
- char b[128];
- char * cp;
- int n;
-
- if (NULL == (fp = fopen(proc_devices, "r"))) {
- if (verbose)
- pr2ws("fopen %s failed: %s\n", proc_devices, strerror(errno));
- return;
- }
- while ((cp = fgets(b, sizeof(b), fp))) {
- if ((1 == sscanf(b, "%126s", a)) &&
- (0 == memcmp(a, "Character", 9)))
- break;
- }
- while (cp && (cp = fgets(b, sizeof(b), fp))) {
- if (2 == sscanf(b, "%d %126s", &n, a)) {
- if (0 == strcmp("bsg", a)) {
- bsg_major = n;
- break;
- }
- } else
- break;
- }
- if (verbose > 3) {
- if (cp)
- pr2ws("found bsg_major=%d\n", bsg_major);
- else
- pr2ws("found no bsg char device in %s\n", proc_devices);
- }
- fclose(fp);
-}
-
/* Returns >= 0 if successful. If error in Unix returns negated errno. */
int
@@ -616,15 +1019,20 @@ scsi_pt_open_flags(const char * device_name, int flags, int verbose)
{
int fd;
- if (! bsg_major_checked) {
- bsg_major_checked = true;
- find_bsg_major(verbose);
+ if (! bsg_nvme_char_major_checked) {
+ bsg_nvme_char_major_checked = true;
+ find_bsg_nvme_char_major(verbose);
}
- if (verbose > 1)
+ if (verbose > 1) {
pr2ws("open %s with flags=0x%x\n", device_name, flags);
+ }
fd = open(device_name, flags);
- if (fd < 0)
+ if (fd < 0) {
fd = -errno;
+ if (verbose > 1)
+ pr2ws("%s: open(%s, 0x%x) failed: %s\n", __func__, device_name,
+ flags, safe_strerror(-fd));
+ }
return fd;
}
@@ -641,25 +1049,44 @@ scsi_pt_close_device(int device_fd)
}
+/* Caller should additionally call get_scsi_pt_os_err() after this call */
struct sg_pt_base *
-construct_scsi_pt_obj()
+construct_scsi_pt_obj_with_fd(int dev_fd, int verbose)
{
+ int err;
struct sg_pt_linux_scsi * ptp;
+ /* The following 2 lines are temporary. It is to avoid a NULL pointer
+ * crash when an old utility is used with a newer library built after
+ * the sg_warnings_strm cleanup */
+ if (NULL == sg_warnings_strm)
+ sg_warnings_strm = stderr;
+
ptp = (struct sg_pt_linux_scsi *)
calloc(1, sizeof(struct sg_pt_linux_scsi));
if (ptp) {
- ptp->io_hdr.guard = 'Q';
+ err = set_pt_file_handle((struct sg_pt_base *)ptp, dev_fd, verbose);
+ if ((0 == err) && (! ptp->is_nvme)) {
+ ptp->io_hdr.guard = 'Q';
#ifdef BSG_PROTOCOL_SCSI
- ptp->io_hdr.protocol = BSG_PROTOCOL_SCSI;
+ ptp->io_hdr.protocol = BSG_PROTOCOL_SCSI;
#endif
#ifdef BSG_SUB_PROTOCOL_SCSI_CMD
- ptp->io_hdr.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD;
+ ptp->io_hdr.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD;
#endif
- }
+ }
+ } else if (verbose)
+ pr2ws("%s: calloc() failed, out of memory?\n", __func__);
+
return (struct sg_pt_base *)ptp;
}
+struct sg_pt_base *
+construct_scsi_pt_obj()
+{
+ return construct_scsi_pt_obj_with_fd(-1 /* dev_fd */, 0 /* verbose */);
+}
+
void
destruct_scsi_pt_obj(struct sg_pt_base * vp)
{
@@ -669,12 +1096,20 @@ destruct_scsi_pt_obj(struct sg_pt_base * vp)
free(ptp);
}
+/* Remembers previous device file descriptor */
void
clear_scsi_pt_obj(struct sg_pt_base * vp)
{
+ bool is_sg, is_bsg, is_nvme;
+ int fd, nvme_nsid;
struct sg_pt_linux_scsi * ptp = &vp->impl;
if (ptp) {
+ fd = ptp->dev_fd;
+ is_sg = ptp->is_sg;
+ is_bsg = ptp->is_bsg;
+ is_nvme = ptp->is_nvme;
+ nvme_nsid = ptp->nvme_nsid;
memset(ptp, 0, sizeof(struct sg_pt_linux_scsi));
ptp->io_hdr.guard = 'Q';
#ifdef BSG_PROTOCOL_SCSI
@@ -683,9 +1118,53 @@ clear_scsi_pt_obj(struct sg_pt_base * vp)
#ifdef BSG_SUB_PROTOCOL_SCSI_CMD
ptp->io_hdr.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD;
#endif
+ ptp->dev_fd = fd;
+ ptp->is_sg = is_sg;
+ ptp->is_bsg = is_bsg;
+ ptp->is_nvme = is_nvme;
+ ptp->nvme_nsid = nvme_nsid;
}
}
+/* Forget any previous dev_fd and install the one given. May attempt to
+ * find file type (e.g. if pass-though) from OS so there could be an error.
+ * Returns 0 for success or the the same value as get_scsi_pt_os_err()
+ * will return. dev_fd should be >= 0 for a valid file handle or -1 . */
+int
+set_pt_file_handle(struct sg_pt_base * vp, int dev_fd, int verbose)
+{
+ struct sg_pt_linux_scsi * ptp = &vp->impl;
+ struct stat a_stat;
+
+ if (! bsg_nvme_char_major_checked) {
+ bsg_nvme_char_major_checked = true;
+ find_bsg_nvme_char_major(verbose);
+ }
+ ptp->dev_fd = dev_fd;
+ if (dev_fd >= 0)
+ ptp->is_sg = check_file_type(dev_fd, &a_stat, &ptp->is_bsg,
+ &ptp->is_nvme, &ptp->nvme_nsid,
+ &ptp->os_err, verbose);
+ else {
+ ptp->is_sg = false;
+ ptp->is_bsg = false;
+ ptp->is_nvme = false;
+ ptp->nvme_nsid = 0;
+ ptp->os_err = 0;
+ }
+ return ptp->os_err;
+}
+
+/* Valid file handles (which is the return value) are >= 0 . Returns -1
+ * if there is no valid file handle. */
+int
+get_pt_file_handle(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+ return ptp->dev_fd;
+}
+
void
set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb,
int cdb_len)
@@ -694,8 +1173,7 @@ set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb,
if (ptp->io_hdr.request)
++ptp->in_err;
- /* C99 has intptr_t instead of long */
- ptp->io_hdr.request = (__u64)(long)cdb;
+ ptp->io_hdr.request = (__u64)(sg_intptr_t)cdb;
ptp->io_hdr.request_len = cdb_len;
}
@@ -708,37 +1186,37 @@ set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense,
if (ptp->io_hdr.response)
++ptp->in_err;
memset(sense, 0, max_sense_len);
- ptp->io_hdr.response = (__u64)(long)sense;
+ ptp->io_hdr.response = (__u64)(sg_intptr_t)sense;
ptp->io_hdr.max_response_len = max_sense_len;
}
/* Setup for data transfer from device */
void
set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp,
- int dxfer_len)
+ int dxfer_ilen)
{
struct sg_pt_linux_scsi * ptp = &vp->impl;
if (ptp->io_hdr.din_xferp)
++ptp->in_err;
- if (dxfer_len > 0) {
- ptp->io_hdr.din_xferp = (__u64)(long)dxferp;
- ptp->io_hdr.din_xfer_len = dxfer_len;
+ if (dxfer_ilen > 0) {
+ ptp->io_hdr.din_xferp = (__u64)(sg_intptr_t)dxferp;
+ ptp->io_hdr.din_xfer_len = dxfer_ilen;
}
}
/* Setup for data transfer toward device */
void
set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp,
- int dxfer_len)
+ int dxfer_olen)
{
struct sg_pt_linux_scsi * ptp = &vp->impl;
if (ptp->io_hdr.dout_xferp)
++ptp->in_err;
- if (dxfer_len > 0) {
- ptp->io_hdr.dout_xferp = (__u64)(long)dxferp;
- ptp->io_hdr.dout_xfer_len = dxfer_len;
+ if (dxfer_olen > 0) {
+ ptp->io_hdr.dout_xferp = (__u64)(sg_intptr_t)dxferp;
+ ptp->io_hdr.dout_xfer_len = dxfer_olen;
}
}
@@ -766,7 +1244,7 @@ set_scsi_pt_task_management(struct sg_pt_base * vp, int tmf_code)
ptp->io_hdr.subprotocol = 1; /* SCSI task management function */
ptp->tmf_request[0] = (unsigned char)tmf_code; /* assume it fits */
- ptp->io_hdr.request = (__u64)(long)(&(ptp->tmf_request[0]));
+ ptp->io_hdr.request = (__u64)(sg_intptr_t)(&(ptp->tmf_request[0]));
ptp->io_hdr.request_len = 1;
}
@@ -950,6 +1428,17 @@ pt_device_is_nvme(const struct sg_pt_base * vp)
return ptp->is_nvme;
}
+/* If a NVMe block device (which includes the NSID) handle is associated
+ * with 'objp', then its NSID is returned (values range from 0x1 to
+ * 0xffffffe). Otherwise 0 is returned. */
+uint32_t
+get_pt_nvme_nsid(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+ return ptp->nvme_nsid;
+}
+
/* Executes SCSI command using sg v3 interface */
static int
do_scsi_pt_v3(struct sg_pt_linux_scsi * ptp, int fd, int time_secs,
@@ -999,7 +1488,7 @@ do_scsi_pt_v3(struct sg_pt_linux_scsi * ptp, int fd, int time_secs,
ptp->os_err = errno;
if (verbose > 1)
pr2ws("ioctl(SG_IO v3) failed: %s (errno=%d)\n",
- strerror(ptp->os_err), ptp->os_err);
+ safe_strerror(ptp->os_err), ptp->os_err);
return -ptp->os_err;
}
ptp->io_hdr.device_status = (__u32)v3_hdr.status;
@@ -1012,40 +1501,118 @@ do_scsi_pt_v3(struct sg_pt_linux_scsi * ptp, int fd, int time_secs,
return 0;
}
+/* Executes NVMe Admin command (or at least forwards it to lower layers).
+ * Returns 0 for success, negative numbers are negated 'errno' values from
+ * OS system calls. Positive return values are errors from this package.
+ * When time_secs is 0 the Linux NVMe Admin command default of 60 seconds
+ * is used. */
+static int
+do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
+{
+ int n, len;
+ struct sg_pt_linux_scsi * ptp = &vp->impl;
+ struct nvme_passthru_cmd cmd;
+
+ if (vb > 3)
+ pr2ws("%s: fd=%d, time_secs=%d\n", __func__, fd, time_secs);
+ if (! ptp->io_hdr.request) {
+ if (vb)
+ pr2ws("No NVMe command given (set_scsi_pt_cdb())\n");
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ n = ptp->io_hdr.request_len;
+ len = (int)sizeof(cmd);
+ n = (n < len) ? n : len;
+ if (n < 64) {
+ if (vb)
+ pr2ws("%s: command length of %d bytes is too short\n", __func__,
+ n);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ memcpy(&cmd, (unsigned char *)ptp->io_hdr.request, n);
+ if (n < len) /* zero out rest of 'cmd' */
+ memset((unsigned char *)&cmd + n, 0, len - n);
+ if (ptp->io_hdr.din_xfer_len > 0) {
+ cmd.data_len = ptp->io_hdr.din_xfer_len;
+ cmd.addr = (__u64)(sg_intptr_t)ptp->io_hdr.din_xferp;
+ } else if (ptp->io_hdr.dout_xfer_len > 0) {
+ cmd.data_len = ptp->io_hdr.dout_xfer_len;
+ cmd.addr = (__u64)(sg_intptr_t)ptp->io_hdr.dout_xferp;
+ }
+ if (time_secs < 0)
+ cmd.timeout_ms = 0;
+ else
+ cmd.timeout_ms = 1000 * cmd.timeout_ms;
+ if (vb > 2) {
+ pr2ws("NVMe command:\n");
+ dStrHex((const char *)&cmd, len, 1);
+ }
+ if (ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd) < 0) {
+ ptp->os_err = errno;
+ if (vb > 2)
+ pr2ws("%s: ioctl(NVME_IOCTL_ADMIN_CMD) failed: %s (errno=%d)\n",
+ __func__, strerror(ptp->os_err), ptp->os_err);
+ return -ptp->os_err;
+ } else
+ ptp->os_err = 0;
+ n = ptp->io_hdr.max_response_len;
+ if ((n > 0) && ptp->io_hdr.response) {
+ n = (n < len) ? n : len;
+ memcpy((uint8_t *)ptp->io_hdr.response, &cmd, n);
+ }
+ if (vb > 2)
+ pr2ws("%s: timeout_ms=%u, result=%u\n", __func__, cmd.timeout_ms,
+ cmd.result);
+ return 0;
+}
+
/* Executes SCSI command (or at least forwards it to lower layers).
- * Clears os_err field prior to active call (whose result may set it
- * again). */
+ * Returns 0 for success, negative numbers are negated 'errno' values from
+ * OS system calls. Positive return values are errors from this package. */
int
do_scsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose)
{
+ int err;
struct sg_pt_linux_scsi * ptp = &vp->impl;
+ bool have_checked_for_type = (ptp->dev_fd >= 0);
- if (! bsg_major_checked) {
- bsg_major_checked = true;
- find_bsg_major(verbose);
+ if (! bsg_nvme_char_major_checked) {
+ bsg_nvme_char_major_checked = true;
+ find_bsg_nvme_char_major(verbose);
}
- ptp->os_err = 0;
if (ptp->in_err) {
if (verbose)
pr2ws("Replicated or unused set_scsi_pt... functions\n");
return SCSI_PT_DO_BAD_PARAMS;
}
- if (bsg_major <= 0)
- return do_scsi_pt_v3(ptp, fd, time_secs, verbose);
- else {
- struct stat a_stat;
-
- if (fstat(fd, &a_stat) < 0) {
- ptp->os_err = errno;
- if (verbose > 1)
- pr2ws("fstat() failed: %s (errno=%d)\n",
- strerror(ptp->os_err), ptp->os_err);
- return -ptp->os_err;
+ if (fd >= 0) {
+ if ((ptp->dev_fd >= 0) && (fd != ptp->dev_fd)) {
+ if (verbose)
+ pr2ws("%s: file descriptor given to create() and here "
+ "differ\n", __func__);
+ return SCSI_PT_DO_BAD_PARAMS;
}
- if (! S_ISCHR(a_stat.st_mode) ||
- (bsg_major != (int)SG_DEV_MAJOR(a_stat.st_rdev)))
- return do_scsi_pt_v3(ptp, fd, time_secs, verbose);
+ ptp->dev_fd = fd;
+ } else if (ptp->dev_fd < 0) {
+ if (verbose)
+ pr2ws("%s: invalid file descriptors\n", __func__);
+ return SCSI_PT_DO_BAD_PARAMS;
}
+ if (! have_checked_for_type) {
+ err = set_pt_file_handle(vp, ptp->dev_fd, verbose);
+ if (err)
+ return -ptp->os_err;
+ }
+ if (ptp->os_err)
+ return -ptp->os_err;
+ if (ptp->is_nvme)
+ return do_nvme_pt(vp, ptp->dev_fd, time_secs, verbose);
+ else if (bsg_major <= 0)
+ return do_scsi_pt_v3(ptp, fd, time_secs, verbose);
+ else if (ptp->is_bsg)
+ ; /* drop through to sg v4 implementation */
+ else
+ return do_scsi_pt_v3(ptp, fd, time_secs, verbose);
if (! ptp->io_hdr.request) {
if (verbose)
@@ -1068,7 +1635,7 @@ do_scsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose)
ptp->os_err = errno;
if (verbose > 1)
pr2ws("ioctl(SG_IO v4) failed: %s (errno=%d)\n",
- strerror(ptp->os_err), ptp->os_err);
+ safe_strerror(ptp->os_err), ptp->os_err);
return -ptp->os_err;
}
return 0;
diff --git a/lib/sg_pt_osf1.c b/lib/sg_pt_osf1.c
index 3fbe3b51..c2e46912 100644
--- a/lib/sg_pt_osf1.c
+++ b/lib/sg_pt_osf1.c
@@ -57,6 +57,8 @@ struct sg_pt_osf1_scsi {
int in_err;
int os_err;
int transport_err;
+ bool is_nvme;
+ int dev_fd;
};
struct sg_pt_base {
@@ -181,18 +183,27 @@ scsi_pt_close_device(int device_fd)
}
struct sg_pt_base *
-construct_scsi_pt_obj()
+construct_scsi_pt_obj_with_fd(int device_fd, int verbose)
{
struct sg_pt_osf1_scsi * ptp;
ptp = (struct sg_pt_osf1_scsi *)malloc(sizeof(struct sg_pt_osf1_scsi));
if (ptp) {
bzero(ptp, sizeof(struct sg_pt_osf1_scsi));
+ ptp->dev_fd = (device_fd < 0) ? -1 : device_fd;
+ ptp->is_nvme = false;
ptp->dxfer_dir = CAM_DIR_NONE;
- }
+ } else if (verbose)
+ pr2ws("%s: malloc() out of memory\n", __func__);
return (struct sg_pt_base *)ptp;
}
+struct sg_pt_base *
+construct_scsi_pt_obj(void)
+{
+ return construct_scsi_pt_obj_with_fd(-1, 0);
+}
+
void
destruct_scsi_pt_obj(struct sg_pt_base * vp)
{
@@ -353,19 +364,36 @@ do_scsi_pt(struct sg_pt_base * vp, int device_fd, int time_secs, int verbose)
pr2ws("Replicated or unused set_scsi_pt...\n");
return SCSI_PT_DO_BAD_PARAMS;
}
+ if (device_fd < 0) {
+ if (ptp->dev_fd < 0) {
+ if (verbose)
+ pr2ws("%s: No device file descriptor given\n", __func__);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ } else {
+ if (ptp->dev_fd >= 0) {
+ if (device_fd != ptp->dev_fd) {
+ if (verbose)
+ pr2ws("%s: file descriptor given to create and this "
+ "differ\n", __func__);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ } else
+ ptp->dev_fd = device_fd;
+ }
if (NULL == ptp->cdb) {
if (verbose)
pr2ws("No command (cdb) given\n");
return SCSI_PT_DO_BAD_PARAMS;
}
- if ((device_fd < 0) || (device_fd >= OSF1_MAXDEV)) {
+ if ((ptp->dev_fd < 0) || (ptp->dev_fd >= OSF1_MAXDEV)) {
if (verbose)
pr2ws("Bad file descriptor\n");
ptp->os_err = ENODEV;
return -ptp->os_err;
}
- fdchan = devicetable[device_fd];
+ fdchan = devicetable[ptp->dev_fd];
if (NULL == fdchan) {
if (verbose)
pr2ws("File descriptor closed??\n");
@@ -419,7 +447,7 @@ do_scsi_pt(struct sg_pt_base * vp, int device_fd, int time_secs, int verbose)
/* If the SIM queue is frozen, release SIM queue. */
if (ccb.cam_ch.cam_status & CAM_SIM_QFRZN)
- release_sim(vp, device_fd, verbose);
+ release_sim(vp, ptp->dev_fd, verbose);
return 0;
}
@@ -497,8 +525,7 @@ pt_device_is_nvme(const struct sg_pt_base * vp)
{
const struct sg_pt_osf1_scsi * ptp = &vp->impl;
- if (vp) { ; } /* suppress warings */
- return false;
+ return ptp ? ptp->is_nvme : false;
}
char *
diff --git a/lib/sg_pt_solaris.c b/lib/sg_pt_solaris.c
index 588751d7..0fd1f23c 100644
--- a/lib/sg_pt_solaris.c
+++ b/lib/sg_pt_solaris.c
@@ -5,7 +5,7 @@
* license that can be found in the BSD_LICENSE file.
*/
-/* sg_pt_solaris version 1.06 20171030 */
+/* sg_pt_solaris version 1.07 20171107 */
#include <stdio.h>
#include <stdlib.h>
@@ -39,6 +39,8 @@ struct sg_pt_solaris_scsi {
int max_sense_len;
int in_err;
int os_err;
+ bool is_nvme;
+ int dev_fd;
};
struct sg_pt_base {
@@ -89,6 +91,25 @@ scsi_pt_close_device(int device_fd)
}
struct sg_pt_base *
+construct_scsi_pt_obj_with_fd(int dev_fd, int verbose)
+{
+ struct sg_pt_solaris_scsi * ptp;
+
+ ptp = (struct sg_pt_solaris_scsi *)
+ calloc(1, sizeof(struct sg_pt_solaris_scsi));
+ if (ptp) {
+ ptp->dev_fd = (dev_fd < 0) ? -1 : devfd;
+ ptp->is_nvme = false;
+ ptp->uscsi.uscsi_timeout = DEF_TIMEOUT;
+ ptp->uscsi.uscsi_flags = USCSI_READ | USCSI_ISOLATE | USCSI_RQENABLE;
+ ptp->uscsi.uscsi_timeout = DEF_TIMEOUT;
+ } else if (verbose)
+ fprintf(sg_warnings_strm ? sg_warnings_strm : stderr,
+ "%s: calloc() out of memory\n", __func__);
+ return (struct sg_pt_base *)ptp;
+}
+
+struct sg_pt_base *
construct_scsi_pt_obj()
{
struct sg_pt_solaris_scsi * ptp;
@@ -236,33 +257,49 @@ int
do_scsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose)
{
struct sg_pt_solaris_scsi * ptp = &vp->impl;
+ FILE * ferr = sg_warnings_strm ? sg_warnings_strm : stderr;
ptp->os_err = 0;
if (ptp->in_err) {
if (verbose)
- fprintf(sg_warnings_strm ? sg_warnings_strm : stderr,
- "Replicated or unused set_scsi_pt... functions\n");
+ fprintf(ferr, "Replicated or unused set_scsi_pt... functions\n");
return SCSI_PT_DO_BAD_PARAMS;
}
+ if (fd < 0) {
+ if (ptp->dev_fd < 0) {
+ if (verbose)
+ fprintf(ferr, "%s: No device file descriptor given\n",
+ __func__);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ } else {
+ if (ptp->dev_fd >= 0) {
+ if (fd != ptp->dev_fd) {
+ if (verbose)
+ fprintf(ferr, "%s: file descriptor given to create and "
+ "this differ\n", __func__);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ } else
+ ptp->dev_fd = fd;
+ }
if (NULL == ptp->uscsi.uscsi_cdb) {
if (verbose)
- fprintf(sg_warnings_strm ? sg_warnings_strm : stderr,
- "No SCSI command (cdb) given\n");
+ fprintf(ferr, "%s: No SCSI command (cdb) given\n", __func__);
return SCSI_PT_DO_BAD_PARAMS;
}
if (time_secs > 0)
ptp->uscsi.uscsi_timeout = time_secs;
- if (ioctl(fd, USCSICMD, &ptp->uscsi)) {
+ if (ioctl(ptp->dev_fd, USCSICMD, &ptp->uscsi)) {
ptp->os_err = errno;
if ((EIO == ptp->os_err) && ptp->uscsi.uscsi_status) {
ptp->os_err = 0;
return 0;
}
if (verbose)
- fprintf(sg_warnings_strm ? sg_warnings_strm : stderr,
- "ioctl(USCSICMD) failed with os_err (errno) = %d\n",
- ptp->os_err);
+ fprintf(ferr, "%s: ioctl(USCSICMD) failed with os_err (errno) "
+ "= %d\n", __func__, ptp->os_err);
return -ptp->os_err;
}
return 0;
@@ -345,8 +382,7 @@ pt_device_is_nvme(const struct sg_pt_base * vp)
{
const struct sg_pt_solaris_scsi * ptp = &vp->impl;
- if (vp) { ; } /* suppress warings */
- return false;
+ return ptp ? ptp->is_nvme : false;
}
char *
diff --git a/lib/sg_pt_win32.c b/lib/sg_pt_win32.c
index a0cfb4de..f52a4643 100644
--- a/lib/sg_pt_win32.c
+++ b/lib/sg_pt_win32.c
@@ -5,7 +5,7 @@
* license that can be found in the BSD_LICENSE file.
*/
-/* sg_pt_win32 version 1.18 20171030 */
+/* sg_pt_win32 version 1.18 20171108 */
#include <stdio.h>
#include <stdlib.h>
@@ -81,6 +81,8 @@ struct sg_pt_win32_scsi {
int in_err;
int os_err; /* pseudo unix error */
int transport_err; /* windows error number */
+ int dev_fd; /* -1 for no "file descriptor" given */
+ bool is_nvme;
union {
SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER swb_d;
/* Last entry in structure so data buffer can be extended */
@@ -273,7 +275,7 @@ scsi_pt_close_device(int device_fd)
}
struct sg_pt_base *
-construct_scsi_pt_obj()
+construct_scsi_pt_obj_with_fd(int dev_fd, int verbose)
{
struct sg_pt_win32_scsi * psp;
struct sg_pt_base * vp = NULL;
@@ -300,6 +302,8 @@ construct_scsi_pt_obj()
offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
psp->swb_i.spt.TimeOutValue = DEF_TIMEOUT;
}
+ psp->dev_fd = (dev_fd < 0) ? -1 : dev_fd;
+ psp->is_nvme = false;
vp = malloc(sizeof(struct sg_pt_win32_scsi *)); // yes a pointer
if (vp)
vp->implp = psp;
@@ -309,6 +313,12 @@ construct_scsi_pt_obj()
return vp;
}
+struct sg_pt_base *
+construct_scsi_pt_obj(void)
+{
+ return construct_scsi_pt_obj_with_fd(-1, 0);
+}
+
void
destruct_scsi_pt_obj(struct sg_pt_base * vp)
{
@@ -567,7 +577,7 @@ static int
do_scsi_pt_indirect(struct sg_pt_base * vp, int device_fd, int time_secs,
int verbose)
{
- int index = device_fd - WIN32_FDOFFSET;
+ int index;
struct sg_pt_win32_scsi * psp = vp->implp;
struct sg_pt_handle * shp;
BOOL status;
@@ -579,13 +589,30 @@ do_scsi_pt_indirect(struct sg_pt_base * vp, int device_fd, int time_secs,
pr2ws("Replicated or unused set_scsi_pt...\n");
return SCSI_PT_DO_BAD_PARAMS;
}
+ if (device_fd < 0) {
+ if (ptp->dev_fd < 0) {
+ if (verbose)
+ pr2ws("%s: No device file descriptor given\n", __func__);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ } else {
+ if (ptp->dev_fd >= 0) {
+ if (device_fd != ptp->dev_fd) {
+ if (verbose)
+ pr2ws("%s: file descriptor given to create and this "
+ "differ\n", __func__);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ } else
+ ptp->dev_fd = device_fd;
+ }
if (0 == psp->swb_i.spt.CdbLength) {
if (verbose)
pr2ws("No command (cdb) given\n");
return SCSI_PT_DO_BAD_PARAMS;
}
- index = device_fd - WIN32_FDOFFSET;
+ index = psp->dev_fd - WIN32_FDOFFSET;
if ((index < 0) || (index >= WIN32_FDOFFSET)) {
if (verbose)
pr2ws("Bad file descriptor\n");
@@ -770,8 +797,7 @@ pt_device_is_nvme(const struct sg_pt_base * vp)
{
const struct sg_pt_win32_scsi * psp = vp->implp;
- if (vp) { ; } /* suppress warings */
- return false;
+ return psp ? psp->is_nvme : false;;
}
char *
diff --git a/src/Makefile.am b/src/Makefile.am
index 1012a78d..4c3bb1ca 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -8,8 +8,8 @@ bin_PROGRAMS = \
sg_reset_wp sg_rmsn sg_rtpg sg_safte sg_sanitize sg_sat_identify \
sg_sat_phy_event sg_sat_read_gplog sg_sat_set_features sg_senddiag \
sg_ses sg_ses_microcode sg_start sg_stpg sg_sync sg_timestamp \
- sg_turs sg_unmap sg_verify sg_vpd sg_wr_mode sg_write_atomic \
- sg_write_buffer sg_write_long sg_write_same sg_write_verify sg_zone
+ sg_turs sg_unmap sg_verify sg_vpd sg_wr_mode sg_write_buffer \
+ sg_write_long sg_write_same sg_write_verify sg_write_x sg_zone
sg_scan_SOURCES =
@@ -168,8 +168,6 @@ sg_vpd_LDADD = ../lib/libsgutils2.la @os_libs@
sg_wr_mode_LDADD = ../lib/libsgutils2.la @os_libs@
-sg_write_atomic_LDADD = ../lib/libsgutils2.la @os_libs@
-
sg_write_buffer_LDADD = ../lib/libsgutils2.la @os_libs@
sg_write_long_LDADD = ../lib/libsgutils2.la @os_libs@
@@ -178,6 +176,8 @@ sg_write_same_LDADD = ../lib/libsgutils2.la @os_libs@
sg_write_verify_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_write_x_LDADD = ../lib/libsgutils2.la @os_libs@
+
sg_xcopy_LDADD = ../lib/libsgutils2.la @os_libs@
sg_zone_LDADD = ../lib/libsgutils2.la @os_libs@
diff --git a/src/Makefile.in b/src/Makefile.in
index fcfe1332..7b446b09 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -106,9 +106,9 @@ bin_PROGRAMS = sg_bg_ctl$(EXEEXT) sg_compare_and_write$(EXEEXT) \
sg_start$(EXEEXT) sg_stpg$(EXEEXT) sg_sync$(EXEEXT) \
sg_timestamp$(EXEEXT) sg_turs$(EXEEXT) sg_unmap$(EXEEXT) \
sg_verify$(EXEEXT) sg_vpd$(EXEEXT) sg_wr_mode$(EXEEXT) \
- sg_write_atomic$(EXEEXT) sg_write_buffer$(EXEEXT) \
- sg_write_long$(EXEEXT) sg_write_same$(EXEEXT) \
- sg_write_verify$(EXEEXT) sg_zone$(EXEEXT) $(am__EXEEXT_1) \
+ sg_write_buffer$(EXEEXT) sg_write_long$(EXEEXT) \
+ sg_write_same$(EXEEXT) sg_write_verify$(EXEEXT) \
+ sg_write_x$(EXEEXT) sg_zone$(EXEEXT) $(am__EXEEXT_1) \
$(am__EXEEXT_2) $(am__EXEEXT_3)
@OS_LINUX_TRUE@am__append_1 = \
@OS_LINUX_TRUE@ sg_copy_results sg_dd sg_emc_trespass sg_map sg_map26 sg_rbuf \
@@ -317,9 +317,6 @@ sg_vpd_DEPENDENCIES = ../lib/libsgutils2.la
sg_wr_mode_SOURCES = sg_wr_mode.c
sg_wr_mode_OBJECTS = sg_wr_mode.$(OBJEXT)
sg_wr_mode_DEPENDENCIES = ../lib/libsgutils2.la
-sg_write_atomic_SOURCES = sg_write_atomic.c
-sg_write_atomic_OBJECTS = sg_write_atomic.$(OBJEXT)
-sg_write_atomic_DEPENDENCIES = ../lib/libsgutils2.la
sg_write_buffer_SOURCES = sg_write_buffer.c
sg_write_buffer_OBJECTS = sg_write_buffer.$(OBJEXT)
sg_write_buffer_DEPENDENCIES = ../lib/libsgutils2.la
@@ -332,6 +329,9 @@ sg_write_same_DEPENDENCIES = ../lib/libsgutils2.la
sg_write_verify_SOURCES = sg_write_verify.c
sg_write_verify_OBJECTS = sg_write_verify.$(OBJEXT)
sg_write_verify_DEPENDENCIES = ../lib/libsgutils2.la
+sg_write_x_SOURCES = sg_write_x.c
+sg_write_x_OBJECTS = sg_write_x.$(OBJEXT)
+sg_write_x_DEPENDENCIES = ../lib/libsgutils2.la
sg_xcopy_SOURCES = sg_xcopy.c
sg_xcopy_OBJECTS = sg_xcopy.$(OBJEXT)
sg_xcopy_DEPENDENCIES = ../lib/libsgutils2.la
@@ -394,10 +394,9 @@ SOURCES = sg_bg_ctl.c sg_compare_and_write.c sg_copy_results.c sg_dd.c \
sg_sat_read_gplog.c sg_sat_set_features.c $(sg_scan_SOURCES) \
sg_senddiag.c sg_ses.c sg_ses_microcode.c sg_start.c sg_stpg.c \
sg_sync.c sg_test_rwbuf.c sg_timestamp.c sg_turs.c sg_unmap.c \
- sg_verify.c $(sg_vpd_SOURCES) sg_wr_mode.c sg_write_atomic.c \
- sg_write_buffer.c sg_write_long.c sg_write_same.c \
- sg_write_verify.c sg_xcopy.c sg_zone.c sginfo.c sgm_dd.c \
- sgp_dd.c
+ sg_verify.c $(sg_vpd_SOURCES) sg_wr_mode.c sg_write_buffer.c \
+ sg_write_long.c sg_write_same.c sg_write_verify.c sg_write_x.c \
+ sg_xcopy.c sg_zone.c sginfo.c sgm_dd.c sgp_dd.c
DIST_SOURCES = sg_bg_ctl.c sg_compare_and_write.c sg_copy_results.c \
sg_dd.c sg_decode_sense.c sg_emc_trespass.c sg_format.c \
sg_get_config.c sg_get_lba_status.c sg_ident.c \
@@ -412,10 +411,9 @@ DIST_SOURCES = sg_bg_ctl.c sg_compare_and_write.c sg_copy_results.c \
$(am__sg_scan_SOURCES_DIST) sg_senddiag.c sg_ses.c \
sg_ses_microcode.c sg_start.c sg_stpg.c sg_sync.c \
sg_test_rwbuf.c sg_timestamp.c sg_turs.c sg_unmap.c \
- sg_verify.c $(sg_vpd_SOURCES) sg_wr_mode.c sg_write_atomic.c \
- sg_write_buffer.c sg_write_long.c sg_write_same.c \
- sg_write_verify.c sg_xcopy.c sg_zone.c sginfo.c sgm_dd.c \
- sgp_dd.c
+ sg_verify.c $(sg_vpd_SOURCES) sg_wr_mode.c sg_write_buffer.c \
+ sg_write_long.c sg_write_same.c sg_write_verify.c sg_write_x.c \
+ sg_xcopy.c sg_zone.c sginfo.c sgm_dd.c sgp_dd.c
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
@@ -636,11 +634,11 @@ sg_verify_LDADD = ../lib/libsgutils2.la @os_libs@
sg_vpd_SOURCES = sg_vpd.c sg_vpd_vendor.c
sg_vpd_LDADD = ../lib/libsgutils2.la @os_libs@
sg_wr_mode_LDADD = ../lib/libsgutils2.la @os_libs@
-sg_write_atomic_LDADD = ../lib/libsgutils2.la @os_libs@
sg_write_buffer_LDADD = ../lib/libsgutils2.la @os_libs@
sg_write_long_LDADD = ../lib/libsgutils2.la @os_libs@
sg_write_same_LDADD = ../lib/libsgutils2.la @os_libs@
sg_write_verify_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_write_x_LDADD = ../lib/libsgutils2.la @os_libs@
sg_xcopy_LDADD = ../lib/libsgutils2.la @os_libs@
sg_zone_LDADD = ../lib/libsgutils2.la @os_libs@
all: all-am
@@ -950,10 +948,6 @@ sg_wr_mode$(EXEEXT): $(sg_wr_mode_OBJECTS) $(sg_wr_mode_DEPENDENCIES) $(EXTRA_sg
@rm -f sg_wr_mode$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(sg_wr_mode_OBJECTS) $(sg_wr_mode_LDADD) $(LIBS)
-sg_write_atomic$(EXEEXT): $(sg_write_atomic_OBJECTS) $(sg_write_atomic_DEPENDENCIES) $(EXTRA_sg_write_atomic_DEPENDENCIES)
- @rm -f sg_write_atomic$(EXEEXT)
- $(AM_V_CCLD)$(LINK) $(sg_write_atomic_OBJECTS) $(sg_write_atomic_LDADD) $(LIBS)
-
sg_write_buffer$(EXEEXT): $(sg_write_buffer_OBJECTS) $(sg_write_buffer_DEPENDENCIES) $(EXTRA_sg_write_buffer_DEPENDENCIES)
@rm -f sg_write_buffer$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(sg_write_buffer_OBJECTS) $(sg_write_buffer_LDADD) $(LIBS)
@@ -970,6 +964,10 @@ sg_write_verify$(EXEEXT): $(sg_write_verify_OBJECTS) $(sg_write_verify_DEPENDENC
@rm -f sg_write_verify$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(sg_write_verify_OBJECTS) $(sg_write_verify_LDADD) $(LIBS)
+sg_write_x$(EXEEXT): $(sg_write_x_OBJECTS) $(sg_write_x_DEPENDENCIES) $(EXTRA_sg_write_x_DEPENDENCIES)
+ @rm -f sg_write_x$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(sg_write_x_OBJECTS) $(sg_write_x_LDADD) $(LIBS)
+
sg_xcopy$(EXEEXT): $(sg_xcopy_OBJECTS) $(sg_xcopy_DEPENDENCIES) $(EXTRA_sg_xcopy_DEPENDENCIES)
@rm -f sg_xcopy$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(sg_xcopy_OBJECTS) $(sg_xcopy_LDADD) $(LIBS)
@@ -1055,11 +1053,11 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_vpd.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_vpd_vendor.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_wr_mode.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_write_atomic.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_write_buffer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_write_long.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_write_same.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_write_verify.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_write_x.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_xcopy.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_zone.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sginfo.Po@am__quote@
diff --git a/src/sg_format.c b/src/sg_format.c
index 53aa2739..f403e0cd 100644
--- a/src/sg_format.c
+++ b/src/sg_format.c
@@ -37,7 +37,7 @@
#include "sg_pr2serr.h"
#include "sg_pt.h"
-static const char * version_str = "1.40 20171030";
+static const char * version_str = "1.41 20171107";
#define RW_ERROR_RECOVERY_PAGE 1 /* can give alternate with --mode=MP */
@@ -734,7 +734,7 @@ print_read_cap(int fd, const struct opts_t * op)
block_size = sg_get_unaligned_be32(resp_buff + 4);
if (0xffffffff == last_blk_addr) {
if (op->verbose)
- printf("Read Capacity (10) reponse "
+ printf("Read Capacity (10) response "
"indicates that Read Capacity "
"(16) is required\n");
return -2;
diff --git a/src/sg_inq.c b/src/sg_inq.c
index 25dce472..8d25605e 100644
--- a/src/sg_inq.c
+++ b/src/sg_inq.c
@@ -42,8 +42,11 @@
#include "sg_pt.h"
#include "sg_unaligned.h"
#include "sg_pr2serr.h"
+#ifdef HAVE_NVME
+#include "sg_pt_nvme.h"
+#endif
-static const char * version_str = "1.71 20171104"; /* SPC-5 rev 17 */
+static const char * version_str = "1.73 20171114"; /* SPC-5 rev 17 */
/* INQUIRY notes:
* It is recommended that the initial allocation length given to a
@@ -429,10 +432,10 @@ cl_new_process(struct opts_t * op, int argc, char * argv[])
#endif /* SG_SCSI_STRINGS */
#else /* SG_LIB_LINUX */
#ifdef SG_SCSI_STRINGS
- c = getopt_long(argc, argv, "B:cdeEhHiI:l:m:NOp:rsuvVx", long_options,
+ c = getopt_long(argc, argv, "B:cdeEfhHiI:l:m:NOp:rsuvVx", long_options,
&option_index);
#else
- c = getopt_long(argc, argv, "B:cdeEhHiI:l:m:p:rsuvVx", long_options,
+ c = getopt_long(argc, argv, "B:cdeEfhHiI:l:m:p:rsuvVx", long_options,
&option_index);
#endif /* SG_SCSI_STRINGS */
#endif /* SG_LIB_LINUX */
@@ -467,7 +470,7 @@ cl_new_process(struct opts_t * op, int argc, char * argv[])
case 'e':
op->do_vpd = true;
break;
- case 'E':
+ case 'E': /* --extended */
case 'x':
op->do_decode = true;
op->do_vpd = true;
@@ -3220,8 +3223,8 @@ std_inq_process(int sg_fd, const struct opts_t * op, int inhex_len)
res = try_ata_identify(sg_fd, op->do_hex, op->do_raw,
op->do_verbose);
if (0 != res) {
- pr2serr("Both SCSI INQUIRY and fetching ATA information "
- "failed on %s\n", op->device_name);
+ pr2serr("SCSI INQUIRY, NVMe Identify and fetching ATA "
+ "information failed on %s\n", op->device_name);
return SG_LIB_CAT_OTHER;
}
#else
@@ -3765,6 +3768,236 @@ out:
return res;
}
+#ifdef HAVE_NVME
+
+static void
+do_nvme_identify_hex_raw(const unsigned char * b, int b_len,
+ const struct opts_t * op)
+{
+ if (op->do_raw)
+ dStrRaw((const char *)b, b_len);
+ else if (op->do_hex) {
+ if (op->do_hex < 3) {
+ printf("data_in buffer:\n");
+ dStrHex((const char *)b, b_len, 0);
+ } else
+ dStrHex((const char *)b, b_len, -1);
+ }
+}
+
+const char * rperf[] = {"Best", "Better", "Good", "Degraded"};
+
+static int
+do_nvme_id_ns(struct sg_pt_base * ptvp, uint32_t nsid,
+ struct sg_nvme_passthru_cmd * id_cmdp, uint8_t * id_din,
+ int id_din_len, const struct opts_t * op)
+{
+ bool got_eui_128 = false;
+ int ret = 0;
+ int vb = op->do_verbose;
+ uint32_t u, k, off, num_lbaf, flbas, flba_info, md_size, lb_size;
+ uint64_t ns_sz, eui_64;
+ struct sg_nvme_passthru_cmd cmd_back;
+
+ clear_scsi_pt_obj(ptvp);
+ id_cmdp->nsid = nsid;
+ id_cmdp->cdw10 = 0x0; /* CNS=0x0 Identify NS */
+ set_scsi_pt_data_in(ptvp, id_din, id_din_len);
+ set_scsi_pt_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) {
+ pr2serr("do_scsi_pt() result is %d\n", ret);
+ pr2serr("do_scsi_pt() result via sense buffer:\n");
+ dStrHex((const char *)&cmd_back, sizeof(cmd_back), 0);
+ }
+ if (ret)
+ return ret;
+ num_lbaf = id_din[25] + 1; /* spec says this is "0's based value" */
+ flbas = id_din[26] & 0xf; /* index of active LBA format (for this ns) */
+ if (op->do_hex || op->do_raw) {
+ do_nvme_identify_hex_raw(id_din, sizeof(id_din), op);
+ return ret;
+ }
+ ns_sz = sg_get_unaligned_le64(id_din + 0);
+ eui_64 = sg_get_unaligned_be64(id_din + 120); /* N.B. big endian */
+ if (! sg_all_zeros(id_din + 104, 16))
+ got_eui_128 = true;
+ printf(" Namespace size/capacity: %" PRIu64 "/%" PRIu64
+ " blocks\n", ns_sz, sg_get_unaligned_le64(id_din + 8));
+ printf(" Namespace utilization: %" PRIu64 " blocks\n",
+ sg_get_unaligned_le64(id_din + 16));
+ if (got_eui_128) { /* N.B. big endian */
+ printf(" NGUID: 0x%02x", id_din[104]);
+ for (k = 1; k < 16; ++k)
+ printf("%02x", id_din[104 + k]);
+ printf("\n");
+ } else if (op->do_force)
+ printf(" NGUID: 0x0\n");
+ if (eui_64)
+ printf(" EUI-64: 0x%" PRIx64 "\n", eui_64); /* N.B. big endian */
+ printf(" Number of LBA formats: %u\n", num_lbaf);
+ printf(" Index LBA size: %u\n", flbas);
+ for (k = 0, off = 128; k < num_lbaf; ++k, off += 4) {
+ printf(" LBA format %u support:", k);
+ if (k == flbas)
+ printf(" <-- active\n");
+ else
+ printf("\n");
+ flba_info = sg_get_unaligned_le32(id_din + off);
+ md_size = flba_info & 0xffff;
+ lb_size = flba_info >> 16 & 0xff;
+ if (lb_size > 31) {
+ pr2serr("%s: logical block size exponent of %u implies a LB "
+ "size larger than 4 billion bytes, ignore\n", __func__,
+ lb_size);
+ continue;
+ }
+ lb_size = 1U << lb_size;
+ ns_sz *= lb_size;
+ ns_sz /= 500*1000*1000;
+ if (ns_sz & 0x1)
+ ns_sz = (ns_sz / 2) + 1;
+ else
+ ns_sz = ns_sz / 2;
+ u = (flba_info >> 24) & 0x3;
+ printf(" Logical block size: %u bytes\n", lb_size);
+ printf(" Approximate namespace size: %" PRIu64 " GB\n", ns_sz);
+ printf(" Metadata size: %u bytes\n", md_size);
+ printf(" Relative performance: %s [0x%x]\n", rperf[u], u);
+ }
+ return ret;
+}
+
+/* Send a NVMe Identify(CNS=1) and decode Controller info */
+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;
+ uint32_t k, ver, nsid, max_nsid;
+ uint64_t sz1, sz2;
+ struct sg_pt_base * ptvp;
+ struct sg_nvme_passthru_cmd identify_cmd;
+ struct sg_nvme_passthru_cmd cmd_back;
+ struct sg_nvme_passthru_cmd * id_cmdp = &identify_cmd;
+ uint8_t id_din[4096];
+
+ if (op->do_raw) {
+ if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+ perror("sg_set_binary_mode");
+ return SG_LIB_FILE_ERROR;
+ }
+ }
+ ptvp = construct_scsi_pt_obj_with_fd(pt_fd, vb);
+ if (NULL == ptvp) {
+ pr2serr("%s: memory problem\n", __func__);
+ return SG_LIB_CAT_OTHER;
+ }
+ memset(id_cmdp, 0, sizeof(*id_cmdp));
+ id_cmdp->opcode = 0x6;
+ nsid = get_pt_nvme_nsid(ptvp);
+ id_cmdp->cdw10 = 0x1; /* CNS=0x1 Identify controller */
+ set_scsi_pt_data_in(ptvp, id_din, sizeof(id_din));
+ 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) {
+ pr2serr("do_scsi_pt result is %d\n", ret);
+ pr2serr("do_scsi_pt result via sense buffer:\n");
+ dStrHex((const char *)&cmd_back, sizeof(cmd_back), 0);
+ }
+ if (ret)
+ goto err_out;
+ max_nsid = sg_get_unaligned_le16(id_din + 516);
+ if (op->do_raw || op->do_hex) {
+ do_nvme_identify_hex_raw(id_din, sizeof(id_din), op);
+ goto skip1;
+ }
+ printf("Identify controller for %s:\n", op->device_name);
+ printf(" Model number: %.40s\n", (const char *)(id_din + 24));
+ printf(" Serial number: %.20s\n", (const char *)(id_din + 4));
+ printf(" Firmware revision: %.8s\n", (const char *)(id_din + 64));
+ ver = sg_get_unaligned_le32(id_din + 80);
+ ver_maj = (ver >> 16);
+ ver_min = (ver >> 8) & 0xff;
+ ver_ter = (ver & 0xff);
+ printf(" Version: %u.%u", ver_maj, ver_min);
+ if ((ver_maj > 1) || ((1 == ver_maj) && (ver_min > 2)) ||
+ ((1 == ver_maj) && (2 == ver_min) && (ver_ter > 0)))
+ printf(".%u\n", ver_ter);
+ else
+ printf("\n");
+ printf(" PCI vendor ID VID/SSVID: 0x%x/0x%x\n",
+ sg_get_unaligned_le16(id_din + 0),
+ sg_get_unaligned_le16(id_din + 2));
+ printf(" IEEE OUI Identifier: 0x%x\n",
+ sg_get_unaligned_le24(id_din + 73));
+ got_fguid = ! sg_all_zeros(id_din + 112, 16);
+ if (got_fguid) {
+ printf(" FGUID: 0x%02x", id_din[112]);
+ for (k = 1; k < 16; ++k)
+ printf("%02x", id_din[112 + k]);
+ printf("\n");
+ } else if (op->do_force)
+ printf(" FGUID: 0x0\n");
+ printf(" Controller ID: 0x%x\n", sg_get_unaligned_le16(id_din + 78));
+ if (op->do_force) {
+ printf(" Management endpoint capabilities, over a PCIe port: %d\n",
+ !! (0x2 & id_din[255]));
+ printf(" Management endpoint capabilities, over a SMBus/I2C port: "
+ "%d\n", !! (0x1 & id_din[255]));
+ }
+ printf(" Number of namespaces: %u\n", max_nsid);
+ sz1 = sg_get_unaligned_le64(id_din + 280); /* lower 64 bits */
+ sz2 = sg_get_unaligned_le64(id_din + 288); /* upper 64 bits */
+ if (sz2)
+ printf(" Total NVM capacity: huge ...\n");
+ else if (sz1)
+ printf(" Total NVM capacity: %" PRIu64 " bytes\n", sz1);
+ else if (op->do_force)
+ printf(" Total NVM capacity: 0 bytes\n");
+skip1:
+ if (nsid > 0) {
+ if ((! op->do_raw) || (op->do_hex < 3)) {
+ printf(" Namespace %u (derived from device name):\n", nsid);
+ if (nsid > max_nsid)
+ pr2serr("NSID from device (%u) should not exceed number of "
+ "namespaces (%u)\n", nsid, max_nsid);
+ }
+ ret = do_nvme_id_ns(ptvp, nsid, id_cmdp, id_din, sizeof(id_din), op);
+ if (ret)
+ goto err_out;
+
+ } else { /* nsid=0 so char device; loop over all namespaces */
+ for (k = 1; k <= max_nsid; ++k) {
+ if ((! op->do_raw) || (op->do_hex < 3))
+ printf(" Namespace %u (of %u):\n", k, max_nsid);
+ ret = do_nvme_id_ns(ptvp, k, id_cmdp, id_din, sizeof(id_din), op);
+ if (ret)
+ goto err_out;
+ }
+ }
+err_out:
+ destruct_scsi_pt_obj(ptvp);
+ return ret;
+}
+
+#else
+
+static int
+do_nvme_identify(int pt_fd, const struct opts_t * op)
+{
+ pr2serr("%s: not implemented, no <linux/nvme_ioctl.h>\n", __func__);
+ if (op->do_verbose)
+ pr2serr("Need to build on system with NVMe development headers\n");
+ return 0;
+}
+#endif
+
int
main(int argc, char * argv[])
@@ -4007,6 +4240,11 @@ main(int argc, char * argv[])
}
#endif
memset(rsp_buff, 0, sizeof(rsp_buff));
+ n = check_pt_file_handle(sg_fd, op->device_name, op->do_verbose);
+ if ((3 == n) || (4 == n)) { /* NVMe char or NVMe block */
+ ret = do_nvme_identify(sg_fd, op);
+ goto fini;
+ }
#if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS)
if (op->do_ata) {
@@ -4018,7 +4256,7 @@ main(int argc, char * argv[])
ret = SG_LIB_CAT_OTHER;
} else
ret = 0;
- goto err_out;
+ goto fini;
}
#endif
@@ -4045,6 +4283,7 @@ main(int argc, char * argv[])
}
}
+fini:
err_out:
res = sg_cmds_close_device(sg_fd);
if (res < 0) {
diff --git a/src/sg_logs.c b/src/sg_logs.c
index eadfe2e3..e4d4bf97 100644
--- a/src/sg_logs.c
+++ b/src/sg_logs.c
@@ -32,7 +32,7 @@
#include "sg_unaligned.h"
#include "sg_pr2serr.h"
-static const char * version_str = "1.55 20171103"; /* spc5r17 + sbc4r11 */
+static const char * version_str = "1.57 20171112"; /* spc5r17 + sbc4r11 */
#define MX_ALLOC_LEN (0xfffc)
#define SHORT_RESP_LEN 128
@@ -535,9 +535,9 @@ usage(int hval)
pr2serr(
" where sg_logs' lesser used options are:\n"
" --control=PC|-c PC page control(PC) (default: 1)\n"
- " 0: current threshhold, 1: current "
+ " 0: current threshold, 1: current "
"cumulative\n"
- " 2: default threshhold, 3: default "
+ " 2: default threshold, 3: default "
"cumulative\n"
" --list|-l list supported log page names (equivalent to "
"'-p sp')\n"
@@ -593,8 +593,8 @@ usage_old()
" -A fetch and decode all log pages and subpages\n"
" -b shorten the output of some log pages\n"
" -c=PC page control(PC) (default: 1)\n"
- " 0: current threshhold, 1: current cumulative\n"
- " 2: default threshhold, 3: default cumulative\n"
+ " 0: current threshold, 1: current cumulative\n"
+ " 2: default threshold, 3: default cumulative\n"
" -e enumerate known log pages\n"
" -f=FL filter match parameter code or pdt\n"
" -h output in hex (default: decode if known)\n"
@@ -1319,20 +1319,6 @@ dStrRaw(const char* str, int len)
printf("%c", str[k]);
}
-/* Assumes an integral numbers of bytes pointed to by 'xp' of length
- * 'num_bytes' given. [So the number of bits modulo 8 must be zero.]
- * Returns true if all bytes are 0xff (which is the same as all bits
- * being set), else returns false. */
-static bool
-all_bits_set(const uint8_t * xp, int num_bytes)
-{
- for ( ; num_bytes > 0; ++xp, --num_bytes) {
- if (0xff != *xp)
- return false;
- }
- return true; /* also in degenerate case when 'num_bytes' <= 0 */
-}
-
/* Returns 'xp' with "unknown" if all bits set; otherwise decoded (big endian)
* number in 'xp'. Number rendered in decimal if in_hex=false otherwise in
* hex with leading '0x' prepended. */
@@ -1340,7 +1326,7 @@ static char *
num_or_unknown(const uint8_t * xp, int num_bytes /* max is 8 */, bool in_hex,
char * b, int blen)
{
- if (all_bits_set(xp, num_bytes))
+ if (sg_all_ffs(xp, num_bytes))
snprintf(b, blen, "unknown");
else {
uint64_t num = sg_get_unaligned_be(num_bytes, xp);
@@ -2796,7 +2782,7 @@ show_self_test_page(const uint8_t * resp, int len, const struct opts_t * op)
printf(" self-test result: %s [%d]\n", self_test_result[res], res);
if (bp[5])
printf(" self-test number = %d\n", (int)bp[5]);
- if (! all_bits_set(bp + 8, 8)) {
+ if (! sg_all_ffs(bp + 8, 8)) {
ull = sg_get_unaligned_be64(bp + 8);
if ((res > 0) && ( res < 0xf))
printf(" address of first error = 0x%" PRIx64 "\n", ull);
@@ -2950,7 +2936,7 @@ show_start_stop_page(const uint8_t * resp, int len, const struct opts_t * op)
break;
case 3:
if (extra > 7) {
- if (all_bits_set(bp + 4, 4))
+ if (sg_all_ffs(bp + 4, 4))
printf(" Specified cycle count over device lifetime "
"= -1");
else
@@ -2960,7 +2946,7 @@ show_start_stop_page(const uint8_t * resp, int len, const struct opts_t * op)
break;
case 4:
if (extra > 7) {
- if (all_bits_set(bp + 4, 4))
+ if (sg_all_ffs(bp + 4, 4))
printf(" Accumulated start-stop cycles = -1");
else
printf(" Accumulated start-stop cycles = %u",
@@ -2969,7 +2955,7 @@ show_start_stop_page(const uint8_t * resp, int len, const struct opts_t * op)
break;
case 5:
if (extra > 7) {
- if (all_bits_set(bp + 4, 4))
+ if (sg_all_ffs(bp + 4, 4))
printf(" Specified load-unload count over device "
"lifetime = -1");
else
@@ -2979,7 +2965,7 @@ show_start_stop_page(const uint8_t * resp, int len, const struct opts_t * op)
break;
case 6:
if (extra > 7) {
- if (all_bits_set(bp + 4, 4))
+ if (sg_all_ffs(bp + 4, 4))
printf(" Accumulated load-unload cycles = -1");
else
printf(" Accumulated load-unload cycles = %u",
@@ -4051,7 +4037,7 @@ show_format_status_page(const uint8_t * resp, int len,
if (pl < 5)
printf(" Format data out: <empty>\n");
else {
- if (all_bits_set(bp + 4, pl - 4))
+ if (sg_all_ffs(bp + 4, pl - 4))
printf(" Format data out: <not available>\n");
else {
printf(" Format data out:\n");
@@ -4081,7 +4067,7 @@ show_format_status_page(const uint8_t * resp, int len,
if (is_count) {
k = pl - 4;
xp = bp + 4;
- if (all_bits_set(xp, k))
+ if (sg_all_ffs(xp, k))
printf(" <not available>\n");
else {
if (k > (int)sizeof(ull)) {
@@ -5393,7 +5379,7 @@ show_sequential_access_page(const uint8_t * resp, int len,
}
}
ull = sg_get_unaligned_be(pl - 4, bp + 4);
- all_set = all_bits_set(bp + 4, pl - 4);
+ all_set = sg_all_ffs(bp + 4, pl - 4);
gbytes = ull / 1000000000;
switch (pc) {
case 0:
@@ -6000,7 +5986,7 @@ show_mchanger_diag_data_page(const uint8_t * resp, int len,
v = sg_get_unaligned_be16(bp + 48);
printf(" Medium transport address: 0x%x\n", v);
v = sg_get_unaligned_be16(bp + 50);
- printf(" Intial address: 0x%x\n", v);
+ printf(" Initial address: 0x%x\n", v);
v = sg_get_unaligned_be16(bp + 52);
printf(" Last successful address: 0x%x\n", v);
v = sg_get_unaligned_be16(bp + 54);
@@ -6040,7 +6026,7 @@ volume_stats_partition(const uint8_t * xp, int len, bool in_hex)
pn = sg_get_unaligned_be16(xp + 2);
ffs_last_fe = false;
all_ffs = false;
- if (all_bits_set(xp + 4, dl - 3)) {
+ if (sg_all_ffs(xp + 4, dl - 3)) {
switch (xp[4 + dl - 3]) {
case 0xff:
all_ffs = true;
diff --git a/src/sg_read_long.c b/src/sg_read_long.c
index 620c1bae..111c5b3c 100644
--- a/src/sg_read_long.c
+++ b/src/sg_read_long.c
@@ -8,7 +8,7 @@
This program issues the SCSI command READ LONG to a given SCSI device.
It sends the command with the logical block address passed as the lba
argument, and the transfer length set to the xfer_len argument. the
- buffer to be writen to the device filled with 0xff, this buffer includes
+ buffer to be written to the device filled with 0xff, this buffer includes
the sector data and the ECC bytes.
*/
diff --git a/src/sg_sat_identify.c b/src/sg_sat_identify.c
index 5efa5008..ca6431ab 100644
--- a/src/sg_sat_identify.c
+++ b/src/sg_sat_identify.c
@@ -38,7 +38,7 @@
#define SAT_ATA_PASS_THROUGH32_LEN 32
#define SAT_ATA_PASS_THROUGH16 0x85
#define SAT_ATA_PASS_THROUGH16_LEN 16
-#define SAT_ATA_PASS_THROUGH12 0xa1 /* clashes with MMC BLANK comand */
+#define SAT_ATA_PASS_THROUGH12 0xa1 /* clashes with MMC BLANK command */
#define SAT_ATA_PASS_THROUGH12_LEN 12
#define SAT_ATA_RETURN_DESC 9 /* ATA Return (sense) Descriptor */
#define ASCQ_ATA_PT_INFO_AVAILABLE 0x1d
diff --git a/src/sg_sat_phy_event.c b/src/sg_sat_phy_event.c
index fe7d1aeb..720fc9db 100644
--- a/src/sg_sat_phy_event.c
+++ b/src/sg_sat_phy_event.c
@@ -24,7 +24,7 @@
#include "sg_cmds_extra.h"
#include "sg_pr2serr.h"
-static const char * version_str = "1.09 20171010";
+static const char * version_str = "1.10 20171107";
/* This program uses a ATA PASS-THROUGH SCSI command. This usage is
* defined in the SCSI to ATA Translation (SAT) drafts and standards.
@@ -46,7 +46,7 @@ static const char * version_str = "1.09 20171010";
#define SAT_ATA_PASS_THROUGH16 0x85
#define SAT_ATA_PASS_THROUGH16_LEN 16
-#define SAT_ATA_PASS_THROUGH12 0xa1 /* clashes with MMC BLANK comand */
+#define SAT_ATA_PASS_THROUGH12 0xa1 /* clashes with MMC BLANK command */
#define SAT_ATA_PASS_THROUGH12_LEN 12
#define SAT_ATA_RETURN_DESC 9 /* ATA Return (sense) Descriptor */
#define ASCQ_ATA_PT_INFO_AVAILABLE 0x1d
diff --git a/src/sg_sat_read_gplog.c b/src/sg_sat_read_gplog.c
index 5e1bb5ad..7feadeb5 100644
--- a/src/sg_sat_read_gplog.c
+++ b/src/sg_sat_read_gplog.c
@@ -41,7 +41,7 @@
#define SAT_ATA_PASS_THROUGH16 0x85
#define SAT_ATA_PASS_THROUGH16_LEN 16
-#define SAT_ATA_PASS_THROUGH12 0xa1 /* clashes with MMC BLANK comand */
+#define SAT_ATA_PASS_THROUGH12 0xa1 /* clashes with MMC BLANK command */
#define SAT_ATA_PASS_THROUGH12_LEN 12
#define SAT_ATA_RETURN_DESC 9 /* ATA Return (sense) Descriptor */
#define ASCQ_ATA_PT_INFO_AVAILABLE 0x1d
@@ -51,7 +51,7 @@
#define DEF_TIMEOUT 20
-static const char * version_str = "1.15 20171010";
+static const char * version_str = "1.16 20171107";
struct opts_t {
bool ck_cond;
diff --git a/src/sg_sat_set_features.c b/src/sg_sat_set_features.c
index 6215b237..2d84ff78 100644
--- a/src/sg_sat_set_features.c
+++ b/src/sg_sat_set_features.c
@@ -40,7 +40,7 @@
#define SAT_ATA_PASS_THROUGH16 0x85
#define SAT_ATA_PASS_THROUGH16_LEN 16
-#define SAT_ATA_PASS_THROUGH12 0xa1 /* clashes with MMC BLANK comand */
+#define SAT_ATA_PASS_THROUGH12 0xa1 /* clashes with MMC BLANK command */
#define SAT_ATA_PASS_THROUGH12_LEN 12
#define SAT_ATA_RETURN_DESC 9 /* ATA Return (sense) Descriptor */
#define ASCQ_ATA_PT_INFO_AVAILABLE 0x1d
@@ -49,7 +49,7 @@
#define DEF_TIMEOUT 20
-static const char * version_str = "1.14 20171010";
+static const char * version_str = "1.15 20171107";
static struct option long_options[] = {
{"count", required_argument, 0, 'c'},
diff --git a/src/sg_scan_win32.c b/src/sg_scan_win32.c
index 22d14919..7bbad556 100644
--- a/src/sg_scan_win32.c
+++ b/src/sg_scan_win32.c
@@ -40,7 +40,7 @@
#include "sg_pt_win32.h"
-static const char * version_str = "1.17 (win32) 20171007";
+static const char * version_str = "1.18 (win32) 20171107";
#define MAX_SCSI_ELEMS 2048
#define MAX_ADAPTER_NUM 128
@@ -341,7 +341,7 @@ query_dev_uid(HANDLE hdevice, union STORAGE_DEVICE_UID_DATA * data)
}
/* Updates storage_arr based on sep. Returns 1 if update occurred, 0 if
- * no update occured. */
+ * no update occurred. */
static int
check_devices(const struct storage_elem * sep)
{
diff --git a/src/sg_ses.c b/src/sg_ses.c
index c4123d02..2fd810d8 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.23 20171020"; /* ses4r01 */
+static const char * version_str = "2.24 20171112"; /* ses4r01 */
#define MX_ALLOC_LEN ((64 * 1024) - 4) /* max allowable for big enclosures */
#define MX_ELEM_HDR 1024
@@ -2894,13 +2894,7 @@ truncated:
static bool
saddr_non_zero(const uint8_t * bp)
{
- int k;
-
- for (k = 0; k < 8; ++k) {
- if (bp[k])
- return true;
- }
- return false;
+ return ! sg_all_zeros(bp, 8);
}
static const char * sas_device_type[] = {
diff --git a/src/sg_test_rwbuf.c b/src/sg_test_rwbuf.c
index 4e86e7c3..0ab5c613 100644
--- a/src/sg_test_rwbuf.c
+++ b/src/sg_test_rwbuf.c
@@ -3,7 +3,7 @@
* heavily based on Douglas Gilbert's sg_rbuf program.
* (c) 1999-2017 Douglas Gilbert
*
- * Program to test the SCSI host adapter by issueing
+ * Program to test the SCSI host adapter by issuing
* write and read operations on a device's buffer
* and calculating checksums.
* NOTE: If you can not reserve the buffer of the device
@@ -43,7 +43,7 @@
#include "sg_pr2serr.h"
-static const char * version_str = "1.12 20171020";
+static const char * version_str = "1.13 20171107";
#define BPI (signed)(sizeof(int))
diff --git a/src/sg_write_long.c b/src/sg_write_long.c
index 63ca64bf..252425e3 100644
--- a/src/sg_write_long.c
+++ b/src/sg_write_long.c
@@ -8,7 +8,7 @@
* This program issues the SCSI command WRITE LONG to a given SCSI device.
* It sends the command with the logical block address passed as the lba
* argument, and the transfer length set to the xfer_len argument. the
- * buffer to be writen to the device filled with 0xff, this buffer includes
+ * buffer to be written to the device filled with 0xff, this buffer includes
* the sector data and the ECC bytes.
*
* This code was contributed by Saeed Bishara
@@ -34,7 +34,7 @@
#include "sg_cmds_extra.h"
#include "sg_pr2serr.h"
-static const char * version_str = "1.14 20171008";
+static const char * version_str = "1.15 20171107";
#define MAX_XFER_LEN 10000
diff --git a/src/sg_write_atomic.c b/src/sg_write_x.c
index 4d669e91..8fc450a1 100644
--- a/src/sg_write_atomic.c
+++ b/src/sg_write_x.c
@@ -30,10 +30,10 @@
#include "sg_unaligned.h"
#include "sg_pr2serr.h"
-static const char * version_str = "1.02 20171024";
+static const char * version_str = "1.03 20171115";
-#define ME "sg_write_atomic: "
+#define ME "sg_write_x: "
#define WRITE_ATOMIC16_OP 0x9c
#define WRITE_16_OP 0x8a
@@ -121,7 +121,7 @@ struct opts_t {
static void
usage()
{
- pr2serr("Usage: sg_write_atomic [--16] [--32] [--app-tag=AT] "
+ pr2serr("Usage: sg_write_x [--16] [--32] [--app-tag=AT] "
"[--boundary=AB]\n"
" [--bs=LBS] [--dld=DLD] [--dpo] "
"[--fua] "
diff --git a/src/sginfo.c b/src/sginfo.c
index 8e6f946a..7d1faa59 100644
--- a/src/sginfo.c
+++ b/src/sginfo.c
@@ -5,7 +5,7 @@
* ./sginfo [options] /dev/sg2 [replace parameters]
*
* Options are:
- * -6 do 6 byte mode sense + select (deafult: 10 byte)
+ * -6 do 6 byte mode sense + select (default: 10 byte)
* -a display all mode pages reported by the device: equivalent to '-t 63'.
* -A display all mode pages and subpages reported by the device: equivalent
* to '-t 63,255'.