diff -Nru sdparm-1.10/aclocal.m4 sdparm-1.12/aclocal.m4 --- sdparm-1.10/aclocal.m4 2015-12-02 00:45:13.000000000 +0000 +++ sdparm-1.12/aclocal.m4 2020-12-25 01:49:20.000000000 +0000 @@ -1,6 +1,6 @@ -# generated automatically by aclocal 1.15 -*- Autoconf -*- +# generated automatically by aclocal 1.16.1 -*- Autoconf -*- -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -14,13 +14,13 @@ m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl -m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, -[m4_warning([this file was generated for autoconf 2.69. +m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.70],, +[m4_warning([this file was generated for autoconf 2.70. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) -# Copyright (C) 2002-2014 Free Software Foundation, Inc. +# Copyright (C) 2002-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -32,10 +32,10 @@ # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], -[am__api_version='1.15' +[am__api_version='1.16' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. -m4_if([$1], [1.15], [], +m4_if([$1], [1.16.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) @@ -51,14 +51,74 @@ # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], -[AM_AUTOMAKE_VERSION([1.15])dnl +[AM_AUTOMAKE_VERSION([1.16.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) +# Copyright (C) 2011-2018 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_AR([ACT-IF-FAIL]) +# ------------------------- +# Try to determine the archiver interface, and trigger the ar-lib wrapper +# if it is needed. If the detection of archiver interface fails, run +# ACT-IF-FAIL (default is to abort configure with a proper error message). +AC_DEFUN([AM_PROG_AR], +[AC_BEFORE([$0], [LT_INIT])dnl +AC_BEFORE([$0], [AC_PROG_LIBTOOL])dnl +AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([ar-lib])dnl +AC_CHECK_TOOLS([AR], [ar lib "link -lib"], [false]) +: ${AR=ar} + +AC_CACHE_CHECK([the archiver ($AR) interface], [am_cv_ar_interface], + [AC_LANG_PUSH([C]) + am_cv_ar_interface=ar + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[int some_variable = 0;]])], + [am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&AS_MESSAGE_LOG_FD' + AC_TRY_EVAL([am_ar_try]) + if test "$ac_status" -eq 0; then + am_cv_ar_interface=ar + else + am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&AS_MESSAGE_LOG_FD' + AC_TRY_EVAL([am_ar_try]) + if test "$ac_status" -eq 0; then + am_cv_ar_interface=lib + else + am_cv_ar_interface=unknown + fi + fi + rm -f conftest.lib libconftest.a + ]) + AC_LANG_POP([C])]) + +case $am_cv_ar_interface in +ar) + ;; +lib) + # Microsoft lib, so override with the ar-lib wrapper script. + # FIXME: It is wrong to rewrite AR. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__AR in this case, + # and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something + # similar. + AR="$am_aux_dir/ar-lib $AR" + ;; +unknown) + m4_default([$1], + [AC_MSG_ERROR([could not determine $AR interface])]) + ;; +esac +AC_SUBST([AR])dnl +]) + # AM_AUX_DIR_EXPAND -*- Autoconf -*- -# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -110,7 +170,7 @@ # AM_CONDITIONAL -*- Autoconf -*- -# Copyright (C) 1997-2014 Free Software Foundation, Inc. +# Copyright (C) 1997-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -141,7 +201,7 @@ Usually this means the macro was only invoked conditionally.]]) fi])]) -# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -332,13 +392,12 @@ # Generate code to set up dependency tracking. -*- Autoconf -*- -# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. - # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], @@ -346,49 +405,41 @@ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. - case $CONFIG_FILES in - *\'*) eval set x "$CONFIG_FILES" ;; - *) set x $CONFIG_FILES ;; - esac + # TODO: see whether this extra hack can be removed once we start + # requiring Autoconf 2.70 or later. + AS_CASE([$CONFIG_FILES], + [*\'*], [eval set x "$CONFIG_FILES"], + [*], [set x $CONFIG_FILES]) shift - for mf + # Used to flag and report bootstrapping failures. + am_rc=0 + for am_mf do # Strip MF so we end up with the name of the file. - mf=`echo "$mf" | sed -e 's/:.*$//'` - # Check whether this is an Automake generated Makefile or not. - # We used to match only the files named 'Makefile.in', but - # some people rename them; so instead we look at the file content. - # Grep'ing the first line is not enough: some people post-process - # each Makefile.in and add a new line on top of each file to say so. - # Grep'ing the whole file is not good either: AIX grep has a line + am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile which includes + # dependency-tracking related rules and includes. + # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. - if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then - dirpart=`AS_DIRNAME("$mf")` - else - continue - fi - # Extract the definition of DEPDIR, am__include, and am__quote - # from the Makefile without running 'make'. - DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` - test -z "$DEPDIR" && continue - am__include=`sed -n 's/^am__include = //p' < "$mf"` - test -z "$am__include" && continue - am__quote=`sed -n 's/^am__quote = //p' < "$mf"` - # Find all dependency output files, they are included files with - # $(DEPDIR) in their names. We invoke sed twice because it is the - # simplest approach to changing $(DEPDIR) to its actual value in the - # expansion. - for file in `sed -n " - s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ - sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do - # Make sure the directory exists. - test -f "$dirpart/$file" && continue - fdir=`AS_DIRNAME(["$file"])` - AS_MKDIR_P([$dirpart/$fdir]) - # echo "creating $dirpart/$file" - echo '# dummy' > "$dirpart/$file" - done + sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ + || continue + am_dirpart=`AS_DIRNAME(["$am_mf"])` + am_filepart=`AS_BASENAME(["$am_mf"])` + AM_RUN_LOG([cd "$am_dirpart" \ + && sed -e '/# am--include-marker/d' "$am_filepart" \ + | $MAKE -f - am--depfiles]) || am_rc=$? done + if test $am_rc -ne 0; then + AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments + for automatic dependency tracking. Try re-running configure with the + '--disable-dependency-tracking' option to at least be able to build + the package (albeit without support for automatic dependency tracking).]) + fi + AS_UNSET([am_dirpart]) + AS_UNSET([am_filepart]) + AS_UNSET([am_mf]) + AS_UNSET([am_rc]) + rm -f conftest-deps.mk } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS @@ -397,18 +448,17 @@ # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # -# This code is only required when automatic dependency tracking -# is enabled. FIXME. This creates each '.P' file that we will -# need in order to bootstrap the dependency handling code. +# This code is only required when automatic dependency tracking is enabled. +# This creates each '.Po' and '.Plo' makefile fragment that we'll need in +# order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], - [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) -]) + [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])]) # Do all the work for Automake. -*- Autoconf -*- -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -495,8 +545,8 @@ AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: -# -# +# +# AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. @@ -563,7 +613,7 @@ Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation -that behaves properly: . +that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM @@ -605,7 +655,7 @@ done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) -# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -626,7 +676,7 @@ fi AC_SUBST([install_sh])]) -# Copyright (C) 2003-2014 Free Software Foundation, Inc. +# Copyright (C) 2003-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -648,7 +698,7 @@ # Add --enable-maintainer-mode option to configure. -*- Autoconf -*- # From Jim Meyering -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -683,7 +733,7 @@ # Check to see how 'make' treats includes. -*- Autoconf -*- -# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -691,49 +741,42 @@ # AM_MAKE_INCLUDE() # ----------------- -# Check to see how make treats includes. +# Check whether make has an 'include' directive that can support all +# the idioms we need for our automatic dependency tracking code. AC_DEFUN([AM_MAKE_INCLUDE], -[am_make=${MAKE-make} -cat > confinc << 'END' +[AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive]) +cat > confinc.mk << 'END' am__doit: - @echo this is the am__doit target + @echo this is the am__doit target >confinc.out .PHONY: am__doit END -# If we don't find an include directive, just comment out the code. -AC_MSG_CHECKING([for style of include used by $am_make]) am__include="#" am__quote= -_am_result=none -# First try GNU make style include. -echo "include confinc" > confmf -# Ignore all kinds of additional output from 'make'. -case `$am_make -s -f confmf 2> /dev/null` in #( -*the\ am__doit\ target*) - am__include=include - am__quote= - _am_result=GNU - ;; -esac -# Now try BSD make style include. -if test "$am__include" = "#"; then - echo '.include "confinc"' > confmf - case `$am_make -s -f confmf 2> /dev/null` in #( - *the\ am__doit\ target*) - am__include=.include - am__quote="\"" - _am_result=BSD - ;; - esac -fi -AC_SUBST([am__include]) -AC_SUBST([am__quote]) -AC_MSG_RESULT([$_am_result]) -rm -f confinc confmf -]) +# BSD make does it like this. +echo '.include "confinc.mk" # ignored' > confmf.BSD +# Other make implementations (GNU, Solaris 10, AIX) do it like this. +echo 'include confinc.mk # ignored' > confmf.GNU +_am_result=no +for s in GNU BSD; do + AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out]) + AS_CASE([$?:`cat confinc.out 2>/dev/null`], + ['0:this is the am__doit target'], + [AS_CASE([$s], + [BSD], [am__include='.include' am__quote='"'], + [am__include='include' am__quote=''])]) + if test "$am__include" != "#"; then + _am_result="yes ($s style)" + break + fi +done +rm -f confinc.* confmf.* +AC_MSG_RESULT([${_am_result}]) +AC_SUBST([am__include])]) +AC_SUBST([am__quote])]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- -# Copyright (C) 1997-2014 Free Software Foundation, Inc. +# Copyright (C) 1997-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -774,7 +817,7 @@ # Obsolete and "removed" macros, that must however still report explicit # error messages when used, to smooth transition. # -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -801,7 +844,7 @@ # Helper functions for option handling. -*- Autoconf -*- -# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -830,7 +873,7 @@ AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) -# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -877,7 +920,7 @@ # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) -# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -896,7 +939,7 @@ # Check to make sure that the build environment is sane. -*- Autoconf -*- -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -977,7 +1020,7 @@ rm -f conftest.file ]) -# Copyright (C) 2009-2014 Free Software Foundation, Inc. +# Copyright (C) 2009-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1037,7 +1080,7 @@ _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) -# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1065,7 +1108,7 @@ INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) -# Copyright (C) 2006-2014 Free Software Foundation, Inc. +# Copyright (C) 2006-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1084,7 +1127,7 @@ # Check how to create a tarball. -*- Autoconf -*- -# Copyright (C) 2004-2014 Free Software Foundation, Inc. +# Copyright (C) 2004-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, diff -Nru sdparm-1.10/ar-lib sdparm-1.12/ar-lib --- sdparm-1.10/ar-lib 1970-01-01 00:00:00.000000000 +0000 +++ sdparm-1.12/ar-lib 2020-12-25 01:49:20.000000000 +0000 @@ -0,0 +1,270 @@ +#! /bin/sh +# Wrapper for Microsoft lib.exe + +me=ar-lib +scriptversion=2012-03-01.08; # UTC + +# Copyright (C) 2010-2018 Free Software Foundation, Inc. +# Written by Peter Rosin . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that 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. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + + +# func_error message +func_error () +{ + echo "$me: $1" 1>&2 + exit 1 +} + +file_conv= + +# func_file_conv build_file +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv in + mingw) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_at_file at_file operation archive +# Iterate over all members in AT_FILE performing OPERATION on ARCHIVE +# for each of them. +# When interpreting the content of the @FILE, do NOT use func_file_conv, +# since the user would need to supply preconverted file names to +# binutils ar, at least for MinGW. +func_at_file () +{ + operation=$2 + archive=$3 + at_file_contents=`cat "$1"` + eval set x "$at_file_contents" + shift + + for member + do + $AR -NOLOGO $operation:"$member" "$archive" || exit $? + done +} + +case $1 in + '') + func_error "no command. Try '$0 --help' for more information." + ;; + -h | --h*) + cat <IDLE_A; + STANDBY->STANDBY_Z; ICT->IACT and SCT->SZCT + - device configuration extension mpage: expand PEWS + field with added PE_UN (PEWS units) field (ssc5r05) + - add Zoned block device control mpage (zbc2r04a) + - --defaults option can be used twice: reverts all + pages to their defaults (new in spc5r11, RTD bit) + - vpd: decode TransportIDs in SCSI port page + - --all option used twice lists all VPD pages + - decode SCSI Feature sets page (spc5r16) + - extended inquiry data, sync with spc5r09 + sbc4r11 + - 3 party copy page improvements including + Copy group identifier + - block limits and block limit extension VPD pages: + add extra info about corner cases + - add maximum inquiry|mode_page change logs fields + to extended inquiry vpd page (spc5r17) + - fully implement Device constituents VPD page + - command=capacity with --long force read capacity(16) + with full reporting of response + - --wscan option: expand bus type to include NVMe + - mode page output with -HHH suitable for --inhex= + - add flexible geometery page (obsolete) sbc2r00 + - point svn:externals to rev 843 of sg3_utils [v 1.45] + - convert many two valued 'int's to bool + - shellcheck corrections on scripts + - upgrade automake to version 1.15 (U16.04) + - rework configure.ac and src/Makefile.am + - add --enable-debug to ./configure + - update BSD license from 3 to 2 clause aka FreeBSD + license (without reference to FreeBSD project) + - debian: bump compat file contents from 7 to 10 + ChangeLog for sdparm-1.10 [20160222] [svn: r279] - add --inhex=FN option for decoding without device present, FN is interpreted as response to mode sense(10) command diff -Nru sdparm-1.10/compile sdparm-1.12/compile --- sdparm-1.10/compile 2015-12-02 00:45:13.000000000 +0000 +++ sdparm-1.12/compile 2020-12-25 01:49:20.000000000 +0000 @@ -1,9 +1,9 @@ #! /bin/sh # Wrapper for compilers which do not understand '-c -o'. -scriptversion=2012-10-14.11; # UTC +scriptversion=2018-03-07.03; # UTC -# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # Written by Tom Tromey . # # This program is free software; you can redistribute it and/or modify @@ -17,7 +17,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -255,7 +255,8 @@ echo "compile $scriptversion" exit $? ;; - cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ + icl | *[/\\]icl | icl.exe | *[/\\]icl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac @@ -339,9 +340,9 @@ # Local Variables: # mode: shell-script # sh-indentation: 2 -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" +# time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: diff -Nru sdparm-1.10/config.guess sdparm-1.12/config.guess --- sdparm-1.10/config.guess 2015-01-01 20:30:01.000000000 +0000 +++ sdparm-1.12/config.guess 2020-12-25 01:49:20.000000000 +0000 @@ -1,8 +1,8 @@ #! /bin/sh # Attempt to guess a canonical system name. -# Copyright 1992-2014 Free Software Foundation, Inc. +# Copyright 1992-2020 Free Software Foundation, Inc. -timestamp='2014-03-23' +timestamp='2020-11-19' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ # General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, see . +# along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -24,22 +24,22 @@ # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # -# Originally written by Per Bothner. +# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD +# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess # -# Please send patches with a ChangeLog entry to config-patches@gnu.org. +# Please send patches to . -me=`echo "$0" | sed -e 's,.*/,,'` +me=$(echo "$0" | sed -e 's,.*/,,') usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. -Operation modes: +Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit @@ -50,7 +50,7 @@ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright 1992-2014 Free Software Foundation, Inc. +Copyright 1992-2020 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -84,8 +84,6 @@ exit 1 fi -trap 'exit 1' 1 2 15 - # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a @@ -96,66 +94,89 @@ # Portable tmp directory creation inspired by the Autoconf team. -set_cc_for_build=' -trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; -trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; -: ${TMPDIR=/tmp} ; - { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || - { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || - { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || - { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; -dummy=$tmp/dummy ; -tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; -case $CC_FOR_BUILD,$HOST_CC,$CC in - ,,) echo "int x;" > $dummy.c ; - for c in cc gcc c89 c99 ; do - if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then - CC_FOR_BUILD="$c"; break ; - fi ; - done ; - if test x"$CC_FOR_BUILD" = x ; then - CC_FOR_BUILD=no_compiler_found ; - fi - ;; - ,,*) CC_FOR_BUILD=$CC ;; - ,*,*) CC_FOR_BUILD=$HOST_CC ;; -esac ; set_cc_for_build= ;' +tmp= +# shellcheck disable=SC2172 +trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 + +set_cc_for_build() { + # prevent multiple calls if $tmp is already set + test "$tmp" && return 0 + : "${TMPDIR=/tmp}" + # shellcheck disable=SC2039 + { tmp=$( (umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null) && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } + dummy=$tmp/dummy + case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in + ,,) echo "int x;" > "$dummy.c" + for driver in cc gcc c89 c99 ; do + if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then + CC_FOR_BUILD="$driver" + break + fi + done + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; + esac +} # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) -if (test -f /.attbin/uname) >/dev/null 2>&1 ; then +if test -f /.attbin/uname ; then PATH=$PATH:/.attbin ; export PATH fi -UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown -UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown -UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown -UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown +UNAME_MACHINE=$( (uname -m) 2>/dev/null) || UNAME_MACHINE=unknown +UNAME_RELEASE=$( (uname -r) 2>/dev/null) || UNAME_RELEASE=unknown +UNAME_SYSTEM=$( (uname -s) 2>/dev/null) || UNAME_SYSTEM=unknown +UNAME_VERSION=$( (uname -v) 2>/dev/null) || UNAME_VERSION=unknown -case "${UNAME_SYSTEM}" in +case "$UNAME_SYSTEM" in Linux|GNU|GNU/*) - # If the system lacks a compiler, then just pick glibc. - # We could probably try harder. - LIBC=gnu + LIBC=unknown - eval $set_cc_for_build - cat <<-EOF > $dummy.c + set_cc_for_build + cat <<-EOF > "$dummy.c" #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc - #else + #elif defined(__GLIBC__) LIBC=gnu + #else + #include + /* First heuristic to detect musl libc. */ + #ifdef __DEFINED_va_list + LIBC=musl + #endif #endif EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + eval "$($CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g')" + + # Second heuristic to detect musl libc. + if [ "$LIBC" = unknown ] && + command -v ldd >/dev/null && + ldd --version 2>&1 | grep -q ^musl; then + LIBC=musl + fi + + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + if [ "$LIBC" = unknown ]; then + LIBC=gnu + fi ;; esac # Note: order is significant - the case branches are not exclusive. -case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in +case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, @@ -168,21 +189,32 @@ # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" - UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ - /usr/sbin/$sysctl 2>/dev/null || echo unknown)` - case "${UNAME_MACHINE_ARCH}" in + UNAME_MACHINE_ARCH=$( (uname -p 2>/dev/null || \ + "/sbin/$sysctl" 2>/dev/null || \ + "/usr/sbin/$sysctl" 2>/dev/null || \ + echo unknown)) + case "$UNAME_MACHINE_ARCH" in + aarch64eb) machine=aarch64_be-unknown ;; armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; - *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + earmv*) + arch=$(echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,') + endian=$(echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p') + machine="${arch}${endian}"-unknown + ;; + *) machine="$UNAME_MACHINE_ARCH"-unknown ;; esac # The Operating System including object format, if it has switched - # to ELF recently, or will in the future. - case "${UNAME_MACHINE_ARCH}" in + # to ELF recently (or will in the future) and ABI. + case "$UNAME_MACHINE_ARCH" in + earm*) + os=netbsdelf + ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) - eval $set_cc_for_build + set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then @@ -197,117 +229,137 @@ os=netbsd ;; esac + # Determine ABI tags. + case "$UNAME_MACHINE_ARCH" in + earm*) + expr='s/^earmv[0-9]/-eabi/;s/eb$//' + abi=$(echo "$UNAME_MACHINE_ARCH" | sed -e "$expr") + ;; + esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. - case "${UNAME_VERSION}" in + case "$UNAME_VERSION" in Debian*) release='-gnu' ;; *) - release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + release=$(echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2) ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "${machine}-${os}${release}" + echo "$machine-${os}${release}${abi-}" exit ;; *:Bitrig:*:*) - UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} + UNAME_MACHINE_ARCH=$(arch | sed 's/Bitrig.//') + echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE" exit ;; *:OpenBSD:*:*) - UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + UNAME_MACHINE_ARCH=$(arch | sed 's/OpenBSD.//') + echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE" + exit ;; + *:LibertyBSD:*:*) + UNAME_MACHINE_ARCH=$(arch | sed 's/^.*BSD\.//') + echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE" + exit ;; + *:MidnightBSD:*:*) + echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE" exit ;; *:ekkoBSD:*:*) - echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE" exit ;; *:SolidBSD:*:*) - echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE" + exit ;; + *:OS108:*:*) + echo "$UNAME_MACHINE"-unknown-os108_"$UNAME_RELEASE" exit ;; macppc:MirBSD:*:*) - echo powerpc-unknown-mirbsd${UNAME_RELEASE} + echo powerpc-unknown-mirbsd"$UNAME_RELEASE" exit ;; *:MirBSD:*:*) - echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE" + exit ;; + *:Sortix:*:*) + echo "$UNAME_MACHINE"-unknown-sortix + exit ;; + *:Twizzler:*:*) + echo "$UNAME_MACHINE"-unknown-twizzler + exit ;; + *:Redox:*:*) + echo "$UNAME_MACHINE"-unknown-redox + exit ;; + mips:OSF1:*.*) + echo mips-dec-osf1 exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) - UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + UNAME_RELEASE=$(/usr/sbin/sizer -v | awk '{print $3}') ;; *5.*) - UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + UNAME_RELEASE=$(/usr/sbin/sizer -v | awk '{print $4}') ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. - ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + ALPHA_CPU_TYPE=$(/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1) case "$ALPHA_CPU_TYPE" in "EV4 (21064)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "EV4.5 (21064)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "EV5 (21164)") - UNAME_MACHINE="alphaev5" ;; + UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") - UNAME_MACHINE="alphaev56" ;; + UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") - UNAME_MACHINE="alphapca56" ;; + UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") - UNAME_MACHINE="alphapca57" ;; + UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") - UNAME_MACHINE="alphaev6" ;; + UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") - UNAME_MACHINE="alphaev67" ;; + UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") - UNAME_MACHINE="alphaev69" ;; + UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") - UNAME_MACHINE="alphaev7" ;; + UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") - UNAME_MACHINE="alphaev79" ;; + UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. - echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + echo "$UNAME_MACHINE"-dec-osf"$(echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz)" # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; - Alpha\ *:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # Should we change UNAME_MACHINE based on the output of uname instead - # of the specific Alpha model? - echo alpha-pc-interix - exit ;; - 21064:Windows_NT:50:3) - echo alpha-dec-winnt3.5 - exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-amigaos + echo "$UNAME_MACHINE"-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-morphos + echo "$UNAME_MACHINE"-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition @@ -319,7 +371,7 @@ echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) - echo arm-acorn-riscix${UNAME_RELEASE} + echo arm-acorn-riscix"$UNAME_RELEASE" exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos @@ -329,7 +381,7 @@ exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. - if test "`(/bin/universe) 2>/dev/null`" = att ; then + if test "$( (/bin/universe) 2>/dev/null)" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd @@ -342,69 +394,69 @@ echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) - case `/usr/bin/uname -p` in + case $(/usr/bin/uname -p) in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) - echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + echo "$UNAME_MACHINE"-ibm-solaris2"$(echo "$UNAME_RELEASE" | sed -e 's/[^.]*//')" exit ;; sun4H:SunOS:5.*:*) - echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + echo sparc-hal-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) - echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + echo sparc-sun-solaris2"$(echo "$UNAME_RELEASE" | sed -e 's/[^.]*//')" exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) - echo i386-pc-auroraux${UNAME_RELEASE} + echo i386-pc-auroraux"$UNAME_RELEASE" exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) - eval $set_cc_for_build - SUN_ARCH="i386" + set_cc_for_build + SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then - SUN_ARCH="x86_64" + SUN_ARCH=x86_64 fi fi - echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + echo "$SUN_ARCH"-pc-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. - echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + echo sparc-sun-solaris3"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" exit ;; sun4*:SunOS:*:*) - case "`/usr/bin/arch -k`" in + case "$(/usr/bin/arch -k)" in Series*|S4*) - UNAME_RELEASE=`uname -v` + UNAME_RELEASE=$(uname -v) ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. - echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + echo sparc-sun-sunos"$(echo "$UNAME_RELEASE"|sed -e 's/-/_/')" exit ;; sun3*:SunOS:*:*) - echo m68k-sun-sunos${UNAME_RELEASE} + echo m68k-sun-sunos"$UNAME_RELEASE" exit ;; sun*:*:4.2BSD:*) - UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` - test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 - case "`/bin/arch`" in + UNAME_RELEASE=$( (sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null) + test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 + case "$(/bin/arch)" in sun3) - echo m68k-sun-sunos${UNAME_RELEASE} + echo m68k-sun-sunos"$UNAME_RELEASE" ;; sun4) - echo sparc-sun-sunos${UNAME_RELEASE} + echo sparc-sun-sunos"$UNAME_RELEASE" ;; esac exit ;; aushp:SunOS:*:*) - echo sparc-auspex-sunos${UNAME_RELEASE} + echo sparc-auspex-sunos"$UNAME_RELEASE" exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not @@ -415,44 +467,44 @@ # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} + echo m68k-atari-mint"$UNAME_RELEASE" exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} + echo m68k-atari-mint"$UNAME_RELEASE" exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} + echo m68k-atari-mint"$UNAME_RELEASE" exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) - echo m68k-milan-mint${UNAME_RELEASE} + echo m68k-milan-mint"$UNAME_RELEASE" exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) - echo m68k-hades-mint${UNAME_RELEASE} + echo m68k-hades-mint"$UNAME_RELEASE" exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) - echo m68k-unknown-mint${UNAME_RELEASE} + echo m68k-unknown-mint"$UNAME_RELEASE" exit ;; m68k:machten:*:*) - echo m68k-apple-machten${UNAME_RELEASE} + echo m68k-apple-machten"$UNAME_RELEASE" exit ;; powerpc:machten:*:*) - echo powerpc-apple-machten${UNAME_RELEASE} + echo powerpc-apple-machten"$UNAME_RELEASE" exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) - echo mips-dec-ultrix${UNAME_RELEASE} + echo mips-dec-ultrix"$UNAME_RELEASE" exit ;; VAX*:ULTRIX*:*:*) - echo vax-dec-ultrix${UNAME_RELEASE} + echo vax-dec-ultrix"$UNAME_RELEASE" exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) - echo clipper-intergraph-clix${UNAME_RELEASE} + echo clipper-intergraph-clix"$UNAME_RELEASE" exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { @@ -461,23 +513,23 @@ #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) - printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) - printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) - printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF - $CC_FOR_BUILD -o $dummy $dummy.c && - dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && - SYSTEM_NAME=`$dummy $dummyarg` && + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && + dummyarg=$(echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p') && + SYSTEM_NAME=$("$dummy" "$dummyarg") && { echo "$SYSTEM_NAME"; exit; } - echo mips-mips-riscos${UNAME_RELEASE} + echo mips-mips-riscos"$UNAME_RELEASE" exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax @@ -502,18 +554,18 @@ exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures - UNAME_PROCESSOR=`/usr/bin/uname -p` - if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + UNAME_PROCESSOR=$(/usr/bin/uname -p) + if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 then - if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ - [ ${TARGET_BINARY_INTERFACE}x = x ] + if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ + test "$TARGET_BINARY_INTERFACE"x = x then - echo m88k-dg-dgux${UNAME_RELEASE} + echo m88k-dg-dgux"$UNAME_RELEASE" else - echo m88k-dg-dguxbcs${UNAME_RELEASE} + echo m88k-dg-dguxbcs"$UNAME_RELEASE" fi else - echo i586-dg-dgux${UNAME_RELEASE} + echo i586-dg-dgux"$UNAME_RELEASE" fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) @@ -530,26 +582,26 @@ echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) - echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + echo mips-sgi-irix"$(echo "$UNAME_RELEASE"|sed -e 's/-/_/g')" exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id - exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + exit ;; # Note that: echo "'$(uname -s)'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` + if test -x /usr/bin/oslevel ; then + IBM_REV=$(/usr/bin/oslevel) else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi - echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV" exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #include main() @@ -560,7 +612,7 @@ exit(0); } EOF - if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=$("$dummy") then echo "$SYSTEM_NAME" else @@ -573,27 +625,28 @@ fi exit ;; *:AIX:*:[4567]) - IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` - if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_CPU_ID=$(/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }') + if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` + if test -x /usr/bin/lslpp ; then + IBM_REV=$(/usr/bin/lslpp -Lqc bos.rte.libc | + awk -F: '{ print $3 }' | sed s/[0-9]*$/0/) else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi - echo ${IBM_ARCH}-ibm-aix${IBM_REV} + echo "$IBM_ARCH"-ibm-aix"$IBM_REV" exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; - ibmrt:4.4BSD:*|romp-ibm:BSD:*) + ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and - echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx @@ -608,28 +661,28 @@ echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - case "${UNAME_MACHINE}" in - 9000/31? ) HP_ARCH=m68000 ;; - 9000/[34]?? ) HP_ARCH=m68k ;; + HPUX_REV=$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//') + case "$UNAME_MACHINE" in + 9000/31?) HP_ARCH=m68000 ;; + 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) - if [ -x /usr/bin/getconf ]; then - sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` - sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` - case "${sc_cpu_version}" in - 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 - 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + if test -x /usr/bin/getconf; then + sc_cpu_version=$(/usr/bin/getconf SC_CPU_VERSION 2>/dev/null) + sc_kernel_bits=$(/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null) + case "$sc_cpu_version" in + 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 + 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 - case "${sc_kernel_bits}" in - 32) HP_ARCH="hppa2.0n" ;; - 64) HP_ARCH="hppa2.0w" ;; - '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + case "$sc_kernel_bits" in + 32) HP_ARCH=hppa2.0n ;; + 64) HP_ARCH=hppa2.0w ;; + '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi - if [ "${HP_ARCH}" = "" ]; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + if test "$HP_ARCH" = ""; then + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE #include @@ -662,13 +715,13 @@ exit (0); } EOF - (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=$("$dummy") test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac - if [ ${HP_ARCH} = "hppa2.0w" ] + if test "$HP_ARCH" = hppa2.0w then - eval $set_cc_for_build + set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler @@ -679,23 +732,23 @@ # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 - if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then - HP_ARCH="hppa2.0w" + HP_ARCH=hppa2.0w else - HP_ARCH="hppa64" + HP_ARCH=hppa64 fi fi - echo ${HP_ARCH}-hp-hpux${HPUX_REV} + echo "$HP_ARCH"-hp-hpux"$HPUX_REV" exit ;; ia64:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - echo ia64-hp-hpux${HPUX_REV} + HPUX_REV=$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//') + echo ia64-hp-hpux"$HPUX_REV" exit ;; 3050*:HI-UX:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #include int main () @@ -720,11 +773,11 @@ exit (0); } EOF - $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=$("$dummy") && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; - 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) @@ -733,17 +786,17 @@ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; - hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) - if [ -x /usr/sbin/sysversion ] ; then - echo ${UNAME_MACHINE}-unknown-osf1mk + if test -x /usr/sbin/sysversion ; then + echo "$UNAME_MACHINE"-unknown-osf1mk else - echo ${UNAME_MACHINE}-unknown-osf1 + echo "$UNAME_MACHINE"-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) @@ -768,130 +821,123 @@ echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) - echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) - echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) - echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) - echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) - echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) - echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) - FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + FUJITSU_PROC=$(uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz) + FUJITSU_SYS=$(uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///') + FUJITSU_REL=$(echo "$UNAME_RELEASE" | sed -e 's/ /_/') echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + FUJITSU_SYS=$(uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///') + FUJITSU_REL=$(echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/') echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) - echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE" exit ;; sparc*:BSD/OS:*:*) - echo sparc-unknown-bsdi${UNAME_RELEASE} + echo sparc-unknown-bsdi"$UNAME_RELEASE" exit ;; *:BSD/OS:*:*) - echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE" + exit ;; + arm:FreeBSD:*:*) + UNAME_PROCESSOR=$(uname -p) + set_cc_for_build + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo "${UNAME_PROCESSOR}"-unknown-freebsd"$(echo ${UNAME_RELEASE}|sed -e 's/[-(].*//')"-gnueabi + else + echo "${UNAME_PROCESSOR}"-unknown-freebsd"$(echo ${UNAME_RELEASE}|sed -e 's/[-(].*//')"-gnueabihf + fi exit ;; *:FreeBSD:*:*) - UNAME_PROCESSOR=`/usr/bin/uname -p` - case ${UNAME_PROCESSOR} in + UNAME_PROCESSOR=$(/usr/bin/uname -p) + case "$UNAME_PROCESSOR" in amd64) - echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; - *) - echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + UNAME_PROCESSOR=x86_64 ;; + i386) + UNAME_PROCESSOR=i586 ;; esac + echo "$UNAME_PROCESSOR"-unknown-freebsd"$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')" exit ;; i*:CYGWIN*:*) - echo ${UNAME_MACHINE}-pc-cygwin + echo "$UNAME_MACHINE"-pc-cygwin exit ;; *:MINGW64*:*) - echo ${UNAME_MACHINE}-pc-mingw64 + echo "$UNAME_MACHINE"-pc-mingw64 exit ;; *:MINGW*:*) - echo ${UNAME_MACHINE}-pc-mingw32 + echo "$UNAME_MACHINE"-pc-mingw32 exit ;; *:MSYS*:*) - echo ${UNAME_MACHINE}-pc-msys - exit ;; - i*:windows32*:*) - # uname -m includes "-pc" on this system. - echo ${UNAME_MACHINE}-mingw32 + echo "$UNAME_MACHINE"-pc-msys exit ;; i*:PW*:*) - echo ${UNAME_MACHINE}-pc-pw32 + echo "$UNAME_MACHINE"-pc-pw32 exit ;; *:Interix*:*) - case ${UNAME_MACHINE} in + case "$UNAME_MACHINE" in x86) - echo i586-pc-interix${UNAME_RELEASE} + echo i586-pc-interix"$UNAME_RELEASE" exit ;; authenticamd | genuineintel | EM64T) - echo x86_64-unknown-interix${UNAME_RELEASE} + echo x86_64-unknown-interix"$UNAME_RELEASE" exit ;; IA64) - echo ia64-unknown-interix${UNAME_RELEASE} + echo ia64-unknown-interix"$UNAME_RELEASE" exit ;; esac ;; - [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) - echo i${UNAME_MACHINE}-pc-mks - exit ;; - 8664:Windows_NT:*) - echo x86_64-pc-mks - exit ;; - i*:Windows_NT*:* | Pentium*:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we - # UNAME_MACHINE based on the output of uname instead of i386? - echo i586-pc-interix - exit ;; i*:UWIN*:*) - echo ${UNAME_MACHINE}-pc-uwin + echo "$UNAME_MACHINE"-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) - echo x86_64-unknown-cygwin - exit ;; - p*:CYGWIN*:*) - echo powerpcle-unknown-cygwin + echo x86_64-pc-cygwin exit ;; prep*:SunOS:5.*:*) - echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + echo powerpcle-unknown-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" exit ;; *:GNU:*:*) # the GNU system - echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + echo "$(echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,')-unknown-$LIBC$(echo "$UNAME_RELEASE"|sed -e 's,/.*$,,')" exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland - echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} + echo "$UNAME_MACHINE-unknown-$(echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]")$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')-$LIBC" exit ;; - i*86:Minix:*:*) - echo ${UNAME_MACHINE}-pc-minix + *:Minix:*:*) + echo "$UNAME_MACHINE"-unknown-minix exit ;; aarch64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; alpha:Linux:*:*) - case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + case $(sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null) in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; @@ -901,129 +947,179 @@ EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 - if test "$?" = 0 ; then LIBC="gnulibc1" ; fi - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + if test "$?" = 0 ; then LIBC=gnulibc1 ; fi + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; arc:Linux:*:* | arceb:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; arm*:Linux:*:*) - eval $set_cc_for_build + set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then - echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi else - echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf fi fi exit ;; avr32*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; cris:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-${LIBC} + echo "$UNAME_MACHINE"-axis-linux-"$LIBC" exit ;; crisv32:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-${LIBC} + echo "$UNAME_MACHINE"-axis-linux-"$LIBC" + exit ;; + e2k:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; frv:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; hexagon:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; i*86:Linux:*:*) - echo ${UNAME_MACHINE}-pc-linux-${LIBC} + echo "$UNAME_MACHINE"-pc-linux-"$LIBC" exit ;; ia64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + k1om:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; m32r*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; m68*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; mips:Linux:*:* | mips64:Linux:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + IS_GLIBC=0 + test x"${LIBC}" = xgnu && IS_GLIBC=1 + sed 's/^ //' << EOF > "$dummy.c" #undef CPU - #undef ${UNAME_MACHINE} - #undef ${UNAME_MACHINE}el + #undef mips + #undef mipsel + #undef mips64 + #undef mips64el + #if ${IS_GLIBC} && defined(_ABI64) + LIBCABI=gnuabi64 + #else + #if ${IS_GLIBC} && defined(_ABIN32) + LIBCABI=gnuabin32 + #else + LIBCABI=${LIBC} + #endif + #endif + + #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa64r6 + #else + #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa32r6 + #else + #if defined(__mips64) + CPU=mips64 + #else + CPU=mips + #endif + #endif + #endif + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=${UNAME_MACHINE}el + MIPS_ENDIAN=el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=${UNAME_MACHINE} + MIPS_ENDIAN= #else - CPU= + MIPS_ENDIAN= #endif #endif EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` - test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } + eval "$($CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI')" + test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } ;; + mips64el:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; openrisc*:Linux:*:*) - echo or1k-unknown-linux-${LIBC} + echo or1k-unknown-linux-"$LIBC" exit ;; or32:Linux:*:* | or1k*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; padre:Linux:*:*) - echo sparc-unknown-linux-${LIBC} + echo sparc-unknown-linux-"$LIBC" exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-${LIBC} + echo hppa64-unknown-linux-"$LIBC" exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level - case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in - PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; - PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; - *) echo hppa-unknown-linux-${LIBC} ;; + case $(grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2) in + PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;; + PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;; + *) echo hppa-unknown-linux-"$LIBC" ;; esac exit ;; ppc64:Linux:*:*) - echo powerpc64-unknown-linux-${LIBC} + echo powerpc64-unknown-linux-"$LIBC" exit ;; ppc:Linux:*:*) - echo powerpc-unknown-linux-${LIBC} + echo powerpc-unknown-linux-"$LIBC" exit ;; ppc64le:Linux:*:*) - echo powerpc64le-unknown-linux-${LIBC} + echo powerpc64le-unknown-linux-"$LIBC" exit ;; ppcle:Linux:*:*) - echo powerpcle-unknown-linux-${LIBC} + echo powerpcle-unknown-linux-"$LIBC" + exit ;; + riscv32:Linux:*:* | riscv64:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; s390:Linux:*:* | s390x:Linux:*:*) - echo ${UNAME_MACHINE}-ibm-linux-${LIBC} + echo "$UNAME_MACHINE"-ibm-linux-"$LIBC" exit ;; sh64*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; sh*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; tile*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; vax:Linux:*:*) - echo ${UNAME_MACHINE}-dec-linux-${LIBC} + echo "$UNAME_MACHINE"-dec-linux-"$LIBC" exit ;; x86_64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + set_cc_for_build + LIBCABI=$LIBC + if test "$CC_FOR_BUILD" != no_compiler_found; then + if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_X32 >/dev/null + then + LIBCABI="$LIBC"x32 + fi + fi + echo "$UNAME_MACHINE"-pc-linux-"$LIBCABI" exit ;; xtensa*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. @@ -1037,51 +1133,51 @@ # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. - echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION" exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. - echo ${UNAME_MACHINE}-pc-os2-emx + echo "$UNAME_MACHINE"-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) - echo ${UNAME_MACHINE}-unknown-stop + echo "$UNAME_MACHINE"-unknown-stop exit ;; i*86:atheos:*:*) - echo ${UNAME_MACHINE}-unknown-atheos + echo "$UNAME_MACHINE"-unknown-atheos exit ;; i*86:syllable:*:*) - echo ${UNAME_MACHINE}-pc-syllable + echo "$UNAME_MACHINE"-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) - echo i386-unknown-lynxos${UNAME_RELEASE} + echo i386-unknown-lynxos"$UNAME_RELEASE" exit ;; i*86:*DOS:*:*) - echo ${UNAME_MACHINE}-pc-msdosdjgpp + echo "$UNAME_MACHINE"-pc-msdosdjgpp exit ;; - i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) - UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + i*86:*:4.*:*) + UNAME_REL=$(echo "$UNAME_RELEASE" | sed 's/\/MP$//') if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then - echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL" else - echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL" fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. - case `/bin/uname -X | grep "^Machine"` in + case $(/bin/uname -X | grep "^Machine") in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac - echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}" exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then - UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then - UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + UNAME_REL=$( (/bin/uname -X|grep Release|sed -e 's/.*= //')) (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 @@ -1089,9 +1185,9 @@ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 - echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL" else - echo ${UNAME_MACHINE}-pc-sysv32 + echo "$UNAME_MACHINE"-pc-sysv32 fi exit ;; pc:*:*:*) @@ -1099,7 +1195,7 @@ # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub - # prints for the "djgpp" host, or else GDB configury will decide that + # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; @@ -1111,9 +1207,9 @@ exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then - echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. - echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) @@ -1131,41 +1227,41 @@ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ - && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + && OS_REL=.$(sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ - && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + && OS_REL=.$(sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) - echo m68k-unknown-lynxos${UNAME_RELEASE} + echo m68k-unknown-lynxos"$UNAME_RELEASE" exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) - echo sparc-unknown-lynxos${UNAME_RELEASE} + echo sparc-unknown-lynxos"$UNAME_RELEASE" exit ;; rs6000:LynxOS:2.*:*) - echo rs6000-unknown-lynxos${UNAME_RELEASE} + echo rs6000-unknown-lynxos"$UNAME_RELEASE" exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) - echo powerpc-unknown-lynxos${UNAME_RELEASE} + echo powerpc-unknown-lynxos"$UNAME_RELEASE" exit ;; SM[BE]S:UNIX_SV:*:*) - echo mips-dde-sysv${UNAME_RELEASE} + echo mips-dde-sysv"$UNAME_RELEASE" exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 @@ -1175,8 +1271,8 @@ exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then - UNAME_MACHINE=`(uname -p) 2>/dev/null` - echo ${UNAME_MACHINE}-sni-sysv4 + UNAME_MACHINE=$( (uname -p) 2>/dev/null) + echo "$UNAME_MACHINE"-sni-sysv4 else echo ns32k-sni-sysv fi @@ -1196,23 +1292,23 @@ exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. - echo ${UNAME_MACHINE}-stratus-vos + echo "$UNAME_MACHINE"-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) - echo m68k-apple-aux${UNAME_RELEASE} + echo m68k-apple-aux"$UNAME_RELEASE" exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) - if [ -d /usr/nec ]; then - echo mips-nec-sysv${UNAME_RELEASE} + if test -d /usr/nec; then + echo mips-nec-sysv"$UNAME_RELEASE" else - echo mips-unknown-sysv${UNAME_RELEASE} + echo mips-unknown-sysv"$UNAME_RELEASE" fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. @@ -1231,77 +1327,97 @@ echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) - echo sx4-nec-superux${UNAME_RELEASE} + echo sx4-nec-superux"$UNAME_RELEASE" exit ;; SX-5:SUPER-UX:*:*) - echo sx5-nec-superux${UNAME_RELEASE} + echo sx5-nec-superux"$UNAME_RELEASE" exit ;; SX-6:SUPER-UX:*:*) - echo sx6-nec-superux${UNAME_RELEASE} + echo sx6-nec-superux"$UNAME_RELEASE" exit ;; SX-7:SUPER-UX:*:*) - echo sx7-nec-superux${UNAME_RELEASE} + echo sx7-nec-superux"$UNAME_RELEASE" exit ;; SX-8:SUPER-UX:*:*) - echo sx8-nec-superux${UNAME_RELEASE} + echo sx8-nec-superux"$UNAME_RELEASE" exit ;; SX-8R:SUPER-UX:*:*) - echo sx8r-nec-superux${UNAME_RELEASE} + echo sx8r-nec-superux"$UNAME_RELEASE" + exit ;; + SX-ACE:SUPER-UX:*:*) + echo sxace-nec-superux"$UNAME_RELEASE" exit ;; Power*:Rhapsody:*:*) - echo powerpc-apple-rhapsody${UNAME_RELEASE} + echo powerpc-apple-rhapsody"$UNAME_RELEASE" exit ;; *:Rhapsody:*:*) - echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE" + exit ;; + arm64:Darwin:*:*) + echo aarch64-apple-darwin"$UNAME_RELEASE" exit ;; *:Darwin:*:*) - UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown - eval $set_cc_for_build - if test "$UNAME_PROCESSOR" = unknown ; then - UNAME_PROCESSOR=powerpc - fi - if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then - if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - case $UNAME_PROCESSOR in - i386) UNAME_PROCESSOR=x86_64 ;; - powerpc) UNAME_PROCESSOR=powerpc64 ;; - esac - fi + UNAME_PROCESSOR=$(uname -p) + case $UNAME_PROCESSOR in + unknown) UNAME_PROCESSOR=powerpc ;; + esac + if command -v xcode-select > /dev/null 2> /dev/null && \ + ! xcode-select --print-path > /dev/null 2> /dev/null ; then + # Avoid executing cc if there is no toolchain installed as + # cc will be a stub that puts up a graphical alert + # prompting the user to install developer tools. + CC_FOR_BUILD=no_compiler_found + else + set_cc_for_build + fi + if test "$CC_FOR_BUILD" != no_compiler_found; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc + if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_PPC >/dev/null + then + UNAME_PROCESSOR=powerpc fi elif test "$UNAME_PROCESSOR" = i386 ; then - # Avoid executing cc on OS X 10.9, as it ships with a stub - # that puts up a graphical alert prompting to install - # developer tools. Any system running Mac OS X 10.7 or - # later (Darwin 11 and later) is required to have a 64-bit - # processor. This is not true of the ARM version of Darwin - # that Apple uses in portable devices. - UNAME_PROCESSOR=x86_64 + # uname -m returns i386 or x86_64 + UNAME_PROCESSOR=$UNAME_MACHINE fi - echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE" exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) - UNAME_PROCESSOR=`uname -p` - if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=$(uname -p) + if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi - echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE" exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; - NEO-?:NONSTOP_KERNEL:*:*) - echo neo-tandem-nsk${UNAME_RELEASE} + NEO-*:NONSTOP_KERNEL:*:*) + echo neo-tandem-nsk"$UNAME_RELEASE" exit ;; NSE-*:NONSTOP_KERNEL:*:*) - echo nse-tandem-nsk${UNAME_RELEASE} + echo nse-tandem-nsk"$UNAME_RELEASE" + exit ;; + NSR-*:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk"$UNAME_RELEASE" + exit ;; + NSV-*:NONSTOP_KERNEL:*:*) + echo nsv-tandem-nsk"$UNAME_RELEASE" exit ;; - NSR-?:NONSTOP_KERNEL:*:*) - echo nsr-tandem-nsk${UNAME_RELEASE} + NSX-*:NONSTOP_KERNEL:*:*) + echo nsx-tandem-nsk"$UNAME_RELEASE" exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux @@ -1310,18 +1426,19 @@ echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) - echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE" exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. - if test "$cputype" = "386"; then + # shellcheck disable=SC2154 + if test "$cputype" = 386; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi - echo ${UNAME_MACHINE}-unknown-plan9 + echo "$UNAME_MACHINE"-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 @@ -1342,14 +1459,14 @@ echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) - echo mips-sei-seiux${UNAME_RELEASE} + echo mips-sei-seiux"$UNAME_RELEASE" exit ;; *:DragonFly:*:*) - echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + echo "$UNAME_MACHINE"-unknown-dragonfly"$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')" exit ;; *:*VMS:*:*) - UNAME_MACHINE=`(uname -p) 2>/dev/null` - case "${UNAME_MACHINE}" in + UNAME_MACHINE=$( (uname -p) 2>/dev/null) + case "$UNAME_MACHINE" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; @@ -1358,62 +1475,223 @@ echo i386-pc-xenix exit ;; i*86:skyos:*:*) - echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + echo "$UNAME_MACHINE"-pc-skyos"$(echo "$UNAME_RELEASE" | sed -e 's/ .*$//')" exit ;; i*86:rdos:*:*) - echo ${UNAME_MACHINE}-pc-rdos + echo "$UNAME_MACHINE"-pc-rdos exit ;; i*86:AROS:*:*) - echo ${UNAME_MACHINE}-pc-aros + echo "$UNAME_MACHINE"-pc-aros exit ;; x86_64:VMkernel:*:*) - echo ${UNAME_MACHINE}-unknown-esx + echo "$UNAME_MACHINE"-unknown-esx + exit ;; + amd64:Isilon\ OneFS:*:*) + echo x86_64-unknown-onefs + exit ;; + *:Unleashed:*:*) + echo "$UNAME_MACHINE"-unknown-unleashed"$UNAME_RELEASE" exit ;; esac +# No uname command or uname output not recognized. +set_cc_for_build +cat > "$dummy.c" < +#include +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#include +#if defined(_SIZE_T_) || defined(SIGLOST) +#include +#endif +#endif +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=$( (hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null); + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); +#endif + +#if defined (vax) +#if !defined (ultrix) +#include +#if defined (BSD) +#if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +#else +#if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#endif +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#else +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname un; + uname (&un); + printf ("vax-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("vax-dec-ultrix\n"); exit (0); +#endif +#endif +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname *un; + uname (&un); + printf ("mips-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("mips-dec-ultrix\n"); exit (0); +#endif +#endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=$($dummy) && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. +test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } + +echo "$0: unable to guess system type" >&2 + +case "$UNAME_MACHINE:$UNAME_SYSTEM" in + mips:Linux | mips64:Linux) + # If we got here on MIPS GNU/Linux, output extra information. + cat >&2 <&2 < in order to provide the needed -information to handle your system. +year=$(echo $timestamp | sed 's,-.*,,') +# shellcheck disable=SC2003 +if test "$(expr "$(date +%Y)" - "$year")" -lt 3 ; then + cat >&2 </dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` - -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null` - -hostinfo = `(hostinfo) 2>/dev/null` -/bin/universe = `(/bin/universe) 2>/dev/null` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` -/bin/arch = `(/bin/arch) 2>/dev/null` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` - -UNAME_MACHINE = ${UNAME_MACHINE} -UNAME_RELEASE = ${UNAME_RELEASE} -UNAME_SYSTEM = ${UNAME_SYSTEM} -UNAME_VERSION = ${UNAME_VERSION} +uname -m = $( (uname -m) 2>/dev/null || echo unknown) +uname -r = $( (uname -r) 2>/dev/null || echo unknown) +uname -s = $( (uname -s) 2>/dev/null || echo unknown) +uname -v = $( (uname -v) 2>/dev/null || echo unknown) + +/usr/bin/uname -p = $( (/usr/bin/uname -p) 2>/dev/null) +/bin/uname -X = $( (/bin/uname -X) 2>/dev/null) + +hostinfo = $( (hostinfo) 2>/dev/null) +/bin/universe = $( (/bin/universe) 2>/dev/null) +/usr/bin/arch -k = $( (/usr/bin/arch -k) 2>/dev/null) +/bin/arch = $( (/bin/arch) 2>/dev/null) +/usr/bin/oslevel = $( (/usr/bin/oslevel) 2>/dev/null) +/usr/convex/getsysinfo = $( (/usr/convex/getsysinfo) 2>/dev/null) + +UNAME_MACHINE = "$UNAME_MACHINE" +UNAME_RELEASE = "$UNAME_RELEASE" +UNAME_SYSTEM = "$UNAME_SYSTEM" +UNAME_VERSION = "$UNAME_VERSION" EOF +fi exit 1 # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" diff -Nru sdparm-1.10/config.h.in sdparm-1.12/config.h.in --- sdparm-1.10/config.h.in 2012-03-17 11:21:48.000000000 +0000 +++ sdparm-1.12/config.h.in 2020-12-25 01:49:20.000000000 +0000 @@ -1,20 +1,74 @@ /* config.h.in. Generated from configure.ac by autoheader. */ +/* Define to 1 if you have the header file. */ +#undef HAVE_BYTESWAP_H + /* Define to 1 if you have the `getopt_long' function. */ #undef HAVE_GETOPT_LONG +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_BSG_H /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_KDEV_T_H +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_NVME_IOCTL_H + /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_TYPES_H -/* ignore linux bsg */ +/* Define to 1 if you have the `lseek64' function. */ +#undef HAVE_LSEEK64 + +/* Found NVMe */ +#undef HAVE_NVME + +/* Define to 1 if you have the `posix_fadvise' function. */ +#undef HAVE_POSIX_FADVISE + +/* Define to 1 if you have the `posix_memalign' function. */ +#undef HAVE_POSIX_MEMALIGN + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `sysconf' function. */ +#undef HAVE_SYSCONF + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* use generic little-endian/big-endian instead */ +#undef IGNORE_FAST_LEBE + +/* option ignored */ #undef IGNORE_LINUX_BSG +/* compile out NVMe support */ +#undef IGNORE_NVME + /* Name of package */ #undef PACKAGE @@ -36,13 +90,16 @@ /* Define to the version of this package. */ #undef PACKAGE_VERSION +/* sdparm on android */ +#undef SG_LIB_ANDROID + /* sdparm Build Host */ #undef SG_LIB_BUILD_HOST /* sdparm on FreeBSD */ #undef SG_LIB_FREEBSD -/* assume sdparm on linux */ +/* sdparm on linux */ #undef SG_LIB_LINUX /* also MinGW environment */ @@ -60,11 +117,10 @@ /* full SCSI sense strings */ #undef SG_SCSI_STRINGS -/* Define to 1 if you have the ANSI C header files. */ +/* Define to 1 if all of the C90 standard headers exist (not just the ones + required in a freestanding environment). This macro is provided for + backward compatibility; new code need not use it. */ #undef STDC_HEADERS /* Version number of package */ #undef VERSION - -/* allow large buffers, aligned? */ -#undef WIN32_SPT_DIRECT diff -Nru sdparm-1.10/config.sub sdparm-1.12/config.sub --- sdparm-1.10/config.sub 2015-01-01 20:30:01.000000000 +0000 +++ sdparm-1.12/config.sub 2020-12-25 01:49:20.000000000 +0000 @@ -1,8 +1,8 @@ #! /bin/sh # Configuration validation subroutine script. -# Copyright 1992-2014 Free Software Foundation, Inc. +# Copyright 1992-2020 Free Software Foundation, Inc. -timestamp='2014-09-11' +timestamp='2020-12-02' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ # General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, see . +# along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -25,7 +25,7 @@ # of the GNU General Public License, version 3 ("GPLv3"). -# Please send patches with a ChangeLog entry to config-patches@gnu.org. +# Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. @@ -33,7 +33,7 @@ # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD +# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases @@ -50,15 +50,14 @@ # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. -me=`echo "$0" | sed -e 's,.*/,,'` +me=$(echo "$0" | sed -e 's,.*/,,') usage="\ -Usage: $0 [OPTION] CPU-MFR-OPSYS - $0 [OPTION] ALIAS +Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. -Operation modes: +Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit @@ -68,7 +67,7 @@ version="\ GNU config.sub ($timestamp) -Copyright 1992-2014 Free Software Foundation, Inc. +Copyright 1992-2020 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -90,12 +89,12 @@ - ) # Use stdin as input. break ;; -* ) - echo "$me: invalid option $1$help" + echo "$me: invalid option $1$help" >&2 exit 1 ;; *local*) # First pass through any local machine types. - echo $1 + echo "$1" exit ;; * ) @@ -111,1220 +110,1168 @@ exit 1;; esac -# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). -# Here we must recognize all the valid KERNEL-OS combinations. -maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` -case $maybe_os in - nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ - linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ - knetbsd*-gnu* | netbsd*-gnu* | \ - kopensolaris*-gnu* | \ - storm-chaos* | os2-emx* | rtmk-nova*) - os=-$maybe_os - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` - ;; - android-linux) - os=-linux-android - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown - ;; - *) - basic_machine=`echo $1 | sed 's/-[^-]*$//'` - if [ $basic_machine != $1 ] - then os=`echo $1 | sed 's/.*-/-/'` - else os=; fi - ;; -esac - -### Let's recognize common machines as not being operating systems so -### that things like config.sub decstation-3100 work. We also -### recognize some manufacturers as not being operating systems, so we -### can provide default operating systems below. -case $os in - -sun*os*) - # Prevent following clause from handling this invalid input. - ;; - -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ - -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ - -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ - -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ - -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ - -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ - -apple | -axis | -knuth | -cray | -microblaze*) - os= - basic_machine=$1 - ;; - -bluegene*) - os=-cnk - ;; - -sim | -cisco | -oki | -wec | -winbond) - os= - basic_machine=$1 - ;; - -scout) - ;; - -wrs) - os=-vxworks - basic_machine=$1 - ;; - -chorusos*) - os=-chorusos - basic_machine=$1 - ;; - -chorusrdb) - os=-chorusrdb - basic_machine=$1 - ;; - -hiux*) - os=-hiuxwe2 - ;; - -sco6) - os=-sco5v6 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco5) - os=-sco3.2v5 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco4) - os=-sco3.2v4 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2.[4-9]*) - os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2v[4-9]*) - # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco5v6*) - # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco*) - os=-sco3.2v2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -udk*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -isc) - os=-isc2.2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -clix*) - basic_machine=clipper-intergraph - ;; - -isc*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -lynx*178) - os=-lynxos178 - ;; - -lynx*5) - os=-lynxos5 - ;; - -lynx*) - os=-lynxos +# Split fields of configuration type +# shellcheck disable=SC2162 +IFS="-" read field1 field2 field3 field4 <&2 + exit 1 ;; - -ptx*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + *-*-*-*) + basic_machine=$field1-$field2 + basic_os=$field3-$field4 ;; - -windowsnt*) - os=`echo $os | sed -e 's/windowsnt/winnt/'` + *-*-*) + # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two + # parts + maybe_os=$field2-$field3 + case $maybe_os in + nto-qnx* | linux-* | uclinux-uclibc* \ + | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ + | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ + | storm-chaos* | os2-emx* | rtmk-nova*) + basic_machine=$field1 + basic_os=$maybe_os + ;; + android-linux) + basic_machine=$field1-unknown + basic_os=linux-android + ;; + *) + basic_machine=$field1-$field2 + basic_os=$field3 + ;; + esac ;; - -psos*) - os=-psos + *-*) + # A lone config we happen to match not fitting any pattern + case $field1-$field2 in + decstation-3100) + basic_machine=mips-dec + basic_os= + ;; + *-*) + # Second component is usually, but not always the OS + case $field2 in + # Prevent following clause from handling this valid os + sun*os*) + basic_machine=$field1 + basic_os=$field2 + ;; + # Manufacturers + dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ + | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ + | unicom* | ibm* | next | hp | isi* | apollo | altos* \ + | convergent* | ncr* | news | 32* | 3600* | 3100* \ + | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ + | ultra | tti* | harris | dolphin | highlevel | gould \ + | cbm | ns | masscomp | apple | axis | knuth | cray \ + | microblaze* | sim | cisco \ + | oki | wec | wrs | winbond) + basic_machine=$field1-$field2 + basic_os= + ;; + *) + basic_machine=$field1 + basic_os=$field2 + ;; + esac + ;; + esac ;; - -mint | -mint[0-9]*) - basic_machine=m68k-atari - os=-mint + *) + # Convert single-component short-hands not valid as part of + # multi-component configurations. + case $field1 in + 386bsd) + basic_machine=i386-pc + basic_os=bsd + ;; + a29khif) + basic_machine=a29k-amd + basic_os=udi + ;; + adobe68k) + basic_machine=m68010-adobe + basic_os=scout + ;; + alliant) + basic_machine=fx80-alliant + basic_os= + ;; + altos | altos3068) + basic_machine=m68k-altos + basic_os= + ;; + am29k) + basic_machine=a29k-none + basic_os=bsd + ;; + amdahl) + basic_machine=580-amdahl + basic_os=sysv + ;; + amiga) + basic_machine=m68k-unknown + basic_os= + ;; + amigaos | amigados) + basic_machine=m68k-unknown + basic_os=amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + basic_os=sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + basic_os=sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + basic_os=bsd + ;; + aros) + basic_machine=i386-pc + basic_os=aros + ;; + aux) + basic_machine=m68k-apple + basic_os=aux + ;; + balance) + basic_machine=ns32k-sequent + basic_os=dynix + ;; + blackfin) + basic_machine=bfin-unknown + basic_os=linux + ;; + cegcc) + basic_machine=arm-unknown + basic_os=cegcc + ;; + convex-c1) + basic_machine=c1-convex + basic_os=bsd + ;; + convex-c2) + basic_machine=c2-convex + basic_os=bsd + ;; + convex-c32) + basic_machine=c32-convex + basic_os=bsd + ;; + convex-c34) + basic_machine=c34-convex + basic_os=bsd + ;; + convex-c38) + basic_machine=c38-convex + basic_os=bsd + ;; + cray) + basic_machine=j90-cray + basic_os=unicos + ;; + crds | unos) + basic_machine=m68k-crds + basic_os= + ;; + da30) + basic_machine=m68k-da30 + basic_os= + ;; + decstation | pmax | pmin | dec3100 | decstatn) + basic_machine=mips-dec + basic_os= + ;; + delta88) + basic_machine=m88k-motorola + basic_os=sysv3 + ;; + dicos) + basic_machine=i686-pc + basic_os=dicos + ;; + djgpp) + basic_machine=i586-pc + basic_os=msdosdjgpp + ;; + ebmon29k) + basic_machine=a29k-amd + basic_os=ebmon + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + basic_os=ose + ;; + gmicro) + basic_machine=tron-gmicro + basic_os=sysv + ;; + go32) + basic_machine=i386-pc + basic_os=go32 + ;; + h8300hms) + basic_machine=h8300-hitachi + basic_os=hms + ;; + h8300xray) + basic_machine=h8300-hitachi + basic_os=xray + ;; + h8500hms) + basic_machine=h8500-hitachi + basic_os=hms + ;; + harris) + basic_machine=m88k-harris + basic_os=sysv3 + ;; + hp300 | hp300hpux) + basic_machine=m68k-hp + basic_os=hpux + ;; + hp300bsd) + basic_machine=m68k-hp + basic_os=bsd + ;; + hppaosf) + basic_machine=hppa1.1-hp + basic_os=osf + ;; + hppro) + basic_machine=hppa1.1-hp + basic_os=proelf + ;; + i386mach) + basic_machine=i386-mach + basic_os=mach + ;; + isi68 | isi) + basic_machine=m68k-isi + basic_os=sysv + ;; + m68knommu) + basic_machine=m68k-unknown + basic_os=linux + ;; + magnum | m3230) + basic_machine=mips-mips + basic_os=sysv + ;; + merlin) + basic_machine=ns32k-utek + basic_os=sysv + ;; + mingw64) + basic_machine=x86_64-pc + basic_os=mingw64 + ;; + mingw32) + basic_machine=i686-pc + basic_os=mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + basic_os=mingw32ce + ;; + monitor) + basic_machine=m68k-rom68k + basic_os=coff + ;; + morphos) + basic_machine=powerpc-unknown + basic_os=morphos + ;; + moxiebox) + basic_machine=moxie-unknown + basic_os=moxiebox + ;; + msdos) + basic_machine=i386-pc + basic_os=msdos + ;; + msys) + basic_machine=i686-pc + basic_os=msys + ;; + mvs) + basic_machine=i370-ibm + basic_os=mvs + ;; + nacl) + basic_machine=le32-unknown + basic_os=nacl + ;; + ncr3000) + basic_machine=i486-ncr + basic_os=sysv4 + ;; + netbsd386) + basic_machine=i386-pc + basic_os=netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + basic_os=linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + basic_os=newsos + ;; + news1000) + basic_machine=m68030-sony + basic_os=newsos + ;; + necv70) + basic_machine=v70-nec + basic_os=sysv + ;; + nh3000) + basic_machine=m68k-harris + basic_os=cxux + ;; + nh[45]000) + basic_machine=m88k-harris + basic_os=cxux + ;; + nindy960) + basic_machine=i960-intel + basic_os=nindy + ;; + mon960) + basic_machine=i960-intel + basic_os=mon960 + ;; + nonstopux) + basic_machine=mips-compaq + basic_os=nonstopux + ;; + os400) + basic_machine=powerpc-ibm + basic_os=os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + basic_os=ose + ;; + os68k) + basic_machine=m68k-none + basic_os=os68k + ;; + paragon) + basic_machine=i860-intel + basic_os=osf + ;; + parisc) + basic_machine=hppa-unknown + basic_os=linux + ;; + psp) + basic_machine=mipsallegrexel-sony + basic_os=psp + ;; + pw32) + basic_machine=i586-unknown + basic_os=pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + basic_os=rdos + ;; + rdos32) + basic_machine=i386-pc + basic_os=rdos + ;; + rom68k) + basic_machine=m68k-rom68k + basic_os=coff + ;; + sa29200) + basic_machine=a29k-amd + basic_os=udi + ;; + sei) + basic_machine=mips-sei + basic_os=seiux + ;; + sequent) + basic_machine=i386-sequent + basic_os= + ;; + sps7) + basic_machine=m68k-bull + basic_os=sysv2 + ;; + st2000) + basic_machine=m68k-tandem + basic_os= + ;; + stratus) + basic_machine=i860-stratus + basic_os=sysv4 + ;; + sun2) + basic_machine=m68000-sun + basic_os= + ;; + sun2os3) + basic_machine=m68000-sun + basic_os=sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + basic_os=sunos4 + ;; + sun3) + basic_machine=m68k-sun + basic_os= + ;; + sun3os3) + basic_machine=m68k-sun + basic_os=sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + basic_os=sunos4 + ;; + sun4) + basic_machine=sparc-sun + basic_os= + ;; + sun4os3) + basic_machine=sparc-sun + basic_os=sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + basic_os=sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + basic_os=solaris2 + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + basic_os= + ;; + sv1) + basic_machine=sv1-cray + basic_os=unicos + ;; + symmetry) + basic_machine=i386-sequent + basic_os=dynix + ;; + t3e) + basic_machine=alphaev5-cray + basic_os=unicos + ;; + t90) + basic_machine=t90-cray + basic_os=unicos + ;; + toad1) + basic_machine=pdp10-xkl + basic_os=tops20 + ;; + tpf) + basic_machine=s390x-ibm + basic_os=tpf + ;; + udi29k) + basic_machine=a29k-amd + basic_os=udi + ;; + ultra3) + basic_machine=a29k-nyu + basic_os=sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + basic_os=none + ;; + vaxv) + basic_machine=vax-dec + basic_os=sysv + ;; + vms) + basic_machine=vax-dec + basic_os=vms + ;; + vsta) + basic_machine=i386-pc + basic_os=vsta + ;; + vxworks960) + basic_machine=i960-wrs + basic_os=vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + basic_os=vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + basic_os=vxworks + ;; + xbox) + basic_machine=i686-pc + basic_os=mingw32 + ;; + ymp) + basic_machine=ymp-cray + basic_os=unicos + ;; + *) + basic_machine=$1 + basic_os= + ;; + esac ;; esac -# Decode aliases for certain CPU-COMPANY combinations. +# Decode 1-component or ad-hoc basic machines case $basic_machine in - # Recognize the basic CPU types without company name. - # Some are omitted here because they have special meanings below. - 1750a | 580 \ - | a29k \ - | aarch64 | aarch64_be \ - | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ - | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ - | am33_2.0 \ - | arc | arceb \ - | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ - | avr | avr32 \ - | be32 | be64 \ - | bfin \ - | c4x | c8051 | clipper \ - | d10v | d30v | dlx | dsp16xx \ - | epiphany \ - | fido | fr30 | frv \ - | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ - | hexagon \ - | i370 | i860 | i960 | ia64 \ - | ip2k | iq2000 \ - | k1om \ - | le32 | le64 \ - | lm32 \ - | m32c | m32r | m32rle | m68000 | m68k | m88k \ - | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ - | mips | mipsbe | mipseb | mipsel | mipsle \ - | mips16 \ - | mips64 | mips64el \ - | mips64octeon | mips64octeonel \ - | mips64orion | mips64orionel \ - | mips64r5900 | mips64r5900el \ - | mips64vr | mips64vrel \ - | mips64vr4100 | mips64vr4100el \ - | mips64vr4300 | mips64vr4300el \ - | mips64vr5000 | mips64vr5000el \ - | mips64vr5900 | mips64vr5900el \ - | mipsisa32 | mipsisa32el \ - | mipsisa32r2 | mipsisa32r2el \ - | mipsisa32r6 | mipsisa32r6el \ - | mipsisa64 | mipsisa64el \ - | mipsisa64r2 | mipsisa64r2el \ - | mipsisa64r6 | mipsisa64r6el \ - | mipsisa64sb1 | mipsisa64sb1el \ - | mipsisa64sr71k | mipsisa64sr71kel \ - | mipsr5900 | mipsr5900el \ - | mipstx39 | mipstx39el \ - | mn10200 | mn10300 \ - | moxie \ - | mt \ - | msp430 \ - | nds32 | nds32le | nds32be \ - | nios | nios2 | nios2eb | nios2el \ - | ns16k | ns32k \ - | open8 | or1k | or1knd | or32 \ - | pdp10 | pdp11 | pj | pjl \ - | powerpc | powerpc64 | powerpc64le | powerpcle \ - | pyramid \ - | riscv32 | riscv64 \ - | rl78 | rx \ - | score \ - | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ - | sh64 | sh64le \ - | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ - | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ - | spu \ - | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ - | ubicom32 \ - | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ - | we32k \ - | x86 | xc16x | xstormy16 | xtensa \ - | z8k | z80) - basic_machine=$basic_machine-unknown - ;; - c54x) - basic_machine=tic54x-unknown - ;; - c55x) - basic_machine=tic55x-unknown - ;; - c6x) - basic_machine=tic6x-unknown - ;; - m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) - basic_machine=$basic_machine-unknown - os=-none - ;; - m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) - ;; - ms1) - basic_machine=mt-unknown - ;; - - strongarm | thumb | xscale) - basic_machine=arm-unknown - ;; - xgate) - basic_machine=$basic_machine-unknown - os=-none + # Here we handle the default manufacturer of certain CPU types. It is in + # some cases the only manufacturer, in others, it is the most popular. + w89k) + cpu=hppa1.1 + vendor=winbond ;; - xscaleeb) - basic_machine=armeb-unknown + op50n) + cpu=hppa1.1 + vendor=oki ;; - - xscaleel) - basic_machine=armel-unknown + op60c) + cpu=hppa1.1 + vendor=oki ;; - - # We use `pc' rather than `unknown' - # because (1) that's what they normally are, and - # (2) the word "unknown" tends to confuse beginning users. - i*86 | x86_64) - basic_machine=$basic_machine-pc - ;; - # Object if more than one company name word. - *-*-*) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 + ibm*) + cpu=i370 + vendor=ibm ;; - # Recognize the basic CPU types with company name. - 580-* \ - | a29k-* \ - | aarch64-* | aarch64_be-* \ - | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ - | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ - | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ - | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ - | avr-* | avr32-* \ - | be32-* | be64-* \ - | bfin-* | bs2000-* \ - | c[123]* | c30-* | [cjt]90-* | c4x-* \ - | c8051-* | clipper-* | craynv-* | cydra-* \ - | d10v-* | d30v-* | dlx-* \ - | elxsi-* \ - | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ - | h8300-* | h8500-* \ - | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ - | hexagon-* \ - | i*86-* | i860-* | i960-* | ia64-* \ - | ip2k-* | iq2000-* \ - | k1om-* \ - | le32-* | le64-* \ - | lm32-* \ - | m32c-* | m32r-* | m32rle-* \ - | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ - | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ - | microblaze-* | microblazeel-* \ - | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ - | mips16-* \ - | mips64-* | mips64el-* \ - | mips64octeon-* | mips64octeonel-* \ - | mips64orion-* | mips64orionel-* \ - | mips64r5900-* | mips64r5900el-* \ - | mips64vr-* | mips64vrel-* \ - | mips64vr4100-* | mips64vr4100el-* \ - | mips64vr4300-* | mips64vr4300el-* \ - | mips64vr5000-* | mips64vr5000el-* \ - | mips64vr5900-* | mips64vr5900el-* \ - | mipsisa32-* | mipsisa32el-* \ - | mipsisa32r2-* | mipsisa32r2el-* \ - | mipsisa32r6-* | mipsisa32r6el-* \ - | mipsisa64-* | mipsisa64el-* \ - | mipsisa64r2-* | mipsisa64r2el-* \ - | mipsisa64r6-* | mipsisa64r6el-* \ - | mipsisa64sb1-* | mipsisa64sb1el-* \ - | mipsisa64sr71k-* | mipsisa64sr71kel-* \ - | mipsr5900-* | mipsr5900el-* \ - | mipstx39-* | mipstx39el-* \ - | mmix-* \ - | mt-* \ - | msp430-* \ - | nds32-* | nds32le-* | nds32be-* \ - | nios-* | nios2-* | nios2eb-* | nios2el-* \ - | none-* | np1-* | ns16k-* | ns32k-* \ - | open8-* \ - | or1k*-* \ - | orion-* \ - | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ - | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ - | pyramid-* \ - | rl78-* | romp-* | rs6000-* | rx-* \ - | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ - | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ - | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ - | sparclite-* \ - | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ - | tahoe-* \ - | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ - | tile*-* \ - | tron-* \ - | ubicom32-* \ - | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ - | vax-* \ - | we32k-* \ - | x86-* | x86_64-* | xc16x-* | xps100-* \ - | xstormy16-* | xtensa*-* \ - | ymp-* \ - | z8k-* | z80-*) - ;; - # Recognize the basic CPU types without company name, with glob match. - xtensa*) - basic_machine=$basic_machine-unknown + orion105) + cpu=clipper + vendor=highlevel + ;; + mac | mpw | mac-mpw) + cpu=m68k + vendor=apple + ;; + pmac | pmac-mpw) + cpu=powerpc + vendor=apple ;; + # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. - 386bsd) - basic_machine=i386-unknown - os=-bsd - ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) - basic_machine=m68000-att + cpu=m68000 + vendor=att ;; 3b*) - basic_machine=we32k-att - ;; - a29khif) - basic_machine=a29k-amd - os=-udi - ;; - abacus) - basic_machine=abacus-unknown - ;; - adobe68k) - basic_machine=m68010-adobe - os=-scout - ;; - alliant | fx80) - basic_machine=fx80-alliant - ;; - altos | altos3068) - basic_machine=m68k-altos - ;; - am29k) - basic_machine=a29k-none - os=-bsd - ;; - amd64) - basic_machine=x86_64-pc - ;; - amd64-*) - basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - amdahl) - basic_machine=580-amdahl - os=-sysv - ;; - amiga | amiga-*) - basic_machine=m68k-unknown - ;; - amigaos | amigados) - basic_machine=m68k-unknown - os=-amigaos - ;; - amigaunix | amix) - basic_machine=m68k-unknown - os=-sysv4 - ;; - apollo68) - basic_machine=m68k-apollo - os=-sysv - ;; - apollo68bsd) - basic_machine=m68k-apollo - os=-bsd - ;; - aros) - basic_machine=i386-pc - os=-aros - ;; - aux) - basic_machine=m68k-apple - os=-aux - ;; - balance) - basic_machine=ns32k-sequent - os=-dynix - ;; - blackfin) - basic_machine=bfin-unknown - os=-linux - ;; - blackfin-*) - basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux + cpu=we32k + vendor=att ;; bluegene*) - basic_machine=powerpc-ibm - os=-cnk - ;; - c54x-*) - basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c55x-*) - basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c6x-*) - basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c90) - basic_machine=c90-cray - os=-unicos - ;; - cegcc) - basic_machine=arm-unknown - os=-cegcc - ;; - convex-c1) - basic_machine=c1-convex - os=-bsd - ;; - convex-c2) - basic_machine=c2-convex - os=-bsd - ;; - convex-c32) - basic_machine=c32-convex - os=-bsd - ;; - convex-c34) - basic_machine=c34-convex - os=-bsd - ;; - convex-c38) - basic_machine=c38-convex - os=-bsd - ;; - cray | j90) - basic_machine=j90-cray - os=-unicos - ;; - craynv) - basic_machine=craynv-cray - os=-unicosmp - ;; - cr16 | cr16-*) - basic_machine=cr16-unknown - os=-elf - ;; - crds | unos) - basic_machine=m68k-crds - ;; - crisv32 | crisv32-* | etraxfs*) - basic_machine=crisv32-axis - ;; - cris | cris-* | etrax*) - basic_machine=cris-axis - ;; - crx) - basic_machine=crx-unknown - os=-elf - ;; - da30 | da30-*) - basic_machine=m68k-da30 - ;; - decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) - basic_machine=mips-dec + cpu=powerpc + vendor=ibm + basic_os=cnk ;; decsystem10* | dec10*) - basic_machine=pdp10-dec - os=-tops10 + cpu=pdp10 + vendor=dec + basic_os=tops10 ;; decsystem20* | dec20*) - basic_machine=pdp10-dec - os=-tops20 + cpu=pdp10 + vendor=dec + basic_os=tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) - basic_machine=m68k-motorola + cpu=m68k + vendor=motorola ;; - delta88) - basic_machine=m88k-motorola - os=-sysv3 - ;; - dicos) - basic_machine=i686-pc - os=-dicos - ;; - djgpp) - basic_machine=i586-pc - os=-msdosdjgpp - ;; - dpx20 | dpx20-*) - basic_machine=rs6000-bull - os=-bosx - ;; - dpx2* | dpx2*-bull) - basic_machine=m68k-bull - os=-sysv3 - ;; - ebmon29k) - basic_machine=a29k-amd - os=-ebmon - ;; - elxsi) - basic_machine=elxsi-elxsi - os=-bsd + dpx2*) + cpu=m68k + vendor=bull + basic_os=sysv3 ;; encore | umax | mmax) - basic_machine=ns32k-encore + cpu=ns32k + vendor=encore ;; - es1800 | OSE68k | ose68k | ose | OSE) - basic_machine=m68k-ericsson - os=-ose + elxsi) + cpu=elxsi + vendor=elxsi + basic_os=${basic_os:-bsd} ;; fx2800) - basic_machine=i860-alliant + cpu=i860 + vendor=alliant ;; genix) - basic_machine=ns32k-ns - ;; - gmicro) - basic_machine=tron-gmicro - os=-sysv - ;; - go32) - basic_machine=i386-pc - os=-go32 + cpu=ns32k + vendor=ns ;; h3050r* | hiux*) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - h8300hms) - basic_machine=h8300-hitachi - os=-hms - ;; - h8300xray) - basic_machine=h8300-hitachi - os=-xray - ;; - h8500hms) - basic_machine=h8500-hitachi - os=-hms - ;; - harris) - basic_machine=m88k-harris - os=-sysv3 - ;; - hp300-*) - basic_machine=m68k-hp - ;; - hp300bsd) - basic_machine=m68k-hp - os=-bsd - ;; - hp300hpux) - basic_machine=m68k-hp - os=-hpux + cpu=hppa1.1 + vendor=hitachi + basic_os=hiuxwe2 ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) - basic_machine=hppa1.0-hp + cpu=hppa1.0 + vendor=hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) - basic_machine=m68000-hp + cpu=m68000 + vendor=hp ;; hp9k3[2-9][0-9]) - basic_machine=m68k-hp + cpu=m68k + vendor=hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) - basic_machine=hppa1.0-hp + cpu=hppa1.0 + vendor=hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) - basic_machine=hppa1.1-hp + cpu=hppa1.1 + vendor=hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp + cpu=hppa1.1 + vendor=hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp + cpu=hppa1.1 + vendor=hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) - basic_machine=hppa1.1-hp + cpu=hppa1.1 + vendor=hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hppa-next) - os=-nextstep3 - ;; - hppaosf) - basic_machine=hppa1.1-hp - os=-osf - ;; - hppro) - basic_machine=hppa1.1-hp - os=-proelf - ;; - i370-ibm* | ibm*) - basic_machine=i370-ibm + cpu=hppa1.0 + vendor=hp ;; i*86v32) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv32 + cpu=$(echo "$1" | sed -e 's/86.*/86/') + vendor=pc + basic_os=sysv32 ;; i*86v4*) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv4 + cpu=$(echo "$1" | sed -e 's/86.*/86/') + vendor=pc + basic_os=sysv4 ;; i*86v) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv + cpu=$(echo "$1" | sed -e 's/86.*/86/') + vendor=pc + basic_os=sysv ;; i*86sol2) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-solaris2 - ;; - i386mach) - basic_machine=i386-mach - os=-mach - ;; - i386-vsta | vsta) - basic_machine=i386-unknown - os=-vsta + cpu=$(echo "$1" | sed -e 's/86.*/86/') + vendor=pc + basic_os=solaris2 + ;; + j90 | j90-cray) + cpu=j90 + vendor=cray + basic_os=${basic_os:-unicos} ;; iris | iris4d) - basic_machine=mips-sgi - case $os in - -irix*) + cpu=mips + vendor=sgi + case $basic_os in + irix*) ;; *) - os=-irix4 + basic_os=irix4 ;; esac ;; - isi68 | isi) - basic_machine=m68k-isi - os=-sysv - ;; - m68knommu) - basic_machine=m68k-unknown - os=-linux - ;; - m68knommu-*) - basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux - ;; - m88k-omron*) - basic_machine=m88k-omron - ;; - magnum | m3230) - basic_machine=mips-mips - os=-sysv - ;; - merlin) - basic_machine=ns32k-utek - os=-sysv - ;; - microblaze*) - basic_machine=microblaze-xilinx - ;; - mingw64) - basic_machine=x86_64-pc - os=-mingw64 - ;; - mingw32) - basic_machine=i686-pc - os=-mingw32 - ;; - mingw32ce) - basic_machine=arm-unknown - os=-mingw32ce - ;; miniframe) - basic_machine=m68000-convergent - ;; - *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; - mips3*-*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` - ;; - mips3*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + cpu=m68000 + vendor=convergent ;; - monitor) - basic_machine=m68k-rom68k - os=-coff - ;; - morphos) - basic_machine=powerpc-unknown - os=-morphos - ;; - moxiebox) - basic_machine=moxie-unknown - os=-moxiebox - ;; - msdos) - basic_machine=i386-pc - os=-msdos - ;; - ms1-*) - basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` - ;; - msys) - basic_machine=i686-pc - os=-msys - ;; - mvs) - basic_machine=i370-ibm - os=-mvs - ;; - nacl) - basic_machine=le32-unknown - os=-nacl - ;; - ncr3000) - basic_machine=i486-ncr - os=-sysv4 - ;; - netbsd386) - basic_machine=i386-unknown - os=-netbsd - ;; - netwinder) - basic_machine=armv4l-rebel - os=-linux - ;; - news | news700 | news800 | news900) - basic_machine=m68k-sony - os=-newsos - ;; - news1000) - basic_machine=m68030-sony - os=-newsos + *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) + cpu=m68k + vendor=atari + basic_os=mint ;; news-3600 | risc-news) - basic_machine=mips-sony - os=-newsos - ;; - necv70) - basic_machine=v70-nec - os=-sysv - ;; - next | m*-next ) - basic_machine=m68k-next - case $os in - -nextstep* ) + cpu=mips + vendor=sony + basic_os=newsos + ;; + next | m*-next) + cpu=m68k + vendor=next + case $basic_os in + openstep*) + ;; + nextstep*) ;; - -ns2*) - os=-nextstep2 + ns2*) + basic_os=nextstep2 ;; *) - os=-nextstep3 + basic_os=nextstep3 ;; esac ;; - nh3000) - basic_machine=m68k-harris - os=-cxux - ;; - nh[45]000) - basic_machine=m88k-harris - os=-cxux - ;; - nindy960) - basic_machine=i960-intel - os=-nindy - ;; - mon960) - basic_machine=i960-intel - os=-mon960 - ;; - nonstopux) - basic_machine=mips-compaq - os=-nonstopux - ;; np1) - basic_machine=np1-gould - ;; - neo-tandem) - basic_machine=neo-tandem - ;; - nse-tandem) - basic_machine=nse-tandem - ;; - nsr-tandem) - basic_machine=nsr-tandem + cpu=np1 + vendor=gould ;; op50n-* | op60c-*) - basic_machine=hppa1.1-oki - os=-proelf - ;; - openrisc | openrisc-*) - basic_machine=or32-unknown - ;; - os400) - basic_machine=powerpc-ibm - os=-os400 - ;; - OSE68000 | ose68000) - basic_machine=m68000-ericsson - os=-ose - ;; - os68k) - basic_machine=m68k-none - os=-os68k + cpu=hppa1.1 + vendor=oki + basic_os=proelf ;; pa-hitachi) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - paragon) - basic_machine=i860-intel - os=-osf - ;; - parisc) - basic_machine=hppa-unknown - os=-linux - ;; - parisc-*) - basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux + cpu=hppa1.1 + vendor=hitachi + basic_os=hiuxwe2 ;; pbd) - basic_machine=sparc-tti + cpu=sparc + vendor=tti ;; pbb) - basic_machine=m68k-tti + cpu=m68k + vendor=tti ;; - pc532 | pc532-*) - basic_machine=ns32k-pc532 - ;; - pc98) - basic_machine=i386-pc - ;; - pc98-*) - basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentium | p5 | k5 | k6 | nexgen | viac3) - basic_machine=i586-pc - ;; - pentiumpro | p6 | 6x86 | athlon | athlon_*) - basic_machine=i686-pc - ;; - pentiumii | pentium2 | pentiumiii | pentium3) - basic_machine=i686-pc - ;; - pentium4) - basic_machine=i786-pc - ;; - pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) - basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumpro-* | p6-* | 6x86-* | athlon-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentium4-*) - basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + pc532) + cpu=ns32k + vendor=pc532 ;; pn) - basic_machine=pn-gould - ;; - power) basic_machine=power-ibm + cpu=pn + vendor=gould ;; - ppc | ppcbe) basic_machine=powerpc-unknown - ;; - ppc-* | ppcbe-*) - basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppcle | powerpclittle | ppc-le | powerpc-little) - basic_machine=powerpcle-unknown - ;; - ppcle-* | powerpclittle-*) - basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppc64) basic_machine=powerpc64-unknown - ;; - ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppc64le | powerpc64little | ppc64-le | powerpc64-little) - basic_machine=powerpc64le-unknown - ;; - ppc64le-* | powerpc64little-*) - basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + power) + cpu=power + vendor=ibm ;; ps2) - basic_machine=i386-ibm - ;; - pw32) - basic_machine=i586-unknown - os=-pw32 - ;; - rdos | rdos64) - basic_machine=x86_64-pc - os=-rdos - ;; - rdos32) - basic_machine=i386-pc - os=-rdos - ;; - rom68k) - basic_machine=m68k-rom68k - os=-coff + cpu=i386 + vendor=ibm ;; rm[46]00) - basic_machine=mips-siemens + cpu=mips + vendor=siemens ;; rtpc | rtpc-*) - basic_machine=romp-ibm + cpu=romp + vendor=ibm ;; - s390 | s390-*) - basic_machine=s390-ibm + sde) + cpu=mipsisa32 + vendor=sde + basic_os=${basic_os:-elf} + ;; + simso-wrs) + cpu=sparclite + vendor=wrs + basic_os=vxworks ;; - s390x | s390x-*) - basic_machine=s390x-ibm + tower | tower-32) + cpu=m68k + vendor=ncr ;; - sa29200) - basic_machine=a29k-amd - os=-udi + vpp*|vx|vx-*) + cpu=f301 + vendor=fujitsu ;; - sb1) - basic_machine=mipsisa64sb1-unknown + w65) + cpu=w65 + vendor=wdc ;; - sb1el) - basic_machine=mipsisa64sb1el-unknown + w89k-*) + cpu=hppa1.1 + vendor=winbond + basic_os=proelf ;; - sde) - basic_machine=mipsisa32-sde - os=-elf + none) + cpu=none + vendor=none ;; - sei) - basic_machine=mips-sei - os=-seiux + leon|leon[3-9]) + cpu=sparc + vendor=$basic_machine + ;; + leon-*|leon[3-9]-*) + cpu=sparc + vendor=$(echo "$basic_machine" | sed 's/-.*//') + ;; + + *-*) + # shellcheck disable=SC2162 + IFS="-" read cpu vendor <&2 - exit 1 + # Recognize the canonical CPU types that are allowed with any + # company name. + case $cpu in + 1750a | 580 \ + | a29k \ + | aarch64 | aarch64_be \ + | abacus \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] \ + | alphapca5[67] | alpha64pca5[67] \ + | am33_2.0 \ + | amdgcn \ + | arc | arceb \ + | arm | arm[lb]e | arme[lb] | armv* \ + | avr | avr32 \ + | asmjs \ + | ba \ + | be32 | be64 \ + | bfin | bpf | bs2000 \ + | c[123]* | c30 | [cjt]90 | c4x \ + | c8051 | clipper | craynv | csky | cydra \ + | d10v | d30v | dlx | dsp16xx \ + | e2k | elxsi | epiphany \ + | f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \ + | h8300 | h8500 \ + | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | hexagon \ + | i370 | i*86 | i860 | i960 | ia16 | ia64 \ + | ip2k | iq2000 \ + | k1om \ + | le32 | le64 \ + | lm32 \ + | m32c | m32r | m32rle \ + | m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \ + | m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \ + | m88110 | m88k | maxq | mb | mcore | mep | metag \ + | microblaze | microblazeel \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64eb | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa32r6 | mipsisa32r6el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64r6 | mipsisa64r6el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipsr5900 | mipsr5900el \ + | mipstx39 | mipstx39el \ + | mmix \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nfp \ + | nios | nios2 | nios2eb | nios2el \ + | none | np1 | ns16k | ns32k | nvptx \ + | open8 \ + | or1k* \ + | or32 \ + | orion \ + | picochip \ + | pdp10 | pdp11 | pj | pjl | pn | power \ + | powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \ + | pru \ + | pyramid \ + | riscv | riscv32 | riscv64 \ + | rl78 | romp | rs6000 | rx \ + | s390 | s390x \ + | score \ + | sh | shl \ + | sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \ + | sh[1234]e[lb] | sh[12345][lb]e | sh[23]ele | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet \ + | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \ + | spu \ + | tahoe \ + | thumbv7* \ + | tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \ + | tron \ + | ubicom32 \ + | v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \ + | vax \ + | visium \ + | w65 \ + | wasm32 | wasm64 \ + | we32k \ + | x86 | x86_64 | xc16x | xgate | xps100 \ + | xstormy16 | xtensa* \ + | ymp \ + | z8k | z80) + ;; + + *) + echo Invalid configuration \`"$1"\': machine \`"$cpu-$vendor"\' not recognized 1>&2 + exit 1 + ;; + esac ;; esac # Here we canonicalize certain aliases for manufacturers. -case $basic_machine in - *-digital*) - basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` +case $vendor in + digital*) + vendor=dec ;; - *-commodore*) - basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + commodore*) + vendor=cbm ;; *) ;; @@ -1332,200 +1279,213 @@ # Decode manufacturer-specific aliases for certain operating systems. -if [ x"$os" != x"" ] +if test x$basic_os != x then + +# First recognize some ad-hoc caes, or perhaps split kernel-os, or else just +# set os. +case $basic_os in + gnu/linux*) + kernel=linux + os=$(echo $basic_os | sed -e 's|gnu/linux|gnu|') + ;; + os2-emx) + kernel=os2 + os=$(echo $basic_os | sed -e 's|os2-emx|emx|') + ;; + nto-qnx*) + kernel=nto + os=$(echo $basic_os | sed -e 's|nto-qnx|qnx|') + ;; + *-*) + # shellcheck disable=SC2162 + IFS="-" read kernel os <&2 - exit 1 + # No normalization, but not necessarily accepted, that comes below. ;; esac + else # Here we handle the default operating systems that come with various machines. @@ -1538,261 +1498,356 @@ # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. -case $basic_machine in +kernel= +case $cpu-$vendor in score-*) - os=-elf + os=elf ;; spu-*) - os=-elf + os=elf ;; *-acorn) - os=-riscix1.2 + os=riscix1.2 ;; arm*-rebel) - os=-linux + kernel=linux + os=gnu ;; arm*-semi) - os=-aout + os=aout ;; c4x-* | tic4x-*) - os=-coff + os=coff ;; c8051-*) - os=-elf + os=elf + ;; + clipper-intergraph) + os=clix ;; hexagon-*) - os=-elf + os=elf ;; tic54x-*) - os=-coff + os=coff ;; tic55x-*) - os=-coff + os=coff ;; tic6x-*) - os=-coff + os=coff ;; # This must come before the *-dec entry. pdp10-*) - os=-tops20 + os=tops20 ;; pdp11-*) - os=-none + os=none ;; *-dec | vax-*) - os=-ultrix4.2 + os=ultrix4.2 ;; m68*-apollo) - os=-domain + os=domain ;; i386-sun) - os=-sunos4.0.2 + os=sunos4.0.2 ;; m68000-sun) - os=-sunos3 + os=sunos3 ;; m68*-cisco) - os=-aout + os=aout ;; mep-*) - os=-elf + os=elf ;; mips*-cisco) - os=-elf + os=elf ;; mips*-*) - os=-elf + os=elf ;; or32-*) - os=-coff + os=coff ;; *-tti) # must be before sparc entry or we get the wrong os. - os=-sysv3 + os=sysv3 ;; sparc-* | *-sun) - os=-sunos4.1.1 + os=sunos4.1.1 ;; - *-be) - os=-beos + pru-*) + os=elf ;; - *-haiku) - os=-haiku + *-be) + os=beos ;; *-ibm) - os=-aix + os=aix ;; *-knuth) - os=-mmixware + os=mmixware ;; *-wec) - os=-proelf + os=proelf ;; *-winbond) - os=-proelf + os=proelf ;; *-oki) - os=-proelf + os=proelf ;; *-hp) - os=-hpux + os=hpux ;; *-hitachi) - os=-hiux + os=hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) - os=-sysv + os=sysv ;; *-cbm) - os=-amigaos + os=amigaos ;; *-dg) - os=-dgux + os=dgux ;; *-dolphin) - os=-sysv3 + os=sysv3 ;; m68k-ccur) - os=-rtu + os=rtu ;; m88k-omron*) - os=-luna + os=luna ;; - *-next ) - os=-nextstep + *-next) + os=nextstep ;; *-sequent) - os=-ptx + os=ptx ;; *-crds) - os=-unos + os=unos ;; *-ns) - os=-genix + os=genix ;; i370-*) - os=-mvs - ;; - *-next) - os=-nextstep3 + os=mvs ;; *-gould) - os=-sysv + os=sysv ;; *-highlevel) - os=-bsd + os=bsd ;; *-encore) - os=-bsd + os=bsd ;; *-sgi) - os=-irix + os=irix ;; *-siemens) - os=-sysv4 + os=sysv4 ;; *-masscomp) - os=-rtu + os=rtu ;; f30[01]-fujitsu | f700-fujitsu) - os=-uxpv + os=uxpv ;; *-rom68k) - os=-coff + os=coff ;; *-*bug) - os=-coff + os=coff ;; *-apple) - os=-macos + os=macos ;; *-atari*) - os=-mint + os=mint + ;; + *-wrs) + os=vxworks ;; *) - os=-none + os=none ;; esac + fi +# Now, validate our (potentially fixed-up) OS. +case $os in + # Sometimes we do "kernel-abi", so those need to count as OSes. + musl* | newlib* | uclibc*) + ;; + # Likewise for "kernel-libc" + eabi | eabihf | gnueabi | gnueabihf) + ;; + # Now accept the basic system types. + # The portable systems comes first. + # Each alternative MUST end in a * to match a version number. + gnu* | android* | bsd* | mach* | minix* | genix* | ultrix* | irix* \ + | *vms* | esix* | aix* | cnk* | sunos | sunos[34]* \ + | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ + | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ + | hiux* | abug | nacl* | netware* | windows* \ + | os9* | macos* | osx* | ios* \ + | mpw* | magic* | mmixware* | mon960* | lnews* \ + | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ + | aos* | aros* | cloudabi* | sortix* | twizzler* \ + | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \ + | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \ + | mirbsd* | netbsd* | dicos* | openedition* | ose* \ + | bitrig* | openbsd* | solidbsd* | libertybsd* | os108* \ + | ekkobsd* | freebsd* | riscix* | lynxos* | os400* \ + | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \ + | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \ + | udi* | lites* | ieee* | go32* | aux* | hcos* \ + | chorusrdb* | cegcc* | glidix* \ + | cygwin* | msys* | pe* | moss* | proelf* | rtems* \ + | midipix* | mingw32* | mingw64* | mint* \ + | uxpv* | beos* | mpeix* | udk* | moxiebox* \ + | interix* | uwin* | mks* | rhapsody* | darwin* \ + | openstep* | oskit* | conix* | pw32* | nonstopux* \ + | storm-chaos* | tops10* | tenex* | tops20* | its* \ + | os2* | vos* | palmos* | uclinux* | nucleus* | morphos* \ + | scout* | superux* | sysv* | rtmk* | tpf* | windiss* \ + | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \ + | skyos* | haiku* | rdos* | toppers* | drops* | es* \ + | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ + | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \ + | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx*) + ;; + # This one is extra strict with allowed versions + sco3.2v2 | sco3.2v[4-9]* | sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + ;; + none) + ;; + *) + echo Invalid configuration \`"$1"\': OS \`"$os"\' not recognized 1>&2 + exit 1 + ;; +esac + +# As a final step for OS-related things, validate the OS-kernel combination +# (given a valid OS), if there is a kernel. +case $kernel-$os in + linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* | linux-musl* | linux-uclibc* ) + ;; + uclinux-uclibc* ) + ;; + -dietlibc* | -newlib* | -musl* | -uclibc* ) + # These are just libc implementations, not actual OSes, and thus + # require a kernel. + echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2 + exit 1 + ;; + kfreebsd*-gnu* | kopensolaris*-gnu*) + ;; + nto-qnx*) + ;; + os2-emx) + ;; + *-eabi* | *-gnueabi*) + ;; + -*) + # Blank kernel with real OS is always fine. + ;; + *-*) + echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2 + exit 1 + ;; +esac + # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. -vendor=unknown -case $basic_machine in - *-unknown) - case $os in - -riscix*) +case $vendor in + unknown) + case $cpu-$os in + *-riscix*) vendor=acorn ;; - -sunos*) + *-sunos*) vendor=sun ;; - -cnk*|-aix*) + *-cnk* | *-aix*) vendor=ibm ;; - -beos*) + *-beos*) vendor=be ;; - -hpux*) + *-hpux*) vendor=hp ;; - -mpeix*) + *-mpeix*) vendor=hp ;; - -hiux*) + *-hiux*) vendor=hitachi ;; - -unos*) + *-unos*) vendor=crds ;; - -dgux*) + *-dgux*) vendor=dg ;; - -luna*) + *-luna*) vendor=omron ;; - -genix*) + *-genix*) vendor=ns ;; - -mvs* | -opened*) + *-clix*) + vendor=intergraph + ;; + *-mvs* | *-opened*) + vendor=ibm + ;; + *-os400*) vendor=ibm ;; - -os400*) + s390-* | s390x-*) vendor=ibm ;; - -ptx*) + *-ptx*) vendor=sequent ;; - -tpf*) + *-tpf*) vendor=ibm ;; - -vxsim* | -vxworks* | -windiss*) + *-vxsim* | *-vxworks* | *-windiss*) vendor=wrs ;; - -aux*) + *-aux*) vendor=apple ;; - -hms*) + *-hms*) vendor=hitachi ;; - -mpw* | -macos*) + *-mpw* | *-macos*) vendor=apple ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) vendor=atari ;; - -vos*) + *-vos*) vendor=stratus ;; esac - basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac -echo $basic_machine$os +echo "$cpu-$vendor-${kernel:+$kernel-}$os" exit # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" diff -Nru sdparm-1.10/configure sdparm-1.12/configure --- sdparm-1.10/configure 2015-12-02 00:45:13.000000000 +0000 +++ sdparm-1.12/configure 2020-12-25 01:49:20.000000000 +0000 @@ -1,11 +1,11 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for sdparm 1.10. +# Generated by GNU Autoconf 2.70 for sdparm 1.12. # # Report bugs to . # # -# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# Copyright (C) 1992-1996, 1998-2017, 2020 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation @@ -16,14 +16,16 @@ # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : +as_nop=: +if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 +then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST -else +else $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( @@ -33,46 +35,46 @@ fi + +# Reset variables that may have inherited troublesome values from +# the environment. + +# IFS needs to be set, to space, tab, and newline, in precisely that order. +# (If _AS_PATH_WALK were called with IFS unset, it would have the +# side effect of setting IFS to empty, thus disabling word splitting.) +# Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl -# Printing a long string crashes Solaris 7 /usr/bin/printf. -as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo -# Prefer a ksh shell builtin over an external printf program on Solaris, -# but without wasting forks for bash or zsh. -if test -z "$BASH_VERSION$ZSH_VERSION" \ - && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='print -r --' - as_echo_n='print -rn --' -elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='printf %s\n' - as_echo_n='printf %s' -else - if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then - as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' - as_echo_n='/usr/ucb/echo -n' - else - as_echo_body='eval expr "X$1" : "X\\(.*\\)"' - as_echo_n_body='eval - arg=$1; - case $arg in #( - *"$as_nl"*) - expr "X$arg" : "X\\(.*\\)$as_nl"; - arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; - esac; - expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" - ' - export as_echo_n_body - as_echo_n='sh -c $as_echo_n_body as_echo' - fi - export as_echo_body - as_echo='sh -c $as_echo_body as_echo' -fi +IFS=" "" $as_nl" + +PS1='$ ' +PS2='> ' +PS4='+ ' + +# Ensure predictable behavior from utilities with locale-dependent output. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# We cannot yet rely on "unset" to work, but we need these variables +# to be unset--not just set to an empty or harmless value--now, to +# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct +# also avoids known problems related to "unset" and subshell syntax +# in other old shells (e.g. bash 2.01 and pdksh 5.2.14). +for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH +do eval test \${$as_var+y} \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done + +# Ensure that fds 0, 1, and 2 are open. +if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi +if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then +if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || @@ -81,13 +83,6 @@ fi -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) -IFS=" "" $as_nl" - # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( @@ -96,8 +91,12 @@ for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS @@ -109,30 +108,10 @@ as_myself=$0 fi if test ! -f "$as_myself"; then - $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi -# Unset variables that we do not need and which cause bugs (e.g. in -# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" -# suppresses any "Segmentation fault" message there. '((' could -# trigger a bug in pdksh 5.2.14. -for as_var in BASH_ENV ENV MAIL MAILPATH -do eval test x\${$as_var+set} = xset \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# CDPATH. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. @@ -154,20 +133,22 @@ exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 -as_fn_exit 255 +printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then - as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + as_bourne_compatible="as_nop=: +if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 +then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST -else +else \$as_nop case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( @@ -187,42 +168,53 @@ as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } -if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : +if ( set x; as_fn_ret_success y && test x = \"\$1\" ) +then : -else +else \$as_nop exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 +blah=\$(echo \$(echo blah)) +test x\"\$blah\" = xblah || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" - if (eval "$as_required") 2>/dev/null; then : + if (eval "$as_required") 2>/dev/null +then : as_have_required=yes -else +else $as_nop as_have_required=no fi - if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null +then : -else +else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. - as_shell=$as_dir/$as_base + as_shell=$as_dir$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && - { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null +then : CONFIG_SHELL=$as_shell as_have_required=yes - if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null +then : break 2 fi fi @@ -230,14 +222,21 @@ esac as_found=false done -$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && - { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : - CONFIG_SHELL=$SHELL as_have_required=yes -fi; } IFS=$as_save_IFS +if $as_found +then : + +else $as_nop + if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null +then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi +fi - if test "x$CONFIG_SHELL" != x; then : + if test "x$CONFIG_SHELL" != x +then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also @@ -255,18 +254,19 @@ exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi - if test x$as_have_required = xno; then : - $as_echo "$0: This script requires a shell more modern than all" - $as_echo "$0: the shells that I found on your system." - if test x${ZSH_VERSION+set} = xset ; then - $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" - $as_echo "$0: be upgraded to zsh 4.3.4 or later." + if test x$as_have_required = xno +then : + printf "%s\n" "$0: This script requires a shell more modern than all" + printf "%s\n" "$0: the shells that I found on your system." + if test ${ZSH_VERSION+y} ; then + printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should" + printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later." else - $as_echo "$0: Please tell bug-autoconf@gnu.org and + printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and $0: dgilbert@interlog.com about your system, including any $0: error possibly output before this message. Then install $0: a modern shell, or manually run the script under such a @@ -294,6 +294,7 @@ } as_unset=as_fn_unset + # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. @@ -311,6 +312,14 @@ as_fn_set_status $1 exit $1 } # as_fn_exit +# as_fn_nop +# --------- +# Do nothing but, unlike ":", preserve the value of $?. +as_fn_nop () +{ + return $? +} +as_nop=as_fn_nop # as_fn_mkdir_p # ------------- @@ -325,7 +334,7 @@ as_dirs= while :; do case $as_dir in #( - *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" @@ -334,7 +343,7 @@ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_dir" | +printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q @@ -373,12 +382,13 @@ # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null +then : eval 'as_fn_append () { eval $1+=\$2 }' -else +else $as_nop as_fn_append () { eval $1=\$$1\$2 @@ -390,18 +400,27 @@ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null +then : eval 'as_fn_arith () { as_val=$(( $* )) }' -else +else $as_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith +# as_fn_nop +# --------- +# Do nothing but, unlike ":", preserve the value of $?. +as_fn_nop () +{ + return $? +} +as_nop=as_fn_nop # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- @@ -413,9 +432,9 @@ as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi - $as_echo "$as_me: error: $2" >&2 + printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error @@ -442,7 +461,7 @@ $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X/"$0" | +printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q @@ -486,7 +505,7 @@ s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || - { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall @@ -500,6 +519,10 @@ exit } + +# Determine whether it's possible to make 'echo' print without a newline. +# These variables are no longer used directly by Autoconf, but are AC_SUBSTed +# for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) @@ -513,6 +536,13 @@ ECHO_N='-n';; esac +# For backward compatibility with old third-party macros, we provide +# the shell variables $as_echo and $as_echo_n. New code should use +# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. +as_echo='printf %s\n' +as_echo_n='printf %s' + + rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file @@ -580,11 +610,43 @@ # Identity of this package. PACKAGE_NAME='sdparm' PACKAGE_TARNAME='sdparm' -PACKAGE_VERSION='1.10' -PACKAGE_STRING='sdparm 1.10' +PACKAGE_VERSION='1.12' +PACKAGE_STRING='sdparm 1.12' PACKAGE_BUGREPORT='dgilbert@interlog.com' PACKAGE_URL='' +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_STDIO_H +# include +#endif +#ifdef HAVE_STDLIB_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_header_c_list= ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS @@ -592,6 +654,10 @@ HAVE_SGUTILS_FALSE HAVE_SGUTILS_TRUE SGUTILS_LIBS +DEBUG_FALSE +DEBUG_TRUE +OS_ANDROID_FALSE +OS_ANDROID_TRUE OS_WIN32_CYGWIN_FALSE OS_WIN32_CYGWIN_TRUE OS_WIN32_MINGW_FALSE @@ -604,8 +670,6 @@ OS_LINUX_TRUE OS_FREEBSD_FALSE OS_FREEBSD_TRUE -os_libs -os_cflags os_deps host_os host_vendor @@ -618,7 +682,8 @@ GETOPT_O_FILES EGREP GREP -CPP +ac_ct_AR +AR am__fastdepCC_FALSE am__fastdepCC_TRUE CCDEPMODE @@ -626,7 +691,6 @@ AMDEPBACKSLASH AMDEP_FALSE AMDEP_TRUE -am__quote am__include DEPDIR OBJEXT @@ -685,6 +749,7 @@ docdir oldincludedir includedir +runstatedir localstatedir sharedstatedir sysconfdir @@ -703,16 +768,20 @@ PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR -SHELL' +SHELL +am__quote' ac_subst_files='' ac_user_opts=' enable_option_checking enable_silent_rules enable_maintainer_mode enable_dependency_tracking +enable_debug enable_linuxbsg enable_libsgutils enable_scsistrings +enable_nvme_supp +enable_fast_lebe ' ac_precious_vars='build_alias host_alias @@ -721,8 +790,7 @@ CFLAGS LDFLAGS LIBS -CPPFLAGS -CPP' +CPPFLAGS' # Initialize some variables set by options. @@ -761,6 +829,7 @@ sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' @@ -790,8 +859,6 @@ *) ac_optarg=yes ;; esac - # Accept the important Cygnus configure options, so we can diagnose typos. - case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; @@ -832,9 +899,9 @@ ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" + as_fn_error $? "invalid feature name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" @@ -858,9 +925,9 @@ ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" + as_fn_error $? "invalid feature name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" @@ -1013,6 +1080,15 @@ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ @@ -1062,9 +1138,9 @@ ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" + as_fn_error $? "invalid package name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" @@ -1078,9 +1154,9 @@ ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" + as_fn_error $? "invalid package name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" @@ -1124,9 +1200,9 @@ *) # FIXME: should be removed in autoconf 3.0. - $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && - $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; @@ -1142,7 +1218,7 @@ case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; - *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + *) printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi @@ -1150,7 +1226,7 @@ for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir + libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. @@ -1206,7 +1282,7 @@ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_myself" | +printf "%s\n" X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q @@ -1263,7 +1339,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures sdparm 1.10 to adapt to many kinds of systems. +\`configure' configures sdparm 1.12 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1303,6 +1379,7 @@ --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] @@ -1333,7 +1410,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sdparm 1.10:";; + short | recursive ) echo "Configuration of sdparm 1.12:";; esac cat <<\_ACEOF @@ -1350,9 +1427,12 @@ do not reject slow dependency extractors --disable-dependency-tracking speeds up one-time build - --disable-linuxbsg ignore linux bsg (sgv4) if present + --enable-debug Turn on debugging + --disable-linuxbsg option ignored, this is placeholder --disable-libsgutils ignore libsgutils if present --disable-scsistrings Disable full SCSI sense strings + --disable-nvme-supp remove all or most NVMe code + --disable-fast-lebe use generic little-endian/big-endian code instead Some influential environment variables: CC C compiler command @@ -1362,7 +1442,6 @@ LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory - CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. @@ -1383,9 +1462,9 @@ case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) - ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; @@ -1413,7 +1492,8 @@ ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } - # Check for guested configure. + # Check for configure.gnu first; this name is used for a wrapper for + # Metaconfig's "Configure" on case-insensitive file systems. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive @@ -1421,7 +1501,7 @@ echo && $SHELL "$ac_srcdir/configure" --help=recursive else - $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done @@ -1430,10 +1510,10 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sdparm configure 1.10 -generated by GNU Autoconf 2.69 +sdparm configure 1.12 +generated by GNU Autoconf 2.70 -Copyright (C) 2012 Free Software Foundation, Inc. +Copyright (C) 2020 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF @@ -1450,14 +1530,14 @@ ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext + rm -f conftest.$ac_objext conftest.beam if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 +printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then @@ -1465,14 +1545,15 @@ cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err - } && test -s conftest.$ac_objext; then : + } && test -s conftest.$ac_objext +then : ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 @@ -1482,85 +1563,6 @@ } # ac_fn_c_try_compile -# ac_fn_c_try_cpp LINENO -# ---------------------- -# Try to preprocess conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_cpp () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } > conftest.i && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_cpp - -# ac_fn_c_try_run LINENO -# ---------------------- -# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes -# that executables *can* be run. -ac_fn_c_try_run () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then : - ac_retval=0 -else - $as_echo "$as_me: program exited with status $ac_status" >&5 - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=$ac_status -fi - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_run - # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in @@ -1568,26 +1570,28 @@ ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $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 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +printf %s "checking for $2... " >&6; } +if eval test \${$3+y} +then : + printf %s "(cached) " >&6 +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : eval "$3=yes" -else +else $as_nop eval "$3=no" fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile @@ -1598,14 +1602,14 @@ ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext conftest$ac_exeext + rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 +printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then @@ -1613,17 +1617,18 @@ cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext - }; then : + } +then : ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 @@ -1644,11 +1649,12 @@ ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $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 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +printf %s "checking for $2... " >&6; } +if eval test \${$3+y} +then : + printf %s "(cached) " >&6 +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. @@ -1656,16 +1662,9 @@ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $2 (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ - -#ifdef __STDC__ -# include -#else -# include -#endif + which can conflict with char $2 (); below. */ +#include #undef $2 /* Override any GCC internal prototype to avoid an error. @@ -1683,35 +1682,56 @@ #endif int -main () +main (void) { return $2 (); ; return 0; } _ACEOF -if ac_fn_c_try_link "$LINENO"; then : +if ac_fn_c_try_link "$LINENO" +then : eval "$3=yes" -else +else $as_nop eval "$3=no" fi -rm -f core conftest.err conftest.$ac_objext \ +rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func +ac_configure_args_raw= +for ac_arg +do + case $ac_arg in + *\'*) + ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append ac_configure_args_raw " '$ac_arg'" +done + +case $ac_configure_args_raw in + *$as_nl*) + ac_safe_unquote= ;; + *) + ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab. + ac_unsafe_a="$ac_unsafe_z#~" + ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g" + ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;; +esac + cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by sdparm $as_me 1.10, which was -generated by GNU Autoconf 2.69. Invocation command line was +It was created by sdparm $as_me 1.12, which was +generated by GNU Autoconf 2.70. Invocation command line was - $ $0 $@ + $ $0$ac_configure_args_raw _ACEOF exec 5>>config.log @@ -1744,8 +1764,12 @@ for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - $as_echo "PATH: $as_dir" + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + printf "%s\n" "PATH: $as_dir" done IFS=$as_save_IFS @@ -1780,7 +1804,7 @@ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) - ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; @@ -1815,11 +1839,13 @@ # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? + # Sanitize IFS. + IFS=" "" $as_nl" # Save into config.log some information that might help in debugging. { echo - $as_echo "## ---------------- ## + printf "%s\n" "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo @@ -1830,8 +1856,8 @@ case $ac_val in #( *${as_nl}*) case $ac_var in #( - *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( @@ -1855,7 +1881,7 @@ ) echo - $as_echo "## ----------------- ## + printf "%s\n" "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo @@ -1863,14 +1889,14 @@ do eval ac_val=\$$ac_var case $ac_val in - *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac - $as_echo "$ac_var='\''$ac_val'\''" + printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then - $as_echo "## ------------------- ## + printf "%s\n" "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo @@ -1878,15 +1904,15 @@ do eval ac_val=\$$ac_var case $ac_val in - *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac - $as_echo "$ac_var='\''$ac_val'\''" + printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then - $as_echo "## ----------- ## + printf "%s\n" "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo @@ -1894,8 +1920,8 @@ echo fi test "$ac_signal" != 0 && - $as_echo "$as_me: caught signal $ac_signal" - $as_echo "$as_me: exit $exit_status" + printf "%s\n" "$as_me: caught signal $ac_signal" + printf "%s\n" "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && @@ -1909,63 +1935,48 @@ # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h -$as_echo "/* confdefs.h */" > confdefs.h +printf "%s\n" "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. -cat >>confdefs.h <<_ACEOF -#define PACKAGE_NAME "$PACKAGE_NAME" -_ACEOF +printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h -cat >>confdefs.h <<_ACEOF -#define PACKAGE_TARNAME "$PACKAGE_TARNAME" -_ACEOF +printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h -cat >>confdefs.h <<_ACEOF -#define PACKAGE_VERSION "$PACKAGE_VERSION" -_ACEOF +printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h -cat >>confdefs.h <<_ACEOF -#define PACKAGE_STRING "$PACKAGE_STRING" -_ACEOF +printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h -cat >>confdefs.h <<_ACEOF -#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" -_ACEOF +printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h -cat >>confdefs.h <<_ACEOF -#define PACKAGE_URL "$PACKAGE_URL" -_ACEOF +printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. -ac_site_file1=NONE -ac_site_file2=NONE if test -n "$CONFIG_SITE"; then - # We do not want a PATH search for config.site. - case $CONFIG_SITE in #(( - -*) ac_site_file1=./$CONFIG_SITE;; - */*) ac_site_file1=$CONFIG_SITE;; - *) ac_site_file1=./$CONFIG_SITE;; - esac + ac_site_files="$CONFIG_SITE" elif test "x$prefix" != xNONE; then - ac_site_file1=$prefix/share/config.site - ac_site_file2=$prefix/etc/config.site + ac_site_files="$prefix/share/config.site $prefix/etc/config.site" else - ac_site_file1=$ac_default_prefix/share/config.site - ac_site_file2=$ac_default_prefix/etc/config.site + ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi -for ac_site_file in "$ac_site_file1" "$ac_site_file2" + +for ac_site_file in $ac_site_files do - test "x$ac_site_file" = xNONE && continue - if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 -$as_echo "$as_me: loading site script $ac_site_file" >&6;} + case $ac_site_file in #( + */*) : + ;; #( + *) : + ac_site_file=./$ac_site_file ;; +esac + if test -f "$ac_site_file" && test -r "$ac_site_file"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ - || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi @@ -1975,163 +1986,557 @@ # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 -$as_echo "$as_me: loading cache $cache_file" >&6;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +printf "%s\n" "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else - { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 -$as_echo "$as_me: creating cache $cache_file" >&6;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +printf "%s\n" "$as_me: creating cache $cache_file" >&6;} >$cache_file fi -# Check that the precious variables saved in the cache have kept the same -# value. -ac_cache_corrupted=false -for ac_var in $ac_precious_vars; do - eval ac_old_set=\$ac_cv_env_${ac_var}_set - eval ac_new_set=\$ac_env_${ac_var}_set - eval ac_old_val=\$ac_cv_env_${ac_var}_value - eval ac_new_val=\$ac_env_${ac_var}_value - case $ac_old_set,$ac_new_set in - set,) - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 -$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,set) - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 -$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,);; - *) - if test "x$ac_old_val" != "x$ac_new_val"; then - # differences in whitespace do not lead to failure. - ac_old_val_w=`echo x $ac_old_val` - ac_new_val_w=`echo x $ac_new_val` - if test "$ac_old_val_w" != "$ac_new_val_w"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 -$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} - ac_cache_corrupted=: - else - { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 -$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} - eval $ac_var=\$ac_old_val - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 -$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 -$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} - fi;; - esac - # Pass precious variables to config.status. - if test "$ac_new_set" = set; then - case $ac_new_val in - *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; - *) ac_arg=$ac_var=$ac_new_val ;; - esac - case " $ac_configure_args " in - *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. - *) as_fn_append ac_configure_args " '$ac_arg'" ;; - esac - fi -done -if $ac_cache_corrupted; then - { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 -$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} - as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 -fi -## -------------------- ## -## Main body of script. ## -## -------------------- ## +# Test code for whether the C compiler supports C89 (global declarations) +ac_c_conftest_c89_globals=' +/* Does the compiler advertise C89 conformance? + Do not test the value of __STDC__, because some compilers set it to 0 + while being otherwise adequately conformant. */ +#if !defined __STDC__ +# error "Compiler does not advertise C89 conformance" +#endif -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ +struct buf { int x; }; +struct buf * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not \xHH hex character constants. + These do not provoke an error unfortunately, instead are silently treated + as an "x". The following induces an error, until -std is added to get + proper ANSI mode. Curiously \x00 != x always comes out true, for an + array size at least. It is necessary to write \x00 == 0 to get something + that is true only with -std. */ +int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1]; +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) '\''x'\'' +int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1]; -am__api_version='1.15' +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int), + int, int);' -ac_aux_dir= -for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do - if test -f "$ac_dir/install-sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install-sh -c" - break - elif test -f "$ac_dir/install.sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install.sh -c" - break - elif test -f "$ac_dir/shtool"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/shtool install -c" - break - fi -done -if test -z "$ac_aux_dir"; then - as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 -fi +# Test code for whether the C compiler supports C89 (body of main). +ac_c_conftest_c89_main=' +ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); +' -# These three variables are undocumented and unsupported, -# and are intended to be withdrawn in a future Autoconf release. -# They can cause serious problems if a builder's source tree is in a directory -# whose full name contains unusual characters. -ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. -ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. -ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. +# Test code for whether the C compiler supports C99 (global declarations) +ac_c_conftest_c99_globals=' +// Does the compiler advertise C99 conformance? +#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L +# error "Compiler does not advertise C99 conformance" +#endif +#include +extern int puts (const char *); +extern int printf (const char *, ...); +extern int dprintf (int, const char *, ...); +extern void *malloc (size_t); + +// Check varargs macros. These examples are taken from C99 6.10.3.5. +// dprintf is used instead of fprintf to avoid needing to declare +// FILE and stderr. +#define debug(...) dprintf (2, __VA_ARGS__) +#define showlist(...) puts (#__VA_ARGS__) +#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) +static void +test_varargs_macros (void) +{ + int x = 1234; + int y = 5678; + debug ("Flag"); + debug ("X = %d\n", x); + showlist (The first, second, and third items.); + report (x>y, "x is %d but y is %d", x, y); +} -# Find a good install program. We prefer a C program (faster), -# so one script is as good as another. But avoid the broken or -# incompatible versions: -# SysV /etc/install, /usr/sbin/install -# SunOS /usr/etc/install -# IRIX /sbin/install -# AIX /bin/install -# AmigaOS /C/install, which installs bootblocks on floppy discs -# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag -# AFS /usr/afsws/bin/install, which mishandles nonexistent args -# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" -# OS/2's system install, which has a completely different semantic -# ./install, which can be erroneously created by make from ./install.sh. -# Reject install programs that cannot install multiple files. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 -$as_echo_n "checking for a BSD-compatible install... " >&6; } -if test -z "$INSTALL"; then -if ${ac_cv_path_install+:} false; then : - $as_echo_n "(cached) " >&6 -else - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - # Account for people who put trailing slashes in PATH elements. -case $as_dir/ in #(( - ./ | .// | /[cC]/* | \ - /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ - ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ - /usr/ucb/* ) ;; - *) +// Check long long types. +#define BIG64 18446744073709551615ull +#define BIG32 4294967295ul +#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) +#if !BIG_OK + #error "your preprocessor is broken" +#endif +#if BIG_OK +#else + #error "your preprocessor is broken" +#endif +static long long int bignum = -9223372036854775807LL; +static unsigned long long int ubignum = BIG64; + +struct incomplete_array +{ + int datasize; + double data[]; +}; + +struct named_init { + int number; + const wchar_t *name; + double average; +}; + +typedef const char *ccp; + +static inline int +test_restrict (ccp restrict text) +{ + // See if C++-style comments work. + // Iterate through items via the restricted pointer. + // Also check for declarations in for loops. + for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) + continue; + return 0; +} + +// Check varargs and va_copy. +static bool +test_varargs (const char *format, ...) +{ + va_list args; + va_start (args, format); + va_list args_copy; + va_copy (args_copy, args); + + const char *str = ""; + int number = 0; + float fnumber = 0; + + while (*format) + { + switch (*format++) + { + case '\''s'\'': // string + str = va_arg (args_copy, const char *); + break; + case '\''d'\'': // int + number = va_arg (args_copy, int); + break; + case '\''f'\'': // float + fnumber = va_arg (args_copy, double); + break; + default: + break; + } + } + va_end (args_copy); + va_end (args); + + return *str && number && fnumber; +} +' + +# Test code for whether the C compiler supports C99 (body of main). +ac_c_conftest_c99_main=' + // Check bool. + _Bool success = false; + success |= (argc != 0); + + // Check restrict. + if (test_restrict ("String literal") == 0) + success = true; + char *restrict newvar = "Another string"; + + // Check varargs. + success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234); + test_varargs_macros (); + + // Check flexible array members. + struct incomplete_array *ia = + malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); + ia->datasize = 10; + for (int i = 0; i < ia->datasize; ++i) + ia->data[i] = i * 1.234; + + // Check named initializers. + struct named_init ni = { + .number = 34, + .name = L"Test wide string", + .average = 543.34343, + }; + + ni.number = 58; + + int dynamic_array[ni.number]; + dynamic_array[0] = argv[0][0]; + dynamic_array[ni.number - 1] = 543; + + // work around unused variable warnings + ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' + || dynamic_array[ni.number - 1] != 543); +' + +# Test code for whether the C compiler supports C11 (global declarations) +ac_c_conftest_c11_globals=' +// Does the compiler advertise C11 conformance? +#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L +# error "Compiler does not advertise C11 conformance" +#endif + +// Check _Alignas. +char _Alignas (double) aligned_as_double; +char _Alignas (0) no_special_alignment; +extern char aligned_as_int; +char _Alignas (0) _Alignas (int) aligned_as_int; + +// Check _Alignof. +enum +{ + int_alignment = _Alignof (int), + int_array_alignment = _Alignof (int[100]), + char_alignment = _Alignof (char) +}; +_Static_assert (0 < -_Alignof (int), "_Alignof is signed"); + +// Check _Noreturn. +int _Noreturn does_not_return (void) { for (;;) continue; } + +// Check _Static_assert. +struct test_static_assert +{ + int x; + _Static_assert (sizeof (int) <= sizeof (long int), + "_Static_assert does not work in struct"); + long int y; +}; + +// Check UTF-8 literals. +#define u8 syntax error! +char const utf8_literal[] = u8"happens to be ASCII" "another string"; + +// Check duplicate typedefs. +typedef long *long_ptr; +typedef long int *long_ptr; +typedef long_ptr long_ptr; + +// Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. +struct anonymous +{ + union { + struct { int i; int j; }; + struct { int k; long int l; } w; + }; + int m; +} v1; +' + +# Test code for whether the C compiler supports C11 (body of main). +ac_c_conftest_c11_main=' + _Static_assert ((offsetof (struct anonymous, i) + == offsetof (struct anonymous, w.k)), + "Anonymous union alignment botch"); + v1.i = 2; + v1.w.k = 5; + ok |= v1.i != 5; +' + +# Test code for whether the C compiler supports C11 (complete). +ac_c_conftest_c11_program="${ac_c_conftest_c89_globals} +${ac_c_conftest_c99_globals} +${ac_c_conftest_c11_globals} + +int +main (int argc, char **argv) +{ + int ok = 0; + ${ac_c_conftest_c89_main} + ${ac_c_conftest_c99_main} + ${ac_c_conftest_c11_main} + return ok; +} +" + +# Test code for whether the C compiler supports C99 (complete). +ac_c_conftest_c99_program="${ac_c_conftest_c89_globals} +${ac_c_conftest_c99_globals} + +int +main (int argc, char **argv) +{ + int ok = 0; + ${ac_c_conftest_c89_main} + ${ac_c_conftest_c99_main} + return ok; +} +" + +# Test code for whether the C compiler supports C89 (complete). +ac_c_conftest_c89_program="${ac_c_conftest_c89_globals} + +int +main (int argc, char **argv) +{ + int ok = 0; + ${ac_c_conftest_c89_main} + return ok; +} +" + +as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H" +as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H" +as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H" +as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H" +as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H" +as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H" +as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H" +as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" +as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" + +# Auxiliary files required by this configure script. +ac_aux_files="config.guess config.sub ar-lib compile missing install-sh" + +# Locations in which to look for auxiliary files. +ac_aux_dir_candidates="${srcdir}${PATH_SEPARATOR}${srcdir}/..${PATH_SEPARATOR}${srcdir}/../.." + +# Search for a directory containing all of the required auxiliary files, +# $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates. +# If we don't find one directory that contains all the files we need, +# we report the set of missing files from the *first* directory in +# $ac_aux_dir_candidates and give up. +ac_missing_aux_files="" +ac_first_candidate=: +printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5 +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in $ac_aux_dir_candidates +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + as_found=: + + printf "%s\n" "$as_me:${as_lineno-$LINENO}: trying $as_dir" >&5 + ac_aux_dir_found=yes + ac_install_sh= + for ac_aux in $ac_aux_files + do + # As a special case, if "install-sh" is required, that requirement + # can be satisfied by any of "install-sh", "install.sh", or "shtool", + # and $ac_install_sh is set appropriately for whichever one is found. + if test x"$ac_aux" = x"install-sh" + then + if test -f "${as_dir}install-sh"; then + printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install-sh found" >&5 + ac_install_sh="${as_dir}install-sh -c" + elif test -f "${as_dir}install.sh"; then + printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install.sh found" >&5 + ac_install_sh="${as_dir}install.sh -c" + elif test -f "${as_dir}shtool"; then + printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}shtool found" >&5 + ac_install_sh="${as_dir}shtool install -c" + else + ac_aux_dir_found=no + if $ac_first_candidate; then + ac_missing_aux_files="${ac_missing_aux_files} install-sh" + else + break + fi + fi + else + if test -f "${as_dir}${ac_aux}"; then + printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}${ac_aux} found" >&5 + else + ac_aux_dir_found=no + if $ac_first_candidate; then + ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}" + else + break + fi + fi + fi + done + if test "$ac_aux_dir_found" = yes; then + ac_aux_dir="$as_dir" + break + fi + ac_first_candidate=false + + as_found=false +done +IFS=$as_save_IFS +if $as_found +then : + +else $as_nop + as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 +fi + + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +if test -f "${ac_aux_dir}config.guess"; then + ac_config_guess="$SHELL ${ac_aux_dir}config.guess" +fi +if test -f "${ac_aux_dir}config.sub"; then + ac_config_sub="$SHELL ${ac_aux_dir}config.sub" +fi +if test -f "$ac_aux_dir/configure"; then + ac_configure="$SHELL ${ac_aux_dir}configure" +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file' + and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +am__api_version='1.16' + + + + # Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +printf %s "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if test ${ac_cv_path_install+y} +then : + printf %s "(cached) " >&6 +else $as_nop + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + # Account for fact that we put trailing slashes in our PATH walk. +case $as_dir in #(( + ./ | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext"; then if test $ac_prog = install && - grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + grep dspmsg "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && - grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + grep pwplus "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else @@ -2139,12 +2544,12 @@ echo one > conftest.one echo two > conftest.two mkdir conftest.dir - if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + if "$as_dir$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir/" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then - ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + ac_cv_path_install="$as_dir$ac_prog$ac_exec_ext -c" break 3 fi fi @@ -2160,7 +2565,7 @@ rm -rf conftest.one conftest.two conftest.dir fi - if test "${ac_cv_path_install+set}" = set; then + if test ${ac_cv_path_install+y}; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a @@ -2170,8 +2575,8 @@ INSTALL=$ac_install_sh fi fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 -$as_echo "$INSTALL" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +printf "%s\n" "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. @@ -2181,8 +2586,8 @@ test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 -$as_echo_n "checking whether build environment is sane... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 +printf %s "checking whether build environment is sane... " >&6; } # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' @@ -2236,8 +2641,8 @@ as_fn_error $? "newly created file is older than distributed files! Check your system clock" "$LINENO" 5 fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= @@ -2256,12 +2661,14 @@ # Double any \ or $. # By default was `s,x,x', remove it if useless. ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' -program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` +program_transform_name=`printf "%s\n" "$program_transform_name" | sed "$ac_script"` + # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` -if test x"${MISSING+set}" != xset; then + + if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; @@ -2274,8 +2681,8 @@ am_missing_run="$MISSING " else am_missing_run= - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 -$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 +printf "%s\n" "$as_me: WARNING: 'missing' script is too old or missing" >&2;} fi if test x"${install_sh+set}" != xset; then @@ -2295,11 +2702,12 @@ if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_STRIP+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_STRIP+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else @@ -2307,11 +2715,15 @@ for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done @@ -2322,11 +2734,11 @@ fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 -$as_echo "$STRIP" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +printf "%s\n" "$STRIP" >&6; } else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi @@ -2335,11 +2747,12 @@ ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_STRIP+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_STRIP+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else @@ -2347,11 +2760,15 @@ for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done @@ -2362,11 +2779,11 @@ fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 -$as_echo "$ac_ct_STRIP" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +printf "%s\n" "$ac_ct_STRIP" >&6; } else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then @@ -2374,8 +2791,8 @@ else case $cross_compiling:$ac_tool_warned in yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP @@ -2387,25 +2804,31 @@ fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 -$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a race-free mkdir -p" >&5 +printf %s "checking for a race-free mkdir -p... " >&6; } if test -z "$MKDIR_P"; then - if ${ac_cv_path_mkdir+:} false; then : - $as_echo_n "(cached) " >&6 -else + if test ${ac_cv_path_mkdir+y} +then : + printf %s "(cached) " >&6 +else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do - as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue - case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( - 'mkdir (GNU coreutils) '* | \ - 'mkdir (coreutils) '* | \ + as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext" || continue + case `"$as_dir$ac_prog$ac_exec_ext" --version 2>&1` in #( + 'mkdir ('*'coreutils) '* | \ + 'BusyBox '* | \ 'mkdir (fileutils) '4.1*) - ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext + ac_cv_path_mkdir=$as_dir$ac_prog$ac_exec_ext break 3;; esac done @@ -2416,7 +2839,7 @@ fi test -d ./--version && rmdir ./--version - if test "${ac_cv_path_mkdir+set}" = set; then + if test ${ac_cv_path_mkdir+y}; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use the slow shell script. Don't cache a @@ -2426,18 +2849,19 @@ MKDIR_P="$ac_install_sh -d" fi fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 -$as_echo "$MKDIR_P" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 +printf "%s\n" "$MKDIR_P" >&6; } for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_AWK+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_AWK+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else @@ -2445,11 +2869,15 @@ for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done @@ -2460,24 +2888,25 @@ fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 -$as_echo "$AWK" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +printf "%s\n" "$AWK" >&6; } else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi test -n "$AWK" && break done -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 -$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +printf %s "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} -ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` -if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : - $as_echo_n "(cached) " >&6 -else +ac_make=`printf "%s\n" "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval test \${ac_cv_prog_make_${ac_make}_set+y} +then : + printf %s "(cached) " >&6 +else $as_nop cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @@ -2493,12 +2922,12 @@ rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } SET_MAKE= else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi @@ -2512,7 +2941,8 @@ rmdir .tst 2>/dev/null # Check whether --enable-silent-rules was given. -if test "${enable_silent_rules+set}" = set; then : +if test ${enable_silent_rules+y} +then : enableval=$enable_silent_rules; fi @@ -2522,12 +2952,13 @@ *) AM_DEFAULT_VERBOSITY=1;; esac am_make=${MAKE-make} -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 -$as_echo_n "checking whether $am_make supports nested variables... " >&6; } -if ${am_cv_make_support_nested_variables+:} false; then : - $as_echo_n "(cached) " >&6 -else - if $as_echo 'TRUE=$(BAR$(V)) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +printf %s "checking whether $am_make supports nested variables... " >&6; } +if test ${am_cv_make_support_nested_variables+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if printf "%s\n" 'TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 @@ -2539,8 +2970,8 @@ am_cv_make_support_nested_variables=no fi fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 -$as_echo "$am_cv_make_support_nested_variables" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +printf "%s\n" "$am_cv_make_support_nested_variables" >&6; } if test $am_cv_make_support_nested_variables = yes; then AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' @@ -2572,17 +3003,13 @@ # Define the identity of the package. PACKAGE='sdparm' - VERSION='1.10' + VERSION='1.12' -cat >>confdefs.h <<_ACEOF -#define PACKAGE "$PACKAGE" -_ACEOF +printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h -cat >>confdefs.h <<_ACEOF -#define VERSION "$VERSION" -_ACEOF +printf "%s\n" "#define VERSION \"$VERSION\"" >>confdefs.h # Some tools Automake needs. @@ -2602,8 +3029,8 @@ # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: -# -# +# +# mkdir_p='$(MKDIR_P)' # We need awk for the "check" target (and possibly the TAP driver). The @@ -2654,7 +3081,7 @@ Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation -that behaves properly: . +that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM @@ -2666,17 +3093,18 @@ fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5 -$as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5 +printf %s "checking whether to enable maintainer-specific portions of Makefiles... " >&6; } # Check whether --enable-maintainer-mode was given. -if test "${enable_maintainer_mode+set}" = set; then : +if test ${enable_maintainer_mode+y} +then : enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval -else +else $as_nop USE_MAINTAINER_MODE=no fi - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5 -$as_echo "$USE_MAINTAINER_MODE" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5 +printf "%s\n" "$USE_MAINTAINER_MODE" >&6; } if test $USE_MAINTAINER_MODE = yes; then MAINTAINER_MODE_TRUE= MAINTAINER_MODE_FALSE='#' @@ -2691,6 +3119,16 @@ ac_config_headers="$ac_config_headers config.h" + + + + + + + + + + ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' @@ -2699,11 +3137,12 @@ if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else @@ -2711,11 +3150,15 @@ for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done @@ -2726,11 +3169,11 @@ fi CC=$ac_cv_prog_CC if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi @@ -2739,11 +3182,12 @@ ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else @@ -2751,11 +3195,15 @@ for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done @@ -2766,11 +3214,11 @@ fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then @@ -2778,8 +3226,8 @@ else case $cross_compiling:$ac_tool_warned in yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC @@ -2792,11 +3240,12 @@ if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else @@ -2804,11 +3253,15 @@ for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done @@ -2819,11 +3272,11 @@ fi CC=$ac_cv_prog_CC if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi @@ -2832,11 +3285,12 @@ if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else @@ -2845,15 +3299,19 @@ for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done @@ -2869,18 +3327,18 @@ # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift - ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi @@ -2891,11 +3349,12 @@ do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else @@ -2903,11 +3362,15 @@ for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done @@ -2918,11 +3381,11 @@ fi CC=$ac_cv_prog_CC if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi @@ -2935,11 +3398,12 @@ do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else @@ -2947,11 +3411,15 @@ for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done @@ -2962,11 +3430,11 @@ fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi @@ -2978,8 +3446,8 @@ else case $cross_compiling:$ac_tool_warned in yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC @@ -2987,25 +3455,129 @@ fi fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args. +set dummy ${ac_tool_prefix}clang; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}clang" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi -test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "clang", so it can be a program name with args. +set dummy clang; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="clang" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +fi + + +test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 -for ac_option in --version -v -V -qversion; do +for ac_option in --version -v -V -qversion -version; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 +printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then @@ -3015,7 +3587,7 @@ cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done @@ -3023,7 +3595,7 @@ /* end confdefs.h. */ int -main () +main (void) { ; @@ -3035,9 +3607,9 @@ # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 -$as_echo_n "checking whether the C compiler works... " >&6; } -ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +printf %s "checking whether the C compiler works... " >&6; } +ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" @@ -3058,11 +3630,12 @@ *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 +printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, @@ -3079,7 +3652,7 @@ # certainly right. break;; *.* ) - if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi @@ -3095,44 +3668,46 @@ done test "$ac_cv_exeext" = no && ac_cv_exeext= -else +else $as_nop ac_file='' fi -if test -z "$ac_file"; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -$as_echo "$as_me: failed program was:" >&5 +if test -z "$ac_file" +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 -{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 -$as_echo_n "checking for C compiler default output file name... " >&6; } -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 -$as_echo "$ac_file" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +printf %s "checking for C compiler default output file name... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +printf "%s\n" "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 -$as_echo_n "checking for suffix of executables... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +printf %s "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 +printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with @@ -3146,15 +3721,15 @@ * ) break;; esac done -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +else $as_nop + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 -$as_echo "$ac_cv_exeext" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +printf "%s\n" "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext @@ -3163,7 +3738,7 @@ /* end confdefs.h. */ #include int -main () +main (void) { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; @@ -3175,8 +3750,8 @@ ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 -$as_echo_n "checking whether we are cross compiling... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +printf %s "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in @@ -3184,10 +3759,10 @@ *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 +printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in @@ -3195,39 +3770,40 @@ *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 +printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot run C compiled programs. + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 -$as_echo "$cross_compiling" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +printf "%s\n" "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 -$as_echo_n "checking for suffix of object files... " >&6; } -if ${ac_cv_objext+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +printf %s "checking for suffix of object files... " >&6; } +if test ${ac_cv_objext+y} +then : + printf %s "(cached) " >&6 +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int -main () +main (void) { ; @@ -3241,11 +3817,12 @@ *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 +printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in @@ -3254,31 +3831,32 @@ break;; esac done -else - $as_echo "$as_me: failed program was:" >&5 +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 -{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 -$as_echo "$ac_cv_objext" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +printf "%s\n" "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 -$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } -if ${ac_cv_c_compiler_gnu+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 +printf %s "checking whether the compiler supports GNU C... " >&6; } +if test ${ac_cv_c_compiler_gnu+y} +then : + printf %s "(cached) " >&6 +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int -main () +main (void) { #ifndef __GNUC__ choke me @@ -3288,29 +3866,33 @@ return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : ac_compiler_gnu=yes -else +else $as_nop ac_compiler_gnu=no fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 -$as_echo "$ac_cv_c_compiler_gnu" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } +ac_compiler_gnu=$ac_cv_c_compiler_gnu + if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi -ac_test_CFLAGS=${CFLAGS+set} +ac_test_CFLAGS=${CFLAGS+y} ac_save_CFLAGS=$CFLAGS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 -$as_echo_n "checking whether $CC accepts -g... " >&6; } -if ${ac_cv_prog_cc_g+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +printf %s "checking whether $CC accepts -g... " >&6; } +if test ${ac_cv_prog_cc_g+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no @@ -3319,57 +3901,60 @@ /* end confdefs.h. */ int -main () +main (void) { ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : ac_cv_prog_cc_g=yes -else +else $as_nop CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int -main () +main (void) { ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : -else +else $as_nop ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int -main () +main (void) { ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : ac_cv_prog_cc_g=yes fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 -$as_echo "$ac_cv_prog_cc_g" >&6; } -if test "$ac_test_CFLAGS" = set; then +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +printf "%s\n" "$ac_cv_prog_cc_g" >&6; } +if test $ac_test_CFLAGS; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then @@ -3384,73 +3969,119 @@ CFLAGS= fi fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 -$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } -if ${ac_cv_prog_cc_c89+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_prog_cc_c89=no +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 +printf %s "checking for $CC option to enable C11 features... " >&6; } +if test ${ac_cv_prog_cc_c11+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c11=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include -#include -struct stat; -/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ -struct buf { int x; }; -FILE * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} - -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not '\xHH' hex character constants. - These don't provoke an error unfortunately, instead are silently treated - as 'x'. The following induces an error, until -std is added to get - proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an - array size at least. It's necessary to write '\x00'==0 to get something - that's true only with -std. */ -int osf4_cc_array ['\x00' == 0 ? 1 : -1]; +$ac_c_conftest_c11_program +_ACEOF +for ac_arg in '' -std=gnu11 +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_c11=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam + test "x$ac_cv_prog_cc_c11" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC -/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters - inside strings and character constants. */ -#define FOO(x) 'x' -int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; +fi +# AC_CACHE_VAL +ac_prog_cc_stdc_options= +case "x$ac_cv_prog_cc_c11" in #( + x) : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } ;; #( + xno) : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } ;; #( + *) : + ac_prog_cc_stdc_options=" $ac_cv_prog_cc_c11" + CC="$CC$ac_prog_cc_stdc_options" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 +printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c11" != xno +then : + ac_prog_cc_stdc=c11 + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 +printf %s "checking for $CC option to enable C99 features... " >&6; } +if test ${ac_cv_prog_cc_c99+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c99=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_c_conftest_c89_program +_ACEOF +for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc1x -qlanglvl=extc99 +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_c99=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam + test "x$ac_cv_prog_cc_c99" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); -int argc; -char **argv; -int -main () -{ -return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; - ; - return 0; -} +fi +# AC_CACHE_VAL +ac_prog_cc_stdc_options= +case "x$ac_cv_prog_cc_c99" in #( + x) : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } ;; #( + xno) : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } ;; #( + *) : + ac_prog_cc_stdc_options=" $ac_cv_prog_cc_c99" + CC="$CC$ac_prog_cc_stdc_options" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 +printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c99" != xno +then : + ac_prog_cc_stdc=c99 + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 +printf %s "checking for $CC option to enable C89 features... " >&6; } +if test ${ac_cv_prog_cc_c89+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_c_conftest_c89_program _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO"; then : + if ac_fn_c_try_compile "$LINENO" +then : ac_cv_prog_cc_c89=$ac_arg fi -rm -f core conftest.err conftest.$ac_objext +rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext @@ -3458,19 +4089,30 @@ fi # AC_CACHE_VAL -case "x$ac_cv_prog_cc_c89" in - x) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -$as_echo "none needed" >&6; } ;; - xno) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -$as_echo "unsupported" >&6; } ;; - *) - CC="$CC $ac_cv_prog_cc_c89" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 -$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; -esac -if test "x$ac_cv_prog_cc_c89" != xno; then : +ac_prog_cc_stdc_options= +case "x$ac_cv_prog_cc_c89" in #( + x) : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } ;; #( + xno) : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } ;; #( + *) : + ac_prog_cc_stdc_options=" $ac_cv_prog_cc_c89" + CC="$CC$ac_prog_cc_stdc_options" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno +then : + ac_prog_cc_stdc=c89 + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 +else $as_nop + ac_prog_cc_stdc=no + ac_cv_prog_cc_stdc=no +fi + +fi fi @@ -3480,21 +4122,23 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu -ac_ext=c + + ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 -$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } -if ${am_cv_prog_cc_c_o+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 +printf %s "checking whether $CC understands -c and -o together... " >&6; } +if test ${am_cv_prog_cc_c_o+y} +then : + printf %s "(cached) " >&6 +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int -main () +main (void) { ; @@ -3522,8 +4166,8 @@ rm -f core conftest* unset am_i fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 -$as_echo "$am_cv_prog_cc_c_o" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 +printf "%s\n" "$am_cv_prog_cc_c_o" >&6; } if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. @@ -3542,48 +4186,49 @@ ac_config_commands="$ac_config_commands depfiles" - -am_make=${MAKE-make} -cat > confinc << 'END' +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5 +printf %s "checking whether ${MAKE-make} supports the include directive... " >&6; } +cat > confinc.mk << 'END' am__doit: - @echo this is the am__doit target + @echo this is the am__doit target >confinc.out .PHONY: am__doit END -# If we don't find an include directive, just comment out the code. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 -$as_echo_n "checking for style of include used by $am_make... " >&6; } am__include="#" am__quote= -_am_result=none -# First try GNU make style include. -echo "include confinc" > confmf -# Ignore all kinds of additional output from 'make'. -case `$am_make -s -f confmf 2> /dev/null` in #( -*the\ am__doit\ target*) - am__include=include - am__quote= - _am_result=GNU - ;; -esac -# Now try BSD make style include. -if test "$am__include" = "#"; then - echo '.include "confinc"' > confmf - case `$am_make -s -f confmf 2> /dev/null` in #( - *the\ am__doit\ target*) - am__include=.include - am__quote="\"" - _am_result=BSD +# BSD make does it like this. +echo '.include "confinc.mk" # ignored' > confmf.BSD +# Other make implementations (GNU, Solaris 10, AIX) do it like this. +echo 'include confinc.mk # ignored' > confmf.GNU +_am_result=no +for s in GNU BSD; do + { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5 + (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + case $?:`cat confinc.out 2>/dev/null` in #( + '0:this is the am__doit target') : + case $s in #( + BSD) : + am__include='.include' am__quote='"' ;; #( + *) : + am__include='include' am__quote='' ;; +esac ;; #( + *) : ;; - esac -fi - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 -$as_echo "$_am_result" >&6; } -rm -f confinc confmf +esac + if test "$am__include" != "#"; then + _am_result="yes ($s style)" + break + fi +done +rm -f confinc.* confmf.* +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5 +printf "%s\n" "${_am_result}" >&6; } # Check whether --enable-dependency-tracking was given. -if test "${enable_dependency_tracking+set}" = set; then : +if test ${enable_dependency_tracking+y} +then : enableval=$enable_dependency_tracking; fi @@ -3604,11 +4249,12 @@ depcc="$CC" am_compiler_list= -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 -$as_echo_n "checking dependency style of $depcc... " >&6; } -if ${am_cv_CC_dependencies_compiler_type+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +printf %s "checking dependency style of $depcc... " >&6; } +if test ${am_cv_CC_dependencies_compiler_type+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For @@ -3715,8 +4361,8 @@ fi fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 -$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +printf "%s\n" "$am_cv_CC_dependencies_compiler_type" >&6; } CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type if @@ -3733,151 +4379,229 @@ # AC_PROG_CXX -# check for headers +# AM_PROG_AR is supported and needed since automake v1.12+ -ac_ext=c + + if test -n "$ac_tool_prefix"; then + for ac_prog in ar lib "link -lib" + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_AR+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_AR="$ac_tool_prefix$ac_prog" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 +printf "%s\n" "$AR" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + + test -n "$AR" && break + done +fi +if test -z "$AR"; then + ac_ct_AR=$AR + for ac_prog in ar lib "link -lib" +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_AR+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AR="$ac_prog" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 +printf "%s\n" "$ac_ct_AR" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + + test -n "$ac_ct_AR" && break +done + + if test "x$ac_ct_AR" = x; then + AR="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AR=$ac_ct_AR + fi +fi + +: ${AR=ar} + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking the archiver ($AR) interface" >&5 +printf %s "checking the archiver ($AR) interface... " >&6; } +if test ${am_cv_ar_interface+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + am_cv_ar_interface=ar + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int some_variable = 0; +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5 + (eval $am_ar_try) 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test "$ac_status" -eq 0; then + am_cv_ar_interface=ar + else + am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5 + (eval $am_ar_try) 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test "$ac_status" -eq 0; then + am_cv_ar_interface=lib + else + am_cv_ar_interface=unknown + fi + fi + rm -f conftest.lib libconftest.a + +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 -$as_echo_n "checking how to run the C preprocessor... " >&6; } -# On Suns, sometimes $CPP names a directory. -if test -n "$CPP" && test -d "$CPP"; then - CPP= -fi -if test -z "$CPP"; then - if ${ac_cv_prog_CPP+:} false; then : - $as_echo_n "(cached) " >&6 -else - # Double quotes because CPP needs to be expanded - for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" - do - ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : -else - # Broken: fails on valid input. -continue fi -rm -f conftest.err conftest.i conftest.$ac_ext +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_ar_interface" >&5 +printf "%s\n" "$am_cv_ar_interface" >&6; } - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext +case $am_cv_ar_interface in +ar) + ;; +lib) + # Microsoft lib, so override with the ar-lib wrapper script. + # FIXME: It is wrong to rewrite AR. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__AR in this case, + # and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something + # similar. + AR="$am_aux_dir/ar-lib $AR" + ;; +unknown) + as_fn_error $? "could not determine $AR interface" "$LINENO" 5 + ;; +esac + +# check for headers +ac_header= ac_cache= +for ac_item in $ac_header_c_list +do + if test $ac_cache; then + ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default" + if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then + printf "%s\n" "#define $ac_item 1" >> confdefs.h + fi + ac_header= ac_cache= + elif test $ac_header; then + ac_cache=$ac_item + else + ac_header=$ac_item + fi done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - break -fi - done - ac_cv_prog_CPP=$CPP -fi - CPP=$ac_cv_prog_CPP -else - ac_cv_prog_CPP=$CPP -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 -$as_echo "$CPP" >&6; } -ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5; } -fi -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes +then : -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 -$as_echo_n "checking for grep that handles long lines and -e... " >&6; } -if ${ac_cv_path_GREP+:} false; then : - $as_echo_n "(cached) " >&6 -else +printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h + +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +printf %s "checking for grep that handles long lines and -e... " >&6; } +if test ${ac_cv_path_GREP+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST @@ -3885,10 +4609,15 @@ for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in grep ggrep; do + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_prog in grep ggrep + do for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + ac_path_GREP="$as_dir$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP @@ -3897,13 +4626,13 @@ ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 - $as_echo_n 0123456789 >"conftest.in" + printf %s 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" - $as_echo 'GREP' >> "conftest.nl" + printf "%s\n" 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val @@ -3931,16 +4660,20 @@ fi fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 -$as_echo "$ac_cv_path_GREP" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +printf "%s\n" "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 -$as_echo_n "checking for egrep... " >&6; } -if ${ac_cv_path_EGREP+:} false; then : - $as_echo_n "(cached) " >&6 -else +# Autoupdate added the next two lines to ensure that your configure +# script's behavior did not change. They are probably safe to remove. + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +printf %s "checking for egrep... " >&6; } +if test ${ac_cv_path_EGREP+y} +then : + printf %s "(cached) " >&6 +else $as_nop if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else @@ -3951,10 +4684,15 @@ for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in egrep; do + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_prog in egrep + do for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + ac_path_EGREP="$as_dir$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP @@ -3963,13 +4701,13 @@ ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 - $as_echo_n 0123456789 >"conftest.in" + printf %s 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" - $as_echo 'EGREP' >> "conftest.nl" + printf "%s\n" 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val @@ -3998,176 +4736,86 @@ fi fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 -$as_echo "$ac_cv_path_EGREP" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +printf "%s\n" "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 -$as_echo_n "checking for ANSI C header files... " >&6; } -if ${ac_cv_header_stdc+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#include -#include -int -main () -{ +ac_fn_c_check_header_compile "$LINENO" "byteswap.h" "ac_cv_header_byteswap_h" "$ac_includes_default" +if test "x$ac_cv_header_byteswap_h" = xyes +then : + printf "%s\n" "#define HAVE_BYTESWAP_H 1" >>confdefs.h - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_header_stdc=yes -else - ac_cv_header_stdc=no fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - -if test $ac_cv_header_stdc = yes; then - # SunOS 4.x string.h does not declare mem*, contrary to ANSI. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "memchr" >/dev/null 2>&1; then : -else - ac_cv_header_stdc=no -fi -rm -f conftest* + for ac_func in getopt_long +do : + ac_fn_c_check_func "$LINENO" "getopt_long" "ac_cv_func_getopt_long" +if test "x$ac_cv_func_getopt_long" = xyes +then : + printf "%s\n" "#define HAVE_GETOPT_LONG 1" >>confdefs.h + GETOPT_O_FILES='' +else $as_nop + GETOPT_O_FILES='getopt_long.o' fi -if test $ac_cv_header_stdc = yes; then - # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "free" >/dev/null 2>&1; then : - -else - ac_cv_header_stdc=no -fi -rm -f conftest* +done +ac_fn_c_check_func "$LINENO" "posix_fadvise" "ac_cv_func_posix_fadvise" +if test "x$ac_cv_func_posix_fadvise" = xyes +then : + printf "%s\n" "#define HAVE_POSIX_FADVISE 1" >>confdefs.h fi -if test $ac_cv_header_stdc = yes; then - # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. - if test "$cross_compiling" = yes; then : - : -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#if ((' ' & 0x0FF) == 0x020) -# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') -# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) -#else -# define ISLOWER(c) \ - (('a' <= (c) && (c) <= 'i') \ - || ('j' <= (c) && (c) <= 'r') \ - || ('s' <= (c) && (c) <= 'z')) -# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) -#endif - -#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) -int -main () -{ - int i; - for (i = 0; i < 256; i++) - if (XOR (islower (i), ISLOWER (i)) - || toupper (i) != TOUPPER (i)) - return 2; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - -else - ac_cv_header_stdc=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi +ac_fn_c_check_func "$LINENO" "posix_memalign" "ac_cv_func_posix_memalign" +if test "x$ac_cv_func_posix_memalign" = xyes +then : + printf "%s\n" "#define HAVE_POSIX_MEMALIGN 1" >>confdefs.h fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 -$as_echo "$ac_cv_header_stdc" >&6; } -if test $ac_cv_header_stdc = yes; then -$as_echo "#define STDC_HEADERS 1" >>confdefs.h +ac_fn_c_check_func "$LINENO" "sysconf" "ac_cv_func_sysconf" +if test "x$ac_cv_func_sysconf" = xyes +then : + printf "%s\n" "#define HAVE_SYSCONF 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 - #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 +ac_fn_c_check_func "$LINENO" "lseek64" "ac_cv_func_lseek64" +if test "x$ac_cv_func_lseek64" = xyes +then : + printf "%s\n" "#define HAVE_LSEEK64 1" >>confdefs.h fi -done -for ac_func in getopt_long -do : - ac_fn_c_check_func "$LINENO" "getopt_long" "ac_cv_func_getopt_long" -if test "x$ac_cv_func_getopt_long" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_GETOPT_LONG 1 -_ACEOF - GETOPT_O_FILES='' -else - GETOPT_O_FILES='getopt_long.o' -fi -done + # Make sure we can run config.sub. +$SHELL "${ac_aux_dir}config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL ${ac_aux_dir}config.sub" "$LINENO" 5 -# Make sure we can run config.sub. -$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || - as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 -$as_echo_n "checking build system type... " >&6; } -if ${ac_cv_build+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +printf %s "checking build system type... " >&6; } +if test ${ac_cv_build+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_build_alias=$build_alias test "x$ac_build_alias" = x && - ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` + ac_build_alias=`$SHELL "${ac_aux_dir}config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 -ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 +ac_cv_build=`$SHELL "${ac_aux_dir}config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $ac_build_alias failed" "$LINENO" 5 fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 -$as_echo "$ac_cv_build" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +printf "%s\n" "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; @@ -4186,21 +4834,22 @@ case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 -$as_echo_n "checking host system type... " >&6; } -if ${ac_cv_host+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +printf %s "checking host system type... " >&6; } +if test ${ac_cv_host+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else - ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 + ac_cv_host=`$SHELL "${ac_aux_dir}config.sub" $host_alias` || + as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $host_alias failed" "$LINENO" 5 fi fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 -$as_echo "$ac_cv_host" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +printf "%s\n" "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; @@ -4221,123 +4870,118 @@ -cat >>confdefs.h <<_ACEOF -#define SG_LIB_BUILD_HOST "${host}" -_ACEOF +printf "%s\n" "#define SG_LIB_BUILD_HOST \"${host}\"" >>confdefs.h -case "${host}" in - *-*-linux-gnu*) - os_deps='sg_pt_linux.o' +check_for_linux_nvme_headers() { + for ac_header in linux/nvme_ioctl.h +do : + ac_fn_c_check_header_compile "$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 : + printf "%s\n" "#define HAVE_LINUX_NVME_IOCTL_H 1" >>confdefs.h +printf "%s\n" "#define HAVE_NVME 1" >>confdefs.h -cat >>confdefs.h <<_ACEOF -#define SG_LIB_LINUX 1 -_ACEOF +fi - os_cflags='' +done + ac_fn_c_check_header_compile "$LINENO" "linux/types.h" "ac_cv_header_linux_types_h" "#ifdef HAVE_LINUX_TYPES_H + # include + #endif - os_libs='' - ;; - *-*-linux*) - os_deps='sg_pt_linux.o' +" +if test "x$ac_cv_header_linux_types_h" = xyes +then : + printf "%s\n" "#define HAVE_LINUX_TYPES_H 1" >>confdefs.h +fi +ac_fn_c_check_header_compile "$LINENO" "linux/bsg.h" "ac_cv_header_linux_bsg_h" "#ifdef HAVE_LINUX_TYPES_H + # include + #endif -cat >>confdefs.h <<_ACEOF -#define SG_LIB_LINUX 1 -_ACEOF +" +if test "x$ac_cv_header_linux_bsg_h" = xyes +then : + printf "%s\n" "#define HAVE_LINUX_BSG_H 1" >>confdefs.h - os_cflags='' +fi +ac_fn_c_check_header_compile "$LINENO" "linux/kdev_t.h" "ac_cv_header_linux_kdev_t_h" "#ifdef HAVE_LINUX_TYPES_H + # include + #endif - os_libs='' - ;; - *-*-freebsd*|*-*-kfreebsd*-gnu*) - os_deps='sg_pt_freebsd.o' +" +if test "x$ac_cv_header_linux_kdev_t_h" = xyes +then : + printf "%s\n" "#define HAVE_LINUX_KDEV_T_H 1" >>confdefs.h +fi -cat >>confdefs.h <<_ACEOF -#define SG_LIB_FREEBSD 1 -_ACEOF +} - os_cflags='' +case "${host}" in + *-*-android*) + os_deps='sg_pt_linux.o sg_pt_linux_nvme.o' - os_libs='-lcam' -;; - *-*-solaris*) - os_deps='sg_pt_solaris.o' +printf "%s\n" "#define SG_LIB_ANDROID 1" >>confdefs.h -cat >>confdefs.h <<_ACEOF -#define SG_LIB_SOLARIS 1 -_ACEOF - os_cflags='' +printf "%s\n" "#define SG_LIB_LINUX 1" >>confdefs.h - os_libs='' -;; - *-*-osf*) - os_deps='sg_pt_osf1.o' + check_for_linux_nvme_headers;; + *-*-freebsd*|*-*-kfreebsd*-gnu*) + os_deps='sg_pt_freebsd.o' -cat >>confdefs.h <<_ACEOF -#define SG_LIB_OSF1 1 -_ACEOF +printf "%s\n" "#define SG_LIB_FREEBSD 1" >>confdefs.h - os_cflags='' - os_libs='' - ;; - *-*-cygwin*) - os_deps='sg_pt_win32.o' +printf "%s\n" "#define HAVE_NVME 1" >>confdefs.h + LIBS="$LIBS -lcam";; + *-*-solaris*) + os_deps='sg_pt_solaris.o' -cat >>confdefs.h <<_ACEOF -#define SG_LIB_WIN32 1 -_ACEOF +printf "%s\n" "#define SG_LIB_SOLARIS 1" >>confdefs.h +;; + *-*-osf*) + os_deps='sg_pt_osf1.o' -cat >>confdefs.h <<_ACEOF -#define WIN32_SPT_DIRECT 1 -_ACEOF - os_cflags='-Wno-char-subscripts' +printf "%s\n" "#define SG_LIB_OSF1 1" >>confdefs.h +;; + *-*-cygwin*) + os_deps='sg_pt_win32.o' - os_libs='' - ;; - *-*-mingw*) - os_deps='sg_pt_win32.o' +printf "%s\n" "#define SG_LIB_WIN32 1" >>confdefs.h -cat >>confdefs.h <<_ACEOF -#define SG_LIB_WIN32 1 -_ACEOF +printf "%s\n" "#define HAVE_NVME 1" >>confdefs.h -cat >>confdefs.h <<_ACEOF -#define SG_LIB_MINGW 1 -_ACEOF + CFLAGS="$CFLAGS -Wno-char-subscripts";; + *-*-mingw*) + os_deps='sg_pt_win32.o' -cat >>confdefs.h <<_ACEOF -#define WIN32_SPT_DIRECT 1 -_ACEOF +printf "%s\n" "#define SG_LIB_WIN32 1" >>confdefs.h - os_cflags='' - os_libs='' - ;; - *) - os_deps='sg_pt_linux.o' +printf "%s\n" "#define SG_LIB_MINGW 1" >>confdefs.h -cat >>confdefs.h <<_ACEOF -#define SG_LIB_LINUX 1 -_ACEOF +printf "%s\n" "#define HAVE_NVME 1" >>confdefs.h - os_cflags='' + CFLAGS="$CFLAGS -D__USE_MINGW_ANSI_STDIO";; + *-*-linux-gnu* | *-*-linux* | *) + os_deps='sg_pt_linux.o sg_pt_linux_nvme.o' - os_libs='' - ;; + +printf "%s\n" "#define SG_LIB_LINUX 1" >>confdefs.h + + check_for_linux_nvme_headers;; esac # Define platform-specific symbol. @@ -4389,26 +5033,56 @@ OS_WIN32_CYGWIN_FALSE= fi + if echo $host_os | grep 'android' > /dev/null; then + OS_ANDROID_TRUE= + OS_ANDROID_FALSE='#' +else + OS_ANDROID_TRUE='#' + OS_ANDROID_FALSE= +fi + + +# Check whether --enable-debug was given. +if test ${enable_debug+y} +then : + enableval=$enable_debug; case "${enableval}" in + yes) debug=true ;; + no) debug=false ;; + *) as_fn_error $? "bad value ${enableval} for --enable-debug" "$LINENO" 5 ;; + esac +else $as_nop + debug=false +fi + + if test x$debug = xtrue; then + DEBUG_TRUE= + DEBUG_FALSE='#' +else + DEBUG_TRUE='#' + DEBUG_FALSE= +fi + # Check whether --enable-linuxbsg was given. -if test "${enable_linuxbsg+set}" = set; then : +if test ${enable_linuxbsg+y} +then : enableval=$enable_linuxbsg; -cat >>confdefs.h <<_ACEOF -#define IGNORE_LINUX_BSG 1 -_ACEOF +printf "%s\n" "#define IGNORE_LINUX_BSG 1" >>confdefs.h fi # Check whether --enable-libsgutils was given. -if test "${enable_libsgutils+set}" = set; then : +if test ${enable_libsgutils+y} +then : enableval=$enable_libsgutils; have_sgutils=no -else - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sg_ll_inquiry in -lsgutils2" >&5 -$as_echo_n "checking for sg_ll_inquiry in -lsgutils2... " >&6; } -if ${ac_cv_lib_sgutils2_sg_ll_inquiry+:} false; then : - $as_echo_n "(cached) " >&6 -else +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sg_ll_inquiry_v2 in -lsgutils2" >&5 +printf %s "checking for sg_ll_inquiry_v2 in -lsgutils2... " >&6; } +if test ${ac_cv_lib_sgutils2_sg_ll_inquiry_v2+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lsgutils2 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -4417,32 +5091,31 @@ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char sg_ll_inquiry (); +char sg_ll_inquiry_v2 (); int -main () +main (void) { -return sg_ll_inquiry (); +return sg_ll_inquiry_v2 (); ; return 0; } _ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_sgutils2_sg_ll_inquiry=yes -else - ac_cv_lib_sgutils2_sg_ll_inquiry=no +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_lib_sgutils2_sg_ll_inquiry_v2=yes +else $as_nop + ac_cv_lib_sgutils2_sg_ll_inquiry_v2=no fi -rm -f core conftest.err conftest.$ac_objext \ +rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sgutils2_sg_ll_inquiry" >&5 -$as_echo "$ac_cv_lib_sgutils2_sg_ll_inquiry" >&6; } -if test "x$ac_cv_lib_sgutils2_sg_ll_inquiry" = xyes; then : +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sgutils2_sg_ll_inquiry_v2" >&5 +printf "%s\n" "$ac_cv_lib_sgutils2_sg_ll_inquiry_v2" >&6; } +if test "x$ac_cv_lib_sgutils2_sg_ll_inquiry_v2" = xyes +then : SGUTILS_LIBS="-lsgutils2"; have_sgutils=yes -else +else $as_nop have_sgutils=no fi @@ -4459,13 +5132,30 @@ # Check whether --enable-scsistrings was given. -if test "${enable_scsistrings+set}" = set; then : +if test ${enable_scsistrings+y} +then : enableval=$enable_scsistrings; -else +else $as_nop -cat >>confdefs.h <<_ACEOF -#define SG_SCSI_STRINGS 1 -_ACEOF +printf "%s\n" "#define SG_SCSI_STRINGS 1" >>confdefs.h + +fi + + +# Check whether --enable-nvme-supp was given. +if test ${enable_nvme_supp+y} +then : + enableval=$enable_nvme_supp; +printf "%s\n" "#define IGNORE_NVME 1" >>confdefs.h + +fi + + +# Check whether --enable-fast-lebe was given. +if test ${enable_fast_lebe+y} +then : + enableval=$enable_fast_lebe; +printf "%s\n" "#define IGNORE_FAST_LEBE 1" >>confdefs.h fi @@ -4500,8 +5190,8 @@ case $ac_val in #( *${as_nl}*) case $ac_var in #( - *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( @@ -4531,15 +5221,15 @@ /^ac_cv_env_/b end t clear :clear - s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 -$as_echo "$as_me: updating cache $cache_file" >&6;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +printf "%s\n" "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else @@ -4553,8 +5243,8 @@ fi fi else - { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 -$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache @@ -4570,7 +5260,7 @@ for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' - ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" @@ -4581,14 +5271,14 @@ LTLIBOBJS=$ac_ltlibobjs -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 -$as_echo_n "checking that generated files are newer than configure... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 +printf %s "checking that generated files are newer than configure... " >&6; } if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi - { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 -$as_echo "done" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: done" >&5 +printf "%s\n" "done" >&6; } if test -n "$EXEEXT"; then am__EXEEXT_TRUE= am__EXEEXT_FALSE='#' @@ -4633,6 +5323,14 @@ as_fn_error $? "conditional \"OS_WIN32_CYGWIN\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${OS_ANDROID_TRUE}" && test -z "${OS_ANDROID_FALSE}"; then + as_fn_error $? "conditional \"OS_ANDROID\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${DEBUG_TRUE}" && test -z "${DEBUG_FALSE}"; then + as_fn_error $? "conditional \"DEBUG\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${HAVE_SGUTILS_TRUE}" && test -z "${HAVE_SGUTILS_FALSE}"; then as_fn_error $? "conditional \"HAVE_SGUTILS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 @@ -4642,8 +5340,8 @@ ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" -{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 -$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL @@ -4666,14 +5364,16 @@ # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : +as_nop=: +if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 +then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST -else +else $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( @@ -4683,46 +5383,46 @@ fi + +# Reset variables that may have inherited troublesome values from +# the environment. + +# IFS needs to be set, to space, tab, and newline, in precisely that order. +# (If _AS_PATH_WALK were called with IFS unset, it would have the +# side effect of setting IFS to empty, thus disabling word splitting.) +# Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl -# Printing a long string crashes Solaris 7 /usr/bin/printf. -as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo -# Prefer a ksh shell builtin over an external printf program on Solaris, -# but without wasting forks for bash or zsh. -if test -z "$BASH_VERSION$ZSH_VERSION" \ - && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='print -r --' - as_echo_n='print -rn --' -elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='printf %s\n' - as_echo_n='printf %s' -else - if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then - as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' - as_echo_n='/usr/ucb/echo -n' - else - as_echo_body='eval expr "X$1" : "X\\(.*\\)"' - as_echo_n_body='eval - arg=$1; - case $arg in #( - *"$as_nl"*) - expr "X$arg" : "X\\(.*\\)$as_nl"; - arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; - esac; - expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" - ' - export as_echo_n_body - as_echo_n='sh -c $as_echo_n_body as_echo' - fi - export as_echo_body - as_echo='sh -c $as_echo_body as_echo' -fi +IFS=" "" $as_nl" + +PS1='$ ' +PS2='> ' +PS4='+ ' + +# Ensure predictable behavior from utilities with locale-dependent output. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# We cannot yet rely on "unset" to work, but we need these variables +# to be unset--not just set to an empty or harmless value--now, to +# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct +# also avoids known problems related to "unset" and subshell syntax +# in other old shells (e.g. bash 2.01 and pdksh 5.2.14). +for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH +do eval test \${$as_var+y} \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done + +# Ensure that fds 0, 1, and 2 are open. +if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi +if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then +if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || @@ -4731,13 +5431,6 @@ fi -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) -IFS=" "" $as_nl" - # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( @@ -4746,8 +5439,12 @@ for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS @@ -4759,30 +5456,10 @@ as_myself=$0 fi if test ! -f "$as_myself"; then - $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi -# Unset variables that we do not need and which cause bugs (e.g. in -# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" -# suppresses any "Segmentation fault" message there. '((' could -# trigger a bug in pdksh 5.2.14. -for as_var in BASH_ENV ENV MAIL MAILPATH -do eval test x\${$as_var+set} = xset \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# CDPATH. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] @@ -4795,13 +5472,14 @@ as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi - $as_echo "$as_me: error: $2" >&2 + printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error + # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. @@ -4828,18 +5506,20 @@ { eval $1=; unset $1;} } as_unset=as_fn_unset + # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null +then : eval 'as_fn_append () { eval $1+=\$2 }' -else +else $as_nop as_fn_append () { eval $1=\$$1\$2 @@ -4851,12 +5531,13 @@ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null +then : eval 'as_fn_arith () { as_val=$(( $* )) }' -else +else $as_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` @@ -4887,7 +5568,7 @@ $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X/"$0" | +printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q @@ -4909,6 +5590,10 @@ as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits + +# Determine whether it's possible to make 'echo' print without a newline. +# These variables are no longer used directly by Autoconf, but are AC_SUBSTed +# for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) @@ -4922,6 +5607,12 @@ ECHO_N='-n';; esac +# For backward compatibility with old third-party macros, we provide +# the shell variables $as_echo and $as_echo_n. New code should use +# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. +as_echo='printf %s\n' +as_echo_n='printf %s' + rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file @@ -4963,7 +5654,7 @@ as_dirs= while :; do case $as_dir in #( - *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" @@ -4972,7 +5663,7 @@ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_dir" | +printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q @@ -5034,8 +5725,8 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by sdparm $as_me 1.10, which was -generated by GNU Autoconf 2.69. Invocation command line was +This file was extended by sdparm $as_me 1.12, which was +generated by GNU Autoconf 2.70. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS @@ -5097,14 +5788,16 @@ Report bugs to ." _ACEOF +ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` +ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -sdparm config.status 1.10 -configured by $0, generated by GNU Autoconf 2.69, +sdparm config.status 1.12 +configured by $0, generated by GNU Autoconf 2.70, with options \\"\$ac_cs_config\\" -Copyright (C) 2012 Free Software Foundation, Inc. +Copyright (C) 2020 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." @@ -5144,15 +5837,15 @@ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) - $as_echo "$ac_cs_version"; exit ;; + printf "%s\n" "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) - $as_echo "$ac_cs_config"; exit ;; + printf "%s\n" "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in - *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" @@ -5160,7 +5853,7 @@ --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in - *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; @@ -5169,7 +5862,7 @@ as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) - $as_echo "$ac_cs_usage"; exit ;; + printf "%s\n" "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; @@ -5197,7 +5890,7 @@ if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift - \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" @@ -5211,7 +5904,7 @@ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX - $as_echo "$ac_log" + printf "%s\n" "$ac_log" } >&5 _ACEOF @@ -5219,7 +5912,7 @@ # # INIT-COMMANDS # -AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" +AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}" _ACEOF @@ -5246,9 +5939,9 @@ # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then - test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files - test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers - test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands + test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files + test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers + test ${CONFIG_COMMANDS+y} || CONFIG_COMMANDS=$config_commands fi # Have a temporary directory for convenience. Make it in the build tree @@ -5584,7 +6277,7 @@ esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac - case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done @@ -5592,17 +6285,17 @@ # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` - $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" - { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 -$as_echo "$as_me: creating $ac_file" >&6;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +printf "%s\n" "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) - ac_sed_conf_input=`$as_echo "$configure_input" | + ac_sed_conf_input=`printf "%s\n" "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac @@ -5619,7 +6312,7 @@ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$ac_file" | +printf "%s\n" X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q @@ -5643,9 +6336,9 @@ case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) - ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; @@ -5707,8 +6400,8 @@ case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 -$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' @@ -5752,9 +6445,9 @@ { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 -$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" @@ -5770,20 +6463,20 @@ # if test x"$ac_file" != x-; then { - $as_echo "/* $configure_input */" \ + printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then - { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 -$as_echo "$as_me: $ac_file is unchanged" >&6;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +printf "%s\n" "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else - $as_echo "/* $configure_input */" \ + printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi @@ -5803,7 +6496,7 @@ X"$_am_arg" : 'X\(//\)[^/]' \| \ X"$_am_arg" : 'X\(//\)$' \| \ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$_am_arg" | +printf "%s\n" X"$_am_arg" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q @@ -5823,8 +6516,8 @@ s/.*/./; q'`/stamp-h$_am_stamp_count ;; - :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 -$as_echo "$as_me: executing $ac_file commands" >&6;} + :C) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 +printf "%s\n" "$as_me: executing $ac_file commands" >&6;} ;; esac @@ -5834,29 +6527,35 @@ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. - case $CONFIG_FILES in - *\'*) eval set x "$CONFIG_FILES" ;; - *) set x $CONFIG_FILES ;; - esac + # TODO: see whether this extra hack can be removed once we start + # requiring Autoconf 2.70 or later. + case $CONFIG_FILES in #( + *\'*) : + eval set x "$CONFIG_FILES" ;; #( + *) : + set x $CONFIG_FILES ;; #( + *) : + ;; +esac shift - for mf + # Used to flag and report bootstrapping failures. + am_rc=0 + for am_mf do # Strip MF so we end up with the name of the file. - mf=`echo "$mf" | sed -e 's/:.*$//'` - # Check whether this is an Automake generated Makefile or not. - # We used to match only the files named 'Makefile.in', but - # some people rename them; so instead we look at the file content. - # Grep'ing the first line is not enough: some people post-process - # each Makefile.in and add a new line on top of each file to say so. - # Grep'ing the whole file is not good either: AIX grep has a line + am_mf=`printf "%s\n" "$am_mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile which includes + # dependency-tracking related rules and includes. + # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. - if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then - dirpart=`$as_dirname -- "$mf" || -$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$mf" : 'X\(//\)[^/]' \| \ - X"$mf" : 'X\(//\)$' \| \ - X"$mf" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$mf" | + sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ + || continue + am_dirpart=`$as_dirname -- "$am_mf" || +$as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$am_mf" : 'X\(//\)[^/]' \| \ + X"$am_mf" : 'X\(//\)$' \| \ + X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X"$am_mf" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q @@ -5874,53 +6573,48 @@ q } s/.*/./; q'` - else - continue - fi - # Extract the definition of DEPDIR, am__include, and am__quote - # from the Makefile without running 'make'. - DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` - test -z "$DEPDIR" && continue - am__include=`sed -n 's/^am__include = //p' < "$mf"` - test -z "$am__include" && continue - am__quote=`sed -n 's/^am__quote = //p' < "$mf"` - # Find all dependency output files, they are included files with - # $(DEPDIR) in their names. We invoke sed twice because it is the - # simplest approach to changing $(DEPDIR) to its actual value in the - # expansion. - for file in `sed -n " - s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ - sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do - # Make sure the directory exists. - test -f "$dirpart/$file" && continue - fdir=`$as_dirname -- "$file" || -$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$file" : 'X\(//\)[^/]' \| \ - X"$file" : 'X\(//\)$' \| \ - X"$file" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$file" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ + am_filepart=`$as_basename -- "$am_mf" || +$as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \ + X"$am_mf" : 'X\(//\)$' \| \ + X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X/"$am_mf" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } - /^X\(\/\/\)$/{ + /^X\/\(\/\/\)$/{ s//\1/ q } - /^X\(\/\).*/{ + /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` - as_dir=$dirpart/$fdir; as_fn_mkdir_p - # echo "creating $dirpart/$file" - echo '# dummy' > "$dirpart/$file" - done + { echo "$as_me:$LINENO: cd "$am_dirpart" \ + && sed -e '/# am--include-marker/d' "$am_filepart" \ + | $MAKE -f - am--depfiles" >&5 + (cd "$am_dirpart" \ + && sed -e '/# am--include-marker/d' "$am_filepart" \ + | $MAKE -f - am--depfiles) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } || am_rc=$? done + if test $am_rc -ne 0; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "Something went wrong bootstrapping makefile fragments + for automatic dependency tracking. Try re-running configure with the + '--disable-dependency-tracking' option to at least be able to build + the package (albeit without support for automatic dependency tracking). +See \`config.log' for more details" "$LINENO" 5; } + fi + { am_dirpart=; unset am_dirpart;} + { am_filepart=; unset am_filepart;} + { am_mf=; unset am_mf;} + { am_rc=; unset am_rc;} + rm -f conftest-deps.mk } ;; @@ -5957,7 +6651,8 @@ $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 -$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi + diff -Nru sdparm-1.10/configure.ac sdparm-1.12/configure.ac --- sdparm-1.10/configure.ac 2015-01-01 20:12:06.000000000 +0000 +++ sdparm-1.12/configure.ac 2020-04-13 05:03:13.000000000 +0000 @@ -1,74 +1,75 @@ -AC_INIT(sdparm, 1.10, dgilbert@interlog.com) +AC_INIT(sdparm, 1.12, dgilbert@interlog.com) -AM_INIT_AUTOMAKE +AM_INIT_AUTOMAKE([foreign]) AM_MAINTAINER_MODE AM_CONFIG_HEADER(config.h) + AC_PROG_CC # AC_PROG_CXX AC_PROG_INSTALL +# AM_PROG_AR is supported and needed since automake v1.12+ +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 - #endif - ]]) +AC_CHECK_HEADERS([byteswap.h], [], [], []) AC_CHECK_FUNCS(getopt_long, GETOPT_O_FILES='', GETOPT_O_FILES='getopt_long.o') +AC_CHECK_FUNCS(posix_fadvise) +AC_CHECK_FUNCS(posix_memalign) +AC_CHECK_FUNCS(sysconf) +AC_CHECK_FUNCS(lseek64) AC_SUBST(GETOPT_O_FILES) AC_CANONICAL_HOST AC_DEFINE_UNQUOTED(SG_LIB_BUILD_HOST, "${host}", [sdparm Build Host]) +check_for_linux_nvme_headers() { + AC_CHECK_HEADERS([linux/nvme_ioctl.h], [AC_DEFINE_UNQUOTED(HAVE_NVME, 1, [Found NVMe])], [], []) + AC_CHECK_HEADERS([linux/types.h linux/bsg.h linux/kdev_t.h], [], [], + [[#ifdef HAVE_LINUX_TYPES_H + # include + #endif + ]]) +} + case "${host}" in - *-*-linux-gnu*) - AC_SUBST([os_deps], ['sg_pt_linux.o']) + *-*-android*) + AC_SUBST([os_deps], ['sg_pt_linux.o sg_pt_linux_nvme.o']) + AC_DEFINE_UNQUOTED(SG_LIB_ANDROID, 1, [sdparm on android]) AC_DEFINE_UNQUOTED(SG_LIB_LINUX, 1, [sdparm on linux]) - AC_SUBST([os_cflags], ['']) - AC_SUBST([os_libs], ['']) ;; - *-*-linux*) - AC_SUBST([os_deps], ['sg_pt_linux.o']) - AC_DEFINE_UNQUOTED(SG_LIB_LINUX, 1, [sdparm on linux]) - AC_SUBST([os_cflags], ['']) - AC_SUBST([os_libs], ['']) ;; + check_for_linux_nvme_headers;; *-*-freebsd*|*-*-kfreebsd*-gnu*) - AC_SUBST([os_deps], ['sg_pt_freebsd.o']) - AC_DEFINE_UNQUOTED(SG_LIB_FREEBSD, 1, [sdparm on FreeBSD]) - AC_SUBST([os_cflags], ['']) - AC_SUBST([os_libs], ['-lcam']);; + AC_SUBST([os_deps], ['sg_pt_freebsd.o']) + AC_DEFINE_UNQUOTED(SG_LIB_FREEBSD, 1, [sdparm on FreeBSD]) + AC_DEFINE_UNQUOTED(HAVE_NVME, 1, [Found NVMe]) + LIBS="$LIBS -lcam";; *-*-solaris*) - AC_SUBST([os_deps], ['sg_pt_solaris.o']) - AC_DEFINE_UNQUOTED(SG_LIB_SOLARIS, 1, [sdparm on Solaris]) - AC_SUBST([os_cflags], ['']) - AC_SUBST([os_libs], ['']);; - *-*-osf*) - AC_SUBST([os_deps], ['sg_pt_osf1.o']) - AC_DEFINE_UNQUOTED(SG_LIB_OSF1, 1, [sdparm on Tru64 UNIX]) - AC_SUBST([os_cflags], ['']) - AC_SUBST([os_libs], ['']) ;; + AC_SUBST([os_deps], ['sg_pt_solaris.o']) + AC_DEFINE_UNQUOTED(SG_LIB_SOLARIS, 1, [sdparm on Solaris]);; + *-*-osf*) + AC_SUBST([os_deps], ['sg_pt_osf1.o']) + AC_DEFINE_UNQUOTED(SG_LIB_OSF1, 1, [sdparm on Tru64 UNIX]);; *-*-cygwin*) - AC_SUBST([os_deps], ['sg_pt_win32.o']) - AC_DEFINE_UNQUOTED(SG_LIB_WIN32, 1, [sdparm on Win32]) - AC_DEFINE_UNQUOTED(WIN32_SPT_DIRECT, 1, [allow large buffers, aligned?]) - AC_SUBST([os_cflags], ['-Wno-char-subscripts']) - AC_SUBST([os_libs], ['']) ;; - *-*-mingw*) - AC_SUBST([os_deps], ['sg_pt_win32.o']) - AC_DEFINE_UNQUOTED(SG_LIB_WIN32, 1, [sdparm on Win32]) + AC_SUBST([os_deps], ['sg_pt_win32.o']) + AC_DEFINE_UNQUOTED(SG_LIB_WIN32, 1, [sdparm on Win32]) + AC_DEFINE_UNQUOTED(HAVE_NVME, 1, [Found NVMe]) + CFLAGS="$CFLAGS -Wno-char-subscripts";; + *-*-mingw*) + AC_SUBST([os_deps], ['sg_pt_win32.o']) + AC_DEFINE_UNQUOTED(SG_LIB_WIN32, 1, [sdparm on Win32]) AC_DEFINE_UNQUOTED(SG_LIB_MINGW, 1, [also MinGW environment]) - AC_DEFINE_UNQUOTED(WIN32_SPT_DIRECT, 1, [allow large buffers, aligned?]) - AC_SUBST([os_cflags], ['']) - AC_SUBST([os_libs], ['']) ;; - *) - AC_SUBST([os_deps], ['sg_pt_linux.o']) - AC_DEFINE_UNQUOTED(SG_LIB_LINUX, 1, [assume sdparm on linux]) - AC_SUBST([os_cflags], ['']) - AC_SUBST([os_libs], ['']) ;; + AC_DEFINE_UNQUOTED(HAVE_NVME, 1, [Found NVMe]) + CFLAGS="$CFLAGS -D__USE_MINGW_ANSI_STDIO";; + *-*-linux-gnu* | *-*-linux* | *) + AC_SUBST([os_deps], ['sg_pt_linux.o sg_pt_linux_nvme.o']) + AC_DEFINE_UNQUOTED(SG_LIB_LINUX, 1, [sdparm on linux]) + check_for_linux_nvme_headers;; esac # Define platform-specific symbol. @@ -78,15 +79,25 @@ AM_CONDITIONAL(OS_SOLARIS, [echo $host_os | grep '^solaris' > /dev/null]) AM_CONDITIONAL(OS_WIN32_MINGW, [echo $host_os | grep '^mingw' > /dev/null]) AM_CONDITIONAL(OS_WIN32_CYGWIN, [echo $host_os | grep '^cygwin' > /dev/null]) +AM_CONDITIONAL(OS_ANDROID, [echo $host_os | grep 'android' > /dev/null]) + +AC_ARG_ENABLE([debug], + [ --enable-debug Turn on debugging], + [case "${enableval}" in + yes) debug=true ;; + no) debug=false ;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-debug]) ;; + esac],[debug=false]) +AM_CONDITIONAL([DEBUG], [test x$debug = xtrue]) AC_ARG_ENABLE([linuxbsg], - AC_HELP_STRING([--disable-linuxbsg], [ignore linux bsg (sgv4) if present]), - [AC_DEFINE_UNQUOTED(IGNORE_LINUX_BSG, 1, [ignore linux bsg], )], []) + AC_HELP_STRING([--disable-linuxbsg], [option ignored, this is placeholder]), + [AC_DEFINE_UNQUOTED(IGNORE_LINUX_BSG, 1, [option ignored], )], []) AC_ARG_ENABLE([libsgutils], AC_HELP_STRING([--disable-libsgutils], [ignore libsgutils if present]), [have_sgutils=no], - [AC_CHECK_LIB(sgutils2, sg_ll_inquiry, + [AC_CHECK_LIB(sgutils2, sg_ll_inquiry_v2, [SGUTILS_LIBS="-lsgutils2"; have_sgutils=yes], have_sgutils=no)]) AC_SUBST(SGUTILS_LIBS) AM_CONDITIONAL(HAVE_SGUTILS, test x"$have_sgutils" = xyes) @@ -96,5 +107,13 @@ [Disable full SCSI sense strings])], [], [AC_DEFINE_UNQUOTED(SG_SCSI_STRINGS, 1, [full SCSI sense strings], )]) +AC_ARG_ENABLE([nvme-supp], + AC_HELP_STRING([--disable-nvme-supp], [remove all or most NVMe code]), + [AC_DEFINE_UNQUOTED(IGNORE_NVME, 1, [compile out NVMe support], )], []) + +AC_ARG_ENABLE([fast-lebe], + AC_HELP_STRING([--disable-fast-lebe], [use generic little-endian/big-endian code instead]), + [AC_DEFINE_UNQUOTED(IGNORE_FAST_LEBE, 1, [use generic little-endian/big-endian instead], )], []) + # AC_PROG_LIBTOOL AC_OUTPUT(Makefile src/Makefile doc/Makefile scripts/Makefile) diff -Nru sdparm-1.10/debian/changelog sdparm-1.12/debian/changelog --- sdparm-1.10/debian/changelog 2018-09-19 20:04:06.000000000 +0000 +++ sdparm-1.12/debian/changelog 2022-05-30 04:36:13.000000000 +0000 @@ -1,3 +1,16 @@ +sdparm (1.12-1) unstable; urgency=medium + + * New upstream version. (Closes: #969102) + * d/control: + - Add myself to Uploaders. + - Drop cdbs and autotools-dev from Build-Depends. + - Add Homepage field. (Closes: #969101) + * Bump standards version to 4.6.1. + * Bump debhelper version to 13, drop d/compat. + * d/copyright: modernized. + + -- Gürkan Myczko Mon, 30 May 2022 06:36:13 +0200 + sdparm (1.10-1) unstable; urgency=medium * New upstream version 1.10 diff -Nru sdparm-1.10/debian/compat sdparm-1.12/debian/compat --- sdparm-1.10/debian/compat 2018-09-19 20:04:06.000000000 +0000 +++ sdparm-1.12/debian/compat 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -10 diff -Nru sdparm-1.10/debian/control sdparm-1.12/debian/control --- sdparm-1.10/debian/control 2018-09-19 20:04:06.000000000 +0000 +++ sdparm-1.12/debian/control 2022-05-30 04:36:13.000000000 +0000 @@ -2,8 +2,10 @@ Section: admin Priority: optional Maintainer: Tomas Fasth -Build-Depends: cdbs (>= 0.4.15), debhelper (>= 10), autotools-dev -Standards-Version: 4.2.1 +Uploaders: Gürkan Myczko +Build-Depends: debhelper-compat (= 13) +Homepage: https://sg.danny.cz/sg/sdparm.html +Standards-Version: 4.6.1 Package: sdparm Architecture: any diff -Nru sdparm-1.10/debian/copyright sdparm-1.12/debian/copyright --- sdparm-1.10/debian/copyright 2018-09-19 20:04:06.000000000 +0000 +++ sdparm-1.12/debian/copyright 2022-05-30 04:36:13.000000000 +0000 @@ -1,24 +1,37 @@ -This package was debianized by Tomas Fasth on -Wed, 8 Jun 2005 23:48:13 +0000. +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: sdparm +Upstream-Contact: Douglas Gilbert +Source: http://sg.danny.cz/sg/p/ -It was downloaded from http://sg.danny.cz/sg/p/sdparm-0.93.tgz +Files: * +Copyright: 2005-2021 Douglas Gilbert +License: BSD-3-clause -Copyright Holder: Douglas Gilbert +License: BSD-3-clause + 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. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + . + 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. -License: - Copyright (c) 2005-2012 Douglas Gilbert. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted under the terms of the BSD License. - - THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. +Files: debian/* +Copyright: 2022 Gürkan Myczko + 2005-2021 Tomas Fasth +License: BSD-3-clause diff -Nru sdparm-1.10/debian/rules sdparm-1.12/debian/rules --- sdparm-1.10/debian/rules 2018-09-19 20:04:06.000000000 +0000 +++ sdparm-1.12/debian/rules 2022-05-30 04:36:13.000000000 +0000 @@ -1,5 +1,11 @@ #!/usr/bin/make -f -include /usr/share/cdbs/1/rules/buildcore.mk -include /usr/share/cdbs/1/class/autotools.mk -include /usr/share/cdbs/1/rules/debhelper.mk -#include /usr/share/cdbs/1/rules/simple-patchsys.mk +export DEB_BUILD_MAINT_OPTIONS = hardening=+all + +# see ENVIRONMENT in dpkg-buildflags(1) +# package maintainers to append CFLAGS +export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic +# package maintainers to append LDFLAGS +export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed + +%: + dh $@ diff -Nru sdparm-1.10/depcomp sdparm-1.12/depcomp --- sdparm-1.10/depcomp 2015-12-02 00:45:13.000000000 +0000 +++ sdparm-1.12/depcomp 2020-12-25 01:49:20.000000000 +0000 @@ -1,9 +1,9 @@ #! /bin/sh # depcomp - compile a program generating dependencies as side-effects -scriptversion=2013-05-30.07; # UTC +scriptversion=2018-03-07.03; # UTC -# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ # GNU General Public License for more details. # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -783,9 +783,9 @@ # Local Variables: # mode: shell-script # sh-indentation: 2 -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" +# time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: diff -Nru sdparm-1.10/doc/Makefile.in sdparm-1.12/doc/Makefile.in --- sdparm-1.10/doc/Makefile.in 2015-12-02 00:45:13.000000000 +0000 +++ sdparm-1.12/doc/Makefile.in 2020-12-25 01:49:20.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2014 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -153,6 +153,7 @@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -160,7 +161,6 @@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ -CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ @@ -202,6 +202,7 @@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ @@ -236,13 +237,12 @@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ -os_cflags = @os_cflags@ os_deps = @os_deps@ -os_libs = @os_libs@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ @@ -269,16 +269,16 @@ exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu doc/Makefile'; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign doc/Makefile'; \ $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu doc/Makefile + $(AUTOMAKE) --foreign doc/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -339,7 +339,10 @@ cscope cscopelist: -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ diff -Nru sdparm-1.10/doc/sas_disk_blink.8 sdparm-1.12/doc/sas_disk_blink.8 --- sdparm-1.10/doc/sas_disk_blink.8 2013-05-14 18:27:22.000000000 +0000 +++ sdparm-1.12/doc/sas_disk_blink.8 2016-02-25 04:10:03.000000000 +0000 @@ -1,4 +1,4 @@ -.TH SAS_DISK_BLINK "8" "May 2013" "sdparm\-1.08" SDPARM +.TH SAS_DISK_BLINK "8" "February 2016" "sdparm\-1.11" SDPARM .SH NAME sas_disk_blink \- blink the LED on a SAS disk .SH SYNOPSIS @@ -36,7 +36,7 @@ .SH AUTHORS Written by D. Gilbert .SH COPYRIGHT -Copyright \(co 2013 Douglas Gilbert +Copyright \(co 2013\-2016 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 -Nru sdparm-1.10/doc/sdparm.8 sdparm-1.12/doc/sdparm.8 --- sdparm-1.10/doc/sdparm.8 2016-02-18 19:24:07.000000000 +0000 +++ sdparm-1.12/doc/sdparm.8 2021-04-21 22:25:59.000000000 +0000 @@ -1,13 +1,13 @@ -.TH SDPARM "8" "February 2016" "sdparm\-1.10" SDPARM +.TH SDPARM "8" "April 2021" "sdparm\-1.12" SDPARM .SH NAME sdparm \- access SCSI modes pages; read VPD pages; send simple SCSI commands. .SH SYNOPSIS .B sdparm -[\fI\-\-all\fR] [\fI\-\-dbd\fR] [\fI\-\-flexible\fR] [\fI\-\-get=STR\fR] -[\fI\-\-hex\fR] [\fI\-\-long\fR] [\fI\-\-num\-desc\fR] -[\fI\-\-page=PG[,SPG]\fR] [\fI\-\-quiet\fR] [\fI\-\-readonly\fR] -[\fI\-\-six\fR] [\fI\-\-transport=TN\fR] [\fI\-\-vendor=VN\fR] -[\fI\-\-verbose\fR] \fIDEVICE\fR [\fIDEVICE\fR...] +[\fI\-\-all\fR] [\fI\-\-dbd\fR] [\fI\-\-examine\fR] [\fI\-\-flexible\fR] +[\fI\-\-get=STR\fR] [\fI\-\-hex\fR] [\fI\-\-long\fR] [\fI\-\-num\-desc\fR] +[\fI\-\-out\-mask=OM\fR] [\fI\-\-page=PG[,SPG]\fR] [\fI\-\-quiet\fR] +[\fI\-\-readonly\fR] [\fI\-\-six\fR] [\fI\-\-transport=TN\fR] +[\fI\-\-vendor=VN\fR] [\fI\-\-verbose\fR] \fIDEVICE\fR [\fIDEVICE\fR...] .PP .B sdparm [\fI\-\-clear=STR\fR] [\fI\-\-defaults\fR] [\fI\-\-dummy\fR] @@ -17,13 +17,13 @@ [\fI\-\-verbose\fR] \fIDEVICE\fR [\fIDEVICE\fR...] .PP .B sdparm -\fI\-\-command=CMD\fR [\fI\-\-hex\fR] [\fI\-\-readonly\fR] +\fI\-\-command=CMD\fR [\fI\-\-hex\fR] [\fI\-\-long\fR] [\fI\-\-readonly\fR] [\fI\-\-verbose\fR] \fIDEVICE\fR [\fIDEVICE\fR...] .PP .B sdparm -\fI\-\-inquiry\fR [\fI\-\-all\fR] [\fI\-\-flexible\fR] [\fI\-\-hex\fR] -[\fI\-\-num\-desc\fR] [\fI\-\-page=PG[,SPG]\fR] [\fI\-\-quiet\fR] -[\fI\-\-readonly\fR] [\fI\-\-transport=TN\fR] [\fI\-\-vendor=VN\fR] +\fI\-\-inquiry\fR [\fI\-\-all\fR] [\fI\-\-examine\fR] [\fI\-\-flexible\fR] +[\fI\-\-hex\fR] [\fI\-\-num\-desc\fR] [\fI\-\-page=PG[,SPG]\fR] +[\fI\-\-quiet\fR] [\fI\-\-readonly\fR] [\fI\-\-transport=TN\fR] [\fI\-\-verbose\fR] \fIDEVICE\fR [\fIDEVICE\fR...] .PP .B sdparm @@ -34,6 +34,7 @@ \fI\-\-inhex=FN\fR [\fI\-\-all\fR] [\fI\-\-flexible\fR] [\fI\-\-hex\fR] [\fI\-\-inquiry\fR] [\fI\-\-long\fR] [\fI\-\-pdt=DT\fR] [\fI\-\-raw\fR] [\fI\-\-six\fR] [\fI\-\-transport=TN\fR] [\fI\-\-vendor=VN\fR] +[\fI\-\-verbose\fR] .PP .B sdparm \fI\-\-wscan\fR [\fI\-\-verbose\fR] @@ -43,51 +44,50 @@ .SH DESCRIPTION .\" Add any additional description here .PP -This utility fetches and potentially changes SCSI device (e.g. -disk) mode pages. Inquiry data including Vital Product Data (VPD) -pages can also be displayed. Commands associated with starting -and stopping the medium; loading and unloading the medium; and -other housekeeping function may also be issued by this utility. -.PP -The first invocation shown in the synopsis is for accessing (reading) -mode page fields held on the \fIDEVICE\fR. The second form is for -changing mode page fields held on the \fIDEVICE\fR. The third form is for -executing some simple SCSI commands. The fourth form (i.e. -the '\fI\-\-inquiry\fR ... \fIDEVICE\fR' form) is for fetching and decoding -VPD pages from the given \fIDEVICE\fR. The \fI\-\-enumerate\fR form is for -listing out mode or VPD field data held by this utility (and if a -\fIDEVICE\fR is given then it is ignored). The \fI\-\-inhex=FN\fR form -decodes mode or VPD response data provided in the named file (or from stdin -if \fIFN\fR is '\-'); that data may either be in hexadecimal or binary. The -second last form is for Windows only and lists the available storage device -names; see the OPTIONS entry for \fI\-\-wscan\fR. The final form is to -provide command line help or the version number (and date). -.PP -If no options (other than \fIDEVICE\fR) are given then a selection of -common mode page fields for that device are listed. If the \fI\-\-long\fR -option is also given then a description of the fields is placed on the -right of each line. If the \fI\-\-all\fR option is given then all known -mode page fields for that device are listed. Individual fields can be -displayed with the \fI\-\-get=STR\fR option (e.g. '\-\-get=WCE' to fetch -the state of the Writeback Cache Enable field). +This utility fetches and potentially changes SCSI device (e.g. disk) mode +pages. Inquiry data including Vital Product Data (VPD) pages can also be +displayed. Commands associated with starting and stopping the medium; loading +and unloading the medium; and other housekeeping functions may also be issued +by this utility. +.PP +The first invocation shown in the synopsis is for accessing (i.e. reading) +mode page fields held on the \fIDEVICE\fR. The second form is for changing +mode page fields held on the \fIDEVICE\fR. The third form is for executing +some simple SCSI commands. The fourth form (i.e. the '\fI\-\-inquiry\fR ... +\fIDEVICE\fR' form) is for fetching and decoding VPD pages from the given +\fIDEVICE\fR. The \fI\-\-enumerate\fR form is for listing out mode or VPD +field data held by this utility (and if a \fIDEVICE\fR is given then it is +ignored). The \fI\-\-inhex=FN\fR form decodes mode or VPD response data +provided in the named file (or from stdin if \fIFN=\-\fR is given); that data +may either be in hexadecimal or binary. The second last form is for Windows +only and lists the available storage device names; see the OPTIONS entry for +\fI\-\-wscan\fR. The final form is to provide command line help or the version +number (and date) of this utility. +.PP +If no options (other than \fIDEVICE\fR) are given then a selection of common +mode page fields for that device are listed. If the \fI\-\-long\fR option is +also given then a description of the fields is placed on the right of each +line. If the \fI\-\-all\fR option is given then all known mode page fields +for that device are listed. Individual fields can be displayed with the +\fI\-\-get=STR\fR option (e.g. '\-\-get=WCE' to fetch the state of the +Writeback Cache Enable field). .PP This utility completes with an exit status of 0 when successful. For other values see the EXIT STATUS section below. .PP One or more \fIDEVICE\fR arguments can be given. The utility will -essentially apply the given options to each \fIDEVICE\fR in the list. -If an error is detected, it is noted and the utility continues. -Error value 5 (file open or close problem) is treated as lower priority -when other errors are detected. The exit status is the most recently -detected error value (excluding error value 5 if other errors have -been detected). If all actions succeed the exit status is zero. -.PP -By default this utility shows mode pages that are common to all -transport protocols. These are termed as "generic" mode pages. -If there is no match on a generic mode page name or field then -those pages specific to the SAS transport are checked. -Transport protocol specific mode pages are selected with -the \fI\-\-transport=TN\fR option. See the TRANSPORT section below. +essentially apply the given options to each \fIDEVICE\fR in the list. If a +error is detected, it is noted and the utility continues. Error value 5 (file +open or close problem) is treated as lower priority when other errors are +detected. The exit status is the most recently detected error value +(excluding error value 5 if other errors have been detected). If all actions +succeed the exit status is zero. +.PP +By default this utility shows mode pages that are common to all transport +protocols. These are termed as "generic" mode pages. If there is no match on +a generic mode page name or field then those pages specific to the SAS +transport are checked. Transport protocol specific mode pages are selected +with the \fI\-\-transport=TN\fR option. See the TRANSPORT section below. Vendor specific mode pages are selected with the \fI\-\-vendor=VN\fR option. See the VENDORS section below. .PP @@ -122,6 +122,11 @@ output (irrespective of the setting of this option). For this option's action when used with the \fI\-\-enumerate\fR option see the ENUMERATE section below. .br +When used together with the \fI\-\-inquiry\fR option and a \fIDEVICE\fR, the +Supported VPD Pages VPD page [0x0] is output. When this option is used +twice (short form: '\-iaa') then all VPD pages (listed in the Supported VPD +Pages VPD page) are output. +.br By default \fI\-\-inhex=FN\fR will only decode the first mode page found in \fIFN\fR. With this option, more mode pages will be decoded if present. When \fI\-\-transport=TN\fR or \fI\-\-vendor=VN\fR is also given then if a given @@ -149,6 +154,12 @@ \fI\-\-page=PG[,SPG]\fR option to be given to specify the mode page. To make the default mode page values also the saved mode page values, use the \fI\-\-save\fR option as well. +.br +when this option is used twice, the current values in all modes pages are +reverted to their defaults. If the \fI\-\-save\fR option is given as well, +then the current and saved values in all modes pages are reverted to their +defaults. This feature uses the RTD bit in the MODE SELECT command which +was added in draft SPC\-5 revision 11. .TP \fB\-d\fR, \fB\-\-dummy\fR when set inhibits changes being placed in the \fIDEVICE\fR's mode page. @@ -164,6 +175,23 @@ given without other options then the known (generic) mode pages are listed. See the ENUMERATE section below. .TP +\fB\-E\fR, \fB\-\-examine\fR +for mode pages only those with known field names are probed when the +\fI\-\-all\fR option is given. For VPD pages only those pages listed in +"Supported VPD pages page" are decoded. In both cases some pages may be +missed. With this option (i.e. \fI\-\-examine\fR) all mode and VPD pages can +be probed. +.br +For mode pages, this option will probe all mode pages from page number 0x0 to +0x3e. To probe mode subpages give a mode page number with \fI\-\-page=PG\fR +and then all subpages (from 0x0 to 0xfe) are probed. +.br +For VPD pages, use this option with \fI\-\-inquiry\fR. This will cause all VPD +pages from 0x0 to 0xbf to be probed by default. A sequence of VPD pages can +be probed with \fI\-\-page=PG[,SPG]\fR in which case VPD pages from PG (lower +number) to SPG (high number) inclusive are probed. Vendor specific VPD pages +run from 0xc0 to 0xff and can be probed by setting SPG from 0xc0 to 0xff. +.TP \fB\-f\fR, \fB\-\-flexible\fR Some devices, bridges and/or drivers attempt crude transformations between mode sense 6 and 10 byte commands without correctly rebuilding the response. @@ -197,6 +225,9 @@ response in 16 bit hex words; with '\-HH' outputs that response in hex bytes; with '\-HHH' outputs the same response in a format suitable for 'hdparm \-\-Istdin' to decode. +.br +Mode page output with the '\-HHH' option is suitable for a later invocation +of sdparm with the \fI\-\-inhex=FN\fR option. .TP \fB\-i\fR, \fB\-\-inquiry\fR output a VPD page which is in the response of a SCSI INQUIRY command sent @@ -211,8 +242,9 @@ \fIFN\fR is expected to be a file name (or '\-' for stdin) which contains ASCII hexadecimal (or binary) representing the response to MODE SENSE(10). If \fI\-\-six\fR is also given then the response from MODE SENSE(6) is -assumed. A MODE SENSE response contains one or more mode pages. This -utility will decode the first one unless the \fI\-\-all\fR option is +assumed. A MODE SENSE response contains a mode parameter header, then 0 +or more block descriptors followed by one or more mode pages. This utility +will only decode the first mode page unless the \fI\-\-all\fR option is given. In order to decode a mode page the peripheral device type is often needed and can be supplied with the \fI\-\-pdt=DT\fR option. If the \fI\-\-pdt=DT\fR is not given then a mode page found in two device type @@ -232,11 +264,22 @@ fields more information about its values is given on one or more following lines, each prefixed by a tab character. For usage with \fI\-\-enumerate\fR see the ENUMERATE section below. +.br +When this option is used along with \fI\-\-command=capacity\fR then the +READ CAPACITY(16) is sent to the \fIDEVICE\fR and if successful its extended +response is output. .TP \fB\-n\fR, \fB\-\-num\-desc\fR for a mode page that can have descriptors, the number of descriptors for the given page on the \fIDEVICE\fR is output. Otherwise 0 is output. .TP +\fB\-o\fR, \fB\-\-out\-mask\fR=\fIOM\fR +\fIOM\fR is a bit mask for mode page selections that will be printed/output. +The 0x1 value is for the 'current' values, 0x2 is for the 'changeable' +values, 0x4 is for the 'default' values and 0x8 is for the 'saveable' values. +The default value is 0xf (i.e. the OR of all four values set). The option is +useful for limiting the amount of output with the '\-HHH'. +.TP \fB\-p\fR, \fB\-\-page\fR=\fIPG[,SPG]\fR supply the page number (\fIPG\fR) and optionally the sub page number (\fISPG\fR) of the mode (or VPD) page to fetch. These numbers are @@ -256,8 +299,8 @@ \fB\-P\fR, \fB\-\-pdt\fR=\fIDT\fR This option is only active when the \fI\-\-inhex=FN\fR option is given. \fIDT\fR is the peripheral Device Type, a value between 0 and 31 and -can be found in the reponse to the INQUIRY command. The default value -is -1 (which may also be given for \fIDT\fR) and it is interpreted as +can be found in the response to the INQUIRY command. The default value +is \-1 (which may also be given for \fIDT\fR) and it is interpreted as SPC (i.e. common mode pages) or as a wild card. If available this option should be supplied with the \fI\-\-inhex=FN\fR option. .TP @@ -310,21 +353,32 @@ variants are used. RBC and old SCSI devices may need this option. This utility outputs a suggestion to use this option if the SCSI status indicates that the 10 byte cdb variant is not supported. +.br +The SPC\-4 standard (and SPC\-5 drafts) include a note stating that +implementers migrate away from the SCSI MODE SELECT(6) and MODE SENSE(6) +commands in favour of the 10 byte variants (e.g. MODE SEMSE(10)). .TP \fB\-t\fR, \fB\-\-transport\fR=\fITN\fR -Specifies the transport protocol where \fITN\fR is either a number in -the range 0 to 15 (inclusive) or an abbreviation (e.g. "fcp" for -the Fibre Channel Protocol). One way to list available transport protocols -numbers and their associated abbreviations is to give an invalid -transport protocol number such as '\-t x'; another way is '\-e \-l'. +Specifies the transport protocol where \fITN\fR is either a number in the +range 0 to 15 (inclusive) or an abbreviation (e.g. "fcp" for the Fibre +Channel Protocol). Some transports accept multiple abbreviations, for example +srp (SCSI RDMA Protocol) and ib (short for InfiniBand) both are accepted for +transport protocol 0x4 . Also both upper and lower case are accepted so iscsi +and iSCSI are accepted for transport protocol 0x5 . One way to list available +transport protocols numbers and their associated abbreviations is to give an +invalid transport protocol name such as '\-t x'; another way is '\-e \-l'. N.B. The \fI\-\-all\fR option may still be needed to show all available fields. .TP \fB\-M\fR, \fB\-\-vendor\fR=\fIVN\fR Specifies the vendor (i.e. manufacturer) where \fIVN\fR is either a number (0 or more) or an abbreviation (e.g. "sea" for Seagate disk vendor specific). -One way to list available vendor numbers and their associated abbreviations -is to give an invalid vendor number such as '\-M x'; another way is '\-e \-l'. +For tape drives "lto5" and "lto6" are treated as vendors. One way to list +the available vendor numbers and their associated abbreviations is to give an +invalid vendor number such as '\-M x'; another way is '\-e \-l'. +.br +This option only effects mode page decodes, not VPD pages. For vendor +specific VPD pages see the sg_vpd utility. .TP \fB\-v\fR, \fB\-\-verbose\fR increase the level of verbosity, (i.e. debug output). In some cases @@ -342,11 +396,11 @@ See examples below and the "Win32 port" section in the README file. .SH NOTES The reference document used for interpreting mode and VPD pages (and the -INQUIRY standard response) is T10/BSR INCITS 502 Revision 02 (SPC\-5, 3 -January 2015) found at http://www.t10.org . Obsolete and reserved items +INQUIRY standard response) is T10/BSR INCITS 502 Revision 17 (SPC\-5, 19 +September 2017) found at http://www.t10.org . Obsolete and reserved items in the standard INQUIRY response output are displayed in brackets. Recent drafts of other T10 documents are also used: SBC\-4 (disks), SSC\-5 (tapes), -SPL\-4 (SAS transport) and SAT\-4 (SCSI to ATA Translation). +SPL\-5 (SAS transport) and SAT\-4 (SCSI to ATA Translation). .PP A mode page for which no abbreviation is known (e.g. a vendor specific mode page) can be listed in hexadecimal by using the option @@ -366,7 +420,7 @@ the \fI\-\-save\fR option needs to be given. .PP If the \fI\-\-save\fR option is given but the existing mode page indicates (via -its PS bit) that the page is not savable, then this utility generates +its PS bit) that the page is not saveable, then this utility generates an error message. That message suggests to try again without the \fI\-\-save\fR option. .PP @@ -384,7 +438,7 @@ sets aside VPD pages codes from 0xc0 to 0xff (inclusive) for vendor specific pages some of which are decoded in the sg_vpd utility. .PP -To see all VPD pages supported by a \fIDEVICE\fR use 'sg_vpd --all'. +To see all VPD pages supported by a \fIDEVICE\fR use 'sg_vpd \-\-all'. .PP In the linux kernel 2.6 and 3 series any device node that understands a SCSI command set (e.g. SCSI disks and CD/DVD drives) may be specified. More @@ -559,10 +613,15 @@ To obtain more information about the error use the \fI\-v\fR option. .TP capacity -sends a READ CAPACITY command (valid for -disks and cd/dvd media). If successful yields "blocks: " [the number -of blocks], "block_length: " [typically either 512 or 2048] +sends a READ CAPACITY(10) command (valid for disks and cd/dvd media) by +default. If successful yields "blocks: " [the number of +blocks], "block_length: " [typically either 512 or 2048] and "capacity_mib: " [capacity in MibiBytes (1048576 byte units)]. +.PP +If the number of blocks is too large to fit in the 4 byte field provided +by READ CAPACITY(10) or, the \fI\-\-long\fR option is given, then the +READ CAPACITY(16) command is sent. If the \fI\-\-long\fR option is given, +then the extra fields found in the READ CAPACITY(16) response are output. .TP eject stops the medium and ejects it from the device. @@ -573,8 +632,8 @@ can be overridden with '\-f' option). .TP load -loads the medium and and starts it (i.e. spins it up). -See 'eject' command for supported device types. +loads the medium and starts it (i.e. spins it up). See 'eject' command for +supported device types. .TP profile lists the various formats that a CD/DVD/HD\-DVD/BD drive supports. These are @@ -726,7 +785,7 @@ CDROM0 [E] MATSHITA DVD/CDRW UJDA775 CB03 .PP So 'sdparm \-a CDROM0' and 'sdparm \-a E' will show all the (known) mode page -fields for the Matshita DVD/CD drive. By using the '\-\-wsacan' option twice, +fields for the Matshita DVD/CD drive. By using the '\-\-wscan' option twice, the bus type (as seen by the OS) is added to the output: .PP # sdparm \-ww @@ -792,16 +851,29 @@ device. Note that unit attention conditions are usually only reported once by a device. .TP +.B 7 +the \fIDEVICE\fR reports a "data protect" sense key. This implies some +mechanism has blocked writes (or possibly all access to the media). +.TP .B 9 the \fIDEVICE\fR reports an illegal request with an additional sense code of "invalid operation code" which means that it doesn't support the requested command. .TP +.B 10 +the \fIDEVICE\fR reports a "copy aborted". This implies another command or +device problem has stopped a copy operation. The EXTENDED COPY family of +commands (including WRITE USING TOKEN) may return this sense key. +.TP .B 11 the \fIDEVICE\fR reports an aborted command. In some cases aborted commands can be retried immediately (e.g. if the transport aborted the command due to congestion). .TP +.B 14 +the \fIDEVICE\fR reports a miscompare sense key. VERIFY and COMPARE AND +WRITE commands may report this. +.TP .B 15 the utility is unable to open, close or use the given \fIDEVICE\fR. The given file name could be incorrect or there may be permission @@ -818,6 +890,10 @@ to stderr and continue, probably leaving the utility with an exit status of 0 . .TP +.B 22 +the \fIDEVICE\fR reports that the current command or its parameters imply +a logical block address (LBA) that is out of range. +.TP .B 24 the \fIDEVICE\fR reports a SCSI status of "reservation conflict". This means access to the \fIDEVICE\fR with the current command has been blocked @@ -832,7 +908,7 @@ .B 26 the \fIDEVICE\fR reports a SCSI status of "busy". SAM\-5 defines this status as the logical unit is temporarily unable to process a command. -It is recommended to re-issue the command. +It is recommended to re\-issue the command. .TP .B 27 the \fIDEVICE\fR reports a SCSI status of "task set full". @@ -852,6 +928,30 @@ only; in other ports a command timeout will appear as a transport (or OS) error. .TP +.B 40 +the command sent to \fIDEVICE\fR has received an "aborted command" sense +key with an additional sense code of 0x10. This value is related to +problems with protection information (PI or DIF). For example this error +may occur when reading a block on a drive that has never been written (or +is unmapped) if that drive was formatted with type 1, 2 or 3 protection. +.TP +.B 48 +this is an internal message indicating a NVMe status field (SF) is other +than zero after a command has been executed (i.e. something went wrong). +Work in this area is currently experimental. +.TP +.B 49 +low level driver reports a response's residual count (i.e. number of bytes +actually received by HBA is 'requested_bytes \- residual_count') that is +too high. So no useful processing can be done with that response. +.TP +.B 50 + +OS system calls that fail often return a small integer number to help +indicate what the error is. For example in Unix the inability of a system +call to allocate memory returns (in 'errno') ENOMEM which often is +associated with the integer 12. So 62 (i.e. '50 + 12') may be returned +by a utility in this case. +.TP .B 97 the response to a SCSI command failed sanity checks. .TP @@ -863,6 +963,25 @@ any errors that can't be categorized into values 1 to 98 may yield this value. This includes transport and operating system errors after the command has been sent to the device. +.TP +.B 126 +the utility was found but could not be executed. That might occur if the +executable does not have execute permissions. +.TP +.B 127 +This is the exit status for utility not found. That might occur when a +script calls a utility in this package but the PATH environment variable +has not been properly set up, so the script cannot find the executable. +.TP +.B 128 + +If a signal kills a utility then the exit status is 128 plus the signal +number. For example if a segmentation fault occurs then a utility is +typically killed by SIGSEGV which according to 'man 7 signal' has an +associated signal number of 11; so the exit status will be 139 . +.TP +.B 255 +the utility tried to yield an exit status of 255 or larger. That should +not happen; given here for completeness. .PP Most of the error conditions reported above will be repeatable (an example of one that is not is "unit attention") so the utility can @@ -873,13 +992,13 @@ .SH "REPORTING BUGS" Report bugs to . .SH COPYRIGHT -Copyright \(co 2005\-2016 Douglas Gilbert +Copyright \(co 2005\-2021 Douglas Gilbert .br This software is distributed under a FreeBSD license. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH WEB SITE There is a web page discussing this package at -http://sg.danny.cz/sg/sdparm.html . +https://sg.danny.cz/sg/sdparm.html . .SH "SEE ALSO" .B hdparm(hdparm), .B sg_modes, sg_wr_mode, sginfo, sg_inq, sg_vpd(all in sg3_utils), diff -Nru sdparm-1.10/include/freebsd_nvme_ioctl.h sdparm-1.12/include/freebsd_nvme_ioctl.h --- sdparm-1.10/include/freebsd_nvme_ioctl.h 1970-01-01 00:00:00.000000000 +0000 +++ sdparm-1.12/include/freebsd_nvme_ioctl.h 2019-01-11 06:11:25.000000000 +0000 @@ -0,0 +1,175 @@ +#ifndef FREEBSD_NVME_IOCTL_H +#define FREEBSD_NVME_IOCTL_H + +/*- + * 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 + +#ifdef __cplusplus +extern "C" { +#endif + +#define NVME_PASSTHROUGH_CMD _IOWR('n', 0, struct nvme_pt_command) + +#if __FreeBSD_version < 1100110 + +#define NVME_STATUS_GET_SC(st) (st.sc) +#define NVME_STATUS_GET_SCT(st) (st.sct) + + +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 /* not __FreeBSD_version < 1100110 */ +#include +#endif /* __FreeBSD_version < 1100110 */ + +#ifndef nvme_completion_is_error +#define nvme_completion_is_error(cpl) \ + ((cpl)->status.sc != 0 || (cpl)->status.sct != 0) +#endif + +#define NVME_CTRLR_PREFIX "/dev/nvme" +#define NVME_NS_PREFIX "ns" + +#ifdef __cplusplus +} +#endif + +#endif /* for FREEBSD_NVME_IOCTL_H */ diff -Nru sdparm-1.10/include/Makefile.am sdparm-1.12/include/Makefile.am --- sdparm-1.10/include/Makefile.am 2013-06-13 23:51:16.000000000 +0000 +++ sdparm-1.12/include/Makefile.am 2018-03-16 09:38:38.000000000 +0000 @@ -8,12 +8,16 @@ sg_cmds_basic.h \ sg_cmds_extra.h \ sg_cmds_mmc.h \ - sg_pt.h + sg_pr2serr.h \ + sg_unaligned.h \ + sg_pt.h \ + sg_pt_nvme.h if OS_LINUX scsiinclude_HEADERS += \ sg_linux_inc.h \ - sg_io_linux.h + sg_io_linux.h \ + sg_pt_linux.h noinst_HEADERS = \ sg_pt_win32.h diff -Nru sdparm-1.10/include/Makefile.in sdparm-1.12/include/Makefile.in --- sdparm-1.10/include/Makefile.in 2015-12-01 21:03:58.000000000 +0000 +++ sdparm-1.12/include/Makefile.in 2019-08-29 00:15:08.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2014 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -90,7 +90,8 @@ host_triplet = @host@ @OS_LINUX_TRUE@am__append_1 = \ @OS_LINUX_TRUE@ sg_linux_inc.h \ -@OS_LINUX_TRUE@ sg_io_linux.h +@OS_LINUX_TRUE@ sg_io_linux.h \ +@OS_LINUX_TRUE@ sg_pt_linux.h @OS_WIN32_MINGW_TRUE@am__append_2 = sg_pt_win32.h @OS_WIN32_CYGWIN_TRUE@am__append_3 = sg_pt_win32.h @@ -126,8 +127,9 @@ esac am__noinst_HEADERS_DIST = sg_linux_inc.h sg_io_linux.h sg_pt_win32.h am__scsiinclude_HEADERS_DIST = sg_lib.h sg_lib_data.h sg_cmds.h \ - sg_cmds_basic.h sg_cmds_extra.h sg_cmds_mmc.h sg_pt.h \ - sg_linux_inc.h sg_io_linux.h sg_pt_win32.h + sg_cmds_basic.h sg_cmds_extra.h sg_cmds_mmc.h sg_pr2serr.h \ + sg_unaligned.h sg_pt.h sg_pt_nvme.h sg_linux_inc.h \ + sg_io_linux.h sg_pt_linux.h sg_pt_win32.h am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ @@ -218,6 +220,7 @@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ @@ -236,7 +239,9 @@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ +PTHREAD_LIB = @PTHREAD_LIB@ RANLIB = @RANLIB@ +RT_LIB = @RT_LIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ @@ -282,12 +287,11 @@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ -os_cflags = @os_cflags@ -os_libs = @os_libs@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ @@ -298,8 +302,9 @@ top_srcdir = @top_srcdir@ scsiincludedir = $(includedir)/scsi scsiinclude_HEADERS = sg_lib.h sg_lib_data.h sg_cmds.h sg_cmds_basic.h \ - sg_cmds_extra.h sg_cmds_mmc.h sg_pt.h $(am__append_1) \ - $(am__append_2) $(am__append_3) + sg_cmds_extra.h sg_cmds_mmc.h sg_pr2serr.h sg_unaligned.h \ + sg_pt.h sg_pt_nvme.h $(am__append_1) $(am__append_2) \ + $(am__append_3) @OS_FREEBSD_TRUE@noinst_HEADERS = \ @OS_FREEBSD_TRUE@ sg_linux_inc.h \ @OS_FREEBSD_TRUE@ sg_io_linux.h \ @@ -338,16 +343,16 @@ exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu include/Makefile'; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign include/Makefile'; \ $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu include/Makefile + $(AUTOMAKE) --foreign include/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -438,7 +443,10 @@ distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ diff -Nru sdparm-1.10/include/sg_cmds_basic.h sdparm-1.12/include/sg_cmds_basic.h --- sdparm-1.10/include/sg_cmds_basic.h 2014-06-12 13:35:52.000000000 +0000 +++ sdparm-1.12/include/sg_cmds_basic.h 2020-07-24 18:56:21.000000000 +0000 @@ -2,10 +2,12 @@ #define SG_CMDS_BASIC_H /* - * Copyright (c) 2004-2014 Douglas Gilbert. + * Copyright (c) 2004-2020 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. + * + * SPDX-License-Identifier: BSD-2-Clause */ /* @@ -13,77 +15,135 @@ * sg_warnings_strm which is declared in sg_lib.h and can be set with * the sg_set_warnings_strm() function. If not given sg_warnings_strm * defaults to stderr. - * If 'noisy' and 'verbose' are both zero then following functions should - * not output anything to sg_warnings_strm. If 'noisy' is non-zero and - * 'verbose' is zero then Unit Attention, Recovered, Medium and Hardware - * errors (sense keys) send output to sg_warnings_strm. Increasing values - * of 'verbose' send increasing amounts of (debug) output to - * sg_warnings_strm. + * If 'noisy' is false and 'verbose' is zero then following functions should + * not output anything to sg_warnings_strm. If 'noisy' is true and 'verbose' + * is zero then Unit Attention, Recovered, Medium and Hardware errors (sense + * keys) send output to sg_warnings_strm. Increasing values of 'verbose' + * send increasing amounts of (debug) output to sg_warnings_strm. */ +#include +#include + #ifdef __cplusplus extern "C" { #endif +/* Functions with the "_pt" suffix take a pointer to an object (derived from) + * sg_pt_base rather than an open file descriptor as their first argument. + * That object is assumed to be constructed and have a device file descriptor + * associated with it. clear_scsi_pt_obj() is called at the start of each + * "_pt" function. Caller is responsible for lifetime of ptp. + * If the sense buffer is accessed outside the "_pt" function then the caller + * must invoke set_scsi_pt_sense() _prior_ to the "_pt" function. Otherwise + * a sense buffer local to "_pt" function is used. + * Usually the cdb pointer will be NULL going into the "_pt" functions but + * could be given by the caller in which case it will used rather than a + * locally generated one. */ + +struct sg_pt_base; + /* Invokes a SCSI INQUIRY command and yields the response * Returns 0 when successful, SG_LIB_CAT_INVALID_OP -> not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, - * SG_LIB_CAT_ABORTED_COMMAND, -1 -> other errors */ -int sg_ll_inquiry(int sg_fd, int cmddt, int evpd, int pg_op, void * resp, - int mx_resp_len, int noisy, int verbose); + * SG_LIB_CAT_ABORTED_COMMAND, a negated errno or -1 -> other errors */ +int sg_ll_inquiry(int sg_fd, bool cmddt, bool evpd, int pg_op, void * resp, + int mx_resp_len, bool noisy, int verbose); + +/* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when + * successful, various SG_LIB_CAT_* positive values, negated error or -1 + * for other errors. The CMDDT field is obsolete in the INQUIRY cdb (since + * spc3r16 in 2003) so * an argument to set it has been removed (use the + * REPORT SUPPORTED OPERATION CODES command instead). Adds the ability to + * set the command abort timeout and the ability to report the residual + * count. If timeout_secs is zero or less the default command abort timeout + * (60 seconds) is used. If residp is non-NULL then the residual value is + * written where residp points. A residual value of 0 implies mx_resp_len + * bytes have be written where resp points. If the residual value equals + * mx_resp_len then no bytes have been written. */ +int sg_ll_inquiry_v2(int sg_fd, bool evpd, int pg_op, void * resp, + int mx_resp_len, int timeout_secs, int * residp, + bool noisy, int verbose); + +/* Similar to sg_ll_inquiry_v2(). See note above about "_pt" suffix. */ +int sg_ll_inquiry_pt(struct sg_pt_base * ptp, bool evpd, int pg_op, + void * resp, int mx_resp_len, int timeout_secs, + int * residp, bool noisy, int verbose); /* Invokes a SCSI LOG SELECT command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Log Select not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_NOT_READY -> device not ready, * -1 -> other failure */ -int sg_ll_log_select(int sg_fd, int pcr, int sp, int pc, int pg_code, - int subpg_code, unsigned char * paramp, int param_len, - int noisy, int verbose); +int sg_ll_log_select(int sg_fd, bool pcr, bool sp, int pc, int pg_code, + int subpg_code, uint8_t * paramp, int param_len, + bool noisy, int verbose); /* Invokes a SCSI LOG SENSE command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Log Sense not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ -int sg_ll_log_sense(int sg_fd, int ppc, int sp, int pc, int pg_code, - int subpg_code, int paramp, unsigned char * resp, - int mx_resp_len, int noisy, int verbose); +int sg_ll_log_sense(int sg_fd, bool ppc, bool sp, int pc, int pg_code, + int subpg_code, int paramp, uint8_t * resp, + int mx_resp_len, bool noisy, int verbose); + +/* Same as sg_ll_log_sense() apart from timeout_secs and residp. See + * sg_ll_inquiry_v2() for their description */ +int sg_ll_log_sense_v2(int sg_fd, bool ppc, bool sp, int pc, int pg_code, + int subpg_code, int paramp, uint8_t * resp, + int mx_resp_len, int timeout_secs, int * residp, + bool noisy, int verbose); /* Invokes a SCSI MODE SELECT (6) command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ -> * bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready, * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ -int sg_ll_mode_select6(int sg_fd, int pf, int sp, void * paramp, - int param_len, int noisy, int verbose); +int sg_ll_mode_select6(int sg_fd, bool pf, bool sp, void * paramp, + int param_len, bool noisy, int verbose); +/* v2 adds RTD (revert to defaults) bit, added in spc5r11 */ +int sg_ll_mode_select6_v2(int sg_fd, bool pf, bool rtd, bool sp, + void * paramp, int param_len, bool noisy, + int verbose); /* Invokes a SCSI MODE SELECT (10) command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ -> * bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready, * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ -int sg_ll_mode_select10(int sg_fd, int pf, int sp, void * paramp, - int param_len, int noisy, int verbose); +int sg_ll_mode_select10(int sg_fd, bool pf, bool sp, void * paramp, + int param_len, bool noisy, int verbose); +/* v2 adds RTD (revert to defaults) bit, added in spc5r11 */ +int sg_ll_mode_select10_v2(int sg_fd, bool pf, bool rtd, bool sp, + void * paramp, int param_len, bool noisy, + int verbose); /* Invokes a SCSI MODE SENSE (6) command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ -> * bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready, * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ -int sg_ll_mode_sense6(int sg_fd, int dbd, int pc, int pg_code, +int sg_ll_mode_sense6(int sg_fd, bool dbd, int pc, int pg_code, int sub_pg_code, void * resp, int mx_resp_len, - int noisy, int verbose); + bool noisy, int verbose); /* Invokes a SCSI MODE SENSE (10) command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ -> * bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready, * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ -int sg_ll_mode_sense10(int sg_fd, int llbaa, int dbd, int pc, int pg_code, +int sg_ll_mode_sense10(int sg_fd, bool llbaa, bool dbd, int pc, int pg_code, int sub_pg_code, void * resp, int mx_resp_len, - int noisy, int verbose); + bool noisy, int verbose); + +/* Same as sg_ll_mode_sense10() apart from timeout_secs and residp. See + * sg_ll_inquiry_v2() for their description */ +int sg_ll_mode_sense10_v2(int sg_fd, bool llbaa, bool dbd, int pc, + int pg_code, int sub_pg_code, void * resp, + int mx_resp_len, int timeout_secs, int * residp, + bool noisy, int verbose); /* Invokes a SCSI PREVENT ALLOW MEDIUM REMOVAL command (SPC-3) * prevent==0 allows removal, prevent==1 prevents removal ... @@ -92,37 +152,46 @@ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ -int sg_ll_prevent_allow(int sg_fd, int prevent, int noisy, int verbose); +int sg_ll_prevent_allow(int sg_fd, int prevent, bool noisy, int verbose); /* Invokes a SCSI READ CAPACITY (10) command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_UNIT_ATTENTION * -> perhaps media changed, SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ -int sg_ll_readcap_10(int sg_fd, int pmi, unsigned int lba, void * resp, - int mx_resp_len, int noisy, int verbose); +int sg_ll_readcap_10(int sg_fd, bool pmi, unsigned int lba, void * resp, + int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI READ CAPACITY (16) command. Returns 0 -> success, * SG_LIB_CAT_UNIT_ATTENTION -> media changed??, SG_LIB_CAT_INVALID_OP * -> cdb not supported, SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ -int sg_ll_readcap_16(int sg_fd, int pmi, uint64_t llba, void * resp, - int mx_resp_len, int noisy, int verbose); +int sg_ll_readcap_16(int sg_fd, bool pmi, uint64_t llba, void * resp, + int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI REPORT LUNS command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Report Luns not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_NOT_READY (shouldn't happen), -1 -> other failure */ int sg_ll_report_luns(int sg_fd, int select_report, void * resp, - int mx_resp_len, int noisy, int verbose); + int mx_resp_len, bool noisy, int verbose); + +/* Similar to sg_ll_report_luns(). See note above about "_pt" suffix. */ +int sg_ll_report_luns_pt(struct sg_pt_base * ptp, int select_report, + void * resp, int mx_resp_len, bool noisy, + int verbose); /* Invokes a SCSI REQUEST SENSE command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Request Sense not supported??, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ -int sg_ll_request_sense(int sg_fd, int desc, void * resp, int mx_resp_len, - int noisy, int verbose); +int sg_ll_request_sense(int sg_fd, bool desc, void * resp, int mx_resp_len, + bool noisy, int verbose); + +/* Similar to sg_ll_request_sense(). See note above about "_pt" suffix. */ +int sg_ll_request_sense_pt(struct sg_pt_base * ptp, bool desc, void * resp, + int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI START STOP UNIT command (SBC + MMC). * Return of 0 -> success, @@ -134,17 +203,23 @@ * format_layer_number(mmc) fields. They also overlap on the noflush(sbc) * and fl(mmc) one bit field. This is the cause of the awkardly named * pc_mod__fl_num and noflush__fl arguments to this function. */ -int sg_ll_start_stop_unit(int sg_fd, int immed, int pc_mod__fl_num, - int power_cond, int noflush__fl, int loej, - int start, int noisy, int verbose); +int sg_ll_start_stop_unit(int sg_fd, bool immed, int pc_mod__fl_num, + int power_cond, bool noflush__fl, bool loej, + bool start, bool noisy, int verbose); + +/* Similar to sg_ll_start_stop_unit(). See note above about "_pt" suffix. */ +int sg_ll_start_stop_unit_pt(struct sg_pt_base * ptp, bool immed, + int pc_mod__fl_num, int power_cond, + bool noflush__fl, bool loej, bool start, + bool noisy, int verbose); /* Invokes a SCSI SYNCHRONIZE CACHE (10) command. Return of 0 -> success, * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_INVALID_OP -> cdb not supported, * SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */ -int sg_ll_sync_cache_10(int sg_fd, int sync_nv, int immed, int group, - unsigned int lba, unsigned int count, int noisy, +int sg_ll_sync_cache_10(int sg_fd, bool sync_nv, bool immed, int group, + unsigned int lba, unsigned int count, bool noisy, int verbose); /* Invokes a SCSI TEST UNIT READY command. @@ -152,7 +227,11 @@ * Return of 0 -> success, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, * SG_LIB_CAT_ABORTED_COMMAND, -1 -> other failure */ -int sg_ll_test_unit_ready(int sg_fd, int pack_id, int noisy, int verbose); +int sg_ll_test_unit_ready(int sg_fd, int pack_id, bool noisy, int verbose); + +/* Similar to sg_ll_test_unit_ready(). See note above about "_pt" suffix. */ +int sg_ll_test_unit_ready_pt(struct sg_pt_base * ptp, int pack_id, + bool noisy, int verbose); /* Invokes a SCSI TEST UNIT READY command. * 'pack_id' is just for diagnostics, safe to set to 0. @@ -162,19 +241,24 @@ * SG_LIB_CAT_ABORTED_COMMAND, SG_LIB_CAT_NOT_READY -> * device not ready, -1 -> other failure */ int sg_ll_test_unit_ready_progress(int sg_fd, int pack_id, int * progress, - int noisy, int verbose); + bool noisy, int verbose); + +/* Similar to sg_ll_test_unit_ready_progress(). See note above about "_pt" + * suffix. */ +int sg_ll_test_unit_ready_progress_pt(struct sg_pt_base * ptp, int pack_id, + int * progress, bool noisy, int verbose); struct sg_simple_inquiry_resp { - unsigned char peripheral_qualifier; - unsigned char peripheral_type; - unsigned char byte_1; /* was 'rmb' prior to version 1.39 */ + uint8_t peripheral_qualifier; + uint8_t peripheral_type; + uint8_t byte_1; /* was 'rmb' prior to version 1.39 */ /* now rmb == !!(0x80 & byte_1) */ - unsigned char version; /* as per recent drafts: whole of byte 2 */ - unsigned char byte_3; - unsigned char byte_5; - unsigned char byte_6; - unsigned char byte_7; + uint8_t version; /* as per recent drafts: whole of byte 2 */ + uint8_t byte_3; + uint8_t byte_5; + uint8_t byte_6; + uint8_t byte_7; char vendor[9]; /* T10 field is 8 bytes, NUL char appended */ char product[17]; char revision[5]; @@ -183,23 +267,40 @@ /* Yields most of first 36 bytes of a standard INQUIRY (evpd==0) response. * Returns 0 when successful, SG_LIB_CAT_INVALID_OP -> not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND, - * -1 -> other errors */ + * a negated errno or -1 -> other errors */ int sg_simple_inquiry(int sg_fd, struct sg_simple_inquiry_resp * inq_data, - int noisy, int verbose); + bool noisy, int verbose); -/* MODE SENSE commands yield a response that has block descriptors followed - * by mode pages. In most cases users are interested in the first mode page. - * This function returns the (byte) offset of the start of the first mode - * page. Set mode_sense_6 to 1 for MODE SENSE (6) and 0 for MODE SENSE (10). - * Returns >= 0 is successful or -1 if failure. If there is a failure - * a message is written to err_buff. */ -int sg_mode_page_offset(const unsigned char * resp, int resp_len, - int mode_sense_6, char * err_buff, int err_buff_len); +/* Similar to sg_simple_inquiry(). See note above about "_pt" suffix. */ +int sg_simple_inquiry_pt(struct sg_pt_base * ptvp, + struct sg_simple_inquiry_resp * inq_data, bool noisy, + int verbose); + +/* MODE SENSE commands yield a response that has header then zero or more + * block descriptors followed by mode pages. In most cases users are + * interested in the first mode page. This function returns the (byte) + * offset of the start of the first mode page. Set mode_sense_6 to true for + * MODE SENSE (6) and false for MODE SENSE (10). Returns >= 0 is successful + * or -1 if failure. If there is a failure a message is written to err_buff + * if it is non-NULL and err_buff_len > 0. */ +int sg_mode_page_offset(const uint8_t * resp, int resp_len, + bool mode_sense_6, char * err_buff, int err_buff_len); + +/* MODE SENSE commands yield a response that has header then zero or more + * block descriptors followed by mode pages. This functions returns the + * length (in bytes) of those three components. Note that the return value + * can exceed resp_len in which case the MODE SENSE command should be + * re-issued with a larger response buffer. If bd_lenp is non-NULL and if + * successful the block descriptor length (in bytes) is written to *bd_lenp. + * Set mode_sense_6 to true for MODE SENSE (6) and false for MODE SENSE (10) + * responses. Returns -1 if there is an error (e.g. response too short). */ +int sg_msense_calc_length(const uint8_t * resp, int resp_len, + bool mode_sense_6, int * bd_lenp); /* Fetches current, changeable, default and/or saveable modes pages as * indicated by pcontrol_arr for given pg_code and sub_pg_code. If - * mode6==0 then use MODE SENSE (10) else use MODE SENSE (6). If - * flexible set and mode data length seems wrong then try and + * mode6 is true then use MODE SENSE (6) else use MODE SENSE (10). If + * flexible true and mode data length seems wrong then try and * fix (compensating hack for bad device or driver). pcontrol_arr * should have 4 elements for output of current, changeable, default * and saved values respectively. Each element should be NULL or @@ -213,15 +314,15 @@ * respectively have been fetched. If error on current page * then stops and returns that error; otherwise continues if an error is * detected but returns the first error encountered. */ -int sg_get_mode_page_controls(int sg_fd, int mode6, int pg_code, - int sub_pg_code, int dbd, int flexible, +int sg_get_mode_page_controls(int sg_fd, bool mode6, int pg_code, + int sub_pg_code, bool dbd, bool flexible, int mx_mpage_len, int * success_mask, - void * pcontrol_arr[], int * reported_len, + void * pcontrol_arr[], int * reported_lenp, int verbose); /* Returns file descriptor >= 0 if successful. If error in Unix returns negated errno. Implementation calls scsi_pt_open_device(). */ -int sg_cmds_open_device(const char * device_name, int read_only, int verbose); +int sg_cmds_open_device(const char * device_name, bool read_only, int verbose); /* Returns file descriptor >= 0 if successful. If error in Unix returns negated errno. Implementation calls scsi_pt_open_flags(). */ @@ -233,8 +334,8 @@ const char * sg_cmds_version(); +#define SG_NO_DATA_IN 0 -struct sg_pt_base; /* This is a helper function used by sg_cmds_* implementations after the * call to the pass-through. pt_res is returned from do_scsi_pt(). If valid @@ -248,9 +349,13 @@ * output via 'o_sense_cat' pointer (if not NULL). Note that several sense * categories also have data in bytes received; -2 is still returned. */ int sg_cmds_process_resp(struct sg_pt_base * ptvp, const char * leadin, - int pt_res, int mx_di_len, - const unsigned char * sense_b, int noisy, - int verbose, int * o_sense_cat); + int pt_res, bool noisy, int verbose, + int * o_sense_cat); + +/* NVMe devices use a different command set. This function will return true + * if the device associated with 'pvtp' is a NVME device, else it will + * return false (e.g. for SCSI devices). */ +bool sg_cmds_is_nvme(const struct sg_pt_base * ptvp); #ifdef __cplusplus } diff -Nru sdparm-1.10/include/sg_cmds_extra.h sdparm-1.12/include/sg_cmds_extra.h --- sdparm-1.10/include/sg_cmds_extra.h 2016-01-27 15:13:35.000000000 +0000 +++ sdparm-1.12/include/sg_cmds_extra.h 2018-12-07 16:42:06.000000000 +0000 @@ -2,12 +2,16 @@ #define SG_CMDS_EXTRA_H /* - * Copyright (c) 2004-2016 Douglas Gilbert. + * Copyright (c) 2004-2018 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. + * + * SPDX-License-Identifier: BSD-2-Clause */ +#include + #ifdef __cplusplus extern "C" { #endif @@ -20,56 +24,80 @@ * commands (e.g. FORMAT UNIT and the Third Party copy commands) can take * a lot longer than the default timeout. */ - -/* Invokes a ATA PASS-THROUGH (12 or 16) SCSI command (SAT). If cdb_len is - * 12 then a ATA PASS-THROUGH (12) command is called. If cdb_len is 16 then - * a ATA PASS-THROUGH (16) command is called. If cdb_len is any other value - * -1 is returned. After copying from cdbp to an internal buffer, the first - * byte (i.e. offset 0) is set to 0xa1 if cdb_len is 12; or is set to 0x85 - * if cdb_len is 16. The last byte (offset 11 or offset 15) is set to 0x0 in - * the internal buffer. For data in or out transfers set dinp or doutp, and - * dlen to the number of bytes to transfer. If dlen is zero then no data - * transfer is assumed. If sense buffer obtained then it is written to - * sensep, else sensep[0] is set to 0x0. If ATA return descriptor is obtained - * then written to ata_return_dp, else ata_return_dp[0] is set to 0x0. Either - * sensep or ata_return_dp (or both) may be NULL pointers. Returns SCSI - * status value (>= 0) or -1 if other error. Users are expected to check the - * sense buffer themselves. If available the data in resid is written to - * residp. Note in SAT-2 and later, fixed format sense data may be placed in - * *sensep in which case sensep[0]==0x70 . +/* Functions with the "_pt" suffix ^^^ take a pointer to an object (derived + * from) sg_pt_base rather than an open file descriptor as their first + * argument. That object is assumed to be constructed and have a device file + * descriptor * associated with it. Caller is responsible for lifetime of + * ptp. + * ^^^ apart from sg_ll_ata_pt() as 'pass-through' is part of its name. */ + +struct sg_pt_base; + + +/* Invokes a ATA PASS-THROUGH (12, 16 or 32) SCSI command (SAT). This is + * selected by the cdb_len argument that can take values of 12, 16 or 32 + * only (else -1 is returned). The byte at offset 0 (and bytes 0 to 9 + * inclusive for ATA PT(32)) pointed to be cdbp are ignored and apart from + * the control byte, the rest is copied into an internal cdb which is then + * sent to the device. The control byte is byte 11 for ATA PT(12), byte 15 + * for ATA PT(16) and byte 1 for ATA PT(32). If timeout_secs <= 0 then the + * timeout is set to 60 seconds. For data in or out transfers set dinp or + * doutp, and dlen to the number of bytes to transfer. If dlen is zero then + * no data transfer is assumed. If sense buffer obtained then it is written + * to sensep, else sensep[0] is set to 0x0. If ATA return descriptor is + * obtained then written to ata_return_dp, else ata_return_dp[0] is set to + * 0x0. Either sensep or ata_return_dp (or both) may be NULL pointers. + * Returns SCSI status value (>= 0) or -1 if other error. Users are + * expected to check the sense buffer themselves. If available the data in + * resid is written to residp. Note in SAT-2 and later, fixed format sense + * data may be placed in *sensep in which case sensep[0]==0x70, prior to + * SAT-2 descriptor sense format was required (i.e. sensep[0]==0x72). */ -int sg_ll_ata_pt(int sg_fd, const unsigned char * cdbp, int cdb_len, +int sg_ll_ata_pt(int sg_fd, const uint8_t * cdbp, int cdb_len, int timeout_secs, void * dinp, void * doutp, int dlen, - unsigned char * sensep, int max_sense_len, - unsigned char * ata_return_dp, int max_ata_return_len, - int * residp, int verbose); + uint8_t * sensep, int max_sense_len, uint8_t * ata_return_dp, + int max_ata_return_len, int * residp, int verbose); /* Invokes a FORMAT UNIT (SBC-3) command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Format unit not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, - * -1 -> other failure */ -int sg_ll_format_unit(int sg_fd, int fmtpinfo, int longlist, int fmtdata, - int cmplist, int dlist_format, int timeout_secs, - void * paramp, int param_len, int noisy, int verbose); -int sg_ll_format_unit2(int sg_fd, int fmtpinfo, int longlist, int fmtdata, - int cmplist, int dlist_format, int ffmt, + * -1 -> other failure. Note that sg_ll_format_unit2() and + * sg_ll_format_unit_v2() are the same, both add the ffmt argument. */ +int sg_ll_format_unit(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata, + bool cmplist, int dlist_format, int timeout_secs, + void * paramp, int param_len, bool noisy, int verbose); +int sg_ll_format_unit2(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata, + bool cmplist, int dlist_format, int ffmt, int timeout_secs, void * paramp, int param_len, - int noisy, int verbose); - -/* Invokes a SCSI GET LBA STATUS command (SBC). Returns 0 -> success, - * SG_LIB_CAT_INVALID_OP -> GET LBA STATUS not supported, + bool noisy, int verbose); +int sg_ll_format_unit_v2(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata, + bool cmplist, int dlist_format, int ffmt, + int timeout_secs, void * paramp, int param_len, + bool noisy, int verbose); + +/* Invokes a SCSI GET LBA STATUS(16) or GET LBA STATUS(32) command (SBC). + * Returns 0 -> success, + * SG_LIB_CAT_INVALID_OP -> GET LBA STATUS(16 or 32) not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND, - * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */ + * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure. + * sg_ll_get_lba_status() calls the 16 byte variant with rt=0 . */ int sg_ll_get_lba_status(int sg_fd, uint64_t start_llba, void * resp, - int alloc_len, int noisy, int verbose); + int alloc_len, bool noisy, int verbose); +int sg_ll_get_lba_status16(int sg_fd, uint64_t start_llba, uint8_t rt, + void * resp, int alloc_len, bool noisy, + int verbose); +int sg_ll_get_lba_status32(int sg_fd, uint64_t start_llba, uint32_t scan_len, + uint32_t element_id, uint8_t rt, + void * resp, int alloc_len, bool noisy, + int verbose); /* Invokes a SCSI PERSISTENT RESERVE IN command (SPC). Returns 0 * when successful, SG_LIB_CAT_INVALID_OP if command not supported, * SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported, * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */ int sg_ll_persistent_reserve_in(int sg_fd, int rq_servact, void * resp, - int mx_resp_len, int noisy, int verbose); + int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI PERSISTENT RESERVE OUT command (SPC). Returns 0 * when successful, SG_LIB_CAT_INVALID_OP if command not supported, @@ -77,14 +105,14 @@ * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */ int sg_ll_persistent_reserve_out(int sg_fd, int rq_servact, int rq_scope, unsigned int rq_type, void * paramp, - int param_len, int noisy, int verbose); + int param_len, bool noisy, int verbose); /* Invokes a SCSI READ BLOCK LIMITS command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> READ BLOCK LIMITS not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_NOT_READY (shouldn't happen), -1 -> other failure */ int sg_ll_read_block_limits(int sg_fd, void * resp, int mx_resp_len, - int noisy, int verbose); + bool noisy, int verbose); /* Invokes a SCSI READ BUFFER command (SPC). Return of 0 -> * success, SG_LIB_CAT_INVALID_OP -> invalid opcode, @@ -92,16 +120,16 @@ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_read_buffer(int sg_fd, int mode, int buffer_id, int buffer_offset, - void * resp, int mx_resp_len, int noisy, int verbose); + void * resp, int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI READ DEFECT DATA (10) command (SBC). Return of 0 -> * success, SG_LIB_CAT_INVALID_OP -> invalid opcode, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ -int sg_ll_read_defect10(int sg_fd, int req_plist, int req_glist, +int sg_ll_read_defect10(int sg_fd, bool req_plist, bool req_glist, int dl_format, void * resp, int mx_resp_len, - int noisy, int verbose); + bool noisy, int verbose); /* Invokes a SCSI READ LONG (10) command (SBC). Note that 'xfer_len' * is in bytes. Returns 0 -> success, @@ -111,8 +139,8 @@ * field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ -int sg_ll_read_long10(int sg_fd, int pblock, int correct, unsigned int lba, - void * resp, int xfer_len, int * offsetp, int noisy, +int sg_ll_read_long10(int sg_fd, bool pblock, bool correct, unsigned int lba, + void * resp, int xfer_len, int * offsetp, bool noisy, int verbose); /* Invokes a SCSI READ LONG (16) command (SBC). Note that 'xfer_len' @@ -123,8 +151,8 @@ * field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ -int sg_ll_read_long16(int sg_fd, int pblock, int correct, uint64_t llba, - void * resp, int xfer_len, int * offsetp, int noisy, +int sg_ll_read_long16(int sg_fd, bool pblock, bool correct, uint64_t llba, + void * resp, int xfer_len, int * offsetp, bool noisy, int verbose); /* Invokes a SCSI READ MEDIA SERIAL NUMBER command. Return of 0 -> success, @@ -133,22 +161,39 @@ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_read_media_serial_num(int sg_fd, void * resp, int mx_resp_len, - int noisy, int verbose); + bool noisy, int verbose); /* Invokes a SCSI REASSIGN BLOCKS command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */ -int sg_ll_reassign_blocks(int sg_fd, int longlba, int longlist, void * paramp, - int param_len, int noisy, int verbose); +int sg_ll_reassign_blocks(int sg_fd, bool longlba, bool longlist, + void * paramp, int param_len, bool noisy, + int verbose); /* Invokes a SCSI RECEIVE DIAGNOSTIC RESULTS command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Receive diagnostic results not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ -int sg_ll_receive_diag(int sg_fd, int pcv, int pg_code, void * resp, - int mx_resp_len, int noisy, int verbose); +int sg_ll_receive_diag(int sg_fd, bool pcv, int pg_code, void * resp, + int mx_resp_len, bool noisy, int verbose); + +/* Same as sg_ll_receive_diag() but with added timeout_secs and residp + * arguments. Adds the ability to set the command abort timeout + * and the ability to report the residual count. If timeout_secs is zero + * or less the default command abort timeout (60 seconds) is used. + * If residp is non-NULL then the residual value is written where residp + * points. A residual value of 0 implies mx_resp_len bytes have be written + * where resp points. If the residual value equals mx_resp_len then no + * bytes have been written. */ +int sg_ll_receive_diag_v2(int sg_fd, bool pcv, int pg_code, void * resp, + int mx_resp_len, int timeout_secs, int * residp, + bool noisy, int verbose); + +int sg_ll_receive_diag_pt(struct sg_pt_base * ptp, bool pcv, int pg_code, + void * resp, int mx_resp_len, int timeout_secs, + int * residp, bool noisy, int verbose); /* Invokes a SCSI REPORT IDENTIFYING INFORMATION command. This command was * called REPORT DEVICE IDENTIFIER prior to spc4r07. Return of 0 -> success, @@ -157,43 +202,48 @@ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_report_id_info(int sg_fd, int itype, void * resp, int max_resp_len, - int noisy, int verbose); + bool noisy, int verbose); /* Invokes a SCSI REPORT TARGET PORT GROUPS command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Report Target Port Groups not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */ int sg_ll_report_tgt_prt_grp(int sg_fd, void * resp, int mx_resp_len, - int noisy, int verbose); + bool noisy, int verbose); int sg_ll_report_tgt_prt_grp2(int sg_fd, void * resp, int mx_resp_len, - int extended, int noisy, int verbose); + bool extended, bool noisy, int verbose); /* Invokes a SCSI SET TARGET PORT GROUPS command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Report Target Port Groups not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */ -int sg_ll_set_tgt_prt_grp(int sg_fd, void * paramp, int param_len, int noisy, +int sg_ll_set_tgt_prt_grp(int sg_fd, void * paramp, int param_len, bool noisy, int verbose); /* Invokes a SCSI REPORT REFERRALS command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Report Referrals not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */ -int sg_ll_report_referrals(int sg_fd, uint64_t start_llba, int one_seg, - void * resp, int mx_resp_len, int noisy, +int sg_ll_report_referrals(int sg_fd, uint64_t start_llba, bool one_seg, + void * resp, int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI SEND DIAGNOSTIC command. Foreground, extended self tests can - * take a long time, if so set long_duration flag in which case the timout + * take a long time, if so set long_duration flag in which case the timeout * is set to 7200 seconds; if the value of long_duration is > 7200 then that * value is taken as the timeout value in seconds. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Send diagnostic not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ -int sg_ll_send_diag(int sg_fd, int sf_code, int pf_bit, int sf_bit, - int devofl_bit, int unitofl_bit, int long_duration, - void * paramp, int param_len, int noisy, int verbose); +int sg_ll_send_diag(int sg_fd, int st_code, bool pf_bit, bool st_bit, + bool devofl_bit, bool unitofl_bit, int long_duration, + void * paramp, int param_len, bool noisy, int verbose); + +int sg_ll_send_diag_pt(struct sg_pt_base * ptp, int st_code, bool pf_bit, + bool st_bit, bool devofl_bit, bool unitofl_bit, + int long_duration, void * paramp, int param_len, + bool noisy, int verbose); /* Invokes a SCSI SET IDENTIFYING INFORMATION command. This command was * called SET DEVICE IDENTIFIER prior to spc4r07. Return of 0 -> success, @@ -202,18 +252,18 @@ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_set_id_info(int sg_fd, int itype, void * paramp, int param_len, - int noisy, int verbose); + bool noisy, int verbose); /* Invokes a SCSI UNMAP (SBC-3) command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> command not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */ int sg_ll_unmap(int sg_fd, int group_num, int timeout_secs, void * paramp, - int param_len, int noisy, int verbose); + int param_len, bool noisy, int verbose); /* Invokes a SCSI UNMAP (SBC-3) command. Version 2 adds anchor field * (sbc3r22). Otherwise same as sg_ll_unmap() . */ -int sg_ll_unmap_v2(int sg_fd, int anchor, int group_num, int timeout_secs, - void * paramp, int param_len, int noisy, int verbose); +int sg_ll_unmap_v2(int sg_fd, bool anchor, int group_num, int timeout_secs, + void * paramp, int param_len, bool noisy, int verbose); /* Invokes a SCSI VERIFY (10) command (SBC and MMC). * Note that 'veri_len' is in blocks while 'data_out_len' is in bytes. @@ -224,9 +274,9 @@ * SG_LIB_CAT_MEDIUM_HARD_WITH_INFO -> as previous, with valid info, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_MISCOMPARE, -1 -> other failure */ -int sg_ll_verify10(int sg_fd, int vrprotect, int dpo, int bytechk, +int sg_ll_verify10(int sg_fd, int vrprotect, bool dpo, int bytechk, unsigned int lba, int veri_len, void * data_out, - int data_out_len, unsigned int * infop, int noisy, + int data_out_len, unsigned int * infop, bool noisy, int verbose); /* Invokes a SCSI VERIFY (16) command (SBC). @@ -238,10 +288,10 @@ * SG_LIB_CAT_MEDIUM_HARD_WITH_INFO -> as previous, with valid info, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_MISCOMPARE, -1 -> other failure */ -int sg_ll_verify16(int sg_fd, int vrprotect, int dpo, int bytechk, +int sg_ll_verify16(int sg_fd, int vrprotect, bool dpo, int bytechk, uint64_t llba, int veri_len, int group_num, void * data_out, int data_out_len, uint64_t * infop, - int noisy, int verbose); + bool noisy, int verbose); /* Invokes a SCSI WRITE BUFFER command (SPC). Return of 0 -> * success, SG_LIB_CAT_INVALID_OP -> invalid opcode, @@ -249,9 +299,20 @@ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_write_buffer(int sg_fd, int mode, int buffer_id, int buffer_offset, - void * paramp, int param_len, int noisy, int verbose); -/* Need a sg_ll_write_buffer_v2() function because SPC-4 rev32 has added - * a "mode specific" field. Wait for next rev change of this library */ + void * paramp, int param_len, bool noisy, int verbose); + +/* Invokes a SCSI WRITE BUFFER command (SPC). Return of 0 -> + * success, SG_LIB_CAT_INVALID_OP -> invalid opcode, + * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, + * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, + * -1 -> other failure. Adds mode specific field (spc4r32) and timeout + * to command abort to override default of 60 seconds. If timeout_secs is + * 0 or less then the default timeout is used instead. */ +int +sg_ll_write_buffer_v2(int sg_fd, int mode, int m_specific, int buffer_id, + uint32_t buffer_offset, void * paramp, + uint32_t param_len, int timeout_secs, bool noisy, + int verbose); /* Invokes a SCSI WRITE LONG (10) command (SBC). Note that 'xfer_len' * is in bytes. Returns 0 -> success, @@ -261,9 +322,9 @@ * field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ -int sg_ll_write_long10(int sg_fd, int cor_dis, int wr_uncor, int pblock, +int sg_ll_write_long10(int sg_fd, bool cor_dis, bool wr_uncor, bool pblock, unsigned int lba, void * data_out, int xfer_len, - int * offsetp, int noisy, int verbose); + int * offsetp, bool noisy, int verbose); /* Invokes a SCSI WRITE LONG (16) command (SBC). Note that 'xfer_len' * is in bytes. Returns 0 -> success, @@ -273,9 +334,9 @@ * field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ -int sg_ll_write_long16(int sg_fd, int cor_dis, int wr_uncor, int pblock, +int sg_ll_write_long16(int sg_fd, bool cor_dis, bool wr_uncor, bool pblock, uint64_t llba, void * data_out, int xfer_len, - int * offsetp, int noisy, int verbose); + int * offsetp, bool noisy, int verbose); /* Invokes a SPC-3 SCSI RECEIVE COPY RESULTS command. In SPC-4 this function * supports all service action variants of the THIRD-PARTY COPY IN opcode. @@ -284,7 +345,7 @@ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_receive_copy_results(int sg_fd, int sa, int list_id, void * resp, - int mx_resp_len, int noisy, int verbose); + int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI EXTENDED COPY(LID1) command. For EXTENDED COPY(LID4) * including POPULATE TOKEN and WRITE USING TOKEN use @@ -293,7 +354,7 @@ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ -int sg_ll_extended_copy(int sg_fd, void * paramp, int param_len, int noisy, +int sg_ll_extended_copy(int sg_fd, void * paramp, int param_len, bool noisy, int verbose); /* Handles various service actions associated with opcode 0x83 which is @@ -305,7 +366,20 @@ * -1 -> other failure */ int sg_ll_3party_copy_out(int sg_fd, int sa, unsigned int list_id, int group_num, int timeout_secs, void * paramp, - int param_len, int noisy, int verbose); + int param_len, bool noisy, int verbose); + +/* Invokes a SCSI PRE-FETCH(10), PRE-FETCH(16) or SEEK(10) command (SBC). + * Returns 0 -> success, 25 (SG_LIB_CAT_CONDITION_MET), various SG_LIB_CAT_* + * positive values or -1 -> other errors. Note that CONDITION MET status + * is returned when immed=true and num_blocks can fit in device's cache, + * somewaht strangely, GOOD status (return 0) is returned if num_blocks + * cannot fit in device's cache. If do_seek10==true then does a SEEK(10) + * command with given lba, if that LBA is < 2**32 . Unclear what SEEK(10) + * does, assume it is like PRE-FETCH. If timeout_secs is 0 (or less) then + * use DEF_PT_TIMEOUT (60 seconds) as command timeout. */ +int sg_ll_pre_fetch_x(int sg_fd, bool do_seek10, bool cdb16, bool immed, + uint64_t lba, uint32_t num_blocks, int group_num, + int timeout_secs, bool noisy, int verbose); #ifdef __cplusplus } diff -Nru sdparm-1.10/include/sg_cmds_mmc.h sdparm-1.12/include/sg_cmds_mmc.h --- sdparm-1.10/include/sg_cmds_mmc.h 2013-08-28 13:44:26.000000000 +0000 +++ sdparm-1.12/include/sg_cmds_mmc.h 2018-12-07 16:42:06.000000000 +0000 @@ -2,10 +2,12 @@ #define SG_CMDS_MMC_H /* - * Copyright (c) 2008-2013 Douglas Gilbert. + * Copyright (c) 2008-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. + * + * SPDX-License-Identifier: BSD-2-Clause */ #ifdef __cplusplus @@ -18,7 +20,7 @@ * supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported, * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */ int sg_ll_get_config(int sg_fd, int rt, int starting, void * resp, - int mx_resp_len, int noisy, int verbose); + int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI GET PERFORMANCE command (MMC-3...6). * Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not @@ -26,7 +28,7 @@ * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */ int sg_ll_get_performance(int sg_fd, int data_type, unsigned int starting_lba, int max_num_desc, int type, void * resp, - int mx_resp_len, int noisy, int verbose); + int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI SET CD SPEED command (MMC). * Return of 0 -> success, SG_LIB_CAT_INVALID_OP -> command not supported, @@ -34,7 +36,7 @@ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_set_cd_speed(int sg_fd, int rot_control, int drv_read_speed, - int drv_write_speed, int noisy, int verbose); + int drv_write_speed, bool noisy, int verbose); /* Invokes a SCSI SET STREAMING command (MMC). Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Set Streaming not supported, @@ -42,7 +44,7 @@ * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_NOT_READY -> device not ready, * -1 -> other failure */ int sg_ll_set_streaming(int sg_fd, int type, void * paramp, int param_len, - int noisy, int verbose); + bool noisy, int verbose); #ifdef __cplusplus diff -Nru sdparm-1.10/include/sg_io_linux.h sdparm-1.12/include/sg_io_linux.h --- sdparm-1.10/include/sg_io_linux.h 2015-12-20 16:23:44.000000000 +0000 +++ sdparm-1.12/include/sg_io_linux.h 2020-11-10 01:46:05.000000000 +0000 @@ -2,19 +2,21 @@ #define SG_IO_LINUX_H /* - * Copyright (c) 2004-2015 Douglas Gilbert. + * Copyright (c) 2004-2020 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. + * + * SPDX-License-Identifier: BSD-2-Clause */ /* - * Version 1.04 [20151217] + * Version 1.08 [20201102] */ /* - * This header file contains linux specific information related to the SCSI - * command pass through in the SCSI generic (sg) driver and the linux + * This header file contains Linux specific information related to the SCSI + * command pass through in the SCSI generic (sg) driver and the Linux * block layer. */ @@ -25,7 +27,7 @@ extern "C" { #endif -/* The following are 'host_status' codes */ +/* host_bytes: DID_* are Linux SCSI result (a 32 bit variable) bits 16:23 */ #ifndef DID_OK #define DID_OK 0x00 #endif @@ -82,7 +84,7 @@ #define SG_LIB_DID_TARGET_FAILURE DID_TARGET_FAILURE #define SG_LIB_DID_NEXUS_FAILURE DID_NEXUS_FAILURE -/* The following are 'driver_status' codes */ +/* DRIVER_* are Linux SCSI result (a 32 bit variable) bits 24:27 */ #ifndef DRIVER_OK #define DRIVER_OK 0x00 #endif @@ -96,6 +98,7 @@ #define DRIVER_HARD 0x07 #define DRIVER_SENSE 0x08 /* Sense_buffer has been set */ +/* SUGGEST_* are Linux SCSI result (a 32 bit variable) bits 28:31 */ /* N.B. the SUGGEST_* codes are no longer used in Linux and are only kept * to stop compilation breakages. * Following "suggests" are "or-ed" with one of previous 8 entries */ @@ -142,41 +145,55 @@ void sg_print_driver_status(int driver_status); /* sg_chk_n_print() returns 1 quietly if there are no errors/warnings - else it prints errors/warnings (prefixed by 'leadin') to - 'sg_warnings_fd' and returns 0. raw_sinfo indicates whether the - raw sense buffer (in ASCII hex) should be printed. */ + * else it prints errors/warnings (prefixed by 'leadin') to + * 'sg_warnings_fd' and returns 0. raw_sinfo indicates whether the + * raw sense buffer (in ASCII hex) should be printed. */ int sg_chk_n_print(const char * leadin, int masked_status, int host_status, - int driver_status, const unsigned char * sense_buffer, - int sb_len, int raw_sinfo); + int driver_status, const uint8_t * sense_buffer, + int sb_len, bool raw_sinfo); /* The following function declaration is for the sg version 3 driver. */ struct sg_io_hdr; + /* sg_chk_n_print3() returns 1 quietly if there are no errors/warnings; - else it prints errors/warnings (prefixed by 'leadin') to - 'sg_warnings_fd' and returns 0. */ + * else it prints errors/warnings (prefixed by 'leadin') to + * 'sg_warnings_fd' and returns 0. For sg_io_v4 interface use + * sg_linux_sense_print() instead. */ int sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp, - int raw_sinfo); + bool raw_sinfo); + +/* Returns 1 if no errors found and thus nothing printed; otherwise + * prints error/warning (prefix by 'leadin') to stderr (pr2ws) and + * returns 0. */ +int sg_linux_sense_print(const char * leadin, int scsi_status, + int host_status, int driver_status, + const uint8_t * sense_buffer, int sb_len, + bool raw_sinfo); /* Calls sg_scsi_normalize_sense() after obtaining the sense buffer and - its length from the struct sg_io_hdr pointer. If these cannot be - obtained, 0 is returned. */ -int sg_normalize_sense(const struct sg_io_hdr * hp, - struct sg_scsi_sense_hdr * sshp); + * its length from the struct sg_io_hdr pointer. If these cannot be + * obtained, false is returned. For sg_io_v4 interface use + * sg_scsi_normalize_sense() function instead [see sg_lib.h]. */ +bool sg_normalize_sense(const struct sg_io_hdr * hp, + struct sg_scsi_sense_hdr * sshp); +/* Returns SG_LIB_CAT_* value. */ int sg_err_category(int masked_status, int host_status, int driver_status, - const unsigned char * sense_buffer, int sb_len); + const uint8_t * sense_buffer, int sb_len); +/* Returns SG_LIB_CAT_* value. */ int sg_err_category_new(int scsi_status, int host_status, int driver_status, - const unsigned char * sense_buffer, int sb_len); + const uint8_t * sense_buffer, int sb_len); -/* The following function declaration is for the sg version 3 driver. */ +/* The following function declaration is for the sg version 3 driver. for + * sg_io_v4 interface use sg_err_category_new() function instead */ int sg_err_category3(struct sg_io_hdr * hp); /* Note about SCSI status codes found in older versions of Linux. - Linux has traditionally used a 1 bit right shifted and masked - version of SCSI standard status codes. Now CHECK_CONDITION - and friends (in ) are deprecated. */ + * Linux has traditionally used a 1 bit right shifted and masked + * version of SCSI standard status codes. Now CHECK_CONDITION + * and friends (in ) are deprecated. */ #ifdef __cplusplus } diff -Nru sdparm-1.10/include/sg_lib_data.h sdparm-1.12/include/sg_lib_data.h --- sdparm-1.10/include/sg_lib_data.h 2016-02-03 15:09:29.000000000 +0000 +++ sdparm-1.12/include/sg_lib_data.h 2019-01-14 02:42:05.000000000 +0000 @@ -2,10 +2,12 @@ #define SG_LIB_DATA_H /* - * Copyright (c) 2007-2016 Douglas Gilbert. + * Copyright (c) 2007-2019 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. + * + * SPDX-License-Identifier: BSD-2-Clause */ /* @@ -32,7 +34,8 @@ #define SG_PERSISTENT_RESERVE_IN 0x5e #define SG_PERSISTENT_RESERVE_OUT 0x5f #define SG_READ_ATTRIBUTE 0x8c -#define SG_READ_BUFFER 0x3c +#define SG_READ_BUFFER 0x3c /* now READ BUFFER(10) */ +#define SG_READ_BUFFER_16 0x9b #define SG_READ_POSITION 0x34 /* SSC command with service actions */ #define SG_SANITIZE 0x48 #define SG_SERVICE_ACTION_BIDI 0x9d @@ -47,25 +50,52 @@ +struct sg_lib_simple_value_name_t { + int value; + const char * name; +}; + struct sg_lib_value_name_t { int value; int peri_dev_type; /* 0 -> SPC and/or PDT_DISK, >0 -> PDT */ const char * name; }; +struct sg_value_2names_t { + int value; + const char * name; + const char * name2; +}; + struct sg_lib_asc_ascq_t { - unsigned char asc; /* additional sense code */ - unsigned char ascq; /* additional sense code qualifier */ + uint8_t asc; /* additional sense code */ + uint8_t ascq; /* additional sense code qualifier */ const char * text; }; struct sg_lib_asc_ascq_range_t { - unsigned char asc; /* additional sense code (ASC) */ - unsigned char ascq_min; /* ASCQ minimum in range */ - unsigned char ascq_max; /* ASCQ maximum in range */ + uint8_t asc; /* additional sense code (ASC) */ + uint8_t ascq_min; /* ASCQ minimum in range */ + uint8_t ascq_max; /* ASCQ maximum in range */ const char * text; }; +/* First use: SCSI status, sense_key, asc, ascq tuple */ +struct sg_lib_4tuple_u8 { + uint8_t t1; + uint8_t t2; + uint8_t t3; + uint8_t t4; +}; + +struct sg_cmd_response_t { + int din_len; + int dout_len; + int resid; + int resid2; + const uint8_t * sbp; +}; + extern const char * sg_lib_version_str; @@ -91,11 +121,18 @@ extern struct sg_lib_value_name_t sg_lib_read_pos_arr[]; extern struct sg_lib_asc_ascq_range_t sg_lib_asc_ascq_range[]; extern struct sg_lib_asc_ascq_t sg_lib_asc_ascq[]; +extern struct sg_lib_value_name_t sg_lib_scsi_feature_sets[]; extern const char * sg_lib_sense_key_desc[]; extern const char * sg_lib_pdt_strs[]; extern const char * sg_lib_transport_proto_strs[]; extern int sg_lib_pdt_decay_arr[]; +extern struct sg_lib_simple_value_name_t sg_lib_nvme_admin_cmd_arr[]; +extern struct sg_lib_simple_value_name_t sg_lib_nvme_nvm_cmd_arr[]; +extern struct sg_lib_value_name_t sg_lib_nvme_cmd_status_arr[]; +extern struct sg_lib_4tuple_u8 sg_lib_scsi_status_sense_arr[]; + +extern struct sg_value_2names_t sg_exit_str_arr[]; #ifdef __cplusplus } diff -Nru sdparm-1.10/include/sg_lib.h sdparm-1.12/include/sg_lib.h --- sdparm-1.10/include/sg_lib.h 2016-02-02 04:21:27.000000000 +0000 +++ sdparm-1.12/include/sg_lib.h 2021-03-20 18:27:09.000000000 +0000 @@ -2,10 +2,12 @@ #define SG_LIB_H /* - * Copyright (c) 2004-2016 Douglas Gilbert. + * Copyright (c) 2004-2021 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. + * + * SPDX-License-Identifier: BSD-2-Clause */ /* @@ -14,8 +16,8 @@ * The intention is to keep this file and the related sg_lib.c file * as open source and encourage their unencumbered use. * - * Current version number is in the sg_lib.c file and can be accessed - * with the sg_lib_version() function. + * Current version number of this library is in the sg_lib_data.c file and + * can be accessed with the sg_lib_version() function. */ @@ -27,11 +29,12 @@ * http://www.t10.org . Virtually all devices in the Linux SCSI subsystem * utilize SCSI command sets. Many devices in other Linux device subsystems * utilize SCSI command sets either natively or via emulation (e.g. a - * parallel ATA disk in a USB enclosure). + * SATA disk in a USB enclosure). */ #include #include +#include #ifdef __cplusplus extern "C" { @@ -66,10 +69,10 @@ #define SAM_STAT_CHECK_CONDITION 0x2 #define SAM_STAT_CONDITION_MET 0x4 #define SAM_STAT_BUSY 0x8 -#define SAM_STAT_INTERMEDIATE 0x10 /* obsolete in SAM-4 */ +#define SAM_STAT_INTERMEDIATE 0x10 /* obsolete in SAM-4 */ #define SAM_STAT_INTERMEDIATE_CONDITION_MET 0x14 /* obsolete in SAM-4 */ #define SAM_STAT_RESERVATION_CONFLICT 0x18 -#define SAM_STAT_COMMAND_TERMINATED 0x22 /* obsolete in SAM-3 */ +#define SAM_STAT_COMMAND_TERMINATED 0x22 /* obsolete in SAM-3 */ #define SAM_STAT_TASK_SET_FULL 0x28 #define SAM_STAT_ACA_ACTIVE 0x30 #define SAM_STAT_TASK_ABORTED 0x40 @@ -98,7 +101,7 @@ #define TPROTO_SPI 1 #define TPROTO_SSA 2 #define TPROTO_1394 3 -#define TPROTO_SRP 4 +#define TPROTO_SRP 4 /* SCSI over RDMA */ #define TPROTO_ISCSI 5 #define TPROTO_SAS 6 #define TPROTO_ADT 7 @@ -108,31 +111,64 @@ #define TPROTO_PCIE 0xb /* includes NVMe */ #define TPROTO_NONE 0xf +/* SCSI Feature Sets (sfs) */ +#define SCSI_FS_SPC_DISCOVERY_2016 0x1 +#define SCSI_FS_SBC_BASE_2010 0x102 +#define SCSI_FS_SBC_BASE_2016 0x101 +#define SCSI_FS_SBC_BASIC_PROV_2016 0x103 +#define SCSI_FS_SBC_DRIVE_MAINT_2016 0x104 +#define SCSI_FS_ZBC_HOST_AWARE_2020 0x300 +#define SCSI_FS_ZBC_HOST_MANAGED_2020 0x301 +#define SCSI_FS_ZBC_DOMAINS_REALMS_2020 0x302 + +/* Often SCSI responses use the highest integer that can fit in a field + * to indicate "unbounded" or limit does not apply. Sometimes represented + * in output as "-1" for brevity */ +#define SG_LIB_UNBOUNDED_16BIT 0xffff +#define SG_LIB_UNBOUNDED_32BIT 0xffffffffU +#define SG_LIB_UNBOUNDED_64BIT 0xffffffffffffffffULL + +#if (__STDC_VERSION__ >= 199901L) /* C99 or later */ + typedef uintptr_t sg_uintptr_t; +#else + typedef unsigned long sg_uintptr_t; +#endif + +/* Borrowed from Linux kernel; no check that 'arr' actually is one */ +#define SG_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) -/* The format of the version string is like this: "1.87 20130731" */ + +/* The format of the version string is like this: "2.26 20170906" */ const char * sg_lib_version(); /* Returns length of SCSI command given the opcode (first byte). * Yields the wrong answer for variable length commands (opcode=0x7f) * and potentially some vendor specific commands. */ -int sg_get_command_size(unsigned char cdb_byte0); +int sg_get_command_size(uint8_t cdb_byte0); /* Command name given pointer to the cdb. Certain command names - * depend on peripheral type (give 0 if unknown). Places command + * depend on peripheral type (give 0 or -1 if unknown). Places command * name into buff and will write no more than buff_len bytes. */ -void sg_get_command_name(const unsigned char * cdbp, int peri_type, - int buff_len, char * buff); +void sg_get_command_name(const uint8_t * cdbp, int peri_type, int buff_len, + char * buff); /* Command name given only the first byte (byte 0) of a cdb and - * peripheral type. */ -void sg_get_opcode_name(unsigned char cdb_byte0, int peri_type, int buff_len, + * peripheral type (give 0 or -1 if unknown). */ +void sg_get_opcode_name(uint8_t cdb_byte0, int peri_type, int buff_len, char * buff); /* Command name given opcode (byte 0), service action and peripheral type. - * If no service action give 0, if unknown peripheral type give 0. */ -void sg_get_opcode_sa_name(unsigned char cdb_byte0, int service_action, + * If no service action give 0, if unknown peripheral type give 0 or -1 . */ +void sg_get_opcode_sa_name(uint8_t cdb_byte0, int service_action, int peri_type, int buff_len, char * buff); +/* Fetch NVMe command name given first byte (byte offset 0 in 64 byte + * command) of command. Gets Admin NVMe command name if 'admin' is true + * (e.g. opcode=0x6 -> Identify), otherwise gets NVM command set name + * (e.g. opcode=0 -> Flush). Returns 'buff'. */ +char * sg_get_nvme_opcode_name(uint8_t cmd_byte0, bool admin, int buff_len, + char * buff); + /* Fetch scsi status string. */ void sg_get_scsi_status_str(int scsi_status, int buff_len, char * buff); @@ -143,35 +179,35 @@ * The original sense buffer should be kept around for those cases * in which more information is required (e.g. the LBA of a MEDIUM ERROR). */ struct sg_scsi_sense_hdr { - unsigned char response_code; /* permit: 0x0, 0x70, 0x71, 0x72, 0x73 */ - unsigned char sense_key; - unsigned char asc; - unsigned char ascq; - unsigned char byte4; - unsigned char byte5; - unsigned char byte6; - unsigned char additional_length; + uint8_t response_code; /* permit: 0x0, 0x70, 0x71, 0x72, 0x73 */ + uint8_t sense_key; + uint8_t asc; + uint8_t ascq; + uint8_t byte4; /* descriptor: SDAT_OVFL; fixed: lower three ... */ + uint8_t byte5; /* ... bytes of INFO field */ + uint8_t byte6; + uint8_t additional_length; /* zero for fixed format sense data */ }; /* Maps the salient data from a sense buffer which is in either fixed or * descriptor format into a structure mimicking a descriptor format * header (i.e. the first 8 bytes of sense descriptor format). - * If zero response code returns 0. Otherwise returns 1 and if 'sshp' is - * non-NULL then zero all fields and then set the appropriate fields in + * If zero response code returns false. Otherwise returns true and if 'sshp' + * is non-NULL then zero all fields and then set the appropriate fields in * that structure. sshp::additional_length is always 0 for response * codes 0x70 and 0x71 (fixed format). */ -int sg_scsi_normalize_sense(const unsigned char * sensep, int sense_len, - struct sg_scsi_sense_hdr * sshp); +bool sg_scsi_normalize_sense(const uint8_t * sensep, int sense_len, + struct sg_scsi_sense_hdr * sshp); /* Attempt to find the first SCSI sense data descriptor that matches the * given 'desc_type'. If found return pointer to start of sense data * descriptor; otherwise (including fixed format sense data) returns NULL. */ -const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep, - int sense_len, int desc_type); +const uint8_t * sg_scsi_sense_desc_find(const uint8_t * sensep, int sense_len, + int desc_type); /* Get sense key from sense buffer. If successful returns a sense key value * between 0 and 15. If sense buffer cannot be decode, returns -1 . */ -int sg_get_sense_key(const unsigned char * sensep, int sense_len); +int sg_get_sense_key(const uint8_t * sensep, int sense_len); /* Yield string associated with sense_key value. Returns 'buff'. */ char * sg_get_sense_key_str(int sense_key, int buff_len, char * buff); @@ -179,27 +215,36 @@ /* Yield string associated with ASC/ASCQ values. Returns 'buff'. */ char * sg_get_asc_ascq_str(int asc, int ascq, int buff_len, char * buff); -/* Returns 1 if valid bit set, 0 if valid bit clear. Irrespective the +/* Returns true if valid bit set, false if valid bit clear. Irrespective the * information field is written out via 'info_outp' (except when it is * NULL). Handles both fixed and descriptor sense formats. */ -int sg_get_sense_info_fld(const unsigned char * sensep, int sb_len, - uint64_t * info_outp); +bool sg_get_sense_info_fld(const uint8_t * sensep, int sb_len, + uint64_t * info_outp); + +/* Returns true if fixed format or command specific information descriptor + * is found in the descriptor sense; else false. If available the command + * specific information field (4 byte integer in fixed format, 8 byte + * integer in descriptor format) is written out via 'cmd_spec_outp'. + * Handles both fixed and descriptor sense formats. */ +bool sg_get_sense_cmd_spec_fld(const uint8_t * sensep, int sb_len, + uint64_t * cmd_spec_outp); -/* Returns 1 if any of the 3 bits (i.e. FILEMARK, EOM or ILI) are set. +/* Returns true if any of the 3 bits (i.e. FILEMARK, EOM or ILI) are set. * In descriptor format if the stream commands descriptor not found - * then returns 0. Writes 1 or 0 corresponding to these bits to the - * last three arguments if they are non-NULL. */ -int sg_get_sense_filemark_eom_ili(const unsigned char * sensep, int sb_len, - int * filemark_p, int * eom_p, int * ili_p); + * then returns false. Writes true or false corresponding to these bits to + * the last three arguments if they are non-NULL. */ +bool sg_get_sense_filemark_eom_ili(const uint8_t * sensep, int sb_len, + bool * filemark_p, bool * eom_p, + bool * ili_p); -/* Returns 1 if SKSV is set and sense key is NO_SENSE or NOT_READY. Also - * returns 1 if progress indication sense data descriptor found. Places +/* Returns true if SKSV is set and sense key is NO_SENSE or NOT_READY. Also + * returns true if progress indication sense data descriptor found. Places * progress field from sense data where progress_outp points. If progress - * field is not available returns 0. Handles both fixed and descriptor + * field is not available returns false. Handles both fixed and descriptor * sense formats. N.B. App should multiply by 100 and divide by 65536 * to get percentage completion from given value. */ -int sg_get_sense_progress_fld(const unsigned char * sensep, int sb_len, - int * progress_outp); +bool sg_get_sense_progress_fld(const uint8_t * sensep, int sb_len, + int * progress_outp); /* Closely related to sg_print_sense(). Puts decoded sense data in 'buff'. * Usually multiline with multiple '\n' including one trailing. If @@ -208,15 +253,15 @@ * bytes written to 'buff' excluding the trailing '\0'. * N.B. prior to sg3_utils v 1.42 'leadin' was only prepended to the first * line output. Also this function returned type void. */ -int sg_get_sense_str(const char * leadin, const unsigned char * sense_buffer, - int sb_len, int raw_sinfo, int buff_len, char * buff); +int sg_get_sense_str(const char * leadin, const uint8_t * sense_buffer, + int sb_len, bool raw_sinfo, int buff_len, char * buff); /* Decode descriptor format sense descriptors (assumes sense buffer is * in descriptor format). 'leadin' is string prepended to each line written * to 'b', NULL treated as "". Returns the number of bytes written to 'b' - * excluding the trailing '\0'. */ + * excluding the trailing '\0'. If problem, returns 0. */ int sg_get_sense_descriptors_str(const char * leadin, - const unsigned char * sense_buffer, + const uint8_t * sense_buffer, int sb_len, int blen, char * b); /* Decodes a designation descriptor (e.g. as found in the Device @@ -225,9 +270,21 @@ * treated as "". Returns the number of bytes written to 'b' excluding the * trailing '\0'. */ int sg_get_designation_descriptor_str(const char * leadin, - const unsigned char * ddp, int dd_len, - int print_assoc, int do_long, int blen, - char * b); + const uint8_t * ddp, int dd_len, + bool print_assoc, bool do_long, + int blen, char * b); + +/* Expects a T10 UUID designator (as found in the Device Identification VPD + * page) pointed to by 'dp'. To not produce an error string in 'b', c_set + * should be 1 (binary) and dlen should be 18. Currently T10 only supports + * locally assigned UUIDs. Writes output to string 'b' of no more than blen + * bytes and returns the number of bytes actually written to 'b' but doesn't + * count the trailing null character it always appends (if blen > 0). 'lip' + * is lead-in string (on each line) than may be NULL. skip_prefix avoids + * outputting: ' Locally assigned UUID: ' before the UUID. */ +int sg_t10_uuid_desig2str(const uint8_t * dp, int dlen, int c_set, + bool do_long, bool skip_prefix, + const char * lip, int blen, char * b); /* Yield string associated with peripheral device type (pdt). Returns * 'buff'. If 'pdt' out of range yields "bad pdt" string. */ @@ -237,13 +294,19 @@ * Examples are PDT_ADC decaying to PDT_TAPE and PDT_ZBC to PDT_DISK. * If such a lesser used 'pdt' is given to this function, then it will * return the more used PDT (i.e. "decays to"); otherwise 'pdt' is returned. - * Valid for 'pdt' 0 to 31, for other values returns 'pdt'. */ + * Valid for 'pdt' 0 to 31, for other values returns 0. */ int sg_lib_pdt_decay(int pdt); /* Yield string associated with transport protocol identifier (tpi). Returns - * 'buff'. If 'tpi' out of range yields "bad tpi" string. */ + * 'buff'. If 'tpi' out of range yields "bad tpi" string. */ char * sg_get_trans_proto_str(int tpi, int buff_len, char * buff); +/* Decode TransportID pointed to by 'bp' of length 'bplen'. Place decoded + * string output in 'buff' which is also the return value. Each new line + * is prefixed by 'leadin'. If leadin NULL treat as "". */ +char * sg_decode_transportid_str(const char * leadin, uint8_t * bp, int bplen, + bool only_one, int buff_len, char * buff); + /* Returns a designator's type string given 'val' (0 to 15 inclusive), * otherwise returns NULL. */ const char * sg_get_desig_type_str(int val); @@ -256,75 +319,191 @@ * otherwise returns NULL. */ const char * sg_get_desig_assoc_str(int val); +/* Yield SCSI Feature Set (sfs) string. When 'peri_type' is < -1 (or > 31) + * returns pointer to string (same as 'buff') associated with 'sfs_code'. + * When 'peri_type' is between -1 (for SPC) and 31 (inclusive) then a match + * on both 'sfs_code' and 'peri_type' is required. If 'foundp' is not NULL + * then where it points is set to true if a match is found else it is set to + * false. If 'buff' is not NULL then in the case of a match a descriptive + * string is written to 'buff' while if there is not a not then a string + * ending in "Reserved" is written (and may be prefixed with SPC, SBC, SSC + * or ZBC). Returns 'buff' (i.e. a pointer value) even if it is NULL. + * Example: + * char b[64]; + * ... + * printf("%s\n", sg_get_sfs_str(sfs_code, -2, sizeof(b), b, NULL, 0)); + */ +const char * sg_get_sfs_str(uint16_t sfs_code, int peri_type, int buff_len, + char * buff, bool * foundp, int verbose); + +/* This is a heuristic that takes into account the command bytes and length + * to decide whether the presented unstructured sequence of bytes could be + * a SCSI command. If so it returns true otherwise false. Vendor specific + * SCSI commands (i.e. opcodes from 0xc0 to 0xff), if presented, are assumed + * to follow SCSI conventions (i.e. length of 6, 10, 12 or 16 bytes). The + * only SCSI commands considered above 16 bytes of length are the Variable + * Length Commands (opcode 0x7f) and the XCDB wrapped commands (opcode 0x7e). + * Both have an inbuilt length field which can be cross checked with clen. + * No NVMe commands (64 bytes long plus some extra added by some OSes) have + * opcodes 0x7e or 0x7f yet. ATA is register based but SATA has FIS + * structures that are sent across the wire. The 'FIS register' structure is + * used to move a command from a SATA host to device, but the ATA 'command' + * is not the first byte. So it is harder to say what will happen if a + * FIS structure is presented as a SCSI command, hopefully there is a low + * probability this function will yield true in that case. */ +bool sg_is_scsi_cdb(const uint8_t * cdbp, int clen); + +/* Yield string associated with NVMe command status value in sct_sc. It + * expects to decode DW3 bits 27:17 from the completion queue. Bits 27:25 + * are the Status Code Type (SCT) and bits 24:17 are the Status Code (SC). + * Bit 17 in DW3 should be bit 0 in sct_sc. If no status string is found + * a string of the form "Reserved [0x]" is generated. + * Returns 'buff'. Does nothing if buff_len<=0 or if buff is NULL.*/ +char * sg_get_nvme_cmd_status_str(uint16_t sct_sc, int buff_len, char * buff); + +/* Attempts to map NVMe status value ((SCT << 8) | SC) n sct_sc to a SCSI + * status, sense_key, asc and ascq tuple. If successful returns true and + * writes to non-NULL pointer arguments; otherwise returns false. */ +bool sg_nvme_status2scsi(uint16_t sct_sc, uint8_t * status_p, uint8_t * sk_p, + uint8_t * asc_p, uint8_t * ascq_p); + +/* Add vendor (sg3_utils) specific sense descriptor for the NVMe Status + * field. Assumes descriptor (i.e. not fixed) sense. Assume sbp has room. */ +void sg_nvme_desc2sense(uint8_t * sbp, bool dnr, bool more, uint16_t sct_sc); + +/* Build minimum sense buffer, either descriptor type (desc=true) or fixed + * type (desc=false). Assume sbp has enough room (8 or 14 bytes + * respectively). sbp should have room for 32 or 18 bytes respectively */ +void sg_build_sense_buffer(bool desc, uint8_t *sbp, uint8_t skey, + uint8_t asc, uint8_t ascq); + extern FILE * sg_warnings_strm; void sg_set_warnings_strm(FILE * warnings_strm); -/* The following "print" functions send ACSII to 'sg_warnings_strm' file +/* Given a SCSI command pointed to by cdbp of sz bytes this function forms a + * SCSI command in ASCII hex surrounded by square brackets in 'b'. 'b' is at + * least blen bytes long. If cmd_name is true then the command is prefixed + * by its SCSI command name (e.g. "VERIFY(10) [2f ...]". The command is + * shown as spaced separated pairs of hexadecimal digits (i.e. 0-9, a-f). + * Each pair represents byte. The leftmost pair of digits is cdbp[0] . If + * sz <= 0 then this function tries to guess the length of the command. */ +char * +sg_get_command_str(const uint8_t * cdbp, int sz, bool cmd_name, int blen, + char * b); + +/* The following "print" functions send ASCII to 'sg_warnings_strm' file * descriptor (default value is stderr). 'leadin' is string prepended to * each line printed out, NULL treated as "". */ -void sg_print_command(const unsigned char * command); +void sg_print_command_len(const uint8_t * command, int len); +void sg_print_command(const uint8_t * command); void sg_print_scsi_status(int scsi_status); +/* DSENSE is 'descriptor sense' as opposed to the older 'fixed sense'. Reads + * environment variable SG3_UTILS_DSENSE. Only (currently) used in SNTL. */ +bool sg_get_initial_dsense(void); + /* 'leadin' is string prepended to each line printed out, NULL treated as * "". N.B. prior to sg3_utils v 1.42 'leadin' was only prepended to the * first line printed. */ -void sg_print_sense(const char * leadin, const unsigned char * sense_buffer, - int sb_len, int raw_info); +void sg_print_sense(const char * leadin, const uint8_t * sense_buffer, + int sb_len, bool raw_info); + +/* This examines exit_status and if an error message is known it is output + * to stdout/stderr and true is returned. If no error message is + * available nothing is output and false is returned. If exit_status is + * zero (no error) nothing is output and true is returned. If exit_status + * is negative then nothing is output and false is returned. If leadin is + * non-NULL then it is printed before the error message. All messages are + * a single line with a trailing LF. */ +bool sg_if_can2stdout(const char * leadin, int exit_status); +bool sg_if_can2stderr(const char * leadin, int exit_status); + +/* This examines exit_status and if an error message is known it is output + * as a string to 'b' and true is returned. If 'longer' is true and extra + * information is available then it is added to the output. If no error + * message is available a null character is output and false is returned. + * If exit_status is zero (no error) and 'longer' is true then the string + * 'No errors' is output; if 'longer' is false then a null character is + * output; in both cases true is returned. If exit_status is negative then + * a null character is output and false is returned. All messages are a + * single line (less than 80 characters) with no trailing LF. The output + * string including the trailing null character is no longer than b_len. */ +bool sg_exit2str(int exit_status, bool longer, int b_len, char * b); /* Utilities can use these exit status values for syntax errors and * file (device node) problems (e.g. not found or permissions). */ #define SG_LIB_SYNTAX_ERROR 1 /* command line syntax problem */ -#define SG_LIB_FILE_ERROR 15 /* device or other file problem */ /* The sg_err_category_sense() function returns one of the following. * These may be used as exit status values (from a process). Notice that * some of the lower values correspond to SCSI sense key values. */ #define SG_LIB_CAT_CLEAN 0 /* No errors or other information */ +#define SG_LIB_OK_TRUE SG_LIB_CAT_CLEAN /* No error, reporting true */ /* Value 1 left unused for utilities to use SG_LIB_SYNTAX_ERROR */ -#define SG_LIB_CAT_NOT_READY 2 /* sense key, unit stopped? */ - /* [sk,asc,ascq: 0x2,*,*] */ -#define SG_LIB_CAT_MEDIUM_HARD 3 /* medium or hardware error, blank check */ - /* [sk,asc,ascq: 0x3/0x4/0x8,*,*] */ -#define SG_LIB_CAT_ILLEGAL_REQ 5 /* Illegal request (other than invalid */ - /* opcode): [sk,asc,ascq: 0x5,*,*] */ -#define SG_LIB_CAT_UNIT_ATTENTION 6 /* sense key, device state changed */ - /* [sk,asc,ascq: 0x6,*,*] */ +#define SG_LIB_CAT_NOT_READY 2 /* sense key, unit stopped? + * [sk,asc,ascq: 0x2,*,*] */ +#define SG_LIB_CAT_MEDIUM_HARD 3 /* medium or hardware error, blank check + * [sk,asc,ascq: 0x3/0x4/0x8,*,*] */ +#define SG_LIB_CAT_ILLEGAL_REQ 5 /* Illegal request (other than invalid + * opcode): [sk,asc,ascq: 0x5,*,*] */ +#define SG_LIB_CAT_UNIT_ATTENTION 6 /* sense key, device state changed + * [sk,asc,ascq: 0x6,*,*] */ /* was SG_LIB_CAT_MEDIA_CHANGED earlier [sk,asc,ascq: 0x6,0x28,*] */ -#define SG_LIB_CAT_DATA_PROTECT 7 /* sense key, media write protected? */ - /* [sk,asc,ascq: 0x7,*,*] */ -#define SG_LIB_CAT_INVALID_OP 9 /* (Illegal request,) Invalid opcode: */ - /* [sk,asc,ascq: 0x5,0x20,0x0] */ -#define SG_LIB_CAT_COPY_ABORTED 10 /* sense key, some data transferred */ - /* [sk,asc,ascq: 0xa,*,*] */ -#define SG_LIB_CAT_ABORTED_COMMAND 11 /* interpreted from sense buffer */ - /* [sk,asc,ascq: 0xb,! 0x10,*] */ -#define SG_LIB_CAT_MISCOMPARE 14 /* sense key, probably verify */ - /* [sk,asc,ascq: 0xe,*,*] */ -#define SG_LIB_CAT_NO_SENSE 20 /* sense data with key of "no sense" */ - /* [sk,asc,ascq: 0x0,*,*] */ -#define SG_LIB_CAT_RECOVERED 21 /* Successful command after recovered err */ - /* [sk,asc,ascq: 0x1,*,*] */ +#define SG_LIB_CAT_DATA_PROTECT 7 /* sense key, media write protected? + * [sk,asc,ascq: 0x7,*,*] */ +#define SG_LIB_CAT_INVALID_OP 9 /* (Illegal request,) Invalid opcode: + * [sk,asc,ascq: 0x5,0x20,0x0] */ +#define SG_LIB_CAT_COPY_ABORTED 10 /* sense key, some data transferred + * [sk,asc,ascq: 0xa,*,*] */ +#define SG_LIB_CAT_ABORTED_COMMAND 11 /* interpreted from sense buffer + * [sk,asc,ascq: 0xb,! 0x10,*] */ +#define SG_LIB_CAT_MISCOMPARE 14 /* sense key, probably verify + * [sk,asc,ascq: 0xe,*,*] */ +#define SG_LIB_FILE_ERROR 15 /* device or other file problem */ +#define SG_LIB_CAT_NO_SENSE 20 /* sense data with key of "no sense" + * [sk,asc,ascq: 0x0,*,*] */ +#define SG_LIB_CAT_RECOVERED 21 /* Successful command after recovered err + * [sk,asc,ascq: 0x1,*,*] */ +#define SG_LIB_LBA_OUT_OF_RANGE 22 /* Illegal request, LBA Out Of Range + * [sk,asc,ascq: 0x5,0x21,0x0] */ #define SG_LIB_CAT_RES_CONFLICT SAM_STAT_RESERVATION_CONFLICT - /* 24: this is a SCSI status, not sense. */ - /* It indicates reservation by another */ - /* machine blocks this command */ -#define SG_LIB_CAT_CONDITION_MET 25 /* SCSI status, not sense key. */ - /* Only from PRE-FETCH (SBC-4) */ + /* 24: this is a SCSI status, not sense. + * It indicates reservation by another + * machine blocks this command */ +#define SG_LIB_CAT_CONDITION_MET 25 /* SCSI status, not sense key. + * Only from PRE-FETCH (SBC-4) */ #define SG_LIB_CAT_BUSY 26 /* SCSI status, not sense. Invites retry */ #define SG_LIB_CAT_TS_FULL 27 /* SCSI status, not sense. Wait then retry */ #define SG_LIB_CAT_ACA_ACTIVE 28 /* SCSI status; ACA seldom used */ #define SG_LIB_CAT_TASK_ABORTED 29 /* SCSI status, this command aborted by? */ -#define SG_LIB_CAT_PROTECTION 40 /* subset of aborted command (for PI, DIF) */ - /* [sk,asc,ascq: 0xb,0x10,*] */ +#define SG_LIB_CONTRADICT 31 /* error involving two or more cl options */ +#define SG_LIB_LOGIC_ERROR 32 /* unexpected situation in code */ +#define SG_LIB_WINDOWS_ERR 34 /* Windows error number don't fit in 7 bits so + * map to a single value for exit statuses */ +#define SG_LIB_OK_FALSE 36 /* no error, reporting false (cf. no error, + * reporting true is SG_LIB_OK_TRUE(0) ) */ +#define SG_LIB_CAT_PROTECTION 40 /* subset of aborted command (for PI, DIF) + * [sk,asc,ascq: 0xb,0x10,*] */ +/* 47: flock error used in ddpt utility */ +#define SG_LIB_NVME_STATUS 48 /* NVMe Status Field (SF) other than 0 */ +#define SG_LIB_WILD_RESID 49 /* Residual value for data-in transfer of a + * SCSI command is nonsensical */ +#define SG_LIB_OS_BASE_ERR 50 /* in Linux: values found in: + * include/uapi/asm-generic/errno-base.h + * Example: ENOMEM reported as 62 (=50+12) + * if errno > 46 then use this value */ +/* 51-->96 set aside for Unix errno values shifted by SG_LIB_OS_BASE_ERR */ #define SG_LIB_CAT_MALFORMED 97 /* Response to SCSI command malformed */ #define SG_LIB_CAT_SENSE 98 /* Something else is in the sense buffer */ -#define SG_LIB_CAT_OTHER 99 /* Some other error/warning has occurred */ - /* (e.g. a transport or driver error) */ +#define SG_LIB_CAT_OTHER 99 /* Some other error/warning has occurred + * (e.g. a transport or driver error) */ +/* 100 to 120 (inclusive) used by ddpt utility */ +#define SG_LIB_UNUSED_ABOVE 120 /* Put extra errors in holes below this */ /* Returns a SG_LIB_CAT_* value. If cannot decode sense_buffer or a less * common sense key then return SG_LIB_CAT_SENSE .*/ -int sg_err_category_sense(const unsigned char * sense_buffer, int sb_len); +int sg_err_category_sense(const uint8_t * sense_buffer, int sb_len); /* Here are some additional sense data categories that are not returned * by sg_err_category_sense() but are returned by some related functions. */ @@ -334,14 +513,18 @@ #define SG_LIB_CAT_MEDIUM_HARD_WITH_INFO 18 /* medium or hardware error */ /* sense key plus 'info' field: */ /* [sk,asc,ascq: 0x3/0x4,*,*] */ +#define SG_LIB_CAT_TIMEOUT 33 /* SCSI command timeout */ #define SG_LIB_CAT_PROTECTION_WITH_INFO 41 /* aborted command sense key, */ /* protection plus 'info' field: */ /* [sk,asc,ascq: 0xb,0x10,*] */ -#define SG_LIB_CAT_TIMEOUT 33 /* Yield string associated with sense category. Returns 'buff' (or pointer * to "Bad sense category" if 'buff' is NULL). If sense_cat unknown then - * yield "Sense category: " string. */ + * yield "Sense category: " string. The original 'sense + * category' concept has been expanded to most detected errors and is + * returned by these utilities as their exit status value (an (unsigned) + * 8 bit value where 0 means good (i.e. no errors)). Uses the + * sg_exit2str() function. */ const char * sg_get_category_sense_str(int sense_cat, int buff_len, char * buff, int verbose); @@ -355,7 +538,7 @@ * descriptor; returns -1 if normal end condition and -2 for an abnormal * termination. Matches association, designator_type and/or code_set when * any of those values are greater than or equal to zero. */ -int sg_vpd_dev_id_iter(const unsigned char * initial_desig_desc, int page_len, +int sg_vpd_dev_id_iter(const uint8_t * initial_desig_desc, int page_len, int * off, int m_assoc, int m_desig_type, int m_code_set); @@ -375,34 +558,59 @@ * = 0 in addition, the bytes are listed in ASCII to the right * < 0 only the ASCII-hex bytes are listed (i.e. without address) */ -void dStrHex(const char* str, int len, int no_ascii); +void dStrHex(const char * str, int len, int no_ascii); /* Print (to sg_warnings_strm (stderr)) 'str' of bytes in hex, 16 bytes per * line optionally followed at right by its ASCII interpretation. Same * logic as dStrHex() with different output stream (i.e. stderr). */ -void dStrHexErr(const char* str, int len, int no_ascii); +void dStrHexErr(const char * str, int len, int no_ascii); /* Read 'len' bytes from 'str' and output as ASCII-Hex bytes (space * separated) to 'b' not to exceed 'b_len' characters. Each line * starts with 'leadin' (NULL for no leadin) and there are 16 bytes * per line with an extra space between the 8th and 9th bytes. 'format' - * is unused, set to 0 . Returns number of bytes written to 'b' excluding - * the trailing '\0'.*/ -int dStrHexStr(const char* str, int len, const char * leadin, int format, - int b_len, char * b); + * is 0 for repeat in printable ASCII ('.' for non printable chars) to + * right of each line; 1 don't (so just output ASCII hex). Returns + * number of bytes written to 'b' excluding the trailing '\0'. */ +int dStrHexStr(const char * str, int len, const char * leadin, int format, + int cb_len, char * cbp); + +/* The following 3 functions are equivalent to dStrHex(), dStrHexErr() and + * dStrHexStr() respectively. The difference is the type of the first of + * argument: uint8_t instead of char. The name of the argument is changed + * to b_str to stress it is a pointer to the start of a binary string. */ +void hex2stdout(const uint8_t * b_str, int len, int no_ascii); +void hex2stderr(const uint8_t * b_str, int len, int no_ascii); +int hex2str(const uint8_t * b_str, int len, const char * leadin, int format, + int cb_len, char * cbp); + +/* Read ASCII hex bytes or binary from fname (a file named '-' taken as + * stdin). If reading ASCII hex then there should be either one entry per + * line or a comma, space or tab separated list of bytes. If no_space is + * set then a string of ACSII hex digits is expected, 2 per byte. Everything + * from and including a '#' on a line is ignored. Returns 0 if ok, or an + * error code. If the error code is SG_LIB_LBA_OUT_OF_RANGE then mp_arr + * would be exceeded and both mp_arr and mp_arr_len are written to. */ +int sg_f2hex_arr(const char * fname, bool as_binary, bool no_space, + uint8_t * mp_arr, int * mp_arr_len, int max_arr_len); -/* Returns 1 when executed on big endian machine; else returns 0. +/* Returns true when executed on big endian machine; else returns false. * Useful for displaying ATA identify words (which need swapping on a - * big endian machine). -*/ -int sg_is_big_endian(); + * big endian machine). */ +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 or 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 * are processed. */ -int sg_ata_get_chars(const unsigned short * word_arr, int start_word, - int num_words, int is_big_endian, char * ochars); +int sg_ata_get_chars(const uint16_t * word_arr, int start_word, + int num_words, bool is_big_endian, char * ochars); /* Print (to stdout) 16 bit 'words' in hex, 8 words per line optionally * followed at the right hand side of the line with an ASCII interpretation @@ -414,35 +622,85 @@ * = -1 only the ASCII-hex words are listed (i.e. without address) * = -2 only the ASCII-hex words, formatted for "hdparm --Istdin" * < -2 same as -1 - * If 'swapb' non-zero then bytes in each word swapped. Needs to be set + * If 'swapb' is true then bytes in each word swapped. Needs to be set * for ATA IDENTIFY DEVICE response on big-endian machines. */ -void dWordHex(const unsigned short* words, int num, int no_ascii, int swapb); +void dWordHex(const uint16_t * words, int num, int no_ascii, bool swapb); /* If the number in 'buf' can not be decoded or the multiplier is unknown - * then -1 is returned. Accepts a hex prefix (0x or 0X) or a 'h' (or 'H') - * suffix. Otherwise a decimal multiplier suffix may be given. Recognised - * multipliers: c C *1; w W *2; b B *512; k K KiB *1,024; - * KB *1,000; m M MiB *1,048,576; MB *1,000,000; g G GiB *1,073,741,824; - * GB *1,000,000,000 and x which multiplies by . Ignore leading - * spaces and tabs; accept comma, space, tab and hash as terminator. */ + * then -1 is returned. Accepts a hex prefix (0x or 0X) or a decimal + * multiplier suffix (as per GNU's dd (since 2002: SI and IEC 60027-2)). + * Main (SI) multipliers supported: K, M, G. Ignore leading spaces and + * tabs; accept comma, hyphen, space, tab and hash as terminator. + * Handles zero and positive values up to 2**31-1 . + * Experimental: left argument (must in with hexadecimal digit) added + * to, or multiplied, by right argument. No embedded spaces. + * Examples: '3+1k' (evaluates to 1027) and '0xf+0x3'. */ int sg_get_num(const char * buf); /* If the number in 'buf' can not be decoded then -1 is returned. Accepts a * hex prefix (0x or 0X) or a 'h' (or 'H') suffix; otherwise decimal is - * assumed. Does not accept multipliers. Accept a comma (","), a whitespace - * or newline as terminator. */ + * assumed. Does not accept multipliers. Accept a comma (","), hyphen ("-"), + * a whitespace or newline as terminator. Only decimal numbers can represent + * negative numbers and '-1' must be treated separately. */ int sg_get_num_nomult(const char * buf); /* If the number in 'buf' can not be decoded or the multiplier is unknown - * then -1LL is returned. Accepts a hex prefix (0x or 0X) or a 'h' (or 'H') - * suffix. Otherwise a decimal multiplier suffix may be given. In addition - * to supporting the multipliers of sg_get_num(), this function supports: - * t T TiB *(2**40); TB *(10**12); p P PiB *(2**50); PB *(10**15) . - * Ignore leading spaces and tabs; accept comma, space, tab and hash as - * terminator. */ + * then -1LL is returned. Accepts a hex prefix (0x or 0X), hex suffix + * (h or H), or a decimal multiplier suffix (as per GNU's dd (since 2002: + * SI and IEC 60027-2)). Main (SI) multipliers supported: K, M, G, T, P + * and E. Ignore leading spaces and tabs; accept comma, hyphen, space, tab + * and hash as terminator. Handles zero and positive values up to 2**63-1 . + * Experimental: the left argument (must end in with hexadecimal digit) + * added to, or multiplied by, the right argument. No embedded spaces. + * Examples: '3+1k' (evaluates to 1027) and '0xf+0x3'. */ int64_t sg_get_llnum(const char * buf); +/* If the number in 'buf' can not be decoded then -1 is returned. Accepts a + * hex prefix (0x or 0X) or a 'h' (or 'H') suffix; otherwise decimal is + * assumed. Does not accept multipliers. Accept a comma (","), hyphen ("-"), + * a whitespace or newline as terminator. Only decimal numbers can represent + * negative numbers and '-1' must be treated separately. */ +int64_t sg_get_llnum_nomult(const char * buf); + +/* Returns pointer to heap (or NULL) that is aligned to a align_to byte + * boundary. Sends back *buff_to_free pointer in third argument that may be + * different from the return value. If it is different then the *buff_to_free + * pointer should be freed (rather than the returned value) when the heap is + * no longer needed. If align_to is 0 then aligns to OS's page size. Sets all + * returned heap to zeros. If num_bytes is 0 then set to page size. */ +uint8_t * sg_memalign(uint32_t num_bytes, uint32_t align_to, + uint8_t ** buff_to_free, bool vb); + +/* Returns OS page size in bytes. If uncertain returns 4096. */ +uint32_t sg_get_page_size(void); + +/* If byte_count is 0 or less then the OS page size is used as denominator. + * Returns true if the remainder of ((unsigned)pointer % byte_count) is 0, + * else returns false. */ +bool sg_is_aligned(const void * pointer, int byte_count); + +/* Does similar job to sg_get_unaligned_be*() but this function starts at + * a given start_bit (i.e. within byte, so 7 is MSbit of byte and 0 is LSbit) + * offset. Maximum number of num_bits is 64. For example, these two + * invocations are equivalent (and should yield the same result); + * sg_get_big_endian(from_bp, 7, 16) + * sg_get_unaligned_be16(from_bp) */ +uint64_t sg_get_big_endian(const uint8_t * from_bp, + int start_bit /* 0 to 7 */, + int num_bits /* 1 to 64 */); + +/* Does similar job to sg_put_unaligned_be*() but this function starts at + * a given start_bit offset. Maximum number of num_bits is 64. Preserves + * residual bits in partially written bytes. start_bit 7 is MSb. */ +void sg_set_big_endian(uint64_t val, uint8_t * to, int start_bit /* 0 to 7 */, + int num_bits /* 1 to 64 */); + +/* If os_err_num is within bounds then the returned value is 'os_err_num + + * SG_LIB_OS_BASE_ERR' otherwise SG_LIB_OS_BASE_ERR is returned. If + * os_err_num is 0 then 0 is returned. */ +int sg_convert_errno(int os_err_num); + /* <<< Architectural support functions [is there a better place?] >>> */ @@ -459,4 +717,4 @@ } #endif -#endif +#endif /* SG_LIB_H */ diff -Nru sdparm-1.10/include/sg_linux_inc.h sdparm-1.12/include/sg_linux_inc.h --- sdparm-1.10/include/sg_linux_inc.h 2007-09-10 00:54:57.000000000 +0000 +++ sdparm-1.12/include/sg_linux_inc.h 2019-01-11 06:11:25.000000000 +0000 @@ -2,8 +2,9 @@ #define SG_LINUX_INC_H #ifdef SG_KERNEL_INCLUDES + #include /* C99 header for exact integer types */ #define __user - typedef unsigned char u8; + typedef uint8_t u8; #include "/usr/src/linux/include/scsi/sg.h" #include "/usr/src/linux/include/scsi/scsi.h" #else @@ -11,6 +12,7 @@ #include #include #else + #define __user #include #include #endif diff -Nru sdparm-1.10/include/sg_pr2serr.h sdparm-1.12/include/sg_pr2serr.h --- sdparm-1.10/include/sg_pr2serr.h 2015-12-20 16:23:44.000000000 +0000 +++ sdparm-1.12/include/sg_pr2serr.h 2018-12-07 16:42:06.000000000 +0000 @@ -2,19 +2,71 @@ #define SG_PR2SERR_H /* - * Copyright (c) 2004-2015 Douglas Gilbert. + * Copyright (c) 2004-2018 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. + * + * SPDX-License-Identifier: BSD-2-Clause */ +/* These are convenience functions that replace the somewhat long-winded + * fprintf(stderr, ....). The second form (i.e. pr2ws() ) is for internal + * library use and may place its output somewhere other than stderr; it + * depends on the external variable sg_warnings_strm which can be set + * with sg_set_warnings_strm(). By default it uses stderr. */ + +/* With regard to sg_scnpr(): + * Want safe, 'n += snprintf(b + n, blen - n, ...)' style sequence of + * functions. Returns number of chars placed in cp excluding the + * trailing null char. So for cp_max_len > 0 the return value is always + * < cp_max_len; for cp_max_len <= 1 the return value is 0 and no chars are + * written to cp. Note this means that when cp_max_len = 1, this function + * assumes that cp[0] is the null character and does nothing (and returns + * 0). Linux kernel has a similar function called scnprintf(). */ + + #include -#ifdef __GNUC__ +#ifdef __cplusplus +extern "C" { +#endif + + +#if defined(__GNUC__) || defined(__clang__) +#ifdef SG_LIB_MINGW +/* MinGW uses Microsoft's printf */ +int pr2serr(const char * fmt, ...); + +int pr2ws(const char * fmt, ...); + +int sg_scnpr(char * cp, int cp_max_len, const char * fmt, ...); + +#else /* GNU/clang other than MinGW */ + int pr2serr(const char * fmt, ...) __attribute__ ((format (printf, 1, 2))); -#else + +int pr2ws(const char * fmt, ...) + __attribute__ ((format (printf, 1, 2))); + +int sg_scnpr(char * cp, int cp_max_len, const char * fmt, ...) + __attribute__ ((format (printf, 3, 4))); +#endif + +#else /* not GNU (and not clang) */ + int pr2serr(const char * fmt, ...); + +int pr2ws(const char * fmt, ...); + +int sg_scnpr(char * cp, int cp_max_len, const char * fmt, ...); + +#endif + + +#ifdef __cplusplus +} #endif #endif diff -Nru sdparm-1.10/include/sg_pt.h sdparm-1.12/include/sg_pt.h --- sdparm-1.10/include/sg_pt.h 2014-06-04 00:24:20.000000000 +0000 +++ sdparm-1.12/include/sg_pt.h 2020-08-03 04:16:31.000000000 +0000 @@ -2,13 +2,16 @@ #define SG_PT_H /* - * Copyright (c) 2005-2014 Douglas Gilbert. + * Copyright (c) 2005-2020 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. + * + * SPDX-License-Identifier: BSD-2-Clause */ #include +#include #ifdef __cplusplus extern "C" { @@ -18,28 +21,46 @@ * structure "derived" (using a C++ term) from this one. It compiles * because 'struct sg_pt_base' is only referenced (by pointer: 'objp') * in this interface. An instance of this structure represents the - * context of one SCSI command. */ + * context of one SCSI command. + * If an instance of sg_pt_base is shared across several threads then + * it is up to the application to take care of multi-threaded issues + * with that instance. */ struct sg_pt_base; -/* The format of the version string is like this: "2.01 20090201". +/* The format of the version string is like this: "3.04 20180213". * The leading digit will be incremented if this interface changes * in a way that may impact backward compatibility. */ const char * scsi_pt_version(); +const char * sg_pt_version(); /* both functions give same result */ -/* Returns >= 0 if successful. If error in Unix returns negated errno. */ -int scsi_pt_open_device(const char * device_name, int read_only, int verbose); +/* Returns file descriptor or file handle and is >= 0 if successful. + * If error in Unix returns negated errno. */ +int 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. Returns valid file descriptor( >= 0 ) if successful, otherwise - * returns -1 or a negated errno. + * together. Returns valid file descriptor or handle ( >= 0 ) if successful, + * otherwise returns -1 or a negated errno. * In Win32 O_EXCL translated to equivalent. */ int scsi_pt_open_flags(const char * device_name, int flags, int verbose); -/* Returns 0 if successful. If error in Unix returns negated errno. */ +/* Returns 0 if successful. 'device_fd' should be a value that was previously + * returned by scsi_pt_open_device() or scsi_pt_open_flags() that has not + * already been closed. 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. @@ -47,22 +68,67 @@ * destruct_scsi_pt_obj() when it is no longer needed. */ struct sg_pt_base * construct_scsi_pt_obj(void); +/* An alternate and preferred way to create an object that can be used to + * issue one or more SCSI (or NVMe) commands (or task management functions). + * This variant associates a device file descriptor (handle) with the object + * and a verbose argument that causes messages to be written to stderr 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 to check + * for errors. The dev_fd argument may be -1 to indicate no device file + * descriptor. */ +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 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) */ -void set_scsi_pt_cdb(struct sg_pt_base * objp, const unsigned char * cdb, +/* Partially clear state information held in *objp . Any error settings and + * the data-in and data-out settings are cleared. So dev_fd, cdb and sense + * settings are kept. */ +void partial_clear_scsi_pt_obj(struct sg_pt_base * objp); + +/* Set the CDB (command descriptor block). May also be a NVMe Admin command + * which will be 64 bytes long. + * + * Note that the sg_cmds_is_nvme() function found in sg_cmds_basic.h can be + * called after this function to "guess" which command set the given command + * belongs to. It is valid to supply a cdb value of NULL. */ +void set_scsi_pt_cdb(struct sg_pt_base * objp, const uint8_t * cdb, int cdb_len); -/* Set the sense buffer and the maximum length that it can handle */ -void set_scsi_pt_sense(struct sg_pt_base * objp, unsigned char * sense, + +/* Set the sense buffer and the maximum length of that buffer. For NVMe + * commands this "sense" buffer will receive the 4 DWORDs of from the + * completion queue. It is valid to supply a sense value of NULL. */ +void set_scsi_pt_sense(struct sg_pt_base * objp, uint8_t * 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); + uint8_t * 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 uint8_t * dxferp, int dxfer_olen); + +/* Set a pointer and length to be used for metadata transferred to + * (out_true=true) or from (out_true=false) device (NVMe only) */ +void set_pt_metadata_xfer(struct sg_pt_base * objp, uint8_t * mdxferp, + uint32_t mdxfer_len, bool out_true); + /* The following "set_"s implementations may be dummies */ void set_scsi_pt_packet_id(struct sg_pt_base * objp, int pack_id); void set_scsi_pt_tag(struct sg_pt_base * objp, uint64_t tag); @@ -77,54 +143,131 @@ * are given, use the pass-through default. */ #define SCSI_PT_FLAGS_QUEUE_AT_TAIL 0x10 #define SCSI_PT_FLAGS_QUEUE_AT_HEAD 0x20 -/* Set (potentially OS dependant) flags for pass-through mechanism. +/* Set (potentially OS dependent) flags for pass-through mechanism. * Apart from contradictions, flags can be OR-ed together. */ void set_scsi_pt_flags(struct sg_pt_base * objp, int flags); #define SCSI_PT_DO_START_OK 0 #define SCSI_PT_DO_BAD_PARAMS 1 #define SCSI_PT_DO_TIMEOUT 2 +#define SCSI_PT_DO_NOT_SUPPORTED 4 +#define SCSI_PT_DO_NVME_STATUS 48 /* == SG_LIB_NVME_STATUS */ /* If OS error prior to or during command submission then returns negated * error value (e.g. Unix '-errno'). This includes interrupted system calls * (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); +/* NVMe Admin commands can be sent directly to do_scsi_pt(). Unfortunately + * NVMe has at least one other command set: "NVM" to access user data and + * the opcodes in the NVM command set overlap with the Admin command set. + * So NVMe Admin commands should be sent do_scsi_pt() while NVMe "NVM" + * commands should be sent to this function. No SCSI commands should be + * sent to this function. Currently submq is not implemented and all + * submitted NVM commands are sent on queue 0, the same queue use for + * Admin commands. The return values follow the same pattern as do_scsi_pt(), + * with 0 returned being good. The NVMe device file descriptor must either + * be given to the obj constructor, or a prior set_pt_file_handle() call. */ +int do_nvm_pt(struct sg_pt_base * objp, int submq, int timeout_secs, + int verbose); + #define SCSI_PT_RESULT_GOOD 0 #define SCSI_PT_RESULT_STATUS 1 /* other than GOOD and CHECK CONDITION */ #define SCSI_PT_RESULT_SENSE 2 #define SCSI_PT_RESULT_TRANSPORT_ERR 3 #define SCSI_PT_RESULT_OS_ERR 4 -/* highest numbered applicable category returned */ +/* This function, called soon after do_scsi_pt(), returns one of the above + * result categories. The highest numbered applicable category is returned. + * + * Note that the sg_cmds_process_resp() function found in sg_cmds_basic.h + * is useful for processing SCSI command responses. + * And the sg_cmds_is_nvme() function found in sg_cmds_basic.h can be called + * after set_scsi_pt_cdb() to "guess" which command set the given command + * belongs to. */ 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 it is the number of bytes requested to transfer less the + * number actually transferred. This it typically important for data-in + * transfers. For data-out (only) transfers, the 'dout_req_len - + * dout_act_len' is returned. For bidi transfer the data-in residual is + * returned. */ int get_scsi_pt_resid(const struct sg_pt_base * objp); -/* Returns SCSI status value (from device that received the - command). */ + +/* Returns SCSI status value (from device that received the command). If an + * NVMe command was issued directly (i.e. through do_scsi_pt() then return + * NVMe status (i.e. ((SCT << 8) | SC)). If problem returns -1. */ int get_scsi_pt_status_response(const struct sg_pt_base * objp); + +/* Returns SCSI status value or, if NVMe command given to do_scsi_pt(), + * then returns NVMe result (i.e. DWord(0) from completion queue). If + * 'objp' is NULL then returns 0xffffffff. */ +uint32_t get_pt_result(const struct sg_pt_base * objp); + +/* These two get functions should just echo what has been given to + * set_scsi_pt_cdb(). If it has not been called or clear_scsi_pt_obj() + * has been called then return 0 and NULL respectively. */ +int get_scsi_pt_cdb_len(const struct sg_pt_base * objp); +uint8_t * get_scsi_pt_cdb_buf(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 */ +uint8_t * get_scsi_pt_sense_buf(const struct sg_pt_base * objp); + +/* 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); +void set_scsi_pt_transport_err(struct sg_pt_base * objp, int err); char * get_scsi_pt_transport_err_str(const struct sg_pt_base * objp, int max_b_len, char * b); -/* If not available return -1 */ +/* If not available return -1 otherwise return number of milliseconds + * that the lower layers (and hardware) took to execute the previous + * command. */ int get_scsi_pt_duration_ms(const struct sg_pt_base * objp); +/* If not available return 0 otherwise return number of nanoseconds that the + * lower layers (and hardware) took to execute the command just completed. */ +uint64_t get_pt_duration_ns(const struct sg_pt_base * objp); + +/* The two functions yield requested and actual data transfer lengths in + * bytes. The second argument is a pointer to the data-in length; the third + * argument is a pointer to the data-out length. The pointers may be NULL. + * The _actual_ values are related to resid (residual count from DMA) */ +void get_pt_req_lengths(const struct sg_pt_base * objp, int * req_dinp, + int * req_doutp); +void get_pt_actual_lengths(const struct sg_pt_base * objp, int * act_dinp, + int * act_doutp); + +/* 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 this object + * then the dev_fd provided to that constructor is not altered by this + * destructor. So the user should still close dev_fd (perhaps with + * scsi_pt_close_device() ). */ void destruct_scsi_pt_obj(struct sg_pt_base * objp); #ifdef SG_LIB_WIN32 @@ -144,4 +287,4 @@ } #endif -#endif +#endif /* SG_PT_H */ diff -Nru sdparm-1.10/include/sg_pt_linux.h sdparm-1.12/include/sg_pt_linux.h --- sdparm-1.10/include/sg_pt_linux.h 1970-01-01 00:00:00.000000000 +0000 +++ sdparm-1.12/include/sg_pt_linux.h 2020-08-03 04:16:31.000000000 +0000 @@ -0,0 +1,202 @@ +#ifndef SG_PT_LINUX_H +#define SG_PT_LINUX_H + +/* + * Copyright (c) 2017-2018 Douglas Gilbert. + * All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the BSD_LICENSE file. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +#include + +#include "sg_pt_nvme.h" + +/* This header is for internal use by the sg3_utils library (libsgutils) + * and is Linux specific. Best not to include it directly in code that + * is meant to be OS independent. */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef HAVE_LINUX_BSG_H + +#define BSG_PROTOCOL_SCSI 0 + +#define BSG_SUB_PROTOCOL_SCSI_CMD 0 +#define BSG_SUB_PROTOCOL_SCSI_TMF 1 +#define BSG_SUB_PROTOCOL_SCSI_TRANSPORT 2 + +/* + * For flag constants below: + * sg.h sg_io_hdr also has bits defined for it's flags member. These + * two flag values (0x10 and 0x20) have the same meaning in sg.h . For + * bsg the BSG_FLAG_Q_AT_HEAD flag is ignored since it is the default. + */ +#define BSG_FLAG_Q_AT_TAIL 0x10 /* default is Q_AT_HEAD */ +#define BSG_FLAG_Q_AT_HEAD 0x20 + +#ifndef SGV4_FLAG_YIELD_TAG +#define SGV4_FLAG_YIELD_TAG 0x8 +#endif +#ifndef SGV4_FLAG_FIND_BY_TAG +#define SGV4_FLAG_FIND_BY_TAG 0x100 +#endif +#ifndef SGV4_FLAG_IMMED +#define SGV4_FLAG_IMMED 0x400 +#endif +#ifndef SGV4_FLAG_IMMED +#define SGV4_FLAG_IMMED 0x400 +#endif +#ifndef SGV4_FLAG_DEV_SCOPE +#define SGV4_FLAG_DEV_SCOPE 0x1000 +#endif +#ifndef SGV4_FLAG_SHARE +#define SGV4_FLAG_SHARE 0x2000 +#endif + +struct sg_io_v4 { + __s32 guard; /* [i] 'Q' to differentiate from v3 */ + __u32 protocol; /* [i] 0 -> SCSI , .... */ + __u32 subprotocol; /* [i] 0 -> SCSI command, 1 -> SCSI task + management function, .... */ + + __u32 request_len; /* [i] in bytes */ + __u64 request; /* [i], [*i] {SCSI: cdb} */ + __u64 request_tag; /* [i] {in sg 4.0+ this is out parameter} */ + __u32 request_attr; /* [i] {SCSI: task attribute} */ + __u32 request_priority; /* [i] {SCSI: task priority} */ + __u32 request_extra; /* [i] {used for pack_id} */ + __u32 max_response_len; /* [i] in bytes */ + __u64 response; /* [i], [*o] {SCSI: (auto)sense data} */ + + /* "dout_": data out (to device); "din_": data in (from device) */ + __u32 dout_iovec_count; /* [i] 0 -> "flat" dout transfer else + dout_xfer points to array of iovec */ + __u32 dout_xfer_len; /* [i] bytes to be transferred to device */ + __u32 din_iovec_count; /* [i] 0 -> "flat" din transfer */ + __u32 din_xfer_len; /* [i] bytes to be transferred from device */ + __u64 dout_xferp; /* [i], [*i] */ + __u64 din_xferp; /* [i], [*o] */ + + __u32 timeout; /* [i] units: millisecond */ + __u32 flags; /* [i] bit mask */ + __u64 usr_ptr; /* [i->o] unused internally */ + __u32 spare_in; /* [i] */ + + __u32 driver_status; /* [o] 0 -> ok */ + __u32 transport_status; /* [o] 0 -> ok */ + __u32 device_status; /* [o] {SCSI: command completion status} */ + __u32 retry_delay; /* [o] {SCSI: status auxiliary information} */ + __u32 info; /* [o] additional information */ + __u32 duration; /* [o] time to complete, in milliseconds */ + __u32 response_len; /* [o] bytes of response actually written */ + __s32 din_resid; /* [o] din_xfer_len - actual_din_xfer_len */ + __s32 dout_resid; /* [o] dout_xfer_len - actual_dout_xfer_len */ + __u64 generated_tag; /* [o] {SCSI: transport generated task tag} */ + __u32 spare_out; /* [o] */ + + __u32 padding; +}; + +#else + +#include + +#endif + + +struct sg_pt_linux_scsi { + struct sg_io_v4 io_hdr; /* use v4 header as it is more general */ + /* Leave io_hdr in first place of this structure */ + bool is_sg; + bool is_bsg; + bool is_nvme; /* OS device type, if false ignore nvme_our_sntl */ + bool nvme_our_sntl; /* true: our SNTL; false: received NVMe command */ + bool nvme_stat_dnr; /* Do No Retry, part of completion status field */ + bool nvme_stat_more; /* More, part of completion status field */ + bool mdxfer_out; /* direction of metadata xfer, true->data-out */ + int dev_fd; /* -1 if not given (yet) */ + int in_err; + int os_err; + int sg_version; /* for deciding whether to use v3 or v4 interface */ + uint32_t nvme_nsid; /* 1 to 0xfffffffe are possibly valid, 0 + * implies dev_fd is not a NVMe device + * (is_nvme=false) or it is a NVMe char + * device (e.g. /dev/nvme0 ) */ + uint32_t nvme_result; /* DW0 from completion queue */ + uint32_t nvme_status; /* SCT|SC: DW3 27:17 from completion queue, + * note: the DNR+More bit are not there. + * The whole 16 byte completion q entry is + * sent back as sense data */ + uint32_t mdxfer_len; + struct sg_sntl_dev_state_t dev_stat; + void * mdxferp; + uint8_t * nvme_id_ctlp; /* cached response to controller IDENTIFY */ + uint8_t * free_nvme_id_ctlp; + uint8_t tmf_request[4]; +}; + +struct sg_pt_base { + struct sg_pt_linux_scsi impl; +}; + + +#ifndef sg_nvme_admin_cmd +#define sg_nvme_admin_cmd sg_nvme_passthru_cmd +#endif + +/* Linux NVMe related ioctls */ +#ifndef NVME_IOCTL_ID +#define NVME_IOCTL_ID _IO('N', 0x40) +#endif +#ifndef NVME_IOCTL_ADMIN_CMD +#define NVME_IOCTL_ADMIN_CMD _IOWR('N', 0x41, struct sg_nvme_admin_cmd) +#endif +#ifndef NVME_IOCTL_SUBMIT_IO +#define NVME_IOCTL_SUBMIT_IO _IOW('N', 0x42, struct sg_nvme_user_io) +#endif +#ifndef NVME_IOCTL_IO_CMD +#define NVME_IOCTL_IO_CMD _IOWR('N', 0x43, struct sg_nvme_passthru_cmd) +#endif +#ifndef NVME_IOCTL_RESET +#define NVME_IOCTL_RESET _IO('N', 0x44) +#endif +#ifndef NVME_IOCTL_SUBSYS_RESET +#define NVME_IOCTL_SUBSYS_RESET _IO('N', 0x45) +#endif +#ifndef NVME_IOCTL_RESCAN +#define NVME_IOCTL_RESCAN _IO('N', 0x46) +#endif +#if 0 +#define NVME_IOCTL_ADMIN64_CMD _IOWR('N', 0x47, struct nvme_passthru_cmd64) +#define NVME_IOCTL_IO64_CMD _IOWR('N', 0x48, struct nvme_passthru_cmd64) +#endif + +extern bool sg_bsg_nvme_char_major_checked; +extern int sg_bsg_major; +extern volatile int sg_nvme_char_major; +extern long sg_lin_page_size; + +void sg_find_bsg_nvme_char_major(int verbose); +int sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb); +int sg_linux_get_sg_version(const struct sg_pt_base * vp); + +/* This trims given NVMe block device name in Linux (e.g. /dev/nvme0n1p5) + * to the name of its associated char device (e.g. /dev/nvme0). If this + * occurs true is returned and the char device name is placed in 'b' (as + * long as b_len is sufficient). Otherwise false is returned. */ +bool sg_get_nvme_char_devname(const char * nvme_block_devname, uint32_t b_len, + char * b); + +#ifdef __cplusplus +} +#endif + +#endif /* end of SG_PT_LINUX_H */ diff -Nru sdparm-1.10/include/sg_pt_nvme.h sdparm-1.12/include/sg_pt_nvme.h --- sdparm-1.10/include/sg_pt_nvme.h 1970-01-01 00:00:00.000000000 +0000 +++ sdparm-1.12/include/sg_pt_nvme.h 2020-04-13 04:43:10.000000000 +0000 @@ -0,0 +1,224 @@ +#ifndef SG_PT_NVME_H +#define SG_PT_NVME_H + +/* + * Copyright (c) 2017-2019 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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* structures copied and slightly modified from which + * is Copyright (c) 2011-2014, Intel Corporation. */ + + +/* Note that the command input structure is in (packed) "cpu" format. That + * means, for example, if the CPU is little endian (most are) then so is the + * structure. However what comes out in the data-in buffer (e.g. for the + * Admin Identify command response) is almost all little endian following ATA + * (but no SCSI and IP which are big endian) and Intel's preference. There + * are exceptions, for example the EUI-64 identifiers in the Admin Identify + * response are big endian. + * + * Code online (e.g. nvme-cli at github.com) seems to favour packed + * structures, while the author prefers byte offset plus a range of unaligned + * integer builders such as those in sg_unaligned.h . + */ + +#ifdef __GNUC__ +#ifndef __clang__ + struct __attribute__((__packed__)) sg_nvme_user_io +#else + struct sg_nvme_user_io +#endif +#else +struct sg_nvme_user_io +#endif +{ + 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 + +/* Using byte offsets and unaligned be/le copies safer than packed + * structures. These are for sg_nvme_user_io . */ +#define SG_NVME_IO_OPCODE 0 +#define SG_NVME_IO_FLAGS 1 +#define SG_NVME_IO_CONTROL 2 +#define SG_NVME_IO_NBLOCKS 4 +#define SG_NVME_IO_RSVD 6 +#define SG_NVME_IO_METADATA 8 +#define SG_NVME_IO_ADDR 16 +#define SG_NVME_IO_SLBA 24 +#define SG_NVME_IO_DSMGMT 32 +#define SG_NVME_IO_REFTAG 36 +#define SG_NVME_IO_APPTAG 40 +#define SG_NVME_IO_APPMASK 42 + +#ifdef __GNUC__ +#ifndef __clang__ + struct __attribute__((__packed__)) sg_nvme_passthru_cmd +#else + struct sg_nvme_passthru_cmd +#endif +#else +struct sg_nvme_passthru_cmd +#endif +{ + 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; +#ifdef SG_LIB_LINUX + uint32_t timeout_ms; + uint32_t result; /* out: DWord(0) from completion queue */ +#endif +} +#ifdef SG_LIB_FREEBSD +__packed; +#else +; +#endif + +struct sg_sntl_dev_state_t { + uint8_t scsi_dsense; + uint8_t enclosure_override; /* ENC_OV in sdparm */ + uint8_t pdt; /* 6 bit value in INQUIRY response */ + uint8_t enc_serv; /* single bit in INQUIRY response */ + uint8_t id_ctl253; /* NVMSR field of Identify controller (byte 253) */ + bool wce; /* Write Cache Enable (WCE) setting */ + bool wce_changed; /* WCE setting has been changed */ +}; + +struct sg_sntl_result_t { + uint8_t sstatus; + uint8_t sk; + uint8_t asc; + uint8_t ascq; + uint8_t in_byte; + uint8_t in_bit; /* use 255 for 'no bit position given' */ +}; + +struct sg_opcode_info_t { + uint8_t opcode; + uint16_t sa; /* service action, 0 for none */ + uint32_t flags; /* OR-ed set of F_* flags */ + uint8_t len_mask[16]; /* len=len_mask[0], then mask for cdb[1]... */ + /* ignore cdb bytes after position 15 */ +}; + +/* Using byte offsets and unaligned be/le copies safer than packed + * structures. These are for sg_nvme_passthru_cmd . */ +#define SG_NVME_PT_OPCODE 0 /* length: 1 byte */ +#define SG_NVME_PT_FLAGS 1 /* length: 1 byte */ +#define SG_NVME_PT_RSVD1 2 /* length: 2 bytes */ +#define SG_NVME_PT_NSID 4 /* length: 4 bytes */ +#define SG_NVME_PT_CDW2 8 /* length: 4 bytes */ +#define SG_NVME_PT_CDW3 12 /* length: 4 bytes */ +#define SG_NVME_PT_METADATA 16 /* length: 8 bytes */ +#define SG_NVME_PT_ADDR 24 /* length: 8 bytes */ +#define SG_NVME_PT_METADATA_LEN 32 /* length: 4 bytes */ +#define SG_NVME_PT_DATA_LEN 36 /* length: 4 bytes */ +#define SG_NVME_PT_CDW10 40 /* length: 4 bytes */ +#define SG_NVME_PT_CDW11 44 /* length: 4 bytes */ +#define SG_NVME_PT_CDW12 48 /* length: 4 bytes */ +#define SG_NVME_PT_CDW13 52 /* length: 4 bytes */ +#define SG_NVME_PT_CDW14 56 /* length: 4 bytes */ +#define SG_NVME_PT_CDW15 60 /* length: 4 bytes */ + +#ifdef SG_LIB_LINUX +/* General references state that "all NVMe commands are 64 bytes long". If + * so then the following are add-ons by Linux, go to the OS and not the + * the NVMe device. */ +#define SG_NVME_PT_TIMEOUT_MS 64 /* length: 4 bytes */ +#define SG_NVME_PT_RESULT 68 /* length: 4 bytes */ +#endif + +/* Byte offset of Result and Status (plus phase bit) in CQ */ +#define SG_NVME_PT_CQ_RESULT 0 /* CDW0, length: 4 bytes */ +#define SG_NVME_PT_CQ_DW0 0 /* CDW0, length: 4 bytes */ +#define SG_NVME_PT_CQ_DW1 4 /* CDW1, length: 4 bytes */ +#define SG_NVME_PT_CQ_DW2 8 /* CDW2, length: 4 bytes */ +#define SG_NVME_PT_CQ_DW3 12 /* CDW3, length: 4 bytes */ +#define SG_NVME_PT_CQ_STATUS_P 14 /* CDW3 31:16, length: 2 bytes */ + + +/* Valid namespace IDs (nsid_s) range from 1 to 0xfffffffe, leaving: */ +#define SG_NVME_BROADCAST_NSID 0xffffffff /* all namespaces */ +#define SG_NVME_CTL_NSID 0x0 /* the "controller's" namespace */ + +/* Vendor specific (sg3_utils) VPD pages */ +#define SG_NVME_VPD_NICR 0xde /* NVME Identify controller response */ + + +/* Given the NVMe Identify Controller response and optionally the NVMe + * Identify Namespace response (NULL otherwise), generate the SCSI VPD + * page 0x83 (device identification) descriptor(s) in dop. Return the + * number of bytes written which will not exceed max_do_len. Probably use + * Peripheral Device Type (pdt) of 0 (disk) for don't know. Transport + * protocol (tproto) should be -1 if not known, else SCSI value. + * N.B. Does not write total VPD page length into dop[2:3] . */ +int sg_make_vpd_devid_for_nvme(const uint8_t * nvme_id_ctl_p, + const uint8_t * nvme_id_ns_p, int pdt, + int tproto, uint8_t * dop, int max_do_len); + +/* Initialize dev_stat pointed to by dsp */ +void sntl_init_dev_stat(struct sg_sntl_dev_state_t * dsp); + +/* Internal function (common to all OSes) to support the SNTL SCSI MODE + * SENSE(10) command. Has a vendor specific Unit Attention mpage which + * has only one field currently: ENC_OV (enclosure override) */ +int sntl_resp_mode_sense10(const struct sg_sntl_dev_state_t * dsp, + const uint8_t * cdbp, uint8_t * dip, int mx_di_len, + struct sg_sntl_result_t * resp); + +/* Internal function (common to all OSes) to support the SNTL SCSI MODE + * SELECT(10) command. */ +int sntl_resp_mode_select10(struct sg_sntl_dev_state_t * dsp, + const uint8_t * cdbp, const uint8_t * dop, + int do_len, struct sg_sntl_result_t * resp); + +/* Returns pointer to array of struct sg_opcode_info_t of SCSI commands + * translated to NVMe. */ +const struct sg_opcode_info_t * sg_get_opcode_translation(void); + +#ifdef __cplusplus +} +#endif + +#endif /* SG_PT_NVME_H */ diff -Nru sdparm-1.10/include/sg_pt_win32.h sdparm-1.12/include/sg_pt_win32.h --- sdparm-1.10/include/sg_pt_win32.h 2012-12-11 12:56:34.000000000 +0000 +++ sdparm-1.12/include/sg_pt_win32.h 2018-02-28 03:48:11.000000000 +0000 @@ -11,108 +11,115 @@ * Updated for cygwin version 1.7.17 changes 20121026 */ +/* WIN32_LEAN_AND_MEAN may be required to prevent inclusion of */ +#define WIN32_LEAN_AND_MEAN #include +#ifdef __cplusplus +extern "C" { +#endif + #define SCSI_MAX_SENSE_LEN 64 #define SCSI_MAX_CDB_LEN 16 #define SCSI_MAX_INDIRECT_DATA 16384 typedef struct { - USHORT Length; - UCHAR ScsiStatus; - UCHAR PathId; - UCHAR TargetId; - UCHAR Lun; - UCHAR CdbLength; - UCHAR SenseInfoLength; - UCHAR DataIn; - ULONG DataTransferLength; - ULONG TimeOutValue; - ULONG_PTR DataBufferOffset; /* was ULONG; problem in 64 bit */ - ULONG SenseInfoOffset; - UCHAR Cdb[SCSI_MAX_CDB_LEN]; + USHORT Length; + UCHAR ScsiStatus; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + UCHAR CdbLength; + UCHAR SenseInfoLength; + UCHAR DataIn; + ULONG DataTransferLength; + ULONG TimeOutValue; + ULONG_PTR DataBufferOffset; /* was ULONG; problem in 64 bit */ + ULONG SenseInfoOffset; + UCHAR Cdb[SCSI_MAX_CDB_LEN]; } SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH; typedef struct { - USHORT Length; - UCHAR ScsiStatus; - UCHAR PathId; - UCHAR TargetId; - UCHAR Lun; - UCHAR CdbLength; - UCHAR SenseInfoLength; - UCHAR DataIn; - ULONG DataTransferLength; - ULONG TimeOutValue; - PVOID DataBuffer; - ULONG SenseInfoOffset; - UCHAR Cdb[SCSI_MAX_CDB_LEN]; + USHORT Length; + UCHAR ScsiStatus; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + UCHAR CdbLength; + UCHAR SenseInfoLength; + UCHAR DataIn; + ULONG DataTransferLength; + ULONG TimeOutValue; + PVOID DataBuffer; + ULONG SenseInfoOffset; + UCHAR Cdb[SCSI_MAX_CDB_LEN]; } SCSI_PASS_THROUGH_DIRECT, *PSCSI_PASS_THROUGH_DIRECT; typedef struct { - SCSI_PASS_THROUGH spt; - /* plscsi shows a follow on 16 bytes allowing 32 byte cdb */ - ULONG Filler; - UCHAR ucSenseBuf[SCSI_MAX_SENSE_LEN]; - UCHAR ucDataBuf[SCSI_MAX_INDIRECT_DATA]; + SCSI_PASS_THROUGH spt; + /* plscsi shows a follow on 16 bytes allowing 32 byte cdb */ + ULONG Filler; + UCHAR ucSenseBuf[SCSI_MAX_SENSE_LEN]; + UCHAR ucDataBuf[SCSI_MAX_INDIRECT_DATA]; } SCSI_PASS_THROUGH_WITH_BUFFERS, *PSCSI_PASS_THROUGH_WITH_BUFFERS; typedef struct { - SCSI_PASS_THROUGH_DIRECT spt; - ULONG Filler; - UCHAR ucSenseBuf[SCSI_MAX_SENSE_LEN]; + SCSI_PASS_THROUGH_DIRECT spt; + ULONG Filler; + UCHAR ucSenseBuf[SCSI_MAX_SENSE_LEN]; } SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, *PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER; typedef struct { - UCHAR NumberOfLogicalUnits; - UCHAR InitiatorBusId; - ULONG InquiryDataOffset; + UCHAR NumberOfLogicalUnits; + UCHAR InitiatorBusId; + ULONG InquiryDataOffset; } SCSI_BUS_DATA, *PSCSI_BUS_DATA; typedef struct { - UCHAR NumberOfBusses; - SCSI_BUS_DATA BusData[1]; + UCHAR NumberOfBusses; + SCSI_BUS_DATA BusData[1]; } SCSI_ADAPTER_BUS_INFO, *PSCSI_ADAPTER_BUS_INFO; typedef struct { - UCHAR PathId; - UCHAR TargetId; - UCHAR Lun; - BOOLEAN DeviceClaimed; - ULONG InquiryDataLength; - ULONG NextInquiryDataOffset; - UCHAR InquiryData[1]; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + BOOLEAN DeviceClaimed; + ULONG InquiryDataLength; + ULONG NextInquiryDataOffset; + UCHAR InquiryData[1]; } SCSI_INQUIRY_DATA, *PSCSI_INQUIRY_DATA; typedef struct { - ULONG Length; - UCHAR PortNumber; - UCHAR PathId; - UCHAR TargetId; - UCHAR Lun; + ULONG Length; + UCHAR PortNumber; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; } SCSI_ADDRESS, *PSCSI_ADDRESS; - /* - * method codes + * Standard IOCTL define */ -#define METHOD_BUFFERED 0 -#define METHOD_IN_DIRECT 1 -#define METHOD_OUT_DIRECT 2 -#define METHOD_NEITHER 3 +#ifndef CTL_CODE +#define CTL_CODE(DevType, Function, Method, Access) \ + (((DevType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) +#endif /* * file access values */ +#ifndef FILE_ANY_ACCESS #define FILE_ANY_ACCESS 0 +#endif #ifndef FILE_READ_ACCESS #define FILE_READ_ACCESS 0x0001 #endif @@ -120,6 +127,322 @@ #define FILE_WRITE_ACCESS 0x0002 #endif +// IOCTL_STORAGE_QUERY_PROPERTY + +#define FILE_DEVICE_MASS_STORAGE 0x0000002d +#define IOCTL_STORAGE_BASE FILE_DEVICE_MASS_STORAGE +#define FILE_ANY_ACCESS 0 + +// #define METHOD_BUFFERED 0 + +#define IOCTL_STORAGE_QUERY_PROPERTY \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) + + +#ifndef _DEVIOCTL_ +typedef enum _STORAGE_BUS_TYPE { + BusTypeUnknown = 0x00, + BusTypeScsi = 0x01, + BusTypeAtapi = 0x02, + BusTypeAta = 0x03, + BusType1394 = 0x04, + BusTypeSsa = 0x05, + BusTypeFibre = 0x06, + BusTypeUsb = 0x07, + BusTypeRAID = 0x08, + BusTypeiScsi = 0x09, + BusTypeSas = 0x0A, + BusTypeSata = 0x0B, + BusTypeSd = 0x0C, + BusTypeMmc = 0x0D, + BusTypeVirtual = 0xE, + BusTypeFileBackedVirtual = 0xF, + BusTypeSpaces = 0x10, + BusTypeNvme = 0x11, + BusTypeSCM = 0x12, + BusTypeUfs = 0x13, + BusTypeMax = 0x14, + BusTypeMaxReserved = 0x7F +} STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE; + +typedef enum _STORAGE_PROTOCOL_TYPE { + ProtocolTypeUnknown = 0, + ProtocolTypeScsi, + ProtocolTypeAta, + ProtocolTypeNvme, + ProtocolTypeSd +} STORAGE_PROTOCOL_TYPE; + +typedef enum _STORAGE_PROTOCOL_NVME_DATA_TYPE { + NVMeDataTypeUnknown = 0, + NVMeDataTypeIdentify, + NVMeDataTypeLogPage, + NVMeDataTypeFeature +} STORAGE_PROTOCOL_NVME_DATA_TYPE; + +typedef struct _STORAGE_PROTOCOL_SPECIFIC_DATA { + STORAGE_PROTOCOL_TYPE ProtocolType; + ULONG DataType; + ULONG ProtocolDataRequestValue; + ULONG ProtocolDataRequestSubValue; + ULONG ProtocolDataOffset; + ULONG ProtocolDataLength; + ULONG FixedProtocolReturnData; + ULONG Reserved[3]; +} STORAGE_PROTOCOL_SPECIFIC_DATA; + + +typedef struct _STORAGE_DEVICE_DESCRIPTOR { + ULONG Version; + ULONG Size; + UCHAR DeviceType; + UCHAR DeviceTypeModifier; + BOOLEAN RemovableMedia; + BOOLEAN CommandQueueing; + ULONG VendorIdOffset; /* 0 if not available */ + ULONG ProductIdOffset; /* 0 if not available */ + ULONG ProductRevisionOffset;/* 0 if not available */ + ULONG SerialNumberOffset; /* -1 if not available ?? */ + STORAGE_BUS_TYPE BusType; + ULONG RawPropertiesLength; + UCHAR RawDeviceProperties[1]; +} STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR; + +#define STORAGE_PROTOCOL_STRUCTURE_VERSION 0x1 + +#define IOCTL_STORAGE_PROTOCOL_COMMAND \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x04F0, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +typedef struct _STORAGE_PROTOCOL_COMMAND { + DWORD Version; /* STORAGE_PROTOCOL_STRUCTURE_VERSION */ + DWORD Length; + STORAGE_PROTOCOL_TYPE ProtocolType; + DWORD Flags; + DWORD ReturnStatus; + DWORD ErrorCode; + DWORD CommandLength; + DWORD ErrorInfoLength; + DWORD DataToDeviceTransferLength; + DWORD DataFromDeviceTransferLength; + DWORD TimeOutValue; + DWORD ErrorInfoOffset; + DWORD DataToDeviceBufferOffset; + DWORD DataFromDeviceBufferOffset; + DWORD CommandSpecific; + DWORD Reserved0; + DWORD FixedProtocolReturnData; + DWORD Reserved1[3]; + BYTE Command[1]; /* has CommandLength elements */ +} STORAGE_PROTOCOL_COMMAND, *PSTORAGE_PROTOCOL_COMMAND; + +#endif /* _DEVIOCTL_ */ + +typedef struct _STORAGE_DEVICE_UNIQUE_IDENTIFIER { + ULONG Version; + ULONG Size; + ULONG StorageDeviceIdOffset; + ULONG StorageDeviceOffset; + ULONG DriveLayoutSignatureOffset; +} STORAGE_DEVICE_UNIQUE_IDENTIFIER, *PSTORAGE_DEVICE_UNIQUE_IDENTIFIER; + +// Use CompareStorageDuids(PSTORAGE_DEVICE_UNIQUE_IDENTIFIER duid1, duid2) +// to test for equality + +#ifndef _DEVIOCTL_ +typedef enum _STORAGE_QUERY_TYPE { + PropertyStandardQuery = 0, + PropertyExistsQuery, + PropertyMaskQuery, + PropertyQueryMaxDefined +} STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE; + +typedef enum _STORAGE_PROPERTY_ID { + StorageDeviceProperty = 0, + StorageAdapterProperty, + StorageDeviceIdProperty, + StorageDeviceUniqueIdProperty, + StorageDeviceWriteCacheProperty, + StorageMiniportProperty, + StorageAccessAlignmentProperty, + /* Identify controller goes to adapter; Identify namespace to device */ + StorageAdapterProtocolSpecificProperty = 49, + StorageDeviceProtocolSpecificProperty = 50 +} STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID; + +typedef struct _STORAGE_PROPERTY_QUERY { + STORAGE_PROPERTY_ID PropertyId; + STORAGE_QUERY_TYPE QueryType; + UCHAR AdditionalParameters[1]; +} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY; + +typedef struct _STORAGE_PROTOCOL_DATA_DESCRIPTOR { + DWORD Version; + DWORD Size; + STORAGE_PROTOCOL_SPECIFIC_DATA ProtocolSpecificData; +} STORAGE_PROTOCOL_DATA_DESCRIPTOR, *PSTORAGE_PROTOCOL_DATA_DESCRIPTOR; + +// Command completion status +// The "Phase Tag" field and "Status Field" are separated in spec. We define +// them in the same data structure to ease the memory access from software. +// +typedef union { + struct { + USHORT P : 1; // Phase Tag (P) + + USHORT SC : 8; // Status Code (SC) + USHORT SCT : 3; // Status Code Type (SCT) + USHORT Reserved : 2; + USHORT M : 1; // More (M) + USHORT DNR : 1; // Do Not Retry (DNR) + } DUMMYSTRUCTNAME; + USHORT AsUshort; +} NVME_COMMAND_STATUS, *PNVME_COMMAND_STATUS; + +// Information of log: NVME_LOG_PAGE_ERROR_INFO. Size: 64 bytes +// +typedef struct { + ULONGLONG ErrorCount; + USHORT SQID; // Submission Queue ID + USHORT CMDID; // Command ID + NVME_COMMAND_STATUS Status; // Status Field: This field indicates the + // Status Field for the command that + // completed. The Status Field is located in + // bits 15:01, bit 00 corresponds to the Phase + // Tag posted for the command. + struct { + USHORT Byte : 8; // Byte in command that contained error + USHORT Bit : 3; // Bit in command that contained error + USHORT Reserved : 5; + } ParameterErrorLocation; + + ULONGLONG Lba; // LBA: This field indicates the first LBA + // that experienced the error condition, if + // applicable. + ULONG NameSpace; // Namespace: This field indicates the nsid + // that the error is associated with, if + // applicable. + UCHAR VendorInfoAvailable; // Vendor Specific Information Available + UCHAR Reserved0[3]; + ULONGLONG CommandSpecificInfo; // This field contains command specific + // information. If used, the command + // definition specifies the information + // returned. + UCHAR Reserved1[24]; +} NVME_ERROR_INFO_LOG, *PNVME_ERROR_INFO_LOG; + +typedef struct { + + ULONG DW0; + ULONG Reserved; + + union { + struct { + USHORT SQHD; // SQ Head Pointer (SQHD) + USHORT SQID; // SQ Identifier (SQID) + } DUMMYSTRUCTNAME; + + ULONG AsUlong; + } DW2; + + union { + struct { + USHORT CID; // Command Identifier (CID) + NVME_COMMAND_STATUS Status; + } DUMMYSTRUCTNAME; + + ULONG AsUlong; + } DW3; + +} NVME_COMPLETION_ENTRY, *PNVME_COMPLETION_ENTRY; + + +// Bit-mask values for STORAGE_PROTOCOL_COMMAND - "Flags" field. +// +// Flag indicates the request targeting to adapter instead of device. +#define STORAGE_PROTOCOL_COMMAND_FLAG_ADAPTER_REQUEST 0x80000000 + +// +// Status values for STORAGE_PROTOCOL_COMMAND - "ReturnStatus" field. +// +#define STORAGE_PROTOCOL_STATUS_PENDING 0x0 +#define STORAGE_PROTOCOL_STATUS_SUCCESS 0x1 +#define STORAGE_PROTOCOL_STATUS_ERROR 0x2 +#define STORAGE_PROTOCOL_STATUS_INVALID_REQUEST 0x3 +#define STORAGE_PROTOCOL_STATUS_NO_DEVICE 0x4 +#define STORAGE_PROTOCOL_STATUS_BUSY 0x5 +#define STORAGE_PROTOCOL_STATUS_DATA_OVERRUN 0x6 +#define STORAGE_PROTOCOL_STATUS_INSUFFICIENT_RESOURCES 0x7 + +#define STORAGE_PROTOCOL_STATUS_NOT_SUPPORTED 0xFF + +// Command Length for Storage Protocols. +// +// NVMe commands are always 64 bytes. +#define STORAGE_PROTOCOL_COMMAND_LENGTH_NVME 0x40 + +// Command Specific Information for Storage Protocols - CommandSpecific field +// +#define STORAGE_PROTOCOL_SPECIFIC_NVME_ADMIN_COMMAND 0x01 +#define STORAGE_PROTOCOL_SPECIFIC_NVME_NVM_COMMAND 0x02 + +#endif /* _DEVIOCTL_ */ + + +// NVME_PASS_THROUGH + +#ifndef STB_IO_CONTROL +typedef struct _SRB_IO_CONTROL { + ULONG HeaderLength; + UCHAR Signature[8]; + ULONG Timeout; + ULONG ControlCode; + ULONG ReturnCode; + ULONG Length; +} SRB_IO_CONTROL, *PSRB_IO_CONTROL; +#endif + +#ifndef NVME_PASS_THROUGH_SRB_IO_CODE + +#define NVME_SIG_STR "NvmeMini" +#define NVME_STORPORT_DRIVER 0xe000 + +#define NVME_PASS_THROUGH_SRB_IO_CODE \ + CTL_CODE(NVME_STORPORT_DRIVER, 0x0800, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#pragma pack(1) + +/* Following is pre-Win10; used with DeviceIoControl(IOCTL_SCSI_MINIPORT), + * in Win10 need DeviceIoControl(IOCTL_STORAGE_PROTOCOL_COMMAND) for pure + * pass-through. Win10 also has "Protocol specific queries" for things like + * Identify and Get feature. */ +typedef struct _NVME_PASS_THROUGH_IOCTL +{ + SRB_IO_CONTROL SrbIoCtrl; + ULONG VendorSpecific[6]; + ULONG NVMeCmd[16]; /* Command DW[0...15] */ + ULONG CplEntry[4]; /* Completion DW[0...3] */ + ULONG Direction; /* 0=None, 1=Out, 2=In, 3=I/O */ + ULONG QueueId; /* 0=AdminQ */ + ULONG DataBufferLen; /* sizeof(DataBuffer) if Data In */ + ULONG MetaDataLen; + ULONG ReturnBufferLen; /* offsetof(DataBuffer), plus + * sizeof(DataBuffer) if Data Out */ + UCHAR DataBuffer[1]; +} NVME_PASS_THROUGH_IOCTL; +#pragma pack() + +#endif // NVME_PASS_THROUGH_SRB_IO_CODE + + +/* + * method codes + */ +#define METHOD_BUFFERED 0 +#define METHOD_IN_DIRECT 1 +#define METHOD_OUT_DIRECT 2 +#define METHOD_NEITHER 3 + #define IOCTL_SCSI_BASE 0x00000004 @@ -130,25 +453,21 @@ #define SCSI_IOCTL_DATA_IN 1 #define SCSI_IOCTL_DATA_UNSPECIFIED 2 -/* - * Standard IOCTL define - */ -#ifndef CTL_CODE -#define CTL_CODE(DevType, Function, Method, Access) \ - (((DevType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) -#endif - #define IOCTL_SCSI_PASS_THROUGH CTL_CODE(IOCTL_SCSI_BASE, 0x0401, \ - METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_SCSI_MINIPORT CTL_CODE(IOCTL_SCSI_BASE, 0x0402, \ - METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_SCSI_GET_INQUIRY_DATA CTL_CODE(IOCTL_SCSI_BASE, 0x0403, \ - METHOD_BUFFERED, FILE_ANY_ACCESS) + METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_SCSI_GET_CAPABILITIES CTL_CODE(IOCTL_SCSI_BASE, 0x0404, \ - METHOD_BUFFERED, FILE_ANY_ACCESS) + METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_SCSI_PASS_THROUGH_DIRECT CTL_CODE(IOCTL_SCSI_BASE, 0x0405, \ - METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_SCSI_GET_ADDRESS CTL_CODE(IOCTL_SCSI_BASE, 0x0406, \ - METHOD_BUFFERED, FILE_ANY_ACCESS) + METHOD_BUFFERED, FILE_ANY_ACCESS) +#ifdef __cplusplus +} #endif + +#endif /* SG_PT_WIN32_H */ diff -Nru sdparm-1.10/include/sg_unaligned.h sdparm-1.12/include/sg_unaligned.h --- sdparm-1.10/include/sg_unaligned.h 2016-02-02 04:21:27.000000000 +0000 +++ sdparm-1.12/include/sg_unaligned.h 2018-12-07 16:42:06.000000000 +0000 @@ -2,261 +2,393 @@ #define SG_UNALIGNED_H /* - * Copyright (c) 2014-2016 Douglas Gilbert. + * Copyright (c) 2014-2018 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. + * + * SPDX-License-Identifier: BSD-2-Clause */ -#include +#include +#include /* for uint8_t and friends */ +#include /* for memcpy */ #ifdef __cplusplus extern "C" { #endif -/* Borrowed from the Linux kernel, via mhvtl */ +/* These inline functions convert integers (always unsigned) to byte streams + * and vice versa. They have two goals: + * - change the byte ordering of integers between host order and big + * endian ("_be") or little endian ("_le") + * - copy the big or little endian byte stream so it complies with any + * alignment that host integers require + * + * Host integer to given endian byte stream is a "_put_" function taking + * two arguments (integer and pointer to byte stream) returning void. + * Given endian byte stream to host integer is a "_get_" function that takes + * one argument and returns an integer of appropriate size (uint32_t for 24 + * bit operations, uint64_t for 48 bit operations). + * + * Big endian byte format "on the wire" is the default used by SCSI + * standards (www.t10.org). Big endian is also the network byte order. + * Little endian is used by ATA, PCI and NVMe. + */ + +/* The generic form of these routines was borrowed from the Linux kernel, + * via mhvtl. There is a specialised version of the main functions for + * little endian or big endian provided that not-quite-standard defines for + * endianness are available from the compiler and the header + * (a GNU extension) has been detected by ./configure . To force the + * generic version, use './configure --disable-fast-lebe ' . */ + +/* Note: Assumes that the source and destination locations do not overlap. + * An example of overlapping source and destination: + * sg_put_unaligned_le64(j, ((uint8_t *)&j) + 1); + * Best not to do things like that. + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" /* need this to see if HAVE_BYTESWAP_H */ +#endif + +#undef GOT_UNALIGNED_SPECIALS /* just in case */ -/* In the first section below, functions that copy unsigned integers in a - * computer's native format, to and from an unaligned big endian sequence of - * bytes. Big endian byte format "on the wire" is the default used by SCSI - * standards (www.t10.org). Big endian is also the network byte order. */ +#if defined(__BYTE_ORDER__) && defined(HAVE_BYTESWAP_H) && \ + ! defined(IGNORE_FAST_LEBE) -static inline uint16_t __get_unaligned_be16(const uint8_t *p) +#if defined(__LITTLE_ENDIAN__) || (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + +#define GOT_UNALIGNED_SPECIALS 1 + +#include /* for bswap_16(), bswap_32() and bswap_64() */ + +// #warning ">>>>>> Doing Little endian special unaligneds" + +static inline uint16_t sg_get_unaligned_be16(const void *p) { - return p[0] << 8 | p[1]; + uint16_t u; + + memcpy(&u, p, 2); + return bswap_16(u); } -static inline uint32_t __get_unaligned_be32(const uint8_t *p) +static inline uint32_t sg_get_unaligned_be32(const void *p) { - return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; + uint32_t u; + + memcpy(&u, p, 4); + return bswap_32(u); } -/* Assume 48 bit value placed in uint64_t */ -static inline uint64_t __get_unaligned_be48(const uint8_t *p) +static inline uint64_t sg_get_unaligned_be64(const void *p) { - return (uint64_t)__get_unaligned_be16(p) << 32 | - __get_unaligned_be32(p + 2); + uint64_t u; + + memcpy(&u, p, 8); + return bswap_64(u); } -static inline uint64_t __get_unaligned_be64(const uint8_t *p) +static inline void sg_put_unaligned_be16(uint16_t val, void *p) { - return (uint64_t)__get_unaligned_be32(p) << 32 | - __get_unaligned_be32(p + 4); + uint16_t u = bswap_16(val); + + memcpy(p, &u, 2); } -static inline void __put_unaligned_be16(uint16_t val, uint8_t *p) +static inline void sg_put_unaligned_be32(uint32_t val, void *p) { - *p++ = val >> 8; - *p++ = val; + uint32_t u = bswap_32(val); + + memcpy(p, &u, 4); } -static inline void __put_unaligned_be32(uint32_t val, uint8_t *p) +static inline void sg_put_unaligned_be64(uint64_t val, void *p) { - __put_unaligned_be16(val >> 16, p); - __put_unaligned_be16(val, p + 2); + uint64_t u = bswap_64(val); + + memcpy(p, &u, 8); } -/* Assume 48 bit value placed in uint64_t */ -static inline void __put_unaligned_be48(uint64_t val, uint8_t *p) +static inline uint16_t sg_get_unaligned_le16(const void *p) { - __put_unaligned_be16(val >> 32, p); - __put_unaligned_be32(val, p + 2); + uint16_t u; + + memcpy(&u, p, 2); + return u; } -static inline void __put_unaligned_be64(uint64_t val, uint8_t *p) +static inline uint32_t sg_get_unaligned_le32(const void *p) { - __put_unaligned_be32(val >> 32, p); - __put_unaligned_be32(val, p + 4); + uint32_t u; + + memcpy(&u, p, 4); + return u; } -static inline uint16_t sg_get_unaligned_be16(const void *p) +static inline uint64_t sg_get_unaligned_le64(const void *p) { - return __get_unaligned_be16((const uint8_t *)p); + uint64_t u; + + memcpy(&u, p, 8); + return u; } -static inline uint32_t sg_get_unaligned_be24(const uint8_t *p) +static inline void sg_put_unaligned_le16(uint16_t val, void *p) { - return p[0] << 16 | p[1] << 8 | p[2]; + memcpy(p, &val, 2); } -static inline uint32_t sg_get_unaligned_be32(const void *p) +static inline void sg_put_unaligned_le32(uint32_t val, void *p) { - return __get_unaligned_be32((const uint8_t *)p); + memcpy(p, &val, 4); } -/* Assume 48 bit value placed in uint64_t */ -static inline uint64_t sg_get_unaligned_be48(const void *p) +static inline void sg_put_unaligned_le64(uint64_t val, void *p) { - return __get_unaligned_be48((const uint8_t *)p); + memcpy(p, &val, 8); } -static inline uint64_t sg_get_unaligned_be64(const void *p) +#elif defined(__BIG_ENDIAN__) || (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) + +#define GOT_UNALIGNED_SPECIALS 1 + +#include + +// #warning ">>>>>> Doing BIG endian special unaligneds" + +static inline uint16_t sg_get_unaligned_le16(const void *p) { - return __get_unaligned_be64((const uint8_t *)p); + uint16_t u; + + memcpy(&u, p, 2); + return bswap_16(u); } -/* Returns 0 if 'num_bytes' is less than or equal to 0 or greater than - * 8 (i.e. sizeof(uint64_t)). Else returns result in uint64_t which is - * an 8 bytes unsigned integer. */ -static inline uint64_t sg_get_unaligned_be(int num_bytes, const void *p) +static inline uint32_t sg_get_unaligned_le32(const void *p) { - if ((num_bytes <= 0) || (num_bytes > (int)sizeof(uint64_t))) - return 0; - else { - const uint8_t * xp = (const uint8_t *)p; - uint64_t res = *xp; + uint32_t u; - for (++xp; num_bytes > 1; ++xp, --num_bytes) - res = (res << 8) | *xp; - return res; - } + memcpy(&u, p, 4); + return bswap_32(u); } -static inline void sg_put_unaligned_be16(uint16_t val, void *p) +static inline uint64_t sg_get_unaligned_le64(const void *p) { - __put_unaligned_be16(val, (uint8_t *)p); + uint64_t u; + + memcpy(&u, p, 8); + return bswap_64(u); } -static inline void sg_put_unaligned_be24(uint32_t val, void *p) +static inline void sg_put_unaligned_le16(uint16_t val, void *p) { - ((uint8_t *)p)[0] = (val >> 16) & 0xff; - ((uint8_t *)p)[1] = (val >> 8) & 0xff; - ((uint8_t *)p)[2] = val & 0xff; + uint16_t u = bswap_16(val); + + memcpy(p, &u, 2); } -static inline void sg_put_unaligned_be32(uint32_t val, void *p) +static inline void sg_put_unaligned_le32(uint32_t val, void *p) { - __put_unaligned_be32(val, (uint8_t *)p); + uint32_t u = bswap_32(val); + + memcpy(p, &u, 4); } -/* Assume 48 bit value placed in uint64_t */ -static inline void sg_put_unaligned_be48(uint64_t val, void *p) +static inline void sg_put_unaligned_le64(uint64_t val, void *p) { - __put_unaligned_be48(val, (uint8_t *)p); + uint64_t u = bswap_64(val); + + memcpy(p, &u, 8); } -static inline void sg_put_unaligned_be64(uint64_t val, void *p) +static inline uint16_t sg_get_unaligned_be16(const void *p) { - __put_unaligned_be64(val, (uint8_t *)p); + uint16_t u; + + memcpy(&u, p, 2); + return u; } -/* Since cdb and parameter blocks are often memset to zero before these - * unaligned function partially fill them, then check for a val of zero - * and ignore if it is with these variants. */ -static inline void sg_nz_put_unaligned_be16(uint16_t val, void *p) +static inline uint32_t sg_get_unaligned_be32(const void *p) { - if (val) - __put_unaligned_be16(val, (uint8_t *)p); + uint32_t u; + + memcpy(&u, p, 4); + return u; } -static inline void sg_nz_put_unaligned_be24(uint32_t val, void *p) +static inline uint64_t sg_get_unaligned_be64(const void *p) { - if (val) { - ((uint8_t *)p)[0] = (val >> 16) & 0xff; - ((uint8_t *)p)[1] = (val >> 8) & 0xff; - ((uint8_t *)p)[2] = val & 0xff; - } + uint64_t u; + + memcpy(&u, p, 8); + return u; } -static inline void sg_nz_put_unaligned_be32(uint32_t val, void *p) +static inline void sg_put_unaligned_be16(uint16_t val, void *p) { - if (val) - __put_unaligned_be32(val, (uint8_t *)p); + memcpy(p, &val, 2); } -static inline void sg_nz_put_unaligned_be64(uint64_t val, void *p) +static inline void sg_put_unaligned_be32(uint32_t val, void *p) { - if (val) - __put_unaligned_be64(val, (uint8_t *)p); + memcpy(p, &val, 4); } +static inline void sg_put_unaligned_be64(uint64_t val, void *p) +{ + memcpy(p, &val, 8); +} -/* Below are the little endian equivalents of the big endian functions - * above. Little endian is used by ATA, networking and PCI. - */ +#endif /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ */ +#endif /* #if defined __BYTE_ORDER__ && defined && + * ! defined IGNORE_FAST_LEBE */ + + +#ifndef GOT_UNALIGNED_SPECIALS -static inline uint16_t __get_unaligned_le16(const uint8_t *p) +/* Now we have no tricks left, so use the only way this can be done + * correctly in C safely: lots of shifts. */ + +// #warning ">>>>>> Doing GENERIC unaligneds" + +static inline uint16_t sg_get_unaligned_be16(const void *p) { - return p[1] << 8 | p[0]; + return ((const uint8_t *)p)[0] << 8 | ((const uint8_t *)p)[1]; } -static inline uint32_t __get_unaligned_le32(const uint8_t *p) +static inline uint32_t sg_get_unaligned_be32(const void *p) { - return p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0]; + return ((const uint8_t *)p)[0] << 24 | ((const uint8_t *)p)[1] << 16 | + ((const uint8_t *)p)[2] << 8 | ((const uint8_t *)p)[3]; } -static inline uint64_t __get_unaligned_le64(const uint8_t *p) +static inline uint64_t sg_get_unaligned_be64(const void *p) { - return (uint64_t)__get_unaligned_le32(p + 4) << 32 | - __get_unaligned_le32(p); + return (uint64_t)sg_get_unaligned_be32(p) << 32 | + sg_get_unaligned_be32((const uint8_t *)p + 4); } -static inline void __put_unaligned_le16(uint16_t val, uint8_t *p) +static inline void sg_put_unaligned_be16(uint16_t val, void *p) { - *p++ = val; - *p++ = val >> 8; + ((uint8_t *)p)[0] = (uint8_t)(val >> 8); + ((uint8_t *)p)[1] = (uint8_t)val; } -static inline void __put_unaligned_le32(uint32_t val, uint8_t *p) +static inline void sg_put_unaligned_be32(uint32_t val, void *p) { - __put_unaligned_le16(val >> 16, p + 2); - __put_unaligned_le16(val, p); + sg_put_unaligned_be16(val >> 16, p); + sg_put_unaligned_be16(val, (uint8_t *)p + 2); } -static inline void __put_unaligned_le64(uint64_t val, uint8_t *p) +static inline void sg_put_unaligned_be64(uint64_t val, void *p) { - __put_unaligned_le32(val >> 32, p + 4); - __put_unaligned_le32(val, p); + sg_put_unaligned_be32(val >> 32, p); + sg_put_unaligned_be32(val, (uint8_t *)p + 4); } + static inline uint16_t sg_get_unaligned_le16(const void *p) { - return __get_unaligned_le16((const uint8_t *)p); + return ((const uint8_t *)p)[1] << 8 | ((const uint8_t *)p)[0]; } -static inline uint32_t sg_get_unaligned_le24(const void *p) +static inline uint32_t sg_get_unaligned_le32(const void *p) { - return (uint32_t)__get_unaligned_le16((const uint8_t *)p) | - ((const uint8_t *)p)[2] << 16; + return ((const uint8_t *)p)[3] << 24 | ((const uint8_t *)p)[2] << 16 | + ((const uint8_t *)p)[1] << 8 | ((const uint8_t *)p)[0]; } -static inline uint32_t sg_get_unaligned_le32(const void *p) +static inline uint64_t sg_get_unaligned_le64(const void *p) { - return __get_unaligned_le32((const uint8_t *)p); + return (uint64_t)sg_get_unaligned_le32((const uint8_t *)p + 4) << 32 | + sg_get_unaligned_le32(p); } -/* Assume 48 bit value placed in uint64_t */ -static inline uint64_t sg_get_unaligned_le48(const void *p) +static inline void sg_put_unaligned_le16(uint16_t val, void *p) { - return (uint64_t)__get_unaligned_le16((const uint8_t *)p + 4) << 32 | - __get_unaligned_le32((const uint8_t *)p); + ((uint8_t *)p)[0] = val & 0xff; + ((uint8_t *)p)[1] = val >> 8; } -static inline uint64_t sg_get_unaligned_le64(const void *p) +static inline void sg_put_unaligned_le32(uint32_t val, void *p) +{ + sg_put_unaligned_le16(val >> 16, (uint8_t *)p + 2); + sg_put_unaligned_le16(val, p); +} + +static inline void sg_put_unaligned_le64(uint64_t val, void *p) +{ + sg_put_unaligned_le32(val >> 32, (uint8_t *)p + 4); + sg_put_unaligned_le32(val, p); +} + +#endif /* #ifndef GOT_UNALIGNED_SPECIALS */ + +/* Following are lesser used conversions that don't have specializations + * for endianness; big endian first. In summary these are the 24, 48 bit and + * given-length conversions plus the "nz" conditional put conversions. */ + +/* Now big endian, get 24+48 then put 24+48 */ +static inline uint32_t sg_get_unaligned_be24(const void *p) { - return __get_unaligned_le64((const uint8_t *)p); + return ((const uint8_t *)p)[0] << 16 | ((const uint8_t *)p)[1] << 8 | + ((const uint8_t *)p)[2]; +} + +/* Assume 48 bit value placed in uint64_t */ +static inline uint64_t sg_get_unaligned_be48(const void *p) +{ + return (uint64_t)sg_get_unaligned_be16(p) << 32 | + sg_get_unaligned_be32((const uint8_t *)p + 2); } /* Returns 0 if 'num_bytes' is less than or equal to 0 or greater than * 8 (i.e. sizeof(uint64_t)). Else returns result in uint64_t which is - * an 8 bytes unsigned integer. */ -static inline uint64_t sg_get_unaligned_le(int num_bytes, const void *p) + * an 8 byte unsigned integer. */ +static inline uint64_t sg_get_unaligned_be(int num_bytes, const void *p) { if ((num_bytes <= 0) || (num_bytes > (int)sizeof(uint64_t))) return 0; else { - const uint8_t * xp = (const uint8_t *)p + (num_bytes - 1); + const uint8_t * xp = (const uint8_t *)p; uint64_t res = *xp; - for (--xp; num_bytes > 1; --xp, --num_bytes) + for (++xp; num_bytes > 1; ++xp, --num_bytes) res = (res << 8) | *xp; return res; } } -static inline void sg_put_unaligned_le16(uint16_t val, void *p) +static inline void sg_put_unaligned_be24(uint32_t val, void *p) { - __put_unaligned_le16(val, (uint8_t *)p); + ((uint8_t *)p)[0] = (val >> 16) & 0xff; + ((uint8_t *)p)[1] = (val >> 8) & 0xff; + ((uint8_t *)p)[2] = val & 0xff; +} + +/* Assume 48 bit value placed in uint64_t */ +static inline void sg_put_unaligned_be48(uint64_t val, void *p) +{ + sg_put_unaligned_be16(val >> 32, p); + sg_put_unaligned_be32(val, (uint8_t *)p + 2); +} + +/* Now little endian, get 24+48 then put 24+48 */ +static inline uint32_t sg_get_unaligned_le24(const void *p) +{ + return (uint32_t)sg_get_unaligned_le16(p) | + ((const uint8_t *)p)[2] << 16; +} + +/* Assume 48 bit value placed in uint64_t */ +static inline uint64_t sg_get_unaligned_le48(const void *p) +{ + return (uint64_t)sg_get_unaligned_le16((const uint8_t *)p + 4) << 32 | + sg_get_unaligned_le32(p); } static inline void sg_put_unaligned_le24(uint32_t val, void *p) @@ -266,11 +398,6 @@ ((uint8_t *)p)[0] = val & 0xff; } -static inline void sg_put_unaligned_le32(uint32_t val, void *p) -{ - __put_unaligned_le32(val, (uint8_t *)p); -} - /* Assume 48 bit value placed in uint64_t */ static inline void sg_put_unaligned_le48(uint64_t val, void *p) { @@ -282,18 +409,57 @@ ((uint8_t *)p)[0] = val & 0xff; } -static inline void sg_put_unaligned_le64(uint64_t val, void *p) +/* Returns 0 if 'num_bytes' is less than or equal to 0 or greater than + * 8 (i.e. sizeof(uint64_t)). Else returns result in uint64_t which is + * an 8 byte unsigned integer. */ +static inline uint64_t sg_get_unaligned_le(int num_bytes, const void *p) { - __put_unaligned_le64(val, (uint8_t *)p); + if ((num_bytes <= 0) || (num_bytes > (int)sizeof(uint64_t))) + return 0; + else { + const uint8_t * xp = (const uint8_t *)p + (num_bytes - 1); + uint64_t res = *xp; + + for (--xp; num_bytes > 1; --xp, --num_bytes) + res = (res << 8) | *xp; + return res; + } } /* Since cdb and parameter blocks are often memset to zero before these * unaligned function partially fill them, then check for a val of zero - * and ignore if it is with these variants. */ + * and ignore if it is with these variants. First big endian, then little */ +static inline void sg_nz_put_unaligned_be16(uint16_t val, void *p) +{ + if (val) + sg_put_unaligned_be16(val, p); +} + +static inline void sg_nz_put_unaligned_be24(uint32_t val, void *p) +{ + if (val) { + ((uint8_t *)p)[0] = (val >> 16) & 0xff; + ((uint8_t *)p)[1] = (val >> 8) & 0xff; + ((uint8_t *)p)[2] = val & 0xff; + } +} + +static inline void sg_nz_put_unaligned_be32(uint32_t val, void *p) +{ + if (val) + sg_put_unaligned_be32(val, p); +} + +static inline void sg_nz_put_unaligned_be64(uint64_t val, void *p) +{ + if (val) + sg_put_unaligned_be64(val, p); +} + static inline void sg_nz_put_unaligned_le16(uint16_t val, void *p) { if (val) - __put_unaligned_le16(val, (uint8_t *)p); + sg_put_unaligned_le16(val, p); } static inline void sg_nz_put_unaligned_le24(uint32_t val, void *p) @@ -308,15 +474,16 @@ static inline void sg_nz_put_unaligned_le32(uint32_t val, void *p) { if (val) - __put_unaligned_le32(val, (uint8_t *)p); + sg_put_unaligned_le32(val, p); } static inline void sg_nz_put_unaligned_le64(uint64_t val, void *p) { if (val) - __put_unaligned_le64(val, (uint8_t *)p); + sg_put_unaligned_le64(val, p); } + #ifdef __cplusplus } #endif diff -Nru sdparm-1.10/install-sh sdparm-1.12/install-sh --- sdparm-1.10/install-sh 2015-12-02 00:45:13.000000000 +0000 +++ sdparm-1.12/install-sh 2020-12-25 01:49:20.000000000 +0000 @@ -1,7 +1,7 @@ #!/bin/sh # install - install a program, script, or datafile -scriptversion=2013-12-25.23; # UTC +scriptversion=2020-11-14.01; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the @@ -69,6 +69,11 @@ # Desired mode of installed file. mode=0755 +# Create dirs (including intermediate dirs) using mode 755. +# This is like GNU 'install' as of coreutils 8.32 (2020). +mkdir_umask=22 + +backupsuffix= chgrpcmd= chmodcmd=$chmodprog chowncmd= @@ -99,18 +104,28 @@ --version display version info and exit. -c (ignored) - -C install only if different (preserve the last data modification time) + -C install only if different (preserve data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. + -p pass -p to $cpprog. -s $stripprog installed files. + -S SUFFIX attempt to back up existing files, with suffix SUFFIX. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG + +By default, rm is invoked with -f; when overridden with RMPROG, +it's up to you to specify -f if you want it. + +If -S is not specified, no backups are attempted. + +Email bug reports to bug-automake@gnu.org. +Automake home page: https://www.gnu.org/software/automake/ " while test $# -ne 0; do @@ -137,8 +152,13 @@ -o) chowncmd="$chownprog $2" shift;; + -p) cpprog="$cpprog -p";; + -s) stripcmd=$stripprog;; + -S) backupsuffix="$2" + shift;; + -t) is_target_a_directory=always dst_arg=$2 @@ -255,6 +275,10 @@ dstdir=$dst test -d "$dstdir" dstdir_status=$? + # Don't chown directories that already exist. + if test $dstdir_status = 0; then + chowncmd="" + fi else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command @@ -271,15 +295,18 @@ fi dst=$dst_arg - # If destination is a directory, append the input filename; won't work - # if double slashes aren't ignored. + # If destination is a directory, append the input filename. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst - dst=$dstdir/`basename "$src"` + dstbase=`basename "$src"` + case $dst in + */) dst=$dst$dstbase;; + *) dst=$dst/$dstbase;; + esac dstdir_status=0 else dstdir=`dirname "$dst"` @@ -288,27 +315,16 @@ fi fi + case $dstdir in + */) dstdirslash=$dstdir;; + *) dstdirslash=$dstdir/;; + esac + obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') - # Create intermediate dirs using mode 755 as modified by the umask. - # This is like FreeBSD 'install' as of 1997-10-28. - umask=`umask` - case $stripcmd.$umask in - # Optimize common cases. - *[2367][2367]) mkdir_umask=$umask;; - .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; - - *[0-7]) - mkdir_umask=`expr $umask + 22 \ - - $umask % 100 % 40 + $umask % 20 \ - - $umask % 10 % 4 + $umask % 2 - `;; - *) mkdir_umask=$umask,go-w;; - esac - # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then @@ -318,43 +334,49 @@ fi posix_mkdir=false - case $umask in - *[123567][0-7][0-7]) - # POSIX mkdir -p sets u+wx bits regardless of umask, which - # is incompatible with FreeBSD 'install' when (umask & 300) != 0. - ;; - *) - tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ - trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 - - if (umask $mkdir_umask && - exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 - then - if test -z "$dir_arg" || { - # Check for POSIX incompatibilities with -m. - # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or - # other-writable bit of parent directory when it shouldn't. - # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. - ls_ld_tmpdir=`ls -ld "$tmpdir"` - case $ls_ld_tmpdir in - d????-?r-*) different_mode=700;; - d????-?--*) different_mode=755;; - *) false;; - esac && - $mkdirprog -m$different_mode -p -- "$tmpdir" && { - ls_ld_tmpdir_1=`ls -ld "$tmpdir"` - test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" - } - } - then posix_mkdir=: - fi - rmdir "$tmpdir/d" "$tmpdir" - else - # Remove any dirs left behind by ancient mkdir implementations. - rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null - fi - trap '' 0;; - esac;; + # The $RANDOM variable is not portable (e.g., dash). Use it + # here however when possible just to lower collision chance. + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + + trap ' + ret=$? + rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null + exit $ret + ' 0 + + # Because "mkdir -p" follows existing symlinks and we likely work + # directly in world-writeable /tmp, make sure that the '$tmpdir' + # directory is successfully created first before we actually test + # 'mkdir -p'. + if (umask $mkdir_umask && + $mkdirprog $mkdir_mode "$tmpdir" && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + test_tmpdir="$tmpdir/a" + ls_ld_tmpdir=`ls -ld "$test_tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null + fi + trap '' 0;; esac if @@ -365,7 +387,7 @@ then : else - # The umask is ridiculous, or mkdir does not conform to POSIX, + # mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. @@ -394,7 +416,7 @@ prefixes= else if $posix_mkdir; then - (umask=$mkdir_umask && + (umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 @@ -427,14 +449,25 @@ else # Make a couple of temp file names in the proper directory. - dsttmp=$dstdir/_inst.$$_ - rmtmp=$dstdir/_rm.$$_ + dsttmp=${dstdirslash}_inst.$$_ + rmtmp=${dstdirslash}_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. - (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + (umask $cp_umask && + { test -z "$stripcmd" || { + # Create $dsttmp read-write so that cp doesn't create it read-only, + # which would cause strip to fail. + if test -z "$doit"; then + : >"$dsttmp" # No need to fork-exec 'touch'. + else + $doit touch "$dsttmp" + fi + } + } && + $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # @@ -460,6 +493,13 @@ then rm -f "$dsttmp" else + # If $backupsuffix is set, and the file being installed + # already exists, attempt a backup. Don't worry if it fails, + # e.g., if mv doesn't support -f. + if test -n "$backupsuffix" && test -f "$dst"; then + $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null + fi + # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || @@ -474,9 +514,9 @@ # file should still install successfully. { test ! -f "$dst" || - $doit $rmcmd -f "$dst" 2>/dev/null || + $doit $rmcmd "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && - { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + { $doit $rmcmd "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 @@ -493,9 +533,9 @@ done # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" +# time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: diff -Nru sdparm-1.10/lib/BSD_LICENSE sdparm-1.12/lib/BSD_LICENSE --- sdparm-1.10/lib/BSD_LICENSE 2010-03-12 16:48:12.000000000 +0000 +++ sdparm-1.12/lib/BSD_LICENSE 2019-01-11 06:11:25.000000000 +0000 @@ -1,27 +1,26 @@ -/* - * Copyright (c) 1999-2010 Douglas Gilbert. - * 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. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * 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. - */ + +Copyright (c) 1999-2019, Douglas Gilbert +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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + +Above is the: +SPDX-License-Identifier: BSD-2-Clause diff -Nru sdparm-1.10/lib/Makefile.am sdparm-1.12/lib/Makefile.am --- sdparm-1.10/lib/Makefile.am 2016-02-08 04:53:45.000000000 +0000 +++ sdparm-1.12/lib/Makefile.am 2021-02-09 01:22:53.000000000 +0000 @@ -5,12 +5,13 @@ sg_cmds_basic2.c \ sg_cmds_extra.c \ sg_cmds_mmc.c \ - sg_pt_common.c + sg_pt_common.c if OS_LINUX libsgutils2_la_SOURCES += \ sg_pt_linux.c \ - sg_io_linux.c + sg_io_linux.c \ + sg_pt_linux_nvme.c endif if OS_WIN32_MINGW @@ -33,24 +34,40 @@ libsgutils2_la_SOURCES += sg_pt_osf1.c endif +if DEBUG +# This is active if --enable-debug given to ./configure +# removed -Wduplicated-branches because needs gcc-8 +DBG_CFLAGS = -Wextra -Wmisleading-indentation -Wduplicated-cond -Wlogical-op -Wnull-dereference -Wshadow -Wjump-misses-init +DBG_CPPFLAGS = -DDEBUG +else +DBG_CFLAGS = +DBG_CPPFLAGS = +endif + # For C++/clang testing -## CC = gcc -## CC = g++ +## CC = gcc-9 +## CXX = g++ ## CC = clang +## CXX = clang++ ## CC = clang++ +## CC = powerpc64-linux-gnu-gcc -# -std= can be c99, c11, gnu11, etc. Default is gnu89 (gnu90 is the same) +# -std= can be c99, c11, gnu11, etc. Default is gnu11 for C code # -Wall is no longer all warnings. Add -W (since renamed to -Wextra) for more -AM_CPPFLAGS = -iquote ${top_srcdir}/include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -AM_CFLAGS = -Wall -W +AM_CPPFLAGS = -iquote ${top_srcdir}/include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 $(DBG_CPPFLAGS) +AM_CFLAGS = -Wall -W $(DBG_CFLAGS) +# AM_CFLAGS = -Wall -W -Wextra -Wmisleading-indentation -Wduplicated-cond -Wduplicated-branches -Wlogical-op -Wnull-dereference -Wshadow -Wjump-misses-init # AM_CFLAGS = -Wall -W -pedantic -std=c11 -# AM_CFLAGS = -Wall -W -pedantic -std=c++11 +# AM_CFLAGS = -Wall -W -pedantic -std=c11 --analyze +# AM_CFLAGS = -Wall -W -pedantic -std=c++14 +# AM_CFLAGS = -Wall -W -pedantic -std=c++1z +# AM_CFLAGS = -Wall -W -pedantic -std=c++20 lib_LTLIBRARIES = libsgutils2.la -libsgutils2_la_LDFLAGS = -version-info 2:0:0 -no-undefined +libsgutils2_la_LDFLAGS = -version-info 2:0:0 -no-undefined -release ${PACKAGE_VERSION} -libsgutils2_la_LIBADD = @GETOPT_O_FILES@ @os_libs@ +libsgutils2_la_LIBADD = @GETOPT_O_FILES@ libsgutils2_la_DEPENDENCIES = @GETOPT_O_FILES@ diff -Nru sdparm-1.10/lib/Makefile.in sdparm-1.12/lib/Makefile.in --- sdparm-1.10/lib/Makefile.in 2015-12-01 21:03:58.000000000 +0000 +++ sdparm-1.12/lib/Makefile.in 2021-01-04 06:17:04.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2014 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -90,7 +90,8 @@ host_triplet = @host@ @OS_LINUX_TRUE@am__append_1 = \ @OS_LINUX_TRUE@ sg_pt_linux.c \ -@OS_LINUX_TRUE@ sg_io_linux.c +@OS_LINUX_TRUE@ sg_io_linux.c \ +@OS_LINUX_TRUE@ sg_pt_linux_nvme.c @OS_WIN32_MINGW_TRUE@am__append_2 = sg_pt_win32.c @OS_WIN32_CYGWIN_TRUE@am__append_3 = sg_pt_win32.c @@ -138,9 +139,10 @@ LTLIBRARIES = $(lib_LTLIBRARIES) am__libsgutils2_la_SOURCES_DIST = sg_lib.c sg_lib_data.c \ sg_cmds_basic.c sg_cmds_basic2.c sg_cmds_extra.c sg_cmds_mmc.c \ - sg_pt_common.c sg_pt_linux.c sg_io_linux.c sg_pt_win32.c \ - sg_pt_freebsd.c sg_pt_solaris.c sg_pt_osf1.c -@OS_LINUX_TRUE@am__objects_1 = sg_pt_linux.lo sg_io_linux.lo + sg_pt_common.c sg_pt_linux.c sg_io_linux.c sg_pt_linux_nvme.c \ + sg_pt_win32.c sg_pt_freebsd.c sg_pt_solaris.c sg_pt_osf1.c +@OS_LINUX_TRUE@am__objects_1 = sg_pt_linux.lo sg_io_linux.lo \ +@OS_LINUX_TRUE@ sg_pt_linux_nvme.lo @OS_WIN32_MINGW_TRUE@am__objects_2 = sg_pt_win32.lo @OS_WIN32_CYGWIN_TRUE@am__objects_3 = sg_pt_win32.lo @OS_FREEBSD_TRUE@am__objects_4 = sg_pt_freebsd.lo @@ -174,7 +176,15 @@ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp -am__depfiles_maybe = depfiles +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/sg_cmds_basic.Plo \ + ./$(DEPDIR)/sg_cmds_basic2.Plo ./$(DEPDIR)/sg_cmds_extra.Plo \ + ./$(DEPDIR)/sg_cmds_mmc.Plo ./$(DEPDIR)/sg_io_linux.Plo \ + ./$(DEPDIR)/sg_lib.Plo ./$(DEPDIR)/sg_lib_data.Plo \ + ./$(DEPDIR)/sg_pt_common.Plo ./$(DEPDIR)/sg_pt_freebsd.Plo \ + ./$(DEPDIR)/sg_pt_linux.Plo ./$(DEPDIR)/sg_pt_linux_nvme.Plo \ + ./$(DEPDIR)/sg_pt_osf1.Plo ./$(DEPDIR)/sg_pt_solaris.Plo \ + ./$(DEPDIR)/sg_pt_win32.Plo am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) @@ -262,6 +272,7 @@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ @@ -280,7 +291,9 @@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ +PTHREAD_LIB = @PTHREAD_LIB@ RANLIB = @RANLIB@ +RT_LIB = @RT_LIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ @@ -326,12 +339,11 @@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ -os_cflags = @os_cflags@ -os_libs = @os_libs@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ @@ -344,18 +356,29 @@ sg_cmds_basic2.c sg_cmds_extra.c sg_cmds_mmc.c sg_pt_common.c \ $(am__append_1) $(am__append_2) $(am__append_3) \ $(am__append_4) $(am__append_5) $(am__append_6) +@DEBUG_FALSE@DBG_CFLAGS = + +# This is active if --enable-debug given to ./configure +# removed -Wduplicated-branches because needs gcc-8 +@DEBUG_TRUE@DBG_CFLAGS = -Wextra -Wmisleading-indentation -Wduplicated-cond -Wlogical-op -Wnull-dereference -Wshadow -Wjump-misses-init +@DEBUG_FALSE@DBG_CPPFLAGS = +@DEBUG_TRUE@DBG_CPPFLAGS = -DDEBUG # For C++/clang testing -# -std= can be c99, c11, gnu11, etc. Default is gnu89 (gnu90 is the same) +# -std= can be c99, c11, gnu11, etc. Default is gnu11 for C code # -Wall is no longer all warnings. Add -W (since renamed to -Wextra) for more -AM_CPPFLAGS = -iquote ${top_srcdir}/include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -AM_CFLAGS = -Wall -W +AM_CPPFLAGS = -iquote ${top_srcdir}/include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 $(DBG_CPPFLAGS) +AM_CFLAGS = -Wall -W $(DBG_CFLAGS) +# AM_CFLAGS = -Wall -W -Wextra -Wmisleading-indentation -Wduplicated-cond -Wduplicated-branches -Wlogical-op -Wnull-dereference -Wshadow -Wjump-misses-init # AM_CFLAGS = -Wall -W -pedantic -std=c11 -# AM_CFLAGS = -Wall -W -pedantic -std=c++11 +# AM_CFLAGS = -Wall -W -pedantic -std=c11 --analyze +# AM_CFLAGS = -Wall -W -pedantic -std=c++14 +# AM_CFLAGS = -Wall -W -pedantic -std=c++1z +# AM_CFLAGS = -Wall -W -pedantic -std=c++20 lib_LTLIBRARIES = libsgutils2.la -libsgutils2_la_LDFLAGS = -version-info 2:0:0 -no-undefined -libsgutils2_la_LIBADD = @GETOPT_O_FILES@ @os_libs@ +libsgutils2_la_LDFLAGS = -version-info 2:0:0 -no-undefined -release ${PACKAGE_VERSION} +libsgutils2_la_LIBADD = @GETOPT_O_FILES@ libsgutils2_la_DEPENDENCIES = @GETOPT_O_FILES@ all: all-am @@ -370,16 +393,16 @@ exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lib/Makefile'; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/Makefile'; \ $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu lib/Makefile + $(AUTOMAKE) --foreign lib/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -435,19 +458,26 @@ distclean-compile: -rm -f *.tab.c -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_cmds_basic.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_cmds_basic2.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_cmds_extra.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_cmds_mmc.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_io_linux.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_lib.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_lib_data.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_common.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_freebsd.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_linux.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_osf1.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_solaris.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_win32.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_cmds_basic.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_cmds_basic2.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_cmds_extra.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_cmds_mmc.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_io_linux.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_lib.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_lib_data.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_freebsd.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_linux.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_linux_nvme.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_osf1.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_solaris.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_win32.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -528,7 +558,10 @@ distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ @@ -601,7 +634,20 @@ mostlyclean-am distclean: distclean-am - -rm -rf ./$(DEPDIR) + -rm -f ./$(DEPDIR)/sg_cmds_basic.Plo + -rm -f ./$(DEPDIR)/sg_cmds_basic2.Plo + -rm -f ./$(DEPDIR)/sg_cmds_extra.Plo + -rm -f ./$(DEPDIR)/sg_cmds_mmc.Plo + -rm -f ./$(DEPDIR)/sg_io_linux.Plo + -rm -f ./$(DEPDIR)/sg_lib.Plo + -rm -f ./$(DEPDIR)/sg_lib_data.Plo + -rm -f ./$(DEPDIR)/sg_pt_common.Plo + -rm -f ./$(DEPDIR)/sg_pt_freebsd.Plo + -rm -f ./$(DEPDIR)/sg_pt_linux.Plo + -rm -f ./$(DEPDIR)/sg_pt_linux_nvme.Plo + -rm -f ./$(DEPDIR)/sg_pt_osf1.Plo + -rm -f ./$(DEPDIR)/sg_pt_solaris.Plo + -rm -f ./$(DEPDIR)/sg_pt_win32.Plo -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags @@ -647,7 +693,20 @@ installcheck-am: maintainer-clean: maintainer-clean-am - -rm -rf ./$(DEPDIR) + -rm -f ./$(DEPDIR)/sg_cmds_basic.Plo + -rm -f ./$(DEPDIR)/sg_cmds_basic2.Plo + -rm -f ./$(DEPDIR)/sg_cmds_extra.Plo + -rm -f ./$(DEPDIR)/sg_cmds_mmc.Plo + -rm -f ./$(DEPDIR)/sg_io_linux.Plo + -rm -f ./$(DEPDIR)/sg_lib.Plo + -rm -f ./$(DEPDIR)/sg_lib_data.Plo + -rm -f ./$(DEPDIR)/sg_pt_common.Plo + -rm -f ./$(DEPDIR)/sg_pt_freebsd.Plo + -rm -f ./$(DEPDIR)/sg_pt_linux.Plo + -rm -f ./$(DEPDIR)/sg_pt_linux_nvme.Plo + -rm -f ./$(DEPDIR)/sg_pt_osf1.Plo + -rm -f ./$(DEPDIR)/sg_pt_solaris.Plo + -rm -f ./$(DEPDIR)/sg_pt_win32.Plo -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic @@ -668,9 +727,9 @@ .MAKE: install-am install-strip -.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ - clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \ - ctags-am distclean distclean-compile distclean-generic \ +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libLTLIBRARIES clean-libtool cscopelist-am \ + ctags ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ diff -Nru sdparm-1.10/lib/sg_cmds_basic2.c sdparm-1.12/lib/sg_cmds_basic2.c --- sdparm-1.10/lib/sg_cmds_basic2.c 2015-12-02 03:08:31.000000000 +0000 +++ sdparm-1.12/lib/sg_cmds_basic2.c 2020-07-24 18:56:21.000000000 +0000 @@ -1,8 +1,10 @@ /* - * Copyright (c) 1999-2015 Douglas Gilbert. + * Copyright (c) 1999-2020 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. + * + * SPDX-License-Identifier: BSD-2-Clause */ /* @@ -19,15 +21,18 @@ #include #include #include -#include "sg_lib.h" -#include "sg_cmds_basic.h" -#include "sg_pt.h" -#include "sg_unaligned.h" +#include #ifdef HAVE_CONFIG_H #include "config.h" #endif +#include "sg_lib.h" +#include "sg_cmds_basic.h" +#include "sg_pt.h" +#include "sg_unaligned.h" +#include "sg_pr2serr.h" + #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */ @@ -68,69 +73,56 @@ #define INQUIRY_RESP_INITIAL_LEN 36 -#ifdef __GNUC__ -static int pr2ws(const char * fmt, ...) - __attribute__ ((format (printf, 1, 2))); -#else -static int pr2ws(const char * fmt, ...); -#endif - - -static int -pr2ws(const char * fmt, ...) +static struct sg_pt_base * +create_pt_obj(const char * cname) { - va_list args; - int n; - - va_start(args, fmt); - n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args); - va_end(args); - return n; + struct sg_pt_base * ptvp = construct_scsi_pt_obj(); + if (NULL == ptvp) + pr2ws("%s: out of memory\n", cname); + return ptvp; } /* Invokes a SCSI SYNCHRONIZE CACHE (10) command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int -sg_ll_sync_cache_10(int sg_fd, int sync_nv, int immed, int group, - unsigned int lba, unsigned int count, int noisy, +sg_ll_sync_cache_10(int sg_fd, bool sync_nv, bool immed, int group, + unsigned int lba, unsigned int count, bool noisy, int verbose) { - int res, ret, k, sense_cat; - unsigned char scCmdBlk[SYNCHRONIZE_CACHE_CMDLEN] = + static const char * const cdb_s = "synchronize cache(10)"; + int res, ret, sense_cat; + uint8_t sc_cdb[SYNCHRONIZE_CACHE_CMDLEN] = {SYNCHRONIZE_CACHE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (sync_nv) - scCmdBlk[1] |= 4; + sc_cdb[1] |= 4; if (immed) - scCmdBlk[1] |= 2; - sg_put_unaligned_be32((uint32_t)lba, scCmdBlk + 2); - scCmdBlk[6] = group & 0x1f; + sc_cdb[1] |= 2; + sg_put_unaligned_be32((uint32_t)lba, sc_cdb + 2); + sc_cdb[6] = group & 0x1f; if (count > 0xffff) { pr2ws("count too big\n"); return -1; } - sg_put_unaligned_be16((int16_t)count, scCmdBlk + 7); + sg_put_unaligned_be16((int16_t)count, sc_cdb + 7); if (verbose) { - pr2ws(" synchronize cache(10) cdb: "); - for (k = 0; k < SYNCHRONIZE_CACHE_CMDLEN; ++k) - pr2ws("%02x ", scCmdBlk[k]); - pr2ws("\n"); + char b[128]; + + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(sc_cdb, SYNCHRONIZE_CACHE_CMDLEN, false, + sizeof(b), b)); } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("synchronize cache(10): out of memory\n"); + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) return -1; - } - set_scsi_pt_cdb(ptvp, scCmdBlk, sizeof(scCmdBlk)); + set_scsi_pt_cdb(ptvp, sc_cdb, sizeof(sc_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "synchronize cache(10)", res, 0, - sense_b, noisy, verbose, &sense_cat); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: @@ -151,41 +143,39 @@ /* Invokes a SCSI READ CAPACITY (16) command. Returns 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int -sg_ll_readcap_16(int sg_fd, int pmi, uint64_t llba, void * resp, - int mx_resp_len, int noisy, int verbose) +sg_ll_readcap_16(int sg_fd, bool pmi, uint64_t llba, void * resp, + int mx_resp_len, bool noisy, int verbose) { - int k, ret, res, sense_cat; - unsigned char rcCmdBlk[SERVICE_ACTION_IN_16_CMDLEN] = + static const char * const cdb_s = "read capacity(16)"; + int ret, res, sense_cat; + uint8_t rc_cdb[SERVICE_ACTION_IN_16_CMDLEN] = {SERVICE_ACTION_IN_16_CMD, READ_CAPACITY_16_SA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (pmi) { /* lbs only valid when pmi set */ - rcCmdBlk[14] |= 1; - sg_put_unaligned_be64(llba, rcCmdBlk + 2); + rc_cdb[14] |= 1; + sg_put_unaligned_be64(llba, rc_cdb + 2); } /* Allocation length, no guidance in SBC-2 rev 15b */ - sg_put_unaligned_be32((uint32_t)mx_resp_len, rcCmdBlk + 10); + sg_put_unaligned_be32((uint32_t)mx_resp_len, rc_cdb + 10); if (verbose) { - pr2ws(" read capacity (16) cdb: "); - for (k = 0; k < SERVICE_ACTION_IN_16_CMDLEN; ++k) - pr2ws("%02x ", rcCmdBlk[k]); - pr2ws("\n"); + char b[128]; + + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(rc_cdb, SERVICE_ACTION_IN_16_CMDLEN, false, + sizeof(b), b)); } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("read capacity (16): out of memory\n"); + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) return -1; - } - set_scsi_pt_cdb(ptvp, rcCmdBlk, sizeof(rcCmdBlk)); + set_scsi_pt_cdb(ptvp, rc_cdb, sizeof(rc_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "read capacity (16)", res, mx_resp_len, - sense_b, noisy, verbose, &sense_cat); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: @@ -206,38 +196,36 @@ /* Invokes a SCSI READ CAPACITY (10) command. Returns 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int -sg_ll_readcap_10(int sg_fd, int pmi, unsigned int lba, void * resp, - int mx_resp_len, int noisy, int verbose) +sg_ll_readcap_10(int sg_fd, bool pmi, unsigned int lba, void * resp, + int mx_resp_len, bool noisy, int verbose) { - int k, ret, res, sense_cat; - unsigned char rcCmdBlk[READ_CAPACITY_10_CMDLEN] = + static const char * const cdb_s = "read capacity(10)"; + int ret, res, sense_cat; + uint8_t rc_cdb[READ_CAPACITY_10_CMDLEN] = {READ_CAPACITY_10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (pmi) { /* lbs only valid when pmi set */ - rcCmdBlk[8] |= 1; - sg_put_unaligned_be32((uint32_t)lba, rcCmdBlk + 2); + rc_cdb[8] |= 1; + sg_put_unaligned_be32((uint32_t)lba, rc_cdb + 2); } if (verbose) { - pr2ws(" read capacity (10) cdb: "); - for (k = 0; k < READ_CAPACITY_10_CMDLEN; ++k) - pr2ws("%02x ", rcCmdBlk[k]); - pr2ws("\n"); + char b[128]; + + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(rc_cdb, READ_CAPACITY_10_CMDLEN, false, + sizeof(b), b)); } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("read capacity (10): out of memory\n"); + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) return -1; - } - set_scsi_pt_cdb(ptvp, rcCmdBlk, sizeof(rcCmdBlk)); + set_scsi_pt_cdb(ptvp, rc_cdb, sizeof(rc_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "read capacity (10)", res, mx_resp_len, - sense_b, noisy, verbose, &sense_cat); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: @@ -258,44 +246,41 @@ /* Invokes a SCSI MODE SENSE (6) command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int -sg_ll_mode_sense6(int sg_fd, int dbd, int pc, int pg_code, int sub_pg_code, - void * resp, int mx_resp_len, int noisy, int verbose) +sg_ll_mode_sense6(int sg_fd, bool dbd, int pc, int pg_code, int sub_pg_code, + void * resp, int mx_resp_len, bool noisy, int verbose) { - int res, ret, k, sense_cat, resid; - unsigned char modesCmdBlk[MODE_SENSE6_CMDLEN] = + static const char * const cdb_s = "mode sense(6)"; + int res, ret, sense_cat, resid; + uint8_t modes_cdb[MODE_SENSE6_CMDLEN] = {MODE_SENSE6_CMD, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; - modesCmdBlk[1] = (unsigned char)(dbd ? 0x8 : 0); - modesCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); - modesCmdBlk[3] = (unsigned char)(sub_pg_code & 0xff); - modesCmdBlk[4] = (unsigned char)(mx_resp_len & 0xff); + modes_cdb[1] = (uint8_t)(dbd ? 0x8 : 0); + modes_cdb[2] = (uint8_t)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); + modes_cdb[3] = (uint8_t)(sub_pg_code & 0xff); + modes_cdb[4] = (uint8_t)(mx_resp_len & 0xff); if (mx_resp_len > 0xff) { pr2ws("mx_resp_len too big\n"); return -1; } if (verbose) { - pr2ws(" mode sense (6) cdb: "); - for (k = 0; k < MODE_SENSE6_CMDLEN; ++k) - pr2ws("%02x ", modesCmdBlk[k]); - pr2ws("\n"); + char b[128]; + + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(modes_cdb, MODE_SENSE6_CMDLEN, false, + sizeof(b), b)); } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("mode sense (6): out of memory\n"); + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) return -1; - } - set_scsi_pt_cdb(ptvp, modesCmdBlk, sizeof(modesCmdBlk)); + set_scsi_pt_cdb(ptvp, modes_cdb, sizeof(modes_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "mode sense (6)", res, mx_resp_len, - sense_b, noisy, verbose, &sense_cat); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat); resid = get_scsi_pt_resid(ptvp); - destruct_scsi_pt_obj(ptvp); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: @@ -308,21 +293,28 @@ } } else { if ((verbose > 2) && (ret > 0)) { - pr2ws(" mode sense (6): response%s\n", - (ret > 256 ? ", first 256 bytes" : "")); - dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1); + pr2ws(" %s: response", cdb_s); + if (3 == verbose) { + pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); + hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), + -1); + } else { + pr2ws(":\n"); + hex2stderr((const uint8_t *)resp, ret, 0); + } } ret = 0; } + destruct_scsi_pt_obj(ptvp); if (resid > 0) { if (resid > mx_resp_len) { - pr2ws("mode sense(6): resid (%d) should never exceed requested " - "len=%d\n", resid, mx_resp_len); + pr2ws("%s: resid (%d) should never exceed requested len=%d\n", + cdb_s, resid, mx_resp_len); return ret ? ret : SG_LIB_CAT_MALFORMED; } /* zero unfilled section of response buffer */ - memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid); + memset((uint8_t *)resp + (mx_resp_len - resid), 0, resid); } return ret; } @@ -330,45 +322,65 @@ /* Invokes a SCSI MODE SENSE (10) command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int -sg_ll_mode_sense10(int sg_fd, int llbaa, int dbd, int pc, int pg_code, +sg_ll_mode_sense10(int sg_fd, bool llbaa, bool dbd, int pc, int pg_code, int sub_pg_code, void * resp, int mx_resp_len, - int noisy, int verbose) + bool noisy, int verbose) { - int res, ret, k, sense_cat, resid; - unsigned char modesCmdBlk[MODE_SENSE10_CMDLEN] = - {MODE_SENSE10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + return sg_ll_mode_sense10_v2(sg_fd, llbaa, dbd, pc, pg_code, sub_pg_code, + resp, mx_resp_len, 0, NULL, noisy, verbose); +} + +/* Invokes a SCSI MODE SENSE (10) command. Return of 0 -> success, + * various SG_LIB_CAT_* positive values or -1 -> other errors. + * Adds the ability to set the command abort timeout + * and the ability to report the residual count. If timeout_secs is zero + * or less the default command abort timeout (60 seconds) is used. + * If residp is non-NULL then the residual value is written where residp + * points. A residual value of 0 implies mx_resp_len bytes have be written + * where resp points. If the residual value equals mx_resp_len then no + * bytes have been written. */ +int +sg_ll_mode_sense10_v2(int sg_fd, bool llbaa, bool dbd, int pc, int pg_code, + int sub_pg_code, void * resp, int mx_resp_len, + int timeout_secs, int * residp, bool noisy, int verbose) +{ + int res, ret, sense_cat, resid; + static const char * const cdb_s = "mode sense(10)"; struct sg_pt_base * ptvp; + uint8_t modes_cdb[MODE_SENSE10_CMDLEN] = + {MODE_SENSE10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t sense_b[SENSE_BUFF_LEN]; - modesCmdBlk[1] = (unsigned char)((dbd ? 0x8 : 0) | (llbaa ? 0x10 : 0)); - modesCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); - modesCmdBlk[3] = (unsigned char)(sub_pg_code & 0xff); - sg_put_unaligned_be16((int16_t)mx_resp_len, modesCmdBlk + 7); + modes_cdb[1] = (uint8_t)((dbd ? 0x8 : 0) | (llbaa ? 0x10 : 0)); + modes_cdb[2] = (uint8_t)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); + modes_cdb[3] = (uint8_t)(sub_pg_code & 0xff); + sg_put_unaligned_be16((int16_t)mx_resp_len, modes_cdb + 7); if (mx_resp_len > 0xffff) { pr2ws("mx_resp_len too big\n"); - return -1; + goto gen_err; } if (verbose) { - pr2ws(" mode sense (10) cdb: "); - for (k = 0; k < MODE_SENSE10_CMDLEN; ++k) - pr2ws("%02x ", modesCmdBlk[k]); - pr2ws("\n"); - } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("mode sense (10): out of memory\n"); - return -1; - } - set_scsi_pt_cdb(ptvp, modesCmdBlk, sizeof(modesCmdBlk)); + char b[128]; + + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(modes_cdb, MODE_SENSE10_CMDLEN, false, + sizeof(b), b)); + } + if (timeout_secs <= 0) + timeout_secs = DEF_PT_TIMEOUT; + + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + goto gen_err; + set_scsi_pt_cdb(ptvp, modes_cdb, sizeof(modes_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "mode sense (10)", res, mx_resp_len, - sense_b, noisy, verbose, &sense_cat); + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len); + res = do_scsi_pt(ptvp, sg_fd, timeout_secs, verbose); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat); resid = get_scsi_pt_resid(ptvp); - destruct_scsi_pt_obj(ptvp); + if (residp) + *residp = resid; if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: @@ -381,67 +393,78 @@ } } else { if ((verbose > 2) && (ret > 0)) { - pr2ws(" mode sense (10): response%s\n", - (ret > 256 ? ", first 256 bytes" : "")); - dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1); + pr2ws(" %s: response", cdb_s); + if (3 == verbose) { + pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); + hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), + -1); + } else { + pr2ws(":\n"); + hex2stderr((const uint8_t *)resp, ret, 0); + } } ret = 0; } + destruct_scsi_pt_obj(ptvp); if (resid > 0) { if (resid > mx_resp_len) { - pr2ws("mode sense(10): resid (%d) should never exceed requested " - "len=%d\n", resid, mx_resp_len); + pr2ws("%s: resid (%d) should never exceed requested len=%d\n", + cdb_s, resid, mx_resp_len); return ret ? ret : SG_LIB_CAT_MALFORMED; } /* zero unfilled section of response buffer */ - memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid); + memset((uint8_t *)resp + (mx_resp_len - resid), 0, resid); } return ret; +gen_err: + if (residp) + *residp = 0; + return -1; } /* Invokes a SCSI MODE SELECT (6) command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int -sg_ll_mode_select6(int sg_fd, int pf, int sp, void * paramp, int param_len, - int noisy, int verbose) +sg_ll_mode_select6_v2(int sg_fd, bool pf, bool rtd, bool sp, void * paramp, + int param_len, bool noisy, int verbose) { - int res, ret, k, sense_cat; - unsigned char modesCmdBlk[MODE_SELECT6_CMDLEN] = + static const char * const cdb_s = "mode select(6)"; + int res, ret, sense_cat; + uint8_t modes_cdb[MODE_SELECT6_CMDLEN] = {MODE_SELECT6_CMD, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; - modesCmdBlk[1] = (unsigned char)(((pf << 4) & 0x10) | (sp & 0x1)); - modesCmdBlk[4] = (unsigned char)(param_len & 0xff); + modes_cdb[1] = (uint8_t)((pf ? 0x10 : 0x0) | (sp ? 0x1 : 0x0)); + if (rtd) + modes_cdb[1] |= 0x2; + modes_cdb[4] = (uint8_t)(param_len & 0xff); if (param_len > 0xff) { - pr2ws("mode select (6): param_len too big\n"); + pr2ws("%s: param_len too big\n", cdb_s); return -1; } if (verbose) { - pr2ws(" mode select (6) cdb: "); - for (k = 0; k < MODE_SELECT6_CMDLEN; ++k) - pr2ws("%02x ", modesCmdBlk[k]); - pr2ws("\n"); + char b[128]; + + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(modes_cdb, MODE_SELECT6_CMDLEN, false, + sizeof(b), b)); } if (verbose > 1) { - pr2ws(" mode select (6) parameter list\n"); - dStrHexErr((const char *)paramp, param_len, -1); + pr2ws(" %s parameter list\n", cdb_s); + hex2stderr((const uint8_t *)paramp, param_len, -1); } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("mode select (6): out of memory\n"); + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) return -1; - } - set_scsi_pt_cdb(ptvp, modesCmdBlk, sizeof(modesCmdBlk)); + set_scsi_pt_cdb(ptvp, modes_cdb, sizeof(modes_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); + set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "mode select (6)", res, 0, sense_b, - noisy, verbose, &sense_cat); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: @@ -459,48 +482,57 @@ return ret; } +int +sg_ll_mode_select6(int sg_fd, bool pf, bool sp, void * paramp, int param_len, + bool noisy, int verbose) +{ + return sg_ll_mode_select6_v2(sg_fd, pf, false, sp, paramp, param_len, + noisy, verbose); +} + /* Invokes a SCSI MODE SELECT (10) command. Return of 0 -> success, - * various SG_LIB_CAT_* positive values or -1 -> other errors */ + * various SG_LIB_CAT_* positive values or -1 -> other errors, + * v2 adds rtd (revert to defaults) bit (spc5r11). */ int -sg_ll_mode_select10(int sg_fd, int pf, int sp, void * paramp, int param_len, - int noisy, int verbose) +sg_ll_mode_select10_v2(int sg_fd, bool pf, bool rtd, bool sp, void * paramp, + int param_len, bool noisy, int verbose) { - int res, ret, k, sense_cat; - unsigned char modesCmdBlk[MODE_SELECT10_CMDLEN] = + static const char * const cdb_s = "mode select(10)"; + int res, ret, sense_cat; + uint8_t modes_cdb[MODE_SELECT10_CMDLEN] = {MODE_SELECT10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; - modesCmdBlk[1] = (unsigned char)(((pf << 4) & 0x10) | (sp & 0x1)); - sg_put_unaligned_be16((int16_t)param_len, modesCmdBlk + 7); + modes_cdb[1] = (uint8_t)((pf ? 0x10 : 0x0) | (sp ? 0x1 : 0x0)); + if (rtd) + modes_cdb[1] |= 0x2; + sg_put_unaligned_be16((int16_t)param_len, modes_cdb + 7); if (param_len > 0xffff) { - pr2ws("mode select (10): param_len too big\n"); + pr2ws("%s: param_len too big\n", cdb_s); return -1; } if (verbose) { - pr2ws(" mode select (10) cdb: "); - for (k = 0; k < MODE_SELECT10_CMDLEN; ++k) - pr2ws("%02x ", modesCmdBlk[k]); - pr2ws("\n"); + char b[128]; + + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(modes_cdb, MODE_SELECT10_CMDLEN, false, + sizeof(b), b)); } if (verbose > 1) { - pr2ws(" mode select (10) parameter list\n"); - dStrHexErr((const char *)paramp, param_len, -1); + pr2ws(" %s parameter list\n", cdb_s); + hex2stderr((const uint8_t *)paramp, param_len, -1); } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("mode select (10): out of memory\n"); + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) return -1; - } - set_scsi_pt_cdb(ptvp, modesCmdBlk, sizeof(modesCmdBlk)); + set_scsi_pt_cdb(ptvp, modes_cdb, sizeof(modes_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); + set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "mode select (10)", res, 0, sense_b, - noisy, verbose, &sense_cat); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: @@ -518,56 +550,94 @@ return ret; } -/* MODE SENSE commands yield a response that has block descriptors followed - * by mode pages. In most cases users are interested in the first mode page. - * This function returns the (byte) offset of the start of the first mode - * page. Set mode_sense_6 to 1 for MODE SENSE (6) and 0 for MODE SENSE (10). - * Returns >= 0 is successful or -1 if failure. If there is a failure - * a message is written to err_buff if err_buff_len > 0. */ int -sg_mode_page_offset(const unsigned char * resp, int resp_len, - int mode_sense_6, char * err_buff, int err_buff_len) +sg_ll_mode_select10(int sg_fd, bool pf, bool sp, void * paramp, + int param_len, bool noisy, int verbose) { - int bd_len; - int calc_len; - int offset; + return sg_ll_mode_select10_v2(sg_fd, pf, false, sp, paramp, param_len, + noisy, verbose); +} - if ((NULL == resp) || (resp_len < 4) || - ((! mode_sense_6) && (resp_len < 8))) { - if ((err_buff_len > 0) && err_buff) - snprintf(err_buff, err_buff_len, "given response length too " - "short: %d\n", resp_len); - return -1; - } +/* MODE SENSE commands yield a response that has header then zero or more + * block descriptors followed by mode pages. In most cases users are + * interested in the first mode page. This function returns the (byte) + * offset of the start of the first mode page. Set mode_sense_6 to true for + * MODE SENSE (6) and false for MODE SENSE (10). Returns >= 0 is successful + * or -1 if failure. If there is a failure a message is written to err_buff + * if it is non-NULL and err_buff_len > 0. */ +int +sg_mode_page_offset(const uint8_t * resp, int resp_len, + bool mode_sense_6, char * err_buff, int err_buff_len) +{ + int bd_len, calc_len, offset; + bool err_buff_ok = ((err_buff_len > 0) && err_buff); + + if ((NULL == resp) || (resp_len < 4)) + goto too_short; if (mode_sense_6) { calc_len = resp[0] + 1; bd_len = resp[3]; offset = bd_len + MODE6_RESP_HDR_LEN; - } else { + } else { /* Mode sense(10) */ + if (resp_len < 8) + goto too_short; calc_len = sg_get_unaligned_be16(resp) + 2; bd_len = sg_get_unaligned_be16(resp + 6); /* LongLBA doesn't change this calculation */ offset = bd_len + MODE10_RESP_HDR_LEN; } - if ((offset + 2) > resp_len) { - if ((err_buff_len > 0) && err_buff) - snprintf(err_buff, err_buff_len, "given response length " - "too small, offset=%d given_len=%d bd_len=%d\n", - offset, resp_len, bd_len); - offset = -1; - } else if ((offset + 2) > calc_len) { - if ((err_buff_len > 0) && err_buff) + if ((offset + 2) > calc_len) { + if (err_buff_ok) snprintf(err_buff, err_buff_len, "calculated response " "length too small, offset=%d calc_len=%d bd_len=%d\n", offset, calc_len, bd_len); offset = -1; } return offset; +too_short: + if (err_buff_ok) + snprintf(err_buff, err_buff_len, "given MS(%d) response length (%d) " + "too short\n", (mode_sense_6 ? 6 : 10), resp_len); + return -1; +} + +/* MODE SENSE commands yield a response that has header then zero or more + * block descriptors followed by mode pages. This functions returns the + * length (in bytes) of those three components. Note that the return value + * can exceed resp_len in which case the MODE SENSE command should be + * re-issued with a larger response buffer. If bd_lenp is non-NULL and if + * successful the block descriptor length (in bytes) is written to *bd_lenp. + * Set mode_sense_6 to true for MODE SENSE (6) and false for MODE SENSE (10) + * responses. Returns -1 if there is an error (e.g. response too short). */ +int +sg_msense_calc_length(const uint8_t * resp, int resp_len, + bool mode_sense_6, int * bd_lenp) +{ + int calc_len; + + if (NULL == resp) + goto an_err; + if (mode_sense_6) { + if (resp_len < 4) + goto an_err; + calc_len = resp[0] + 1; + } else { + if (resp_len < 8) + goto an_err; + calc_len = sg_get_unaligned_be16(resp + 0) + 2; + } + if (bd_lenp) + *bd_lenp = mode_sense_6 ? resp[3] : sg_get_unaligned_be16(resp + 6); + return calc_len; +an_err: + if (bd_lenp) + *bd_lenp = 0; + return -1; } /* Fetches current, changeable, default and/or saveable modes pages as * indicated by pcontrol_arr for given pg_code and sub_pg_code. If - * mode6==0 then use MODE SENSE (10) else use MODE SENSE (6). If + * mode6==false then use MODE SENSE (10) else use MODE SENSE (6). If * flexible set and mode data length seems wrong then try and * fix (compensating hack for bad device or driver). pcontrol_arr * should have 4 elements for output of current, changeable, default @@ -581,47 +651,56 @@ * then stops and returns that error; otherwise continues if an error is * detected but returns the first error encountered. */ int -sg_get_mode_page_controls(int sg_fd, int mode6, int pg_code, int sub_pg_code, - int dbd, int flexible, int mx_mpage_len, +sg_get_mode_page_controls(int sg_fd, bool mode6, int pg_code, int sub_pg_code, + bool dbd, bool flexible, int mx_mpage_len, int * success_mask, void * pcontrol_arr[], - int * reported_len, int verbose) + int * reported_lenp, int verbose) { - int k, n, res, offset, calc_len, xfer_len, resp_mode6; - unsigned char buff[MODE_RESP_ARB_LEN]; + bool resp_mode6; + int k, n, res, offset, calc_len, xfer_len; + int resid = 0; + const int msense10_hlen = MODE10_RESP_HDR_LEN; + uint8_t buff[MODE_RESP_ARB_LEN]; char ebuff[EBUFF_SZ]; int first_err = 0; if (success_mask) *success_mask = 0; - if (reported_len) - *reported_len = 0; + if (reported_lenp) + *reported_lenp = 0; if (mx_mpage_len < 4) return 0; memset(ebuff, 0, sizeof(ebuff)); /* first try to find length of current page response */ - memset(buff, 0, MODE10_RESP_HDR_LEN); + memset(buff, 0, msense10_hlen); if (mode6) /* want first 8 bytes just in case */ res = sg_ll_mode_sense6(sg_fd, dbd, 0 /* pc */, pg_code, - sub_pg_code, buff, MODE10_RESP_HDR_LEN, 1, + sub_pg_code, buff, msense10_hlen, true, verbose); - else - res = sg_ll_mode_sense10(sg_fd, 0 /* llbaa */, dbd, - 0 /* pc */, pg_code, sub_pg_code, buff, - MODE10_RESP_HDR_LEN, 1, verbose); + else /* MODE SENSE(10) obviously */ + res = sg_ll_mode_sense10_v2(sg_fd, false /* llbaa */, dbd, + 0 /* pc */, pg_code, sub_pg_code, buff, + msense10_hlen, 0, &resid, true, verbose); if (0 != res) return res; n = buff[0]; - if (reported_len) - *reported_len = mode6 ? (n + 1) : (sg_get_unaligned_be16(buff) + 2); + if (reported_lenp) { + int m; + + m = sg_msense_calc_length(buff, msense10_hlen, mode6, NULL) - resid; + if (m < 0) /* Grrr, this should not happen */ + m = 0; + *reported_lenp = m; + } resp_mode6 = mode6; if (flexible) { if (mode6 && (n < 3)) { - resp_mode6 = 0; + resp_mode6 = false; if (verbose) pr2ws(">>> msense(6) but resp[0]=%d so try msense(10) " "response processing\n", n); } - if ((0 == mode6) && (n > 5)) { + if ((! mode6) && (n > 5)) { if ((n > 11) && (0 == (n % 2)) && (0 == buff[4]) && (0 == buff[5]) && (0 == buff[6])) { buff[1] = n; @@ -630,20 +709,19 @@ pr2ws(">>> msense(10) but resp[0]=%d and not msense(6) " "response so fix length\n", n); } else - resp_mode6 = 1; + resp_mode6 = true; } } if (verbose && (resp_mode6 != mode6)) pr2ws(">>> msense(%d) but resp[0]=%d so switch response " "processing\n", (mode6 ? 6 : 10), buff[0]); - calc_len = resp_mode6 ? (buff[0] + 1) : (sg_get_unaligned_be16(buff) + 2); + calc_len = sg_msense_calc_length(buff, msense10_hlen, resp_mode6, NULL); if (calc_len > MODE_RESP_ARB_LEN) calc_len = MODE_RESP_ARB_LEN; - offset = sg_mode_page_offset(buff, calc_len, resp_mode6, - ebuff, EBUFF_SZ); + offset = sg_mode_page_offset(buff, calc_len, resp_mode6, ebuff, EBUFF_SZ); if (offset < 0) { if (('\0' != ebuff[0]) && (verbose > 0)) - pr2ws("sg_get_mode_page_controls: %s\n", ebuff); + pr2ws("%s: %s\n", __func__, ebuff); return SG_LIB_CAT_MALFORMED; } xfer_len = calc_len - offset; @@ -654,17 +732,27 @@ if (NULL == pcontrol_arr[k]) continue; memset(pcontrol_arr[k], 0, mx_mpage_len); + resid = 0; if (mode6) res = sg_ll_mode_sense6(sg_fd, dbd, k /* pc */, pg_code, sub_pg_code, buff, - calc_len, 1, verbose); + calc_len, true, verbose); else - res = sg_ll_mode_sense10(sg_fd, 0 /* llbaa */, dbd, - k /* pc */, pg_code, sub_pg_code, - buff, calc_len, 1, verbose); - if (0 != res) { - if (0 == first_err) - first_err = res; + res = sg_ll_mode_sense10_v2(sg_fd, false /* llbaa */, dbd, + k /* pc */, pg_code, sub_pg_code, + buff, calc_len, 0, &resid, true, + verbose); + if (res || resid) { + if (0 == first_err) { + if (res) + first_err = res; + else { + first_err = -49; /* unexpected resid != 0 */ + if (verbose) + pr2ws("%s: unexpected resid=%d, page=0x%x, " + "pcontrol=%d\n", __func__, resid, pg_code, k); + } + } if (0 == k) break; /* if problem on current page, it won't improve */ else @@ -679,49 +767,70 @@ } /* Invokes a SCSI LOG SENSE command. Return of 0 -> success, - * various SG_LIB_CAT_* positive values or -1 -> other errors */ + * various SG_LIB_CAT_* positive values or -1 -> other errors. */ +int +sg_ll_log_sense(int sg_fd, bool ppc, bool sp, int pc, int pg_code, + int subpg_code, int paramp, uint8_t * resp, + int mx_resp_len, bool noisy, int verbose) +{ + return sg_ll_log_sense_v2(sg_fd, ppc, sp, pc, pg_code, subpg_code, + paramp, resp, mx_resp_len, 0, NULL, noisy, + verbose); +} + +/* Invokes a SCSI LOG SENSE command. Return of 0 -> success, + * various SG_LIB_CAT_* positive values or -1 -> other errors. + * Adds the ability to set the command abort timeout + * and the ability to report the residual count. If timeout_secs is zero + * or less the default command abort timeout (60 seconds) is used. + * If residp is non-NULL then the residual value is written where residp + * points. A residual value of 0 implies mx_resp_len bytes have be written + * where resp points. If the residual value equals mx_resp_len then no + * bytes have been written. */ int -sg_ll_log_sense(int sg_fd, int ppc, int sp, int pc, int pg_code, - int subpg_code, int paramp, unsigned char * resp, - int mx_resp_len, int noisy, int verbose) +sg_ll_log_sense_v2(int sg_fd, bool ppc, bool sp, int pc, int pg_code, + int subpg_code, int paramp, uint8_t * resp, + int mx_resp_len, int timeout_secs, int * residp, + bool noisy, int verbose) { - int res, ret, k, sense_cat, resid; - unsigned char logsCmdBlk[LOG_SENSE_CMDLEN] = + static const char * const cdb_s = "log sense"; + int res, ret, sense_cat, resid; + uint8_t logs_cdb[LOG_SENSE_CMDLEN] = {LOG_SENSE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (mx_resp_len > 0xffff) { pr2ws("mx_resp_len too big\n"); - return -1; + goto gen_err; } - logsCmdBlk[1] = (unsigned char)((ppc ? 2 : 0) | (sp ? 1 : 0)); - logsCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); - logsCmdBlk[3] = (unsigned char)(subpg_code & 0xff); - sg_put_unaligned_be16((int16_t)paramp, logsCmdBlk + 5); - sg_put_unaligned_be16((int16_t)mx_resp_len, logsCmdBlk + 7); + logs_cdb[1] = (uint8_t)((ppc ? 2 : 0) | (sp ? 1 : 0)); + logs_cdb[2] = (uint8_t)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); + logs_cdb[3] = (uint8_t)(subpg_code & 0xff); + sg_put_unaligned_be16((int16_t)paramp, logs_cdb + 5); + sg_put_unaligned_be16((int16_t)mx_resp_len, logs_cdb + 7); if (verbose) { - pr2ws(" log sense cdb: "); - for (k = 0; k < LOG_SENSE_CMDLEN; ++k) - pr2ws("%02x ", logsCmdBlk[k]); - pr2ws("\n"); - } + char b[128]; - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("log sense: out of memory\n"); - return -1; - } - set_scsi_pt_cdb(ptvp, logsCmdBlk, sizeof(logsCmdBlk)); + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(logs_cdb, LOG_SENSE_CMDLEN, false, + sizeof(b), b)); + } + if (timeout_secs <= 0) + timeout_secs = DEF_PT_TIMEOUT; + + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + goto gen_err; + set_scsi_pt_cdb(ptvp, logs_cdb, sizeof(logs_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, resp, mx_resp_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "log sense", res, mx_resp_len, - sense_b, noisy, verbose, &sense_cat); + res = do_scsi_pt(ptvp, sg_fd, timeout_secs, verbose); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat); resid = get_scsi_pt_resid(ptvp); - destruct_scsi_pt_obj(ptvp); + if (residp) + *residp = resid; if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: @@ -740,64 +849,67 @@ } ret = 0; } + destruct_scsi_pt_obj(ptvp); if (resid > 0) { if (resid > mx_resp_len) { - pr2ws("log sense: resid (%d) should never exceed requested " - "len=%d\n", resid, mx_resp_len); + pr2ws("%s: resid (%d) should never exceed requested len=%d\n", + cdb_s, resid, mx_resp_len); return ret ? ret : SG_LIB_CAT_MALFORMED; } /* zero unfilled section of response buffer */ - memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid); + memset((uint8_t *)resp + (mx_resp_len - resid), 0, resid); } return ret; +gen_err: + if (residp) + *residp = 0; + return -1; } /* Invokes a SCSI LOG SELECT command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int -sg_ll_log_select(int sg_fd, int pcr, int sp, int pc, int pg_code, - int subpg_code, unsigned char * paramp, int param_len, - int noisy, int verbose) +sg_ll_log_select(int sg_fd, bool pcr, bool sp, int pc, int pg_code, + int subpg_code, uint8_t * paramp, int param_len, + bool noisy, int verbose) { - int res, ret, k, sense_cat; - unsigned char logsCmdBlk[LOG_SELECT_CMDLEN] = + static const char * const cdb_s = "log select"; + int res, ret, sense_cat; + uint8_t logs_cdb[LOG_SELECT_CMDLEN] = {LOG_SELECT_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (param_len > 0xffff) { - pr2ws("log select: param_len too big\n"); + pr2ws("%s: param_len too big\n", cdb_s); return -1; } - logsCmdBlk[1] = (unsigned char)((pcr ? 2 : 0) | (sp ? 1 : 0)); - logsCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); - logsCmdBlk[3] = (unsigned char)(subpg_code & 0xff); - sg_put_unaligned_be16((int16_t)param_len, logsCmdBlk + 7); - if (verbose) { - pr2ws(" log select cdb: "); - for (k = 0; k < LOG_SELECT_CMDLEN; ++k) - pr2ws("%02x ", logsCmdBlk[k]); - pr2ws("\n"); + logs_cdb[1] = (uint8_t)((pcr ? 2 : 0) | (sp ? 1 : 0)); + logs_cdb[2] = (uint8_t)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); + logs_cdb[3] = (uint8_t)(subpg_code & 0xff); + sg_put_unaligned_be16((int16_t)param_len, logs_cdb + 7); + if (verbose) { + char b[128]; + + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(logs_cdb, LOG_SELECT_CMDLEN, false, + sizeof(b), b)); } if ((verbose > 1) && (param_len > 0)) { - pr2ws(" log select parameter list\n"); - dStrHexErr((const char *)paramp, param_len, -1); + pr2ws(" %s parameter list\n", cdb_s); + hex2stderr(paramp, param_len, -1); } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("log select: out of memory\n"); + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) return -1; - } - set_scsi_pt_cdb(ptvp, logsCmdBlk, sizeof(logsCmdBlk)); + set_scsi_pt_cdb(ptvp, logs_cdb, sizeof(logs_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "log select", res, 0, sense_b, - noisy, verbose, &sense_cat); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: @@ -823,39 +935,58 @@ * and fl(mmc) one bit field. This is the cause of the awkardly named * pc_mod__fl_num and noflush__fl arguments to this function. * */ -int -sg_ll_start_stop_unit(int sg_fd, int immed, int pc_mod__fl_num, - int power_cond, int noflush__fl, int loej, int start, - int noisy, int verbose) -{ - unsigned char ssuBlk[START_STOP_CMDLEN] = {START_STOP_CMD, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; - int k, res, ret, sense_cat; - struct sg_pt_base * ptvp; +static int +sg_ll_start_stop_unit_com(struct sg_pt_base * ptvp, int sg_fd, bool immed, + int pc_mod__fl_num, int power_cond, bool noflush__fl, + bool loej, bool start, bool noisy, int verbose) +{ + static const char * const cdb_s = "start stop unit"; + bool ptvp_given = false; + bool local_sense = true; + bool local_cdb = true; + int res, ret, sense_cat; + uint8_t ssuBlk[START_STOP_CMDLEN] = {START_STOP_CMD, 0, 0, 0, 0, 0}; + uint8_t sense_b[SENSE_BUFF_LEN]; - ssuBlk[1] = immed & 1; + if (immed) + ssuBlk[1] = 0x1; ssuBlk[3] = pc_mod__fl_num & 0xf; /* bits 2 and 3 are reserved in MMC */ - ssuBlk[4] = ((power_cond & 0xf) << 4) | (noflush__fl ? 0x4 : 0) | - (loej ? 0x2 : 0) | (start ? 0x1 : 0); + ssuBlk[4] = ((power_cond & 0xf) << 4); + if (noflush__fl) + ssuBlk[4] |= 0x4; + if (loej) + ssuBlk[4] |= 0x2; + if (start) + ssuBlk[4] |= 0x1; if (verbose) { - pr2ws(" Start stop unit command:"); - for (k = 0; k < (int)sizeof(ssuBlk); ++k) - pr2ws(" %02x", ssuBlk[k]); - pr2ws("\n"); - } + char b[128]; - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("start stop unit: out of memory\n"); - return -1; + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(ssuBlk, sizeof(ssuBlk), false, + sizeof(b), b)); + } + if (ptvp) { + ptvp_given = true; + partial_clear_scsi_pt_obj(ptvp); + if (get_scsi_pt_cdb_buf(ptvp)) + local_cdb = false; /* N.B. Ignores locally built cdb */ + else + set_scsi_pt_cdb(ptvp, ssuBlk, sizeof(ssuBlk)); + if (get_scsi_pt_sense_buf(ptvp)) + local_sense = false; + else + set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); + } else { + ptvp = construct_scsi_pt_obj_with_fd(sg_fd, verbose); + if (NULL == ptvp) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, ssuBlk, sizeof(ssuBlk)); + set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); } - set_scsi_pt_cdb(ptvp, ssuBlk, sizeof(ssuBlk)); - set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - res = do_scsi_pt(ptvp, sg_fd, START_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "start stop unit", res, 0, - sense_b, noisy, verbose, &sense_cat); + res = do_scsi_pt(ptvp, -1, START_PT_TIMEOUT, verbose); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: @@ -868,48 +999,74 @@ } } else ret = 0; - destruct_scsi_pt_obj(ptvp); + if (ptvp_given) { + if (local_sense) /* stop caller trying to access local sense */ + set_scsi_pt_sense(ptvp, NULL, 0); + if (local_cdb) + set_scsi_pt_cdb(ptvp, NULL, 0); + } else { + if (ptvp) + destruct_scsi_pt_obj(ptvp); + } return ret; } +int +sg_ll_start_stop_unit(int sg_fd, bool immed, int pc_mod__fl_num, + int power_cond, bool noflush__fl, bool loej, bool start, + bool noisy, int verbose) +{ + return sg_ll_start_stop_unit_com(NULL, sg_fd, immed, pc_mod__fl_num, + power_cond, noflush__fl, loej, start, + noisy, verbose); +} + +int +sg_ll_start_stop_unit_pt(struct sg_pt_base * ptvp, bool immed, + int pc_mod__fl_num, int power_cond, bool noflush__fl, + bool loej, bool start, bool noisy, int verbose) +{ + return sg_ll_start_stop_unit_com(ptvp, -1, immed, pc_mod__fl_num, + power_cond, noflush__fl, loej, start, + noisy, verbose); +} + /* Invokes a SCSI PREVENT ALLOW MEDIUM REMOVAL command * [was in SPC-3 but displaced from SPC-4 into SBC-3, MMC-5, SSC-3] * prevent==0 allows removal, prevent==1 prevents removal ... * Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int -sg_ll_prevent_allow(int sg_fd, int prevent, int noisy, int verbose) +sg_ll_prevent_allow(int sg_fd, int prevent, bool noisy, int verbose) { - int k, res, ret, sense_cat; - unsigned char pCmdBlk[PREVENT_ALLOW_CMDLEN] = + static const char * const cdb_s = "prevent allow medium removal"; + int res, ret, sense_cat; + uint8_t p_cdb[PREVENT_ALLOW_CMDLEN] = {PREVENT_ALLOW_CMD, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if ((prevent < 0) || (prevent > 3)) { pr2ws("prevent argument should be 0, 1, 2 or 3\n"); return -1; } - pCmdBlk[4] |= (prevent & 0x3); + p_cdb[4] |= (prevent & 0x3); if (verbose) { - pr2ws(" Prevent allow medium removal cdb: "); - for (k = 0; k < PREVENT_ALLOW_CMDLEN; ++k) - pr2ws("%02x ", pCmdBlk[k]); - pr2ws("\n"); + char b[128]; + + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(p_cdb, PREVENT_ALLOW_CMDLEN, false, + sizeof(b), b)); } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("prevent allow medium removal: out of memory\n"); + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) return -1; - } - set_scsi_pt_cdb(ptvp, pCmdBlk, sizeof(pCmdBlk)); + set_scsi_pt_cdb(ptvp, p_cdb, sizeof(p_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "prevent allow medium removal", res, 0, - sense_b, noisy, verbose, &sense_cat); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: diff -Nru sdparm-1.10/lib/sg_cmds_basic.c sdparm-1.12/lib/sg_cmds_basic.c --- sdparm-1.10/lib/sg_cmds_basic.c 2016-01-27 15:13:35.000000000 +0000 +++ sdparm-1.12/lib/sg_cmds_basic.c 2020-11-10 01:46:05.000000000 +0000 @@ -1,8 +1,10 @@ /* - * Copyright (c) 1999-2016 Douglas Gilbert. + * Copyright (c) 1999-2020 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. + * + * SPDX-License-Identifier: BSD-2-Clause */ /* @@ -17,24 +19,30 @@ #include #include #include +#include +#include #include #include -#include "sg_lib.h" -#include "sg_cmds_basic.h" -#include "sg_pt.h" -#include "sg_unaligned.h" +#define __STDC_FORMAT_MACROS 1 +#include #ifdef HAVE_CONFIG_H #include "config.h" #endif +#include "sg_lib.h" +#include "sg_cmds_basic.h" +#include "sg_pt.h" +#include "sg_unaligned.h" +#include "sg_pr2serr.h" + /* Needs to be after config.h */ #ifdef SG_LIB_LINUX #include #endif -static const char * version_str = "1.72 20160126"; +static const char * const version_str = "1.97 20200722"; #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */ @@ -62,37 +70,11 @@ return version_str; } -#ifdef __GNUC__ -static int pr2ws(const char * fmt, ...) - __attribute__ ((format (printf, 1, 2))); -#else -static int pr2ws(const char * fmt, ...); -#endif - - -static int -pr2ws(const char * fmt, ...) -{ - va_list args; - int n; - - va_start(args, fmt); - n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args); - va_end(args); - return n; -} - /* Returns file descriptor >= 0 if successful. If error in Unix returns negated errno. */ int -sg_cmds_open_device(const char * device_name, int read_only, int verbose) +sg_cmds_open_device(const char * device_name, bool read_only, int verbose) { - /* 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; - return scsi_pt_open_device(device_name, read_only, verbose); } @@ -111,14 +93,26 @@ return scsi_pt_close_device(device_fd); } +static const char * const pass_through_s = "pass-through"; + +static void +sg_cmds_resid_print(const char * leadin, bool is_din, int num_req, + int num_got) +{ + pr2ws(" %s: %s requested %d bytes (data-%s got %d " + "bytes%s\n", leadin, pass_through_s,num_req, + (is_din ? "in), got" : "out) but reported"), num_got, + (is_din ? "" : " sent")); +} + static int -sg_cmds_process_helper(const char * leadin, int mx_di_len, int resid, - const unsigned char * sbp, int slen, int noisy, - int verbose, int * o_sense_cat) -{ - int scat, got; - int n = 0; - int check_data_in = 0; +sg_cmds_process_helper(const char * leadin, int req_din_x, int act_din_x, + int req_dout_x, int act_dout_x, const uint8_t * sbp, + int slen, bool noisy, int verbose, int * o_sense_cat) +{ + int scat; + bool n = false; + bool check_data_in = false; char b[512]; scat = sg_err_category_sense(sbp, slen); @@ -126,18 +120,24 @@ case SG_LIB_CAT_NOT_READY: case SG_LIB_CAT_INVALID_OP: case SG_LIB_CAT_ILLEGAL_REQ: + case SG_LIB_LBA_OUT_OF_RANGE: case SG_LIB_CAT_ABORTED_COMMAND: case SG_LIB_CAT_COPY_ABORTED: case SG_LIB_CAT_DATA_PROTECT: case SG_LIB_CAT_PROTECTION: case SG_LIB_CAT_NO_SENSE: case SG_LIB_CAT_MISCOMPARE: - n = 0; + n = false; break; case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_MEDIUM_HARD: - ++check_data_in; - /* drop through */ + check_data_in = true; +#if defined(__GNUC__) +#if (__GNUC__ >= 7) + __attribute__((fallthrough)); + /* FALL THROUGH */ +#endif +#endif case SG_LIB_CAT_UNIT_ATTENTION: case SG_LIB_CAT_SENSE: default: @@ -150,11 +150,27 @@ sg_get_sense_str(NULL, sbp, slen, (verbose > 1), sizeof(b), b); pr2ws("%s", b); - if ((mx_di_len > 0) && (resid > 0)) { - got = mx_di_len - resid; - if ((verbose > 2) || check_data_in || (got > 0)) - pr2ws(" pass-through requested %d bytes (data-in) but " - "got %d bytes\n", mx_di_len, got); + if (req_din_x > 0) { + if (act_din_x != req_din_x) { + if ((verbose > 2) || check_data_in || (act_din_x > 0)) + sg_cmds_resid_print(leadin, true, req_din_x, act_din_x); + if (act_din_x < 0) { + if (verbose) + pr2ws(" %s: %s can't get negative bytes, say it " + "got none\n", leadin, pass_through_s); + } + } + } + if (req_dout_x > 0) { + if (act_dout_x != req_dout_x) { + if ((verbose > 1) && (act_dout_x > 0)) + sg_cmds_resid_print(leadin, false, req_dout_x, act_dout_x); + if (act_dout_x < 0) { + if (verbose) + pr2ws(" %s: %s can't send negative bytes, say it " + "sent none\n", leadin, pass_through_s); + } + } } } if (o_sense_cat) @@ -166,19 +182,19 @@ * call to the pass-through. pt_res is returned from do_scsi_pt(). If valid * sense data is found it is decoded and output to sg_warnings_strm (def: * stderr); depending on the 'noisy' and 'verbose' settings. Returns -2 for - * "sense" category (may not be fatal), -1 for failed, 0, or a positive - * number. If 'mx_di_len > 0' then asks pass-through for resid and returns - * (mx_di_len - resid); otherwise returns 0. So for data-in it should return - * the actual number of bytes received. For data-out (to device) or no data - * call with 'mx_di_len' set to 0 or less. If -2 returned then sense category - * output via 'o_sense_cat' pointer (if not NULL). Note that several sense - * categories also have data in bytes received; -2 is still returned. */ + * o_sense_cat (sense category) written which may not be fatal. Returns + * -1 for other types of failure. Returns 0, or a positive number. If data-in + * type command (or bidi) then returns actual number of bytes read + * (din_len - resid); otherwise returns 0. Note that several sense categories + * also have data in bytes received; -2 is still returned. */ int sg_cmds_process_resp(struct sg_pt_base * ptvp, const char * leadin, - int pt_res, int mx_di_len, const unsigned char * sbp, - int noisy, int verbose, int * o_sense_cat) + int pt_res, bool noisy, int verbose, int * o_sense_cat) { - int got, cat, duration, slen, resid, resp_code, sstat, transport_sense; + bool favour_sense; + int cat, slen, resp_code, sstat, req_din_x, req_dout_x; + int act_din_x, act_dout_x; + const uint8_t * sbp; char b[1024]; if (NULL == leadin) @@ -186,7 +202,7 @@ if (pt_res < 0) { #ifdef SG_LIB_LINUX if (verbose) - pr2ws("%s: pass through os error: %s\n", leadin, + pr2ws("%s: %s os error: %s\n", leadin, pass_through_s, safe_strerror(-pt_res)); if ((-ENXIO == pt_res) && o_sense_cat) { if (verbose > 2) @@ -194,25 +210,37 @@ *o_sense_cat = SG_LIB_CAT_NOT_READY; return -2; } else if (noisy && (0 == verbose)) - pr2ws("%s: pass through os error: %s\n", leadin, + pr2ws("%s: %s os error: %s\n", leadin, pass_through_s, safe_strerror(-pt_res)); #else if (noisy || verbose) - pr2ws("%s: pass through os error: %s\n", leadin, + pr2ws("%s: %s os error: %s\n", leadin, pass_through_s, safe_strerror(-pt_res)); #endif return -1; } else if (SCSI_PT_DO_BAD_PARAMS == pt_res) { - pr2ws("%s: bad pass through setup\n", leadin); + pr2ws("%s: bad %s setup\n", leadin, pass_through_s); return -1; } else if (SCSI_PT_DO_TIMEOUT == pt_res) { - pr2ws("%s: pass through timeout\n", leadin); + pr2ws("%s: %s timeout\n", leadin, pass_through_s); return -1; } - if ((verbose > 2) && ((duration = get_scsi_pt_duration_ms(ptvp)) >= 0)) - pr2ws(" duration=%d ms\n", duration); - resid = (mx_di_len > 0) ? get_scsi_pt_resid(ptvp) : 0; + if (verbose > 2) { + uint64_t duration = get_pt_duration_ns(ptvp); + + if (duration > 0) + pr2ws(" duration=%" PRIu64 " ns\n", duration); + else { + int d = get_scsi_pt_duration_ms(ptvp); + + if (d != -1) + pr2ws(" duration=%u ms\n", (uint32_t)d); + } + } + get_pt_req_lengths(ptvp, &req_din_x, &req_dout_x); + get_pt_actual_lengths(ptvp, &act_din_x, &act_dout_x); slen = get_scsi_pt_sense_len(ptvp); + sbp = get_scsi_pt_sense_buf(ptvp); switch ((cat = get_scsi_pt_result_category(ptvp))) { case SCSI_PT_RESULT_GOOD: if (sbp && (slen > 7)) { @@ -228,21 +256,31 @@ } } } - if (mx_di_len > 0) { - got = mx_di_len - resid; - if ((verbose > 1) && (resid != 0)) - pr2ws(" %s: pass-through requested %d bytes (data-in) " - "but got %d bytes\n", leadin, mx_di_len, got); - if (got >= 0) - return got; - else { - if (verbose) - pr2ws(" %s: pass-through can't get negative bytes, " - "say it got none\n", leadin); - return 0; + if (req_din_x > 0) { + if (act_din_x != req_din_x) { + if ((verbose > 1) && (act_din_x >= 0)) + sg_cmds_resid_print(leadin, true, req_din_x, act_din_x); + if (act_din_x < 0) { + if (verbose) + pr2ws(" %s: %s can't get negative bytes, say it " + "got none\n", leadin, pass_through_s); + act_din_x = 0; + } } - } else - return 0; + } + if (req_dout_x > 0) { + if (act_dout_x != req_dout_x) { + if ((verbose > 1) && (act_dout_x >= 0)) + sg_cmds_resid_print(leadin, false, req_dout_x, act_dout_x); + if (act_dout_x < 0) { + if (verbose) + pr2ws(" %s: %s can't send negative bytes, say it " + "sent none\n", leadin, pass_through_s); + act_dout_x = 0; + } + } + } + return act_din_x; case SCSI_PT_RESULT_STATUS: /* other than GOOD and CHECK CONDITION */ sstat = get_scsi_pt_status_response(ptvp); if (o_sense_cat) { @@ -275,22 +313,25 @@ } return -1; case SCSI_PT_RESULT_SENSE: - return sg_cmds_process_helper(leadin, mx_di_len, resid, sbp, slen, + return sg_cmds_process_helper(leadin, req_din_x, act_din_x, + req_dout_x, act_dout_x, sbp, slen, noisy, verbose, o_sense_cat); case SCSI_PT_RESULT_TRANSPORT_ERR: if (verbose || noisy) { get_scsi_pt_transport_err_str(ptvp, sizeof(b), b); pr2ws("%s: transport: %s\n", leadin, b); } + /* Shall we favour sense data over a transport error (given both) */ #ifdef SG_LIB_LINUX - transport_sense = (slen > 0); + favour_sense = false; /* DRIVER_SENSE is not passed through */ #else - transport_sense = ((SAM_STAT_CHECK_CONDITION == + favour_sense = ((SAM_STAT_CHECK_CONDITION == get_scsi_pt_status_response(ptvp)) && (slen > 0)); #endif - if (transport_sense) - return sg_cmds_process_helper(leadin, mx_di_len, resid, sbp, - slen, noisy, verbose, o_sense_cat); + if (favour_sense) + return sg_cmds_process_helper(leadin, req_din_x, act_din_x, + req_dout_x, act_dout_x, sbp, slen, + noisy, verbose, o_sense_cat); else return -1; case SCSI_PT_RESULT_OS_ERR: @@ -300,57 +341,99 @@ } return -1; default: - pr2ws("%s: unknown pass through result category (%d)\n", leadin, cat); + pr2ws("%s: unknown %s result category (%d)\n", leadin, pass_through_s, + cat); return -1; } } -/* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when - * successful, various SG_LIB_CAT_* positive values or -1 -> other errors */ -int -sg_ll_inquiry(int sg_fd, int cmddt, int evpd, int pg_op, void * resp, - int mx_resp_len, int noisy, int verbose) +bool +sg_cmds_is_nvme(const struct sg_pt_base * ptvp) { - int res, ret, k, sense_cat, resid; - unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; - unsigned char * up; - struct sg_pt_base * ptvp; + return pt_device_is_nvme(ptvp); +} + +static struct sg_pt_base * +create_pt_obj(const char * cname) +{ + struct sg_pt_base * ptvp = construct_scsi_pt_obj(); + if (NULL == ptvp) + pr2ws("%s: out of memory\n", cname); + return ptvp; +} + +static const char * const inquiry_s = "inquiry"; + +/* Returns 0 on success, while positive values are SG_LIB_CAT_* errors + * (e.g. SG_LIB_CAT_MALFORMED). If OS error, returns negated errno or -1. */ +static int +sg_ll_inquiry_com(struct sg_pt_base * ptvp, int sg_fd, bool cmddt, bool evpd, + int pg_op, void * resp, int mx_resp_len, int timeout_secs, + int * residp, bool noisy, int verbose) +{ + bool ptvp_given = false; + bool local_sense = true; + bool local_cdb = true; + int res, ret, sense_cat, resid; + uint8_t inq_cdb[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0}; + uint8_t sense_b[SENSE_BUFF_LEN]; + uint8_t * up; + + if (resp == NULL) { + if (verbose) + pr2ws("Got NULL `resp` pointer"); + return SG_LIB_CAT_MALFORMED; + } if (cmddt) - inqCmdBlk[1] |= 2; + inq_cdb[1] |= 0x2; if (evpd) - inqCmdBlk[1] |= 1; - inqCmdBlk[2] = (unsigned char)pg_op; - /* 16 bit allocation length (was 8) is a recent SPC-3 addition */ - sg_put_unaligned_be16((uint16_t)mx_resp_len, inqCmdBlk + 3); + inq_cdb[1] |= 0x1; + inq_cdb[2] = (uint8_t)pg_op; + /* 16 bit allocation length (was 8, increased in spc3r09, 200209) */ + sg_put_unaligned_be16((uint16_t)mx_resp_len, inq_cdb + 3); if (verbose) { - pr2ws(" inquiry cdb: "); - for (k = 0; k < INQUIRY_CMDLEN; ++k) - pr2ws("%02x ", inqCmdBlk[k]); - pr2ws("\n"); + char b[128]; + + pr2ws(" %s cdb: %s\n", inquiry_s, + sg_get_command_str(inq_cdb, INQUIRY_CMDLEN, false, sizeof(b), + b)); } if (resp && (mx_resp_len > 0)) { - up = (unsigned char *)resp; + up = (uint8_t *)resp; up[0] = 0x7f; /* defensive prefill */ if (mx_resp_len > 4) up[4] = 0; } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("inquiry: out of memory\n"); - return -1; - } - set_scsi_pt_cdb(ptvp, inqCmdBlk, sizeof(inqCmdBlk)); - set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "inquiry", res, mx_resp_len, sense_b, - noisy, verbose, &sense_cat); + if (timeout_secs <= 0) + timeout_secs = DEF_PT_TIMEOUT; + if (ptvp) { + ptvp_given = true; + partial_clear_scsi_pt_obj(ptvp); + if (get_scsi_pt_cdb_buf(ptvp)) + local_cdb = false; /* N.B. Ignores locally built cdb */ + else + set_scsi_pt_cdb(ptvp, inq_cdb, sizeof(inq_cdb)); + if (get_scsi_pt_sense_buf(ptvp)) + local_sense = false; + else + set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); + } else { + ptvp = construct_scsi_pt_obj_with_fd(sg_fd, verbose); + if (NULL == ptvp) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, inq_cdb, sizeof(inq_cdb)); + set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); + } + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len); + res = do_scsi_pt(ptvp, -1, timeout_secs, verbose); + ret = sg_cmds_process_resp(ptvp, inquiry_s, res, noisy, verbose, + &sense_cat); resid = get_scsi_pt_resid(ptvp); - destruct_scsi_pt_obj(ptvp); + if (residp) + *residp = resid; if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: @@ -363,79 +446,145 @@ } } else if (ret < 4) { if (verbose) - pr2ws("inquiry: got too few bytes (%d)\n", ret); + pr2ws("%s: got too few bytes (%d)\n", __func__, ret); ret = SG_LIB_CAT_MALFORMED; } else ret = 0; if (resid > 0) { if (resid > mx_resp_len) { - pr2ws("inquiry: resid (%d) should never exceed requested " - "len=%d\n", resid, mx_resp_len); - return ret ? ret : SG_LIB_CAT_MALFORMED; - } - /* zero unfilled section of response buffer */ - memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid); + pr2ws("%s resid (%d) should never exceed requested " + "len=%d\n", inquiry_s, resid, mx_resp_len); + if (0 == ret) + ret = SG_LIB_CAT_MALFORMED; + goto fini; + } + /* zero unfilled section of response buffer, based on resid */ + memset((uint8_t *)resp + (mx_resp_len - resid), 0, resid); + } +fini: + if (ptvp_given) { + if (local_sense) /* stop caller trying to access local sense */ + set_scsi_pt_sense(ptvp, NULL, 0); + if (local_cdb) + set_scsi_pt_cdb(ptvp, NULL, 0); + } else { + if (ptvp) + destruct_scsi_pt_obj(ptvp); } return ret; } +/* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when + * successful, various SG_LIB_CAT_* positive values, negated errno or + * -1 -> other errors. The CMDDT field is obsolete in the INQUIRY cdb. */ +int +sg_ll_inquiry(int sg_fd, bool cmddt, bool evpd, int pg_op, void * resp, + int mx_resp_len, bool noisy, int verbose) +{ + return sg_ll_inquiry_com(NULL, sg_fd, cmddt, evpd, pg_op, resp, + mx_resp_len, 0 /* timeout_sec */, NULL, noisy, + verbose); +} + +/* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when + * successful, various SG_LIB_CAT_* positive values or -1 -> other errors. + * The CMDDT field is obsolete in the INQUIRY cdb (since spc3r16 in 2003) so + * an argument to set it has been removed (use the REPORT SUPPORTED OPERATION + * CODES command instead). Adds the ability to set the command abort timeout + * and the ability to report the residual count. If timeout_secs is zero + * or less the default command abort timeout (60 seconds) is used. + * If residp is non-NULL then the residual value is written where residp + * points. A residual value of 0 implies mx_resp_len bytes have be written + * where resp points. If the residual value equals mx_resp_len then no + * bytes have been written. */ +int +sg_ll_inquiry_v2(int sg_fd, bool evpd, int pg_op, void * resp, + int mx_resp_len, int timeout_secs, int * residp, + bool noisy, int verbose) +{ + return sg_ll_inquiry_com(NULL, sg_fd, false, evpd, pg_op, resp, + mx_resp_len, timeout_secs, residp, noisy, + verbose); +} + +/* Similar to _v2 but takes a pointer to an object (derived from) sg_pt_base. + * That object is assumed to be constructed and have a device file descriptor + * associated with it. Caller is responsible for lifetime of ptp. */ +int +sg_ll_inquiry_pt(struct sg_pt_base * ptvp, bool evpd, int pg_op, void * resp, + int mx_resp_len, int timeout_secs, int * residp, bool noisy, + int verbose) +{ + return sg_ll_inquiry_com(ptvp, -1, false, evpd, pg_op, resp, mx_resp_len, + timeout_secs, residp, noisy, verbose); +} + /* Yields most of first 36 bytes of a standard INQUIRY (evpd==0) response. - * Returns 0 when successful, various SG_LIB_CAT_* positive values or - * -1 -> other errors */ + * Returns 0 when successful, various SG_LIB_CAT_* positive values, negated + * errno or -1 -> other errors */ int sg_simple_inquiry(int sg_fd, struct sg_simple_inquiry_resp * inq_data, - int noisy, int verbose) + bool noisy, int verbose) { - int res, ret, k, sense_cat; - unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; - unsigned char inq_resp[SAFE_STD_INQ_RESP_LEN]; - struct sg_pt_base * ptvp; + int ret; + uint8_t * inq_resp = NULL; + uint8_t * free_irp = NULL; if (inq_data) { memset(inq_data, 0, sizeof(* inq_data)); inq_data->peripheral_qualifier = 0x3; inq_data->peripheral_type = 0x1f; } - inqCmdBlk[4] = (unsigned char)sizeof(inq_resp); - if (verbose) { - pr2ws(" inquiry cdb: "); - for (k = 0; k < INQUIRY_CMDLEN; ++k) - pr2ws("%02x ", inqCmdBlk[k]); - pr2ws("\n"); - } - memset(inq_resp, 0, sizeof(inq_resp)); - inq_resp[0] = 0x7f; /* defensive prefill */ - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("inquiry: out of memory\n"); - return -1; + inq_resp = sg_memalign(SAFE_STD_INQ_RESP_LEN, 0, &free_irp, false); + if (NULL == inq_resp) { + pr2ws("%s: out of memory\n", __func__); + return sg_convert_errno(ENOMEM); } - set_scsi_pt_cdb(ptvp, inqCmdBlk, sizeof(inqCmdBlk)); - set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, inq_resp, sizeof(inq_resp)); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "inquiry", res, sizeof(inq_resp), - sense_b, noisy, verbose, &sense_cat); - if (-1 == ret) - ; - else if (-2 == ret) { - switch (sense_cat) { - case SG_LIB_CAT_RECOVERED: - case SG_LIB_CAT_NO_SENSE: - ret = 0; - break; - default: - ret = sense_cat; - break; - } - } else if (ret < 4) { - if (verbose) - pr2ws("inquiry: got too few bytes (%d)\n", ret); - ret = SG_LIB_CAT_MALFORMED; - } else - ret = 0; + ret = sg_ll_inquiry_com(NULL, sg_fd, false, false, 0, inq_resp, + SAFE_STD_INQ_RESP_LEN, 0, NULL, noisy, verbose); + + if (inq_data && (0 == ret)) { + inq_data->peripheral_qualifier = (inq_resp[0] >> 5) & 0x7; + inq_data->peripheral_type = inq_resp[0] & 0x1f; + inq_data->byte_1 = inq_resp[1]; + inq_data->version = inq_resp[2]; + inq_data->byte_3 = inq_resp[3]; + inq_data->byte_5 = inq_resp[5]; + inq_data->byte_6 = inq_resp[6]; + inq_data->byte_7 = inq_resp[7]; + memcpy(inq_data->vendor, inq_resp + 8, 8); + memcpy(inq_data->product, inq_resp + 16, 16); + memcpy(inq_data->revision, inq_resp + 32, 4); + } + if (free_irp) + free(free_irp); + return ret; +} + +/* Similar to sg_simple_inquiry() but takes pointer to pt object rather + * than device file descriptor. */ +int +sg_simple_inquiry_pt(struct sg_pt_base * ptvp, + struct sg_simple_inquiry_resp * inq_data, + bool noisy, int verbose) +{ + int ret; + uint8_t * inq_resp = NULL; + uint8_t * free_irp = NULL; + + if (inq_data) { + memset(inq_data, 0, sizeof(* inq_data)); + inq_data->peripheral_qualifier = 0x3; + inq_data->peripheral_type = 0x1f; + } + inq_resp = sg_memalign(SAFE_STD_INQ_RESP_LEN, 0, &free_irp, false); + if (NULL == inq_resp) { + pr2ws("%s: out of memory\n", __func__); + return sg_convert_errno(ENOMEM); + } + ret = sg_ll_inquiry_com(ptvp, -1, false, false, 0, inq_resp, + SAFE_STD_INQ_RESP_LEN, 0, NULL, noisy, verbose); if (inq_data && (0 == ret)) { inq_data->peripheral_qualifier = (inq_resp[0] >> 5) & 0x7; @@ -450,45 +599,60 @@ memcpy(inq_data->product, inq_resp + 16, 16); memcpy(inq_data->revision, inq_resp + 32, 4); } - destruct_scsi_pt_obj(ptvp); + if (free_irp) + free(free_irp); return ret; } /* Invokes a SCSI TEST UNIT READY command. + * N.B. To access the sense buffer outside this routine then one be + * provided by the caller. * 'pack_id' is just for diagnostics, safe to set to 0. * Looks for progress indicator if 'progress' non-NULL; * if found writes value [0..65535] else write -1. * Returns 0 when successful, various SG_LIB_CAT_* positive values or * -1 -> other errors */ -int -sg_ll_test_unit_ready_progress(int sg_fd, int pack_id, int * progress, - int noisy, int verbose) +static int +sg_ll_test_unit_ready_com(struct sg_pt_base * ptvp, int sg_fd, int pack_id, + int * progress, bool noisy, int verbose) { - int res, ret, k, sense_cat; - unsigned char turCmdBlk[TUR_CMDLEN] = {TUR_CMD, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; - struct sg_pt_base * ptvp; + static const char * const tur_s = "test unit ready"; + bool ptvp_given = false; + bool local_sense = true; + bool local_cdb = true; + int res, ret, sense_cat; + uint8_t tur_cdb[TUR_CMDLEN] = {TUR_CMD, 0, 0, 0, 0, 0}; + uint8_t sense_b[SENSE_BUFF_LEN]; if (verbose) { - pr2ws(" test unit ready cdb: "); - for (k = 0; k < TUR_CMDLEN; ++k) - pr2ws("%02x ", turCmdBlk[k]); - pr2ws("\n"); - } + char b[128]; - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("test unit ready: out of memory\n"); - return -1; + pr2ws(" %s cdb: %s\n", tur_s, + sg_get_command_str(tur_cdb, TUR_CMDLEN, false, sizeof(b), b)); + } + if (ptvp) { + ptvp_given = true; + partial_clear_scsi_pt_obj(ptvp); + if (get_scsi_pt_cdb_buf(ptvp)) + local_cdb = false; /* N.B. Ignores locally built cdb */ + else + set_scsi_pt_cdb(ptvp, tur_cdb, sizeof(tur_cdb)); + if (get_scsi_pt_sense_buf(ptvp)) + local_sense = false; + else + set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); + } else { + ptvp = construct_scsi_pt_obj_with_fd(sg_fd, verbose); + if (NULL == ptvp) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, tur_cdb, sizeof(tur_cdb)); + set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); } - set_scsi_pt_cdb(ptvp, turCmdBlk, sizeof(turCmdBlk)); - set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_packet_id(ptvp, pack_id); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "test unit ready", res, 0, sense_b, - noisy, verbose, &sense_cat); + res = do_scsi_pt(ptvp, -1, DEF_PT_TIMEOUT, verbose); + ret = sg_cmds_process_resp(ptvp, tur_s, res, noisy, verbose, &sense_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { if (progress) { int slen = get_scsi_pt_sense_len(ptvp); @@ -507,61 +671,103 @@ } } else ret = 0; - - destruct_scsi_pt_obj(ptvp); + if (ptvp_given) { + if (local_sense) /* stop caller trying to access local sense */ + set_scsi_pt_sense(ptvp, NULL, 0); + if (local_cdb) + set_scsi_pt_cdb(ptvp, NULL, 0); + } else { + if (ptvp) + destruct_scsi_pt_obj(ptvp); + } return ret; } +int +sg_ll_test_unit_ready_progress_pt(struct sg_pt_base * ptvp, int pack_id, + int * progress, bool noisy, int verbose) +{ + return sg_ll_test_unit_ready_com(ptvp, -1, pack_id, progress, noisy, + verbose); +} + +int +sg_ll_test_unit_ready_progress(int sg_fd, int pack_id, int * progress, + bool noisy, int verbose) +{ + return sg_ll_test_unit_ready_com(NULL, sg_fd, pack_id, progress, noisy, + verbose); +} + /* Invokes a SCSI TEST UNIT READY command. * 'pack_id' is just for diagnostics, safe to set to 0. * Returns 0 when successful, various SG_LIB_CAT_* positive values or * -1 -> other errors */ int -sg_ll_test_unit_ready(int sg_fd, int pack_id, int noisy, int verbose) +sg_ll_test_unit_ready(int sg_fd, int pack_id, bool noisy, int verbose) +{ + return sg_ll_test_unit_ready_com(NULL, sg_fd, pack_id, NULL, noisy, + verbose); +} + +int +sg_ll_test_unit_ready_pt(struct sg_pt_base * ptvp, int pack_id, bool noisy, + int verbose) { - return sg_ll_test_unit_ready_progress(sg_fd, pack_id, NULL, noisy, - verbose); + return sg_ll_test_unit_ready_com(ptvp, -1, pack_id, NULL, noisy, verbose); } /* Invokes a SCSI REQUEST SENSE command. Returns 0 when successful, various * SG_LIB_CAT_* positive values or -1 -> other errors */ -int -sg_ll_request_sense(int sg_fd, int desc, void * resp, int mx_resp_len, - int noisy, int verbose) +static int +sg_ll_request_sense_com(struct sg_pt_base * ptvp, int sg_fd, bool desc, + void * resp, int mx_resp_len, bool noisy, int verbose) { - int k, ret, res, sense_cat; - unsigned char rsCmdBlk[REQUEST_SENSE_CMDLEN] = + bool ptvp_given = false; + bool local_cdb = true; + bool local_sense = true; + int ret, res, sense_cat; + static const char * const rq_s = "request sense"; + uint8_t rs_cdb[REQUEST_SENSE_CMDLEN] = {REQUEST_SENSE_CMD, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; - struct sg_pt_base * ptvp; + uint8_t sense_b[SENSE_BUFF_LEN]; if (desc) - rsCmdBlk[1] |= 0x1; + rs_cdb[1] |= 0x1; if (mx_resp_len > 0xff) { pr2ws("mx_resp_len cannot exceed 255\n"); return -1; } - rsCmdBlk[4] = mx_resp_len & 0xff; + rs_cdb[4] = mx_resp_len & 0xff; if (verbose) { - pr2ws(" Request Sense cmd: "); - for (k = 0; k < REQUEST_SENSE_CMDLEN; ++k) - pr2ws("%02x ", rsCmdBlk[k]); - pr2ws("\n"); - } + char b[128]; - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("request sense: out of memory\n"); - return -1; - } - set_scsi_pt_cdb(ptvp, rsCmdBlk, sizeof(rsCmdBlk)); - set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "request sense", res, mx_resp_len, - sense_b, noisy, verbose, &sense_cat); + pr2ws(" %s cdb: %s\n", rq_s, + sg_get_command_str(rs_cdb, REQUEST_SENSE_CMDLEN, false, + sizeof(b), b)); + } + if (ptvp) { + ptvp_given = true; + if (get_scsi_pt_sense_buf(ptvp)) + local_cdb = false; + else + set_scsi_pt_cdb(ptvp, rs_cdb, sizeof(rs_cdb)); + if (get_scsi_pt_sense_buf(ptvp)) + local_sense = false; + else + set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); + } else { + ptvp = construct_scsi_pt_obj_with_fd(sg_fd, verbose); + if (NULL == ptvp) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, rs_cdb, sizeof(rs_cdb)); + set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); + } + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len); + res = do_scsi_pt(ptvp, -1, DEF_PT_TIMEOUT, verbose); + ret = sg_cmds_process_resp(ptvp, rq_s, res, noisy, verbose, &sense_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: @@ -575,50 +781,85 @@ } else { if ((mx_resp_len >= 8) && (ret < 8)) { if (verbose) - pr2ws(" request sense: got %d bytes in response, too " - "short\n", ret); + pr2ws(" %s: got %d bytes in response, too short\n", rq_s, + ret); ret = -1; } else ret = 0; } - destruct_scsi_pt_obj(ptvp); + if (ptvp_given) { + if (local_sense) /* stop caller accessing local sense */ + set_scsi_pt_sense(ptvp, NULL, 0); + if (local_cdb) /* stop caller accessing local sense */ + set_scsi_pt_cdb(ptvp, NULL, 0); + } else if (ptvp) + destruct_scsi_pt_obj(ptvp); return ret; } +int +sg_ll_request_sense(int sg_fd, bool desc, void * resp, int mx_resp_len, + bool noisy, int verbose) +{ + return sg_ll_request_sense_com(NULL, sg_fd, desc, resp, mx_resp_len, + noisy, verbose); +} + +int +sg_ll_request_sense_pt(struct sg_pt_base * ptvp, bool desc, void * resp, + int mx_resp_len, bool noisy, int verbose) +{ + return sg_ll_request_sense_com(ptvp, -1, desc, resp, mx_resp_len, + noisy, verbose); +} + /* Invokes a SCSI REPORT LUNS command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ -int -sg_ll_report_luns(int sg_fd, int select_report, void * resp, int mx_resp_len, - int noisy, int verbose) +static int +sg_ll_report_luns_com(struct sg_pt_base * ptvp, int sg_fd, int select_report, + void * resp, int mx_resp_len, bool noisy, int verbose) { - int k, ret, res, sense_cat; - unsigned char rlCmdBlk[REPORT_LUNS_CMDLEN] = + static const char * const report_luns_s = "report luns"; + bool ptvp_given = false; + bool local_cdb = true; + bool local_sense = true; + int ret, res, sense_cat; + uint8_t rl_cdb[REPORT_LUNS_CMDLEN] = {REPORT_LUNS_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; - struct sg_pt_base * ptvp; + uint8_t sense_b[SENSE_BUFF_LEN]; - rlCmdBlk[2] = select_report & 0xff; - sg_put_unaligned_be32((uint32_t)mx_resp_len, rlCmdBlk + 6); + rl_cdb[2] = select_report & 0xff; + sg_put_unaligned_be32((uint32_t)mx_resp_len, rl_cdb + 6); if (verbose) { - pr2ws(" report luns cdb: "); - for (k = 0; k < REPORT_LUNS_CMDLEN; ++k) - pr2ws("%02x ", rlCmdBlk[k]); - pr2ws("\n"); - } + char b[128]; - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("report luns: out of memory\n"); - return -1; + pr2ws(" %s cdb: %s\n", report_luns_s, + sg_get_command_str(rl_cdb, REPORT_LUNS_CMDLEN, false, + sizeof(b), b)); + } + if (ptvp) { + ptvp_given = true; + partial_clear_scsi_pt_obj(ptvp); + if (get_scsi_pt_cdb_buf(ptvp)) + local_cdb = false; + else + set_scsi_pt_cdb(ptvp, rl_cdb, sizeof(rl_cdb)); + if (get_scsi_pt_sense_buf(ptvp)) + local_sense = false; + else + set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); + } else { + if (NULL == ((ptvp = create_pt_obj(report_luns_s)))) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, rl_cdb, sizeof(rl_cdb)); + set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); } - set_scsi_pt_cdb(ptvp, rlCmdBlk, sizeof(rlCmdBlk)); - set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "report luns", res, mx_resp_len, - sense_b, noisy, verbose, &sense_cat); + ret = sg_cmds_process_resp(ptvp, report_luns_s, res, noisy, verbose, + &sense_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: @@ -631,6 +872,37 @@ } } else ret = 0; - destruct_scsi_pt_obj(ptvp); + if (ptvp_given) { + if (local_sense) /* stop caller accessing local sense */ + set_scsi_pt_sense(ptvp, NULL, 0); + if (local_cdb) + set_scsi_pt_cdb(ptvp, NULL, 0); + } else { + if (ptvp) + destruct_scsi_pt_obj(ptvp); + } return ret; } + +/* Invokes a SCSI REPORT LUNS command. Return of 0 -> success, + * various SG_LIB_CAT_* positive values or -1 -> other errors, + * Expects sg_fd to be >= 0 representing an open device fd. */ +int +sg_ll_report_luns(int sg_fd, int select_report, void * resp, int mx_resp_len, + bool noisy, int verbose) +{ + return sg_ll_report_luns_com(NULL, sg_fd, select_report, resp, + mx_resp_len, noisy, verbose); +} + + +/* Invokes a SCSI REPORT LUNS command. Return of 0 -> success, + * various SG_LIB_CAT_* positive values or -1 -> other errors. + * Expects a non-NULL ptvp containing an open device fd. */ +int +sg_ll_report_luns_pt(struct sg_pt_base * ptvp, int select_report, + void * resp, int mx_resp_len, bool noisy, int verbose) +{ + return sg_ll_report_luns_com(ptvp, -1, select_report, resp, + mx_resp_len, noisy, verbose); +} diff -Nru sdparm-1.10/lib/sg_cmds_extra.c sdparm-1.12/lib/sg_cmds_extra.c --- sdparm-1.10/lib/sg_cmds_extra.c 2016-01-27 15:13:35.000000000 +0000 +++ sdparm-1.12/lib/sg_cmds_extra.c 2020-07-24 18:56:21.000000000 +0000 @@ -1,28 +1,33 @@ /* - * Copyright (c) 1999-2016 Douglas Gilbert. + * Copyright (c) 1999-2020 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. + * + * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include +#include #include +#include #include #define __STDC_FORMAT_MACROS 1 #include +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "sg_lib.h" #include "sg_lib_data.h" #include "sg_cmds_basic.h" #include "sg_cmds_extra.h" #include "sg_pt.h" #include "sg_unaligned.h" - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#include "sg_pr2serr.h" #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */ @@ -43,6 +48,8 @@ #define ATA_PT_12_CMDLEN 12 #define ATA_PT_16_CMD 0x85 #define ATA_PT_16_CMDLEN 16 +#define ATA_PT_32_SA 0x1ff0 +#define ATA_PT_32_CMDLEN 32 #define FORMAT_UNIT_CMD 0x4 #define FORMAT_UNIT_CMDLEN 6 #define PERSISTENT_RESERVE_IN_CMD 0x5e @@ -79,8 +86,15 @@ #define WRITE_LONG10_CMDLEN 10 #define WRITE_BUFFER_CMD 0x3b #define WRITE_BUFFER_CMDLEN 10 +#define PRE_FETCH10_CMD 0x34 +#define PRE_FETCH10_CMDLEN 10 +#define PRE_FETCH16_CMD 0x90 +#define PRE_FETCH16_CMDLEN 16 +#define SEEK10_CMD 0x2b +#define SEEK10_CMDLEN 10 -#define GET_LBA_STATUS_SA 0x12 +#define GET_LBA_STATUS16_SA 0x12 +#define GET_LBA_STATUS32_SA 0x12 #define READ_LONG_16_SA 0x11 #define READ_MEDIA_SERIAL_NUM_SA 0x1 #define REPORT_IDENTIFYING_INFORMATION_SA 0x5 @@ -91,79 +105,150 @@ #define REPORT_REFERRALS_SA 0x13 #define EXTENDED_COPY_LID1_SA 0x0 -#ifdef __GNUC__ -static int pr2ws(const char * fmt, ...) - __attribute__ ((format (printf, 1, 2))); -#else -static int pr2ws(const char * fmt, ...); -#endif - -static int -pr2ws(const char * fmt, ...) +static struct sg_pt_base * +create_pt_obj(const char * cname) { - va_list args; - int n; - - va_start(args, fmt); - n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args); - va_end(args); - return n; + struct sg_pt_base * ptvp = construct_scsi_pt_obj(); + if (NULL == ptvp) + pr2ws("%s: out of memory\n", cname); + return ptvp; } -/* Invokes a SCSI GET LBA STATUS command (SBC). Returns 0 -> success, +/* Invokes a SCSI GET LBA STATUS(16) command (SBC). Returns 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int -sg_ll_get_lba_status(int sg_fd, uint64_t start_llba, void * resp, - int alloc_len, int noisy, int verbose) +sg_ll_get_lba_status16(int sg_fd, uint64_t start_llba, uint8_t rt, + void * resp, int alloc_len, bool noisy, int vb) { - int k, res, sense_cat, ret; - unsigned char getLbaStatCmd[SERVICE_ACTION_IN_16_CMDLEN]; - unsigned char sense_b[SENSE_BUFF_LEN]; + static const char * const cdb_s = "Get LBA status(16)"; + int res, s_cat, ret; + uint8_t getLbaStatCmd[SERVICE_ACTION_IN_16_CMDLEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; memset(getLbaStatCmd, 0, sizeof(getLbaStatCmd)); getLbaStatCmd[0] = SERVICE_ACTION_IN_16_CMD; - getLbaStatCmd[1] = GET_LBA_STATUS_SA; + getLbaStatCmd[1] = GET_LBA_STATUS16_SA; sg_put_unaligned_be64(start_llba, getLbaStatCmd + 2); sg_put_unaligned_be32((uint32_t)alloc_len, getLbaStatCmd + 10); - if (verbose) { - pr2ws(" Get LBA status cmd: "); - for (k = 0; k < SERVICE_ACTION_IN_16_CMDLEN; ++k) - pr2ws("%02x ", getLbaStatCmd[k]); - pr2ws("\n"); - } + getLbaStatCmd[14] = rt; + if (vb) { + char b[128]; - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("get LBA status: out of memory\n"); - return -1; + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(getLbaStatCmd, SERVICE_ACTION_IN_16_CMDLEN, + false, sizeof(b), b)); } + + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + return sg_convert_errno(ENOMEM); set_scsi_pt_cdb(ptvp, getLbaStatCmd, sizeof(getLbaStatCmd)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, (unsigned char *)resp, alloc_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "get LBA status", res, alloc_len, - sense_b, noisy, verbose, &sense_cat); + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, alloc_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); + if (-1 == ret) + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); + else if (-2 == ret) { + switch (s_cat) { + case SG_LIB_CAT_RECOVERED: + case SG_LIB_CAT_NO_SENSE: + ret = 0; + break; + default: + ret = s_cat; + break; + } + } else { + if ((vb > 2) && (ret > 0)) { + pr2ws(" %s: response\n", cdb_s); + if (3 == vb) { + pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); + hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), + -1); + } else { + pr2ws(":\n"); + hex2stderr((const uint8_t *)resp, ret, 0); + } + } + ret = 0; + } + destruct_scsi_pt_obj(ptvp); + return ret; +} + +int +sg_ll_get_lba_status(int sg_fd, uint64_t start_llba, void * resp, + int alloc_len, bool noisy, int vb) +{ + return sg_ll_get_lba_status16(sg_fd, start_llba, /* rt = */ 0x0, resp, + alloc_len, noisy, vb); +} + +#define GLS32_CMD_LEN 32 + +int +sg_ll_get_lba_status32(int sg_fd, uint64_t start_llba, uint32_t scan_len, + uint32_t element_id, uint8_t rt, + void * resp, int alloc_len, bool noisy, + int vb) +{ + static const char * const cdb_s = "Get LBA status(32)"; + int res, s_cat, ret; + uint8_t gls32_cmd[GLS32_CMD_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; + struct sg_pt_base * ptvp; + + memset(gls32_cmd, 0, sizeof(gls32_cmd)); + gls32_cmd[0] = SG_VARIABLE_LENGTH_CMD; + gls32_cmd[7] = GLS32_CMD_LEN - 8; + sg_put_unaligned_be16((uint16_t)GET_LBA_STATUS32_SA, gls32_cmd + 8); + gls32_cmd[10] = rt; + sg_put_unaligned_be64(start_llba, gls32_cmd + 12); + sg_put_unaligned_be32(scan_len, gls32_cmd + 20); + sg_put_unaligned_be32(element_id, gls32_cmd + 24); + sg_put_unaligned_be32((uint32_t)alloc_len, gls32_cmd + 28); + if (vb) { + char b[128]; + + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(gls32_cmd, GLS32_CMD_LEN, false, sizeof(b), + b)); + } + + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, gls32_cmd, sizeof(gls32_cmd)); + set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, alloc_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: - ret = sense_cat; + ret = s_cat; break; } } else { - if ((verbose > 2) && (ret > 0)) { - pr2ws(" get LBA status: response%s\n", - (ret > 256 ? ", first 256 bytes" : "")); - dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1); + if ((vb > 2) && (ret > 0)) { + pr2ws(" %s: response\n", cdb_s); + if (3 == vb) { + pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); + hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), + -1); + } else { + pr2ws(":\n"); + hex2stderr((const uint8_t *)resp, ret, 0); + } } ret = 0; } @@ -173,65 +258,67 @@ int sg_ll_report_tgt_prt_grp(int sg_fd, void * resp, int mx_resp_len, - int noisy, int verbose) + bool noisy, int vb) { - return sg_ll_report_tgt_prt_grp2(sg_fd, resp, mx_resp_len, 0, noisy, - verbose); + return sg_ll_report_tgt_prt_grp2(sg_fd, resp, mx_resp_len, false, noisy, + vb); } /* Invokes a SCSI REPORT TARGET PORT GROUPS command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_report_tgt_prt_grp2(int sg_fd, void * resp, int mx_resp_len, - int extended, int noisy, int verbose) + bool extended, bool noisy, int vb) { - int k, res, ret, sense_cat; - unsigned char rtpgCmdBlk[MAINTENANCE_IN_CMDLEN] = + static const char * const cdb_s = "Report target port groups"; + int res, ret, s_cat; + uint8_t rtpg_cdb[MAINTENANCE_IN_CMDLEN] = {MAINTENANCE_IN_CMD, REPORT_TGT_PRT_GRP_SA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; - if (extended) { - rtpgCmdBlk[1] |= 0x20; - } - sg_put_unaligned_be32((uint32_t)mx_resp_len, rtpgCmdBlk + 6); - if (verbose) { - pr2ws(" report target port groups cdb: "); - for (k = 0; k < MAINTENANCE_IN_CMDLEN; ++k) - pr2ws("%02x ", rtpgCmdBlk[k]); - pr2ws("\n"); - } + if (extended) + rtpg_cdb[1] |= 0x20; + sg_put_unaligned_be32((uint32_t)mx_resp_len, rtpg_cdb + 6); + if (vb) { + char b[128]; - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("report target port groups: out of memory\n"); - return -1; + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(rtpg_cdb, MAINTENANCE_IN_CMDLEN, + false, sizeof(b), b)); } - set_scsi_pt_cdb(ptvp, rtpgCmdBlk, sizeof(rtpgCmdBlk)); + + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, rtpg_cdb, sizeof(rtpg_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "report target port group", res, - mx_resp_len, sense_b, noisy, verbose, - &sense_cat); + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: - ret = sense_cat; + ret = s_cat; break; } } else { - if ((verbose > 2) && (ret > 0)) { - pr2ws(" report target port group: response%s\n", - (ret > 256 ? ", first 256 bytes" : "")); - dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1); + if ((vb > 2) && (ret > 0)) { + pr2ws(" %s: response", cdb_s); + if (3 == vb) { + pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); + hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), + -1); + } else { + pr2ws(":\n"); + hex2stderr((const uint8_t *)resp, ret, 0); + } } ret = 0; } @@ -242,49 +329,47 @@ /* Invokes a SCSI SET TARGET PORT GROUPS command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int -sg_ll_set_tgt_prt_grp(int sg_fd, void * paramp, int param_len, int noisy, - int verbose) +sg_ll_set_tgt_prt_grp(int sg_fd, void * paramp, int param_len, bool noisy, + int vb) { - int k, res, ret, sense_cat; - unsigned char stpgCmdBlk[MAINTENANCE_OUT_CMDLEN] = + static const char * const cdb_s = "Set target port groups"; + int res, ret, s_cat; + uint8_t stpg_cdb[MAINTENANCE_OUT_CMDLEN] = {MAINTENANCE_OUT_CMD, SET_TGT_PRT_GRP_SA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; - sg_put_unaligned_be32((uint32_t)param_len, stpgCmdBlk + 6); - if (verbose) { - pr2ws(" set target port groups cdb: "); - for (k = 0; k < MAINTENANCE_OUT_CMDLEN; ++k) - pr2ws("%02x ", stpgCmdBlk[k]); - pr2ws("\n"); - if ((verbose > 1) && paramp && param_len) { - pr2ws(" set target port groups parameter list:\n"); - dStrHexErr((const char *)paramp, param_len, -1); + sg_put_unaligned_be32((uint32_t)param_len, stpg_cdb + 6); + if (vb) { + char b[128]; + + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(stpg_cdb, MAINTENANCE_OUT_CMDLEN, + false, sizeof(b), b)); + if ((vb > 1) && paramp && param_len) { + pr2ws(" %s parameter list:\n", cdb_s); + hex2stderr((const uint8_t *)paramp, param_len, -1); } } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("set target port groups: out of memory\n"); - return -1; - } - set_scsi_pt_cdb(ptvp, stpgCmdBlk, sizeof(stpgCmdBlk)); + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, stpg_cdb, sizeof(stpg_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "set target port group", res, 0, - sense_b, noisy, verbose, &sense_cat); + set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, param_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: - ret = sense_cat; + ret = s_cat; break; } } else @@ -296,56 +381,60 @@ /* Invokes a SCSI REPORT REFERRALS command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int -sg_ll_report_referrals(int sg_fd, uint64_t start_llba, int one_seg, - void * resp, int mx_resp_len, int noisy, - int verbose) -{ - int k, res, ret, sense_cat; - unsigned char repRefCmdBlk[SERVICE_ACTION_IN_16_CMDLEN] = +sg_ll_report_referrals(int sg_fd, uint64_t start_llba, bool one_seg, + void * resp, int mx_resp_len, bool noisy, + int vb) +{ + static const char * const cdb_s = "Report referrals"; + int res, ret, s_cat; + uint8_t repRef_cdb[SERVICE_ACTION_IN_16_CMDLEN] = {SERVICE_ACTION_IN_16_CMD, REPORT_REFERRALS_SA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; - sg_put_unaligned_be64(start_llba, repRefCmdBlk + 2); - sg_put_unaligned_be32((uint32_t)mx_resp_len, repRefCmdBlk + 10); - repRefCmdBlk[14] = one_seg & 0x1; - if (verbose) { - pr2ws(" report referrals cdb: "); - for (k = 0; k < SERVICE_ACTION_IN_16_CMDLEN; ++k) - pr2ws("%02x ", repRefCmdBlk[k]); - pr2ws("\n"); - } + sg_put_unaligned_be64(start_llba, repRef_cdb + 2); + sg_put_unaligned_be32((uint32_t)mx_resp_len, repRef_cdb + 10); + if (one_seg) + repRef_cdb[14] = 0x1; + if (vb) { + char b[128]; - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("report target port groups: out of memory\n"); - return -1; + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(repRef_cdb, SERVICE_ACTION_IN_16_CMDLEN, + false, sizeof(b), b)); } - set_scsi_pt_cdb(ptvp, repRefCmdBlk, sizeof(repRefCmdBlk)); + + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, repRef_cdb, sizeof(repRef_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "report referrals", res, - mx_resp_len, sense_b, noisy, verbose, - &sense_cat); + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: - ret = sense_cat; + ret = s_cat; break; } } else { - if ((verbose > 2) && (ret > 0)) { - pr2ws(" report referrals: response%s\n", - (ret > 256 ? ", first 256 bytes" : "")); - dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1); + if ((vb > 2) && (ret > 0)) { + pr2ws(" %s: response", cdb_s); + if (3 == vb) { + pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); + hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), + -1); + } else { + pr2ws(":\n"); + hex2stderr((const uint8_t *)resp, ret, 0); + } } ret = 0; } @@ -354,183 +443,308 @@ } /* Invokes a SCSI SEND DIAGNOSTIC command. Foreground, extended self tests can - * take a long time, if so set long_duration flag in which case the timout + * take a long time, if so set long_duration flag in which case the timeout * is set to 7200 seconds; if the value of long_duration is > 7200 then that * value is taken as the timeout value in seconds. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int -sg_ll_send_diag(int sg_fd, int sf_code, int pf_bit, int sf_bit, int devofl_bit, - int unitofl_bit, int long_duration, void * paramp, - int param_len, int noisy, int verbose) -{ - int k, res, ret, sense_cat, tmout; - unsigned char senddiagCmdBlk[SEND_DIAGNOSTIC_CMDLEN] = +sg_ll_send_diag_com(struct sg_pt_base * ptvp, int sg_fd, int st_code, + bool pf_bit, bool st_bit, bool devofl_bit, + bool unitofl_bit, int long_duration, void * paramp, + int param_len, bool noisy, int vb) +{ + static const char * const cdb_s = "Send diagnostic"; + bool ptvp_given = false; + bool local_sense = true; + bool local_cdb = true; + int res, ret, s_cat, tmout; + uint8_t senddiag_cdb[SEND_DIAGNOSTIC_CMDLEN] = {SEND_DIAGNOSTIC_CMD, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; - struct sg_pt_base * ptvp; + uint8_t sense_b[SENSE_BUFF_LEN]; - senddiagCmdBlk[1] = (unsigned char)((sf_code << 5) | (pf_bit << 4) | - (sf_bit << 2) | (devofl_bit << 1) | unitofl_bit); - sg_put_unaligned_be16((uint16_t)param_len, senddiagCmdBlk + 3); - - if (verbose) { - pr2ws(" Send diagnostic cmd: "); - for (k = 0; k < SEND_DIAGNOSTIC_CMDLEN; ++k) - pr2ws("%02x ", senddiagCmdBlk[k]); - pr2ws("\n"); - if ((verbose > 1) && paramp && param_len) { - pr2ws(" Send diagnostic parameter list:\n"); - dStrHexErr((const char *)paramp, param_len, -1); - } - } + senddiag_cdb[1] = (uint8_t)(st_code << 5); + if (pf_bit) + senddiag_cdb[1] |= 0x10; + if (st_bit) + senddiag_cdb[1] |= 0x4; + if (devofl_bit) + senddiag_cdb[1] |= 0x2; + if (unitofl_bit) + senddiag_cdb[1] |= 0x1; + sg_put_unaligned_be16((uint16_t)param_len, senddiag_cdb + 3); if (long_duration > LONG_PT_TIMEOUT) tmout = long_duration; else tmout = long_duration ? LONG_PT_TIMEOUT : DEF_PT_TIMEOUT; - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("send diagnostic: out of memory\n"); - return -1; + if (vb) { + char b[128]; + + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(senddiag_cdb, SEND_DIAGNOSTIC_CMDLEN, + false, sizeof(b), b)); + if (vb > 1) { + if (paramp && param_len) { + pr2ws(" %s parameter list:\n", cdb_s); + hex2stderr((const uint8_t *)paramp, param_len, -1); + } + pr2ws(" %s timeout: %d seconds\n", cdb_s, tmout); + } } - set_scsi_pt_cdb(ptvp, senddiagCmdBlk, sizeof(senddiagCmdBlk)); - set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); - res = do_scsi_pt(ptvp, sg_fd, tmout, verbose); - ret = sg_cmds_process_resp(ptvp, "send diagnostic", res, 0, sense_b, - noisy, verbose, &sense_cat); + if (ptvp) { + ptvp_given = true; + partial_clear_scsi_pt_obj(ptvp); + if (get_scsi_pt_cdb_buf(ptvp)) + local_cdb = false; /* N.B. Ignores locally built cdb */ + else + set_scsi_pt_cdb(ptvp, senddiag_cdb, sizeof(senddiag_cdb)); + if (get_scsi_pt_sense_buf(ptvp)) + local_sense = false; + else + set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); + } else { + ptvp = construct_scsi_pt_obj_with_fd(sg_fd, vb); + if (NULL == ptvp) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, senddiag_cdb, sizeof(senddiag_cdb)); + set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); + } + set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, param_len); + res = do_scsi_pt(ptvp, -1, tmout, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: - ret = sense_cat; + ret = s_cat; break; } } else ret = 0; - destruct_scsi_pt_obj(ptvp); + if (ptvp_given) { + if (local_sense) /* stop caller trying to access local sense */ + set_scsi_pt_sense(ptvp, NULL, 0); + if (local_cdb) + set_scsi_pt_cdb(ptvp, NULL, 0); + } else { + if (ptvp) + destruct_scsi_pt_obj(ptvp); + } return ret; } -/* Invokes a SCSI RECEIVE DIAGNOSTIC RESULTS command. Return of 0 -> success, - * various SG_LIB_CAT_* positive values or -1 -> other errors */ int -sg_ll_receive_diag(int sg_fd, int pcv, int pg_code, void * resp, - int mx_resp_len, int noisy, int verbose) +sg_ll_send_diag_pt(struct sg_pt_base * ptvp, int st_code, bool pf_bit, + bool st_bit, bool devofl_bit, bool unitofl_bit, + int long_duration, void * paramp, int param_len, + bool noisy, int vb) { - int k, res, ret, sense_cat; - unsigned char rcvdiagCmdBlk[RECEIVE_DIAGNOSTICS_CMDLEN] = - {RECEIVE_DIAGNOSTICS_CMD, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; - struct sg_pt_base * ptvp; + return sg_ll_send_diag_com(ptvp, -1, st_code, pf_bit, st_bit, devofl_bit, + unitofl_bit, long_duration, paramp, + param_len, noisy, vb); +} - rcvdiagCmdBlk[1] = (unsigned char)(pcv ? 0x1 : 0); - rcvdiagCmdBlk[2] = (unsigned char)(pg_code); - sg_put_unaligned_be16((uint16_t)mx_resp_len, rcvdiagCmdBlk + 3); - - if (verbose) { - pr2ws(" Receive diagnostic results cmd: "); - for (k = 0; k < RECEIVE_DIAGNOSTICS_CMDLEN; ++k) - pr2ws("%02x ", rcvdiagCmdBlk[k]); - pr2ws("\n"); - } +int +sg_ll_send_diag(int sg_fd, int st_code, bool pf_bit, bool st_bit, + bool devofl_bit, bool unitofl_bit, int long_duration, + void * paramp, int param_len, bool noisy, int vb) +{ + return sg_ll_send_diag_com(NULL, sg_fd, st_code, pf_bit, st_bit, + devofl_bit, unitofl_bit, long_duration, paramp, + param_len, noisy, vb); +} - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("receive diagnostic results: out of memory\n"); - return -1; +/* Invokes a SCSI RECEIVE DIAGNOSTIC RESULTS command. Return of 0 -> success, + * various SG_LIB_CAT_* positive values or -1 -> other errors */ +static int +sg_ll_receive_diag_com(struct sg_pt_base * ptvp, int sg_fd, bool pcv, + int pg_code, void * resp, int mx_resp_len, + int timeout_secs, int * residp, bool noisy, int vb) +{ + bool ptvp_given = false; + bool local_sense = true; + bool local_cdb = true; + int resid = 0; + int res, ret, s_cat; + static const char * const cdb_s = "Receive diagnostic results"; + uint8_t rcvdiag_cdb[RECEIVE_DIAGNOSTICS_CMDLEN] = + {RECEIVE_DIAGNOSTICS_CMD, 0, 0, 0, 0, 0}; + uint8_t sense_b[SENSE_BUFF_LEN]; + + if (pcv) + rcvdiag_cdb[1] = 0x1; + rcvdiag_cdb[2] = (uint8_t)(pg_code); + sg_put_unaligned_be16((uint16_t)mx_resp_len, rcvdiag_cdb + 3); + + if (vb) { + char b[128]; + + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(rcvdiag_cdb, RECEIVE_DIAGNOSTICS_CMDLEN, + false, sizeof(b), b)); + } + if (timeout_secs <= 0) + timeout_secs = DEF_PT_TIMEOUT; + + if (ptvp) { + ptvp_given = true; + partial_clear_scsi_pt_obj(ptvp); + if (get_scsi_pt_cdb_buf(ptvp)) + local_cdb = false; /* N.B. Ignores locally built cdb */ + else + set_scsi_pt_cdb(ptvp, rcvdiag_cdb, sizeof(rcvdiag_cdb)); + if (get_scsi_pt_sense_buf(ptvp)) + local_sense = false; + else + set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); + } else { + ptvp = construct_scsi_pt_obj_with_fd(sg_fd, vb); + if (NULL == ptvp) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, rcvdiag_cdb, sizeof(rcvdiag_cdb)); + set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); } - set_scsi_pt_cdb(ptvp, rcvdiagCmdBlk, sizeof(rcvdiagCmdBlk)); - set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "receive diagnostic results", res, - mx_resp_len, sense_b, noisy, verbose, - &sense_cat); + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len); + res = do_scsi_pt(ptvp, -1, timeout_secs, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); + resid = get_scsi_pt_resid(ptvp); + if (residp) + *residp = resid; if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: - ret = sense_cat; + ret = s_cat; break; } } else { - if ((verbose > 2) && (ret > 0)) { - pr2ws(" receive diagnostic results: response%s\n", - (ret > 256 ? ", first 256 bytes" : "")); - dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1); + if ((vb > 2) && (ret > 0)) { + pr2ws(" %s: response", cdb_s); + if (3 == vb) { + pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); + hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), + -1); + } else { + pr2ws(":\n"); + hex2stderr((const uint8_t *)resp, ret, 0); + } } ret = 0; } - destruct_scsi_pt_obj(ptvp); + + if (ptvp_given) { + if (local_sense) /* stop caller trying to access local sense */ + set_scsi_pt_sense(ptvp, NULL, 0); + if (local_cdb) + set_scsi_pt_cdb(ptvp, NULL, 0); + } else { + if (ptvp) + destruct_scsi_pt_obj(ptvp); + } return ret; } +int +sg_ll_receive_diag_pt(struct sg_pt_base * ptvp, bool pcv, int pg_code, + void * resp, int mx_resp_len, int timeout_secs, + int * residp, bool noisy, int vb) +{ + return sg_ll_receive_diag_com(ptvp, -1, pcv, pg_code, resp, mx_resp_len, + timeout_secs, residp, noisy, vb); +} + +/* Invokes a SCSI RECEIVE DIAGNOSTIC RESULTS command. Return of 0 -> success, + * various SG_LIB_CAT_* positive values or -1 -> other errors */ +int +sg_ll_receive_diag(int sg_fd, bool pcv, int pg_code, void * resp, + int mx_resp_len, bool noisy, int vb) +{ + return sg_ll_receive_diag_com(NULL, sg_fd, pcv, pg_code, resp, + mx_resp_len, 0, NULL, noisy, vb); +} + +int +sg_ll_receive_diag_v2(int sg_fd, bool pcv, int pg_code, void * resp, + int mx_resp_len, int timeout_secs, int * residp, + bool noisy, int vb) +{ + return sg_ll_receive_diag_com(NULL, sg_fd, pcv, pg_code, resp, + mx_resp_len, timeout_secs, residp, noisy, + vb); +} + /* Invokes a SCSI READ DEFECT DATA (10) command (SBC). Return of 0 -> success * various SG_LIB_CAT_* positive values or -1 -> other errors */ int -sg_ll_read_defect10(int sg_fd, int req_plist, int req_glist, int dl_format, - void * resp, int mx_resp_len, int noisy, int verbose) +sg_ll_read_defect10(int sg_fd, bool req_plist, bool req_glist, int dl_format, + void * resp, int mx_resp_len, bool noisy, int vb) { - int res, k, ret, sense_cat; - unsigned char rdefCmdBlk[READ_DEFECT10_CMDLEN] = + static const char * const cdb_s = "Read defect(10)"; + int res, ret, s_cat; + uint8_t rdef_cdb[READ_DEFECT10_CMDLEN] = {READ_DEFECT10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; - rdefCmdBlk[2] = (unsigned char)(((req_plist << 4) & 0x10) | - ((req_glist << 3) & 0x8) | (dl_format & 0x7)); - sg_put_unaligned_be16((uint16_t)mx_resp_len, rdefCmdBlk + 7); + rdef_cdb[2] = (dl_format & 0x7); + if (req_plist) + rdef_cdb[2] |= 0x10; + if (req_glist) + rdef_cdb[2] |= 0x8; + sg_put_unaligned_be16((uint16_t)mx_resp_len, rdef_cdb + 7); if (mx_resp_len > 0xffff) { pr2ws("mx_resp_len too big\n"); return -1; } - if (verbose) { - pr2ws(" read defect (10) cdb: "); - for (k = 0; k < READ_DEFECT10_CMDLEN; ++k) - pr2ws("%02x ", rdefCmdBlk[k]); - pr2ws("\n"); - } + if (vb) { + char b[128]; - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("read defect (10): out of memory\n"); - return -1; + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(rdef_cdb, READ_DEFECT10_CMDLEN, + false, sizeof(b), b)); } - set_scsi_pt_cdb(ptvp, rdefCmdBlk, sizeof(rdefCmdBlk)); + + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, rdef_cdb, sizeof(rdef_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "read defect (10)", res, mx_resp_len, - sense_b, noisy, verbose, &sense_cat); + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: - ret = sense_cat; + ret = s_cat; break; } } else { - if ((verbose > 2) && (ret > 0)) { - pr2ws(" read defect (10): response%s\n", - (ret > 256 ? ", first 256 bytes" : "")); - dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1); + if ((vb > 2) && (ret > 0)) { + pr2ws(" %s: response\n", cdb_s); + if (3 == vb) { + pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); + hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), + -1); + } else { + pr2ws(":\n"); + hex2stderr((const uint8_t *)resp, ret, 0); + } } ret = 0; } @@ -542,52 +756,55 @@ * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_read_media_serial_num(int sg_fd, void * resp, int mx_resp_len, - int noisy, int verbose) + bool noisy, int vb) { - int k, res, ret, sense_cat; - unsigned char rmsnCmdBlk[SERVICE_ACTION_IN_12_CMDLEN] = + static const char * const cdb_s = "Read media serial number"; + int res, ret, s_cat; + uint8_t rmsn_cdb[SERVICE_ACTION_IN_12_CMDLEN] = {SERVICE_ACTION_IN_12_CMD, READ_MEDIA_SERIAL_NUM_SA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; - sg_put_unaligned_be32((uint32_t)mx_resp_len, rmsnCmdBlk + 6); - if (verbose) { - pr2ws(" read media serial number cdb: "); - for (k = 0; k < SERVICE_ACTION_IN_12_CMDLEN; ++k) - pr2ws("%02x ", rmsnCmdBlk[k]); - pr2ws("\n"); - } + sg_put_unaligned_be32((uint32_t)mx_resp_len, rmsn_cdb + 6); + if (vb) { + char b[128]; - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("read media serial number: out of memory\n"); - return -1; + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(rmsn_cdb, SERVICE_ACTION_IN_12_CMDLEN, + false, sizeof(b), b)); } - set_scsi_pt_cdb(ptvp, rmsnCmdBlk, sizeof(rmsnCmdBlk)); + + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, rmsn_cdb, sizeof(rmsn_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "read media serial number", res, - mx_resp_len, sense_b, noisy, verbose, - &sense_cat); + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: - ret = sense_cat; + ret = s_cat; break; } } else { - if ((verbose > 2) && (ret > 0)) { - pr2ws(" read media serial number: response%s\n", - (ret > 256 ? ", first 256 bytes" : "")); - dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1); + if ((vb > 2) && (ret > 0)) { + pr2ws(" %s: response", cdb_s); + if (3 == vb) { + pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); + hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), + -1); + } else { + pr2ws(":\n"); + hex2stderr((const uint8_t *)resp, ret, 0); + } } ret = 0; } @@ -600,54 +817,57 @@ * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_report_id_info(int sg_fd, int itype, void * resp, int max_resp_len, - int noisy, int verbose) + bool noisy, int vb) { - int k, res, ret, sense_cat; - unsigned char riiCmdBlk[MAINTENANCE_IN_CMDLEN] = {MAINTENANCE_IN_CMD, + static const char * const cdb_s = "Report identifying information"; + int res, ret, s_cat; + uint8_t rii_cdb[MAINTENANCE_IN_CMDLEN] = {MAINTENANCE_IN_CMD, REPORT_IDENTIFYING_INFORMATION_SA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; - sg_put_unaligned_be32((uint32_t)max_resp_len, riiCmdBlk + 6); - riiCmdBlk[10] |= (itype << 1) & 0xfe; + sg_put_unaligned_be32((uint32_t)max_resp_len, rii_cdb + 6); + rii_cdb[10] |= (itype << 1) & 0xfe; - if (verbose) { - pr2ws(" Report identifying information cdb: "); - for (k = 0; k < MAINTENANCE_IN_CMDLEN; ++k) - pr2ws("%02x ", riiCmdBlk[k]); - pr2ws("\n"); - } + if (vb) { + char b[128]; - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("report identifying information: out of memory\n"); - return -1; + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(rii_cdb, MAINTENANCE_IN_CMDLEN, + false, sizeof(b), b)); } - set_scsi_pt_cdb(ptvp, riiCmdBlk, sizeof(riiCmdBlk)); + + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, rii_cdb, sizeof(rii_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, (unsigned char *)resp, max_resp_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "report identifying information", res, - max_resp_len, sense_b, noisy, verbose, - &sense_cat); + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, max_resp_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: - ret = sense_cat; + ret = s_cat; break; } } else { - if ((verbose > 2) && (ret > 0)) { - pr2ws(" report identifying information: response%s\n", - (ret > 256 ? ", first 256 bytes" : "")); - dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1); + if ((vb > 2) && (ret > 0)) { + pr2ws(" %s: response", cdb_s); + if (3 == vb) { + pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); + hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), + -1); + } else { + pr2ws(":\n"); + hex2stderr((const uint8_t *)resp, ret, 0); + } } ret = 0; } @@ -660,49 +880,47 @@ * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_set_id_info(int sg_fd, int itype, void * paramp, int param_len, - int noisy, int verbose) + bool noisy, int vb) { - int k, res, ret, sense_cat; - unsigned char siiCmdBlk[MAINTENANCE_OUT_CMDLEN] = {MAINTENANCE_OUT_CMD, + static const char * const cdb_s = "Set identifying information"; + int res, ret, s_cat; + uint8_t sii_cdb[MAINTENANCE_OUT_CMDLEN] = {MAINTENANCE_OUT_CMD, SET_IDENTIFYING_INFORMATION_SA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; - sg_put_unaligned_be32((uint32_t)param_len, siiCmdBlk + 6); - siiCmdBlk[10] |= (itype << 1) & 0xfe; - if (verbose) { - pr2ws(" Set identifying information cdb: "); - for (k = 0; k < MAINTENANCE_OUT_CMDLEN; ++k) - pr2ws("%02x ", siiCmdBlk[k]); - pr2ws("\n"); - if ((verbose > 1) && paramp && param_len) { - pr2ws(" Set identifying information parameter list:\n"); - dStrHexErr((const char *)paramp, param_len, -1); + sg_put_unaligned_be32((uint32_t)param_len, sii_cdb + 6); + sii_cdb[10] |= (itype << 1) & 0xfe; + if (vb) { + char b[128]; + + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(sii_cdb, MAINTENANCE_OUT_CMDLEN, + false, sizeof(b), b)); + if ((vb > 1) && paramp && param_len) { + pr2ws(" %s parameter list:\n", cdb_s); + hex2stderr((const uint8_t *)paramp, param_len, -1); } } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("Set identifying information: out of memory\n"); - return -1; - } - set_scsi_pt_cdb(ptvp, siiCmdBlk, sizeof(siiCmdBlk)); + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, sii_cdb, sizeof(sii_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "set identifying information", res, 0, - sense_b, noisy, verbose, &sense_cat); + set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, param_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: - ret = sense_cat; + ret = s_cat; break; } } else @@ -715,74 +933,87 @@ /* Invokes a FORMAT UNIT (SBC-3) command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int -sg_ll_format_unit(int sg_fd, int fmtpinfo, int longlist, int fmtdata, - int cmplst, int dlist_format, int timeout_secs, - void * paramp, int param_len, int noisy, int verbose) -{ - return sg_ll_format_unit2(sg_fd, fmtpinfo, longlist, fmtdata, cmplst, - dlist_format, 0, timeout_secs, paramp, - param_len, noisy, verbose); +sg_ll_format_unit(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata, + bool cmplst, int dlist_format, int timeout_secs, + void * paramp, int param_len, bool noisy, int vb) +{ + return sg_ll_format_unit_v2(sg_fd, fmtpinfo, longlist, fmtdata, cmplst, + dlist_format, 0, timeout_secs, paramp, + param_len, noisy, vb); +} + +/* Invokes a FORMAT UNIT (SBC-3) command. Return of 0 -> success, + * various SG_LIB_CAT_* positive values or -1 -> other errors */ +int +sg_ll_format_unit2(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata, + bool cmplst, int dlist_format, int ffmt, int timeout_secs, + void * paramp, int param_len, bool noisy, int vb) +{ + return sg_ll_format_unit_v2(sg_fd, fmtpinfo, longlist, fmtdata, cmplst, + dlist_format, ffmt, timeout_secs, paramp, + param_len, noisy, vb); } /* Invokes a FORMAT UNIT (SBC-4) command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors. * FFMT field added in sbc4r10 [20160121] */ int -sg_ll_format_unit2(int sg_fd, int fmtpinfo, int longlist, int fmtdata, - int cmplst, int dlist_format, int ffmt, int timeout_secs, - void * paramp, int param_len, int noisy, int verbose) -{ - int k, res, ret, sense_cat, tmout; - unsigned char fuCmdBlk[FORMAT_UNIT_CMDLEN] = +sg_ll_format_unit_v2(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata, + bool cmplst, int dlist_format, int ffmt, + int timeout_secs, void * paramp, int param_len, + bool noisy, int vb) +{ + static const char * const cdb_s = "Format unit"; + int res, ret, s_cat, tmout; + uint8_t fu_cdb[FORMAT_UNIT_CMDLEN] = {FORMAT_UNIT_CMD, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (fmtpinfo) - fuCmdBlk[1] |= (fmtpinfo << 6); + fu_cdb[1] |= (fmtpinfo << 6); if (longlist) - fuCmdBlk[1] |= 0x20; + fu_cdb[1] |= 0x20; if (fmtdata) - fuCmdBlk[1] |= 0x10; + fu_cdb[1] |= 0x10; if (cmplst) - fuCmdBlk[1] |= 0x8; + fu_cdb[1] |= 0x8; if (dlist_format) - fuCmdBlk[1] |= (dlist_format & 0x7); + fu_cdb[1] |= (dlist_format & 0x7); if (ffmt) - fuCmdBlk[4] |= (ffmt & 0x3); + fu_cdb[4] |= (ffmt & 0x3); tmout = (timeout_secs > 0) ? timeout_secs : DEF_PT_TIMEOUT; - if (verbose) { - pr2ws(" format unit cdb: "); - for (k = 0; k < 6; ++k) - pr2ws("%02x ", fuCmdBlk[k]); - pr2ws("\n"); - } - if ((verbose > 1) && (param_len > 0)) { - pr2ws(" format parameter list:\n"); - dStrHexErr((const char *)paramp, param_len, -1); - } + if (vb) { + char b[128]; - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("format unit: out of memory\n"); - return -1; + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(fu_cdb, 6, false, sizeof(b), b)); + if (vb > 1) { + if (param_len > 0) { + pr2ws(" %s parameter list:\n", cdb_s); + hex2stderr((const uint8_t *)paramp, param_len, -1); + } + pr2ws(" %s timeout: %d seconds\n", cdb_s, tmout); + } } - set_scsi_pt_cdb(ptvp, fuCmdBlk, sizeof(fuCmdBlk)); + + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, fu_cdb, sizeof(fu_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); - res = do_scsi_pt(ptvp, sg_fd, tmout, verbose); - ret = sg_cmds_process_resp(ptvp, "format unit", res, 0, sense_b, - noisy, verbose, &sense_cat); + set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, param_len); + res = do_scsi_pt(ptvp, sg_fd, tmout, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: - ret = sense_cat; + ret = s_cat; break; } } else @@ -795,49 +1026,49 @@ /* Invokes a SCSI REASSIGN BLOCKS command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int -sg_ll_reassign_blocks(int sg_fd, int longlba, int longlist, void * paramp, - int param_len, int noisy, int verbose) +sg_ll_reassign_blocks(int sg_fd, bool longlba, bool longlist, void * paramp, + int param_len, bool noisy, int vb) { - int res, k, ret, sense_cat; - unsigned char reassCmdBlk[REASSIGN_BLKS_CMDLEN] = + static const char * const cdb_s = "Reassign blocks"; + int res, ret, s_cat; + uint8_t reass_cdb[REASSIGN_BLKS_CMDLEN] = {REASSIGN_BLKS_CMD, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; - reassCmdBlk[1] = (unsigned char)(((longlba << 1) & 0x2) | - (longlist & 0x1)); - if (verbose) { - pr2ws(" reassign blocks cdb: "); - for (k = 0; k < REASSIGN_BLKS_CMDLEN; ++k) - pr2ws("%02x ", reassCmdBlk[k]); - pr2ws("\n"); - } - if (verbose > 1) { - pr2ws(" reassign blocks parameter list\n"); - dStrHexErr((const char *)paramp, param_len, -1); - } + if (longlba) + reass_cdb[1] = 0x2; + if (longlist) + reass_cdb[1] |= 0x1; + if (vb) { + char b[128]; - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("reassign blocks: out of memory\n"); - return -1; + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(reass_cdb, REASSIGN_BLKS_CMDLEN, false, + sizeof(b), b)); + } + if (vb > 1) { + pr2ws(" %s parameter list\n", cdb_s); + hex2stderr((const uint8_t *)paramp, param_len, -1); } - set_scsi_pt_cdb(ptvp, reassCmdBlk, sizeof(reassCmdBlk)); + + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, reass_cdb, sizeof(reass_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "reassign blocks", res, 0, sense_b, - noisy, verbose, &sense_cat); + set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, param_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: - ret = sense_cat; + ret = s_cat; break; } } else @@ -852,54 +1083,57 @@ * -1 -> other errors */ int sg_ll_persistent_reserve_in(int sg_fd, int rq_servact, void * resp, - int mx_resp_len, int noisy, int verbose) + int mx_resp_len, bool noisy, int vb) { - int res, k, ret, sense_cat; - unsigned char prinCmdBlk[PERSISTENT_RESERVE_IN_CMDLEN] = + static const char * const cdb_s = "Persistent reservation in"; + int res, ret, s_cat; + uint8_t prin_cdb[PERSISTENT_RESERVE_IN_CMDLEN] = {PERSISTENT_RESERVE_IN_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (rq_servact > 0) - prinCmdBlk[1] = (unsigned char)(rq_servact & 0x1f); - sg_put_unaligned_be16((uint16_t)mx_resp_len, prinCmdBlk + 7); + prin_cdb[1] = (uint8_t)(rq_servact & 0x1f); + sg_put_unaligned_be16((uint16_t)mx_resp_len, prin_cdb + 7); - if (verbose) { - pr2ws(" Persistent Reservation In cmd: "); - for (k = 0; k < PERSISTENT_RESERVE_IN_CMDLEN; ++k) - pr2ws("%02x ", prinCmdBlk[k]); - pr2ws("\n"); - } + if (vb) { + char b[128]; - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("persistent reservation in: out of memory\n"); - return -1; + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(prin_cdb, PERSISTENT_RESERVE_IN_CMDLEN, + false, sizeof(b), b)); } - set_scsi_pt_cdb(ptvp, prinCmdBlk, sizeof(prinCmdBlk)); + + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, prin_cdb, sizeof(prin_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "persistent reservation in", res, - mx_resp_len, sense_b, noisy, verbose, - &sense_cat); + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: - ret = sense_cat; + ret = s_cat; break; } } else { - if ((verbose > 2) && (ret > 0)) { - pr2ws(" persistent reserve in: response%s\n", - (ret > 256 ? ", first 256 bytes" : "")); - dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1); + if ((vb > 2) && (ret > 0)) { + pr2ws(" %s: response", cdb_s); + if (3 == vb) { + pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); + hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), + -1); + } else { + pr2ws(":\n"); + hex2stderr((const uint8_t *)resp, ret, 0); + } } ret = 0; } @@ -913,51 +1147,49 @@ int sg_ll_persistent_reserve_out(int sg_fd, int rq_servact, int rq_scope, unsigned int rq_type, void * paramp, - int param_len, int noisy, int verbose) + int param_len, bool noisy, int vb) { - int res, k, ret, sense_cat; - unsigned char proutCmdBlk[PERSISTENT_RESERVE_OUT_CMDLEN] = + static const char * const cdb_s = "Persistent reservation out"; + int res, ret, s_cat; + uint8_t prout_cdb[PERSISTENT_RESERVE_OUT_CMDLEN] = {PERSISTENT_RESERVE_OUT_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (rq_servact > 0) - proutCmdBlk[1] = (unsigned char)(rq_servact & 0x1f); - proutCmdBlk[2] = (((rq_scope & 0xf) << 4) | (rq_type & 0xf)); - sg_put_unaligned_be16((uint16_t)param_len, proutCmdBlk + 7); - - if (verbose) { - pr2ws(" Persistent Reservation Out cmd: "); - for (k = 0; k < PERSISTENT_RESERVE_OUT_CMDLEN; ++k) - pr2ws("%02x ", proutCmdBlk[k]); - pr2ws("\n"); - if (verbose > 1) { - pr2ws(" Persistent Reservation Out parameters:\n"); - dStrHexErr((const char *)paramp, param_len, 0); + prout_cdb[1] = (uint8_t)(rq_servact & 0x1f); + prout_cdb[2] = (((rq_scope & 0xf) << 4) | (rq_type & 0xf)); + sg_put_unaligned_be16((uint16_t)param_len, prout_cdb + 7); + + if (vb) { + char b[128]; + + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(prout_cdb, PERSISTENT_RESERVE_OUT_CMDLEN, + false, sizeof(b), b)); + if (vb > 1) { + pr2ws(" %s parameters:\n", cdb_s); + hex2stderr((const uint8_t *)paramp, param_len, 0); } } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("persistent reserve out: out of memory\n"); - return -1; - } - set_scsi_pt_cdb(ptvp, proutCmdBlk, sizeof(proutCmdBlk)); + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, prout_cdb, sizeof(prout_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "persistent reserve out", res, 0, - sense_b, noisy, verbose, &sense_cat); + set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, param_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: - ret = sense_cat; + ret = s_cat; break; } } else @@ -967,75 +1199,74 @@ return ret; } -static int -has_blk_ili(unsigned char * sensep, int sb_len) +static bool +has_blk_ili(uint8_t * sensep, int sb_len) { int resp_code; - const unsigned char * cup; + const uint8_t * cup; if (sb_len < 8) - return 0; + return false; resp_code = (0x7f & sensep[0]); if (resp_code >= 0x72) { /* descriptor format */ /* find block command descriptor */ if ((cup = sg_scsi_sense_desc_find(sensep, sb_len, 0x5))) - return ((cup[3] & 0x20) ? 1 : 0); + return (cup[3] & 0x20); } else /* fixed */ - return ((sensep[2] & 0x20) ? 1 : 0); - return 0; + return (sensep[2] & 0x20); + return false; } /* Invokes a SCSI READ LONG (10) command (SBC). Note that 'xfer_len' * is in bytes. Returns 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int -sg_ll_read_long10(int sg_fd, int pblock, int correct, unsigned int lba, - void * resp, int xfer_len, int * offsetp, int noisy, - int verbose) -{ - int k, res, sense_cat, ret; - unsigned char readLongCmdBlk[READ_LONG10_CMDLEN]; - unsigned char sense_b[SENSE_BUFF_LEN]; +sg_ll_read_long10(int sg_fd, bool pblock, bool correct, unsigned int lba, + void * resp, int xfer_len, int * offsetp, bool noisy, + int vb) +{ + static const char * const cdb_s = "read long(10)"; + int res, s_cat, ret; + uint8_t readLong_cdb[READ_LONG10_CMDLEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; - memset(readLongCmdBlk, 0, READ_LONG10_CMDLEN); - readLongCmdBlk[0] = READ_LONG10_CMD; + memset(readLong_cdb, 0, READ_LONG10_CMDLEN); + readLong_cdb[0] = READ_LONG10_CMD; if (pblock) - readLongCmdBlk[1] |= 0x4; + readLong_cdb[1] |= 0x4; if (correct) - readLongCmdBlk[1] |= 0x2; + readLong_cdb[1] |= 0x2; - sg_put_unaligned_be32((uint32_t)lba, readLongCmdBlk + 2); - sg_put_unaligned_be16((uint16_t)xfer_len, readLongCmdBlk + 7); - if (verbose) { - pr2ws(" Read Long (10) cmd: "); - for (k = 0; k < READ_LONG10_CMDLEN; ++k) - pr2ws("%02x ", readLongCmdBlk[k]); - pr2ws("\n"); - } + sg_put_unaligned_be32((uint32_t)lba, readLong_cdb + 2); + sg_put_unaligned_be16((uint16_t)xfer_len, readLong_cdb + 7); + if (vb) { + char b[128]; - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("read long (10): out of memory\n"); - return -1; + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(readLong_cdb, READ_LONG10_CMDLEN, + false, sizeof(b), b)); } - set_scsi_pt_cdb(ptvp, readLongCmdBlk, sizeof(readLongCmdBlk)); + + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, readLong_cdb, sizeof(readLong_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, (unsigned char *)resp, xfer_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "read long (10)", res, xfer_len, - sense_b, noisy, verbose, &sense_cat); + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, xfer_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; case SG_LIB_CAT_ILLEGAL_REQ: { - int valid, slen, ili; + bool valid, ili; + int slen; uint64_t ull = 0; slen = get_scsi_pt_sense_len(ptvp); @@ -1046,7 +1277,7 @@ *offsetp = (int)(int64_t)ull; ret = SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO; } else { - if (verbose > 1) + if (vb > 1) pr2ws(" info field: 0x%" PRIx64 ", valid: %d, " "ili: %d\n", ull, valid, ili); ret = SG_LIB_CAT_ILLEGAL_REQ; @@ -1054,14 +1285,20 @@ } break; default: - ret = sense_cat; + ret = s_cat; break; } } else { - if ((verbose > 2) && (ret > 0)) { - pr2ws(" read long(10): response%s\n", - (ret > 256 ? ", first 256 bytes" : "")); - dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1); + if ((vb > 2) && (ret > 0)) { + pr2ws(" %s: response", cdb_s); + if (3 == vb) { + pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); + hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), + -1); + } else { + pr2ws(":\n"); + hex2stderr((const uint8_t *)resp, ret, 0); + } } ret = 0; } @@ -1073,54 +1310,53 @@ * is in bytes. Returns 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int -sg_ll_read_long16(int sg_fd, int pblock, int correct, uint64_t llba, - void * resp, int xfer_len, int * offsetp, int noisy, - int verbose) -{ - int k, res, sense_cat, ret; - unsigned char readLongCmdBlk[SERVICE_ACTION_IN_16_CMDLEN]; - unsigned char sense_b[SENSE_BUFF_LEN]; +sg_ll_read_long16(int sg_fd, bool pblock, bool correct, uint64_t llba, + void * resp, int xfer_len, int * offsetp, bool noisy, + int vb) +{ + static const char * const cdb_s = "read long(16)"; + int res, s_cat, ret; + uint8_t readLong_cdb[SERVICE_ACTION_IN_16_CMDLEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; - memset(readLongCmdBlk, 0, sizeof(readLongCmdBlk)); - readLongCmdBlk[0] = SERVICE_ACTION_IN_16_CMD; - readLongCmdBlk[1] = READ_LONG_16_SA; + memset(readLong_cdb, 0, sizeof(readLong_cdb)); + readLong_cdb[0] = SERVICE_ACTION_IN_16_CMD; + readLong_cdb[1] = READ_LONG_16_SA; if (pblock) - readLongCmdBlk[14] |= 0x2; + readLong_cdb[14] |= 0x2; if (correct) - readLongCmdBlk[14] |= 0x1; + readLong_cdb[14] |= 0x1; - sg_put_unaligned_be64(llba, readLongCmdBlk + 2); - sg_put_unaligned_be16((uint16_t)xfer_len, readLongCmdBlk + 12); - if (verbose) { - pr2ws(" Read Long (16) cmd: "); - for (k = 0; k < SERVICE_ACTION_IN_16_CMDLEN; ++k) - pr2ws("%02x ", readLongCmdBlk[k]); - pr2ws("\n"); - } + sg_put_unaligned_be64(llba, readLong_cdb + 2); + sg_put_unaligned_be16((uint16_t)xfer_len, readLong_cdb + 12); + if (vb) { + char b[128]; - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("read long (16): out of memory\n"); - return -1; + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(readLong_cdb, SERVICE_ACTION_IN_16_CMDLEN, + false, sizeof(b), b)); } - set_scsi_pt_cdb(ptvp, readLongCmdBlk, sizeof(readLongCmdBlk)); + + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, readLong_cdb, sizeof(readLong_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, (unsigned char *)resp, xfer_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "read long (16)", res, xfer_len, - sense_b, noisy, verbose, &sense_cat); + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, xfer_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; case SG_LIB_CAT_ILLEGAL_REQ: { - int valid, slen, ili; + bool valid, ili; + int slen; uint64_t ull = 0; slen = get_scsi_pt_sense_len(ptvp); @@ -1131,22 +1367,28 @@ *offsetp = (int)(int64_t)ull; ret = SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO; } else { - if (verbose > 1) + if (vb > 1) pr2ws(" info field: 0x%" PRIx64 ", valid: %d, " - "ili: %d\n", ull, valid, ili); + "ili: %d\n", ull, (int)valid, (int)ili); ret = SG_LIB_CAT_ILLEGAL_REQ; } } break; default: - ret = sense_cat; + ret = s_cat; break; } } else { - if ((verbose > 2) && (ret > 0)) { - pr2ws(" read long(16): response%s\n", - (ret > 256 ? ", first 256 bytes" : "")); - dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1); + if ((vb > 2) && (ret > 0)) { + pr2ws(" %s: response", cdb_s); + if (3 == vb) { + pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); + hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), + -1); + } else { + pr2ws(":\n"); + hex2stderr((const uint8_t *)resp, ret, 0); + } } ret = 0; } @@ -1158,48 +1400,46 @@ * is in bytes. Returns 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int -sg_ll_write_long10(int sg_fd, int cor_dis, int wr_uncor, int pblock, +sg_ll_write_long10(int sg_fd, bool cor_dis, bool wr_uncor, bool pblock, unsigned int lba, void * data_out, int xfer_len, - int * offsetp, int noisy, int verbose) + int * offsetp, bool noisy, int vb) { - int k, res, sense_cat, ret; - unsigned char writeLongCmdBlk[WRITE_LONG10_CMDLEN]; - unsigned char sense_b[SENSE_BUFF_LEN]; + static const char * const cdb_s = "write long(10)"; + int res, s_cat, ret; + uint8_t writeLong_cdb[WRITE_LONG10_CMDLEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; - memset(writeLongCmdBlk, 0, WRITE_LONG10_CMDLEN); - writeLongCmdBlk[0] = WRITE_LONG10_CMD; + memset(writeLong_cdb, 0, WRITE_LONG10_CMDLEN); + writeLong_cdb[0] = WRITE_LONG10_CMD; if (cor_dis) - writeLongCmdBlk[1] |= 0x80; + writeLong_cdb[1] |= 0x80; if (wr_uncor) - writeLongCmdBlk[1] |= 0x40; + writeLong_cdb[1] |= 0x40; if (pblock) - writeLongCmdBlk[1] |= 0x20; + writeLong_cdb[1] |= 0x20; - sg_put_unaligned_be32((uint32_t)lba, writeLongCmdBlk + 2); - sg_put_unaligned_be16((uint16_t)xfer_len, writeLongCmdBlk + 7); - if (verbose) { - pr2ws(" Write Long (10) cmd: "); - for (k = 0; k < (int)sizeof(writeLongCmdBlk); ++k) - pr2ws("%02x ", writeLongCmdBlk[k]); - pr2ws("\n"); - } + sg_put_unaligned_be32((uint32_t)lba, writeLong_cdb + 2); + sg_put_unaligned_be16((uint16_t)xfer_len, writeLong_cdb + 7); + if (vb) { + char b[128]; - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("write long(10): out of memory\n"); - return -1; + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(writeLong_cdb, (int)sizeof(writeLong_cdb), + false, sizeof(b), b)); } - set_scsi_pt_cdb(ptvp, writeLongCmdBlk, sizeof(writeLongCmdBlk)); + + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, writeLong_cdb, sizeof(writeLong_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_out(ptvp, (unsigned char *)data_out, xfer_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "write long(10)", res, 0, sense_b, - noisy, verbose, &sense_cat); + set_scsi_pt_data_out(ptvp, (uint8_t *)data_out, xfer_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; @@ -1217,15 +1457,15 @@ *offsetp = (int)(int64_t)ull; ret = SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO; } else { - if (verbose > 1) + if (vb > 1) pr2ws(" info field: 0x%" PRIx64 ", valid: %d, " - "ili: %d\n", ull, valid, ili); + "ili: %d\n", ull, (int)valid, (int)ili); ret = SG_LIB_CAT_ILLEGAL_REQ; } } break; default: - ret = sense_cat; + ret = s_cat; break; } } else @@ -1239,56 +1479,55 @@ * is in bytes. Returns 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int -sg_ll_write_long16(int sg_fd, int cor_dis, int wr_uncor, int pblock, +sg_ll_write_long16(int sg_fd, bool cor_dis, bool wr_uncor, bool pblock, uint64_t llba, void * data_out, int xfer_len, - int * offsetp, int noisy, int verbose) + int * offsetp, bool noisy, int vb) { - int k, res, sense_cat, ret; - unsigned char writeLongCmdBlk[SERVICE_ACTION_OUT_16_CMDLEN]; - unsigned char sense_b[SENSE_BUFF_LEN]; + static const char * const cdb_s = "write long(16)"; + int res, s_cat, ret; + uint8_t writeLong_cdb[SERVICE_ACTION_OUT_16_CMDLEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; - memset(writeLongCmdBlk, 0, sizeof(writeLongCmdBlk)); - writeLongCmdBlk[0] = SERVICE_ACTION_OUT_16_CMD; - writeLongCmdBlk[1] = WRITE_LONG_16_SA; + memset(writeLong_cdb, 0, sizeof(writeLong_cdb)); + writeLong_cdb[0] = SERVICE_ACTION_OUT_16_CMD; + writeLong_cdb[1] = WRITE_LONG_16_SA; if (cor_dis) - writeLongCmdBlk[1] |= 0x80; + writeLong_cdb[1] |= 0x80; if (wr_uncor) - writeLongCmdBlk[1] |= 0x40; + writeLong_cdb[1] |= 0x40; if (pblock) - writeLongCmdBlk[1] |= 0x20; + writeLong_cdb[1] |= 0x20; - sg_put_unaligned_be64(llba, writeLongCmdBlk + 2); - sg_put_unaligned_be16((uint16_t)xfer_len, writeLongCmdBlk + 12); - if (verbose) { - pr2ws(" Write Long (16) cmd: "); - for (k = 0; k < SERVICE_ACTION_OUT_16_CMDLEN; ++k) - pr2ws("%02x ", writeLongCmdBlk[k]); - pr2ws("\n"); - } + sg_put_unaligned_be64(llba, writeLong_cdb + 2); + sg_put_unaligned_be16((uint16_t)xfer_len, writeLong_cdb + 12); + if (vb) { + char b[128]; - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("write long(16): out of memory\n"); - return -1; + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(writeLong_cdb, SERVICE_ACTION_OUT_16_CMDLEN, + false, sizeof(b), b)); } - set_scsi_pt_cdb(ptvp, writeLongCmdBlk, sizeof(writeLongCmdBlk)); + + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, writeLong_cdb, sizeof(writeLong_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_out(ptvp, (unsigned char *)data_out, xfer_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "write long(16)", res, 0, sense_b, - noisy, verbose, &sense_cat); + set_scsi_pt_data_out(ptvp, (uint8_t *)data_out, xfer_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; case SG_LIB_CAT_ILLEGAL_REQ: { - int valid, slen, ili; + bool valid, ili; + int slen; uint64_t ull = 0; slen = get_scsi_pt_sense_len(ptvp); @@ -1299,15 +1538,15 @@ *offsetp = (int)(int64_t)ull; ret = SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO; } else { - if (verbose > 1) + if (vb > 1) pr2ws(" info field: 0x%" PRIx64 ", valid: %d, " - "ili: %d\n", ull, valid, ili); + "ili: %d\n", ull, (int)valid, (int)ili); ret = SG_LIB_CAT_ILLEGAL_REQ; } } break; default: - ret = sense_cat; + ret = s_cat; break; } } else @@ -1319,60 +1558,59 @@ /* Invokes a SCSI VERIFY (10) command (SBC and MMC). * Note that 'veri_len' is in blocks while 'data_out_len' is in bytes. - * Returns of 0 -> success, - * various SG_LIB_CAT_* positive values or -1 -> other errors */ + * Returns of 0 -> success, * various SG_LIB_CAT_* positive values or + * -1 -> other errors */ int -sg_ll_verify10(int sg_fd, int vrprotect, int dpo, int bytchk, +sg_ll_verify10(int sg_fd, int vrprotect, bool dpo, int bytchk, unsigned int lba, int veri_len, void * data_out, - int data_out_len, unsigned int * infop, int noisy, - int verbose) + int data_out_len, unsigned int * infop, bool noisy, + int vb) { - int k, res, ret, sense_cat, slen; - unsigned char vCmdBlk[VERIFY10_CMDLEN] = + static const char * const cdb_s = "verify(10)"; + int k, res, ret, s_cat, slen; + uint8_t v_cdb[VERIFY10_CMDLEN] = {VERIFY10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; /* N.B. BYTCHK field expanded to 2 bits sbc3r34 */ - vCmdBlk[1] = ((vrprotect & 0x7) << 5) | ((dpo & 0x1) << 4) | - ((bytchk & 0x3) << 1) ; - sg_put_unaligned_be32((uint32_t)lba, vCmdBlk + 2); - sg_put_unaligned_be16((uint16_t)veri_len, vCmdBlk + 7); - if (verbose > 1) { - pr2ws(" Verify(10) cdb: "); - for (k = 0; k < VERIFY10_CMDLEN; ++k) - pr2ws("%02x ", vCmdBlk[k]); - pr2ws("\n"); - if ((verbose > 3) && bytchk && data_out && (data_out_len > 0)) { + v_cdb[1] = (((vrprotect & 0x7) << 5) | ((bytchk & 0x3) << 1)) ; + if (dpo) + v_cdb[1] |= 0x10; + sg_put_unaligned_be32((uint32_t)lba, v_cdb + 2); + sg_put_unaligned_be16((uint16_t)veri_len, v_cdb + 7); + if (vb > 1) { + char b[128]; + + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(v_cdb, VERIFY10_CMDLEN, + false, sizeof(b), b)); + if ((vb > 3) && bytchk && data_out && (data_out_len > 0)) { k = data_out_len > 4104 ? 4104 : data_out_len; pr2ws(" data_out buffer%s\n", (data_out_len > 4104 ? ", first 4104 bytes" : "")); - dStrHexErr((const char *)data_out, k, verbose < 5); + hex2stderr((const uint8_t *)data_out, k, vb < 5); } } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("verify (10): out of memory\n"); - return -1; - } - set_scsi_pt_cdb(ptvp, vCmdBlk, sizeof(vCmdBlk)); + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, v_cdb, sizeof(v_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); if (data_out_len > 0) - set_scsi_pt_data_out(ptvp, (unsigned char *)data_out, data_out_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "verify (10)", res, 0, sense_b, - noisy, verbose, &sense_cat); + set_scsi_pt_data_out(ptvp, (uint8_t *)data_out, data_out_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; case SG_LIB_CAT_MEDIUM_HARD: { - int valid; + bool valid; uint64_t ull = 0; slen = get_scsi_pt_sense_len(ptvp); @@ -1386,7 +1624,7 @@ } break; default: - ret = sense_cat; + ret = s_cat; break; } } else @@ -1401,57 +1639,56 @@ * Returns of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int -sg_ll_verify16(int sg_fd, int vrprotect, int dpo, int bytchk, uint64_t llba, +sg_ll_verify16(int sg_fd, int vrprotect, bool dpo, int bytchk, uint64_t llba, int veri_len, int group_num, void * data_out, - int data_out_len, uint64_t * infop, int noisy, int verbose) + int data_out_len, uint64_t * infop, bool noisy, int vb) { - int k, res, ret, sense_cat, slen; - unsigned char vCmdBlk[VERIFY16_CMDLEN] = + static const char * const cdb_s = "verify(16)"; + int k, res, ret, s_cat, slen; + uint8_t v_cdb[VERIFY16_CMDLEN] = {VERIFY16_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; /* N.B. BYTCHK field expanded to 2 bits sbc3r34 */ - vCmdBlk[1] = ((vrprotect & 0x7) << 5) | ((dpo & 0x1) << 4) | - ((bytchk & 0x3) << 1) ; - sg_put_unaligned_be64(llba, vCmdBlk + 2); - sg_put_unaligned_be32((uint32_t)veri_len, vCmdBlk + 10); - vCmdBlk[14] = group_num & 0x1f; - if (verbose > 1) { - pr2ws(" Verify(16) cdb: "); - for (k = 0; k < VERIFY16_CMDLEN; ++k) - pr2ws("%02x ", vCmdBlk[k]); - pr2ws("\n"); - if ((verbose > 3) && bytchk && data_out && (data_out_len > 0)) { + v_cdb[1] = (((vrprotect & 0x7) << 5) | ((bytchk & 0x3) << 1)) ; + if (dpo) + v_cdb[1] |= 0x10; + sg_put_unaligned_be64(llba, v_cdb + 2); + sg_put_unaligned_be32((uint32_t)veri_len, v_cdb + 10); + v_cdb[14] = group_num & 0x1f; + if (vb > 1) { + char b[128]; + + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(v_cdb, VERIFY16_CMDLEN, + false, sizeof(b), b)); + if ((vb > 3) && bytchk && data_out && (data_out_len > 0)) { k = data_out_len > 4104 ? 4104 : data_out_len; pr2ws(" data_out buffer%s\n", (data_out_len > 4104 ? ", first 4104 bytes" : "")); - dStrHexErr((const char *)data_out, k, verbose < 5); + hex2stderr((const uint8_t *)data_out, k, vb < 5); } } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("verify (16): out of memory\n"); - return -1; - } - set_scsi_pt_cdb(ptvp, vCmdBlk, sizeof(vCmdBlk)); + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + return sg_convert_errno(ENOMEM); + set_scsi_pt_cdb(ptvp, v_cdb, sizeof(v_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); if (data_out_len > 0) - set_scsi_pt_data_out(ptvp, (unsigned char *)data_out, data_out_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "verify (16)", res, 0, sense_b, - noisy, verbose, &sense_cat); + set_scsi_pt_data_out(ptvp, (uint8_t *)data_out, data_out_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; case SG_LIB_CAT_MEDIUM_HARD: { - int valid; + bool valid; uint64_t ull = 0; slen = get_scsi_pt_sense_len(ptvp); @@ -1465,7 +1702,7 @@ } break; default: - ret = sense_cat; + ret = s_cat; break; } } else @@ -1475,55 +1712,74 @@ return ret; } -/* Invokes a ATA PASS-THROUGH (12 or 16) SCSI command (SAT). If cdb_len - * is 12 then a ATA PASS-THROUGH (12) command is called. If cdb_len is 16 - * then a ATA PASS-THROUGH (16) command is called. If cdb_len is any other - * value -1 is returned. After copying from cdbp to an internal buffer, - * the first byte (i.e. offset 0) is set to 0xa1 if cdb_len is 12; or is - * set to 0x85 if cdb_len is 16. The last byte (offset 11 or offset 15) is - * set to 0x0 in the internal buffer. If timeout_secs <= 0 then the timeout - * is set to 60 seconds. For data in or out transfers set dinp or doutp, - * and dlen to the number of bytes to transfer. If dlen is zero then no data - * transfer is assumed. If sense buffer obtained then it is written to - * sensep, else sensep[0] is set to 0x0. If ATA return descriptor is obtained - * then written to ata_return_dp, else ata_return_dp[0] is set to 0x0. Either - * sensep or ata_return_dp (or both) may be NULL pointers. Returns SCSI - * status value (>= 0) or -1 if other error. Users are expected to check the - * sense buffer themselves. If available the data in resid is written to - * residp. Note in SAT-2 and later, fixed format sense data may be placed in - * *sensep in which case sensep[0]==0x70 . +/* Invokes a ATA PASS-THROUGH (12, 16 or 32) SCSI command (SAT). This is + * selected by the cdb_len argument that can take values of 12, 16 or 32 + * only (else -1 is returned). The byte at offset 0 (and bytes 0 to 9 + * inclusive for ATA PT(32)) pointed to be cdbp are ignored and apart from + * the control byte, the rest is copied into an internal cdb which is then + * sent to the device. The control byte is byte 11 for ATA PT(12), byte 15 + * for ATA PT(16) and byte 1 for ATA PT(32). If timeout_secs <= 0 then the + * timeout is set to 60 seconds. For data in or out transfers set dinp or + * doutp, and dlen to the number of bytes to transfer. If dlen is zero then + * no data transfer is assumed. If sense buffer obtained then it is written + * to sensep, else sensep[0] is set to 0x0. If ATA return descriptor is + * obtained then written to ata_return_dp, else ata_return_dp[0] is set to + * 0x0. Either sensep or ata_return_dp (or both) may be NULL pointers. + * Returns SCSI status value (>= 0) or -1 if other error. Users are + * expected to check the sense buffer themselves. If available the data in + * resid is written to residp. Note in SAT-2 and later, fixed format sense + * data may be placed in *sensep in which case sensep[0]==0x70, prior to + * SAT-2 descriptor sense format was required (i.e. sensep[0]==0x72). */ int -sg_ll_ata_pt(int sg_fd, const unsigned char * cdbp, int cdb_len, +sg_ll_ata_pt(int sg_fd, const uint8_t * cdbp, int cdb_len, int timeout_secs, void * dinp, void * doutp, int dlen, - unsigned char * sensep, int max_sense_len, - unsigned char * ata_return_dp, int max_ata_return_len, - int * residp, int verbose) + uint8_t * sensep, int max_sense_len, + uint8_t * ata_return_dp, int max_ata_return_len, + int * residp, int vb) { int k, res, slen, duration; - unsigned char aptCmdBlk[ATA_PT_16_CMDLEN] = - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; - unsigned char * sp; - const unsigned char * ucp; + int ret = -1; + uint8_t apt_cdb[ATA_PT_32_CMDLEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; + uint8_t * sp; + const uint8_t * bp; struct sg_pt_base * ptvp; const char * cnamep; char b[256]; - int ret = -1; + memset(apt_cdb, 0, sizeof(apt_cdb)); b[0] = '\0'; - cnamep = (12 == cdb_len) ? - "ATA pass through (12)" : "ATA pass through (16)"; - if ((NULL == cdbp) || ((12 != cdb_len) && (16 != cdb_len))) { - if (verbose) { - if (NULL == cdbp) - pr2ws("%s NULL cdb pointer\n", cnamep); - else - pr2ws("cdb_len must be 12 or 16\n"); - } + switch (cdb_len) { + case 12: + cnamep = "ATA pass-through(12)"; + apt_cdb[0] = ATA_PT_12_CMD; + memcpy(apt_cdb + 1, cdbp + 1, 10); + /* control byte at cdb[11] left at zero */ + break; + case 16: + cnamep = "ATA pass-through(16)"; + apt_cdb[0] = ATA_PT_16_CMD; + memcpy(apt_cdb + 1, cdbp + 1, 14); + /* control byte at cdb[15] left at zero */ + break; + case 32: + cnamep = "ATA pass-through(32)"; + apt_cdb[0] = SG_VARIABLE_LENGTH_CMD; + /* control byte at cdb[1] left at zero */ + apt_cdb[7] = 0x18; /* length starting at next byte */ + sg_put_unaligned_be16(ATA_PT_32_SA, apt_cdb + 8); + memcpy(apt_cdb + 10, cdbp + 10, 32 - 10); + break; + default: + pr2ws("cdb_len must be 12, 16 or 32\n"); + return -1; + } + if (NULL == cdbp) { + if (vb) + pr2ws("%s NULL cdb pointer\n", cnamep); return -1; } - aptCmdBlk[0] = (12 == cdb_len) ? ATA_PT_12_CMD : ATA_PT_16_CMD; if (sensep && (max_sense_len >= (int)sizeof(sense_b))) { sp = sensep; slen = max_sense_len; @@ -1531,46 +1787,44 @@ sp = sense_b; slen = sizeof(sense_b); } - if (12 == cdb_len) - memcpy(aptCmdBlk + 1, cdbp + 1, ((cdb_len > 11) ? 10 : (cdb_len - 1))); - else - memcpy(aptCmdBlk + 1, cdbp + 1, ((cdb_len > 15) ? 14 : (cdb_len - 1))); - if (verbose) { - pr2ws(" %s cdb: ", cnamep); - for (k = 0; k < cdb_len; ++k) - pr2ws("%02x ", aptCmdBlk[k]); - pr2ws("\n"); + if (vb) { + if (cdb_len < 32) { + char d[128]; + + pr2ws(" %s cdb: %s\n", cnamep, + sg_get_command_str(apt_cdb, cdb_len, false, sizeof(d), d)); + } else { + pr2ws(" %s cdb:\n", cnamep); + hex2stderr(apt_cdb, cdb_len, -1); + } } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("%s: out of memory\n", cnamep); + if (NULL == ((ptvp = create_pt_obj(cnamep)))) return -1; - } - set_scsi_pt_cdb(ptvp, aptCmdBlk, cdb_len); + set_scsi_pt_cdb(ptvp, apt_cdb, cdb_len); set_scsi_pt_sense(ptvp, sp, slen); if (dlen > 0) { if (dinp) - set_scsi_pt_data_in(ptvp, (unsigned char *)dinp, dlen); + set_scsi_pt_data_in(ptvp, (uint8_t *)dinp, dlen); else if (doutp) - set_scsi_pt_data_out(ptvp, (unsigned char *)doutp, dlen); + set_scsi_pt_data_out(ptvp, (uint8_t *)doutp, dlen); } res = do_scsi_pt(ptvp, sg_fd, ((timeout_secs > 0) ? timeout_secs : DEF_PT_TIMEOUT), - verbose); + vb); if (SCSI_PT_DO_BAD_PARAMS == res) { - if (verbose) + if (vb) pr2ws("%s: bad parameters\n", cnamep); goto out; } else if (SCSI_PT_DO_TIMEOUT == res) { - if (verbose) + if (vb) pr2ws("%s: timeout\n", cnamep); goto out; } else if (res > 2) { - if (verbose) + if (vb) pr2ws("%s: do_scsi_pt: errno=%d\n", cnamep, -res); } - if ((verbose > 2) && ((duration = get_scsi_pt_duration_ms(ptvp)) >= 0)) + if ((vb > 2) && ((duration = get_scsi_pt_duration_ms(ptvp)) >= 0)) pr2ws(" duration=%d ms\n", duration); switch (get_scsi_pt_result_category(ptvp)) { @@ -1598,11 +1852,11 @@ } if (ata_return_dp && (max_ata_return_len > 0)) { /* search for ATA return descriptor */ - ucp = sg_scsi_sense_desc_find(sp, slen, 0x9); - if (ucp) { - k = ucp[1] + 2; + bp = sg_scsi_sense_desc_find(sp, slen, 0x9); + if (bp) { + k = bp[1] + 2; k = (k > max_ata_return_len) ? max_ata_return_len : k; - memcpy(ata_return_dp, ucp, k); + memcpy(ata_return_dp, bp, k); } else ata_return_dp[0] = 0x0; } @@ -1611,17 +1865,17 @@ ret = get_scsi_pt_status_response(ptvp); break; case SCSI_PT_RESULT_TRANSPORT_ERR: - if (verbose) + if (vb) pr2ws("%s: transport error: %s\n", cnamep, get_scsi_pt_transport_err_str(ptvp, sizeof(b), b)); break; case SCSI_PT_RESULT_OS_ERR: - if (verbose) + if (vb) pr2ws("%s: os error: %s\n", cnamep, get_scsi_pt_os_err_str(ptvp, sizeof(b) , b)); break; default: - if (verbose) + if (vb) pr2ws("%s: unknown pt_result_category=%d\n", cnamep, get_scsi_pt_result_category(ptvp)); break; @@ -1632,57 +1886,61 @@ return ret; } -/* Invokes a SCSI READ BUFFER command (SPC). Return of 0 -> success +/* Invokes a SCSI READ BUFFER(10) command (SPC). Return of 0 -> success * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_read_buffer(int sg_fd, int mode, int buffer_id, int buffer_offset, - void * resp, int mx_resp_len, int noisy, int verbose) + void * resp, int mx_resp_len, bool noisy, int vb) { - int res, k, ret, sense_cat; - unsigned char rbufCmdBlk[READ_BUFFER_CMDLEN] = + static const char * const cdb_s = "read buffer(10)"; + int res, ret, s_cat; + uint8_t rbuf_cdb[READ_BUFFER_CMDLEN] = {READ_BUFFER_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; - rbufCmdBlk[1] = (unsigned char)(mode & 0x1f); - rbufCmdBlk[2] = (unsigned char)(buffer_id & 0xff); - sg_put_unaligned_be24((uint32_t)buffer_offset, rbufCmdBlk + 3); - sg_put_unaligned_be24((uint32_t)mx_resp_len, rbufCmdBlk + 6); - if (verbose) { - pr2ws(" read buffer cdb: "); - for (k = 0; k < READ_BUFFER_CMDLEN; ++k) - pr2ws("%02x ", rbufCmdBlk[k]); - pr2ws("\n"); + rbuf_cdb[1] = (uint8_t)(mode & 0x1f); + rbuf_cdb[2] = (uint8_t)(buffer_id & 0xff); + sg_put_unaligned_be24((uint32_t)buffer_offset, rbuf_cdb + 3); + sg_put_unaligned_be24((uint32_t)mx_resp_len, rbuf_cdb + 6); + if (vb) { + char b[128]; + + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(rbuf_cdb, READ_BUFFER_CMDLEN, + false, sizeof(b), b)); } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("read buffer: out of memory\n"); + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) return -1; - } - set_scsi_pt_cdb(ptvp, rbufCmdBlk, sizeof(rbufCmdBlk)); + set_scsi_pt_cdb(ptvp, rbuf_cdb, sizeof(rbuf_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "read buffer", res, mx_resp_len, - sense_b, noisy, verbose, &sense_cat); + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: - ret = sense_cat; + ret = s_cat; break; } } else { - if ((verbose > 2) && (ret > 0)) { - pr2ws(" read buffer: response%s\n", - (ret > 256 ? ", first 256 bytes" : "")); - dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1); + if ((vb > 2) && (ret > 0)) { + pr2ws(" %s: response", cdb_s); + if (3 == vb) { + pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); + hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), + -1); + } else { + pr2ws(":\n"); + hex2stderr((const uint8_t *)resp, ret, 0); + } } ret = 0; } @@ -1694,52 +1952,132 @@ * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_write_buffer(int sg_fd, int mode, int buffer_id, int buffer_offset, - void * paramp, int param_len, int noisy, int verbose) + void * paramp, int param_len, bool noisy, int vb) +{ + static const char * const cdb_s = "write buffer"; + int res, ret, s_cat; + uint8_t wbuf_cdb[WRITE_BUFFER_CMDLEN] = + {WRITE_BUFFER_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t sense_b[SENSE_BUFF_LEN]; + struct sg_pt_base * ptvp; + + wbuf_cdb[1] = (uint8_t)(mode & 0x1f); + wbuf_cdb[2] = (uint8_t)(buffer_id & 0xff); + sg_put_unaligned_be24((uint32_t)buffer_offset, wbuf_cdb + 3); + sg_put_unaligned_be24((uint32_t)param_len, wbuf_cdb + 6); + if (vb) { + char b[128]; + + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(wbuf_cdb, WRITE_BUFFER_CMDLEN, + false, sizeof(b), b)); + if ((vb > 1) && paramp && param_len) { + pr2ws(" %s parameter list", cdb_s); + if (2 == vb) { + pr2ws("%s:\n", (param_len > 256 ? ", first 256 bytes" : "")); + hex2stderr((const uint8_t *)paramp, + (param_len > 256 ? 256 : param_len), -1); + } else { + pr2ws(":\n"); + hex2stderr((const uint8_t *)paramp, param_len, 0); + } + } + } + + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + return -1; + set_scsi_pt_cdb(ptvp, wbuf_cdb, sizeof(wbuf_cdb)); + set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); + set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, param_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); + if (-1 == ret) + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); + else if (-2 == ret) { + switch (s_cat) { + case SG_LIB_CAT_RECOVERED: + case SG_LIB_CAT_NO_SENSE: + ret = 0; + break; + default: + ret = s_cat; + break; + } + } else + ret = 0; + + destruct_scsi_pt_obj(ptvp); + return ret; +} + +/* Invokes a SCSI WRITE BUFFER command (SPC). Return of 0 -> + * success, SG_LIB_CAT_INVALID_OP -> invalid opcode, + * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, + * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, + * -1 -> other failure. Adds mode specific field (spc4r32) and timeout + * to command abort to override default of 60 seconds. If timeout_secs is + * 0 or less then the default timeout is used instead. */ +int +sg_ll_write_buffer_v2(int sg_fd, int mode, int m_specific, int buffer_id, + uint32_t buffer_offset, void * paramp, + uint32_t param_len, int timeout_secs, bool noisy, + int vb) { - int k, res, ret, sense_cat; - unsigned char wbufCmdBlk[WRITE_BUFFER_CMDLEN] = + int res, ret, s_cat; + uint8_t wbuf_cdb[WRITE_BUFFER_CMDLEN] = {WRITE_BUFFER_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; - wbufCmdBlk[1] = (unsigned char)(mode & 0x1f); - wbufCmdBlk[2] = (unsigned char)(buffer_id & 0xff); - sg_put_unaligned_be24((uint32_t)buffer_offset, wbufCmdBlk + 3); - sg_put_unaligned_be24((uint32_t)param_len, wbufCmdBlk + 6); - if (verbose) { - pr2ws(" Write buffer cmd: "); - for (k = 0; k < WRITE_BUFFER_CMDLEN; ++k) - pr2ws("%02x ", wbufCmdBlk[k]); - pr2ws("\n"); - if ((verbose > 1) && paramp && param_len) { + if (buffer_offset > 0xffffff) { + pr2ws("%s: buffer_offset value too large for 24 bits\n", __func__); + return -1; + } + if (param_len > 0xffffff) { + pr2ws("%s: param_len value too large for 24 bits\n", __func__); + return -1; + } + wbuf_cdb[1] = (uint8_t)(mode & 0x1f); + wbuf_cdb[1] |= (uint8_t)((m_specific & 0x7) << 5); + wbuf_cdb[2] = (uint8_t)(buffer_id & 0xff); + sg_put_unaligned_be24(buffer_offset, wbuf_cdb + 3); + sg_put_unaligned_be24(param_len, wbuf_cdb + 6); + if (vb) { + char b[128]; + + pr2ws(" Write buffer cdb: %s\n", + sg_get_command_str(wbuf_cdb, WRITE_BUFFER_CMDLEN, + false, sizeof(b), b)); + if ((vb > 1) && paramp && param_len) { pr2ws(" Write buffer parameter list%s:\n", ((param_len > 256) ? " (first 256 bytes)" : "")); - dStrHexErr((const char *)paramp, + hex2stderr((const uint8_t *)paramp, ((param_len > 256) ? 256 : param_len), -1); } } + if (timeout_secs <= 0) + timeout_secs = DEF_PT_TIMEOUT; ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) { - pr2ws("write buffer: out of memory\n"); + pr2ws("%s: out of memory\n", __func__); return -1; } - set_scsi_pt_cdb(ptvp, wbufCmdBlk, sizeof(wbufCmdBlk)); + set_scsi_pt_cdb(ptvp, wbuf_cdb, sizeof(wbuf_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "write buffer", res, 0, sense_b, - noisy, verbose, &sense_cat); + set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, param_len); + res = do_scsi_pt(ptvp, sg_fd, timeout_secs, vb); + ret = sg_cmds_process_resp(ptvp, "Write buffer", res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: - ret = sense_cat; + ret = s_cat; break; } } else @@ -1753,61 +2091,59 @@ * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_unmap(int sg_fd, int group_num, int timeout_secs, void * paramp, - int param_len, int noisy, int verbose) + int param_len, bool noisy, int vb) { - return sg_ll_unmap_v2(sg_fd, 0, group_num, timeout_secs, paramp, - param_len, noisy, verbose); + return sg_ll_unmap_v2(sg_fd, false, group_num, timeout_secs, paramp, + param_len, noisy, vb); } /* Invokes a SCSI UNMAP (SBC-3) command. Version 2 adds anchor field * (sbc3r22). Otherwise same as sg_ll_unmap() . */ int -sg_ll_unmap_v2(int sg_fd, int anchor, int group_num, int timeout_secs, - void * paramp, int param_len, int noisy, int verbose) +sg_ll_unmap_v2(int sg_fd, bool anchor, int group_num, int timeout_secs, + void * paramp, int param_len, bool noisy, int vb) { - int k, res, ret, sense_cat, tmout; - unsigned char uCmdBlk[UNMAP_CMDLEN] = + static const char * const cdb_s = "unmap"; + int res, ret, s_cat, tmout; + uint8_t u_cdb[UNMAP_CMDLEN] = {UNMAP_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (anchor) - uCmdBlk[1] |= 0x1; + u_cdb[1] |= 0x1; tmout = (timeout_secs > 0) ? timeout_secs : DEF_PT_TIMEOUT; - uCmdBlk[6] = group_num & 0x1f; - sg_put_unaligned_be16((uint16_t)param_len, uCmdBlk + 7); - if (verbose) { - pr2ws(" unmap cdb: "); - for (k = 0; k < UNMAP_CMDLEN; ++k) - pr2ws("%02x ", uCmdBlk[k]); - pr2ws("\n"); - if ((verbose > 1) && paramp && param_len) { - pr2ws(" unmap parameter list:\n"); - dStrHexErr((const char *)paramp, param_len, -1); + u_cdb[6] = group_num & 0x1f; + sg_put_unaligned_be16((uint16_t)param_len, u_cdb + 7); + if (vb) { + char b[128]; + + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(u_cdb, UNMAP_CMDLEN, + false, sizeof(b), b)); + if ((vb > 1) && paramp && param_len) { + pr2ws(" %s parameter list:\n", cdb_s); + hex2stderr((const uint8_t *)paramp, param_len, -1); } } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("unmap: out of memory\n"); + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) return -1; - } - set_scsi_pt_cdb(ptvp, uCmdBlk, sizeof(uCmdBlk)); + set_scsi_pt_cdb(ptvp, u_cdb, sizeof(u_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); - res = do_scsi_pt(ptvp, sg_fd, tmout, verbose); - ret = sg_cmds_process_resp(ptvp, "unmap", res, 0, - sense_b, noisy, verbose, &sense_cat); + set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, param_len); + res = do_scsi_pt(ptvp, sg_fd, tmout, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: - ret = sense_cat; + ret = s_cat; break; } } else @@ -1820,49 +2156,53 @@ * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_read_block_limits(int sg_fd, void * resp, int mx_resp_len, - int noisy, int verbose) + bool noisy, int vb) { - int k, ret, res, sense_cat; - unsigned char rlCmdBlk[READ_BLOCK_LIMITS_CMDLEN] = + static const char * const cdb_s = "read block limits"; + int ret, res, s_cat; + uint8_t rl_cdb[READ_BLOCK_LIMITS_CMDLEN] = {READ_BLOCK_LIMITS_CMD, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; - if (verbose) { - pr2ws(" read block limits cdb: "); - for (k = 0; k < READ_BLOCK_LIMITS_CMDLEN; ++k) - pr2ws("%02x ", rlCmdBlk[k]); - pr2ws("\n"); + if (vb) { + char b[128]; + + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(rl_cdb, READ_BLOCK_LIMITS_CMDLEN, + false, sizeof(b), b)); } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("read block limits: out of memory\n"); + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) return -1; - } - set_scsi_pt_cdb(ptvp, rlCmdBlk, sizeof(rlCmdBlk)); + set_scsi_pt_cdb(ptvp, rl_cdb, sizeof(rl_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "read block limits", res, mx_resp_len, - sense_b, noisy, verbose, &sense_cat); + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: - ret = sense_cat; + ret = s_cat; break; } } else { - if ((verbose > 2) && (ret > 0)) { - pr2ws(" read block limits: response%s\n", - (ret > 256 ? ", first 256 bytes" : "")); - dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1); + if ((vb > 2) && (ret > 0)) { + pr2ws(" %s: response", cdb_s); + if (3 == vb) { + pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); + hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), + -1); + } else { + pr2ws(":\n"); + hex2stderr((const uint8_t *)resp, ret, 0); + } } ret = 0; } @@ -1875,51 +2215,48 @@ * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_receive_copy_results(int sg_fd, int sa, int list_id, void * resp, - int mx_resp_len, int noisy, int verbose) + int mx_resp_len, bool noisy, int vb) { - int k, res, ret, sense_cat; - unsigned char rcvcopyresCmdBlk[THIRD_PARTY_COPY_IN_CMDLEN] = + int res, ret, s_cat; + uint8_t rcvcopyres_cdb[THIRD_PARTY_COPY_IN_CMDLEN] = {THIRD_PARTY_COPY_IN_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; char b[64]; sg_get_opcode_sa_name(THIRD_PARTY_COPY_IN_CMD, sa, 0, (int)sizeof(b), b); - rcvcopyresCmdBlk[1] = (unsigned char)(sa & 0x1f); + rcvcopyres_cdb[1] = (uint8_t)(sa & 0x1f); if (sa <= 4) /* LID1 variants */ - rcvcopyresCmdBlk[2] = (unsigned char)(list_id); + rcvcopyres_cdb[2] = (uint8_t)(list_id); else if ((sa >= 5) && (sa <= 7)) /* LID4 variants */ - sg_put_unaligned_be32((uint32_t)list_id, rcvcopyresCmdBlk + 2); - sg_put_unaligned_be32((uint32_t)mx_resp_len, rcvcopyresCmdBlk + 10); + sg_put_unaligned_be32((uint32_t)list_id, rcvcopyres_cdb + 2); + sg_put_unaligned_be32((uint32_t)mx_resp_len, rcvcopyres_cdb + 10); + + if (vb) { + char d[128]; - if (verbose) { - pr2ws(" %s cmd: ", b); - for (k = 0; k < THIRD_PARTY_COPY_IN_CMDLEN; ++k) - pr2ws("%02x ", rcvcopyresCmdBlk[k]); - pr2ws("\n"); + pr2ws(" %s cdb: %s\n", b, + sg_get_command_str(rcvcopyres_cdb, THIRD_PARTY_COPY_IN_CMDLEN, + false, sizeof(d), d)); } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("%s: out of memory\n", b); + if (NULL == ((ptvp = create_pt_obj(b)))) return -1; - } - set_scsi_pt_cdb(ptvp, rcvcopyresCmdBlk, sizeof(rcvcopyresCmdBlk)); + set_scsi_pt_cdb(ptvp, rcvcopyres_cdb, sizeof(rcvcopyres_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, b, res, mx_resp_len, sense_b, noisy, - verbose, &sense_cat); + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, b, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: - ret = sense_cat; + ret = s_cat; break; } } else @@ -1937,51 +2274,48 @@ /* Invokes a SCSI EXTENDED COPY (LID1) command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int -sg_ll_extended_copy(int sg_fd, void * paramp, int param_len, int noisy, - int verbose) +sg_ll_extended_copy(int sg_fd, void * paramp, int param_len, bool noisy, + int vb) { - int k, res, ret, sense_cat; - unsigned char xcopyCmdBlk[THIRD_PARTY_COPY_OUT_CMDLEN] = + int res, ret, s_cat; + uint8_t xcopy_cdb[THIRD_PARTY_COPY_OUT_CMDLEN] = {THIRD_PARTY_COPY_OUT_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; - const char * opcode_name = "Extended copy (LID1)"; + const char * cdb_s = "Extended copy (LID1)"; + + xcopy_cdb[1] = (uint8_t)(EXTENDED_COPY_LID1_SA & 0x1f); + sg_put_unaligned_be32((uint32_t)param_len, xcopy_cdb + 10); - xcopyCmdBlk[1] = (unsigned char)(EXTENDED_COPY_LID1_SA & 0x1f); - sg_put_unaligned_be32((uint32_t)param_len, xcopyCmdBlk + 10); + if (vb) { + char b[128]; - if (verbose) { - pr2ws(" %s cmd: ", opcode_name); - for (k = 0; k < THIRD_PARTY_COPY_OUT_CMDLEN; ++k) - pr2ws("%02x ", xcopyCmdBlk[k]); - pr2ws("\n"); - if ((verbose > 1) && paramp && param_len) { - pr2ws(" %s parameter list:\n", opcode_name); - dStrHexErr((const char *)paramp, param_len, -1); + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(xcopy_cdb, THIRD_PARTY_COPY_OUT_CMDLEN, + false, sizeof(b), b)); + if ((vb > 1) && paramp && param_len) { + pr2ws(" %s parameter list:\n", cdb_s); + hex2stderr((const uint8_t *)paramp, param_len, -1); } } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("%s: out of memory\n", opcode_name); + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) return -1; - } - set_scsi_pt_cdb(ptvp, xcopyCmdBlk, sizeof(xcopyCmdBlk)); + set_scsi_pt_cdb(ptvp, xcopy_cdb, sizeof(xcopy_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); - res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, opcode_name, res, 0, sense_b, - noisy, verbose, &sense_cat); + set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, param_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: - ret = sense_cat; + ret = s_cat; break; } } else @@ -1998,74 +2332,176 @@ int sg_ll_3party_copy_out(int sg_fd, int sa, unsigned int list_id, int group_num, int timeout_secs, void * paramp, int param_len, - int noisy, int verbose) + bool noisy, int vb) { - int k, res, ret, sense_cat, tmout; - unsigned char xcopyCmdBlk[THIRD_PARTY_COPY_OUT_CMDLEN] = + int res, ret, s_cat, tmout; + uint8_t xcopy_cdb[THIRD_PARTY_COPY_OUT_CMDLEN] = {THIRD_PARTY_COPY_OUT_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; char cname[80]; sg_get_opcode_sa_name(THIRD_PARTY_COPY_OUT_CMD, sa, 0, sizeof(cname), cname); - xcopyCmdBlk[1] = (unsigned char)(sa & 0x1f); + xcopy_cdb[1] = (uint8_t)(sa & 0x1f); switch (sa) { case 0x0: /* XCOPY(LID1) */ case 0x1: /* XCOPY(LID4) */ - sg_put_unaligned_be32((uint32_t)param_len, xcopyCmdBlk + 10); + sg_put_unaligned_be32((uint32_t)param_len, xcopy_cdb + 10); break; case 0x10: /* POPULATE TOKEN (SBC-3) */ case 0x11: /* WRITE USING TOKEN (SBC-3) */ - sg_put_unaligned_be32((uint32_t)list_id, xcopyCmdBlk + 6); - sg_put_unaligned_be32((uint32_t)param_len, xcopyCmdBlk + 10); - xcopyCmdBlk[14] = (unsigned char)(group_num & 0x1f); + sg_put_unaligned_be32((uint32_t)list_id, xcopy_cdb + 6); + sg_put_unaligned_be32((uint32_t)param_len, xcopy_cdb + 10); + xcopy_cdb[14] = (uint8_t)(group_num & 0x1f); break; case 0x1c: /* COPY OPERATION ABORT */ - sg_put_unaligned_be32((uint32_t)list_id, xcopyCmdBlk + 2); + sg_put_unaligned_be32((uint32_t)list_id, xcopy_cdb + 2); break; default: - pr2ws("sg_ll_3party_copy_out: unknown service action 0x%x\n", sa); + pr2ws("%s: unknown service action 0x%x\n", __func__, sa); return -1; } tmout = (timeout_secs > 0) ? timeout_secs : DEF_PT_TIMEOUT; - if (verbose) { - pr2ws(" %s cmd: ", cname); - for (k = 0; k < THIRD_PARTY_COPY_OUT_CMDLEN; ++k) - pr2ws("%02x ", xcopyCmdBlk[k]); - pr2ws("\n"); - if ((verbose > 1) && paramp && param_len) { + if (vb) { + char b[128]; + + pr2ws(" %s cdb: %s\n", cname, + sg_get_command_str(xcopy_cdb, THIRD_PARTY_COPY_OUT_CMDLEN, + false, sizeof(b), b)); + if ((vb > 1) && paramp && param_len) { pr2ws(" %s parameter list:\n", cname); - dStrHexErr((const char *)paramp, param_len, -1); + hex2stderr((const uint8_t *)paramp, param_len, -1); } } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("%s: out of memory\n", cname); + if (NULL == ((ptvp = create_pt_obj(cname)))) return -1; + set_scsi_pt_cdb(ptvp, xcopy_cdb, sizeof(xcopy_cdb)); + set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); + set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, param_len); + res = do_scsi_pt(ptvp, sg_fd, tmout, vb); + ret = sg_cmds_process_resp(ptvp, cname, res, noisy, vb, &s_cat); + if (-1 == ret) + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); + else if (-2 == ret) { + switch (s_cat) { + case SG_LIB_CAT_RECOVERED: + case SG_LIB_CAT_NO_SENSE: + ret = 0; + break; + default: + ret = s_cat; + break; + } + } else + ret = 0; + destruct_scsi_pt_obj(ptvp); + return ret; +} + +/* Invokes a SCSI PRE-FETCH(10), PRE-FETCH(16) or SEEK(10) command (SBC). + * Returns 0 -> success, 25 (SG_LIB_CAT_CONDITION_MET), various SG_LIB_CAT_* + * positive values or -1 -> other errors. Note that CONDITION MET status + * is returned when immed=true and num_blocks can fit in device's cache, + * somewaht strangely, GOOD status (return 0) is returned if num_blocks + * cannot fit in device's cache. If do_seek10==true then does a SEEK(10) + * command with given lba, if that LBA is < 2**32 . Unclear what SEEK(10) + * does, assume it is like PRE-FETCH. If timeout_secs is 0 (or less) then + * use DEF_PT_TIMEOUT (60 seconds) as command timeout. */ +int +sg_ll_pre_fetch_x(int sg_fd, bool do_seek10, bool cdb16, bool immed, + uint64_t lba, uint32_t num_blocks, int group_num, + int timeout_secs, bool noisy, int vb) +{ + static const char * const cdb10_name_s = "Pre-fetch(10)"; + static const char * const cdb16_name_s = "Pre-fetch(16)"; + static const char * const cdb_seek_name_s = "Seek(10)"; + int res, s_cat, ret, cdb_len, tmout; + const char *cdb_s; + uint8_t preFetchCdb[PRE_FETCH16_CMDLEN]; /* all use longest cdb */ + uint8_t sense_b[SENSE_BUFF_LEN]; + struct sg_pt_base * ptvp; + + memset(preFetchCdb, 0, sizeof(preFetchCdb)); + if (do_seek10) { + if (lba > UINT32_MAX) { + if (vb) + pr2ws("%s: LBA exceeds 2**32 in %s\n", __func__, + cdb_seek_name_s); + return -1; + } + preFetchCdb[0] = SEEK10_CMD; + cdb_len = SEEK10_CMDLEN; + cdb_s = cdb_seek_name_s; + sg_put_unaligned_be32((uint32_t)lba, preFetchCdb + 2); + } else { + if ((! cdb16) && + ((lba > UINT32_MAX) || (num_blocks > UINT16_MAX))) { + cdb16 = true; + if (noisy || vb) + pr2ws("%s: do %s due to %s size\n", __func__, cdb16_name_s, + (lba > UINT32_MAX) ? "LBA" : "NUM_BLOCKS"); + } + if (cdb16) { + preFetchCdb[0] = PRE_FETCH16_CMD; + cdb_len = PRE_FETCH16_CMDLEN; + cdb_s = cdb16_name_s; + if (immed) + preFetchCdb[1] = 0x2; + sg_put_unaligned_be64(lba, preFetchCdb + 2); + sg_put_unaligned_be32(num_blocks, preFetchCdb + 10); + preFetchCdb[14] = 0x3f & group_num; + } else { + preFetchCdb[0] = PRE_FETCH10_CMD; + cdb_len = PRE_FETCH10_CMDLEN; + cdb_s = cdb10_name_s; + if (immed) + preFetchCdb[1] = 0x2; + sg_put_unaligned_be32((uint32_t)lba, preFetchCdb + 2); + preFetchCdb[6] = 0x3f & group_num; + sg_put_unaligned_be16((uint16_t)num_blocks, preFetchCdb + 7); + } + } + tmout = (timeout_secs > 0) ? timeout_secs : DEF_PT_TIMEOUT; + if (vb) { + char b[128]; + + pr2ws(" %s cdb: %s\n", cdb_s, + sg_get_command_str(preFetchCdb, cdb_len, false, sizeof(b), b)); } - set_scsi_pt_cdb(ptvp, xcopyCmdBlk, sizeof(xcopyCmdBlk)); + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) + return -1; + set_scsi_pt_cdb(ptvp, preFetchCdb, cdb_len); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); - res = do_scsi_pt(ptvp, sg_fd, tmout, verbose); - ret = sg_cmds_process_resp(ptvp, cname, res, 0, sense_b, noisy, verbose, - &sense_cat); + res = do_scsi_pt(ptvp, sg_fd, tmout, vb); + if (0 == res) { + int sstat = get_scsi_pt_status_response(ptvp); + + if (SG_LIB_CAT_CONDITION_MET == sstat) { + ret = SG_LIB_CAT_CONDITION_MET; + if (vb > 2) + pr2ws("%s: returns SG_LIB_CAT_CONDITION_MET\n", __func__); + goto fini; + } + } + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { - switch (sense_cat) { + switch (s_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: - ret = sense_cat; + ret = s_cat; break; } } else ret = 0; +fini: destruct_scsi_pt_obj(ptvp); return ret; } diff -Nru sdparm-1.10/lib/sg_cmds_mmc.c sdparm-1.12/lib/sg_cmds_mmc.c --- sdparm-1.10/lib/sg_cmds_mmc.c 2015-12-01 20:16:07.000000000 +0000 +++ sdparm-1.12/lib/sg_cmds_mmc.c 2019-01-14 02:42:05.000000000 +0000 @@ -1,8 +1,10 @@ /* - * Copyright (c) 2008-2015 Douglas Gilbert. + * Copyright (c) 2008-2019 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. + * + * SPDX-License-Identifier: BSD-2-Clause */ #include @@ -13,15 +15,16 @@ #define __STDC_FORMAT_MACROS 1 #include +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "sg_lib.h" #include "sg_cmds_basic.h" #include "sg_cmds_mmc.h" #include "sg_pt.h" #include "sg_unaligned.h" - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#include "sg_pr2serr.h" #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */ @@ -37,24 +40,14 @@ #define SET_STREAMING_CMD 0xb6 #define SET_STREAMING_CMDLEN 12 -#ifdef __GNUC__ -static int pr2ws(const char * fmt, ...) - __attribute__ ((format (printf, 1, 2))); -#else -static int pr2ws(const char * fmt, ...); -#endif - -static int -pr2ws(const char * fmt, ...) +static struct sg_pt_base * +create_pt_obj(const char * cname) { - va_list args; - int n; - - va_start(args, fmt); - n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args); - va_end(args); - return n; + struct sg_pt_base * ptvp = construct_scsi_pt_obj(); + if (NULL == ptvp) + pr2ws("%s: out of memory\n", cname); + return ptvp; } /* Invokes a SCSI SET CD SPEED command (MMC). @@ -64,12 +57,13 @@ * -1 -> other failure */ int sg_ll_set_cd_speed(int sg_fd, int rot_control, int drv_read_speed, - int drv_write_speed, int noisy, int verbose) + int drv_write_speed, bool noisy, int verbose) { + static const char * const cdb_s = "set cd speed"; int res, ret, k, sense_cat; - unsigned char scsCmdBlk[SET_CD_SPEED_CMDLEN] = {SET_CD_SPEED_CMD, 0, + uint8_t scsCmdBlk[SET_CD_SPEED_CMDLEN] = {SET_CD_SPEED_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ,0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; scsCmdBlk[1] |= (rot_control & 0x3); @@ -77,23 +71,19 @@ sg_put_unaligned_be16((uint16_t)drv_write_speed, scsCmdBlk + 4); if (verbose) { - pr2ws(" set cd speed cdb: "); + pr2ws(" %s cdb: ", cdb_s); for (k = 0; k < SET_CD_SPEED_CMDLEN; ++k) pr2ws("%02x ", scsCmdBlk[k]); pr2ws("\n"); } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("set cd speed: out of memory\n"); + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) return -1; - } set_scsi_pt_cdb(ptvp, scsCmdBlk, sizeof(scsCmdBlk)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "set cd speed", res, 0, - sense_b, noisy, verbose, &sense_cat); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_NOT_READY: @@ -124,12 +114,13 @@ * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */ int sg_ll_get_config(int sg_fd, int rt, int starting, void * resp, - int mx_resp_len, int noisy, int verbose) + int mx_resp_len, bool noisy, int verbose) { + static const char * const cdb_s = "get configuration"; int res, k, ret, sense_cat; - unsigned char gcCmdBlk[GET_CONFIG_CMD_LEN] = {GET_CONFIG_CMD, 0, 0, 0, + uint8_t gcCmdBlk[GET_CONFIG_CMD_LEN] = {GET_CONFIG_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if ((rt < 0) || (rt > 3)) { @@ -149,25 +140,21 @@ sg_put_unaligned_be16((uint16_t)mx_resp_len, gcCmdBlk + 7); if (verbose) { - pr2ws(" Get Configuration cdb: "); + pr2ws(" %s cdb: ", cdb_s); for (k = 0; k < GET_CONFIG_CMD_LEN; ++k) pr2ws("%02x ", gcCmdBlk[k]); pr2ws("\n"); } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("get configuration: out of memory\n"); + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) return -1; - } set_scsi_pt_cdb(ptvp, gcCmdBlk, sizeof(gcCmdBlk)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "get configuration", res, mx_resp_len, - sense_b, noisy, verbose, &sense_cat); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_INVALID_OP: @@ -186,17 +173,23 @@ } } else { if ((verbose > 2) && (ret > 3)) { - unsigned char * ucp; + uint8_t * bp; int len; - ucp = (unsigned char *)resp; - len = sg_get_unaligned_be32(ucp + 0); + bp = (uint8_t *)resp; + len = sg_get_unaligned_be32(bp + 0); if (len < 0) len = 0; len = (ret < len) ? ret : len; - pr2ws(" get configuration: response%s\n", - (len > 256 ? ", first 256 bytes" : "")); - dStrHexErr((const char *)resp, (len > 256 ? 256 : len), -1); + pr2ws(" %s: response:\n", cdb_s); + if (3 == verbose) { + pr2ws("%s:\n", (len > 256 ? ", first 256 bytes" : "")); + hex2stderr((const uint8_t *)resp, (len > 256 ? 256 : len), + -1); + } else { + pr2ws(":\n"); + hex2stderr((const uint8_t *)resp, len, 0); + } } ret = 0; } @@ -211,12 +204,13 @@ int sg_ll_get_performance(int sg_fd, int data_type, unsigned int starting_lba, int max_num_desc, int ttype, void * resp, - int mx_resp_len, int noisy, int verbose) + int mx_resp_len, bool noisy, int verbose) { + static const char * const cdb_s = "get performance"; int res, k, ret, sense_cat; - unsigned char gpCmdBlk[GET_PERFORMANCE_CMD_LEN] = {GET_PERFORMANCE_CMD, 0, + uint8_t gpCmdBlk[GET_PERFORMANCE_CMD_LEN] = {GET_PERFORMANCE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if ((data_type < 0) || (data_type > 0x1f)) { @@ -234,28 +228,24 @@ pr2ws("Bad type: 0x%x\n", ttype); return -1; } - gpCmdBlk[10] = (unsigned char)ttype; + gpCmdBlk[10] = (uint8_t)ttype; if (verbose) { - pr2ws(" Get Performance cdb: "); + pr2ws(" %s cdb: ", cdb_s); for (k = 0; k < GET_PERFORMANCE_CMD_LEN; ++k) pr2ws("%02x ", gpCmdBlk[k]); pr2ws("\n"); } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("get performance: out of memory\n"); + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) return -1; - } set_scsi_pt_cdb(ptvp, gpCmdBlk, sizeof(gpCmdBlk)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); + set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "get performance", res, mx_resp_len, - sense_b, noisy, verbose, &sense_cat); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_INVALID_OP: @@ -274,17 +264,23 @@ } } else { if ((verbose > 2) && (ret > 3)) { - unsigned char * ucp; + uint8_t * bp; int len; - ucp = (unsigned char *)resp; - len = sg_get_unaligned_be32(ucp + 0); + bp = (uint8_t *)resp; + len = sg_get_unaligned_be32(bp + 0); if (len < 0) len = 0; len = (ret < len) ? ret : len; - pr2ws(" get performance:: response%s\n", - (len > 256 ? ", first 256 bytes" : "")); - dStrHexErr((const char *)resp, (len > 256 ? 256 : len), -1); + pr2ws(" %s: response", cdb_s); + if (3 == verbose) { + pr2ws("%s:\n", (len > 256 ? ", first 256 bytes" : "")); + hex2stderr((const uint8_t *)resp, (len > 256 ? 256 : len), + -1); + } else { + pr2ws(":\n"); + hex2stderr((const uint8_t *)resp, len, 0); + } } ret = 0; } @@ -299,40 +295,37 @@ * -1 -> other failure */ int sg_ll_set_streaming(int sg_fd, int type, void * paramp, int param_len, - int noisy, int verbose) + bool noisy, int verbose) { + static const char * const cdb_s = "set streaming"; int k, res, ret, sense_cat; - unsigned char ssCmdBlk[SET_STREAMING_CMDLEN] = + uint8_t ssCmdBlk[SET_STREAMING_CMDLEN] = {SET_STREAMING_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; + uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; ssCmdBlk[8] = type; sg_put_unaligned_be16((uint16_t)param_len, ssCmdBlk + 9); if (verbose) { - pr2ws(" set streaming cdb: "); + pr2ws(" %s cdb: ", cdb_s); for (k = 0; k < SET_STREAMING_CMDLEN; ++k) pr2ws("%02x ", ssCmdBlk[k]); pr2ws("\n"); if ((verbose > 1) && paramp && param_len) { - pr2ws(" set streaming parameter list:\n"); - dStrHexErr((const char *)paramp, param_len, -1); + pr2ws(" %s parameter list:\n", cdb_s); + hex2stderr((const uint8_t *)paramp, param_len, -1); } } - ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - pr2ws("set streaming: out of memory\n"); + if (NULL == ((ptvp = create_pt_obj(cdb_s)))) return -1; - } set_scsi_pt_cdb(ptvp, ssCmdBlk, sizeof(ssCmdBlk)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); - set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); + set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); - ret = sg_cmds_process_resp(ptvp, "set streaming", res, 0, - sense_b, noisy, verbose, &sense_cat); + ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat); if (-1 == ret) - ; + ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_NOT_READY: diff -Nru sdparm-1.10/lib/sg_io_linux.c sdparm-1.12/lib/sg_io_linux.c --- sdparm-1.10/lib/sg_io_linux.c 2015-12-20 16:23:44.000000000 +0000 +++ sdparm-1.12/lib/sg_io_linux.c 2020-04-04 02:45:35.000000000 +0000 @@ -1,13 +1,16 @@ /* - * Copyright (c) 1999-2015 Douglas Gilbert. + * Copyright (c) 1999-2020 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. + * + * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include +#include #include #include @@ -18,29 +21,10 @@ #ifdef SG_LIB_LINUX #include "sg_io_linux.h" +#include "sg_pr2serr.h" -/* Version 1.06 20151217 */ - -#ifdef __GNUC__ -static int pr2ws(const char * fmt, ...) - __attribute__ ((format (printf, 1, 2))); -#else -static int pr2ws(const char * fmt, ...); -#endif - - -static int -pr2ws(const char * fmt, ...) -{ - va_list args; - int n; - - va_start(args, fmt); - n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args); - va_end(args); - return n; -} +/* Version 1.11 20200401 */ void @@ -51,6 +35,8 @@ sg_print_scsi_status(scsi_status); } +/* host_bytes: DID_* are Linux SCSI result (a 32 bit variable) bits 16:23 */ + static const char * linux_host_bytes[] = { "DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT", "DID_BAD_TARGET", "DID_ABORT", "DID_PARITY", "DID_ERROR", @@ -60,37 +46,34 @@ "DID_ALLOC_FAILURE", "DID_MEDIUM_ERROR", }; -#define LINUX_HOST_BYTES_SZ \ - (int)(sizeof(linux_host_bytes) / sizeof(linux_host_bytes[0])) - void sg_print_host_status(int host_status) { pr2ws("Host_status=0x%02x ", host_status); - if ((host_status < 0) || (host_status >= LINUX_HOST_BYTES_SZ)) + if ((host_status < 0) || + (host_status >= (int)SG_ARRAY_SIZE(linux_host_bytes))) pr2ws("is invalid "); else pr2ws("[%s] ", linux_host_bytes[host_status]); } +/* DRIVER_* are Linux SCSI result (a 32 bit variable) bits 24:27 */ + static const char * linux_driver_bytes[] = { "DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA", "DRIVER_ERROR", "DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD", - "DRIVER_SENSE" + "DRIVER_SENSE", }; -#define LINUX_DRIVER_BYTES_SZ \ - (int)(sizeof(linux_driver_bytes) / sizeof(linux_driver_bytes[0])) - #if 0 + +/* SUGGEST_* are Linux SCSI result (a 32 bit variable) bits 28:31 */ + static const char * linux_driver_suggests[] = { "SUGGEST_OK", "SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP", "SUGGEST_DIE", "UNKNOWN","UNKNOWN","UNKNOWN", - "SUGGEST_SENSE" + "SUGGEST_SENSE", }; - -#define LINUX_DRIVER_SUGGESTS_SZ \ - (int)(sizeof(linux_driver_suggests) / sizeof(linux_driver_suggests[0])) #endif @@ -101,11 +84,11 @@ const char * driv_cp = "invalid"; driv = driver_status & SG_LIB_DRIVER_MASK; - if (driv < LINUX_DRIVER_BYTES_SZ) + if (driv < (int)SG_ARRAY_SIZE(linux_driver_bytes)) driv_cp = linux_driver_bytes[driv]; #if 0 sugg = (driver_status & SG_LIB_SUGGEST_MASK) >> 4; - if (sugg < LINUX_DRIVER_SUGGESTS_SZ) + if (sugg < (int)SG_ARRAY_SIZE(linux_driver_suggests)) sugg_cp = linux_driver_suggests[sugg]; #endif pr2ws("Driver_status=0x%02x", driver_status); @@ -113,14 +96,15 @@ } /* Returns 1 if no errors found and thus nothing printed; otherwise - prints error/warning (prefix by 'leadin') and returns 0. */ -static int + * prints error/warning (prefix by 'leadin') to stderr (pr2ws) and + * returns 0. */ +int sg_linux_sense_print(const char * leadin, int scsi_status, int host_status, - int driver_status, const unsigned char * sense_buffer, - int sb_len, int raw_sinfo) + int driver_status, const uint8_t * sense_buffer, + int sb_len, bool raw_sinfo) { - int done_leadin = 0; - int done_sense = 0; + bool done_leadin = false; + bool done_sense = false; scsi_status &= 0x7e; /*sanity */ if ((0 == scsi_status) && (0 == host_status) && (0 == driver_status)) @@ -128,7 +112,7 @@ if (0 != scsi_status) { if (leadin) pr2ws("%s: ", leadin); - done_leadin = 1; + done_leadin = true; pr2ws("SCSI status: "); sg_print_scsi_status(scsi_status); pr2ws("\n"); @@ -136,7 +120,7 @@ (scsi_status == SAM_STAT_COMMAND_TERMINATED))) { /* SAM_STAT_COMMAND_TERMINATED is obsolete */ sg_print_sense(0, sense_buffer, sb_len, raw_sinfo); - done_sense = 1; + done_sense = true; } } if (0 != host_status) { @@ -145,7 +129,7 @@ if (done_leadin) pr2ws("plus...: "); else - done_leadin = 1; + done_leadin = true; sg_print_host_status(host_status); pr2ws("\n"); } @@ -157,8 +141,6 @@ pr2ws("%s: ", leadin); if (done_leadin) pr2ws("plus...: "); - else - done_leadin = 1; sg_print_driver_status(driver_status); pr2ws("\n"); if (sense_buffer && (! done_sense) && @@ -170,7 +152,7 @@ #ifdef SG_IO -int +bool sg_normalize_sense(const struct sg_io_hdr * hp, struct sg_scsi_sense_hdr * sshp) { @@ -186,7 +168,7 @@ returns 0. */ int sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp, - int raw_sinfo) + bool raw_sinfo) { return sg_linux_sense_print(leadin, hp->status, hp->host_status, hp->driver_status, hp->sbp, hp->sb_len_wr, @@ -198,8 +180,8 @@ returns 0. */ int sg_chk_n_print(const char * leadin, int masked_status, int host_status, - int driver_status, const unsigned char * sense_buffer, - int sb_len, int raw_sinfo) + int driver_status, const uint8_t * sense_buffer, + int sb_len, bool raw_sinfo) { int scsi_status = (masked_status << 1) & 0x7e; @@ -219,7 +201,7 @@ int sg_err_category(int masked_status, int host_status, int driver_status, - const unsigned char * sense_buffer, int sb_len) + const uint8_t * sense_buffer, int sb_len) { int scsi_status = (masked_status << 1) & 0x7e; @@ -229,7 +211,7 @@ int sg_err_category_new(int scsi_status, int host_status, int driver_status, - const unsigned char * sense_buffer, int sb_len) + const uint8_t * sense_buffer, int sb_len) { int masked_driver_status = (SG_LIB_DRIVER_MASK & driver_status); diff -Nru sdparm-1.10/lib/sg_lib.c sdparm-1.12/lib/sg_lib.c --- sdparm-1.10/lib/sg_lib.c 2016-02-03 15:09:29.000000000 +0000 +++ sdparm-1.12/lib/sg_lib.c 2020-09-03 15:32:35.000000000 +0000 @@ -1,8 +1,10 @@ /* - * Copyright (c) 1999-2016 Douglas Gilbert. + * Copyright (c) 1999-2020 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. + * + * SPDX-License-Identifier: BSD-2-Clause */ /* NOTICE: @@ -27,37 +29,41 @@ * */ +#define _POSIX_C_SOURCE 200809L /* for posix_memalign() */ #include #include #include +#include +#include #include #include #define __STDC_FORMAT_MACROS 1 #include - -#include "sg_lib.h" -#include "sg_lib_data.h" -#include "sg_unaligned.h" +#include +#include +#include +#include +#include #ifdef HAVE_CONFIG_H #include "config.h" #endif +#include "sg_lib.h" +#include "sg_lib_data.h" +#include "sg_unaligned.h" +#include "sg_pr2serr.h" + /* sg_lib_version_str (and datestamp) defined in sg_lib_data.c file */ #define ASCQ_ATA_PT_INFO_AVAILABLE 0x1d /* corresponding ASC is 0 */ -FILE * sg_warnings_strm = NULL; /* would like to default to stderr */ +typedef unsigned int my_uint; /* convenience to save a few line wraps */ -#ifdef __GNUC__ -static int pr2ws(const char * fmt, ...) - __attribute__ ((format (printf, 1, 2))); -#else -static int pr2ws(const char * fmt, ...); -#endif +FILE * sg_warnings_strm = NULL; /* would like to default to stderr */ -static int +int pr2ws(const char * fmt, ...) { va_list args; @@ -69,22 +75,30 @@ return n; } -#ifdef __GNUC__ -static int my_snprintf(char * cp, int cp_max_len, const char * fmt, ...) - __attribute__ ((format (printf, 3, 4))); -#else -static int my_snprintf(char * cp, int cp_max_len, const char * fmt, ...); -#endif +/* Users of the sg_pr2serr.h header need this function definition */ +int +pr2serr(const char * fmt, ...) +{ + va_list args; + int n; + + va_start(args, fmt); + n = vfprintf(stderr, fmt, args); + va_end(args); + return n; +} + /* Want safe, 'n += snprintf(b + n, blen - n, ...)' style sequence of - * functions. Returns number number of chars placed in cp excluding the + * functions. Returns number of chars placed in cp excluding the * trailing null char. So for cp_max_len > 0 the return value is always - * < cp_max_len; for cp_max_len <= 1 the return value is 0 and no chars - * are written to cp. Note this means that when cp_max_len = 1, this - * function assumes that cp[0] is the null character and does nothing - * (and returns 0). */ -static int -my_snprintf(char * cp, int cp_max_len, const char * fmt, ...) + * < cp_max_len; for cp_max_len <= 1 the return value is 0 and no chars are + * written to cp. Note this means that when cp_max_len = 1, this function + * assumes that cp[0] is the null character and does nothing (and returns + * 0). Linux kernel has a similar function called scnprintf(). Public + * declaration in sg_pr2serr.h header */ +int +sg_scnpr(char * cp, int cp_max_len, const char * fmt, ...) { va_list args; int n; @@ -97,6 +111,30 @@ return (n < cp_max_len) ? n : (cp_max_len - 1); } +/* Simple ASCII printable (does not use locale), includes space and excludes + * DEL (0x7f). */ +static inline int +my_isprint(int ch) +{ + return ((ch >= ' ') && (ch < 0x7f)); +} + +/* DSENSE is 'descriptor sense' as opposed to the older 'fixed sense'. + * Only (currently) used in SNTL. */ +bool +sg_get_initial_dsense(void) +{ + int k; + const char * cp; + + cp = getenv("SG3_UTILS_DSENSE"); + if (cp) { + if (1 == sscanf(cp, "%d", &k)) + return k ? true : false; + } + return false; +} + /* Searches 'arr' for match on 'value' then 'peri_type'. If matches 'value' but not 'peri_type' then yields first 'value' match entry. Last element of 'arr' has NULL 'name'. If no match returns NULL. */ @@ -107,6 +145,8 @@ const struct sg_lib_value_name_t * vp = arr; const struct sg_lib_value_name_t * holdp; + if (peri_type < 0) + peri_type = 0; for (; vp->name; ++vp) { if (value == vp->value) { if (peri_type == vp->peri_dev_type) @@ -131,60 +171,116 @@ sg_warnings_strm = warnings_strm; } +/* Take care to minimize printf() parsing delays when printing commands */ +static char bin2hexascii[] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + +/* Given a SCSI command pointed to by cdbp of sz bytes this function forms + * a SCSI command in ASCII surrounded by square brackets in 'b'. 'b' is at + * least blen bytes long. If cmd_name is true then the command is prefixed + * by its SCSI command name (e.g. "VERIFY(10) [2f ...]". The command is + * shown as spaced separated pairs of hexadecimal digits (i.e. 0-9, a-f). + * Each pair represents byte. The leftmost pair of digits is cdbp[0] . If + * sz <= 0 then this function tries to guess the length of the command. */ +char * +sg_get_command_str(const uint8_t * cdbp, int sz, bool cmd_name, int blen, + char * b) +{ + int k, j, jj; + + if ((cdbp == NULL) || (b == NULL) || (blen < 1)) + return b; + if (cmd_name && (blen > 16)) { + sg_get_command_name(cdbp, 0, blen, b); + j = (int)strlen(b); + if (j < (blen - 1)) + b[j++] = ' '; + } else + j = 0; + if (j >= blen) + goto fini; + b[j++] = '['; + if (j >= blen) + goto fini; + if (sz <= 0) { + if (SG_VARIABLE_LENGTH_CMD == cdbp[0]) + sz = cdbp[7] + 8; + else + sz = sg_get_command_size(cdbp[0]); + } + jj = j; + for (k = 0; (k < sz) && (j < (blen - 3)); ++k, j += 3, ++cdbp) { + b[j] = bin2hexascii[(*cdbp >> 4) & 0xf]; + b[j + 1] = bin2hexascii[*cdbp & 0xf]; + b[j + 2] = ' '; + } + if (j > jj) + --j; /* don't want trailing space before ']' */ + if (j >= blen) + goto fini; + b[j++] = ']'; +fini: + if (j >= blen) + b[blen - 1] = '\0'; /* truncated string */ + else + b[j] = '\0'; + return b; +} + #define CMD_NAME_LEN 128 void -sg_print_command(const unsigned char * command) +sg_print_command_len(const uint8_t * cdbp, int sz) { - int k, sz; char buff[CMD_NAME_LEN]; - sg_get_command_name(command, 0, CMD_NAME_LEN, buff); - buff[CMD_NAME_LEN - 1] = '\0'; + sg_get_command_str(cdbp, sz, true, sizeof(buff), buff); + pr2ws("%s\n", buff); +} - pr2ws("%s [", buff); - if (SG_VARIABLE_LENGTH_CMD == command[0]) - sz = command[7] + 8; - else - sz = sg_get_command_size(command[0]); - for (k = 0; k < sz; ++k) - pr2ws("%02x ", command[k]); - pr2ws("]\n"); +void +sg_print_command(const uint8_t * cdbp) +{ + sg_print_command_len(cdbp, 0); } +/* SCSI Status values */ +static const struct sg_lib_simple_value_name_t sstatus_str_arr[] = { + {0x0, "Good"}, + {0x2, "Check Condition"}, + {0x4, "Condition Met"}, + {0x8, "Busy"}, + {0x10, "Intermediate (obsolete)"}, + {0x14, "Intermediate-Condition Met (obsolete)"}, + {0x18, "Reservation Conflict"}, + {0x22, "Command terminated (obsolete)"}, + {0x28, "Task Set Full"}, + {0x30, "ACA Active"}, + {0x40, "Task Aborted"}, + {0xffff, NULL}, +}; + void sg_get_scsi_status_str(int scsi_status, int buff_len, char * buff) { - const char * ccp = NULL; - int unknown = 0; + const struct sg_lib_simple_value_name_t * sstatus_p; if ((NULL == buff) || (buff_len < 1)) return; - else if (1 == buff_len) { + else if (1 == buff_len) { buff[0] = '\0'; return; } scsi_status &= 0x7e; /* sanitize as much as possible */ - switch (scsi_status) { - case 0: ccp = "Good"; break; - case 0x2: ccp = "Check Condition"; break; - case 0x4: ccp = "Condition Met"; break; - case 0x8: ccp = "Busy"; break; - case 0x10: ccp = "Intermediate (obsolete)"; break; - case 0x14: ccp = "Intermediate-Condition Met (obsolete)"; break; - case 0x18: ccp = "Reservation Conflict"; break; - case 0x22: ccp = "Command Terminated (obsolete)"; break; - case 0x28: ccp = "Task set Full"; break; - case 0x30: ccp = "ACA Active"; break; - case 0x40: ccp = "Task Aborted"; break; - default: - unknown = 1; + for (sstatus_p = sstatus_str_arr; sstatus_p->name; ++sstatus_p) { + if (scsi_status == sstatus_p->value) break; } - if (unknown) - my_snprintf(buff, buff_len, "Unknown status [0x%x]", scsi_status); + if (sstatus_p->name) + sg_scnpr(buff, buff_len, "%s", sstatus_p->name); else - my_snprintf(buff, buff_len, "%s", ccp); + sg_scnpr(buff, buff_len, "Unknown status [0x%x]", scsi_status); } void @@ -200,17 +296,17 @@ /* Get sense key from sense buffer. If successful returns a sense key value * between 0 and 15. If sense buffer cannot be decode, returns -1 . */ int -sg_get_sense_key(const unsigned char * sensep, int sense_len) +sg_get_sense_key(const uint8_t * sbp, int sb_len) { - if ((NULL == sensep) || (sense_len < 2)) + if ((NULL == sbp) || (sb_len < 2)) return -1; - switch (sensep[0] & 0x7f) { + switch (sbp[0] & 0x7f) { case 0x70: case 0x71: - return (sense_len < 3) ? -1 : (sensep[2] & 0xf); + return (sb_len < 3) ? -1 : (sbp[2] & 0xf); case 0x72: case 0x73: - return sensep[1] & 0xf; + return sbp[1] & 0xf; default: return -1; } @@ -225,9 +321,9 @@ return buff; } if ((sense_key >= 0) && (sense_key < 16)) - my_snprintf(buff, buff_len, "%s", sg_lib_sense_key_desc[sense_key]); + sg_scnpr(buff, buff_len, "%s", sg_lib_sense_key_desc[sense_key]); else - my_snprintf(buff, buff_len, "invalid value: 0x%x", sense_key); + sg_scnpr(buff, buff_len, "invalid value: 0x%x", sense_key); return buff; } @@ -236,7 +332,7 @@ sg_get_asc_ascq_str(int asc, int ascq, int buff_len, char * buff) { int k, num, rlen; - int found = 0; + bool found = false; struct sg_lib_asc_ascq_t * eip; struct sg_lib_asc_ascq_range_t * ei2p; @@ -249,11 +345,10 @@ if ((ei2p->asc == asc) && (ascq >= ei2p->ascq_min) && (ascq <= ei2p->ascq_max)) { - found = 1; - num = my_snprintf(buff, buff_len, "Additional sense: "); + found = true; + num = sg_scnpr(buff, buff_len, "Additional sense: "); rlen = buff_len - num; - num += my_snprintf(buff + num, ((rlen > 0) ? rlen : 0), - ei2p->text, ascq); + sg_scnpr(buff + num, ((rlen > 0) ? rlen : 0), ei2p->text, ascq); } } if (found) @@ -263,20 +358,19 @@ eip = &sg_lib_asc_ascq[k]; if (eip->asc == asc && eip->ascq == ascq) { - found = 1; - my_snprintf(buff, buff_len, "Additional sense: %s", eip->text); + found = true; + sg_scnpr(buff, buff_len, "Additional sense: %s", eip->text); } } if (! found) { if (asc >= 0x80) - my_snprintf(buff, buff_len, "vendor specific ASC=%02x, " - "ASCQ=%02x (hex)", asc, ascq); + sg_scnpr(buff, buff_len, "vendor specific ASC=%02x, ASCQ=%02x " + "(hex)", asc, ascq); else if (ascq >= 0x80) - my_snprintf(buff, buff_len, "ASC=%02x, vendor specific " - "qualification ASCQ=%02x (hex)", asc, ascq); + sg_scnpr(buff, buff_len, "ASC=%02x, vendor specific qualification " + "ASCQ=%02x (hex)", asc, ascq); else - my_snprintf(buff, buff_len, "ASC=%02x, ASCQ=%02x (hex)", asc, - ascq); + sg_scnpr(buff, buff_len, "ASC=%02x, ASCQ=%02x (hex)", asc, ascq); } return buff; } @@ -284,20 +378,19 @@ /* Attempt to find the first SCSI sense data descriptor that matches the * given 'desc_type'. If found return pointer to start of sense data * descriptor; otherwise (including fixed format sense data) returns NULL. */ -const unsigned char * -sg_scsi_sense_desc_find(const unsigned char * sensep, int sense_len, +const uint8_t * +sg_scsi_sense_desc_find(const uint8_t * sbp, int sb_len, int desc_type) { int add_sb_len, add_d_len, desc_len, k; - const unsigned char * descp; + const uint8_t * descp; - if ((sense_len < 8) || (0 == (add_sb_len = sensep[7]))) + if ((sb_len < 8) || (0 == (add_sb_len = sbp[7]))) return NULL; - if ((sensep[0] < 0x72) || (sensep[0] > 0x73)) + if ((sbp[0] < 0x72) || (sbp[0] > 0x73)) return NULL; - add_sb_len = (add_sb_len < (sense_len - 8)) ? - add_sb_len : (sense_len - 8); - descp = &sensep[8]; + add_sb_len = (add_sb_len < (sb_len - 8)) ? add_sb_len : (sb_len - 8); + descp = &sbp[8]; for (desc_len = 0, k = 0; k < add_sb_len; k += desc_len) { descp += desc_len; add_d_len = (k < (add_sb_len - 1)) ? descp[1]: -1; @@ -310,135 +403,171 @@ return NULL; } -/* Returns 1 if valid bit set, 0 if valid bit clear. Irrespective the +/* Returns true if valid bit set, false if valid bit clear. Irrespective the * information field is written out via 'info_outp' (except when it is * NULL). Handles both fixed and descriptor sense formats. */ -int -sg_get_sense_info_fld(const unsigned char * sensep, int sb_len, +bool +sg_get_sense_info_fld(const uint8_t * sbp, int sb_len, uint64_t * info_outp) { - const unsigned char * ucp; + const uint8_t * bp; uint64_t ull; if (info_outp) *info_outp = 0; if (sb_len < 7) - return 0; - switch (sensep[0] & 0x7f) { + return false; + switch (sbp[0] & 0x7f) { case 0x70: case 0x71: if (info_outp) - *info_outp = sg_get_unaligned_be32(sensep + 3); - return (sensep[0] & 0x80) ? 1 : 0; + *info_outp = sg_get_unaligned_be32(sbp + 3); + return !!(sbp[0] & 0x80); case 0x72: case 0x73: - ucp = sg_scsi_sense_desc_find(sensep, sb_len, 0 /* info desc */); - if (ucp && (0xa == ucp[1])) { - ull = sg_get_unaligned_be64(ucp + 4); + bp = sg_scsi_sense_desc_find(sbp, sb_len, 0 /* info desc */); + if (bp && (0xa == bp[1])) { + ull = sg_get_unaligned_be64(bp + 4); if (info_outp) *info_outp = ull; - return !!(ucp[2] & 0x80); /* since spc3r23 should be set */ + return !!(bp[2] & 0x80); /* since spc3r23 should be set */ } else - return 0; + return false; default: - return 0; + return false; + } +} + +/* Returns true if fixed format or command specific information descriptor + * is found in the descriptor sense; else false. If available the command + * specific information field (4 byte integer in fixed format, 8 byte + * integer in descriptor format) is written out via 'cmd_spec_outp'. + * Handles both fixed and descriptor sense formats. */ +bool +sg_get_sense_cmd_spec_fld(const uint8_t * sbp, int sb_len, + uint64_t * cmd_spec_outp) +{ + const uint8_t * bp; + + if (cmd_spec_outp) + *cmd_spec_outp = 0; + if (sb_len < 7) + return false; + switch (sbp[0] & 0x7f) { + case 0x70: + case 0x71: + if (cmd_spec_outp) + *cmd_spec_outp = sg_get_unaligned_be32(sbp + 8); + return true; + case 0x72: + case 0x73: + bp = sg_scsi_sense_desc_find(sbp, sb_len, + 1 /* command specific info desc */); + if (bp && (0xa == bp[1])) { + if (cmd_spec_outp) + *cmd_spec_outp = sg_get_unaligned_be64(bp + 4); + return true; + } else + return false; + default: + return false; } } -/* Returns 1 if any of the 3 bits (i.e. FILEMARK, EOM or ILI) are set. +/* Returns true if any of the 3 bits (i.e. FILEMARK, EOM or ILI) are set. * In descriptor format if the stream commands descriptor not found - * then returns 0. Writes 1 or 0 corresponding to these bits to the - * last three arguments if they are non-NULL. */ -int -sg_get_sense_filemark_eom_ili(const unsigned char * sensep, int sb_len, - int * filemark_p, int * eom_p, int * ili_p) + * then returns false. Writes true or false corresponding to these bits to + * the last three arguments if they are non-NULL. */ +bool +sg_get_sense_filemark_eom_ili(const uint8_t * sbp, int sb_len, + bool * filemark_p, bool * eom_p, bool * ili_p) { - const unsigned char * ucp; + const uint8_t * bp; if (sb_len < 7) - return 0; - switch (sensep[0] & 0x7f) { + return false; + switch (sbp[0] & 0x7f) { case 0x70: case 0x71: - if (sensep[2] & 0xe0) { + if (sbp[2] & 0xe0) { if (filemark_p) - *filemark_p = !!(sensep[2] & 0x80); + *filemark_p = !!(sbp[2] & 0x80); if (eom_p) - *eom_p = !!(sensep[2] & 0x40); + *eom_p = !!(sbp[2] & 0x40); if (ili_p) - *ili_p = !!(sensep[2] & 0x20); - return 1; + *ili_p = !!(sbp[2] & 0x20); + return true; } else - return 0; + return false; case 0x72: case 0x73: /* Look for stream commands sense data descriptor */ - ucp = sg_scsi_sense_desc_find(sensep, sb_len, 4); - if (ucp && (ucp[1] >= 2)) { - if (ucp[3] & 0xe0) { + bp = sg_scsi_sense_desc_find(sbp, sb_len, 4); + if (bp && (bp[1] >= 2)) { + if (bp[3] & 0xe0) { if (filemark_p) - *filemark_p = !!(ucp[3] & 0x80); + *filemark_p = !!(bp[3] & 0x80); if (eom_p) - *eom_p = !!(ucp[3] & 0x40); + *eom_p = !!(bp[3] & 0x40); if (ili_p) - *ili_p = !!(ucp[3] & 0x20); - return 1; + *ili_p = !!(bp[3] & 0x20); + return true; } } - return 0; + return false; default: - return 0; + return false; } } -/* Returns 1 if SKSV is set and sense key is NO_SENSE or NOT_READY. Also - * returns 1 if progress indication sense data descriptor found. Places +/* Returns true if SKSV is set and sense key is NO_SENSE or NOT_READY. Also + * returns true if progress indication sense data descriptor found. Places * progress field from sense data where progress_outp points. If progress - * field is not available returns 0 and *progress_outp is unaltered. Handles - * both fixed and descriptor sense formats. - * Hint: if 1 is returned *progress_outp may be multiplied by 100 then + * field is not available returns false and *progress_outp is unaltered. + * Handles both fixed and descriptor sense formats. + * Hint: if true is returned *progress_outp may be multiplied by 100 then * divided by 65536 to get the percentage completion. */ -int -sg_get_sense_progress_fld(const unsigned char * sensep, int sb_len, +bool +sg_get_sense_progress_fld(const uint8_t * sbp, int sb_len, int * progress_outp) { - const unsigned char * ucp; + const uint8_t * bp; int sk, sk_pr; if (sb_len < 7) - return 0; - switch (sensep[0] & 0x7f) { + return false; + switch (sbp[0] & 0x7f) { case 0x70: case 0x71: - sk = (sensep[2] & 0xf); + sk = (sbp[2] & 0xf); if ((sb_len < 18) || ((SPC_SK_NO_SENSE != sk) && (SPC_SK_NOT_READY != sk))) - return 0; - if (sensep[15] & 0x80) { /* SKSV bit set */ + return false; + if (sbp[15] & 0x80) { /* SKSV bit set */ if (progress_outp) - *progress_outp = sg_get_unaligned_be16(sensep + 16); - return 1; + *progress_outp = sg_get_unaligned_be16(sbp + 16); + return true; } else - return 0; + return false; case 0x72: case 0x73: /* sense key specific progress (0x2) or progress descriptor (0xa) */ - sk = (sensep[1] & 0xf); + sk = (sbp[1] & 0xf); sk_pr = (SPC_SK_NO_SENSE == sk) || (SPC_SK_NOT_READY == sk); - if (sk_pr && ((ucp = sg_scsi_sense_desc_find(sensep, sb_len, 2))) && - (0x6 == ucp[1]) && (0x80 & ucp[4])) { + if (sk_pr && ((bp = sg_scsi_sense_desc_find(sbp, sb_len, 2))) && + (0x6 == bp[1]) && (0x80 & bp[4])) { if (progress_outp) - *progress_outp = sg_get_unaligned_be16(ucp + 5); - return 1; - } else if (((ucp = sg_scsi_sense_desc_find(sensep, sb_len, 0xa))) && - ((0x6 == ucp[1]))) { + *progress_outp = sg_get_unaligned_be16(bp + 5); + return true; + } else if (((bp = sg_scsi_sense_desc_find(sbp, sb_len, 0xa))) && + ((0x6 == bp[1]))) { if (progress_outp) - *progress_outp = sg_get_unaligned_be16(ucp + 6); - return 1; + *progress_outp = sg_get_unaligned_be16(bp + 6); + return true; } else - return 0; + return false; default: - return 0; + return false; } } @@ -446,9 +575,9 @@ sg_get_pdt_str(int pdt, int buff_len, char * buff) { if ((pdt < 0) || (pdt > 31)) - my_snprintf(buff, buff_len, "bad pdt"); + sg_scnpr(buff, buff_len, "bad pdt"); else - my_snprintf(buff, buff_len, "%s", sg_lib_pdt_strs[pdt]); + sg_scnpr(buff, buff_len, "%s", sg_lib_pdt_strs[pdt]); return buff; } @@ -456,7 +585,7 @@ sg_lib_pdt_decay(int pdt) { if ((pdt < 0) || (pdt > 31)) - return pdt; + return 0; return sg_lib_pdt_decay_arr[pdt]; } @@ -464,12 +593,176 @@ sg_get_trans_proto_str(int tpi, int buff_len, char * buff) { if ((tpi < 0) || (tpi > 15)) - my_snprintf(buff, buff_len, "bad tpi"); + sg_scnpr(buff, buff_len, "bad tpi"); else - my_snprintf(buff, buff_len, "%s", sg_lib_transport_proto_strs[tpi]); + sg_scnpr(buff, buff_len, "%s", sg_lib_transport_proto_strs[tpi]); return buff; } +#define TRANSPORT_ID_MIN_LEN 24 + +char * +sg_decode_transportid_str(const char * lip, uint8_t * bp, int bplen, + bool only_one, int blen, char * b) +{ + int proto_id, num, k, n, normal_len, tpid_format; + uint64_t ull; + int bump; + + if ((NULL == b) || (blen < 1)) + return b; + else if (1 == blen) { + b[0] = '\0'; + return b; + } + if (NULL == lip) + lip = ""; + /* bump = TRANSPORT_ID_MIN_LEN; // some old compilers insisted on this */ + for (k = 0, n = 0; bplen > 0; ++k, bp += bump, bplen -= bump) { + if ((k > 0) && only_one) + break; + if ((bplen < 24) || (0 != (bplen % 4))) + n += sg_scnpr(b + n, blen - n, "%sTransport Id short or not " + "multiple of 4 [length=%d]:\n", lip, blen); + else + n += sg_scnpr(b + n, blen - n, "%sTransport Id of initiator:\n", + lip); + tpid_format = ((bp[0] >> 6) & 0x3); + proto_id = (bp[0] & 0xf); + normal_len = (bplen > TRANSPORT_ID_MIN_LEN) ? + TRANSPORT_ID_MIN_LEN : bplen; + switch (proto_id) { + case TPROTO_FCP: /* Fibre channel */ + n += sg_scnpr(b + n, blen - n, "%s FCP-2 World Wide Name:\n", + lip); + if (0 != tpid_format) + n += sg_scnpr(b + n, blen - n, "%s [Unexpected TPID format: " + "%d]\n", lip, tpid_format); + n += hex2str(bp + 8, 8, lip, 1, blen - n, b + n); + bump = TRANSPORT_ID_MIN_LEN; + break; + case TPROTO_SPI: /* Scsi Parallel Interface, obsolete */ + n += sg_scnpr(b + n, blen - n, "%s Parallel SCSI initiator SCSI " + "address: 0x%x\n", lip, + sg_get_unaligned_be16(bp + 2)); + if (0 != tpid_format) + n += sg_scnpr(b + n, blen - n, "%s [Unexpected TPID format: " + "%d]\n", lip, tpid_format); + n += sg_scnpr(b + n, blen - n, "%s relative port number (of " + "corresponding target): 0x%x\n", lip, + sg_get_unaligned_be16(bp + 6)); + bump = TRANSPORT_ID_MIN_LEN; + break; + case TPROTO_SSA: + n += sg_scnpr(b + n, blen - n, "%s SSA (transport id not " + "defined):\n", lip); + n += sg_scnpr(b + n, blen - n, "%s TPID format: %d\n", lip, + tpid_format); + n += hex2str(bp, normal_len, lip, 1, blen - n, b + n); + bump = TRANSPORT_ID_MIN_LEN; + break; + case TPROTO_1394: /* IEEE 1394 */ + n += sg_scnpr(b + n, blen - n, "%s IEEE 1394 EUI-64 name:\n", + lip); + if (0 != tpid_format) + n += sg_scnpr(b + n, blen - n, "%s [Unexpected TPID format: " + "%d]\n", lip, tpid_format); + n += hex2str(&bp[8], 8, lip, 1, blen - n, b + n); + bump = TRANSPORT_ID_MIN_LEN; + break; + case TPROTO_SRP: /* SCSI over RDMA */ + n += sg_scnpr(b + n, blen - n, "%s RDMA initiator port " + "identifier:\n", lip); + if (0 != tpid_format) + n += sg_scnpr(b + n, blen - n, "%s [Unexpected TPID format: " + "%d]\n", lip, tpid_format); + n += hex2str(bp + 8, 16, lip, 1, blen - n, b + n); + bump = TRANSPORT_ID_MIN_LEN; + break; + case TPROTO_ISCSI: + n += sg_scnpr(b + n, blen - n, "%s iSCSI ", lip); + num = sg_get_unaligned_be16(bp + 2); + if (0 == tpid_format) + n += sg_scnpr(b + n, blen - n, "name: %.*s\n", num, &bp[4]); + else if (1 == tpid_format) + n += sg_scnpr(b + n, blen - n, "world wide unique port id: " + "%.*s\n", num, &bp[4]); + else { + n += sg_scnpr(b + n, blen - n, " [Unexpected TPID format: " + "%d]\n", tpid_format); + n += hex2str(bp, num + 4, lip, 0, blen - n, b + n); + } + bump = (((num + 4) < TRANSPORT_ID_MIN_LEN) ? + TRANSPORT_ID_MIN_LEN : num + 4); + break; + case TPROTO_SAS: + ull = sg_get_unaligned_be64(bp + 4); + n += sg_scnpr(b + n, blen - n, "%s SAS address: 0x%" PRIx64 "\n", + lip, ull); + if (0 != tpid_format) + n += sg_scnpr(b + n, blen - n, "%s [Unexpected TPID format: " + "%d]\n", lip, tpid_format); + bump = TRANSPORT_ID_MIN_LEN; + break; + case TPROTO_ADT: /* no TransportID defined by T10 yet */ + n += sg_scnpr(b + n, blen - n, "%s ADT:\n", lip); + n += sg_scnpr(b + n, blen - n, "%s TPID format: %d\n", lip, + tpid_format); + n += hex2str(bp, normal_len, lip, 1, blen - n, b + n); + bump = TRANSPORT_ID_MIN_LEN; + break; + case TPROTO_ATA: /* no TransportID defined by T10 yet */ + n += sg_scnpr(b + n, blen - n, "%s ATAPI:\n", lip); + n += sg_scnpr(b + n, blen - n, "%s TPID format: %d\n", lip, + tpid_format); + n += hex2str(bp, normal_len, lip, 1, blen - n, b + n); + bump = TRANSPORT_ID_MIN_LEN; + break; + case TPROTO_UAS: /* no TransportID defined by T10 yet */ + n += sg_scnpr(b + n, blen - n, "%s UAS:\n", lip); + n += sg_scnpr(b + n, blen - n, "%s TPID format: %d\n", lip, + tpid_format); + n += hex2str(bp, normal_len, lip, 1, blen - n, b + n); + bump = TRANSPORT_ID_MIN_LEN; + break; + case TPROTO_SOP: + n += sg_scnpr(b + n, blen - n, "%s SOP ", lip); + num = sg_get_unaligned_be16(bp + 2); + if (0 == tpid_format) + n += sg_scnpr(b + n, blen - n, "Routing ID: 0x%x\n", num); + else { + n += sg_scnpr(b + n, blen - n, " [Unexpected TPID format: " + "%d]\n", tpid_format); + n += hex2str(bp, normal_len, lip, 1, blen - n, b + n); + } + bump = TRANSPORT_ID_MIN_LEN; + break; + case TPROTO_PCIE: /* no TransportID defined by T10 yet */ + n += sg_scnpr(b + n, blen - n, "%s PCIE:\n", lip); + n += sg_scnpr(b + n, blen - n, "%s TPID format: %d\n", lip, + tpid_format); + n += hex2str(bp, normal_len, lip, 1, blen - n, b + n); + bump = TRANSPORT_ID_MIN_LEN; + break; + case TPROTO_NONE: /* no TransportID defined by T10 */ + n += sg_scnpr(b + n, blen - n, "%s No specified protocol\n", + lip); + /* n += hex2str(bp, ((bplen > 24) ? 24 : bplen), + * lip, 0, blen - n, b + n); */ + bump = TRANSPORT_ID_MIN_LEN; + break; + default: + n += sg_scnpr(b + n, blen - n, "%s unknown protocol id=0x%x " + "TPID format=%d\n", lip, proto_id, tpid_format); + n += hex2str(bp, normal_len, lip, 1, blen - n, b + n); + bump = TRANSPORT_ID_MIN_LEN; + break; + } + } + return b; +} + + static const char * desig_code_set_str_arr[] = { "Reserved [0x0]", @@ -484,7 +777,7 @@ const char * sg_get_desig_code_set_str(int val) { - if ((val >= 0) && (val < 16)) + if ((val >= 0) && (val < (int)SG_ARRAY_SIZE(desig_code_set_str_arr))) return desig_code_set_str_arr[val]; else return NULL; @@ -501,7 +794,7 @@ const char * sg_get_desig_assoc_str(int val) { - if ((val >= 0) && (val < 4)) + if ((val >= 0) && (val < (int)SG_ARRAY_SIZE(desig_assoc_str_arr))) return desig_assoc_str_arr[val]; else return NULL; @@ -527,40 +820,88 @@ const char * sg_get_desig_type_str(int val) { - if ((val >= 0) && (val < 16)) + if ((val >= 0) && (val < (int)SG_ARRAY_SIZE(desig_type_str_arr))) return desig_type_str_arr[val]; else return NULL; } +/* Expects a T10 UUID designator (as found in the Device Identification VPD + * page) pointed to by 'dp'. To not produce an error string in 'b', c_set + * should be 1 (binary) and dlen should be 18. Currently T10 only supports + * locally assigned UUIDs. Writes output to string 'b' of no more than blen + * bytes and returns the number of bytes actually written to 'b' but doesn't + * count the trailing null character it always appends (if blen > 0). 'lip' + * is lead-in string (on each line) than may be NULL. skip_prefix avoids + * outputting: ' Locally assigned UUID: ' before the UUID. */ +int +sg_t10_uuid_desig2str(const uint8_t *dp, int dlen, int c_set, bool do_long, + bool skip_prefix, const char * lip /* lead-in */, + int blen, char * b) +{ + int m; + int n = 0; + + if (NULL == lip) + lip = ""; + if (1 != c_set) { + n += sg_scnpr(b + n, blen - n, "%s << expected binary " + "code_set >>\n", lip); + n += hex2str(dp, dlen, lip, 0, blen - n, b + n); + return n; + } + if ((1 != ((dp[0] >> 4) & 0xf)) || (18 != dlen)) { + n += sg_scnpr(b + n, blen - n, "%s << expected locally " + "assigned UUID, 16 bytes long >>\n", lip); + n += hex2str(dp, dlen, lip, 0, blen - n, b + n); + return n; + } + if (skip_prefix) { + if (strlen(lip) > 0) + n += sg_scnpr(b + n, blen - n, "%s", lip); + } else + n += sg_scnpr(b + n, blen - n, "%s Locally assigned UUID: ", + lip); + for (m = 0; m < 16; ++m) { + if ((4 == m) || (6 == m) || (8 == m) || (10 == m)) + n += sg_scnpr(b + n, blen - n, "-"); + n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)dp[2 + m]); + } + n += sg_scnpr(b + n, blen - n, "\n"); + if (do_long) { + n += sg_scnpr(b + n, blen - n, "%s [0x", lip); + for (m = 0; m < 16; ++m) + n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)dp[2 + m]); + n += sg_scnpr(b + n, blen - n, "]\n"); + } + return n; +} + int -sg_get_designation_descriptor_str(const char * leadin, - const unsigned char * ddp, int dd_len, - int print_assoc, int do_long, int blen, - char * b) +sg_get_designation_descriptor_str(const char * lip, const uint8_t * ddp, + int dd_len, bool print_assoc, bool do_long, + int blen, char * b) { int m, p_id, piv, c_set, assoc, desig_type, ci_off, c_id, d_id, naa; int vsi, k, n, dlen; - const unsigned char * ip; + const uint8_t * ip; uint64_t vsei; uint64_t id_ext; char e[64]; const char * cp; - const char * lip = ""; n = 0; - if (leadin) - lip = leadin; + if (NULL == lip) + lip = ""; if (dd_len < 4) { - n += my_snprintf(b + n, blen - n, "%sdesignator desc too short: " - "got length of %d want 4 or more\n", lip, dd_len); + n += sg_scnpr(b + n, blen - n, "%sdesignator desc too short: got " + "length of %d want 4 or more\n", lip, dd_len); return n; } dlen = ddp[3]; if (dlen > (dd_len - 4)) { - n += my_snprintf(b + n, blen - n, "%sdesignator too long: says it " - "is %d bytes, but given %d bytes\n", lip, dlen, - dd_len - 4); + n += sg_scnpr(b + n, blen - n, "%sdesignator too long: says it is %d " + "bytes, but given %d bytes\n", lip, dlen, dd_len - 4); return n; } ip = ddp + 4; @@ -570,167 +911,159 @@ assoc = ((ddp[1] >> 4) & 0x3); desig_type = (ddp[1] & 0xf); if (print_assoc && ((cp = sg_get_desig_assoc_str(assoc)))) - n += my_snprintf(b + n, blen - n, "%s %s:\n", lip, cp); - n += my_snprintf(b + n, blen - n, "%s designator type: ", lip); + n += sg_scnpr(b + n, blen - n, "%s %s:\n", lip, cp); + n += sg_scnpr(b + n, blen - n, "%s designator type: ", lip); cp = sg_get_desig_type_str(desig_type); if (cp) - n += my_snprintf(b + n, blen - n, "%s", cp); - n += my_snprintf(b + n, blen - n, ", code set: "); + n += sg_scnpr(b + n, blen - n, "%s", cp); + n += sg_scnpr(b + n, blen - n, ", code set: "); cp = sg_get_desig_code_set_str(c_set); if (cp) - n += my_snprintf(b + n, blen - n, "%s", cp); - n += my_snprintf(b + n, blen - n, "\n"); + n += sg_scnpr(b + n, blen - n, "%s", cp); + n += sg_scnpr(b + n, blen - n, "\n"); if (piv && ((1 == assoc) || (2 == assoc))) - n += my_snprintf(b + n, blen - n, "%s transport: %s\n", lip, - sg_get_trans_proto_str(p_id, sizeof(e), e)); + n += sg_scnpr(b + n, blen - n, "%s transport: %s\n", lip, + sg_get_trans_proto_str(p_id, sizeof(e), e)); /* printf(" associated with the %s\n", sdparm_assoc_arr[assoc]); */ switch (desig_type) { case 0: /* vendor specific */ k = 0; if ((1 == c_set) || (2 == c_set)) { /* ASCII or UTF-8 */ - for (k = 0; (k < dlen) && isprint(ip[k]); ++k) + for (k = 0; (k < dlen) && my_isprint(ip[k]); ++k) ; if (k >= dlen) k = 1; } if (k) - n += my_snprintf(b + n, blen - n, "%s vendor specific: " - "%.*s\n", lip, dlen, ip); + n += sg_scnpr(b + n, blen - n, "%s vendor specific: %.*s\n", + lip, dlen, ip); else { - n += my_snprintf(b + n, blen - n, "%s vendor specific:\n", - lip); - n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n); + n += sg_scnpr(b + n, blen - n, "%s vendor specific:\n", lip); + n += hex2str(ip, dlen, lip, 0, blen - n, b + n); } break; case 1: /* T10 vendor identification */ - n += my_snprintf(b + n, blen - n, "%s vendor id: %.8s\n", lip, - ip); + n += sg_scnpr(b + n, blen - n, "%s vendor id: %.8s\n", lip, ip); if (dlen > 8) { if ((2 == c_set) || (3 == c_set)) { /* ASCII or UTF-8 */ - n += my_snprintf(b + n, blen - n, "%s vendor specific: " - "%.*s\n", lip, dlen - 8, ip + 8); + n += sg_scnpr(b + n, blen - n, "%s vendor specific: " + "%.*s\n", lip, dlen - 8, ip + 8); } else { - n += my_snprintf(b + n, blen - n, "%s vendor specific: " - "0x", lip); + n += sg_scnpr(b + n, blen - n, "%s vendor specific: 0x", + lip); for (m = 8; m < dlen; ++m) - n += my_snprintf(b + n, blen - n, "%02x", - (unsigned int)ip[m]); - n += my_snprintf(b + n, blen - n, "\n"); + n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)ip[m]); + n += sg_scnpr(b + n, blen - n, "\n"); } } break; case 2: /* EUI-64 based */ if (! do_long) { if ((8 != dlen) && (12 != dlen) && (16 != dlen)) { - n += my_snprintf(b + n, blen - n, "%s << expect 8, 12 " - "and 16 byte EUI, got %d >>\n", lip, dlen); - n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, - b + n); + n += sg_scnpr(b + n, blen - n, "%s << expect 8, 12 and " + "16 byte EUI, got %d >>\n", lip, dlen); + n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } - n += my_snprintf(b + n, blen - n, "%s 0x", lip); + n += sg_scnpr(b + n, blen - n, "%s 0x", lip); for (m = 0; m < dlen; ++m) - n += my_snprintf(b + n, blen - n, "%02x", (unsigned int)ip[m]); - n += my_snprintf(b + n, blen - n, "\n"); + n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)ip[m]); + n += sg_scnpr(b + n, blen - n, "\n"); break; } - n += my_snprintf(b + n, blen - n, "%s EUI-64 based %d byte " - "identifier\n", lip, dlen); + n += sg_scnpr(b + n, blen - n, "%s EUI-64 based %d byte " + "identifier\n", lip, dlen); if (1 != c_set) { - n += my_snprintf(b + n, blen - n, "%s << expected binary " - "code_set (1) >>\n", lip); - n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n); + n += sg_scnpr(b + n, blen - n, "%s << expected binary " + "code_set (1) >>\n", lip); + n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } ci_off = 0; - if (16 == dlen) { + if (16 == dlen) { /* first 8 bytes are 'Identifier Extension' */ ci_off = 8; id_ext = sg_get_unaligned_be64(ip); - n += my_snprintf(b + n, blen - n, "%s Identifier extension: " - "0x%" PRIx64 "\n", lip, id_ext); + n += sg_scnpr(b + n, blen - n, "%s Identifier extension: 0x%" + PRIx64 "\n", lip, id_ext); } else if ((8 != dlen) && (12 != dlen)) { - n += my_snprintf(b + n, blen - n, "%s << can only decode 8, " - "12 and 16 byte ids >>\n", lip); - n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n); + n += sg_scnpr(b + n, blen - n, "%s << can only decode 8, 12 " + "and 16 byte ids >>\n", lip); + n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } c_id = sg_get_unaligned_be24(ip + ci_off); - n += my_snprintf(b + n, blen - n, "%s IEEE Company_id: 0x%x\n", - lip, c_id); + n += sg_scnpr(b + n, blen - n, "%s IEEE Company_id: 0x%x\n", lip, + c_id); vsei = 0; for (m = 0; m < 5; ++m) { if (m > 0) vsei <<= 8; vsei |= ip[ci_off + 3 + m]; } - n += my_snprintf(b + n, blen - n, "%s Vendor Specific Extension " - "Identifier: 0x%" PRIx64 "\n", lip, vsei); + n += sg_scnpr(b + n, blen - n, "%s Vendor Specific Extension " + "Identifier: 0x%" PRIx64 "\n", lip, vsei); if (12 == dlen) { d_id = sg_get_unaligned_be32(ip + 8); - n += my_snprintf(b + n, blen - n, "%s Directory ID: 0x%x\n", - lip, d_id); + n += sg_scnpr(b + n, blen - n, "%s Directory ID: 0x%x\n", + lip, d_id); } break; case 3: /* NAA */ if (1 != c_set) { - n += my_snprintf(b + n, blen - n, "%s << unexpected code " - "set %d for NAA >>\n", lip, c_set); - n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n); + n += sg_scnpr(b + n, blen - n, "%s << unexpected code set " + "%d for NAA >>\n", lip, c_set); + n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } naa = (ip[0] >> 4) & 0xff; switch (naa) { case 2: /* NAA 2: IEEE Extended */ if (8 != dlen) { - n += my_snprintf(b + n, blen - n, "%s << unexpected NAA " - "2 identifier length: 0x%x >>\n", lip, dlen); - n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, - b + n); + n += sg_scnpr(b + n, blen - n, "%s << unexpected NAA 2 " + "identifier length: 0x%x >>\n", lip, dlen); + n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } d_id = (((ip[0] & 0xf) << 8) | ip[1]); c_id = sg_get_unaligned_be24(ip + 2); vsi = sg_get_unaligned_be24(ip + 5); if (do_long) { - n += my_snprintf(b + n, blen - n, "%s NAA 2, vendor " - "specific identifier A: 0x%x\n", lip, d_id); - n += my_snprintf(b + n, blen - n, "%s IEEE Company_id: " - "0x%x\n", lip, c_id); - n += my_snprintf(b + n, blen - n, "%s vendor specific " - "identifier B: 0x%x\n", lip, vsi); - n += my_snprintf(b + n, blen - n, "%s [0x", lip); + n += sg_scnpr(b + n, blen - n, "%s NAA 2, vendor " + "specific identifier A: 0x%x\n", lip, d_id); + n += sg_scnpr(b + n, blen - n, "%s IEEE Company_id: " + "0x%x\n", lip, c_id); + n += sg_scnpr(b + n, blen - n, "%s vendor specific " + "identifier B: 0x%x\n", lip, vsi); + n += sg_scnpr(b + n, blen - n, "%s [0x", lip); for (m = 0; m < 8; ++m) - n += my_snprintf(b + n, blen - n, "%02x", - (unsigned int)ip[m]); - n += my_snprintf(b + n, blen - n, "]\n"); + n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)ip[m]); + n += sg_scnpr(b + n, blen - n, "]\n"); } - n += my_snprintf(b + n, blen - n, "%s 0x", lip); + n += sg_scnpr(b + n, blen - n, "%s 0x", lip); for (m = 0; m < 8; ++m) - n += my_snprintf(b + n, blen - n, "%02x", (unsigned int)ip[m]); - n += my_snprintf(b + n, blen - n, "\n"); + n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)ip[m]); + n += sg_scnpr(b + n, blen - n, "\n"); break; case 3: /* NAA 3: Locally assigned */ if (8 != dlen) { - n += my_snprintf(b + n, blen - n, "%s << unexpected NAA " - "3 identifier length: 0x%x >>\n", lip, dlen); - n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, - b + n); + n += sg_scnpr(b + n, blen - n, "%s << unexpected NAA 3 " + "identifier length: 0x%x >>\n", lip, dlen); + n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } if (do_long) - n += my_snprintf(b + n, blen - n, "%s NAA 3, Locally " - "assigned:\n", lip); - n += my_snprintf(b + n, blen - n, "%s 0x", lip); + n += sg_scnpr(b + n, blen - n, "%s NAA 3, Locally " + "assigned:\n", lip); + n += sg_scnpr(b + n, blen - n, "%s 0x", lip); for (m = 0; m < 8; ++m) - n += my_snprintf(b + n, blen - n, "%02x", (unsigned int)ip[m]); - n += my_snprintf(b + n, blen - n, "\n"); + n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)ip[m]); + n += sg_scnpr(b + n, blen - n, "\n"); break; case 5: /* NAA 5: IEEE Registered */ if (8 != dlen) { - n += my_snprintf(b + n, blen - n, "%s << unexpected NAA " - "5 identifier length: 0x%x >>\n", lip, dlen); - n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, - b + n); + n += sg_scnpr(b + n, blen - n, "%s << unexpected NAA 5 " + "identifier length: 0x%x >>\n", lip, dlen); + n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } c_id = (((ip[0] & 0xf) << 20) | (ip[1] << 12) | @@ -741,29 +1074,26 @@ vsei |= ip[3 + m]; } if (do_long) { - n += my_snprintf(b + n, blen - n, "%s NAA 5, IEEE " - "Company_id: 0x%x\n", lip, c_id); - n += my_snprintf(b + n, blen - n, "%s Vendor Specific " - "Identifier: 0x%" PRIx64 "\n", lip, vsei); - n += my_snprintf(b + n, blen - n, "%s [0x", lip); + n += sg_scnpr(b + n, blen - n, "%s NAA 5, IEEE " + "Company_id: 0x%x\n", lip, c_id); + n += sg_scnpr(b + n, blen - n, "%s Vendor Specific " + "Identifier: 0x%" PRIx64 "\n", lip, vsei); + n += sg_scnpr(b + n, blen - n, "%s [0x", lip); for (m = 0; m < 8; ++m) - n += my_snprintf(b + n, blen - n, "%02x", - (unsigned int)ip[m]); - n += my_snprintf(b + n, blen - n, "]\n"); + n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)ip[m]); + n += sg_scnpr(b + n, blen - n, "]\n"); } else { - n += my_snprintf(b + n, blen - n, "%s 0x", lip); + n += sg_scnpr(b + n, blen - n, "%s 0x", lip); for (m = 0; m < 8; ++m) - n += my_snprintf(b + n, blen - n, "%02x", - (unsigned int)ip[m]); - n += my_snprintf(b + n, blen - n, "\n"); + n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)ip[m]); + n += sg_scnpr(b + n, blen - n, "\n"); } break; case 6: /* NAA 6: IEEE Registered extended */ if (16 != dlen) { - n += my_snprintf(b + n, blen - n, "%s << unexpected NAA " - "6 identifier length: 0x%x >>\n", lip, dlen); - n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, - b + n); + n += sg_scnpr(b + n, blen - n, "%s << unexpected NAA 6 " + "identifier length: 0x%x >>\n", lip, dlen); + n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } c_id = (((ip[0] & 0xf) << 20) | (ip[1] << 12) | @@ -774,240 +1104,212 @@ vsei |= ip[3 + m]; } if (do_long) { - n += my_snprintf(b + n, blen - n, "%s NAA 6, IEEE " - "Company_id: 0x%x\n", lip, c_id); - n += my_snprintf(b + n, blen - n, "%s Vendor Specific " - "Identifier: 0x%" PRIx64 "\n", lip, vsei); + n += sg_scnpr(b + n, blen - n, "%s NAA 6, IEEE " + "Company_id: 0x%x\n", lip, c_id); + n += sg_scnpr(b + n, blen - n, "%s Vendor Specific " + "Identifier: 0x%" PRIx64 "\n", lip, vsei); vsei = sg_get_unaligned_be64(ip + 8); - n += my_snprintf(b + n, blen - n, "%s Vendor Specific " - "Identifier Extension: 0x%" PRIx64 "\n", lip, - vsei); - n += my_snprintf(b + n, blen - n, "%s [0x", lip); + n += sg_scnpr(b + n, blen - n, "%s Vendor Specific " + "Identifier Extension: 0x%" PRIx64 "\n", lip, + vsei); + n += sg_scnpr(b + n, blen - n, "%s [0x", lip); for (m = 0; m < 16; ++m) - n += my_snprintf(b + n, blen - n, "%02x", - (unsigned int)ip[m]); - n += my_snprintf(b + n, blen - n, "]\n"); + n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)ip[m]); + n += sg_scnpr(b + n, blen - n, "]\n"); } else { - n += my_snprintf(b + n, blen - n, "%s 0x", lip); + n += sg_scnpr(b + n, blen - n, "%s 0x", lip); for (m = 0; m < 16; ++m) - n += my_snprintf(b + n, blen - n, "%02x", - (unsigned int)ip[m]); - n += my_snprintf(b + n, blen - n, "\n"); + n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)ip[m]); + n += sg_scnpr(b + n, blen - n, "\n"); } break; default: - n += my_snprintf(b + n, blen - n, "%s << unexpected NAA " - "[0x%x] >>\n", lip, naa); - n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n); + n += sg_scnpr(b + n, blen - n, "%s << unexpected NAA [0x%x] " + ">>\n", lip, naa); + n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } break; case 4: /* Relative target port */ if ((1 != c_set) || (1 != assoc) || (4 != dlen)) { - n += my_snprintf(b + n, blen - n, "%s << expected binary " - "code_set, target port association, length 4 " - ">>\n", lip); - n += dStrHexStr((const char *)ip, dlen, "", 0, blen - n, b + n); + n += sg_scnpr(b + n, blen - n, "%s << expected binary " + "code_set, target port association, length 4 >>\n", + lip); + n += hex2str(ip, dlen, "", 1, blen - n, b + n); break; } d_id = sg_get_unaligned_be16(ip + 2); - n += my_snprintf(b + n, blen - n, "%s Relative target port: " - "0x%x\n", lip, d_id); + n += sg_scnpr(b + n, blen - n, "%s Relative target port: 0x%x\n", + lip, d_id); break; case 5: /* (primary) Target port group */ if ((1 != c_set) || (1 != assoc) || (4 != dlen)) { - n += my_snprintf(b + n, blen - n, "%s << expected binary " - "code_set, target port association, length 4 " - ">>\n", lip); - n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n); + n += sg_scnpr(b + n, blen - n, "%s << expected binary " + "code_set, target port association, length 4 >>\n", + lip); + n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } d_id = sg_get_unaligned_be16(ip + 2); - n += my_snprintf(b + n, blen - n, "%s Target port group: 0x%x\n", - lip, d_id); + n += sg_scnpr(b + n, blen - n, "%s Target port group: 0x%x\n", + lip, d_id); break; case 6: /* Logical unit group */ if ((1 != c_set) || (0 != assoc) || (4 != dlen)) { - n += my_snprintf(b + n, blen - n, "%s << expected binary " - "code_set, logical unit association, length " - "4 >>\n", lip); - n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n); + n += sg_scnpr(b + n, blen - n, "%s << expected binary " + "code_set, logical unit association, length 4 >>\n", + lip); + n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } d_id = sg_get_unaligned_be16(ip + 2); - n += my_snprintf(b + n, blen - n, "%s Logical unit group: " - "0x%x\n", lip, d_id); + n += sg_scnpr(b + n, blen - n, "%s Logical unit group: 0x%x\n", + lip, d_id); break; case 7: /* MD5 logical unit identifier */ if ((1 != c_set) || (0 != assoc)) { - n += my_snprintf(b + n, blen - n, "%s << expected binary " - "code_set, logical unit association >>\n", lip); - n += dStrHexStr((const char *)ip, dlen, "", 0, blen - n, b + n); + n += sg_scnpr(b + n, blen - n, "%s << expected binary " + "code_set, logical unit association >>\n", lip); + n += hex2str(ip, dlen, "", 1, blen - n, b + n); break; } - n += my_snprintf(b + n, blen - n, "%s MD5 logical unit " - "identifier:\n", lip); - n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n); + n += sg_scnpr(b + n, blen - n, "%s MD5 logical unit " + "identifier:\n", lip); + n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; case 8: /* SCSI name string */ - if (3 != c_set) { - n += my_snprintf(b + n, blen - n, "%s << expected UTF-8 " - "code_set >>\n", lip); - n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n); - break; + if (3 != c_set) { /* accept ASCII as subset of UTF-8 */ + if (2 == c_set) { + if (do_long) + n += sg_scnpr(b + n, blen - n, "%s << expected " + "UTF-8, use ASCII >>\n", lip); + } else { + n += sg_scnpr(b + n, blen - n, "%s << expected UTF-8 " + "code_set >>\n", lip); + n += hex2str(ip, dlen, lip, 0, blen - n, b + n); + break; + } } - n += my_snprintf(b + n, blen - n, "%s SCSI name string:\n", lip); + n += sg_scnpr(b + n, blen - n, "%s SCSI name string:\n", lip); /* does %s print out UTF-8 ok?? * Seems to depend on the locale. Looks ok here with my * locale setting: en_AU.UTF-8 */ - n += my_snprintf(b + n, blen - n, "%s %s\n", lip, - (const char *)ip); + n += sg_scnpr(b + n, blen - n, "%s %.*s\n", lip, dlen, + (const char *)ip); break; case 9: /* Protocol specific port identifier */ /* added in spc4r36, PIV must be set, proto_id indicates */ /* whether UAS (USB) or SOP (PCIe) or ... */ if (! piv) - n += my_snprintf(b + n, blen - n, " %s >>>> Protocol " - "specific port identifier expects protocol\n" - "%s identifier to be valid and it is " - "not\n", lip, lip); + n += sg_scnpr(b + n, blen - n, " %s >>>> Protocol specific " + "port identifier expects protocol\n%s " + "identifier to be valid and it is not\n", lip, lip); if (TPROTO_UAS == p_id) { - n += my_snprintf(b + n, blen - n, "%s USB device address: " - "0x%x\n", lip, 0x7f & ip[0]); - n += my_snprintf(b + n, blen - n, "%s USB interface number: " - "0x%x\n", lip, ip[2]); + n += sg_scnpr(b + n, blen - n, "%s USB device address: " + "0x%x\n", lip, 0x7f & ip[0]); + n += sg_scnpr(b + n, blen - n, "%s USB interface number: " + "0x%x\n", lip, ip[2]); } else if (TPROTO_SOP == p_id) { - n += my_snprintf(b + n, blen - n, "%s PCIe routing ID, bus " - "number: 0x%x\n", lip, ip[0]); - n += my_snprintf(b + n, blen - n, "%s function number: " - "0x%x\n", lip, ip[1]); - n += my_snprintf(b + n, blen - n, "%s [or device " - "number: 0x%x, function number: 0x%x]\n", lip, - (0x1f & (ip[1] >> 3)), 0x7 & ip[1]); + n += sg_scnpr(b + n, blen - n, "%s PCIe routing ID, bus " + "number: 0x%x\n", lip, ip[0]); + n += sg_scnpr(b + n, blen - n, "%s function number: " + "0x%x\n", lip, ip[1]); + n += sg_scnpr(b + n, blen - n, "%s [or device number: " + "0x%x, function number: 0x%x]\n", lip, + (0x1f & (ip[1] >> 3)), 0x7 & ip[1]); } else - n += my_snprintf(b + n, blen - n, "%s >>>> unexpected " - "protocol indentifier: %s\n%s with " - "Protocol specific port identifier\n", lip, - sg_get_trans_proto_str(p_id, sizeof(e), e), lip); + n += sg_scnpr(b + n, blen - n, "%s >>>> unexpected protocol " + "identifier: %s\n%s with Protocol " + "specific port identifier\n", lip, + sg_get_trans_proto_str(p_id, sizeof(e), e), lip); break; case 0xa: /* UUID identifier */ - if (1 != c_set) { - n += my_snprintf(b + n, blen - n, "%s << expected binary " - "code_set >>\n", lip); - n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n); - break; - } - if ((1 != ((ip[0] >> 4) & 0xf)) || (18 != dlen)) { - n += my_snprintf(b + n, blen - n, "%s << expected locally " - "assigned UUID, 16 bytes long >>\n", lip); - n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n); - break; - } - n += my_snprintf(b + n, blen - n, "%s Locally assigned UUID: ", - lip); - for (m = 0; m < 16; ++m) { - if ((4 == m) || (6 == m) || (8 == m) || (10 == m)) - n += my_snprintf(b + n, blen - n, "-"); - n += my_snprintf(b + n, blen - n, "%02x", (unsigned int)ip[2 + m]); - } - n += my_snprintf(b + n, blen - n, "\n"); - if (do_long) { - n += my_snprintf(b + n, blen - n, "%s [0x", lip); - for (m = 0; m < 16; ++m) - n += my_snprintf(b + n, blen - n, "%02x", - (unsigned int)ip[2 + m]); - n += my_snprintf(b + n, blen - n, "]\n"); - } + n += sg_t10_uuid_desig2str(ip, dlen, c_set, do_long, false, lip, + blen - n, b + n); break; default: /* reserved */ - n += my_snprintf(b + n, blen - n, "%s reserved " - "designator=0x%x\n", lip, desig_type); - n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n); + n += sg_scnpr(b + n, blen - n, "%s reserved designator=0x%x\n", + lip, desig_type); + n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } return n; } static int -decode_sks(const char * leadin, const unsigned char * descp, int add_d_len, - int sense_key, int * processedp, int blen, char * b) +decode_sks(const char * lip, const uint8_t * descp, int add_d_len, + int sense_key, bool * processedp, int blen, char * b) { int progress, pr, rem, n; - const char * lip = ""; n = 0; - if (leadin) - lip = leadin; + if (NULL == lip) + lip = ""; switch (sense_key) { case SPC_SK_ILLEGAL_REQUEST: if (add_d_len < 6) { - n += my_snprintf(b + n, blen - n, "Field pointer: "); + n += sg_scnpr(b + n, blen - n, "Field pointer: "); goto too_short; } /* abbreviate to fit on one line */ - n += my_snprintf(b + n, blen - n, "Field pointer:\n"); - n += my_snprintf(b + n, blen - n, "%s Error in %s: byte %d", - lip, (descp[4] & 0x40) ? "Command" : - "Data parameters", - sg_get_unaligned_be16(descp + 5)); + n += sg_scnpr(b + n, blen - n, "Field pointer:\n"); + n += sg_scnpr(b + n, blen - n, "%s Error in %s: byte %d", lip, + (descp[4] & 0x40) ? "Command" : "Data parameters", + sg_get_unaligned_be16(descp + 5)); if (descp[4] & 0x08) { - n += my_snprintf(b + n, blen - n, " bit %d\n", - descp[4] & 0x07); + n += sg_scnpr(b + n, blen - n, " bit %d\n", descp[4] & 0x07); } else - n += my_snprintf(b + n, blen - n, "\n"); + n += sg_scnpr(b + n, blen - n, "\n"); break; case SPC_SK_HARDWARE_ERROR: case SPC_SK_MEDIUM_ERROR: case SPC_SK_RECOVERED_ERROR: - n += my_snprintf(b + n, blen - n, "Actual retry count: "); + n += sg_scnpr(b + n, blen - n, "Actual retry count: "); if (add_d_len < 6) goto too_short; - n += my_snprintf(b + n, blen - n,"%u\n", - sg_get_unaligned_be16(descp + 5)); + n += sg_scnpr(b + n, blen - n,"%u\n", + sg_get_unaligned_be16(descp + 5)); break; case SPC_SK_NO_SENSE: case SPC_SK_NOT_READY: - n += my_snprintf(b + n, blen - n, "Progress indication: "); + n += sg_scnpr(b + n, blen - n, "Progress indication: "); if (add_d_len < 6) goto too_short; progress = sg_get_unaligned_be16(descp + 5); pr = (progress * 100) / 65536; rem = ((progress * 100) % 65536) / 656; - n += my_snprintf(b + n, blen - n, "%d.%02d%%\n", pr, rem); + n += sg_scnpr(b + n, blen - n, "%d.%02d%%\n", pr, rem); break; case SPC_SK_COPY_ABORTED: - n += my_snprintf(b + n, blen - n, "Segment pointer:\n"); + n += sg_scnpr(b + n, blen - n, "Segment pointer:\n"); if (add_d_len < 6) goto too_short; - n += my_snprintf(b + n, blen - n, "%s Relative to start of " - "%s, byte %d", lip, - (descp[4] & 0x20) ? "segment descriptor" : - "parameter list", - sg_get_unaligned_be16(descp + 5)); + n += sg_scnpr(b + n, blen - n, "%s Relative to start of %s, " + "byte %d", lip, (descp[4] & 0x20) ? + "segment descriptor" : "parameter list", + sg_get_unaligned_be16(descp + 5)); if (descp[4] & 0x08) - n += my_snprintf(b + n, blen - n, " bit %d\n", - descp[4] & 0x07); + n += sg_scnpr(b + n, blen - n, " bit %d\n", descp[4] & 0x07); else - n += my_snprintf(b + n, blen - n, "\n"); + n += sg_scnpr(b + n, blen - n, "\n"); break; case SPC_SK_UNIT_ATTENTION: - n += my_snprintf(b + n, blen - n, "Unit attention condition " - "queue:\n"); - n += my_snprintf(b + n, blen - n, "%s overflow flag is %d\n", - lip, !!(descp[4] & 0x1)); + n += sg_scnpr(b + n, blen - n, "Unit attention condition queue:\n"); + n += sg_scnpr(b + n, blen - n, "%s overflow flag is %d\n", lip, + !!(descp[4] & 0x1)); break; default: - n += my_snprintf(b + n, blen - n, "Sense_key: 0x%x " - "unexpected\n", sense_key); - *processedp = 0; + n += sg_scnpr(b + n, blen - n, "Sense_key: 0x%x unexpected\n", + sense_key); + *processedp = false; break; } return n; too_short: - n += my_snprintf(b + n, blen - n, "%s\n", " >> descriptor too short"); - *processedp = 0; + n += sg_scnpr(b + n, blen - n, "%s\n", " >> descriptor too short"); + *processedp = false; return n; } @@ -1023,59 +1325,58 @@ { switch (st) { case TPGS_STATE_OPTIMIZED: - return my_snprintf(b, blen, "active/optimized"); + return sg_scnpr(b, blen, "active/optimized"); case TPGS_STATE_NONOPTIMIZED: - return my_snprintf(b, blen, "active/non optimized"); + return sg_scnpr(b, blen, "active/non optimized"); case TPGS_STATE_STANDBY: - return my_snprintf(b, blen, "standby"); + return sg_scnpr(b, blen, "standby"); case TPGS_STATE_UNAVAILABLE: - return my_snprintf(b, blen, "unavailable"); + return sg_scnpr(b, blen, "unavailable"); case TPGS_STATE_OFFLINE: - return my_snprintf(b, blen, "offline"); + return sg_scnpr(b, blen, "offline"); case TPGS_STATE_TRANSITIONING: - return my_snprintf(b, blen, "transitioning between states"); + return sg_scnpr(b, blen, "transitioning between states"); default: - return my_snprintf(b, blen, "unknown: 0x%x", st); + return sg_scnpr(b, blen, "unknown: 0x%x", st); } } static int -uds_referral_descriptor_str(char * b, int blen, const unsigned char * dp, - int alen, const char * leadin) +uds_referral_descriptor_str(char * b, int blen, const uint8_t * dp, + int alen, const char * lip) { int n = 0; int dlen = alen - 2; int k, j, g, f, tpgd; - const unsigned char * tp; + const uint8_t * tp; uint64_t ull; char c[40]; - const char * lip = ""; - if (leadin) - lip = leadin; - n += my_snprintf(b + n, blen - n, "%s Not all referrals: %d\n", lip, - !!(dp[2] & 0x1)); + if (NULL == lip) + lip = ""; + n += sg_scnpr(b + n, blen - n, "%s Not all referrals: %d\n", lip, + !!(dp[2] & 0x1)); dp += 4; for (k = 0, f = 1; (k + 4) < dlen; k += g, dp += g, ++f) { tpgd = dp[3]; g = (tpgd * 4) + 20; - n += my_snprintf(b + n, blen - n, "%s Descriptor %d\n", lip, f); + n += sg_scnpr(b + n, blen - n, "%s Descriptor %d\n", lip, f); if ((k + g) > dlen) { - n += my_snprintf(b + n, blen - n, "%s truncated descriptor, " - "stop\n", lip); + n += sg_scnpr(b + n, blen - n, "%s truncated descriptor, " + "stop\n", lip); return n; } ull = sg_get_unaligned_be64(dp + 4); - n += my_snprintf(b + n, blen - n, "%s first uds LBA: 0x%" PRIx64 - "\n", lip, ull); + n += sg_scnpr(b + n, blen - n, "%s first uds LBA: 0x%" PRIx64 + "\n", lip, ull); ull = sg_get_unaligned_be64(dp + 12); - n += my_snprintf(b + n, blen - n, "%s last uds LBA: 0x%" PRIx64 - "\n", lip, ull); + n += sg_scnpr(b + n, blen - n, "%s last uds LBA: 0x%" PRIx64 + "\n", lip, ull); for (j = 0; j < tpgd; ++j) { tp = dp + 20 + (j * 4); decode_tpgs_state(tp[0] & 0xf, c, sizeof(c)); - n += my_snprintf(b + n, blen - n, "%s tpg: %d state: " - "%s\n", lip, sg_get_unaligned_be16(tp + 2), c); + n += sg_scnpr(b + n, blen - n, "%s tpg: %d state: %s\n", + lip, sg_get_unaligned_be16(tp + 2), c); } } return n; @@ -1091,16 +1392,18 @@ /* Decode descriptor format sense descriptors (assumes sense buffer is - * in descriptor format) */ + * in descriptor format). 'leadin' is string prepended to each line written + * to 'b', NULL treated as "". Returns the number of bytes written to 'b' + * excluding the trailing '\0'. If problem, returns 0. */ int -sg_get_sense_descriptors_str(const char * leadin, - const unsigned char * sense_buffer, int sb_len, - int blen, char * b) +sg_get_sense_descriptors_str(const char * lip, const uint8_t * sbp, + int sb_len, int blen, char * b) { - int add_sb_len, add_d_len, desc_len, k, j, sense_key, processed; + int add_sb_len, add_d_len, desc_len, k, j, sense_key; int n, progress, pr, rem; - const unsigned char * descp; - const char * lip = ""; + uint16_t sct_sc; + bool processed; + const uint8_t * descp; const char * dtsp = " >> descriptor too short"; const char * eccp = "Extended copy command"; const char * ddp = "destination device"; @@ -1109,174 +1412,172 @@ if ((NULL == b) || (blen <= 0)) return 0; b[0] = '\0'; - if (leadin) { - lip = leadin; - snprintf(z, sizeof(z), "%.60s ", lip); - } else - snprintf(z, sizeof(z), " "); - if ((sb_len < 8) || (0 == (add_sb_len = sense_buffer[7]))) + if (lip) + sg_scnpr(z, sizeof(z), "%.60s ", lip); + else + sg_scnpr(z, sizeof(z), " "); + if ((sb_len < 8) || (0 == (add_sb_len = sbp[7]))) return 0; add_sb_len = (add_sb_len < (sb_len - 8)) ? add_sb_len : (sb_len - 8); - sense_key = (sense_buffer[1] & 0xf); + sense_key = (sbp[1] & 0xf); - for (descp = (sense_buffer + 8), k = 0, n = 0; + for (descp = (sbp + 8), k = 0, n = 0; (k < add_sb_len) && (n < blen); k += desc_len, descp += desc_len) { add_d_len = (k < (add_sb_len - 1)) ? descp[1] : -1; if ((k + add_d_len + 2) > add_sb_len) add_d_len = add_sb_len - k - 2; desc_len = add_d_len + 2; - n += my_snprintf(b + n, blen - n, "%s Descriptor type: ", lip); - processed = 1; + n += sg_scnpr(b + n, blen - n, "%s Descriptor type: ", lip); + processed = true; switch (descp[0]) { case 0: - n += my_snprintf(b + n, blen - n, "Information: "); - if ((add_d_len >= 10) && (0x80 & descp[2])) { - n += my_snprintf(b + n, blen - n, "0x"); + n += sg_scnpr(b + n, blen - n, "Information: "); + if (add_d_len >= 10) { + if (! (0x80 & descp[2])) + n += sg_scnpr(b + n, blen - n, "Valid=0 (-> vendor " + "specific) "); + n += sg_scnpr(b + n, blen - n, "0x"); for (j = 0; j < 8; ++j) - n += my_snprintf(b + n, blen - n, "%02x", descp[4 + j]); - n += my_snprintf(b + n, blen - n, "\n"); + n += sg_scnpr(b + n, blen - n, "%02x", descp[4 + j]); + n += sg_scnpr(b + n, blen - n, "\n"); } else { - n += my_snprintf(b + n, blen - n, "%s\n", dtsp); - processed = 0; + n += sg_scnpr(b + n, blen - n, "%s\n", dtsp); + processed = false; } break; case 1: - n += my_snprintf(b + n, blen - n, "Command specific: "); + n += sg_scnpr(b + n, blen - n, "Command specific: "); if (add_d_len >= 10) { - n += my_snprintf(b + n, blen - n, "0x"); + n += sg_scnpr(b + n, blen - n, "0x"); for (j = 0; j < 8; ++j) - n += my_snprintf(b + n, blen - n, "%02x", descp[4 + j]); - n += my_snprintf(b + n, blen - n, "\n"); + n += sg_scnpr(b + n, blen - n, "%02x", descp[4 + j]); + n += sg_scnpr(b + n, blen - n, "\n"); } else { - n += my_snprintf(b + n, blen - n, "%s\n", dtsp); - processed = 0; + n += sg_scnpr(b + n, blen - n, "%s\n", dtsp); + processed = false; } break; case 2: /* Sense Key Specific */ - n += my_snprintf(b + n, blen - n, "Sense key specific: "); + n += sg_scnpr(b + n, blen - n, "Sense key specific: "); n += decode_sks(lip, descp, add_d_len, sense_key, &processed, blen - n, b + n); break; case 3: - n += my_snprintf(b + n, blen - n, "Field replaceable unit code: "); + n += sg_scnpr(b + n, blen - n, "Field replaceable unit code: "); if (add_d_len >= 2) - n += my_snprintf(b + n, blen - n, "0x%x\n", descp[3]); + n += sg_scnpr(b + n, blen - n, "0x%x\n", descp[3]); else { - n += my_snprintf(b + n, blen - n, "%s\n", dtsp); - processed = 0; + n += sg_scnpr(b + n, blen - n, "%s\n", dtsp); + processed = false; } break; case 4: - n += my_snprintf(b + n, blen - n, "Stream commands: "); + n += sg_scnpr(b + n, blen - n, "Stream commands: "); if (add_d_len >= 2) { if (descp[3] & 0x80) - n += my_snprintf(b + n, blen - n, "FILEMARK"); + n += sg_scnpr(b + n, blen - n, "FILEMARK"); if (descp[3] & 0x40) - n += my_snprintf(b + n, blen - n, "End Of Medium (EOM)"); + n += sg_scnpr(b + n, blen - n, "End Of Medium (EOM)"); if (descp[3] & 0x20) - n += my_snprintf(b + n, blen - n, "Incorrect Length " - "Indicator (ILI)"); - n += my_snprintf(b + n, blen - n, "\n"); + n += sg_scnpr(b + n, blen - n, "Incorrect Length " + "Indicator (ILI)"); + n += sg_scnpr(b + n, blen - n, "\n"); } else { - n += my_snprintf(b + n, blen - n, "%s\n", dtsp); - processed = 0; + n += sg_scnpr(b + n, blen - n, "%s\n", dtsp); + processed = false; } break; case 5: - n += my_snprintf(b + n, blen - n, "Block commands: "); + n += sg_scnpr(b + n, blen - n, "Block commands: "); if (add_d_len >= 2) - n += my_snprintf(b + n, blen - n, "Incorrect Length " - "Indicator (ILI) %s\n", - (descp[3] & 0x20) ? "set" : "clear"); + n += sg_scnpr(b + n, blen - n, "Incorrect Length Indicator " + "(ILI) %s\n", + (descp[3] & 0x20) ? "set" : "clear"); else { - n += my_snprintf(b + n, blen - n, "%s\n", dtsp); - processed = 0; + n += sg_scnpr(b + n, blen - n, "%s\n", dtsp); + processed = false; } break; case 6: - n += my_snprintf(b + n, blen - n, "OSD object identification\n"); - processed = 0; + n += sg_scnpr(b + n, blen - n, "OSD object identification\n"); + processed = false; break; case 7: - n += my_snprintf(b + n, blen - n, "OSD response integrity check " - "value\n"); - processed = 0; + n += sg_scnpr(b + n, blen - n, "OSD response integrity check " + "value\n"); + processed = false; break; case 8: - n += my_snprintf(b + n, blen - n, "OSD attribute " - "identification\n"); - processed = 0; + n += sg_scnpr(b + n, blen - n, "OSD attribute identification\n"); + processed = false; break; case 9: /* this is defined in SAT (SAT-2) */ - n += my_snprintf(b + n, blen - n, "ATA Status Return: "); + n += sg_scnpr(b + n, blen - n, "ATA Status Return: "); if (add_d_len >= 12) { int extend, count; extend = descp[2] & 1; count = descp[5] + (extend ? (descp[4] << 8) : 0); - n += my_snprintf(b + n, blen - n, "extend=%d error=0x%x " - "\n%s count=0x%x ", extend, - descp[3], lip, count); + n += sg_scnpr(b + n, blen - n, "extend=%d error=0x%x \n%s" + " count=0x%x ", extend, descp[3], lip, + count); if (extend) - n += my_snprintf(b + n, blen - n, - "lba=0x%02x%02x%02x%02x%02x%02x ", - descp[10], descp[8], descp[6], - descp[11], descp[9], descp[7]); + n += sg_scnpr(b + n, blen - n, + "lba=0x%02x%02x%02x%02x%02x%02x ", + descp[10], descp[8], descp[6], descp[11], + descp[9], descp[7]); else - n += my_snprintf(b + n, blen - n, - "lba=0x%02x%02x%02x ", - descp[11], descp[9], descp[7]); - n += my_snprintf(b + n, blen - n, "device=0x%x status=0x%x\n", - descp[12], descp[13]); + n += sg_scnpr(b + n, blen - n, "lba=0x%02x%02x%02x ", + descp[11], descp[9], descp[7]); + n += sg_scnpr(b + n, blen - n, "device=0x%x status=0x%x\n", + descp[12], descp[13]); } else { - n += my_snprintf(b + n, blen - n, "%s\n", dtsp); - processed = 0; + n += sg_scnpr(b + n, blen - n, "%s\n", dtsp); + processed = false; } break; case 0xa: /* Added in SPC-4 rev 17, became 'Another ...' in rev 34 */ - n += my_snprintf(b + n, blen - n, "Another progress " - "indication: "); + n += sg_scnpr(b + n, blen - n, "Another progress indication: "); if (add_d_len < 6) { - n += my_snprintf(b + n, blen - n, "%s\n", dtsp); - processed = 0; + n += sg_scnpr(b + n, blen - n, "%s\n", dtsp); + processed = false; break; } progress = sg_get_unaligned_be16(descp + 6); pr = (progress * 100) / 65536; rem = ((progress * 100) % 65536) / 656; - n += my_snprintf(b + n, blen - n, "%d.02%d%%\n", pr, rem); - n += my_snprintf(b + n, blen - n, "%s [sense_key=0x%x " - "asc,ascq=0x%x,0x%x]\n", lip, descp[2], descp[3], - descp[4]); + n += sg_scnpr(b + n, blen - n, "%d.02%d%%\n", pr, rem); + n += sg_scnpr(b + n, blen - n, "%s [sense_key=0x%x " + "asc,ascq=0x%x,0x%x]\n", lip, descp[2], descp[3], + descp[4]); break; case 0xb: /* Added in SPC-4 rev 23, defined in SBC-3 rev 22 */ - n += my_snprintf(b + n, blen - n, "User data segment referral: "); + n += sg_scnpr(b + n, blen - n, "User data segment referral: "); if (add_d_len < 2) { - n += my_snprintf(b + n, blen - n, "%s\n", dtsp); - processed = 0; + n += sg_scnpr(b + n, blen - n, "%s\n", dtsp); + processed = false; break; } - n += my_snprintf(b + n, blen - n, "\n"); + n += sg_scnpr(b + n, blen - n, "\n"); n += uds_referral_descriptor_str(b + n, blen - n, descp, add_d_len, lip); break; case 0xc: /* Added in SPC-4 rev 28 */ - n += my_snprintf(b + n, blen - n, "Forwarded sense data\n"); + n += sg_scnpr(b + n, blen - n, "Forwarded sense data\n"); if (add_d_len < 2) { - n += my_snprintf(b + n, blen - n, "%s\n", dtsp); - processed = 0; + n += sg_scnpr(b + n, blen - n, "%s\n", dtsp); + processed = false; break; } - n += my_snprintf(b + n, blen - n, "%s FSDT: %s\n", lip, - (descp[2] & 0x80) ? "set" : "clear"); + n += sg_scnpr(b + n, blen - n, "%s FSDT: %s\n", lip, + (descp[2] & 0x80) ? "set" : "clear"); j = descp[2] & 0xf; - n += my_snprintf(b + n, blen - n, "%s Sense data source: ", - lip); + n += sg_scnpr(b + n, blen - n, "%s Sense data source: ", lip); switch (j) { case 0: - n += my_snprintf(b + n, blen - n, "%s source device\n", eccp); + n += sg_scnpr(b + n, blen - n, "%s source device\n", eccp); break; case 1: case 2: @@ -1285,401 +1586,596 @@ case 5: case 6: case 7: - n += my_snprintf(b + n, blen - n, "%s %s %d\n", eccp, ddp, - j - 1); + n += sg_scnpr(b + n, blen - n, "%s %s %d\n", eccp, ddp, j - 1); break; default: - n += my_snprintf(b + n, blen - n, "unknown [%d]\n", j); + n += sg_scnpr(b + n, blen - n, "unknown [%d]\n", j); } { char c[480]; sg_get_scsi_status_str(descp[3], sizeof(c) - 1, c); c[sizeof(c) - 1] = '\0'; - n += my_snprintf(b + n, blen - n, "%s Forwarded status: " - "%s\n", lip, c); + n += sg_scnpr(b + n, blen - n, "%s Forwarded status: %s\n", + lip, c); if (add_d_len > 2) { /* recursing; hope not to get carried away */ - n += my_snprintf(b + n, blen - n, "%s vvvvvvvvvvvvvvvv\n", - lip); - sg_get_sense_str(lip, descp + 4, add_d_len - 2, 0, + n += sg_scnpr(b + n, blen - n, "%s vvvvvvvvvvvvvvvv\n", + lip); + sg_get_sense_str(lip, descp + 4, add_d_len - 2, false, sizeof(c), c); - n += my_snprintf(b + n, blen - n, "%s", c); - n += my_snprintf(b + n, blen - n, "%s ^^^^^^^^^^^^^^^^\n", - lip); + n += sg_scnpr(b + n, blen - n, "%s", c); + n += sg_scnpr(b + n, blen - n, "%s ^^^^^^^^^^^^^^^^\n", + lip); } } break; case 0xd: /* Added in SBC-3 rev 36d */ /* this descriptor combines descriptors 0, 1, 2 and 3 */ - n += my_snprintf(b + n, blen - n, "Direct-access block device\n"); + n += sg_scnpr(b + n, blen - n, "Direct-access block device\n"); if (add_d_len < 28) { - n += my_snprintf(b + n, blen - n, "%s\n", dtsp); - processed = 0; + n += sg_scnpr(b + n, blen - n, "%s\n", dtsp); + processed = false; break; } if (0x20 & descp[2]) - n += my_snprintf(b + n, blen - n, "%s ILI (incorrect " - "length indication) set\n", lip); + n += sg_scnpr(b + n, blen - n, "%s ILI (incorrect length " + "indication) set\n", lip); if (0x80 & descp[4]) { - n += my_snprintf(b + n, blen - n, "%s Sense key " - "specific: ", lip); + n += sg_scnpr(b + n, blen - n, "%s Sense key specific: ", + lip); n += decode_sks(lip, descp, add_d_len, sense_key, &processed, blen - n, b + n); } - n += my_snprintf(b + n, blen - n, "%s Field replaceable unit " - "code: 0x%x\n", lip, descp[7]); + n += sg_scnpr(b + n, blen - n, "%s Field replaceable unit " + "code: 0x%x\n", lip, descp[7]); if (0x80 & descp[2]) { - n += my_snprintf(b + n, blen - n, "%s Information: 0x", - lip); + n += sg_scnpr(b + n, blen - n, "%s Information: 0x", lip); for (j = 0; j < 8; ++j) - n += my_snprintf(b + n, blen - n, "%02x", descp[8 + j]); - n += my_snprintf(b + n, blen - n, "\n"); + n += sg_scnpr(b + n, blen - n, "%02x", descp[8 + j]); + n += sg_scnpr(b + n, blen - n, "\n"); } - n += my_snprintf(b + n, blen - n, "%s Command specific: 0x", - lip); + n += sg_scnpr(b + n, blen - n, "%s Command specific: 0x", lip); for (j = 0; j < 8; ++j) - n += my_snprintf(b + n, blen - n, "%02x", descp[16 + j]); - n += my_snprintf(b + n, blen - n, "\n"); + n += sg_scnpr(b + n, blen - n, "%02x", descp[16 + j]); + n += sg_scnpr(b + n, blen - n, "\n"); break; - case 0xe: /* Added in SPC-5 rev 6 (for bind/unbind) */ - n += my_snprintf(b + n, blen - n, "Device designation\n"); - j = (int)(sizeof(dd_usage_reason_str_arr) / - sizeof(dd_usage_reason_str_arr[0])); + case 0xe: /* Added in SPC-5 rev 6 (for Bind/Unbind) */ + n += sg_scnpr(b + n, blen - n, "Device designation\n"); + j = (int)SG_ARRAY_SIZE(dd_usage_reason_str_arr); if (descp[3] < j) - n += my_snprintf(b + n, blen - n, "%s Usage reason: %s\n", - lip, dd_usage_reason_str_arr[descp[3]]); + n += sg_scnpr(b + n, blen - n, "%s Usage reason: %s\n", + lip, dd_usage_reason_str_arr[descp[3]]); else - n += my_snprintf(b + n, blen - n, "%s Usage reason: " - "reserved[%d]\n", lip, descp[3]); + n += sg_scnpr(b + n, blen - n, "%s Usage reason: " + "reserved[%d]\n", lip, descp[3]); n += sg_get_designation_descriptor_str(z, descp + 4, descp[1] - 2, - 1, 0, blen - n, b + n); + true, false, blen - n, + b + n); + break; + case 0xf: /* Added in SPC-5 rev 10 (for Write buffer) */ + n += sg_scnpr(b + n, blen - n, "Microcode activation "); + if (add_d_len < 6) { + n += sg_scnpr(b + n, blen - n, "%s\n", dtsp); + processed = false; + break; + } + progress = sg_get_unaligned_be16(descp + 6); + n += sg_scnpr(b + n, blen - n, "time: "); + if (0 == progress) + n += sg_scnpr(b + n, blen - n, "unknown\n"); + else + n += sg_scnpr(b + n, blen - n, "%d seconds\n", progress); + break; + case 0xde: /* NVME Status Field; vendor (sg3_utils) specific */ + n += sg_scnpr(b + n, blen - n, "NVMe Status: "); + if (add_d_len < 6) { + n += sg_scnpr(b + n, blen - n, "%s\n", dtsp); + processed = false; + break; + } + n += sg_scnpr(b + n, blen - n, "DNR=%d, M=%d, ", + (int)!!(0x80 & descp[5]), (int)!!(0x40 & descp[5])); + sct_sc = sg_get_unaligned_be16(descp + 6); + n += sg_scnpr(b + n, blen - n, "SCT_SC=0x%x\n", sct_sc); + if (sct_sc > 0) { + char d[80]; + + n += sg_scnpr(b + n, blen - n, " %s\n", + sg_get_nvme_cmd_status_str(sct_sc, sizeof(d), d)); + } break; default: if (descp[0] >= 0x80) - n += my_snprintf(b + n, blen - n, "Vendor specific [0x%x]\n", - descp[0]); + n += sg_scnpr(b + n, blen - n, "Vendor specific [0x%x]\n", + descp[0]); else - n += my_snprintf(b + n, blen - n, "Unknown [0x%x]\n", - descp[0]); - processed = 0; + n += sg_scnpr(b + n, blen - n, "Unknown [0x%x]\n", descp[0]); + processed = false; break; } if (! processed) { if (add_d_len > 0) { - n += my_snprintf(b + n, blen - n, "%s ", lip); + n += sg_scnpr(b + n, blen - n, "%s ", lip); for (j = 0; j < add_d_len; ++j) { if ((j > 0) && (0 == (j % 24))) - n += my_snprintf(b + n, blen - n, "\n%s ", lip); - n += my_snprintf(b + n, blen - n, "%02x ", descp[j + 2]); + n += sg_scnpr(b + n, blen - n, "\n%s ", lip); + n += sg_scnpr(b + n, blen - n, "%02x ", descp[j + 2]); } - n += my_snprintf(b + n, blen - n, "\n"); + n += sg_scnpr(b + n, blen - n, "\n"); } } if (add_d_len < 0) - n += my_snprintf(b + n, blen - n, "%s short descriptor\n", lip); + n += sg_scnpr(b + n, blen - n, "%s short descriptor\n", lip); } return n; } -/* Decode SAT ATA PASS-THROUGH fixed format sense */ +/* Decode SAT ATA PASS-THROUGH fixed format sense. Shows "+" after 'count' + * and/or 'lba' values to indicate that not all data in those fields is shown. + * That extra field information may be available in the ATA pass-through + * results log page parameter with the corresponding 'log_index'. */ static int -sg_get_sense_sat_pt_fixed_str(const char * leadin, const unsigned char * sp, +sg_get_sense_sat_pt_fixed_str(const char * lip, const uint8_t * sp, int slen, int blen, char * b) { int n = 0; - const char * lip = ""; + bool extend, count_upper_nz, lba_upper_nz; if ((blen < 1) || (slen < 12)) return n; - if (leadin) - lip = leadin; + if (NULL == lip) + lip = ""; if (SPC_SK_RECOVERED_ERROR != (0xf & sp[2])) - n += my_snprintf(b + n, blen - n, "%s >> expected Sense key: " - "Recovered Error ??\n", lip); - n += my_snprintf(b + n, blen - n, "%s error=0x%x, status=0x%x, " - "device=0x%x, sector_count(7:0)=0x%x%c\n", lip, sp[3], - sp[4], sp[5], sp[6], ((0x40 & sp[8]) ? '+' : ' ')); - n += my_snprintf(b + n, blen - n, "%s extend=%d, log_index=0x%x, " - "lba_high,mid,low(7:0)=0x%x,0x%x,0x%x%c\n", lip, - (!!(0x80 & sp[8])), (0xf & sp[8]), sp[9], sp[10], sp[11], - ((0x20 & sp[8]) ? '+' : ' ')); + n += sg_scnpr(b + n, blen - n, "%s >> expected Sense key: Recovered " + "Error ??\n", lip); + /* Fixed sense command-specific information field starts at sp + 8 */ + extend = !!(0x80 & sp[8]); + count_upper_nz = !!(0x40 & sp[8]); + lba_upper_nz = !!(0x20 & sp[8]); + /* Fixed sense information field starts at sp + 3 */ + n += sg_scnpr(b + n, blen - n, "%s error=0x%x, status=0x%x, " + "device=0x%x, count(7:0)=0x%x%c\n", lip, sp[3], sp[4], + sp[5], sp[6], (count_upper_nz ? '+' : ' ')); + n += sg_scnpr(b + n, blen - n, "%s extend=%d, log_index=0x%x, " + "lba_high,mid,low(7:0)=0x%x,0x%x,0x%x%c\n", lip, + (int)extend, (0xf & sp[8]), sp[9], sp[10], sp[11], + (lba_upper_nz ? '+' : ' ')); return n; } /* Fetch sense information */ int -sg_get_sense_str(const char * leadin, const unsigned char * sense_buffer, - int sb_len, int raw_sinfo, int buff_len, char * buff) +sg_get_sense_str(const char * lip, const uint8_t * sbp, int sb_len, + bool raw_sinfo, int cblen, char * cbp) { - int len, valid, progress, n, r, pr, rem, blen; + bool descriptor_format = false; + bool sdat_ovfl = false; + bool valid; + int len, progress, n, r, pr, rem, blen; unsigned int info; - int descriptor_format = 0; - int sdat_ovfl = 0; + uint8_t resp_code; const char * ebp = NULL; - char error_buff[64]; + char ebuff[64]; char b[256]; struct sg_scsi_sense_hdr ssh; - const char * lip = ""; - if ((NULL == buff) || (buff_len <= 0)) + if ((NULL == cbp) || (cblen <= 0)) return 0; - else if (1 == buff_len) { - buff[0] = '\0'; + else if (1 == cblen) { + cbp[0] = '\0'; return 0; } blen = sizeof(b); n = 0; - if (leadin) - lip = leadin; - if ((NULL == sense_buffer) || (sb_len < 1)) { - n += my_snprintf(buff, buff_len, "%s >>> sense buffer empty\n", - lip); + if (NULL == lip) + lip = ""; + if ((NULL == sbp) || (sb_len < 1)) { + n += sg_scnpr(cbp, cblen, "%s >>> sense buffer empty\n", lip); return n; } + resp_code = 0x7f & sbp[0]; + valid = !!(sbp[0] & 0x80); len = sb_len; - if (sg_scsi_normalize_sense(sense_buffer, sb_len, &ssh)) { + if (sg_scsi_normalize_sense(sbp, sb_len, &ssh)) { switch (ssh.response_code) { case 0x70: /* fixed, current */ ebp = "Fixed format, current"; - len = (sb_len > 7) ? (sense_buffer[7] + 8) : sb_len; + len = (sb_len > 7) ? (sbp[7] + 8) : sb_len; len = (len > sb_len) ? sb_len : len; - sdat_ovfl = (len > 2) ? !!(sense_buffer[2] & 0x10) : 0; + sdat_ovfl = (len > 2) ? !!(sbp[2] & 0x10) : false; break; case 0x71: /* fixed, deferred */ /* error related to a previous command */ ebp = "Fixed format, <<>>"; - len = (sb_len > 7) ? (sense_buffer[7] + 8) : sb_len; + len = (sb_len > 7) ? (sbp[7] + 8) : sb_len; len = (len > sb_len) ? sb_len : len; - sdat_ovfl = (len > 2) ? !!(sense_buffer[2] & 0x10) : 0; + sdat_ovfl = (len > 2) ? !!(sbp[2] & 0x10) : false; break; case 0x72: /* descriptor, current */ - descriptor_format = 1; + descriptor_format = true; ebp = "Descriptor format, current"; - sdat_ovfl = (sb_len > 4) ? !!(sense_buffer[4] & 0x80) : 0; + sdat_ovfl = (sb_len > 4) ? !!(sbp[4] & 0x80) : false; break; case 0x73: /* descriptor, deferred */ - descriptor_format = 1; + descriptor_format = true; ebp = "Descriptor format, <<>>"; - sdat_ovfl = (sb_len > 4) ? !!(sense_buffer[4] & 0x80) : 0; + sdat_ovfl = (sb_len > 4) ? !!(sbp[4] & 0x80) : false; break; case 0x0: ebp = "Response code: 0x0 (?)"; break; default: - my_snprintf(error_buff, sizeof(error_buff), - "Unknown response code: 0x%x", ssh.response_code); - ebp = error_buff; + sg_scnpr(ebuff, sizeof(ebuff), "Unknown response code: 0x%x", + ssh.response_code); + ebp = ebuff; break; } - n += my_snprintf(buff + n, buff_len - n, "%s%s; Sense key: %s\n", - lip, ebp, sg_lib_sense_key_desc[ssh.sense_key]); + n += sg_scnpr(cbp + n, cblen - n, "%s%s; Sense key: %s\n", lip, ebp, + sg_lib_sense_key_desc[ssh.sense_key]); if (sdat_ovfl) - n += my_snprintf(buff + n, buff_len - n, "%s<<>>\n", lip); + n += sg_scnpr(cbp + n, cblen - n, "%s<<>>\n", lip); if (descriptor_format) { - n += my_snprintf(buff + n, buff_len - n, "%s%s\n", lip, - sg_get_asc_ascq_str(ssh.asc, ssh.ascq, - sizeof(b), b)); - n += sg_get_sense_descriptors_str(lip, sense_buffer, len, - buff_len - n, buff + n); + n += sg_scnpr(cbp + n, cblen - n, "%s%s\n", lip, + sg_get_asc_ascq_str(ssh.asc, ssh.ascq, blen, b)); + n += sg_get_sense_descriptors_str(lip, sbp, len, + cblen - n, cbp + n); } else if ((len > 12) && (0 == ssh.asc) && (ASCQ_ATA_PT_INFO_AVAILABLE == ssh.ascq)) { /* SAT ATA PASS-THROUGH fixed format */ - n += my_snprintf(buff + n, buff_len - n, "%s%s\n", lip, - sg_get_asc_ascq_str(ssh.asc, ssh.ascq, - sizeof(b), b)); - n += sg_get_sense_sat_pt_fixed_str(lip, sense_buffer, len, - buff_len - n, buff + n); + n += sg_scnpr(cbp + n, cblen - n, "%s%s\n", lip, + sg_get_asc_ascq_str(ssh.asc, ssh.ascq, blen, b)); + n += sg_get_sense_sat_pt_fixed_str(lip, sbp, len, + cblen - n, cbp + n); } else if (len > 2) { /* fixed format */ if (len > 12) - n += my_snprintf(buff + n, buff_len - n, "%s%s\n", lip, - sg_get_asc_ascq_str(ssh.asc, ssh.ascq, - sizeof(b), b)); + n += sg_scnpr(cbp + n, cblen - n, "%s%s\n", lip, + sg_get_asc_ascq_str(ssh.asc, ssh.ascq, blen, b)); r = 0; - valid = sense_buffer[0] & 0x80; if (strlen(lip) > 0) - r += my_snprintf(b + r, blen - r, "%s", lip); + r += sg_scnpr(b + r, blen - r, "%s", lip); if (len > 6) { - info = sg_get_unaligned_be32(sense_buffer + 3); + info = sg_get_unaligned_be32(sbp + 3); if (valid) - r += my_snprintf(b + r, blen - r, " Info fld=0x%x [%u] ", - info, info); + r += sg_scnpr(b + r, blen - r, " Info fld=0x%x [%u] ", + info, info); else if (info > 0) - r += my_snprintf(b + r, blen - r, " Valid=0, Info " - "fld=0x%x [%u] ", info, info); + r += sg_scnpr(b + r, blen - r, " Valid=0, Info fld=0x%x " + "[%u] ", info, info); } else info = 0; - if (sense_buffer[2] & 0xe0) { - if (sense_buffer[2] & 0x80) - r += my_snprintf(b + r, blen - r, " FMK"); + if (sbp[2] & 0xe0) { + if (sbp[2] & 0x80) + r += sg_scnpr(b + r, blen - r, " FMK"); /* current command has read a filemark */ - if (sense_buffer[2] & 0x40) - r += my_snprintf(b + r, blen - r, " EOM"); + if (sbp[2] & 0x40) + r += sg_scnpr(b + r, blen - r, " EOM"); /* end-of-medium condition exists */ - if (sense_buffer[2] & 0x20) - r += my_snprintf(b + r, blen - r, " ILI"); + if (sbp[2] & 0x20) + r += sg_scnpr(b + r, blen - r, " ILI"); /* incorrect block length requested */ - r += my_snprintf(b + r, blen - r, "\n"); + r += sg_scnpr(b + r, blen - r, "\n"); } else if (valid || (info > 0)) - r += my_snprintf(b + r, blen - r, "\n"); - if ((len >= 14) && sense_buffer[14]) - r += my_snprintf(b + r, blen - r, "%s Field replaceable unit " - "code: %d\n", lip, sense_buffer[14]); - if ((len >= 18) && (sense_buffer[15] & 0x80)) { + r += sg_scnpr(b + r, blen - r, "\n"); + if ((len >= 14) && sbp[14]) + r += sg_scnpr(b + r, blen - r, "%s Field replaceable unit " + "code: %d\n", lip, sbp[14]); + if ((len >= 18) && (sbp[15] & 0x80)) { /* sense key specific decoding */ switch (ssh.sense_key) { case SPC_SK_ILLEGAL_REQUEST: - r += my_snprintf(b + r, blen - r, "%s Sense Key " - "Specific: Error in %s: byte %d", lip, - ((sense_buffer[15] & 0x40) ? "Command" : - "Data parameters"), - sg_get_unaligned_be16(sense_buffer + 16)); - if (sense_buffer[15] & 0x08) - r += my_snprintf(b + r, blen - r, " bit %d\n", - sense_buffer[15] & 0x07); + r += sg_scnpr(b + r, blen - r, "%s Sense Key Specific: " + "Error in %s: byte %d", lip, + ((sbp[15] & 0x40) ? + "Command" : "Data parameters"), + sg_get_unaligned_be16(sbp + 16)); + if (sbp[15] & 0x08) + r += sg_scnpr(b + r, blen - r, " bit %d\n", + sbp[15] & 0x07); else - r += my_snprintf(b + r, blen - r, "\n"); + r += sg_scnpr(b + r, blen - r, "\n"); break; case SPC_SK_NO_SENSE: case SPC_SK_NOT_READY: - progress = sg_get_unaligned_be16(sense_buffer + 16); + progress = sg_get_unaligned_be16(sbp + 16); pr = (progress * 100) / 65536; rem = ((progress * 100) % 65536) / 656; - r += my_snprintf(b + r, blen - r, "%s Progress " - "indication: %d.%02d%%\n", lip, pr, rem); + r += sg_scnpr(b + r, blen - r, "%s Progress indication: " + "%d.%02d%%\n", lip, pr, rem); break; case SPC_SK_HARDWARE_ERROR: case SPC_SK_MEDIUM_ERROR: case SPC_SK_RECOVERED_ERROR: - r += my_snprintf(b + r, blen - r, "%s Actual retry " - "count: " "0x%02x%02x\n", lip, - sense_buffer[16], sense_buffer[17]); + r += sg_scnpr(b + r, blen - r, "%s Actual retry count: " + "0x%02x%02x\n", lip, sbp[16], sbp[17]); break; case SPC_SK_COPY_ABORTED: - r += my_snprintf(b + r, blen - r, "%s Segment pointer: ", - lip); - r += my_snprintf(b + r, blen - r, "Relative to start of " - "%s, byte %d", - ((sense_buffer[15] & 0x20) ? - "segment descriptor" : "parameter list"), - sg_get_unaligned_be16(sense_buffer + 16)); - if (sense_buffer[15] & 0x08) - r += my_snprintf(b + r, blen - r, " bit %d\n", - sense_buffer[15] & 0x07); + r += sg_scnpr(b + r, blen - r, "%s Segment pointer: ", + lip); + r += sg_scnpr(b + r, blen - r, "Relative to start of %s, " + "byte %d", ((sbp[15] & 0x20) ? + "segment descriptor" : "parameter list"), + sg_get_unaligned_be16(sbp + 16)); + if (sbp[15] & 0x08) + r += sg_scnpr(b + r, blen - r, " bit %d\n", + sbp[15] & 0x07); else - r += my_snprintf(b + r, blen - r, "\n"); + r += sg_scnpr(b + r, blen - r, "\n"); break; case SPC_SK_UNIT_ATTENTION: - r += my_snprintf(b + r, blen - r, "%s Unit attention " - "condition queue: ", lip); - r += my_snprintf(b + r, blen - r, "overflow flag is %d\n", - !!(sense_buffer[15] & 0x1)); + r += sg_scnpr(b + r, blen - r, "%s Unit attention " + "condition queue: ", lip); + r += sg_scnpr(b + r, blen - r, "overflow flag is %d\n", + !!(sbp[15] & 0x1)); break; default: - r += my_snprintf(b + r, blen - r, "%s Sense_key: 0x%x " - "unexpected\n", lip, ssh.sense_key); + r += sg_scnpr(b + r, blen - r, "%s Sense_key: 0x%x " + "unexpected\n", lip, ssh.sense_key); break; } } if (r > 0) - n += my_snprintf(buff + n, buff_len - n, "%s", b); + n += sg_scnpr(cbp + n, cblen - n, "%s", b); } else - n += my_snprintf(buff + n, buff_len - n, "%s fixed descriptor " - "length too short, len=%d\n", lip, len); - } else { /* non-extended SCSI-1 sense data ?? */ - if (sb_len < 4) { - n += my_snprintf(buff + n, buff_len - n, "%ssense buffer too " - "short (4 byte minimum)\n", lip); - return n; + n += sg_scnpr(cbp + n, cblen - n, "%s fixed descriptor length " + "too short, len=%d\n", lip, len); + } else { /* unable to normalise sense buffer, something irregular */ + if (sb_len < 4) { /* Too short */ + n += sg_scnpr(cbp + n, cblen - n, "%ssense buffer too short (4 " + "byte minimum)\n", lip); + goto check_raw; + } + if (0x7f == resp_code) { /* Vendor specific */ + n += sg_scnpr(cbp + n, cblen - n, "%sVendor specific sense " + "buffer, in hex:\n", lip); + n += hex2str(sbp, sb_len, lip, -1, cblen - n, cbp + n); + return n; /* no need to check raw, just output in hex */ } + /* non-extended SCSI-1 sense data ?? */ r = 0; if (strlen(lip) > 0) - r += my_snprintf(b + r, blen - r, "%s", lip); - r += my_snprintf(b + r, blen - r, "Probably uninitialized data.\n%s " - "Try to view as SCSI-1 non-extended sense:\n", lip); - r += my_snprintf(b + r, blen - r, " AdValid=%d Error class=%d " - "Error code=%d\n", !!(sense_buffer[0] & 0x80), - ((sense_buffer[0] >> 4) & 0x7), - (sense_buffer[0] & 0xf)); - if (sense_buffer[0] & 0x80) - r += my_snprintf(b + r, blen - r, "%s lba=0x%x\n", lip, - sg_get_unaligned_be24(sense_buffer + 1) & 0x1fffff); - n += my_snprintf(buff + n, buff_len - n, "%s\n", b); - len = sb_len; - if (len > 32) - len = 32; /* trim in case there is a lot of rubbish */ + r += sg_scnpr(b + r, blen - r, "%s", lip); + r += sg_scnpr(b + r, blen - r, "Probably uninitialized data.\n%s " + "Try to view as SCSI-1 non-extended sense:\n", lip); + r += sg_scnpr(b + r, blen - r, " AdValid=%d Error class=%d Error " + "code=%d\n", valid, ((sbp[0] >> 4) & 0x7), + (sbp[0] & 0xf)); + if (valid) + sg_scnpr(b + r, blen - r, "%s lba=0x%x\n", lip, + sg_get_unaligned_be24(sbp + 1) & 0x1fffff); + n += sg_scnpr(cbp + n, cblen - n, "%s\n", b); } +check_raw: if (raw_sinfo) { + int embed_len; char z[64]; - n += my_snprintf(buff + n, buff_len - n, "%s Raw sense data (in hex):" - "\n", lip); - if (n >= (buff_len - 1)) + n += sg_scnpr(cbp + n, cblen - n, "%s Raw sense data (in hex), " + "sb_len=%d", lip, sb_len); + if (n >= (cblen - 1)) return n; - snprintf(z, sizeof(z), "%.50s ", lip); - n += dStrHexStr((const char *)sense_buffer, len, z, 0, - buff_len - n, buff + n); + if ((sb_len > 7) && (sbp[0] >= 0x70) && (sbp[0] < 0x74)) { + embed_len = sbp[7] + 8; + n += sg_scnpr(cbp + n, cblen - n, ", embedded_len=%d\n", + embed_len); + } else { + embed_len = sb_len; + n += sg_scnpr(cbp + n, cblen - n, "\n"); + } + if (n >= (cblen - 1)) + return n; + + sg_scnpr(z, sizeof(z), "%.50s ", lip); + n += hex2str(sbp, embed_len, z, -1, cblen - n, cbp + n); } return n; } /* Print sense information */ void -sg_print_sense(const char * leadin, const unsigned char * sense_buffer, - int sb_len, int raw_sinfo) +sg_print_sense(const char * leadin, const uint8_t * sbp, int sb_len, + bool raw_sinfo) { - char b[2048]; + uint32_t pg_sz = sg_get_page_size(); + char *cp; + uint8_t *free_cp; - sg_get_sense_str(leadin, sense_buffer, sb_len, raw_sinfo, sizeof(b), b); - pr2ws("%s", b); + cp = (char *)sg_memalign(pg_sz, pg_sz, &free_cp, false); + if (NULL == cp) + return; + sg_get_sense_str(leadin, sbp, sb_len, raw_sinfo, pg_sz, cp); + pr2ws("%s", cp); + free(free_cp); +} + +/* This examines exit_status and if an error message is known it is output + * as a string to 'b' and true is returned. If 'longer' is true and extra + * information is available then it is added to the output. If no error + * message is available a null character is output and false is returned. + * If exit_status is zero (no error) and 'longer' is true then the string + * 'No errors' is output; if 'longer' is false then a null character is + * output; in both cases true is returned. If exit_status is negative then + * a null character is output and false is returned. All messages are a + * single line (less than 80 characters) with no trailing LF. The output + * string including the trailing null character is no longer than b_len. + * exit_status represents the Unix exit status available after a utility + * finishes executing (for whatever reason). */ +bool sg_exit2str(int exit_status, bool longer, int b_len, char *b) +{ + const struct sg_value_2names_t * ess = sg_exit_str_arr; + + if ((b_len < 1) || (NULL == b)) + return false; + /* if there is a valid buffer, initialize it to a valid empty string */ + b[0] = '\0'; + if (exit_status < 0) + return false; + else if ((0 == exit_status) || (SG_LIB_OK_FALSE == exit_status)) { + if (longer) + goto fini; + return true; + } + + if ((exit_status > SG_LIB_OS_BASE_ERR) && /* 51 to 96 inclusive */ + (exit_status < SG_LIB_CAT_MALFORMED)) { + snprintf(b, b_len, "%s%s", (longer ? "OS error: " : ""), + safe_strerror(exit_status - SG_LIB_OS_BASE_ERR)); + return true; + } else if ((exit_status > 128) && (exit_status < 255)) { + snprintf(b, b_len, "Utility stopped/aborted by signal number: %d", + exit_status - 128); + return true; + } +fini: + for ( ; ess->name; ++ess) { + if (exit_status == ess->value) + break; + } + if (ess->name) { + if (longer && ess->name2) + snprintf(b, b_len, "%s, %s", ess->name, ess->name2); + else + snprintf(b, b_len, "%s", ess->name); + return true; + } + return false; } -/* See description in sg_lib.h header file */ +static bool +sg_if_can2fp(const char * leadin, int exit_status, FILE * fp) +{ + char b[256]; + const char * s = leadin ? leadin : ""; + + if ((0 == exit_status) || (SG_LIB_OK_FALSE == exit_status)) + return true; /* don't print anything */ + else if (sg_exit2str(exit_status, false, sizeof(b), b)) { + fprintf(fp, "%s%s\n", s, b); + return true; + } else + return false; +} + +/* This examines exit_status and if an error message is known it is output + * to stdout/stderr and true is returned. If no error message is + * available nothing is output and false is returned. If exit_status is + * zero (no error) nothing is output and true is returned. If exit_status + * is negative then nothing is output and false is returned. If leadin is + * non-NULL then it is printed before the error message. All messages are + * a single line with a trailing LF. */ +bool +sg_if_can2stdout(const char * leadin, int exit_status) +{ + return sg_if_can2fp(leadin, exit_status, stdout); +} + +/* See sg_if_can2stdout() comments */ +bool +sg_if_can2stderr(const char * leadin, int exit_status) +{ + return sg_if_can2fp(leadin, exit_status, + sg_warnings_strm ? sg_warnings_strm : stderr); +} + +/* If os_err_num is within bounds then the returned value is 'os_err_num + + * SG_LIB_OS_BASE_ERR' otherwise SG_LIB_OS_BASE_ERR is returned. If + * os_err_num is 0 then 0 is returned. */ int -sg_scsi_normalize_sense(const unsigned char * sensep, int sb_len, +sg_convert_errno(int os_err_num) +{ + if (os_err_num <= 0) { + if (os_err_num < 0) + return SG_LIB_OS_BASE_ERR; + return os_err_num; /* os_err_num of 0 maps to 0 */ + } + if (os_err_num < (SG_LIB_CAT_MALFORMED - SG_LIB_OS_BASE_ERR)) + return SG_LIB_OS_BASE_ERR + os_err_num; + return SG_LIB_OS_BASE_ERR; +} + +static const char * const bad_sense_cat = "Bad sense category"; + +/* Yield string associated with sense category. Returns 'b' (or pointer + * to "Bad sense category" if 'b' is NULL). If sense_cat unknown then + * yield "Sense category: " string. The original 'sense + * category' concept has been expanded to most detected errors and is + * returned by these utilities as their exit status value (an (unsigned) + * 8 bit value where 0 means good (i.e. no errors)). Uses sg_exit2str() + * function. */ +const char * +sg_get_category_sense_str(int sense_cat, int b_len, char * b, int verbose) +{ + int n; + + if (NULL == b) + return bad_sense_cat; + if (b_len <= 0) + return b; + if (! sg_exit2str(sense_cat, (verbose > 0), b_len, b)) { + n = sg_scnpr(b, b_len, "Sense category: %d", sense_cat); + if ((0 == verbose) && (n < (b_len - 1))) + sg_scnpr(b + n, b_len - n, ", try '-v' option for more " + "information"); + } + return b; /* Note that a valid C string is returned in all cases */ +} + +/* See description in sg_lib.h header file */ +bool +sg_scsi_normalize_sense(const uint8_t * sbp, int sb_len, struct sg_scsi_sense_hdr * sshp) { + uint8_t resp_code; if (sshp) memset(sshp, 0, sizeof(struct sg_scsi_sense_hdr)); - if ((NULL == sensep) || (0 == sb_len) || (0x70 != (0x70 & sensep[0]))) - return 0; + if ((NULL == sbp) || (sb_len < 1)) + return false; + resp_code = 0x7f & sbp[0]; + if ((resp_code < 0x70) || (resp_code > 0x73)) + return false; if (sshp) { - sshp->response_code = (0x7f & sensep[0]); + sshp->response_code = resp_code; if (sshp->response_code >= 0x72) { /* descriptor format */ if (sb_len > 1) - sshp->sense_key = (0xf & sensep[1]); + sshp->sense_key = (0xf & sbp[1]); if (sb_len > 2) - sshp->asc = sensep[2]; + sshp->asc = sbp[2]; if (sb_len > 3) - sshp->ascq = sensep[3]; + sshp->ascq = sbp[3]; if (sb_len > 7) - sshp->additional_length = sensep[7]; + sshp->additional_length = sbp[7]; + sshp->byte4 = sbp[4]; /* bit 7: SDAT_OVFL bit */ + /* sbp[5] and sbp[6] reserved for descriptor format */ } else { /* fixed format */ if (sb_len > 2) - sshp->sense_key = (0xf & sensep[2]); + sshp->sense_key = (0xf & sbp[2]); if (sb_len > 7) { - sb_len = (sb_len < (sensep[7] + 8)) ? sb_len : - (sensep[7] + 8); + sb_len = (sb_len < (sbp[7] + 8)) ? sb_len : (sbp[7] + 8); if (sb_len > 12) - sshp->asc = sensep[12]; + sshp->asc = sbp[12]; if (sb_len > 13) - sshp->ascq = sensep[13]; + sshp->ascq = sbp[13]; + } + if (sb_len > 6) { /* lower 3 bytes of INFO field */ + sshp->byte4 = sbp[4]; + sshp->byte5 = sbp[5]; + sshp->byte6 = sbp[6]; } } } - return 1; + return true; } -/* Returns a SG_LIB_CAT_* value. If cannot decode sense_buffer or a less - * common sense key then return SG_LIB_CAT_SENSE .*/ +/* Returns a SG_LIB_CAT_* value. If cannot decode sense buffer (sbp) or a + * less common sense key then return SG_LIB_CAT_SENSE .*/ int -sg_err_category_sense(const unsigned char * sense_buffer, int sb_len) +sg_err_category_sense(const uint8_t * sbp, int sb_len) { struct sg_scsi_sense_hdr ssh; - if ((sense_buffer && (sb_len > 2)) && - (sg_scsi_normalize_sense(sense_buffer, sb_len, &ssh))) { + if ((sbp && (sb_len > 2)) && + (sg_scsi_normalize_sense(sbp, sb_len, &ssh))) { switch (ssh.sense_key) { /* 0 to 0x1f */ case SPC_SK_NO_SENSE: return SG_LIB_CAT_NO_SENSE; @@ -1697,6 +2193,8 @@ case SPC_SK_ILLEGAL_REQUEST: if ((0x20 == ssh.asc) && (0x0 == ssh.ascq)) return SG_LIB_CAT_INVALID_OP; + else if ((0x21 == ssh.asc) && (0x0 == ssh.ascq)) + return SG_LIB_LBA_OUT_OF_RANGE; else return SG_LIB_CAT_ILLEGAL_REQ; break; @@ -1723,25 +2221,22 @@ /* Beware: gives wrong answer for variable length command (opcode=0x7f) */ int -sg_get_command_size(unsigned char opcode) +sg_get_command_size(uint8_t opcode) { switch ((opcode >> 5) & 0x7) { case 0: return 6; - case 1: case 2: case 6: case 7: - return 10; case 3: case 5: return 12; - break; case 4: return 16; - default: + default: /* 1, 2, 6, 7 */ return 10; } } void -sg_get_command_name(const unsigned char * cmdp, int peri_type, int buff_len, +sg_get_command_name(const uint8_t * cdbp, int peri_type, int buff_len, char * buff) { int service_action; @@ -1752,13 +2247,13 @@ buff[0] = '\0'; return; } - if (NULL == cmdp) { - my_snprintf(buff, buff_len, "%s", " command pointer"); + if (NULL == cdbp) { + sg_scnpr(buff, buff_len, "%s", " command pointer"); return; } - service_action = (SG_VARIABLE_LENGTH_CMD == cmdp[0]) ? - sg_get_unaligned_be16(cmdp + 8) : (cmdp[1] & 0x1f); - sg_get_opcode_sa_name(cmdp[0], service_action, peri_type, buff_len, buff); + service_action = (SG_VARIABLE_LENGTH_CMD == cdbp[0]) ? + sg_get_unaligned_be16(cdbp + 8) : (cdbp[1] & 0x1f); + sg_get_opcode_sa_name(cdbp[0], service_action, peri_type, buff_len, buff); } struct op_code2sa_t { @@ -1782,7 +2277,8 @@ "Persistent reserve out"}, {SG_3PARTY_COPY_OUT, -1, sg_lib_xcopy_sa_arr, NULL}, {SG_3PARTY_COPY_IN, -1, sg_lib_rec_copy_sa_arr, NULL}, - {SG_READ_BUFFER, -1, sg_lib_read_buff_arr, "Read buffer"}, + {SG_READ_BUFFER, -1, sg_lib_read_buff_arr, "Read buffer(10)"}, + {SG_READ_BUFFER_16, -1, sg_lib_read_buff_arr, "Read buffer(16)"}, {SG_READ_ATTRIBUTE, -1, sg_lib_read_attr_arr, "Read attribute"}, {SG_READ_POSITION, 1, sg_lib_read_pos_arr, "Read position"}, {SG_SANITIZE, 0, sg_lib_sanitize_sa_arr, "Sanitize"}, @@ -1793,7 +2289,7 @@ }; void -sg_get_opcode_sa_name(unsigned char cmd_byte0, int service_action, +sg_get_opcode_sa_name(uint8_t cmd_byte0, int service_action, int peri_type, int buff_len, char * buff) { int d_pdt; @@ -1808,6 +2304,8 @@ return; } + if (peri_type < 0) + peri_type = 0; d_pdt = sg_lib_pdt_decay(peri_type); for (osp = op_code2sa_arr; osp->arr; ++osp) { if ((int)cmd_byte0 == osp->op_code) { @@ -1815,14 +2313,14 @@ vnp = get_value_name(osp->arr, service_action, peri_type); if (vnp) { if (osp->prefix) - my_snprintf(buff, buff_len, "%s, %s", osp->prefix, - vnp->name); + sg_scnpr(buff, buff_len, "%s, %s", osp->prefix, + vnp->name); else - my_snprintf(buff, buff_len, "%s", vnp->name); + sg_scnpr(buff, buff_len, "%s", vnp->name); } else { sg_get_opcode_name(cmd_byte0, peri_type, sizeof(b), b); - my_snprintf(buff, buff_len, "%s service action=0x%x", - b, service_action); + sg_scnpr(buff, buff_len, "%s service action=0x%x", b, + service_action); } } else sg_get_opcode_name(cmd_byte0, peri_type, buff_len, buff); @@ -1833,7 +2331,7 @@ } void -sg_get_opcode_name(unsigned char cmd_byte0, int peri_type, int buff_len, +sg_get_opcode_name(uint8_t cmd_byte0, int peri_type, int buff_len, char * buff) { const struct sg_lib_value_name_t * vnp; @@ -1846,7 +2344,7 @@ return; } if (SG_VARIABLE_LENGTH_CMD == cmd_byte0) { - my_snprintf(buff, buff_len, "%s", "Variable length"); + sg_scnpr(buff, buff_len, "%s", "Variable length"); return; } grp = (cmd_byte0 >> 5) & 0x7; @@ -1858,23 +2356,62 @@ case 5: vnp = get_value_name(sg_lib_normal_opcodes, cmd_byte0, peri_type); if (vnp) - my_snprintf(buff, buff_len, "%s", vnp->name); + sg_scnpr(buff, buff_len, "%s", vnp->name); else - my_snprintf(buff, buff_len, "Opcode=0x%x", (int)cmd_byte0); + sg_scnpr(buff, buff_len, "Opcode=0x%x", (int)cmd_byte0); break; case 3: - my_snprintf(buff, buff_len, "Reserved [0x%x]", (int)cmd_byte0); + sg_scnpr(buff, buff_len, "Reserved [0x%x]", (int)cmd_byte0); break; case 6: case 7: - my_snprintf(buff, buff_len, "Vendor specific [0x%x]", (int)cmd_byte0); - break; - default: - my_snprintf(buff, buff_len, "Opcode=0x%x", (int)cmd_byte0); + sg_scnpr(buff, buff_len, "Vendor specific [0x%x]", (int)cmd_byte0); break; } } +/* Fetch NVMe command name given first byte (byte offset 0 in 64 byte + * command) of command. Gets Admin NVMe command name if 'admin' is true + * (e.g. opcode=0x6 -> Identify), otherwise gets NVM command set name + * (e.g. opcode=0 -> Flush). Returns 'buff'. */ +char * +sg_get_nvme_opcode_name(uint8_t cmd_byte0, bool admin, int buff_len, + char * buff) +{ + const struct sg_lib_simple_value_name_t * vnp = admin ? + sg_lib_nvme_admin_cmd_arr : sg_lib_nvme_nvm_cmd_arr; + + if ((NULL == buff) || (buff_len < 1)) + return buff; + else if (1 == buff_len) { + buff[0] = '\0'; + return buff; + } + for ( ; vnp->name; ++vnp) { + if (cmd_byte0 == (uint8_t)vnp->value) { + snprintf(buff, buff_len, "%s", vnp->name); + return buff; + } + } + if (admin) { + if (cmd_byte0 >= 0xc0) + snprintf(buff, buff_len, "Vendor specific opcode: 0x%x", + cmd_byte0); + else if (cmd_byte0 >= 0x80) + snprintf(buff, buff_len, "Command set specific opcode: 0x%x", + cmd_byte0); + else + snprintf(buff, buff_len, "Unknown opcode: 0x%x", cmd_byte0); + } else { /* NVM (non-Admin) command set */ + if (cmd_byte0 >= 0x80) + snprintf(buff, buff_len, "Vendor specific opcode: 0x%x", + cmd_byte0); + else + snprintf(buff, buff_len, "Unknown opcode: 0x%x", cmd_byte0); + } + return buff; +} + /* Iterates to next designation descriptor in the device identification * VPD page. The 'initial_desig_desc' should point to start of first * descriptor with 'page_len' being the number of valid bytes in that @@ -1885,191 +2422,319 @@ * termination. Matches association, designator_type and/or code_set when * any of those values are greater than or equal to zero. */ int -sg_vpd_dev_id_iter(const unsigned char * initial_desig_desc, int page_len, +sg_vpd_dev_id_iter(const uint8_t * initial_desig_desc, int page_len, int * off, int m_assoc, int m_desig_type, int m_code_set) { - const unsigned char * ucp; - int k, c_set, assoc, desig_type; + bool fltr = ((m_assoc >= 0) || (m_desig_type >= 0) || (m_code_set >= 0)); + int k = *off; + const uint8_t * bp = initial_desig_desc; - for (k = *off, ucp = initial_desig_desc ; (k + 3) < page_len; ) { - k = (k < 0) ? 0 : (k + ucp[k + 3] + 4); + while ((k + 3) < page_len) { + k = (k < 0) ? 0 : (k + bp[k + 3] + 4); if ((k + 4) > page_len) break; - c_set = (ucp[k] & 0xf); - if ((m_code_set >= 0) && (m_code_set != c_set)) - continue; - assoc = ((ucp[k + 1] >> 4) & 0x3); - if ((m_assoc >= 0) && (m_assoc != assoc)) - continue; - desig_type = (ucp[k + 1] & 0xf); - if ((m_desig_type >= 0) && (m_desig_type != desig_type)) - continue; + if (fltr) { + if (m_code_set >= 0) { + if ((bp[k] & 0xf) != m_code_set) + continue; + } + if (m_assoc >= 0) { + if (((bp[k + 1] >> 4) & 0x3) != m_assoc) + continue; + } + if (m_desig_type >= 0) { + if ((bp[k + 1] & 0xf) != m_desig_type) + continue; + } + } *off = k; return 0; } return (k == page_len) ? -1 : -2; } -static const char * bad_sense_cat = "Bad sense category"; - -/* Yield string associated with sense++ category. Returns 'buff' (or pointer - * to "Bad sense category" if 'buff' is NULL). If sense_cat unknown then - * yield "Sense category: " string. */ +static const char * sg_sfs_spc_reserved = "SPC Reserved"; +static const char * sg_sfs_sbc_reserved = "SBC Reserved"; +static const char * sg_sfs_ssc_reserved = "SSC Reserved"; +static const char * sg_sfs_zbc_reserved = "ZBC Reserved"; +static const char * sg_sfs_reserved = "Reserved"; + +/* Yield SCSI Feature Set (sfs) string. When 'peri_type' is < -1 (or > 31) + * returns pointer to string (same as 'buff') associated with 'sfs_code'. + * When 'peri_type' is between -1 (for SPC) and 31 (inclusive) then a match + * on both 'sfs_code' and 'peri_type' is required. If 'foundp' is not NULL + * then where it points is set to true if a match is found else it is set to + * false. If 'buff' is not NULL then in the case of a match a descriptive + * string is written to 'buff' while if there is not a not then a string + * ending in "Reserved" is written (and may be prefixed with SPC, SBC, SSC + * or ZBC). Returns 'buff' (i.e. a pointer value) even if it is NULL. + * Example: + * char b[64]; + * ... + * printf("%s\n", sg_get_sfs_str(sfs_code, -2, sizeof(b), b, NULL, 0)); + */ const char * -sg_get_category_sense_str(int sense_cat, int buff_len, char * buff, - int verbose) +sg_get_sfs_str(uint16_t sfs_code, int peri_type, int buff_len, char * buff, + bool * foundp, int verbose) { - int n; + const struct sg_lib_value_name_t * vnp = NULL; + int n = 0; + int my_pdt; - if (NULL == buff) - return bad_sense_cat; - if (buff_len <= 0) - return buff; - switch (sense_cat) { - case SG_LIB_CAT_CLEAN: /* 0 */ - snprintf(buff, buff_len, "No errors"); - break; - case SG_LIB_SYNTAX_ERROR: /* 1 */ - snprintf(buff, buff_len, "Syntax error"); - break; - case SG_LIB_CAT_NOT_READY: /* 2 */ - n = snprintf(buff, buff_len, "Not ready"); - if (verbose && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, " sense key"); - break; - case SG_LIB_CAT_MEDIUM_HARD: /* 3 */ - n = snprintf(buff, buff_len, "Medium or hardware error"); - if (verbose && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, " sense key (plus blank check)"); - break; - case SG_LIB_CAT_ILLEGAL_REQ: /* 5 */ - n = snprintf(buff, buff_len, "Illegal request"); - if (verbose && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, " sense key, apart from Invalid " - "opcode"); - break; - case SG_LIB_CAT_UNIT_ATTENTION: /* 6 */ - n = snprintf(buff, buff_len, "Unit attention"); - if (verbose && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, " sense key"); - break; - case SG_LIB_CAT_DATA_PROTECT: /* 7 */ - n = snprintf(buff, buff_len, "Data protect"); - if (verbose && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, " sense key, write protected " - "media?"); - break; - case SG_LIB_CAT_INVALID_OP: /* 9 */ - n = snprintf(buff, buff_len, "Illegal request, invalid opcode"); - if (verbose && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, " sense key"); - break; - case SG_LIB_CAT_COPY_ABORTED: /* 10 */ - n = snprintf(buff, buff_len, "Copy aborted"); - if (verbose && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, " sense key"); - break; - case SG_LIB_CAT_ABORTED_COMMAND: /* 11 */ - n = snprintf(buff, buff_len, "Aborted command"); - if (verbose && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, " sense key, other than " - "protection related (asc=0x10)"); - break; - case SG_LIB_CAT_MISCOMPARE: /* 14 */ - n = snprintf(buff, buff_len, "Miscompare"); - if (verbose && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, " sense key"); - break; - case SG_LIB_FILE_ERROR: /* 15 */ - snprintf(buff, buff_len, "File error"); - break; - case SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO: /* 17 */ - snprintf(buff, buff_len, "Illegal request with info"); - break; - case SG_LIB_CAT_MEDIUM_HARD_WITH_INFO: /* 18 */ - snprintf(buff, buff_len, "Medium or hardware error with info"); - break; - case SG_LIB_CAT_NO_SENSE: /* 20 */ - n = snprintf(buff, buff_len, "No sense key"); - if (verbose && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, " probably additional sense " - "information"); - break; - case SG_LIB_CAT_RECOVERED: /* 21 */ - n = snprintf(buff, buff_len, "Recovered error"); - if (verbose && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, " sense key"); - break; - case SG_LIB_CAT_RES_CONFLICT: /* 24 */ - n = snprintf(buff, buff_len, "Reservation conflict"); - if (verbose && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, " SCSI status"); - break; - case SG_LIB_CAT_CONDITION_MET: /* 25 */ - n = snprintf(buff, buff_len, "Condition met"); - if (verbose && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, " SCSI status"); - break; - case SG_LIB_CAT_BUSY: /* 26 */ - n = snprintf(buff, buff_len, "Busy"); - if (verbose && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, " SCSI status"); - break; - case SG_LIB_CAT_TS_FULL: /* 27 */ - n = snprintf(buff, buff_len, "Task set full"); - if (verbose && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, " SCSI status"); - break; - case SG_LIB_CAT_ACA_ACTIVE: /* 28 */ - n = snprintf(buff, buff_len, "ACA active"); - if (verbose && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, " SCSI status"); - break; - case SG_LIB_CAT_TASK_ABORTED: /* 29 */ - n = snprintf(buff, buff_len, "Task aborted"); - if (verbose && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, " SCSI status"); - break; - case SG_LIB_CAT_TIMEOUT: /* 33 */ - snprintf(buff, buff_len, "SCSI command timeout"); - break; - case SG_LIB_CAT_PROTECTION: /* 40 */ - n = snprintf(buff, buff_len, "Aborted command, protection"); - if (verbose && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, " information (PI) problem"); - break; - case SG_LIB_CAT_PROTECTION_WITH_INFO: /* 41 */ - n = snprintf(buff, buff_len, "Aborted command with info, protection"); - if (verbose && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, " information (PI) problem"); - break; - case SG_LIB_CAT_MALFORMED: /* 97 */ - n = snprintf(buff, buff_len, "Malformed response"); - if (verbose && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, " to SCSI command"); - break; - case SG_LIB_CAT_SENSE: /* 98 */ - n = snprintf(buff, buff_len, "Some other sense data problem"); - if (verbose && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, ", try '-v' option for more " - "information"); - break; - case SG_LIB_CAT_OTHER: /* 99 */ - n = snprintf(buff, buff_len, "Some other error/warning has occurred"); - if ((0 == verbose) && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, ", possible transport of driver " - "issue"); - break; - default: - n = snprintf(buff, buff_len, "Sense category: %d", sense_cat); - if ((0 == verbose) && (n < (buff_len - 1))) - snprintf(buff + n, buff_len - n, ", try '-v' option for more " - "information"); - break; + if ((NULL == buff) || (buff_len < 1)) { + if (foundp) + *foundp = false; + return NULL; + } else if (1 == buff_len) { + buff[0] = '\0'; + if (foundp) + *foundp = false; + return NULL; } + my_pdt = ((peri_type < -1) || (peri_type > 0x1f)) ? -2 : peri_type; + vnp = get_value_name(sg_lib_scsi_feature_sets, sfs_code, my_pdt); + if (vnp && (-2 != my_pdt)) { + if (peri_type != vnp->peri_dev_type) + vnp = NULL; /* shouldn't really happen */ + } + if (foundp) + *foundp = vnp ? true : false; + if (sfs_code < 0x100) { /* SPC Feature Sets */ + if (vnp) { + if (verbose) + n += sg_scnpr(buff, buff_len, "SPC %s", vnp->name); + else + n += sg_scnpr(buff, buff_len, "%s", vnp->name); + } else + n += sg_scnpr(buff, buff_len, "%s", sg_sfs_spc_reserved); + } else if (sfs_code < 0x200) { /* SBC Feature Sets */ + if (vnp) { + if (verbose) + n += sg_scnpr(buff, buff_len, "SBC %s", vnp->name); + else + n += sg_scnpr(buff, buff_len, "%s", vnp->name); + } else + n += sg_scnpr(buff, buff_len, "%s", sg_sfs_sbc_reserved); + } else if (sfs_code < 0x300) { /* SSC Feature Sets */ + if (vnp) { + if (verbose) + n += sg_scnpr(buff, buff_len, "SSC %s", vnp->name); + else + n += sg_scnpr(buff, buff_len, "%s", vnp->name); + } else + n += sg_scnpr(buff, buff_len, "%s", sg_sfs_ssc_reserved); + } else if (sfs_code < 0x400) { /* ZBC Feature Sets */ + if (vnp) { + if (verbose) + n += sg_scnpr(buff, buff_len, "ZBC %s", vnp->name); + else + n += sg_scnpr(buff, buff_len, "%s", vnp->name); + } else + n += sg_scnpr(buff, buff_len, "%s", sg_sfs_zbc_reserved); + } else { /* Other SCSI Feature Sets */ + if (vnp) { + if (verbose) + n += sg_scnpr(buff, buff_len, "[unrecognized PDT] %s", + vnp->name); + else + n += sg_scnpr(buff, buff_len, "%s", vnp->name); + } else + n += sg_scnpr(buff, buff_len, "%s", sg_sfs_reserved); + + } + if (verbose > 4) + pr2ws("%s: length of returned string (n) %d\n", __func__, n); return buff; } +/* This is a heuristic that takes into account the command bytes and length + * to decide whether the presented unstructured sequence of bytes could be + * a SCSI command. If so it returns true otherwise false. Vendor specific + * SCSI commands (i.e. opcodes from 0xc0 to 0xff), if presented, are assumed + * to follow SCSI conventions (i.e. length of 6, 10, 12 or 16 bytes). The + * only SCSI commands considered above 16 bytes of length are the Variable + * Length Commands (opcode 0x7f) and the XCDB wrapped commands (opcode 0x7e). + * Both have an inbuilt length field which can be cross checked with clen. + * No NVMe commands (64 bytes long plus some extra added by some OSes) have + * opcodes 0x7e or 0x7f yet. ATA is register based but SATA has FIS + * structures that are sent across the wire. The FIS register structure is + * used to move a command from a SATA host to device, but the ATA 'command' + * is not the first byte. So it is harder to say what will happen if a + * FIS structure is presented as a SCSI command, hopfully there is a low + * probability this function will yield true in that case. */ +bool +sg_is_scsi_cdb(const uint8_t * cdbp, int clen) +{ + int ilen, sa; + uint8_t opcode; + uint8_t top3bits; + + if (clen < 6) + return false; + opcode = cdbp[0]; + top3bits = opcode >> 5; + if (0x3 == top3bits) { + if ((clen < 12) || (clen % 4)) + return false; /* must be modulo 4 and 12 or more bytes */ + switch (opcode) { + case 0x7e: /* Extended cdb (XCDB) */ + ilen = 4 + sg_get_unaligned_be16(cdbp + 2); + return (ilen == clen); + case 0x7f: /* Variable Length cdb */ + ilen = 8 + cdbp[7]; + sa = sg_get_unaligned_be16(cdbp + 8); + /* service action (sa) 0x0 is reserved */ + return ((ilen == clen) && sa); + default: + return false; + } + } else if (clen <= 16) { + switch (clen) { + case 6: + if (top3bits > 0x5) /* vendor */ + return true; + return (0x0 == top3bits); /* 6 byte cdb */ + case 10: + if (top3bits > 0x5) /* vendor */ + return true; + return ((0x1 == top3bits) || (0x2 == top3bits)); /* 10 byte cdb */ + case 16: + if (top3bits > 0x5) /* vendor */ + return true; + return (0x4 == top3bits); /* 16 byte cdb */ + case 12: + if (top3bits > 0x5) /* vendor */ + return true; + return (0x5 == top3bits); /* 12 byte cdb */ + default: + return false; + } + } + /* NVMe probably falls out here, clen > 16 and (opcode < 0x60 or + * opcode > 0x7f). */ + return false; +} + +/* Yield string associated with NVMe command status value in sct_sc. It + * expects to decode DW3 bits 27:17 from the completion queue. Bits 27:25 + * are the Status Code Type (SCT) and bits 24:17 are the Status Code (SC). + * Bit 17 in DW3 should be bit 0 in sct_sc. If no status string is found + * a string of the form "Reserved [0x]" is generated. + * Returns 'buff'. Does nothing if buff_len<=0 or if buff is NULL.*/ +char * +sg_get_nvme_cmd_status_str(uint16_t sct_sc, int b_len, char * b) +{ + int k; + uint16_t s = 0x3ff & sct_sc; + const struct sg_lib_value_name_t * vp = sg_lib_nvme_cmd_status_arr; + + if ((b_len <= 0) || (NULL == b)) + return b; + else if (1 == b_len) { + b[0] = '\0'; + return b; + } + for (k = 0; (vp->name && (k < 1000)); ++k, ++vp) { + if (s == (uint16_t)vp->value) { + strncpy(b, vp->name, b_len); + b[b_len - 1] = '\0'; + return b; + } + } + if (k >= 1000) + pr2ws("%s: where is sentinel for sg_lib_nvme_cmd_status_arr ??\n", + __func__); + snprintf(b, b_len, "Reserved [0x%x]", sct_sc); + return b; +} + +/* Attempts to map NVMe status value ((SCT << 8) | SC) to SCSI status, + * sense_key, asc and ascq tuple. If successful returns true and writes to + * non-NULL pointer arguments; otherwise returns false. */ +bool +sg_nvme_status2scsi(uint16_t sct_sc, uint8_t * status_p, uint8_t * sk_p, + uint8_t * asc_p, uint8_t * ascq_p) +{ + int k, ind; + uint16_t s = 0x3ff & sct_sc; + struct sg_lib_value_name_t * vp = sg_lib_nvme_cmd_status_arr; + struct sg_lib_4tuple_u8 * mp = sg_lib_scsi_status_sense_arr; + + for (k = 0; (vp->name && (k < 1000)); ++k, ++vp) { + if (s == (uint16_t)vp->value) + break; + } + if (k >= 1000) { + pr2ws("%s: where is sentinel for sg_lib_nvme_cmd_status_arr ??\n", + __func__); + return false; + } + if (NULL == vp->name) + return false; + ind = vp->peri_dev_type; + + + for (k = 0; (0xff != mp->t2) && k < 1000; ++k, ++mp) + ; /* count entries for valid index range */ + if (k >= 1000) { + pr2ws("%s: where is sentinel for sg_lib_scsi_status_sense_arr ??\n", + __func__); + return false; + } else if (ind >= k) + return false; + + mp = sg_lib_scsi_status_sense_arr + ind; + if (status_p) + *status_p = mp->t1; + if (sk_p) + *sk_p = mp->t2; + if (asc_p) + *asc_p = mp->t3; + if (ascq_p) + *ascq_p = mp->t4; + return true; +} + +/* Add vendor (sg3_utils) specific sense descriptor for the NVMe Status + * field. Assumes descriptor (i.e. not fixed) sense. Assumes sbp has room. */ +void +sg_nvme_desc2sense(uint8_t * sbp, bool dnr, bool more, uint16_t sct_sc) +{ + int len = sbp[7] + 8; + + sbp[len] = 0xde; /* vendor specific descriptor type */ + sbp[len + 1] = 6; /* descriptor is 8 bytes long */ + memset(sbp + len + 2, 0, 6); + if (dnr) + sbp[len + 5] = 0x80; + if (more) + sbp[len + 5] |= 0x40; + sg_put_unaligned_be16(sct_sc, sbp + len + 6); + sbp[7] += 8; +} + +/* Build minimum sense buffer, either descriptor type (desc=true) or fixed + * type (desc=false). Assume sbp has enough room (8 or 14 bytes + * respectively). sbp should have room for 32 or 18 bytes respectively */ +void +sg_build_sense_buffer(bool desc, uint8_t *sbp, uint8_t skey, uint8_t asc, + uint8_t ascq) +{ + if (desc) { + sbp[0] = 0x72; /* descriptor, current */ + sbp[1] = skey; + sbp[2] = asc; + sbp[3] = ascq; + sbp[7] = 0; + } else { + sbp[0] = 0x70; /* fixed, current */ + sbp[2] = skey; + sbp[7] = 0xa; /* Assumes length is 18 bytes */ + sbp[12] = asc; + sbp[13] = ascq; + } +} + /* safe_strerror() contributed by Clayton Weaver * Allows for situation in which strerror() is given a wild value (or the * C library is incomplete) and returns NULL. Still not thread safe. @@ -2089,8 +2754,7 @@ errstr = strerror(errnum); if (NULL == errstr) { len = strlen(safe_errbuf); - my_snprintf(safe_errbuf + len, sizeof(safe_errbuf) - len, "%i", - errnum); + sg_scnpr(safe_errbuf + len, sizeof(safe_errbuf) - len, "%i", errnum); return safe_errbuf; } return errstr; @@ -2120,7 +2784,7 @@ { const char * p = str; const char * formatstr; - unsigned char c; + uint8_t c; char buff[82]; int a = 0; int bpstart = 5; @@ -2134,10 +2798,8 @@ blen = (int)sizeof(buff); if (0 == no_ascii) /* address at left and ASCII at right */ formatstr = "%.76s\n"; - else if (no_ascii > 0) - formatstr = "%s\n"; /* was: "%.58s\n" */ - else /* negative: no address at left and no ASCII at right */ - formatstr = "%s\n"; /* was: "%.48s\n"; */ + else /* previously when > 0 str was "%.58s\n" */ + formatstr = "%s\n"; /* when < 0 str was: "%.48s\n" */ memset(buff, ' ', 80); buff[80] = '\0'; if (no_ascii < 0) { @@ -2147,8 +2809,7 @@ c = *p++; if (bpos == (bpstart + (8 * 3))) bpos++; - my_snprintf(&buff[bpos], blen - bpos, "%.2x", - (int)(unsigned char)c); + sg_scnpr(&buff[bpos], blen - bpos, "%.2x", (int)(uint8_t)c); buff[bpos + 2] = ' '; if ((k > 0) && (0 == ((k + 1) % 16))) { trimTrailingSpaces(buff); @@ -2166,7 +2827,7 @@ return; } /* no_ascii>=0, start each line with address (offset) */ - k = my_snprintf(buff + 1, blen - 1, "%.2x", a); + k = sg_scnpr(buff + 1, blen - 1, "%.2x", a); buff[k + 1] = ' '; for (i = 0; i < len; i++) { @@ -2174,12 +2835,12 @@ bpos += 3; if (bpos == (bpstart + (9 * 3))) bpos++; - my_snprintf(&buff[bpos], blen - bpos, "%.2x", (int)(unsigned char)c); + sg_scnpr(&buff[bpos], blen - bpos, "%.2x", (int)(uint8_t)c); buff[bpos + 2] = ' '; if (no_ascii) buff[cpos++] = ' '; else { - if ((c < ' ') || (c >= 0x7f)) + if (! my_isprint(c)) c = '.'; buff[cpos++] = c; } @@ -2191,7 +2852,7 @@ cpos = cpstart; a += 16; memset(buff, ' ', 80); - k = my_snprintf(buff + 1, blen - 1, "%.2x", a); + k = sg_scnpr(buff + 1, blen - 1, "%.2x", a); buff[k + 1] = ' '; } } @@ -2216,56 +2877,74 @@ (sg_warnings_strm ? sg_warnings_strm : stderr)); } +#define DSHS_LINE_BLEN 160 +#define DSHS_BPL 16 + /* Read 'len' bytes from 'str' and output as ASCII-Hex bytes (space * separated) to 'b' not to exceed 'b_len' characters. Each line * starts with 'leadin' (NULL for no leadin) and there are 16 bytes * per line with an extra space between the 8th and 9th bytes. 'format' - * is unused (currently), set to 0 . Returns number of bytes written - * to 'b' excluding the trailing '\0'. */ + * is 0 for repeat in printable ASCII ('.' for non printable) to + * right of each line; 1 don't (so just output ASCII hex). Returns + * number of bytes written to 'b' excluding the trailing '\0'. */ int -dStrHexStr(const char* str, int len, const char * leadin, int format, +dStrHexStr(const char * str, int len, const char * leadin, int format, int b_len, char * b) { + uint8_t c; + int bpstart, bpos, k, n, prior_ascii_len; + bool want_ascii; + char buff[DSHS_LINE_BLEN + 2]; + char a[DSHS_BPL + 1]; const char * p = str; - unsigned char c; - char buff[122]; - int bpstart, bpos, k, n; if (len <= 0) { if (b_len > 0) b[0] = '\0'; return 0; } - if (0 != format) { - ; /* do nothing different for now */ + if (b_len <= 0) + return 0; + want_ascii = !format; + if (want_ascii) { + memset(a, ' ', DSHS_BPL); + a[DSHS_BPL] = '\0'; } if (leadin) { bpstart = strlen(leadin); - /* Cap leadin at 60 characters */ - if (bpstart > 60) - bpstart = 60; + /* Cap leadin at (DSHS_LINE_BLEN - 70) characters */ + if (bpstart > (DSHS_LINE_BLEN - 70)) + bpstart = DSHS_LINE_BLEN - 70; } else bpstart = 0; bpos = bpstart; + prior_ascii_len = bpstart + (DSHS_BPL * 3) + 1; n = 0; - memset(buff, ' ', 120); - buff[120] = '\0'; + memset(buff, ' ', DSHS_LINE_BLEN); + buff[DSHS_LINE_BLEN] = '\0'; if (bpstart > 0) memcpy(buff, leadin, bpstart); for (k = 0; k < len; k++) { c = *p++; - if (bpos == (bpstart + (8 * 3))) - bpos++; - my_snprintf(&buff[bpos], (int)sizeof(buff) - bpos, "%.2x", - (int)(unsigned char)c); + if (bpos == (bpstart + ((DSHS_BPL / 2) * 3))) + bpos++; /* for extra space in middle of each line's hex */ + sg_scnpr(buff + bpos, (int)sizeof(buff) - bpos, "%.2x", + (int)(uint8_t)c); buff[bpos + 2] = ' '; - if ((k > 0) && (0 == ((k + 1) % 16))) { + if (want_ascii) + a[k % DSHS_BPL] = my_isprint(c) ? c : '.'; + if ((k > 0) && (0 == ((k + 1) % DSHS_BPL))) { trimTrailingSpaces(buff); - n += my_snprintf(b + n, b_len - n, "%s\n", buff); + if (want_ascii) { + n += sg_scnpr(b + n, b_len - n, "%-*s %s\n", + prior_ascii_len, buff, a); + memset(a, ' ', DSHS_BPL); + } else + n += sg_scnpr(b + n, b_len - n, "%s\n", buff); if (n >= (b_len - 1)) return n; + memset(buff, ' ', DSHS_LINE_BLEN); bpos = bpstart; - memset(buff, ' ', 120); if (bpstart > 0) memcpy(buff, leadin, bpstart); } else @@ -2273,20 +2952,43 @@ } if (bpos > bpstart) { trimTrailingSpaces(buff); - n += my_snprintf(b + n, b_len - n, "%s\n", buff); + if (want_ascii) + n += sg_scnpr(b + n, b_len - n, "%-*s %s\n", prior_ascii_len, + buff, a); + else + n += sg_scnpr(b + n, b_len - n, "%s\n", buff); } return n; } -/* Returns 1 when executed on big endian machine; else returns 0. +void +hex2stdout(const uint8_t * b_str, int len, int no_ascii) +{ + dStrHex((const char *)b_str, len, no_ascii); +} + +void +hex2stderr(const uint8_t * b_str, int len, int no_ascii) +{ + dStrHexErr((const char *)b_str, len, no_ascii); +} + +int +hex2str(const uint8_t * b_str, int len, const char * leadin, int format, + int b_len, char * b) +{ + return dStrHexStr((const char *)b_str, len, leadin, format, b_len, b); +} + +/* Returns true when executed on big endian machine; else returns false. * Useful for displaying ATA identify words (which need swapping on a * big endian machine). */ -int +bool sg_is_big_endian() { union u_t { - unsigned short s; - unsigned char c[sizeof(unsigned short)]; + uint16_t s; + uint8_t c[sizeof(uint16_t)]; } u; u.s = 0x0102; @@ -2294,10 +2996,34 @@ the most significant byte */ } -static unsigned short -swapb_ushort(unsigned short u) +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) { - unsigned short r; + 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) +{ + uint16_t r; r = (u >> 8) & 0xff; r |= ((u & 0xff) << 8); @@ -2312,15 +3038,15 @@ * = -1 only the ASCII-hex words are listed (i.e. without address) * = -2 only the ASCII-hex words, formatted for "hdparm --Istdin" * < -2 same as -1 - * If 'swapb' non-zero then bytes in each word swapped. Needs to be set + * If 'swapb' is true then bytes in each word swapped. Needs to be set * for ATA IDENTIFY DEVICE response on big-endian machines. */ void -dWordHex(const unsigned short* words, int num, int no_ascii, int swapb) +dWordHex(const uint16_t* words, int num, int no_ascii, bool swapb) { - const unsigned short * p = words; - unsigned short c; + const uint16_t * p = words; + uint16_t c; char buff[82]; - unsigned char upp, low; + uint8_t upp, low; int a = 0; const int bpstart = 3; const int cpstart = 52; @@ -2337,9 +3063,9 @@ for (k = 0; k < num; k++) { c = *p++; if (swapb) - c = swapb_ushort(c); + c = swapb_uint16(c); bpos += 5; - my_snprintf(&buff[bpos], blen - bpos, "%.4x", (unsigned int)c); + sg_scnpr(buff + bpos, blen - bpos, "%.4x", (my_uint)c); buff[bpos + 4] = ' '; if ((k > 0) && (0 == ((k + 1) % 8))) { if (-2 == no_ascii) @@ -2359,15 +3085,15 @@ return; } /* no_ascii>=0, start each line with address (offset) */ - k = my_snprintf(buff + 1, blen - 1, "%.2x", a); + k = sg_scnpr(buff + 1, blen - 1, "%.2x", a); buff[k + 1] = ' '; for (i = 0; i < num; i++) { c = *p++; if (swapb) - c = swapb_ushort(c); + c = swapb_uint16(c); bpos += 5; - my_snprintf(&buff[bpos], blen - bpos, "%.4x", (unsigned int)c); + sg_scnpr(buff + bpos, blen - bpos, "%.4x", (my_uint)c); buff[bpos + 4] = ' '; if (no_ascii) { buff[cpos++] = ' '; @@ -2376,10 +3102,10 @@ } else { upp = (c >> 8) & 0xff; low = c & 0xff; - if ((upp < 0x20) || (upp >= 0x7f)) + if (! my_isprint(upp)) upp = '.'; buff[cpos++] = upp; - if ((low < 0x20) || (low >= 0x7f)) + if (! my_isprint(low)) low = '.'; buff[cpos++] = low; buff[cpos++] = ' '; @@ -2390,7 +3116,7 @@ cpos = cpstart; a += 8; memset(buff, ' ', 80); - k = my_snprintf(buff + 1, blen - 1, "%.2x", a); + k = sg_scnpr(buff + 1, blen - 1, "%.2x", a); buff[k + 1] = ' '; } } @@ -2398,20 +3124,27 @@ printf("%.76s\n", buff); } -/* If the number in 'buf' can be decoded or the multiplier is unknown +/* If the number in 'buf' can not be decoded or the multiplier is unknown * then -1 is returned. Accepts a hex prefix (0x or 0X) or a decimal * multiplier suffix (as per GNU's dd (since 2002: SI and IEC 60027-2)). * Main (SI) multipliers supported: K, M, G. Ignore leading spaces and - * tabs; accept comma, space, tab and hash as terminator. */ + * tabs; accept comma, hyphen, space, tab and hash as terminator. + * Handles zero and positive values up to 2**31-1 . + * Experimental: left argument (must in with hexadecimal digit) added + * to, or multiplied, by right argument. No embedded spaces. + * Examples: '3+1k' (evaluates to 1027) and '0x34+1m'. */ int sg_get_num(const char * buf) { + bool is_hex = false; int res, num, n, len; unsigned int unum; char * cp; const char * b; + const char * b2p; char c = 'c'; - char c2, c3; + char c2 = '\0'; /* keep static checker happy */ + char c3 = '\0'; /* keep static checker happy */ char lb[16]; if ((NULL == buf) || ('\0' == buf[0])) @@ -2422,10 +3155,10 @@ if (n == len) return -1; buf += n; - len -=n; + len -= n; } /* following hack to keep C++ happy */ - cp = strpbrk((char *)buf, " \t,#"); + cp = strpbrk((char *)buf, " \t,#-"); if (cp) { len = cp - buf; n = (int)sizeof(lb) - 1; @@ -2435,24 +3168,35 @@ b = lb; } else b = buf; + + b2p = b; if (('0' == b[0]) && (('x' == b[1]) || ('X' == b[1]))) { - res = sscanf(b + 2, "%x", &unum); + res = sscanf(b + 2, "%x%c", &unum, &c); num = unum; + is_hex = true; + b2p = b + 2; } else if ('H' == toupper((int)b[len - 1])) { res = sscanf(b, "%x", &unum); num = unum; } else res = sscanf(b, "%d%c%c%c", &num, &c, &c2, &c3); + if (res < 1) - return -1LL; + return -1; else if (1 == res) return num; else { + c = toupper((int)c); + if (is_hex) { + if (! ((c == '+') || (c == 'X'))) + return -1; + } if (res > 2) c2 = toupper((int)c2); if (res > 3) c3 = toupper((int)c3); - switch (toupper((int)c)) { + + switch (c) { case 'C': return num; case 'W': @@ -2483,16 +3227,26 @@ if (('I' == c2) && (4 == res) && ('B' == c3)) return num * 1073741824; return -1; - case 'X': - cp = (char *)strchr(b, 'x'); + case 'X': /* experimental: multiplication */ + /* left argument must end with hexadecimal digit */ + cp = (char *)strchr(b2p, 'x'); if (NULL == cp) - cp = (char *)strchr(b, 'X'); + cp = (char *)strchr(b2p, 'X'); if (cp) { n = sg_get_num(cp + 1); if (-1 != n) return num * n; } return -1; + case '+': /* experimental: addition */ + /* left argument must end with hexadecimal digit */ + cp = (char *)strchr(b2p, '+'); + if (cp) { + n = sg_get_num(cp + 1); + if (-1 != n) + return num + n; + } + return -1; default: pr2ws("unrecognized multiplier\n"); return -1; @@ -2502,8 +3256,8 @@ /* If the number in 'buf' can not be decoded then -1 is returned. Accepts a * hex prefix (0x or 0X) or a 'h' (or 'H') suffix; otherwise decimal is - * assumed. Does not accept multipliers. Accept a comma (","), a whitespace - * or newline as terminator. */ + * assumed. Does not accept multipliers. Accept a comma (","), hyphen ("-"), + * a whitespace or newline as terminator. */ int sg_get_num_nomult(const char * buf) { @@ -2532,21 +3286,28 @@ return -1; } -/* If the number in 'buf' can be decoded or the multiplier is unknown - * then -1LL is returned. Accepts a hex prefix (0x or 0X) or a decimal - * multiplier suffix (as per GNU's dd (since 2002: SI and IEC 60027-2)). - * Main (SI) multipliers supported: K, M, G, T, P. Ignore leading spaces - * and tabs; accept comma, space, tab and hash as terminator. */ +/* If the number in 'buf' can not be decoded or the multiplier is unknown + * then -1LL is returned. Accepts a hex prefix (0x or 0X), hex suffix + * (h or H), or a decimal multiplier suffix (as per GNU's dd (since 2002: + * SI and IEC 60027-2)). Main (SI) multipliers supported: K, M, G, T, P + * and E. Ignore leading spaces and tabs; accept comma, hyphen, space, tab + * and hash as terminator. Handles zero and positive values up to 2**63-1 . + * Experimental: left argument (must in with hexadecimal digit) added + * to, or multiplied by right argument. No embedded spaces. + * Examples: '3+1k' (evaluates to 1027) and '0x34+1m'. */ int64_t sg_get_llnum(const char * buf) { + bool is_hex = false; int res, len, n; int64_t num, ll; uint64_t unum; char * cp; const char * b; + const char * b2p; char c = 'c'; - char c2, c3; + char c2 = '\0'; /* keep static checker happy */ + char c3 = '\0'; /* keep static checker happy */ char lb[32]; if ((NULL == buf) || ('\0' == buf[0])) @@ -2557,10 +3318,10 @@ if (n == len) return -1LL; buf += n; - len -=n; + len -= n; } /* following hack to keep C++ happy */ - cp = strpbrk((char *)buf, " \t,#"); + cp = strpbrk((char *)buf, " \t,#-"); if (cp) { len = cp - buf; n = (int)sizeof(lb) - 1; @@ -2570,63 +3331,74 @@ b = lb; } else b = buf; + + b2p = b; if (('0' == b[0]) && (('x' == b[1]) || ('X' == b[1]))) { - res = sscanf(b + 2, "%" SCNx64 "", &unum); + res = sscanf(b + 2, "%" SCNx64 "%c", &unum, &c); num = unum; + is_hex = true; + b2p = b + 2; } else if ('H' == toupper((int)b[len - 1])) { - res = sscanf(b, "%" SCNx64 "", &unum); + res = sscanf(b, "%" SCNx64 , &unum); num = unum; } else res = sscanf(b, "%" SCNd64 "%c%c%c", &num, &c, &c2, &c3); + if (res < 1) return -1LL; else if (1 == res) return num; else { + c = toupper((int)c); + if (is_hex) { + if (! ((c == '+') || (c == 'X'))) + return -1; + } if (res > 2) c2 = toupper((int)c2); if (res > 3) c3 = toupper((int)c3); - switch (toupper((int)c)) { + + switch (c) { case 'C': return num; case 'W': return num * 2; case 'B': return num * 512; - case 'K': + case 'K': /* kilo or kibi */ if (2 == res) return num * 1024; if (('B' == c2) || ('D' == c2)) return num * 1000; if (('I' == c2) && (4 == res) && ('B' == c3)) - return num * 1024; + return num * 1024; /* KiB */ return -1LL; - case 'M': + case 'M': /* mega or mebi */ if (2 == res) - return num * 1048576; + return num * 1048576; /* M */ if (('B' == c2) || ('D' == c2)) - return num * 1000000; + return num * 1000000; /* MB */ if (('I' == c2) && (4 == res) && ('B' == c3)) - return num * 1048576; + return num * 1048576; /* MiB */ return -1LL; - case 'G': + case 'G': /* giga or gibi */ if (2 == res) - return num * 1073741824; + return num * 1073741824; /* G */ if (('B' == c2) || ('D' == c2)) - return num * 1000000000; + return num * 1000000000; /* GB */ if (('I' == c2) && (4 == res) && ('B' == c3)) - return num * 1073741824; + return num * 1073741824; /* GiB */ return -1LL; - case 'T': + case 'T': /* tera or tebi */ if (2 == res) - return num * 1099511627776LL; + return num * 1099511627776LL; /* T */ if (('B' == c2) || ('D' == c2)) - return num * 1000000000000LL; + return num * 1000000000000LL; /* TB */ if (('I' == c2) && (4 == res) && ('B' == c3)) - return num * 1099511627776LL; + return num * 1099511627776LL; /* TiB */ return -1LL; - case 'P': + case 'P': /* peta or pebi */ if (2 == res) return num * 1099511627776LL * 1024; if (('B' == c2) || ('D' == c2)) @@ -2634,16 +3406,32 @@ if (('I' == c2) && (4 == res) && ('B' == c3)) return num * 1099511627776LL * 1024; return -1LL; - case 'X': - cp = (char *)strchr(b, 'x'); + case 'E': /* exa or exbi */ + if (2 == res) + return num * 1099511627776LL * 1024 * 1024; + if (('B' == c2) || ('D' == c2)) + return num * 1000000000000LL * 1000 * 1000; + if (('I' == c2) && (4 == res) && ('B' == c3)) + return num * 1099511627776LL * 1024 * 1024; + return -1LL; + case 'X': /* experimental: decimal (left arg) multiplication */ + cp = (char *)strchr(b2p, 'x'); if (NULL == cp) - cp = (char *)strchr(b, 'X'); + cp = (char *)strchr(b2p, 'X'); if (cp) { ll = sg_get_llnum(cp + 1); if (-1LL != ll) return num * ll; } return -1LL; + case '+': /* experimental: decimal (left arg) addition */ + cp = (char *)strchr(b2p, '+'); + if (cp) { + ll = sg_get_llnum(cp + 1); + if (-1LL != ll) + return num + ll; + } + return -1LL; default: pr2ws("unrecognized multiplier\n"); return -1LL; @@ -2651,16 +3439,255 @@ } } +/* If the number in 'buf' can not be decoded then -1 is returned. Accepts a + * hex prefix (0x or 0X) or a 'h' (or 'H') suffix; otherwise decimal is + * assumed. Does not accept multipliers. Accept a comma (","), hyphen ("-"), + * a whitespace or newline as terminator. Only decimal numbers can represent + * negative numbers and '-1' must be treated separately. */ +int64_t +sg_get_llnum_nomult(const char * buf) +{ + int res, len; + int64_t num; + uint64_t unum; + + if ((NULL == buf) || ('\0' == buf[0])) + return -1; + len = strlen(buf); + if (('0' == buf[0]) && (('x' == buf[1]) || ('X' == buf[1]))) { + res = sscanf(buf + 2, "%" SCNx64 "", &unum); + num = unum; + } else if ('H' == toupper(buf[len - 1])) { + res = sscanf(buf, "%" SCNx64 "", &unum); + num = unum; + } else + res = sscanf(buf, "%" SCNd64 "", &num); + return (1 == res) ? num : -1; +} + +/* Read ASCII hex bytes or binary from fname (a file named '-' taken as + * stdin). If reading ASCII hex then there should be either one entry per + * line or a comma, space or tab separated list of bytes. If no_space is + * set then a string of ACSII hex digits is expected, 2 per byte. Everything + * from and including a '#' on a line is ignored. Returns 0 if ok, or an + * error code. If the error code is SG_LIB_LBA_OUT_OF_RANGE then mp_arr + * would be exceeded and both mp_arr and mp_arr_len are written to. */ +int +sg_f2hex_arr(const char * fname, bool as_binary, bool no_space, + uint8_t * mp_arr, int * mp_arr_len, int max_arr_len) +{ + bool has_stdin, split_line; + int fn_len, in_len, k, j, m, fd, err; + int off = 0; + int ret = 0; + unsigned int h; + const char * lcp; + FILE * fp; + struct stat a_stat; + char line[512]; + char carry_over[4]; + + if ((NULL == fname) || (NULL == mp_arr) || (NULL == mp_arr_len)) { + pr2ws("%s: bad arguments\n", __func__); + return SG_LIB_LOGIC_ERROR; + } + fn_len = strlen(fname); + if (0 == fn_len) + return SG_LIB_SYNTAX_ERROR; + has_stdin = ((1 == fn_len) && ('-' == fname[0])); /* read from stdin */ + if (as_binary) { + if (has_stdin) + fd = STDIN_FILENO; + else { + fd = open(fname, O_RDONLY); + if (fd < 0) { + err = errno; + pr2ws("unable to open binary file %s: %s\n", fname, + safe_strerror(err)); + return sg_convert_errno(err); + } + } + k = read(fd, mp_arr, max_arr_len); + if (k <= 0) { + if (0 == k) { + ret = SG_LIB_SYNTAX_ERROR; + pr2ws("read 0 bytes from binary file %s\n", fname); + } else { + ret = sg_convert_errno(errno); + pr2ws("read from binary file %s: %s\n", fname, + safe_strerror(errno)); + } + goto bin_fini; + } + if ((0 == fstat(fd, &a_stat)) && S_ISFIFO(a_stat.st_mode)) { + /* pipe; keep reading till error or 0 read */ + while (k < max_arr_len) { + m = read(fd, mp_arr + k, max_arr_len - k); + if (0 == m) + break; + if (m < 0) { + err = errno; + pr2ws("read from binary pipe %s: %s\n", fname, + safe_strerror(err)); + ret = sg_convert_errno(err); + goto bin_fini; + } + k += m; + } + } + *mp_arr_len = k; +bin_fini: + if ((fd >= 0) && (! has_stdin)) + close(fd); + return ret; + } + + /* So read the file as ASCII hex */ + if (has_stdin) + fp = stdin; + else { + fp = fopen(fname, "r"); + if (NULL == fp) { + err = errno; + pr2ws("Unable to open %s for reading: %s\n", fname, + safe_strerror(err)); + ret = sg_convert_errno(err); + goto fini; + } + } + + carry_over[0] = 0; + for (j = 0; j < 512; ++j) { + if (NULL == fgets(line, sizeof(line), fp)) + break; + in_len = strlen(line); + if (in_len > 0) { + if ('\n' == line[in_len - 1]) { + --in_len; + line[in_len] = '\0'; + split_line = false; + } else + split_line = true; + } + if (in_len < 1) { + carry_over[0] = 0; + continue; + } + if (carry_over[0]) { + if (isxdigit(line[0])) { + carry_over[1] = line[0]; + carry_over[2] = '\0'; + if (1 == sscanf(carry_over, "%4x", &h)) + mp_arr[off - 1] = h; /* back up and overwrite */ + else { + pr2ws("%s: carry_over error ['%s'] around line %d\n", + __func__, carry_over, j + 1); + ret = SG_LIB_SYNTAX_ERROR; + goto fini; + } + lcp = line + 1; + --in_len; + } else + lcp = line; + carry_over[0] = 0; + } else + lcp = line; + + m = strspn(lcp, " \t"); + if (m == in_len) + continue; + lcp += m; + in_len -= m; + if ('#' == *lcp) + continue; + k = strspn(lcp, "0123456789aAbBcCdDeEfF ,\t"); + if ((k < in_len) && ('#' != lcp[k]) && ('\r' != lcp[k])) { + pr2ws("%s: syntax error at line %d, pos %d\n", __func__, + j + 1, m + k + 1); + ret = SG_LIB_SYNTAX_ERROR; + goto fini; + } + if (no_space) { + for (k = 0; isxdigit(*lcp) && isxdigit(*(lcp + 1)); + ++k, lcp += 2) { + if (1 != sscanf(lcp, "%2x", &h)) { + pr2ws("%s: bad hex number in line %d, pos %d\n", + __func__, j + 1, (int)(lcp - line + 1)); + ret = SG_LIB_SYNTAX_ERROR; + goto fini; + } + if ((off + k) >= max_arr_len) { + pr2ws("%s: array length exceeded\n", __func__); + *mp_arr_len = max_arr_len; + ret = SG_LIB_LBA_OUT_OF_RANGE; + goto fini; + } + mp_arr[off + k] = h; + } + if (isxdigit(*lcp) && (! isxdigit(*(lcp + 1)))) + carry_over[0] = *lcp; + off += k; + } else { + for (k = 0; k < 1024; ++k) { + if (1 == sscanf(lcp, "%10x", &h)) { + if (h > 0xff) { + pr2ws("%s: hex number larger than 0xff in line " + "%d, pos %d\n", __func__, j + 1, + (int)(lcp - line + 1)); + ret = SG_LIB_SYNTAX_ERROR; + goto fini; + } + if (split_line && (1 == strlen(lcp))) { + /* single trailing hex digit might be a split pair */ + carry_over[0] = *lcp; + } + if ((off + k) >= max_arr_len) { + pr2ws("%s: array length exceeded\n", __func__); + ret = SG_LIB_LBA_OUT_OF_RANGE; + *mp_arr_len = max_arr_len; + goto fini; + } + mp_arr[off + k] = h; + lcp = strpbrk(lcp, " ,\t"); + if (NULL == lcp) + break; + lcp += strspn(lcp, " ,\t"); + if ('\0' == *lcp) + break; + } else { + if (('#' == *lcp) || ('\r' == *lcp)) { + --k; + break; + } + pr2ws("%s: error in line %d, at pos %d\n", __func__, + j + 1, (int)(lcp - line + 1)); + ret = SG_LIB_SYNTAX_ERROR; + goto fini; + } + } + off += (k + 1); + } + } + *mp_arr_len = off; + if (stdin != fp) + fclose(fp); + return 0; +fini: + if (fp && (stdin != fp)) + fclose(fp); + return ret; +} + /* 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 * are processed. */ int -sg_ata_get_chars(const unsigned short * word_arr, int start_word, - int num_words, int is_big_endian, char * ochars) +sg_ata_get_chars(const uint16_t * word_arr, int start_word, + int num_words, bool is_big_endian, char * ochars) { int k; - unsigned short s; + uint16_t s; char a, b; char * op = ochars; @@ -2683,6 +3710,173 @@ return op - ochars; } +#ifdef SG_LIB_FREEBSD +#include +#elif defined(SG_LIB_WIN32) +#include +#endif + +uint32_t +sg_get_page_size(void) +{ +#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE) + return sysconf(_SC_PAGESIZE); /* POSIX.1 (was getpagesize()) */ +#elif defined(SG_LIB_WIN32) + static bool got_page_size = false; + static uint32_t win_page_size; + + if (! got_page_size) { + SYSTEM_INFO si; + + GetSystemInfo(&si); + win_page_size = si.dwPageSize; + got_page_size = true; + } + return win_page_size; +#elif defined(SG_LIB_FREEBSD) + return PAGE_SIZE; +#else + return 4096; /* give up, pick likely figure */ +#endif +} + +/* Returns pointer to heap (or NULL) that is aligned to a align_to byte + * boundary. Sends back *buff_to_free pointer in third argument that may be + * different from the return value. If it is different then the *buff_to_free + * pointer should be freed (rather than the returned value) when the heap is + * no longer needed. If align_to is 0 then aligns to OS's page size. Sets all + * returned heap to zeros. If num_bytes is 0 then set to page size. */ +uint8_t * +sg_memalign(uint32_t num_bytes, uint32_t align_to, uint8_t ** buff_to_free, + bool vb) +{ + size_t psz; + uint8_t * res; + + if (buff_to_free) /* make sure buff_to_free is NULL if alloc fails */ + *buff_to_free = NULL; + psz = (align_to > 0) ? align_to : sg_get_page_size(); + if (0 == num_bytes) + num_bytes = psz; /* ugly to handle otherwise */ + +#ifdef HAVE_POSIX_MEMALIGN + { + int err; + void * wp = NULL; + + err = posix_memalign(&wp, psz, num_bytes); + if (err || (NULL == wp)) { + pr2ws("%s: posix_memalign: error [%d], out of memory?\n", + __func__, err); + return NULL; + } + memset(wp, 0, num_bytes); + if (buff_to_free) + *buff_to_free = (uint8_t *)wp; + res = (uint8_t *)wp; + if (vb) { + pr2ws("%s: posix_ma, len=%d, ", __func__, num_bytes); + if (buff_to_free) + pr2ws("wrkBuffp=%p, ", (void *)res); + pr2ws("psz=%u, rp=%p\n", (unsigned int)psz, (void *)res); + } + return res; + } +#else + { + void * wrkBuff; + sg_uintptr_t align_1 = psz - 1; + + wrkBuff = (uint8_t *)calloc(num_bytes + psz, 1); + if (NULL == wrkBuff) { + if (buff_to_free) + *buff_to_free = NULL; + return NULL; + } else if (buff_to_free) + *buff_to_free = (uint8_t *)wrkBuff; + res = (uint8_t *)(void *) + (((sg_uintptr_t)wrkBuff + align_1) & (~align_1)); + if (vb) { + pr2ws("%s: hack, len=%d, ", __func__, num_bytes); + if (buff_to_free) + pr2ws("buff_to_free=%p, ", wrkBuff); + pr2ws("align_1=%lu, rp=%p\n", align_1, (void *)res); + } + return res; + } +#endif +} + +/* If byte_count is 0 or less then the OS page size is used as denominator. + * Returns true if the remainder of ((unsigned)pointer % byte_count) is 0, + * else returns false. */ +bool +sg_is_aligned(const void * pointer, int byte_count) +{ + return 0 == ((sg_uintptr_t)pointer % + ((byte_count > 0) ? (uint32_t)byte_count : + sg_get_page_size())); +} + +/* Does similar job to sg_get_unaligned_be*() but this function starts at + * a given start_bit (i.e. within byte, so 7 is MSbit of byte and 0 is LSbit) + * offset. Maximum number of num_bits is 64. For example, these two + * invocations are equivalent (and should yield the same result); + * sg_get_big_endian(from_bp, 7, 16) + * sg_get_unaligned_be16(from_bp) */ +uint64_t +sg_get_big_endian(const uint8_t * from_bp, int start_bit /* 0 to 7 */, + int num_bits /* 1 to 64 */) +{ + uint64_t res; + int sbit_o1 = start_bit + 1; + + res = (*from_bp++ & ((1 << sbit_o1) - 1)); + num_bits -= sbit_o1; + while (num_bits > 0) { + res <<= 8; + res |= *from_bp++; + num_bits -= 8; + } + if (num_bits < 0) + res >>= (-num_bits); + return res; +} + +/* Does similar job to sg_put_unaligned_be*() but this function starts at + * a given start_bit offset. Maximum number of num_bits is 64. Preserves + * residual bits in partially written bytes. start_bit 7 is MSb. */ +void +sg_set_big_endian(uint64_t val, uint8_t * to, + int start_bit /* 0 to 7 */, int num_bits /* 1 to 64 */) +{ + int sbit_o1 = start_bit + 1; + int mask, num, k, x; + + if ((NULL == to) || (start_bit > 7) || (num_bits > 64)) { + pr2ws("%s: bad args: start_bit=%d, num_bits=%d\n", __func__, + start_bit, num_bits); + return; + } + mask = (8 != sbit_o1) ? ((1 << sbit_o1) - 1) : 0xff; + k = start_bit - ((num_bits - 1) % 8); + if (0 != k) + val <<= ((k > 0) ? k : (8 + k)); + num = (num_bits + 15 - sbit_o1) / 8; + for (k = 0; k < num; ++k) { + if ((sbit_o1 - num_bits) > 0) + mask &= ~((1 << (sbit_o1 - num_bits)) - 1); + if (k < (num - 1)) + x = (val >> ((num - k - 1) * 8)) & 0xff; + else + x = val & 0xff; + to[k] = (to[k] & ~mask) | (x & mask); + mask = 0xff; + num_bits -= sbit_o1; + sbit_o1 = 8; + } +} + const char * sg_lib_version() { @@ -2695,7 +3889,6 @@ Set text mode on fd. Does nothing in Unix. Returns negative number on failure. */ -#include #include int @@ -2727,15 +3920,3 @@ } #endif - -int -pr2serr(const char * fmt, ...) -{ - va_list args; - int n; - - va_start(args, fmt); - n = vfprintf(stderr, fmt, args); - va_end(args); - return n; -} diff -Nru sdparm-1.10/lib/sg_lib_data.c sdparm-1.12/lib/sg_lib_data.c --- sdparm-1.10/lib/sg_lib_data.c 2016-02-03 15:09:29.000000000 +0000 +++ sdparm-1.12/lib/sg_lib_data.c 2021-03-22 04:36:43.000000000 +0000 @@ -1,23 +1,26 @@ /* - * Copyright (c) 2007-2016 Douglas Gilbert. + * Copyright (c) 2007-2021 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. + * + * SPDX-License-Identifier: BSD-2-Clause */ #include -#include "sg_lib.h" -#include "sg_lib_data.h" - #ifdef HAVE_CONFIG_H #include "config.h" #else #define SG_SCSI_STRINGS 1 #endif +#include "sg_lib.h" +#include "sg_lib_data.h" + -const char * sg_lib_version_str = "2.17 20160202"; /* spc5r08, sbc4r10 */ +const char * sg_lib_version_str = "2.79 20210304"; +/* spc6r05, sbc4r22, zbc2r09 */ /* indexed by pdt; those that map to own index do not decay */ @@ -101,7 +104,8 @@ {0x37, 0, "Read defect data(10)"}, /* SBC-3 r31 recommends Read defect data(12) */ {0x37, PDT_MCHANGER, "Initialize element status with range"}, - {0x38, 0, "Medium scan"}, + {0x38, 0, "Format with preset scan"}, + {0x38, PDT_OCRW, "Medium scan"}, {0x39, 0, "Compare"}, /* obsolete in SPC-4 r11 */ {0x3a, 0, "Copy and verify"}, /* obsolete in SPC-4 r11 */ {0x3b, 0, "Write buffer"}, @@ -125,11 +129,11 @@ {0x4d, 0, "Log sense"}, {0x4e, 0, "Stop play/scan"}, {0x50, 0, "Xdwrite(10)"}, /* obsolete in SBC-3 r31 */ - {0x51, 0, "Xpwrite(10)"}, + {0x51, 0, "Xpwrite(10)"}, /* obsolete in SBC-4 r15 */ {0x51, PDT_MMC, "Read disk information"}, {0x52, 0, "Xdread(10)"}, /* obsolete in SBC-3 r31 */ {0x52, PDT_MMC, "Read track information"}, - {0x53, 0, "Xdwriteread(10)"}, + {0x53, 0, "Xdwriteread(10)"}, /* obsolete in SBC-4 r15 */ {0x54, 0, "Send OPC information"}, {0x55, 0, "Mode select(10)"}, {0x56, 0, "Reserve(10)"}, /* obsolete in SPC-4 r11 */ @@ -144,7 +148,7 @@ {0x5e, 0, "Persistent reserve in"}, {0x5f, 0, "Persistent reserve out"}, {0x7e, 0, "Extended cdb (XCBD)"}, /* added in SPC-4 r12 */ - {0x80, 0, "Xdwrite extended(16)"}, + {0x80, 0, "Xdwrite extended(16)"}, /* obsolete in SBC-4 r15 */ {0x80, PDT_TAPE, "Write filemarks(16)"}, {0x81, 0, "Rebuild(16)"}, {0x81, PDT_TAPE, "Read reverse(16)"}, @@ -229,7 +233,8 @@ {0xffff, 0, NULL}, }; -/* Read buffer [0x3c] service actions, need prefix */ +/* Read buffer(10) [0x3c] and Read buffer(16) [0x9b] service actions (sa), + * need prefix */ struct sg_lib_value_name_t sg_lib_read_buff_arr[] = { {0x0, 0, "combined header and data [or multiple modes]"}, {0x2, 0, "data"}, @@ -271,8 +276,18 @@ /* Maintenance in [0xa3] service actions */ struct sg_lib_value_name_t sg_lib_maint_in_arr[] = { + {0x0, PDT_SAC, "Report assigned/unassigned p_extent"}, + {0x0, PDT_ADC, "Report automation device attributes"}, + {0x1, PDT_SAC, "Report component device"}, + {0x2, PDT_SAC, "Report component device attachments"}, + {0x3, PDT_SAC, "Report peripheral device"}, + {0x4, PDT_SAC, "Report peripheral device associations"}, {0x5, 0, "Report identifying information"}, /* was "Report device identifier" prior to spc4r07 */ + {0x6, PDT_SAC, "Report states"}, + {0x7, PDT_SAC, "Report device identification"}, + {0x8, PDT_SAC, "Report unconfigured capacity"}, + {0x9, PDT_SAC, "Report supported configuration method"}, {0xa, 0, "Report target port groups"}, {0xb, 0, "Report aliases"}, {0xc, 0, "Report supported operation codes"}, @@ -280,21 +295,36 @@ {0xe, 0, "Report priority"}, {0xf, 0, "Report timestamp"}, {0x10, 0, "Management protocol in"}, - {0x1d, 0, "Report provisioning initialization pattern"}, + {0x1d, PDT_DISK, "Report provisioning initialization pattern"}, + /* added in sbc4r07, shares sa 0x1d with ssc5r01 (tape) */ + {0x1d, PDT_TAPE, "Receive recommended access order"}, + {0x1e, PDT_TAPE, "Read dynamic runtime attribute"}, + {0x1e, PDT_ADC, "Report automation device attributes"}, {0x1f, 0, "Maintenance in vendor specific"}, {0xffff, 0, NULL}, }; /* Maintenance out [0xa4] service actions */ struct sg_lib_value_name_t sg_lib_maint_out_arr[] = { + {0x0, PDT_SAC, "Add peripheral device / component device"}, + {0x0, PDT_ADC, "Set automation device attribute"}, + {0x1, PDT_SAC, "Attach to component device"}, + {0x2, PDT_SAC, "Exchange p_extent"}, + {0x3, PDT_SAC, "Exchange peripheral device / component device"}, + {0x4, PDT_SAC, "Instruct component device"}, + {0x5, PDT_SAC, "Remove peripheral device / component device"}, {0x6, 0, "Set identifying information"}, /* was "Set device identifier" prior to spc4r07 */ + {0x7, PDT_SAC, "Break peripheral device / component device"}, {0xa, 0, "Set target port groups"}, {0xb, 0, "Change aliases"}, {0xc, 0, "Remove I_T nexus"}, {0xe, 0, "Set priority"}, {0xf, 0, "Set timestamp"}, {0x10, 0, "Management protocol out"}, + {0x1d, PDT_TAPE, "Generate recommended access order"}, + {0x1e, PDT_TAPE, "write dynamic runtime attribute"}, + {0x1e, PDT_ADC, "Set automation device attributes"}, {0x1f, 0, "Maintenance out vendor specific"}, {0xffff, 0, NULL}, }; @@ -316,25 +346,37 @@ /* Service action out(12) [0xa9] service actions */ struct sg_lib_value_name_t sg_lib_serv_out12_arr[] = { + {0x1f, PDT_ADC, "Set medium attribute"}, {0xff, 0, "Impossible command name"}, {0xffff, 0, NULL}, }; /* Service action in(16) [0x9e] service actions */ struct sg_lib_value_name_t sg_lib_serv_in16_arr[] = { + {0xf, 0, "Receive binding report"}, /* added spc5r11 */ {0x10, 0, "Read capacity(16)"}, {0x11, 0, "Read long(16)"}, /* obsolete in SBC-4 r7 */ - {0x12, 0, "Get LBA status"}, + {0x12, 0, "Get LBA status(16)"}, /* 32 byte variant added in sbc4r14 */ {0x13, 0, "Report referrals"}, {0x14, 0, "Stream control"}, {0x15, 0, "Background control"}, {0x16, 0, "Get stream status"}, + {0x17, 0, "Get physical element status"}, /* added sbc4r13 */ + {0x18, 0, "Remove element and truncate"}, /* added sbc4r13 */ + {0x19, 0, "Restore elements and rebuild"}, /* added sbc4r19 */ + {0x1a, 0, "Remove element and modify zones"}, /* added zbc2r07 */ {0xffff, 0, NULL}, }; /* Service action out(16) [0x9f] service actions */ struct sg_lib_value_name_t sg_lib_serv_out16_arr[] = { + {0x0b, 0, "Test bind"}, /* added spc5r13 */ + {0x0c, 0, "Prepare bind report"}, /* added spc5r11 */ + {0x0d, 0, "Set affiliation"}, + {0x0e, 0, "Bind"}, + {0x0f, 0, "Unbind"}, {0x11, 0, "Write long(16)"}, + {0x12, 0, "Write scattered(16)"}, /* added sbc4r11 */ {0x14, PDT_ZBC, "Reset write pointer"}, {0x1f, PDT_ADC, "Notify data transfer device(16)"}, {0xffff, 0, NULL}, @@ -370,12 +412,15 @@ /* Third party copy in [0x83] service actions * Opcode 'Receive copy results' was renamed 'Third party copy in' in spc4r34 - * LID1 is an abbreviation of List Identifier length of 1 byte */ -struct sg_lib_value_name_t sg_lib_xcopy_sa_arr[] = { + * LID1 is an abbreviation of List Identifier length of 1 byte. In SPC-5 + * LID1 discontinued (references back to SPC-4) and "(LID4)" suffix removed + * as there is no need to differentiate. */ +struct sg_lib_value_name_t sg_lib_xcopy_sa_arr[] = { /* originating */ {0x0, 0, "Extended copy(LID1)"}, - {0x1, 0, "Extended copy(LID4)"}, + {0x1, 0, "Extended copy"}, /* was 'Extended copy(LID4)' */ {0x10, 0, "Populate token"}, {0x11, 0, "Write using token"}, + {0x16, 1, "Set tape stream mirroring"}, /* ADC-4 and SSC-5 */ {0x1c, 0, "Copy operation abort"}, {0xffff, 0, NULL}, }; @@ -383,15 +428,16 @@ /* Third party copy out [0x84] service actions * Opcode 'Extended copy' was renamed 'Third party copy out' in spc4r34 * LID4 is an abbreviation of List Identifier length of 4 bytes */ -struct sg_lib_value_name_t sg_lib_rec_copy_sa_arr[] = { +struct sg_lib_value_name_t sg_lib_rec_copy_sa_arr[] = { /* retrieve */ {0x0, 0, "Receive copy status(LID1)"}, {0x1, 0, "Receive copy data(LID1)"}, {0x3, 0, "Receive copy operating parameters"}, {0x4, 0, "Receive copy failure details(LID1)"}, - {0x5, 0, "Receive copy status(LID4)"}, - {0x6, 0, "Receive copy data(LID4)"}, + {0x5, 0, "Receive copy status"}, /* was 'Receive copy status(LID4)' */ + {0x6, 0, "Receive copy data"}, /* was 'Receive copy data(LID4)' */ {0x7, 0, "Receive ROD token information"}, {0x8, 0, "Report all ROD tokens"}, + {0x16, 1, "Report tape stream mirroring"}, /* SSC-5 */ {0xffff, 0, NULL}, }; @@ -399,21 +445,24 @@ struct sg_lib_value_name_t sg_lib_variable_length_arr[] = { {0x1, 0, "Rebuild(32)"}, {0x2, 0, "Regenerate(32)"}, - {0x3, 0, "Xdread(32)"}, /* obsolete in SBC-3 r31 */ - {0x4, 0, "Xdwrite(32)"}, /* obsolete in SBC-3 r31 */ - {0x5, 0, "Xdwrite extended(32)"}, - {0x6, 0, "Xpwrite(32)"}, - {0x7, 0, "Xdwriteread(32)"}, - {0x8, 0, "Xdwrite extended(64)"}, + {0x3, 0, "Xdread(32)"}, /* obsolete in SBC-3 r31 */ + {0x4, 0, "Xdwrite(32)"}, /* obsolete in SBC-3 r31 */ + {0x5, 0, "Xdwrite extended(32)"}, /* obsolete in SBC-4 r15 */ + {0x6, 0, "Xpwrite(32)"}, /* obsolete in SBC-4 r15 */ + {0x7, 0, "Xdwriteread(32)"}, /* obsolete in SBC-4 r15 */ + {0x8, 0, "Xdwrite extended(64)"}, /* obsolete in SBC-4 r15 */ {0x9, 0, "Read(32)"}, {0xa, 0, "Verify(32)"}, {0xb, 0, "Write(32)"}, {0xc, 0, "Write and verify(32)"}, {0xd, 0, "Write same(32)"}, - {0xe, 0, "Orwrite(32)"}, /* added sbc3r25 */ - {0xf, 0, "Atomic write(32)"}, /* added sbc4r02 */ - {0x10, 0, "Write stream(32)"}, /* added sbc4r07 */ + {0xe, 0, "Orwrite(32)"}, /* added sbc3r25 */ + {0xf, 0, "Atomic write(32)"}, /* added sbc4r02 */ + {0x10, 0, "Write stream(32)"}, /* added sbc4r07 */ + {0x11, 0, "Write scattered(32)"}, /* added sbc4r11 */ + {0x12, 0, "Get LBA status(32)"}, /* added sbc4r14 */ {0x1800, 0, "Receive credential"}, + {0x1ff0, 0, "ATA pass-through(32)"},/* added sat4r05 */ {0x8801, 0, "Format OSD (osd)"}, {0x8802, 0, "Create (osd)"}, {0x8803, 0, "List (osd)"}, @@ -477,12 +526,17 @@ {0x2, PDT_ZBC, "Finish zone"}, {0x3, PDT_ZBC, "Open zone"}, {0x4, PDT_ZBC, "Reset write pointer"}, + {0x10, PDT_ZBC, "Sequentialize zone"}, /* zbc2r01b */ {0xffff, 0, NULL}, }; /* Zoning in [0x95] service actions */ struct sg_lib_value_name_t sg_lib_zoning_in_arr[] = { {0x0, PDT_ZBC, "Report zones"}, + {0x6, PDT_ZBC, "Report realms"}, /* zbc2r04 */ + {0x7, PDT_ZBC, "Report zone domains"}, /* zbc2r04 */ + {0x8, PDT_ZBC, "Zone activate"}, /* zbc2r04 */ + {0x9, PDT_ZBC, "Zone query"}, /* zbc2r04 */ {0xffff, 0, NULL}, }; @@ -510,6 +564,10 @@ {0xffff, 0, NULL}, }; +struct sg_lib_value_name_t sg_lib_read_pos_arr[] = { /* opcode 0x34 (SSC) */ + {0xffff, 0, NULL}, +}; + struct sg_lib_value_name_t sg_lib_maint_in_arr[] = { /* opcode 0xa3 */ {0xffff, 0, NULL}, }; @@ -578,7 +636,7 @@ /* A conveniently formatted list of SCSI ASC/ASCQ codes and their * corresponding text can be found at: www.t10.org/lists/asc-num.txt - * The following should match asc-num.txt dated 20150423 */ + * The following should match asc-num.txt dated 20200817 */ #ifdef SG_SCSI_STRINGS struct sg_lib_asc_ascq_range_t sg_lib_asc_ascq_range[] = @@ -619,6 +677,7 @@ {0x00,0x1f,"Logical unit transitioning to another power condition"}, {0x00,0x20,"Extended copy information available"}, {0x00,0x21,"Atomic command aborted due to ACA"}, + {0x00,0x22,"Deferred microcode is pending"}, {0x01,0x00,"No index/sector signal"}, {0x02,0x00,"No seek complete"}, {0x03,0x00,"Peripheral device write fault"}, @@ -666,6 +725,9 @@ {0x04,0x20,"Logical unit not ready, logical unit reset required"}, {0x04,0x21,"Logical unit not ready, hard reset required"}, {0x04,0x22,"Logical unit not ready, power cycle required"}, + {0x04,0x23,"Logical unit not ready, affiliation required"}, + {0x04,0x24,"Depopulation in progress"}, /* spc5r15 */ + {0x04,0x25,"Depopulation restoration in progress"}, /* spc6r02 */ {0x05,0x00,"Logical unit does not respond to selection"}, {0x06,0x00,"No reference position found"}, {0x07,0x00,"Multiple peripheral devices selected"}, @@ -699,6 +761,9 @@ {0x0B,0x0F,"Warning - low critical humidity limit exceeded"}, {0x0B,0x10,"Warning - high operating humidity limit exceeded"}, {0x0B,0x11,"Warning - low operating humidity limit exceeded"}, + {0x0B,0x12,"Warning - microcode security at risk"}, + {0x0B,0x13,"Warning - microcode digital signature validation failure"}, + {0x0B,0x14,"Warning - physical element status change"}, /* spc5r15 */ {0x0C,0x00,"Write error"}, {0x0C,0x01,"Write error - recovered with auto reallocation"}, {0x0C,0x02,"Write error - auto reallocation failed"}, @@ -831,6 +896,7 @@ {0x21,0x06,"Attempt to read invalid data"}, {0x21,0x07,"Read boundary violation"}, {0x21,0x08,"Misaligned write command"}, + {0x21,0x09,"Attempt to access gap zone"}, {0x22,0x00,"Illegal function (use 20 00, 24 00, or 26 00)"}, {0x23,0x00,"Invalid token operation, cause not reportable"}, {0x23,0x01,"Invalid token operation, unsupported token type"}, @@ -853,6 +919,7 @@ {0x24,0x06,"Nonce not unique"}, {0x24,0x07,"Nonce timestamp out of range"}, {0x24,0x08,"Invalid xcdb"}, + {0x24,0x09,"Invalid fast format"}, {0x25,0x00,"Logical unit not supported"}, {0x26,0x00,"Invalid field in parameter list"}, {0x26,0x01,"Parameter not supported"}, @@ -874,6 +941,9 @@ {0x26,0x11,"Incomplete key-associated data set"}, {0x26,0x12,"Vendor specific key reference not found"}, {0x26,0x13,"Application tag mode page is invalid"}, + {0x26,0x14,"Tape stream mirroring prevented"}, + {0x26,0x15,"Copy source or copy destination not authorized"}, + {0x26,0x16,"Fast copy not possible"}, {0x27,0x00,"Write protected"}, {0x27,0x01,"Hardware write protected"}, {0x27,0x02,"Logical unit software write protected"}, @@ -935,6 +1005,8 @@ {0x2C,0x0F,"Stream not open"}, {0x2C,0x10,"Unwritten data in zone"}, {0x2C,0x11,"Descriptor format sense data required"}, + {0x2C,0x12,"Zone is inactive"}, + {0x2C,0x13,"Well known logical unit access required"}, /* spc6r02 */ {0x2D,0x00,"Overwrite error on update in place"}, {0x2E,0x00,"Insufficient time for operation"}, {0x2E,0x01,"Command timeout before processing"}, @@ -966,6 +1038,8 @@ {0x31,0x01,"Format command failed"}, {0x31,0x02,"Zoned formatting failed due to spare linking"}, {0x31,0x03,"Sanitize command failed"}, + {0x31,0x04,"Depopulation failed"}, /* spc5r15 */ + {0x31,0x05,"Depopulation restoration failed"}, /* spc6r02 */ {0x32,0x00,"No defect spare location available"}, {0x32,0x01,"Defect list update failure"}, {0x33,0x00,"Tape length error"}, @@ -983,6 +1057,7 @@ {0x38,0x04,"Esn - media class event"}, {0x38,0x06,"Esn - device busy class event"}, {0x38,0x07,"Thin provisioning soft threshold reached"}, + {0x38,0x08,"Depopulation interrupted"}, /* spc6r03 */ {0x39,0x00,"Saving parameters not supported"}, {0x3A,0x00,"Medium not present"}, {0x3A,0x01,"Medium not present - tray closed"}, @@ -1017,6 +1092,7 @@ {0x3B,0x1a,"Data transfer device removed"}, {0x3B,0x1b,"Data transfer device inserted"}, {0x3B,0x1c,"Too many logical objects on partition to support operation"}, + {0x3B,0x20,"Element static information changed"}, {0x3D,0x00,"Invalid bits in identify message"}, {0x3E,0x00,"Logical unit has not self-configured yet"}, {0x3E,0x01,"Logical unit failure"}, @@ -1177,6 +1253,7 @@ {0x5D,0x1A,"Hardware impending failure seek time performance"}, {0x5D,0x1B,"Hardware impending failure spin-up retry count"}, {0x5D,0x1C,"Hardware impending failure drive calibration retry count"}, + {0x5D,0x1D,"Hardware impending failure power loss protection circuit"}, {0x5D,0x20,"Controller impending failure general hard drive failure"}, {0x5D,0x21,"Controller impending failure drive error rate too high" }, {0x5D,0x22,"Controller impending failure data error rate too high" }, @@ -1242,6 +1319,7 @@ {0x5D,0x6A,"Firmware impending failure seek time performance"}, {0x5D,0x6B,"Firmware impending failure spin-up retry count"}, {0x5D,0x6C,"Firmware impending failure drive calibration retry count"}, + {0x5D,0x73,"Media impending failure endurance limit met"}, {0x5D,0xFF,"Failure prediction threshold exceeded (false)"}, {0x5E,0x00,"Low power condition on"}, {0x5E,0x01,"Idle condition activated by timer"}, @@ -1405,7 +1483,7 @@ "Completed" /* may occur for successful cmd (spc4r23) */ }; -const char * sg_lib_pdt_strs[] = { +const char * sg_lib_pdt_strs[32] = { /* should have 2**5 elements */ /* 0 */ "disk", "tape", "printer", /* obsolete, spc5r01 */ @@ -1426,16 +1504,17 @@ "object based storage", "automation/driver interface", "security manager device", /* obsolete, spc5r01 */ - "zoned block commands", + "host managed zoned block", "0x15", "0x16", "0x17", "0x18", "0x19", "0x1a", "0x1b", "0x1c", "0x1d", "well known logical unit", - "no physical device on this lu", + "unknown or no device type", /* coupled with PQ=3 for not accessible + via this lu's port (try the other) */ }; const char * sg_lib_transport_proto_strs[] = { - "Fibre Channel Protocol for SCSI (FCP-4)", + "Fibre Channel Protocol for SCSI (FCP-5)", /* now at fcp5r01 */ "SCSI Parallel Interface (SPI-5)", /* obsolete in spc5r01 */ "Serial Storage Architecture SCSI-3 Protocol (SSA-S3P)", "Serial Bus Protocol for IEEE 1394 (SBP-3)", @@ -1450,3 +1529,359 @@ "Oxc", "Oxd", "Oxe", "No specific protocol" }; + +/* SCSI Feature Sets array. code->value, pdt->peri_dev_type (-1 for SPC) */ +struct sg_lib_value_name_t sg_lib_scsi_feature_sets[] = +{ + {SCSI_FS_SPC_DISCOVERY_2016, -1, "Discovery 2016"}, + {SCSI_FS_SBC_BASE_2010, PDT_DISK, "SBC Base 2010"}, + {SCSI_FS_SBC_BASE_2016, PDT_DISK, "SBC Base 2016"}, + {SCSI_FS_SBC_BASIC_PROV_2016, PDT_DISK, "Basic provisioning 2016"}, + {SCSI_FS_SBC_DRIVE_MAINT_2016, PDT_DISK, "Drive maintenance 2016"}, + {SCSI_FS_ZBC_HOST_AWARE_2020, PDT_ZBC, "Host Aware 2020"}, + {SCSI_FS_ZBC_HOST_MANAGED_2020, PDT_ZBC, "Host Managed 2020"}, + {SCSI_FS_ZBC_DOMAINS_REALMS_2020, PDT_ZBC, "Domains and Realms 2020"}, + {0x0, 0, NULL}, /* 0x0 is reserved sfs; trailing sentinel */ +}; + +#if (SG_SCSI_STRINGS && HAVE_NVME && (! IGNORE_NVME)) + +/* Commands sent to the NVMe Admin Queue (queue id 0) have the following + * names in the NVM Express 1.3a document dated 20171024 */ +struct sg_lib_simple_value_name_t sg_lib_nvme_admin_cmd_arr[] = +{ + {0x0, "Delete I/O Submission Queue"}, /* first mandatory command */ + {0x1, "Create I/O Submission Queue"}, + {0x2, "Get Log Page"}, + {0x4, "Delete I/O Completion Queue"}, + {0x5, "Create I/O Completion Queue"}, + {0x6, "Identify"}, + {0x8, "Abort"}, + {0x9, "Set Features"}, + {0xa, "Get Features"}, + {0xc, "Asynchronous Event Request"}, /* last mandatory command */ + {0xd, "Namespace Management"}, /* first optional command */ + {0x10, "Firmware commit"}, + {0x11, "Firmware image download"}, + {0x14, "Device Self-test"}, + {0x15, "Namespace Attachment"}, + {0x18, "Keep Alive"}, + {0x19, "Directive Send"}, + {0x1a, "Directive Receive"}, + {0x1c, "Virtualization Management"}, + {0x1d, "NVMe-MI Send"}, /* SES SEND DIAGNOSTIC cmd passes thru here */ + {0x1e, "NVMe-MI Receive"}, /* RECEIVE DIAGNOSTIC RESULTS thru here */ + {0x7c, "Doorbell Buffer Config"}, + {0x7f, "NVMe over Fabrics"}, + + /* I/O command set specific 0x80 to 0xbf */ + {0x80, "Format NVM"}, /* first NVM specific */ + {0x81, "Security Send"}, + {0x82, "Security Receive"}, + {0x84, "Sanitize"}, /* last NVM specific in 1.3a */ + {0x86, "Get LBA status"}, /* NVM specific, new in 1.4 */ + /* Vendor specific 0xc0 to 0xff */ + {0xffff, NULL}, /* Sentinel */ +}; + +/* Commands sent any NVMe non-Admin Queue (queue id >0) for the NVM command + * set have the following names in the NVM Express 1.3a document dated + * 20171024 */ +struct sg_lib_simple_value_name_t sg_lib_nvme_nvm_cmd_arr[] = +{ + {0x0, "Flush"}, /* first mandatory command */ + {0x1, "Write"}, + {0x2, "Read"}, /* last mandatory command */ + {0x4, "Write Uncorrectable"}, /* first optional command */ + {0x5, "Compare"}, + {0x8, "Write Zeroes"}, + {0x9, "Dataset Management"}, + {0xd, "Reservation Register"}, + {0xe, "Reservation Report"}, + {0x11, "Reservation Acquire"}, + {0x15, "Reservation Release"}, /* last optional command in 1.3a */ + + /* Vendor specific 0x80 to 0xff */ + {0xffff, NULL}, /* Sentinel */ +}; + + +/* .value is completion queue's DW3 as follows: ((DW3 >> 17) & 0x3ff) + * .peri_dev_type is an index for the sg_lib_scsi_status_sense_arr[] + * .name is taken from NVMe 1.3a document, section 4.6.1.2.1 with less + * capitalization. + * NVMe term bits 31:17 of DW3 in the completion field as the "Status + * Field" (SF). Bit 31 is "Do not retry" (DNR) and bit 30 is "More" (M). + * Bits 29:28 are reserved, bit 27:25 are the "Status Code Type" (SCT) + * and bits 24:17 are the Status Code (SC). This table is in ascending + * order of its .value field so a binary search could be done on it. */ +struct sg_lib_value_name_t sg_lib_nvme_cmd_status_arr[] = +{ + /* Generic command status values, Status Code Type (SCT): 0h + * Lowest 8 bits are the Status Code (SC), in this case: + * 00h - 7Fh: Applicable to Admin Command Set, or across multiple + * command sets + * 80h - BFh: I/O Command Set Specific status codes + * c0h - FFh: I/O Vendor Specific status codes */ + {0x0, 0, "Successful completion"}, + {0x1, 1, "Invalid command opcode"}, + {0x2, 2, "Invalid field in command"}, + {0x3, 2, "Command id conflict"}, + {0x4, 3, "Data transfer error"}, + {0x5, 4, "Command aborted due to power loss notication"}, + {0x6, 5, "Internal error"}, + {0x7, 6, "Command abort requested"}, + {0x8, 6, "Command aborted due to SQ deletion"}, + {0x9, 6, "Command aborted due to failed fused command"}, + {0xa, 6, "Command aborted due to missing fused command"}, + {0xb, 7, "Invalid namespace or format"}, + {0xc, 5, "Command sequence error"}, + {0xd, 5, "Invalid SGL segment descriptor"}, + {0xe, 5, "Invalid number of SGL descriptors"}, + {0xf, 5, "Data SGL length invalid"}, + {0x10, 5, "Matadata SGL length invalid"}, + {0x11, 5, "SGL descriptor type invalid"}, + {0x12, 5, "Invalid use of controller memory buffer"}, + {0x13, 5, "PRP offset invalid"}, + {0x14, 2, "Atomic write unit exceeded"}, + {0x15, 8, "Operation denied"}, + {0x16, 5, "SGL offset invalid"}, + {0x17, 5, "Reserved [0x17]"}, + {0x18, 5, "Host identifier inconsistent format"}, + {0x19, 5, "Keep alive timeout expired"}, + {0x1a, 5, "Keep alive timeout invalid"}, + {0x1b, 6, "Command aborted due to Preempt and Abort"}, + {0x1c, 10, "Sanitize failed"}, + {0x1d, 11, "Sanitize in progress"}, + {0x1e, 5, "SGL data block granularity invalid"}, + {0x1f, 5, "Command not supported for queue in CMB"}, + {0x20, 18, "Namespace is write protected"}, /* NVMe 1.4 */ + {0x21, 6, "Command interrupted"}, /* NVMe 1.4 */ + {0x22, 5, "Transient transport error"}, /* NVMe 1.4 */ + + /* 0x80 - 0xbf: I/O command set specific */ + /* Generic command status values, NVM (I/O) Command Set */ + {0x80, 12, "LBA out of range"}, + {0x81, 3, "Capacity exceeded"}, + {0x82, 13, "Namespace not ready"}, + {0x83, 14, "Reservation conflict"}, + {0x84, 15, "Format in progress"}, + /* 0xc0 - 0xff: vendor specific */ + + /* Command specific status values, Status Code Type (SCT): 1h */ + {0x100, 5, "Completion queue invalid"}, + {0x101, 5, "Invalid queue identifier"}, + {0x102, 5, "Invalid queue size"}, + {0x103, 5, "Abort command limit exceeded"}, + {0x104, 5, "Reserved [0x104]"}, + {0x105, 5, "Asynchronous event request limit exceeded"}, + {0x106, 5, "Invalid firmware slot"}, + {0x107, 5, "Invalid firmware image"}, + {0x108, 5, "Invalid interrupt vector"}, + {0x109, 5, "Invalid log page"}, + {0x10a,16, "Invalid format"}, + {0x10b, 5, "Firmware activation requires conventional reset"}, + {0x10c, 5, "Invalid queue deletion"}, + {0x10d, 5, "Feature identifier not saveable"}, + {0x10e, 5, "Feature not changeable"}, + {0x10f, 5, "Feature not namespace specific"}, + {0x110, 5, "Firmware activation requires NVM subsystem reset"}, + {0x111, 5, "Firmware activation requires reset"}, + {0x112, 5, "Firmware activation requires maximum time violation"}, + {0x113, 5, "Firmware activation prohibited"}, + {0x114, 5, "Overlapping range"}, + {0x115, 5, "Namespace insufficient capacity"}, + {0x116, 5, "Namespace identifier unavailable"}, + {0x117, 5, "Reserved [0x107]"}, + {0x118, 5, "Namespace already attached"}, + {0x119, 5, "Namespace is private"}, + {0x11a, 5, "Namespace not attached"}, + {0x11b, 3, "Thin provisioning not supported"}, + {0x11c, 3, "Controller list invalid"}, + {0x11d,17, "Device self-test in progress"}, + {0x11e,18, "Boot partition write prohibited"}, + {0x11f, 5, "Invalid controller identifier"}, + {0x120, 5, "Invalid secondary controller state"}, + {0x121, 5, "Invalid number of controller resources"}, + {0x122, 5, "Invalid resource identifier"}, + {0x123, 5, "Sanitize prohibited while PM enabled"}, /* NVMe 1.4 */ + {0x124, 5, "ANA group identifier invalid"}, /* NVMe 1.4 */ + {0x125, 5, "ANA attach failed"}, /* NVMe 1.4 */ + + /* Command specific status values, Status Code Type (SCT): 1h + * for NVM (I/O) Command Set */ + {0x180, 2, "Conflicting attributes"}, + {0x181,19, "Invalid protection information"}, + {0x182,18, "Attempted write to read only range"}, + /* 0x1c0 - 0x1ff: vendor specific */ + + /* Media and Data Integrity error values, Status Code Type (SCT): 2h */ + {0x280,20, "Write fault"}, + {0x281,21, "Unrecovered read error"}, + {0x282,22, "End-to-end guard check error"}, + {0x283,23, "End-to-end application tag check error"}, + {0x284,24, "End-to-end reference tag check error"}, + {0x285,25, "Compare failure"}, + {0x286, 8, "Access denied"}, + {0x287,26, "Deallocated or unwritten logical block"}, + /* 0x2c0 - 0x2ff: vendor specific */ + + /* Leave this Sentinel value at end of this array */ + {0x3ff, 0, NULL}, +}; + +/* The sg_lib_nvme_cmd_status_arr[n].peri_dev_type field is an index + * to this array. It allows an NVMe status (error) value to be mapped + * to this SCSI tuple: status, sense_key, additional sense code (asc) and + * asc qualifier (ascq). For brevity SAM_STAT_CHECK_CONDITION is written + * as 0x2. */ +struct sg_lib_4tuple_u8 sg_lib_scsi_status_sense_arr[] = +{ + {SAM_STAT_GOOD, SPC_SK_NO_SENSE, 0, 0}, /* it's all good */ /* 0 */ + {SAM_STAT_CHECK_CONDITION, SPC_SK_ILLEGAL_REQUEST, 0x20, 0x0},/* opcode */ + {0x2, SPC_SK_ILLEGAL_REQUEST, 0x24, 0x0}, /* field in cdb */ + {0x2, SPC_SK_MEDIUM_ERROR, 0x0, 0x0}, + {SAM_STAT_TASK_ABORTED, SPC_SK_ABORTED_COMMAND, 0xb, 0x8}, + {0x2, SPC_SK_HARDWARE_ERROR, 0x44, 0x0}, /* internal error */ /* 5 */ + {SAM_STAT_TASK_ABORTED, SPC_SK_ABORTED_COMMAND, 0x0, 0x0}, + {0x2, SPC_SK_ILLEGAL_REQUEST, 0x20, 0x9}, /* invalid LU */ + {0x2, SPC_SK_ILLEGAL_REQUEST, 0x20, 0x2}, /* access denied */ + {0x2, SPC_SK_ILLEGAL_REQUEST, 0x2c, 0x0}, /* cmd sequence error */ + {0x2, SPC_SK_MEDIUM_ERROR, 0x31, 0x3}, /* sanitize failed */ /* 10 */ + {0x2, SPC_SK_NOT_READY, 0x4, 0x1b}, /* sanitize in progress */ + {0x2, SPC_SK_ILLEGAL_REQUEST, 0x21, 0x0}, /* LBA out of range */ + {0x2, SPC_SK_NOT_READY, 0x4, 0x0}, /* not reportable; 0x1: becoming */ + {SAM_STAT_RESERVATION_CONFLICT, 0x0, 0x0, 0x0}, + {0x2, SPC_SK_NOT_READY, 0x4, 0x4}, /* format in progress */ /* 15 */ + {0x2, SPC_SK_ILLEGAL_REQUEST, 0x31, 0x1}, /* format failed */ + {0x2, SPC_SK_NOT_READY, 0x4, 0x9}, /* self-test in progress */ + {0x2, SPC_SK_DATA_PROTECT, 0x27, 0x0}, /* write prohibited */ + {0x2, SPC_SK_ILLEGAL_REQUEST, 0x10, 0x5}, /* protection info */ + {0x2, SPC_SK_MEDIUM_ERROR, 0x3, 0x0}, /* periph dev w fault */ /* 20 */ + {0x2, SPC_SK_MEDIUM_ERROR, 0x11, 0x0}, /* unrecoc rd */ + {0x2, SPC_SK_MEDIUM_ERROR, 0x10, 0x1}, /* PI guard */ + {0x2, SPC_SK_MEDIUM_ERROR, 0x10, 0x2}, /* PI app tag */ + {0x2, SPC_SK_MEDIUM_ERROR, 0x10, 0x2}, /* PI app tag */ + {0x2, SPC_SK_MISCOMPARE, 0x1d, 0x0}, /* during verify */ /* 25 */ + {0x2, SPC_SK_MEDIUM_ERROR, 0x21, 0x6}, /* read invalid data */ + + /* Leave this Sentinel value at end of this array */ + {0xff, 0xff, 0xff, 0xff}, +}; + +/* These are the error (or warning) exit status values and their associated + * strings. They combine utility input syntax errors, SCSI status and sense + * key categories, OS errors (e.g. ENODEV for device not found), one that + * indicates NVMe non-zero status plus listing those that a Unix OS generates + * for any executable (that fails). The convention is 0 means no error and + * that in Unix the exit status is an (unsigned) 8 bit value. */ +struct sg_value_2names_t sg_exit_str_arr[] = { + {0, "No errors", "may also convey true"}, + {1, "Syntax error", "command line options (usually)"}, + {2, "Device not ready", "type: sense key"}, + {3, "Medium or hardware error", "type: sense key (plus blank check for " + "tape)"}, + {5, "Illegal request", "type: sense key, apart from Invalid opcode"}, + {6, "Unit attention", "type: sense key"}, + {7, "Data protect", "type: sense key; write protected media?"}, + {9, "Illegal request, Invalid opcode", "type: sense key + asc,ascq"}, + {10, "Copy aborted", "type: sense key"}, + {11, "Aborted command", + "type: sense key, other than protection related (asc=0x10)"}, + {14, "Miscompare", "type: sense key"}, + {15, "File error", NULL}, + {17, "Illegal request with Info field", NULL}, + {18, "Medium or hardware error with Info", NULL}, + {20, "No sense key", "type: probably additional sense code"}, + {21, "Recovered error (warning)", "tye: sense key"}, + /* N.B. this is a warning not error */ + {22, "LBA out of range", NULL}, + {24, "Reservation conflict", "type: SCSI status"}, + {25, "Condition met", "type: SCSI status"}, /* from PRE-FETCH command */ + {26, "Busy", "type: SCSI status"}, /* could be transport issue */ + {27, "Task set full", "type: SCSI status"}, + {28, "ACA aactive", "type: SCSI status"}, + {29, "Task aborted", "type: SCSI status"}, + {31, "Contradict", "command line options contradict or select bad mode"}, + {32, "Logic error", "unexpected situation, contact author"}, + {33, "SCSI command timeout", NULL}, /* OS timed out command */ + {36, "No errors (false)", NULL}, + {40, "Aborted command, protection error", NULL}, + {41, "Aborted command, protection error with Info field", NULL}, + {47, "flock (Unix system call) error", NULL}, /* ddpt */ + {48, "NVMe command with non-zero status", NULL}, + {50, "An OS error occurred", "(errno > 46 or negative)"}, + /* OS errors (errno in Unix) from 1 to 46 mapped into this range */ + {97, "Malformed SCSI command", "trouble building command"}, + {98, "Some other sense error", "try '-v' option for more information"}, + {99, "Some other error", "possible transport of driver issue"}, + {100, "Parameter list length error", NULL}, /* these for ddpt, xcopy */ + {101, "Invalid field in parameter", NULL}, + {102, "Too many segments in parameters", NULL}, + {103, "Target underrun", NULL}, + {104, "Target overrun", NULL}, + {105, "Operation in progress", NULL}, + {106, "Insufficient resources to create ROD", NULL}, + {107, "Insufficient resources to create ROD token", NULL}, + {108, "Commands cleared by device server", NULL}, + {109, "See leave_reason for error", NULL}, /* internal error */ + /* DDPT_CAT_TOKOP_BASE: asc=0x23, ascq=110 follow */ + {110, "Invalid token operation, cause not reportable", NULL}, + {111, "Invalid token operation, unsupported token type", NULL}, + {112, "Invalid token operation, remote token usage not supported", NULL}, + {113, "Invalid token operation, remote token creation not supported", + NULL}, + {114, "Invalid token operation, token unknown", NULL}, + {115, "Invalid token operation, token corrupt", NULL}, + {116, "Invalid token operation, token revoked", NULL}, + {117, "Invalid token operation, token expired", NULL}, + {118, "Invalid token operation, token cancelled", NULL}, + {119, "Invalid token operation, token deleted", NULL}, + {120, "Invalid token operation, invalid token length", NULL}, + + /* The following error codes are generated by a Unix OS */ + {126, "Utility found but did not have execute permissions", NULL}, + {127, "Utility to be executed was not found", NULL}, + {128, "Utility stopped/aborted by signal number: 0", "signal # 0 ??"}, + /* 128 + : signal number that aborted the utility. + real time signals start at offset SIGRTMIN */ + /* OS signals from 1 to 126 mapped into this range (129 to 254) */ + {255, "Utility returned 255 or higher", "Windows error number?"}, + {0xffff, NULL, NULL}, /* end marking sentinel */ +}; + +#else /* (SG_SCSI_STRINGS && HAVE_NVME && (! IGNORE_NVME)) */ + +struct sg_lib_simple_value_name_t sg_lib_nvme_admin_cmd_arr[] = +{ + + /* Vendor specific 0x80 to 0xff */ + {0xffff, NULL}, /* Sentinel */ +}; + +struct sg_lib_simple_value_name_t sg_lib_nvme_nvm_cmd_arr[] = +{ + + /* Vendor specific 0x80 to 0xff */ + {0xffff, NULL}, /* Sentinel */ +}; + +struct sg_lib_value_name_t sg_lib_nvme_cmd_status_arr[] = +{ + + /* Leave this Sentinel value at end of this array */ + {0x3ff, 0, NULL}, +}; + +struct sg_lib_4tuple_u8 sg_lib_scsi_status_sense_arr[] = +{ + + /* Leave this Sentinel value at end of this array */ + {0xff, 0xff, 0xff, 0xff}, +}; + +struct sg_value_2names_t sg_exit_str_arr[] = { + {0xffff, NULL, NULL}, /* end marking sentinel */ +}; + +#endif /* (SG_SCSI_STRINGS && HAVE_NVME && (! IGNORE_NVME)) */ diff -Nru sdparm-1.10/lib/sg_pt_common.c sdparm-1.12/lib/sg_pt_common.c --- sdparm-1.10/lib/sg_pt_common.c 2014-06-06 16:55:26.000000000 +0000 +++ sdparm-1.12/lib/sg_pt_common.c 2020-07-24 18:56:21.000000000 +0000 @@ -1,23 +1,649 @@ /* - * Copyright (c) 2009-2014 Douglas Gilbert. + * Copyright (c) 2009-2020 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. + * + * SPDX-License-Identifier: BSD-2-Clause */ +#include #include +#include +#include +#include +#include +#define __STDC_FORMAT_MACROS 1 +#include -#include "sg_pt.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif +#include "sg_lib.h" +#include "sg_pt.h" +#include "sg_unaligned.h" +#include "sg_pr2serr.h" +#include "sg_pr2serr.h" + +#if (HAVE_NVME && (! IGNORE_NVME)) +#include "sg_pt_nvme.h" +#endif + +static const char * scsi_pt_version_str = "3.16 20200722"; -static const char * scsi_pt_version_str = "2.12 20140606"; const char * scsi_pt_version() { return scsi_pt_version_str; } + +const char * +sg_pt_version() +{ + return scsi_pt_version_str; +} + + +#if (HAVE_NVME && (! IGNORE_NVME)) +/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ + +#define SAVING_PARAMS_UNSUP 0x39 +#define INVALID_FIELD_IN_CDB 0x24 +#define INVALID_FIELD_IN_PARAM_LIST 0x26 +#define PARAMETER_LIST_LENGTH_ERR 0x1a + +static const char * nvme_scsi_vendor_str = "NVMe "; + + +#define F_SA_LOW 0x80 /* cdb byte 1, bits 4 to 0 */ +#define F_SA_HIGH 0x100 /* as used by variable length cdbs */ +#define FF_SA (F_SA_HIGH | F_SA_LOW) +#define F_INV_OP 0x200 + +/* Table of SCSI operation code (opcodes) supported by SNTL */ +static struct sg_opcode_info_t sg_opcode_info_arr[] = +{ + {0x0, 0, 0, {6, /* TEST UNIT READY */ + 0, 0, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0x3, 0, 0, {6, /* REQUEST SENSE */ + 0xe1, 0, 0, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0x12, 0, 0, {6, /* INQUIRY */ + 0xe3, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0x1b, 0, 0, {6, /* START STOP UNIT */ + 0x1, 0, 0xf, 0xf7, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0x1c, 0, 0, {6, /* RECEIVE DIAGNOSTIC RESULTS */ + 0x1, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0x1d, 0, 0, {6, /* SEND DIAGNOSTIC */ + 0xf7, 0x0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0x25, 0, 0, {10, /* READ CAPACITY(10) */ + 0x1, 0xff, 0xff, 0xff, 0xff, 0, 0, 0x1, 0xc7, 0, 0, 0, 0, 0, 0} }, + {0x28, 0, 0, {10, /* READ(10) */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0, 0, 0, + 0, 0} }, + {0x2a, 0, 0, {10, /* WRITE(10) */ + 0xfb, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0, 0, 0, + 0, 0} }, + {0x2f, 0, 0, {10, /* VERIFY(10) */ + 0xf6, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0, 0, 0, + 0, 0} }, + {0x35, 0, 0, {10, /* SYNCHRONIZE CACHE(10) */ + 0x7, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0, 0, 0, + 0, 0} }, + {0x41, 0, 0, {10, /* WRITE SAME(10) */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0, 0, 0, + 0, 0} }, + {0x55, 0, 0, {10, /* MODE SELECT(10) */ + 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} }, + {0x5a, 0, 0, {10, /* MODE SENSE(10) */ + 0x18, 0xff, 0xff, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} }, + {0x88, 0, 0, {16, /* READ(16) */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xc7} }, + {0x8a, 0, 0, {16, /* WRITE(16) */ + 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xc7} }, + {0x8f, 0, 0, {16, /* VERIFY(16) */ + 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x3f, 0xc7} }, + {0x91, 0, 0, {16, /* SYNCHRONIZE CACHE(16) */ + 0x7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x3f, 0xc7} }, + {0x93, 0, 0, {16, /* WRITE SAME(16) */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x3f, 0xc7} }, + {0x9e, 0x10, F_SA_LOW, {16, /* READ CAPACITY(16) [service action in] */ + 0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x1, 0xc7} }, + {0xa0, 0, 0, {12, /* REPORT LUNS */ + 0xe3, 0xff, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0, 0, 0} }, + {0xa3, 0xc, F_SA_LOW, {12, /* REPORT SUPPORTED OPERATION CODES */ + 0xc, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0, 0, + 0} }, + {0xa3, 0xd, F_SA_LOW, {12, /* REPORT SUPPORTED TASK MAN. FUNCTIONS */ + 0xd, 0x80, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0, 0, 0} }, + + {0xff, 0xffff, 0xffff, {0, /* Sentinel, keep as last element */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, +}; + +/* Returns pointer to array of struct sg_opcode_info_t of SCSI commands + * translated to NVMe. */ +const struct sg_opcode_info_t * +sg_get_opcode_translation(void) +{ + return sg_opcode_info_arr; +} + +/* Given the NVMe Identify controller response and optionally the NVMe + * Identify namespace response (NULL otherwise), generate the SCSI VPD + * page 0x83 (device identification) descriptor(s) in dop. Return the + * number of bytes written which will not exceed max_do_len. Probably use + * Peripheral Device Type (pdt) of 0 (disk) for don't know. Transport + * protocol (tproto) should be -1 if not known, else SCSI value. + * N.B. Does not write total VPD page length into dop[2:3] . */ +int +sg_make_vpd_devid_for_nvme(const uint8_t * nvme_id_ctl_p, + const uint8_t * nvme_id_ns_p, int pdt, + int tproto, uint8_t * dop, int max_do_len) +{ + bool have_nguid, have_eui64; + int k, n; + char b[4]; + + if ((NULL == nvme_id_ctl_p) || (NULL == dop) || (max_do_len < 56)) + return 0; + + memset(dop, 0, max_do_len); + dop[0] = 0x1f & pdt; /* (PQ=0)<<5 | (PDT=pdt); 0 or 0xd (SES) */ + dop[1] = 0x83; /* Device Identification VPD page number */ + /* Build a T10 Vendor ID based designator (desig_id=1) for controller */ + if (tproto >= 0) { + dop[4] = ((0xf & tproto) << 4) | 0x2; + dop[5] = 0xa1; /* PIV=1, ASSOC=2 (target device), desig_id=1 */ + } else { + dop[4] = 0x2; /* Prococol id=0, code_set=2 (ASCII) */ + dop[5] = 0x21; /* PIV=0, ASSOC=2 (target device), desig_id=1 */ + } + memcpy(dop + 8, nvme_scsi_vendor_str, 8); /* N.B. this is "NVMe " */ + memcpy(dop + 16, nvme_id_ctl_p + 24, 40); /* MN */ + for (k = 40; k > 0; --k) { + if (' ' == dop[15 + k]) + dop[15 + k] = '_'; /* convert trailing spaces */ + else + break; + } + if (40 == k) + --k; + n = 16 + 1 + k; + if (max_do_len < (n + 20)) + return 0; + memcpy(dop + n, nvme_id_ctl_p + 4, 20); /* SN */ + for (k = 20; k > 0; --k) { /* trim trailing spaces */ + if (' ' == dop[n + k - 1]) + dop[n + k - 1] = '\0'; + else + break; + } + n += k; + if (0 != (n % 4)) + n = ((n / 4) + 1) * 4; /* round up to next modulo 4 */ + dop[7] = n - 8; + if (NULL == nvme_id_ns_p) + return n; + + /* Look for NGUID (16 byte identifier) or EUI64 (8 byte) fields in + * NVME Identify for namespace. If found form a EUI and a SCSI string + * descriptor for non-zero NGUID or EUI64 (prefer NGUID if both). */ + have_nguid = ! sg_all_zeros(nvme_id_ns_p + 104, 16); + have_eui64 = ! sg_all_zeros(nvme_id_ns_p + 120, 8); + if ((! have_nguid) && (! have_eui64)) + return n; + if (have_nguid) { + if (max_do_len < (n + 20)) + return n; + dop[n + 0] = 0x1; /* Prococol id=0, code_set=1 (binary) */ + dop[n + 1] = 0x02; /* PIV=0, ASSOC=0 (lu), desig_id=2 (eui) */ + dop[n + 3] = 16; + memcpy(dop + n + 4, nvme_id_ns_p + 104, 16); + n += 20; + if (max_do_len < (n + 40)) + return n; + dop[n + 0] = 0x3; /* Prococol id=0, code_set=3 (utf8) */ + dop[n + 1] = 0x08; /* PIV=0, ASSOC=0 (lu), desig_id=8 (scsi string) */ + dop[n + 3] = 36; + memcpy(dop + n + 4, "eui.", 4); + for (k = 0; k < 16; ++k) { + snprintf(b, sizeof(b), "%02X", nvme_id_ns_p[104 + k]); + memcpy(dop + n + 8 + (2 * k), b, 2); + } + return n + 40; + } else { /* have_eui64 is true, 8 byte identifier */ + if (max_do_len < (n + 12)) + return n; + dop[n + 0] = 0x1; /* Prococol id=0, code_set=1 (binary) */ + dop[n + 1] = 0x02; /* PIV=0, ASSOC=0 (lu), desig_id=2 (eui) */ + dop[n + 3] = 8; + memcpy(dop + n + 4, nvme_id_ns_p + 120, 8); + n += 12; + if (max_do_len < (n + 24)) + return n; + dop[n + 0] = 0x3; /* Prococol id=0, code_set=3 (utf8) */ + dop[n + 1] = 0x08; /* PIV=0, ASSOC=0 (lu), desig_id=8 (scsi string) */ + dop[n + 3] = 20; + memcpy(dop + n + 4, "eui.", 4); + for (k = 0; k < 8; ++k) { + snprintf(b, sizeof(b), "%02X", nvme_id_ns_p[120 + k]); + memcpy(dop + n + 8 + (2 * k), b, 2); + } + return n + 24; + } +} + +/* Disconnect-Reconnect page for mode_sense */ +static int +resp_disconnect_pg(uint8_t * p, int pcontrol) +{ + uint8_t disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}; + + memcpy(p, disconnect_pg, sizeof(disconnect_pg)); + if (1 == pcontrol) + memset(p + 2, 0, sizeof(disconnect_pg) - 2); + return sizeof(disconnect_pg); +} + +static uint8_t caching_m_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0, + 0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0, + 0, 0, 0, 0}; + +/* Control mode page (SBC) for mode_sense */ +static int +resp_caching_m_pg(unsigned char *p, int pcontrol, bool wce) +{ /* Caching page for mode_sense */ + uint8_t ch_caching_m_pg[] = {/* 0x8, 18, */ 0x4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t d_caching_m_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0, + 0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0, 0, 0, 0, 0}; + + // if (SDEBUG_OPT_N_WCE & sdebug_opts) + caching_m_pg[2] &= ~0x4; /* set WCE=0 (default WCE=1) */ + if ((0 == pcontrol) || (3 == pcontrol)) { + if (wce) + caching_m_pg[2] |= 0x4; + else + caching_m_pg[2] &= ~0x4; + } + memcpy(p, caching_m_pg, sizeof(caching_m_pg)); + if (1 == pcontrol) { + if (wce) + ch_caching_m_pg[2] |= 0x4; + else + ch_caching_m_pg[2] &= ~0x4; + memcpy(p + 2, ch_caching_m_pg, sizeof(ch_caching_m_pg)); + } + else if (2 == pcontrol) { + if (wce) + d_caching_m_pg[2] |= 0x4; + else + d_caching_m_pg[2] &= ~0x4; + memcpy(p, d_caching_m_pg, sizeof(d_caching_m_pg)); + } + return sizeof(caching_m_pg); +} + +static uint8_t ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0, + 0, 0, 0x2, 0x4b}; + +/* Control mode page for mode_sense */ +static int +resp_ctrl_m_pg(uint8_t *p, int pcontrol) +{ + uint8_t ch_ctrl_m_pg[] = {/* 0xa, 10, */ 0x6, 0, 0, 0, 0, 0, + 0, 0, 0, 0}; + uint8_t d_ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0, + 0, 0, 0x2, 0x4b}; + + memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg)); + if (1 == pcontrol) + memcpy(p + 2, ch_ctrl_m_pg, sizeof(ch_ctrl_m_pg)); + else if (2 == pcontrol) + memcpy(p, d_ctrl_m_pg, sizeof(d_ctrl_m_pg)); + return sizeof(ctrl_m_pg); +} + +static uint8_t ctrl_ext_m_pg[] = {0x4a, 0x1, 0, 0x1c, 0, 0, 0x40, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, }; + +/* Control Extension mode page [0xa,0x1] for mode_sense */ +static int +resp_ctrl_ext_m_pg(uint8_t *p, int pcontrol) +{ + uint8_t ch_ctrl_ext_m_pg[] = {/* 0x4a, 0x1, 0, 0x1c, */ 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, }; + uint8_t d_ctrl_ext_m_pg[] = {0x4a, 0x1, 0, 0x1c, 0, 0, 0x40, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, }; + + memcpy(p, ctrl_ext_m_pg, sizeof(ctrl_ext_m_pg)); + if (1 == pcontrol) + memcpy(p + 4, ch_ctrl_ext_m_pg, sizeof(ch_ctrl_ext_m_pg)); + else if (2 == pcontrol) + memcpy(p, d_ctrl_ext_m_pg, sizeof(d_ctrl_ext_m_pg)); + return sizeof(ctrl_ext_m_pg); +} + +static uint8_t iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0, 0, 0, 0x0, 0x0}; + +/* Informational Exceptions control mode page for mode_sense */ +static int +resp_iec_m_pg(uint8_t *p, int pcontrol) +{ + uint8_t ch_iec_m_pg[] = {/* 0x1c, 0xa, */ 0x4, 0xf, 0, 0, 0, 0, 0, 0, + 0x0, 0x0}; + uint8_t d_iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0, 0, 0, 0x0, 0x0}; + + memcpy(p, iec_m_pg, sizeof(iec_m_pg)); + if (1 == pcontrol) + memcpy(p + 2, ch_iec_m_pg, sizeof(ch_iec_m_pg)); + else if (2 == pcontrol) + memcpy(p, d_iec_m_pg, sizeof(d_iec_m_pg)); + return sizeof(iec_m_pg); +} + +static uint8_t vs_ua_m_pg[] = {0x0, 0xe, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0}; + +/* Vendor specific Unit Attention mode page for mode_sense */ +static int +resp_vs_ua_m_pg(uint8_t *p, int pcontrol) +{ + uint8_t ch_vs_ua_m_pg[] = {/* 0x0, 0xe, */ 0xff, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t d_vs_ua_m_pg[] = {0x0, 0xe, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}; + + memcpy(p, vs_ua_m_pg, sizeof(vs_ua_m_pg)); + if (1 == pcontrol) + memcpy(p + 2, ch_vs_ua_m_pg, sizeof(ch_vs_ua_m_pg)); + else if (2 == pcontrol) + memcpy(p, d_vs_ua_m_pg, sizeof(d_vs_ua_m_pg)); + return sizeof(vs_ua_m_pg); +} + +void +sntl_init_dev_stat(struct sg_sntl_dev_state_t * dsp) +{ + if (dsp) { + dsp->scsi_dsense = !! (0x4 & ctrl_m_pg[2]); + dsp->enclosure_override = vs_ua_m_pg[2]; + } +} + + +#define SDEBUG_MAX_MSENSE_SZ 256 + +/* Only support MODE SENSE(10). Returns the number of bytes written to dip, + * or -1 if error info placed in resp. */ +int +sntl_resp_mode_sense10(const struct sg_sntl_dev_state_t * dsp, + const uint8_t * cdbp, uint8_t * dip, int mx_di_len, + struct sg_sntl_result_t * resp) +{ + bool dbd, llbaa, is_disk, bad_pcode; + int pcontrol, pcode, subpcode, bd_len, alloc_len, offset, len; + const uint32_t num_blocks = 0x100000; /* made up */ + const uint32_t lb_size = 512; /* guess */ + uint8_t dev_spec; + uint8_t * ap; + uint8_t arr[SDEBUG_MAX_MSENSE_SZ]; + + memset(resp, 0, sizeof(*resp)); + dbd = !! (cdbp[1] & 0x8); /* disable block descriptors */ + pcontrol = (cdbp[2] & 0xc0) >> 6; + pcode = cdbp[2] & 0x3f; + subpcode = cdbp[3]; + llbaa = !!(cdbp[1] & 0x10); + is_disk = ((dsp->pdt == PDT_DISK) || (dsp->pdt == PDT_ZBC)); + if (is_disk && !dbd) + bd_len = llbaa ? 16 : 8; + else + bd_len = 0; + alloc_len = sg_get_unaligned_be16(cdbp + 7); + memset(arr, 0, SDEBUG_MAX_MSENSE_SZ); + if (0x3 == pcontrol) { /* Saving values not supported */ + resp->asc = SAVING_PARAMS_UNSUP; + goto err_out; + } + /* for disks set DPOFUA bit and clear write protect (WP) bit */ + if (is_disk) + dev_spec = 0x10; /* =0x90 if WP=1 implies read-only */ + else + dev_spec = 0x0; + arr[3] = dev_spec; + if (16 == bd_len) + arr[4] = 0x1; /* set LONGLBA bit */ + arr[7] = bd_len; /* assume 255 or less */ + offset = 8; + ap = arr + offset; + + if (8 == bd_len) { + sg_put_unaligned_be32(num_blocks, ap + 0); + sg_put_unaligned_be16((uint16_t)lb_size, ap + 6); + offset += bd_len; + ap = arr + offset; + } else if (16 == bd_len) { + sg_put_unaligned_be64(num_blocks, ap + 0); + sg_put_unaligned_be32(lb_size, ap + 12); + offset += bd_len; + ap = arr + offset; + } + bad_pcode = false; + + switch (pcode) { + case 0x2: /* Disconnect-Reconnect page, all devices */ + if (0x0 == subpcode) + len = resp_disconnect_pg(ap, pcontrol); + else { + len = 0; + bad_pcode = true; + } + offset += len; + break; + case 0x8: /* Caching Mode page, disk (like) devices */ + if (! is_disk) { + len = 0; + bad_pcode = true; + } else if (0x0 == subpcode) + len = resp_caching_m_pg(ap, pcontrol, dsp->wce); + else { + len = 0; + bad_pcode = true; + } + offset += len; + break; + case 0xa: /* Control Mode page, all devices */ + if (0x0 == subpcode) + len = resp_ctrl_m_pg(ap, pcontrol); + else if (0x1 == subpcode) + len = resp_ctrl_ext_m_pg(ap, pcontrol); + else { + len = 0; + bad_pcode = true; + } + offset += len; + break; + case 0x1c: /* Informational Exceptions Mode page, all devices */ + if (0x0 == subpcode) + len = resp_iec_m_pg(ap, pcontrol); + else { + len = 0; + bad_pcode = true; + } + offset += len; + break; + case 0x3f: /* Read all Mode pages */ + if ((0 == subpcode) || (0xff == subpcode)) { + len = 0; + len = resp_disconnect_pg(ap + len, pcontrol); + if (is_disk) + len += resp_caching_m_pg(ap + len, pcontrol, dsp->wce); + len += resp_ctrl_m_pg(ap + len, pcontrol); + if (0xff == subpcode) + len += resp_ctrl_ext_m_pg(ap + len, pcontrol); + len += resp_iec_m_pg(ap + len, pcontrol); + len += resp_vs_ua_m_pg(ap + len, pcontrol); + offset += len; + } else { + resp->asc = INVALID_FIELD_IN_CDB; + resp->in_byte = 3; + resp->in_bit = 255; + goto err_out; + } + break; + case 0x0: /* Vendor specific "Unit Attention" mode page */ + /* all sub-page codes ?? */ + len = resp_vs_ua_m_pg(ap, pcontrol); + offset += len; + break; /* vendor is "NVMe " (from INQUIRY field) */ + default: + bad_pcode = true; + break; + } + if (bad_pcode) { + resp->asc = INVALID_FIELD_IN_CDB; + resp->in_byte = 2; + resp->in_bit = 5; + goto err_out; + } + sg_put_unaligned_be16(offset - 2, arr + 0); + len = (alloc_len < offset) ? alloc_len : offset; + len = (len < mx_di_len) ? len : mx_di_len; + memcpy(dip, arr, len); + return len; + +err_out: + resp->sstatus = SAM_STAT_CHECK_CONDITION; + resp->sk = SPC_SK_ILLEGAL_REQUEST; + return -1; +} + +#define SDEBUG_MAX_MSELECT_SZ 512 + +/* Only support MODE SELECT(10). Returns number of bytes used from dop, + * else -1 on error with sense code placed in resp. */ +int +sntl_resp_mode_select10(struct sg_sntl_dev_state_t * dsp, + const uint8_t * cdbp, const uint8_t * dop, int do_len, + struct sg_sntl_result_t * resp) +{ + int pf, sp, ps, md_len, bd_len, off, spf, pg_len, rlen, param_len, mpage; + int sub_mpage; + uint8_t arr[SDEBUG_MAX_MSELECT_SZ]; + + memset(resp, 0, sizeof(*resp)); + memset(arr, 0, sizeof(arr)); + pf = cdbp[1] & 0x10; + sp = cdbp[1] & 0x1; + param_len = sg_get_unaligned_be16(cdbp + 7); + if ((0 == pf) || sp || (param_len > SDEBUG_MAX_MSELECT_SZ)) { + resp->asc = INVALID_FIELD_IN_CDB; + resp->in_byte = 1; + if (sp) + resp->in_bit = 0; + else if (0 == pf) + resp->in_bit = 4; + else { + resp->in_byte = 7; + resp->in_bit = 255; + } + goto err_out; + } + rlen = (do_len < param_len) ? do_len : param_len; + memcpy(arr, dop, rlen); + md_len = sg_get_unaligned_be16(arr + 0) + 2; + bd_len = sg_get_unaligned_be16(arr + 6); + if (md_len > 2) { + resp->asc = INVALID_FIELD_IN_PARAM_LIST; + resp->in_byte = 0; + resp->in_bit = 255; + goto err_out; + } + off = bd_len + 8; + mpage = arr[off] & 0x3f; + ps = !!(arr[off] & 0x80); + if (ps) { + resp->asc = INVALID_FIELD_IN_PARAM_LIST; + resp->in_byte = off; + resp->in_bit = 7; + goto err_out; + } + spf = !!(arr[off] & 0x40); + pg_len = spf ? (sg_get_unaligned_be16(arr + off + 2) + 4) : + (arr[off + 1] + 2); + sub_mpage = spf ? arr[off + 1] : 0; + if ((pg_len + off) > param_len) { + resp->asc = PARAMETER_LIST_LENGTH_ERR; + goto err_out; + } + switch (mpage) { + case 0x8: /* Caching Mode page */ + if (0x0 == sub_mpage) { + if (caching_m_pg[1] == arr[off + 1]) { + memcpy(caching_m_pg + 2, arr + off + 2, + sizeof(caching_m_pg) - 2); + dsp->wce = !!(caching_m_pg[2] & 0x4); + dsp->wce_changed = true; + break; + } + } + goto def_case; + case 0xa: /* Control Mode page */ + if (0x0 == sub_mpage) { + if (ctrl_m_pg[1] == arr[off + 1]) { + memcpy(ctrl_m_pg + 2, arr + off + 2, + sizeof(ctrl_m_pg) - 2); + dsp->scsi_dsense = !!(ctrl_m_pg[2] & 0x4); + break; + } + } + goto def_case; + case 0x1c: /* Informational Exceptions Mode page (SBC) */ + if (0x0 == sub_mpage) { + if (iec_m_pg[1] == arr[off + 1]) { + memcpy(iec_m_pg + 2, arr + off + 2, + sizeof(iec_m_pg) - 2); + break; + } + } + goto def_case; + case 0x0: /* Vendor specific "Unit Attention" mode page */ + if (vs_ua_m_pg[1] == arr[off + 1]) { + memcpy(vs_ua_m_pg + 2, arr + off + 2, + sizeof(vs_ua_m_pg) - 2); + dsp->enclosure_override = vs_ua_m_pg[2]; + } + break; + default: +def_case: + resp->asc = INVALID_FIELD_IN_PARAM_LIST; + resp->in_byte = off; + resp->in_bit = 5; + goto err_out; + } + return rlen; + +err_out: + resp->sk = SPC_SK_ILLEGAL_REQUEST; + resp->sstatus = SAM_STAT_CHECK_CONDITION; + return -1; +} + +#endif /* (HAVE_NVME && (! IGNORE_NVME)) [near line 140] */ diff -Nru sdparm-1.10/lib/sg_pt_freebsd.c sdparm-1.12/lib/sg_pt_freebsd.c --- sdparm-1.10/lib/sg_pt_freebsd.c 2015-05-12 04:21:05.000000000 +0000 +++ sdparm-1.12/lib/sg_pt_freebsd.c 2021-03-03 20:46:45.000000000 +0000 @@ -1,20 +1,27 @@ /* - * Copyright (c) 2005-2015 Douglas Gilbert. + * Copyright (c) 2005-2021 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. + * + * SPDX-License-Identifier: BSD-2-Clause */ -/* sg_pt_freebsd version 1.13 20150511 */ +/* sg_pt_freebsd version 1.39 20210225 */ #include #include #include +#include #include #include #include +#include +#include /* for basename */ #include #include +#define __STDC_FORMAT_MACROS 1 +#include /* from PRIx macros */ #include #include #include @@ -26,11 +33,21 @@ #include #include +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "sg_pt.h" #include "sg_lib.h" +#include "sg_unaligned.h" +#include "sg_pt_nvme.h" +#include "sg_pr2serr.h" -#ifdef HAVE_CONFIG_H -#include "config.h" +#if (HAVE_NVME && (! IGNORE_NVME)) +#include "freebsd_nvme_ioctl.h" +#else +#define NVME_CTRLR_PREFIX "/dev/nvme" +#define NVME_NS_PREFIX "ns" #endif @@ -39,9 +56,21 @@ 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; /* OS device type, if false ignore nvme_our_sntl */ + bool nvme_our_sntl; /* true: our SNTL; false: received NVMe command */ + bool is_char; + uint32_t nsid; + uint32_t nv_ctrlid; + int dev_fd; // for NVMe, use -1 to indicate not provided + uint32_t nvme_result; // cdw0 from completion + uint16_t nvme_status; // from completion: ((sct << 8) | sc) + char* devname; // the device name + struct cam_device* cam_dev; + uint8_t * nvme_id_ctlp; + uint8_t * free_nvme_id_ctlp; + uint8_t cq_dw0_3[16]; + struct sg_sntl_dev_state_t dev_stat; // owner }; // Private table of open devices: guaranteed zero on startup since @@ -53,66 +82,91 @@ struct sg_pt_freebsd_scsi { struct cam_device* cam_dev; // copy held for error processing union ccb *ccb; - unsigned char * cdb; + uint8_t * cdb; int cdb_len; - unsigned char * sense; + uint8_t * sense; int sense_len; - unsigned char * dxferp; + uint8_t * dxferp; int dxfer_len; - int dxfer_dir; + int dxfer_dir; /* CAM_DIR_NONE, _IN, _OUT and _BOTH */ + uint8_t * dxferip; + uint8_t * dxferop; + uint8_t * mdxferp; + uint32_t dxfer_ilen; + uint32_t dxfer_olen; + uint32_t mdxfer_len; + bool mdxfer_out; + int timeout_ms; int scsi_status; int resid; int sense_resid; int in_err; int os_err; int transport_err; + int dev_han; // should be >= FREEBSD_FDOFFSET then + // (dev_han - FREEBSD_FDOFFSET) is the + // index into devicetable[] + bool is_nvme; // copy of same field in fdc object + bool nvme_our_sntl; // copy of same field in fdc object + struct sg_sntl_dev_state_t * dev_statp; // points to associated fdc }; struct sg_pt_base { struct sg_pt_freebsd_scsi impl; }; -#ifdef __GNUC__ -static int pr2ws(const char * fmt, ...) - __attribute__ ((format (printf, 1, 2))); -#else -static int pr2ws(const char * fmt, ...); -#endif +static const uint32_t broadcast_nsid = SG_NVME_BROADCAST_NSID; +static int sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb); -static int -pr2ws(const char * fmt, ...) + + +static struct freebsd_dev_channel * +get_fdc_p(struct sg_pt_freebsd_scsi * ptp) { - va_list args; - int n; + int han = ptp->dev_han - FREEBSD_FDOFFSET; - va_start(args, fmt); - n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args); - va_end(args); - return n; + if ((han < 0) || (han >= FREEBSD_MAXDEV)) + return NULL; + return devicetable[han]; } +static const struct freebsd_dev_channel * +get_fdc_cp(const struct sg_pt_freebsd_scsi * ptp) +{ + int han = ptp->dev_han - FREEBSD_FDOFFSET; + + if ((han < 0) || (han >= FREEBSD_MAXDEV)) + return NULL; + return devicetable[han]; +} /* Returns >= 0 if successful. If error in Unix returns negated errno. */ int -scsi_pt_open_device(const char * device_name, int read_only, int verbose) +scsi_pt_open_device(const char * device_name, bool read_only, int vb) { int oflags = 0 /* O_NONBLOCK*/ ; oflags |= (read_only ? O_RDONLY : O_RDWR); - return scsi_pt_open_flags(device_name, oflags, verbose); + return scsi_pt_open_flags(device_name, oflags, vb); } /* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed - * together. The 'flags' argument is ignored in FreeBSD. + * together. The 'oflags' 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 vb) { - struct freebsd_dev_channel *fdchan; + bool is_char, is_block, possible_nvme; + char tmp, first_ch; + int k, err, dev_fd, ret; + uint32_t nsid, nv_ctrlid; + ssize_t s; + struct freebsd_dev_channel *fdc_p = NULL; struct cam_device* cam_dev; - int k; + struct stat a_stat; + char dev_nm[PATH_MAX]; // Search table for a free entry for (k = 0; k < FREEBSD_MAXDEV; k++) @@ -122,94 +176,259 @@ // If no free entry found, return error. We have max allowed number // of "file descriptors" already allocated. if (k == FREEBSD_MAXDEV) { - if (verbose) + if (vb) pr2ws("too many open file descriptors (%d)\n", FREEBSD_MAXDEV); - errno = EMFILE; - return -1; + ret = -EMFILE; + goto err_out; + } + first_ch = device_name[0]; + if (('/' != first_ch) && ('.' != first_ch)) { + char b[PATH_MAX]; + + /* Step 1: if device_name is symlink, follow it */ + s = readlink(device_name, b, sizeof(b)); + if (s <= 0) { + strncpy(b, device_name, PATH_MAX - 1); + b[PATH_MAX - 1] = '\0'; + } + /* Step 2: if no leading '/' nor '.' given, prepend '/dev/' */ + first_ch = b[0]; + if (('/' != first_ch) && ('.' != first_ch)) + snprintf(dev_nm, PATH_MAX, "%s%s", "/dev/", b); + else + strcpy(dev_nm, b); + } else + strcpy(dev_nm, device_name); + if (stat(dev_nm, &a_stat) < 0) { + err = errno; + pr2ws("%s: unable to stat(%s): %s\n", __func__, dev_nm, + 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 (vb) + pr2ws("%s: %s is not char nor block device\n", __func__, + dev_nm); + ret = -ENODEV; + goto err_out; + } + + /* 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(dev_nm, NVME_CTRLR_PREFIX "%u%c", &nv_ctrlid, &tmp) == 1) { + if(nv_ctrlid == broadcast_nsid) + break; + } else if (sscanf(dev_nm, 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 *) + fdc_p = (struct freebsd_dev_channel *) calloc(1,sizeof(struct freebsd_dev_channel)); - if (fdchan == NULL) { + if (fdc_p == NULL) { // errno already set by call to calloc() - return -1; + ret = -ENOMEM; + goto err_out; + } + fdc_p->dev_fd = -1; +#if (HAVE_NVME && (! IGNORE_NVME)) + sntl_init_dev_stat(&fdc_p->dev_stat); +#endif + if (! (fdc_p->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(fdc_p->devname, DEV_IDLEN, NVME_CTRLR_PREFIX"%d", + nv_ctrlid); + dev_fd = open(fdc_p->devname, oflags); + if (dev_fd < 0) { + err = errno; + if (vb) + pr2ws("%s: open(%s) failed: %s (errno=%d), try SCSI/ATA\n", + __func__, fdc_p->devname, strerror(err), err); + goto scsi_ata_try; + } + fdc_p->is_nvme = true; + fdc_p->nvme_our_sntl = true; /* guess at this stage */ + fdc_p->is_char = is_char; + fdc_p->nsid = (broadcast_nsid == nsid) ? 0 : nsid; + fdc_p->nv_ctrlid = nv_ctrlid; + fdc_p->dev_fd = dev_fd; + devicetable[k] = fdc_p; + return k + FREEBSD_FDOFFSET; + } - if (cam_get_device(device_name, fdchan->devname, DEV_IDLEN, - &(fdchan->unitnum)) == -1) { - if (verbose) +scsi_ata_try: + fdc_p->is_char = is_char; + if (cam_get_device(dev_nm, fdc_p->devname, DEV_IDLEN, + &(fdc_p->unitnum)) == -1) { + if (vb) pr2ws("bad device name structure\n"); errno = EINVAL; - return -1; + ret = -errno; + goto err_out; } - - if (! (cam_dev = cam_open_spec_device(fdchan->devname, - fdchan->unitnum, O_RDWR, NULL))) { - if (verbose) + if (vb > 4) + pr2ws("%s: cam_get_device, f->devname: %s, f->unitnum=%d\n", __func__, + fdc_p->devname, fdc_p->unitnum); + + if (! (cam_dev = cam_open_spec_device(fdc_p->devname, + fdc_p->unitnum, O_RDWR, NULL))) { + if (vb) 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; + fdc_p->cam_dev = cam_dev; // return pointer to "file descriptor" table entry, properly offset. - devicetable[k] = fdchan; + devicetable[k] = fdc_p; return k + FREEBSD_FDOFFSET; + +err_out: /* ret should be negative value (negated errno) */ + if (fdc_p) { + if (fdc_p->devname) + free(fdc_p->devname); + free(fdc_p); + fdc_p = 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; + struct freebsd_dev_channel *fdc_p; + 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]; - if (NULL == fdchan) { + fdc_p = devicetable[han]; + if (NULL == fdc_p) { errno = ENODEV; - return -1; + return -errno; } - if (fdchan->devname) - free(fdchan->devname); - if (fdchan->cam_dev) - cam_close_device(fdchan->cam_dev); - free(fdchan); - devicetable[fd] = NULL; + if (fdc_p->devname) + free(fdc_p->devname); + if (fdc_p->cam_dev) + cam_close_device(fdc_p->cam_dev); + if (fdc_p->is_nvme) { + if (fdc_p->dev_fd >= 0) + close(fdc_p->dev_fd); + if (fdc_p->free_nvme_id_ctlp) { + free(fdc_p->free_nvme_id_ctlp); + fdc_p->nvme_id_ctlp = NULL; + fdc_p->free_nvme_id_ctlp = NULL; + } + } + free(fdc_p); + devicetable[han] = NULL; + errno = 0; return 0; } +/* Assumes device_han 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 device_han < 0. + * If error, returns negated errno (operating system) value. */ +int +check_pt_file_handle(int device_han, const char * device_name, int vb) +{ + struct freebsd_dev_channel *fdc_p; + int han = device_han - FREEBSD_FDOFFSET; + + if ((han < 0) || (han >= FREEBSD_MAXDEV)) + return -ENODEV; + fdc_p = devicetable[han]; + if (NULL == fdc_p) + return -ENODEV; + if (fdc_p->is_nvme) + return 4 - (int)fdc_p->is_char; + else if (fdc_p->cam_dev) + return 2 - (int)fdc_p->is_char; + else { + if (vb) + pr2ws("%s: neither SCSI nor NVMe ... hmm, dvice name: %s\n", + __func__, device_name); + return 0; + } +} + +#if (HAVE_NVME && (! IGNORE_NVME)) +static bool checked_ev_dsense = false; +static bool ev_dsense = false; +#endif + struct sg_pt_base * -construct_scsi_pt_obj() +construct_scsi_pt_obj_with_fd(int dev_han, int vb) { struct sg_pt_freebsd_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_freebsd_scsi *) calloc(1, sizeof(struct sg_pt_freebsd_scsi)); 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; + if (ptp->dev_han >= 0) { + struct freebsd_dev_channel *fdc_p; + + fdc_p = get_fdc_p(ptp); + if (fdc_p) { + ptp->is_nvme = fdc_p->is_nvme; + ptp->cam_dev = fdc_p->cam_dev; + ptp->dev_statp = &fdc_p->dev_stat; +#if (HAVE_NVME && (! IGNORE_NVME)) + sntl_init_dev_stat(ptp->dev_statp); + if (! checked_ev_dsense) { + ev_dsense = sg_get_initial_dsense(); + checked_ev_dsense = true; + } + fdc_p->dev_stat.scsi_dsense = ev_dsense; +#endif + } else if (vb) + pr2ws("%s: bad dev_han=%d\n", __func__, dev_han); + } + } else if (vb) + 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) { - struct sg_pt_freebsd_scsi * ptp = &vp->impl; + struct sg_pt_freebsd_scsi * ptp; - if (ptp) { + if (NULL == vp) { + pr2ws(">>>> %s: given NULL pointer\n", __func__); + return; + } + if ((ptp = &vp->impl)) { if (ptp->ccb) cam_freeccb(ptp->ccb); free(ptp); @@ -219,73 +438,204 @@ void clear_scsi_pt_obj(struct sg_pt_base * vp) { - struct sg_pt_freebsd_scsi * ptp = &vp->impl; + bool is_nvme; + int dev_han; + struct sg_pt_freebsd_scsi * ptp; + struct cam_device* cam_dev; + struct sg_sntl_dev_state_t * dsp; - if (ptp) { + if (NULL == vp) { + pr2ws(">>>>> %s: NULL pointer given\n", __func__); + return; + } + if ((ptp = &vp->impl)) { if (ptp->ccb) cam_freeccb(ptp->ccb); + is_nvme = ptp->is_nvme; + dev_han = ptp->dev_han; + cam_dev = ptp->cam_dev; + dsp = ptp->dev_statp; memset(ptp, 0, sizeof(struct sg_pt_freebsd_scsi)); ptp->dxfer_dir = CAM_DIR_NONE; + ptp->dev_han = dev_han; + ptp->is_nvme = is_nvme; + ptp->cam_dev = cam_dev; + ptp->dev_statp = dsp; } } void -set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb, int cdb_len) +partial_clear_scsi_pt_obj(struct sg_pt_base * vp) { struct sg_pt_freebsd_scsi * ptp = &vp->impl; + struct freebsd_dev_channel *fdc_p; - if (ptp->cdb) - ++ptp->in_err; - ptp->cdb = (unsigned char *)cdb; + if (NULL == ptp) + return; + ptp->in_err = 0; + ptp->os_err = 0; + ptp->transport_err = 0; + ptp->scsi_status = 0; + ptp->dxfer_dir = CAM_DIR_NONE; + ptp->dxferip = NULL; + ptp->dxfer_ilen = 0; + ptp->dxferop = NULL; + ptp->dxfer_olen = 0; + fdc_p = get_fdc_p(ptp); + if (fdc_p) + fdc_p->nvme_result = 0; +} + +/* 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 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 vb) +{ + struct sg_pt_freebsd_scsi * ptp; + + if (NULL == vp) { + if (vb) + pr2ws(">>>> %s: pointer to object is NULL\n", __func__); + return EINVAL; + } + if ((ptp = &vp->impl)) { + struct freebsd_dev_channel *fdc_p; + + if (dev_han < 0) { + ptp->dev_han = -1; + ptp->dxfer_dir = CAM_DIR_NONE; + ptp->is_nvme = false; + ptp->cam_dev = NULL; + return 0; + } + fdc_p = get_fdc_p(ptp); + if (NULL == fdc_p) { + if (vb) + pr2ws("%s: dev_han (%d) is invalid\n", __func__, dev_han); + ptp->os_err = EINVAL; + return ptp->os_err; + } + ptp->os_err = 0; + ptp->transport_err = 0; + ptp->in_err = 0; + ptp->scsi_status = 0; + ptp->dev_han = dev_han; + ptp->dxfer_dir = CAM_DIR_NONE; + ptp->is_nvme = fdc_p->is_nvme; + ptp->cam_dev = fdc_p->cam_dev; + ptp->dev_statp = &fdc_p->dev_stat; + } + 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 uint8_t * cdb, int cdb_len) +{ + struct sg_pt_freebsd_scsi * ptp = &vp->impl; + + ptp->cdb = (uint8_t *)cdb; ptp->cdb_len = cdb_len; } +int +get_scsi_pt_cdb_len(const struct sg_pt_base * vp) +{ + const struct sg_pt_freebsd_scsi * ptp = &vp->impl; + + return ptp->cdb_len; +} + +uint8_t * +get_scsi_pt_cdb_buf(const struct sg_pt_base * vp) +{ + const struct sg_pt_freebsd_scsi * ptp = &vp->impl; + + return ptp->cdb; +} + void -set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense, +set_scsi_pt_sense(struct sg_pt_base * vp, uint8_t * sense, int max_sense_len) { struct sg_pt_freebsd_scsi * ptp = &vp->impl; - if (ptp->sense) - ++ptp->in_err; - memset(sense, 0, max_sense_len); + if (sense) { + if (max_sense_len > 0) + memset(sense, 0, max_sense_len); + } ptp->sense = sense; ptp->sense_len = max_sense_len; } /* Setup for data transfer from device */ void -set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp, +set_scsi_pt_data_in(struct sg_pt_base * vp, uint8_t * dxferp, int dxfer_len) { struct sg_pt_freebsd_scsi * ptp = &vp->impl; - if (ptp->dxferp) + if (ptp->dxferip) ++ptp->in_err; + ptp->dxferip = dxferp; + ptp->dxfer_ilen = dxfer_len; if (dxfer_len > 0) { ptp->dxferp = dxferp; ptp->dxfer_len = dxfer_len; - ptp->dxfer_dir = CAM_DIR_IN; + if (ptp->dxfer_dir == CAM_DIR_OUT) + ptp->dxfer_dir = CAM_DIR_BOTH; + else + ptp->dxfer_dir = CAM_DIR_IN; } } /* Setup for data transfer toward device */ void -set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp, +set_scsi_pt_data_out(struct sg_pt_base * vp, const uint8_t * dxferp, int dxfer_len) { struct sg_pt_freebsd_scsi * ptp = &vp->impl; - if (ptp->dxferp) + if (ptp->dxferop) ++ptp->in_err; + ptp->dxferop = (uint8_t *)dxferp; + ptp->dxfer_olen = dxfer_len; if (dxfer_len > 0) { - ptp->dxferp = (unsigned char *)dxferp; + ptp->dxferp = (uint8_t *)dxferp; ptp->dxfer_len = dxfer_len; - ptp->dxfer_dir = CAM_DIR_OUT; + if (ptp->dxfer_dir == CAM_DIR_IN) + ptp->dxfer_dir = CAM_DIR_BOTH; + else + ptp->dxfer_dir = CAM_DIR_OUT; } } void +set_pt_metadata_xfer(struct sg_pt_base * vp, uint8_t * mdxferp, + uint32_t mdxfer_len, bool out_true) +{ + struct sg_pt_freebsd_scsi * ptp = &vp->impl; + + if (ptp->mdxferp) + ++ptp->in_err; + ptp->mdxferp = mdxferp; + ptp->mdxfer_len = mdxfer_len; + if (mdxfer_len > 0) + ptp->mdxfer_out = out_true; +} + +void set_scsi_pt_packet_id(struct sg_pt_base * vp __attribute__ ((unused)), int pack_id __attribute__ ((unused))) { @@ -329,48 +679,67 @@ * 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 vb) { - int fd = device_fd - FREEBSD_FDOFFSET; + int len; struct sg_pt_freebsd_scsi * ptp = &vp->impl; - struct freebsd_dev_channel *fdchan; + struct freebsd_dev_channel *fdc_p; union ccb *ccb; - int len, timout_ms; ptp->os_err = 0; if (ptp->in_err) { - if (verbose) + if (vb) pr2ws("Replicated or unused set_scsi_pt...\n"); return SCSI_PT_DO_BAD_PARAMS; } + if (dev_han < 0) { + if (ptp->dev_han < 0) { + if (vb) + pr2ws("%s: No device file handle given\n", __func__); + return SCSI_PT_DO_BAD_PARAMS; + } + dev_han = ptp->dev_han; + } else { + if (ptp->dev_han >= 0) { + if (dev_han != ptp->dev_han) { + if (vb) + pr2ws("%s: file handle given to create and this " + "differ\n", __func__); + return SCSI_PT_DO_BAD_PARAMS; + } + } else + ptp->dev_han = dev_han; + } + if (NULL == ptp->cdb) { - if (verbose) + if (vb) pr2ws("No command (cdb) given\n"); return SCSI_PT_DO_BAD_PARAMS; } + if (ptp->is_nvme) + return sg_do_nvme_pt(vp, -1, vb); - if ((fd < 0) || (fd >= FREEBSD_MAXDEV)) { - if (verbose) - pr2ws("Bad file descriptor\n"); + fdc_p = get_fdc_p(ptp); + if (NULL == fdc_p) { + if (vb) + pr2ws("File descriptor bad or closed??\n"); ptp->os_err = ENODEV; return -ptp->os_err; } - fdchan = devicetable[fd]; - if (NULL == fdchan) { - if (verbose) - pr2ws("File descriptor closed??\n"); - ptp->os_err = ENODEV; - return -ptp->os_err; - } - if (NULL == fdchan->cam_dev) { - if (verbose) + ptp->is_nvme = fdc_p->is_nvme; + ptp->dev_statp = &fdc_p->dev_stat; + if (fdc_p->is_nvme) + return sg_do_nvme_pt(vp, -1, vb); + + if (NULL == fdc_p->cam_dev) { + if (vb) pr2ws("No open CAM device\n"); return SCSI_PT_DO_BAD_PARAMS; } if (NULL == ptp->ccb) { /* re-use if we have one already */ - if (! (ccb = cam_getccb(fdchan->cam_dev))) { - if (verbose) + if (! (ccb = cam_getccb(fdc_p->cam_dev))) { + if (vb) pr2ws("cam_getccb: failed\n"); ptp->os_err = ENOMEM; return -ptp->os_err; @@ -383,7 +752,7 @@ bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); - timout_ms = (time_secs > 0) ? (time_secs * 1000) : DEF_TIMEOUT; + ptp->timeout_ms = (time_secs > 0) ? (time_secs * 1000) : DEF_TIMEOUT; cam_fill_csio(&ccb->csio, /* retries */ 1, /* cbfcnp */ NULL, @@ -393,14 +762,14 @@ /* datalen */ ptp->dxfer_len, /* senselen */ ptp->sense_len, /* cdblen */ ptp->cdb_len, - /* timeout (millisecs) */ timout_ms); + /* timeout (millisecs) */ ptp->timeout_ms); memcpy(ccb->csio.cdb_io.cdb_bytes, ptp->cdb, ptp->cdb_len); - if (cam_send_ccb(fdchan->cam_dev, ccb) < 0) { - if (verbose) { + if (cam_send_ccb(fdc_p->cam_dev, ccb) < 0) { + if (vb) { warn("error sending SCSI ccb"); #if __FreeBSD_version > 500000 - cam_error_print(fdchan->cam_dev, ccb, CAM_ESF_ALL, + cam_error_print(fdc_p->cam_dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); #endif } @@ -428,7 +797,7 @@ } else ptp->transport_err = 1; - ptp->cam_dev = fdchan->cam_dev; // for error processing + ptp->cam_dev = fdc_p->cam_dev; // for error processing return 0; } @@ -455,15 +824,92 @@ { const struct sg_pt_freebsd_scsi * ptp = &vp->impl; - return ptp->resid; + return ((NULL == ptp) || (ptp->is_nvme && ! ptp->nvme_our_sntl)) ? + 0 : ptp->resid; +} + +void +get_pt_req_lengths(const struct sg_pt_base * vp, int * req_dinp, + int * req_doutp) +{ + const struct sg_pt_freebsd_scsi * ptp = &vp->impl; + bool bidi = (ptp->dxfer_dir == CAM_DIR_BOTH); + + if (req_dinp) { + if (ptp->dxfer_ilen > 0) + *req_dinp = ptp->dxfer_ilen; + else + *req_dinp = 0; + } + if (req_doutp) { + if ((!bidi) && (ptp->dxfer_olen > 0)) + *req_doutp = ptp->dxfer_olen; + else + *req_doutp = 0; + } +} + +void +get_pt_actual_lengths(const struct sg_pt_base * vp, int * act_dinp, + int * act_doutp) +{ + const struct sg_pt_freebsd_scsi * ptp = &vp->impl; + bool bidi = (ptp->dxfer_dir == CAM_DIR_BOTH); + + if (act_dinp) { + if (ptp->dxfer_ilen > 0) + *act_dinp = ptp->dxfer_ilen - ptp->resid; + else + *act_dinp = 0; + } + if (act_doutp) { + if ((!bidi) && (ptp->dxfer_olen > 0)) + *act_doutp = ptp->dxfer_olen - ptp->resid; + else + *act_doutp = 0; + } } +/* Returns SCSI status value (from device that received the command). If an + * NVMe command was issued directly (i.e. through do_scsi_pt() then return + * NVMe status (i.e. ((SCT << 8) | SC)). If problem returns -1. */ int 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) { + if (ptp->is_nvme && ! ptp->nvme_our_sntl) { + const struct freebsd_dev_channel *fdc_p; + + fdc_p = get_fdc_cp(ptp); + if (NULL == fdc_p) + return -1; + return (int)fdc_p->nvme_status; + } else + return ptp->scsi_status; + } + return -1; +} + +/* For NVMe command: CDW0 from completion (32 bits); for SCSI: the status */ +uint32_t +get_pt_result(const struct sg_pt_base * vp) +{ + const struct sg_pt_freebsd_scsi * ptp = &vp->impl; + + if (ptp) { + if (ptp->is_nvme && ! ptp->nvme_our_sntl) { + const struct freebsd_dev_channel *fdc_p; + + fdc_p = get_fdc_cp(ptp); + if (NULL == fdc_p) + return -1; + return fdc_p->nvme_result; + } else + return (uint32_t)ptp->scsi_status; + } + return 0xffffffff; } int @@ -477,6 +923,15 @@ return ptp->sense_len - ptp->sense_resid; } +uint8_t * +get_scsi_pt_sense_buf(const struct sg_pt_base * vp) +{ + const struct sg_pt_freebsd_scsi * ptp = &vp->impl; + + return ptp->sense; +} + +/* Not implemented so return -1 . */ int get_scsi_pt_duration_ms(const struct sg_pt_base * vp __attribute__ ((unused))) { @@ -485,6 +940,14 @@ return -1; } +/* If not available return 0 otherwise return number of nanoseconds that the + * lower layers (and hardware) took to execute the command just completed. */ +uint64_t +get_pt_duration_ns(const struct sg_pt_base * vp __attribute__ ((unused))) +{ + return 0; +} + int get_scsi_pt_transport_err(const struct sg_pt_base * vp) { @@ -493,6 +956,14 @@ return ptp->transport_err; } +void +set_scsi_pt_transport_err(struct sg_pt_base * vp, int err) +{ + struct sg_pt_freebsd_scsi * ptp = &vp->impl; + + ptp->transport_err = err; +} + int get_scsi_pt_os_err(const struct sg_pt_base * vp) { @@ -512,6 +983,11 @@ b[max_b_len - 1] = '\0'; return b; } + if (ptp->is_nvme) { + snprintf(b, max_b_len, "NVMe has no transport errors at present " + "but tranport_err=%d ??\n", ptp->transport_err); + return b; + } #if __FreeBSD_version > 500000 if (ptp->cam_dev) cam_error_string(ptp->cam_dev, ptp->ccb, b, max_b_len, CAM_ESF_ALL, @@ -527,6 +1003,47 @@ return b; } +bool +pt_device_is_nvme(const struct sg_pt_base * vp) +{ + const struct sg_pt_freebsd_scsi * ptp = &vp->impl; + + if (ptp && (ptp->dev_han >= 0)) { + const struct freebsd_dev_channel *fdc_p; + + fdc_p = get_fdc_cp(ptp); + if (NULL == fdc_p) { + pr2ws("%s: unable to find fdc_p\n", __func__); + errno = ENODEV; + return false; + } + /* if unequal, cast away const and drive fdc_p value into ptp */ + if (ptp->is_nvme != fdc_p->is_nvme) /* indicates logic error */ + ((struct sg_pt_freebsd_scsi *)ptp)->is_nvme = fdc_p->is_nvme; + return fdc_p->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)) { + const struct freebsd_dev_channel *fdc_p; + + fdc_p = get_fdc_cp(ptp); + if (NULL == fdc_p) + return 0; + return fdc_p->nsid; + } + return 0; +} + char * get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b) { @@ -539,3 +1056,1409 @@ b[max_b_len - 1] = '\0'; return b; } + + +#define SCSI_INQUIRY_OPC 0x12 +#define SCSI_MAINT_IN_OPC 0xa3 +#define SCSI_MODE_SENSE10_OPC 0x5a +#define SCSI_MODE_SELECT10_OPC 0x55 +#define SCSI_READ_CAPACITY10_OPC 0x25 +#define SCSI_RECEIVE_DIAGNOSTIC_OPC 0x1c +#define SCSI_REP_SUP_OPCS_OPC 0xc +#define SCSI_REP_SUP_TMFS_OPC 0xd +#define SCSI_REPORT_LUNS_OPC 0xa0 +#define SCSI_REQUEST_SENSE_OPC 0x3 +#define SCSI_SEND_DIAGNOSTIC_OPC 0x1d +#define SCSI_TEST_UNIT_READY_OPC 0x0 +#define SCSI_SERVICE_ACT_IN_OPC 0x9e +#define SCSI_READ_CAPACITY16_SA 0x10 +#define SCSI_SA_MSK 0x1f + +/* Additional Sense Code (ASC) */ +#define NO_ADDITIONAL_SENSE 0x0 +#define LOGICAL_UNIT_NOT_READY 0x4 +#define LOGICAL_UNIT_COMMUNICATION_FAILURE 0x8 +#define UNRECOVERED_READ_ERR 0x11 +#define PARAMETER_LIST_LENGTH_ERR 0x1a +#define INVALID_OPCODE 0x20 +#define LBA_OUT_OF_RANGE 0x21 +#define INVALID_FIELD_IN_CDB 0x24 +#define INVALID_FIELD_IN_PARAM_LIST 0x26 +#define UA_RESET_ASC 0x29 +#define UA_CHANGED_ASC 0x2a +#define TARGET_CHANGED_ASC 0x3f +#define LUNS_CHANGED_ASCQ 0x0e +#define INSUFF_RES_ASC 0x55 +#define INSUFF_RES_ASCQ 0x3 +#define LOW_POWER_COND_ON_ASC 0x5e /* ASCQ=0 */ +#define POWER_ON_RESET_ASCQ 0x0 +#define BUS_RESET_ASCQ 0x2 /* scsi bus reset occurred */ +#define MODE_CHANGED_ASCQ 0x1 /* mode parameters changed */ +#define CAPACITY_CHANGED_ASCQ 0x9 +#define SAVING_PARAMS_UNSUP 0x39 +#define TRANSPORT_PROBLEM 0x4b +#define THRESHOLD_EXCEEDED 0x5d +#define LOW_POWER_COND_ON 0x5e +#define MISCOMPARE_VERIFY_ASC 0x1d +#define MICROCODE_CHANGED_ASCQ 0x1 /* with TARGET_CHANGED_ASC */ +#define MICROCODE_CHANGED_WO_RESET_ASCQ 0x16 + +#if (HAVE_NVME && (! IGNORE_NVME)) + +static void +mk_sense_asc_ascq(struct sg_pt_freebsd_scsi * ptp, int sk, int asc, int ascq, + int vb) +{ + bool dsense = ptp->dev_statp->scsi_dsense; + int n; + uint8_t * sbp = ptp->sense; + + ptp->scsi_status = SAM_STAT_CHECK_CONDITION; + n = ptp->sense_len; + if ((n < 8) || ((! dsense) && (n < 14))) { + pr2ws("%s: sense_len=%d too short, want 14 or more\n", __func__, n); + return; + } else + ptp->sense_resid = ptp->sense_len - + (dsense ? 8 : ((n < 18) ? n : 18)); + memset(sbp, 0, n); + sg_build_sense_buffer(dsense, sbp, sk, asc, ascq); + if (vb > 3) + pr2ws("%s: [sense_key,asc,ascq]: [0x%x,0x%x,0x%x]\n", __func__, + sk, asc, ascq); +} + +static void +mk_sense_from_nvme_status(struct sg_pt_freebsd_scsi * ptp, uint16_t sct_sc, + int vb) +{ + bool ok; + bool dsense = ptp->dev_statp->scsi_dsense; + int n; + uint8_t sstatus, sk, asc, ascq; + uint8_t * sbp = ptp->sense; + + ok = sg_nvme_status2scsi(sct_sc, &sstatus, &sk, &asc, &ascq); + if (! ok) { /* can't find a mapping to a SCSI error, so ... */ + sstatus = SAM_STAT_CHECK_CONDITION; + sk = SPC_SK_ILLEGAL_REQUEST; + asc = 0xb; + ascq = 0x0; /* asc: "WARNING" purposely vague */ + } + + ptp->scsi_status = sstatus; + n = ptp->sense_len; + if ((n < 8) || ((! dsense) && (n < 14))) { + pr2ws("%s: sense_len=%d too short, want 14 or more\n", __func__, n); + return; + } else + ptp->sense_resid = ptp->sense_len - + (dsense ? 8 : ((n < 18) ? n : 18)); + memset(sbp, 0, n); + sg_build_sense_buffer(dsense, sbp, sk, asc, ascq); + if (vb > 3) + pr2ws("%s: [sense_key,asc,ascq]: [0x%x,0x%x,0x%x]\n", __func__, + sk, asc, ascq); + if (dsense && (sct_sc > 0) && (ptp->sense_resid > 7)) { + sg_nvme_desc2sense(sbp, 0x4000 & sct_sc /* dnr */, + 0x2000 & sct_sc /* more */, 0x7ff & sct_sc); + ptp->sense_resid -= 8; + } +} + +/* Set in_bit to -1 to indicate no bit position of invalid field */ +static void +mk_sense_invalid_fld(struct sg_pt_freebsd_scsi * ptp, bool in_cdb, + int in_byte, int in_bit, int vb) +{ + bool ds = ptp->dev_statp->scsi_dsense; + int sl, asc, n; + uint8_t * sbp = (uint8_t *)ptp->sense; + uint8_t sks[4]; + + ptp->scsi_status = SAM_STAT_CHECK_CONDITION; + asc = in_cdb ? INVALID_FIELD_IN_CDB : INVALID_FIELD_IN_PARAM_LIST; + n = ptp->sense_len; + if ((n < 8) || ((! ds) && (n < 14))) { + pr2ws("%s: max_response_len=%d too short, want 14 or more\n", + __func__, n); + return; + } else + ptp->sense_resid = ptp->sense_len - (ds ? 8 : ((n < 18) ? n : 18)); + memset(sbp, 0, n); + sg_build_sense_buffer(ds, sbp, SPC_SK_ILLEGAL_REQUEST, asc, 0); + memset(sks, 0, sizeof(sks)); + sks[0] = 0x80; + if (in_cdb) + sks[0] |= 0x40; + if (in_bit >= 0) { + sks[0] |= 0x8; + sks[0] |= (0x7 & in_bit); + } + sg_put_unaligned_be16(in_byte, sks + 1); + if (ds) { + sl = sbp[7] + 8; + sbp[7] = sl; + sbp[sl] = 0x2; + sbp[sl + 1] = 0x6; + memcpy(sbp + sl + 4, sks, 3); + } else + memcpy(sbp + 15, sks, 3); + if (vb > 3) + pr2ws("%s: [sense_key,asc,ascq]: [0x5,0x%x,0x0] %c byte=%d, bit=%d\n", + __func__, asc, in_cdb ? 'C' : 'D', in_byte, + ((in_bit > 0) ? (0x7 & in_bit) : 0)); +} + +/* Does actual ioctl(NVME_PASSTHROUGH_CMD). Returns 0 on success; negative + * values are Unix negated errno values; positive values are NVMe status + * (i.e. ((SCT << 8) | SC) ). */ +static int +nvme_pt_low(struct freebsd_dev_channel *fdc_p, void * dxferp, uint32_t len, + bool is_read, struct nvme_pt_command * npcp, int vb) +{ + int err; + uint16_t sct_sc; + uint8_t opcode; + char b[80]; + + if (fdc_p->dev_fd < 0) { + if (vb) + pr2ws("%s: is_nvme is true but dev_fd<0, inconsistent\n", + __func__); + return -EINVAL; + } + npcp->buf = dxferp; + npcp->len = len; + npcp->is_read = (uint32_t)is_read; + opcode = npcp->cmd.opc; + err = ioctl(fdc_p->dev_fd, NVME_PASSTHROUGH_CMD, npcp); + if (err < 0) + return -errno; /* Assume Unix error in normal place */ + sct_sc = (NVME_STATUS_GET_SCT(npcp->cpl.status) << 8) | + NVME_STATUS_GET_SC(npcp->cpl.status); + fdc_p->nvme_result = npcp->cpl.cdw0; + sg_put_unaligned_le32(npcp->cpl.cdw0, + fdc_p->cq_dw0_3 + SG_NVME_PT_CQ_RESULT); + sg_put_unaligned_le32(npcp->cpl.rsvd1, fdc_p->cq_dw0_3 + 4); + sg_put_unaligned_le16(npcp->cpl.sqhd, fdc_p->cq_dw0_3 + 8); + sg_put_unaligned_le16(npcp->cpl.sqid, fdc_p->cq_dw0_3 + 10); + sg_put_unaligned_le16(npcp->cpl.cid, fdc_p->cq_dw0_3 + 12); + sg_put_unaligned_le16(*((const uint16_t *)&(npcp->cpl.status)), + fdc_p->cq_dw0_3 + SG_NVME_PT_CQ_STATUS_P); + if (sct_sc && (vb > 1)) { + char nam[64]; + + sg_get_nvme_opcode_name(opcode, true, sizeof(nam), nam); + pr2ws("%s: %s [0x%x], status: %s\n", __func__, nam, opcode, + sg_get_nvme_cmd_status_str(sct_sc, sizeof(b), b)); + } + return sct_sc; +} + +static void +sntl_check_enclosure_override(struct freebsd_dev_channel * fdc_p, int vb) +{ + uint8_t * up = fdc_p->nvme_id_ctlp; + uint8_t nvmsr; + + if (NULL == up) + return; + nvmsr = up[253]; + if (vb > 3) + pr2ws("%s: enter, nvmsr=%u\n", __func__, nvmsr); + fdc_p->dev_stat.id_ctl253 = nvmsr; + switch (fdc_p->dev_stat.enclosure_override) { + case 0x0: /* no override */ + if (0x3 & nvmsr) { + fdc_p->dev_stat.pdt = PDT_DISK; + fdc_p->dev_stat.enc_serv = 1; + } else if (0x2 & nvmsr) { + fdc_p->dev_stat.pdt = PDT_SES; + fdc_p->dev_stat.enc_serv = 1; + } else if (0x1 & nvmsr) { + fdc_p->dev_stat.pdt = PDT_DISK; + fdc_p->dev_stat.enc_serv = 0; + } else { + uint32_t nn = sg_get_unaligned_le32(up + 516); + + fdc_p->dev_stat.pdt = nn ? PDT_DISK : PDT_UNKNOWN; + fdc_p->dev_stat.enc_serv = 0; + } + break; + case 0x1: /* override to SES device */ + fdc_p->dev_stat.pdt = PDT_SES; + fdc_p->dev_stat.enc_serv = 1; + break; + case 0x2: /* override to disk with attached SES device */ + fdc_p->dev_stat.pdt = PDT_DISK; + fdc_p->dev_stat.enc_serv = 1; + break; + case 0x3: /* override to SAFTE device (PDT_PROCESSOR) */ + fdc_p->dev_stat.pdt = PDT_PROCESSOR; + fdc_p->dev_stat.enc_serv = 1; + break; + case 0xff: /* override to normal disk */ + fdc_p->dev_stat.pdt = PDT_DISK; + fdc_p->dev_stat.enc_serv = 0; + break; + default: + pr2ws("%s: unknown enclosure_override value: %d\n", __func__, + fdc_p->dev_stat.enclosure_override); + break; + } +} + +static int +sntl_do_identify(struct freebsd_dev_channel * fdc_p, int cns, int nsid, + int u_len, uint8_t * up, int vb) +{ + int err; + struct nvme_pt_command npc; + uint8_t * npc_up = (uint8_t *)&npc; + + memset(npc_up, 0, sizeof(npc)); + npc_up[SG_NVME_PT_OPCODE] = 0x6; /* Identify */ + sg_put_unaligned_le32(nsid, npc_up + SG_NVME_PT_NSID); + /* CNS=0x1 Identify: controller */ + sg_put_unaligned_le32(cns, npc_up + SG_NVME_PT_CDW10); + sg_put_unaligned_le64((sg_uintptr_t)up, npc_up + SG_NVME_PT_ADDR); + sg_put_unaligned_le32(u_len, npc_up + SG_NVME_PT_DATA_LEN); + err = nvme_pt_low(fdc_p, up, u_len, true, &npc, vb); + if (err) { + if (err < 0) { + if (vb > 1) + pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", __func__, + strerror(-err), -err); + return err; + } else { /* non-zero NVMe command status */ + fdc_p->nvme_status = err; + return SG_LIB_NVME_STATUS; + } + } + return 0; +} + +/* Currently only caches associated controller response (4096 bytes) */ +static int +sntl_cache_identity(struct freebsd_dev_channel * fdc_p, int vb) +{ + int ret; + uint32_t pg_sz = sg_get_page_size(); + + fdc_p->nvme_id_ctlp = sg_memalign(pg_sz, pg_sz, + &fdc_p->free_nvme_id_ctlp, vb > 3); + if (NULL == fdc_p->nvme_id_ctlp) { + pr2ws("%s: sg_memalign() failed to get memory\n", __func__); + return -ENOMEM; + } + ret = sntl_do_identify(fdc_p, 0x1 /* CNS */, 0 /* nsid */, pg_sz, + fdc_p->nvme_id_ctlp, vb); + if (0 == ret) + sntl_check_enclosure_override(fdc_p, vb); + return (ret < 0) ? sg_convert_errno(-ret) : ret; +} + +static const char * nvme_scsi_vendor_str = "NVMe "; +static const uint16_t inq_resp_len = 36; + +static int +sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) +{ + bool evpd; + bool cp_id_ctl = false; + int res; + uint16_t n, alloc_len, pg_cd; + uint32_t pg_sz = sg_get_page_size(); + struct freebsd_dev_channel * fdc_p; + uint8_t * nvme_id_ns = NULL; + uint8_t * free_nvme_id_ns = NULL; + uint8_t inq_dout[256]; + + if (vb > 3) + pr2ws("%s: starting\n", __func__); + + if (0x2 & cdbp[1]) { /* Reject CmdDt=1 */ + mk_sense_invalid_fld(ptp, true, 1, 1, vb); + return 0; + } + fdc_p = get_fdc_p(ptp); + if (NULL == fdc_p) { + pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__); + return -EINVAL; + } + if (NULL == fdc_p->nvme_id_ctlp) { + res = sntl_cache_identity(fdc_p, vb); + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb); + return 0; + } else if (res) /* should be negative errno */ + return res; + } + memset(inq_dout, 0, sizeof(inq_dout)); + alloc_len = sg_get_unaligned_be16(cdbp + 3); + evpd = !!(0x1 & cdbp[1]); + pg_cd = cdbp[2]; + if (evpd) { /* VPD page responses */ + switch (pg_cd) { + case 0: /* Supported VPD pages VPD page */ + /* inq_dout[0] = (PQ=0)<<5 | (PDT=0); prefer pdt=0xd --> SES */ + inq_dout[1] = pg_cd; + n = 11; + sg_put_unaligned_be16(n - 4, inq_dout + 2); + inq_dout[4] = 0x0; + inq_dout[5] = 0x80; + inq_dout[6] = 0x83; + inq_dout[7] = 0x86; + inq_dout[8] = 0x87; + inq_dout[9] = 0x92; + inq_dout[n - 1] = SG_NVME_VPD_NICR; /* last VPD number */ + break; + case 0x80: /* Serial number VPD page */ + /* inq_dout[0] = (PQ=0)<<5 | (PDT=0); prefer pdt=0xd --> SES */ + inq_dout[1] = pg_cd; + n = 24; + sg_put_unaligned_be16(n - 4, inq_dout + 2); + memcpy(inq_dout + 4, fdc_p->nvme_id_ctlp + 4, 20); /* SN */ + break; + case 0x83: /* Device identification VPD page */ + if ((fdc_p->nsid > 0) && (fdc_p->nsid < SG_NVME_BROADCAST_NSID)) { + nvme_id_ns = sg_memalign(pg_sz, pg_sz, &free_nvme_id_ns, + vb > 3); + if (nvme_id_ns) { + struct nvme_pt_command npc; + uint8_t * npc_up = (uint8_t *)&npc; + + memset(npc_up, 0, sizeof(npc)); + npc_up[SG_NVME_PT_OPCODE] = 0x6; /* Identify */ + sg_put_unaligned_le32(fdc_p->nsid, + npc_up + SG_NVME_PT_NSID); + /* CNS=0x0 Identify: namespace */ + sg_put_unaligned_le32(0x0, npc_up + SG_NVME_PT_CDW10); + sg_put_unaligned_le64((sg_uintptr_t)nvme_id_ns, + npc_up + SG_NVME_PT_ADDR); + sg_put_unaligned_le32(pg_sz, + npc_up + SG_NVME_PT_DATA_LEN); + res = nvme_pt_low(fdc_p, nvme_id_ns, pg_sz, true, &npc, + vb > 3); + if (res) { + free(free_nvme_id_ns); + free_nvme_id_ns = NULL; + nvme_id_ns = NULL; + } + } + } + n = sg_make_vpd_devid_for_nvme(fdc_p->nvme_id_ctlp, nvme_id_ns, 0, + -1, inq_dout, sizeof(inq_dout)); + if (n > 3) + sg_put_unaligned_be16(n - 4, inq_dout + 2); + if (free_nvme_id_ns) { + free(free_nvme_id_ns); + free_nvme_id_ns = NULL; + nvme_id_ns = NULL; + } + break; + case 0x86: /* Extended INQUIRY (per SFS SPC Discovery 2016) */ + inq_dout[1] = pg_cd; + n = 64; + sg_put_unaligned_be16(n - 4, inq_dout + 2); + inq_dout[5] = 0x1; /* SIMPSUP=1 */ + inq_dout[7] = 0x1; /* LUICLR=1 */ + inq_dout[13] = 0x40; /* max supported sense data length */ + break; + case 0x87: /* Mode page policy (per SFS SPC Discovery 2016) */ + inq_dout[1] = pg_cd; + n = 8; + sg_put_unaligned_be16(n - 4, inq_dout + 2); + inq_dout[4] = 0x3f; /* all mode pages */ + inq_dout[5] = 0xff; /* and their sub-pages */ + inq_dout[6] = 0x80; /* MLUS=1, policy=shared */ + break; + case 0x92: /* SCSI Feature set: only SPC Discovery 2016 */ + inq_dout[1] = pg_cd; + n = 10; + sg_put_unaligned_be16(n - 4, inq_dout + 2); + inq_dout[9] = 0x1; /* SFS SPC Discovery 2016 */ + break; + case SG_NVME_VPD_NICR: /* 0xde */ + inq_dout[1] = pg_cd; + sg_put_unaligned_be16((16 + 4096) - 4, inq_dout + 2); + n = 16 + 4096; + cp_id_ctl = true; + break; + default: /* Point to page_code field in cdb */ + mk_sense_invalid_fld(ptp, true, 2, 7, vb); + return 0; + } + if (alloc_len > 0) { + n = (alloc_len < n) ? alloc_len : n; + n = (n < ptp->dxfer_len) ? n : ptp->dxfer_len; + ptp->resid = ptp->dxfer_len - n; + if (n > 0) { + if (cp_id_ctl) { + memcpy((uint8_t *)ptp->dxferp, inq_dout, + (n < 16 ? n : 16)); + if (n > 16) + memcpy((uint8_t *)ptp->dxferp + 16, + fdc_p->nvme_id_ctlp, n - 16); + } else + memcpy((uint8_t *)ptp->dxferp, inq_dout, n); + } + } + } else { /* Standard INQUIRY response */ + /* pdt=0 --> disk; pdt=0xd --> SES; pdt=3 --> processor (safte) */ + inq_dout[0] = (0x1f & fdc_p->dev_stat.pdt); /* (PQ=0)<<5 */ + /* inq_dout[1] = (RMD=0)<<7 | (LU_CONG=0)<<6; rest reserved */ + inq_dout[2] = 6; /* version: SPC-4 */ + inq_dout[3] = 2; /* NORMACA=0, HISUP=0, response data format: 2 */ + inq_dout[4] = 31; /* so response length is (or could be) 36 bytes */ + inq_dout[6] = fdc_p->dev_stat.enc_serv ? 0x40 : 0; + inq_dout[7] = 0x2; /* CMDQUE=1 */ + memcpy(inq_dout + 8, nvme_scsi_vendor_str, 8); /* NVMe not Intel */ + memcpy(inq_dout + 16, fdc_p->nvme_id_ctlp + 24, 16);/* Prod <-- MN */ + memcpy(inq_dout + 32, fdc_p->nvme_id_ctlp + 64, 4); /* Rev <-- FR */ + if (alloc_len > 0) { + n = (alloc_len < inq_resp_len) ? alloc_len : inq_resp_len; + n = (n < ptp->dxfer_len) ? n : ptp->dxfer_len; + if (n > 0) + memcpy((uint8_t *)ptp->dxferp, inq_dout, n); + } + } + return 0; +} + +static int +sntl_rluns(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) +{ + int res; + uint16_t sel_report; + uint32_t alloc_len, k, n, num, max_nsid; + struct freebsd_dev_channel * fdc_p; + uint8_t * rl_doutp; + uint8_t * up; + + if (vb > 3) + pr2ws("%s: starting\n", __func__); + fdc_p = get_fdc_p(ptp); + if (NULL == fdc_p) { + pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__); + return -EINVAL; + } + sel_report = cdbp[2]; + alloc_len = sg_get_unaligned_be32(cdbp + 6); + if (NULL == fdc_p->nvme_id_ctlp) { + res = sntl_cache_identity(fdc_p, vb); + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb); + return 0; + } else if (res) + return res; + } + max_nsid = sg_get_unaligned_le32(fdc_p->nvme_id_ctlp + 516); + switch (sel_report) { + case 0: + case 2: + num = max_nsid; + break; + case 1: + case 0x10: + case 0x12: + num = 0; + break; + case 0x11: + num = (1 == fdc_p->nsid) ? max_nsid : 0; + break; + default: + if (vb > 1) + pr2ws("%s: bad select_report value: 0x%x\n", __func__, + sel_report); + mk_sense_invalid_fld(ptp, true, 2, 7, vb); + return 0; + } + rl_doutp = (uint8_t *)calloc(num + 1, 8); + if (NULL == rl_doutp) { + pr2ws("%s: calloc() failed to get memory\n", __func__); + return -ENOMEM; + } + for (k = 0, up = rl_doutp + 8; k < num; ++k, up += 8) + sg_put_unaligned_be16(k, up); + n = num * 8; + sg_put_unaligned_be32(n, rl_doutp); + n+= 8; + if (alloc_len > 0) { + n = (alloc_len < n) ? alloc_len : n; + n = (n < (uint32_t)ptp->dxfer_len) ? n : (uint32_t)ptp->dxfer_len; + ptp->resid = ptp->dxfer_len - (int)n; + if (n > 0) + memcpy((uint8_t *)ptp->dxferp, rl_doutp, n); + } + res = 0; + free(rl_doutp); + return res; +} + +static int +sntl_tur(struct sg_pt_freebsd_scsi * ptp, int vb) +{ + int res, err; + uint32_t pow_state; + struct nvme_pt_command npc; + uint8_t * npc_up = (uint8_t *)&npc; + struct freebsd_dev_channel * fdc_p; + + if (vb > 3) + pr2ws("%s: starting\n", __func__); + fdc_p = get_fdc_p(ptp); + if (NULL == fdc_p) { + pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__); + return -EINVAL; + } + if (NULL == fdc_p->nvme_id_ctlp) { + res = sntl_cache_identity(fdc_p, vb); + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb); + return 0; + } else if (res) + return res; + } + memset(npc_up, 0, sizeof(npc)); + npc_up[SG_NVME_PT_OPCODE] = 0xa; /* Get feature */ + sg_put_unaligned_le32(SG_NVME_BROADCAST_NSID, npc_up + SG_NVME_PT_NSID); + /* SEL=0 (current), Feature=2 Power Management */ + sg_put_unaligned_le32(0x2, npc_up + SG_NVME_PT_CDW10); + err = nvme_pt_low(fdc_p, NULL, 0, false, &npc, vb); + if (err) { + if (err < 0) { + if (vb > 1) + pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", __func__, + strerror(-err), -err); + return err; + } else { + fdc_p->nvme_status = err; + mk_sense_from_nvme_status(ptp, err, vb); + return 0; + } + } + pow_state = (0x1f & fdc_p->nvme_result); + if (vb > 3) + pr2ws("%s: pow_state=%u\n", __func__, pow_state); +#if 0 /* pow_state bounces around too much on laptop */ + if (pow_state) + mk_sense_asc_ascq(ptp, SPC_SK_NOT_READY, LOW_POWER_COND_ON_ASC, 0, + vb); +#endif + return 0; +} + +static int +sntl_req_sense(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) +{ + bool desc; + int res, err; + uint32_t pow_state, alloc_len, n; + struct nvme_pt_command npc; + uint8_t * npc_up = (uint8_t *)&npc; + struct freebsd_dev_channel * fdc_p; + uint8_t rs_dout[64]; + + if (vb > 3) + pr2ws("%s: starting\n", __func__); + fdc_p = get_fdc_p(ptp); + if (NULL == fdc_p) { + pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__); + return -EINVAL; + } + if (NULL == fdc_p->nvme_id_ctlp) { + res = sntl_cache_identity(fdc_p, vb); + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb); + return 0; + } else if (res) + return res; + } + desc = !!(0x1 & cdbp[1]); + alloc_len = cdbp[4]; + memset(npc_up, 0, sizeof(npc)); + npc_up[SG_NVME_PT_OPCODE] = 0xa; /* Get feature */ + sg_put_unaligned_le32(SG_NVME_BROADCAST_NSID, npc_up + SG_NVME_PT_NSID); + /* SEL=0 (current), Feature=2 Power Management */ + sg_put_unaligned_le32(0x2, npc_up + SG_NVME_PT_CDW10); + err = nvme_pt_low(fdc_p, NULL, 0, false, &npc, vb); + if (err) { + if (err < 0) { + if (vb > 1) + pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", __func__, + strerror(-err), -err); + return err; + } else { + fdc_p->nvme_status = err; + mk_sense_from_nvme_status(ptp, err, vb); + return 0; + } + } + pow_state = (0x1f & fdc_p->nvme_result); + if (vb > 3) + pr2ws("%s: pow_state=%u\n", __func__, pow_state); + memset(rs_dout, 0, sizeof(rs_dout)); + if (pow_state) + sg_build_sense_buffer(desc, rs_dout, SPC_SK_NO_SENSE, + LOW_POWER_COND_ON_ASC, 0); + else + sg_build_sense_buffer(desc, rs_dout, SPC_SK_NO_SENSE, + NO_ADDITIONAL_SENSE, 0); + n = desc ? 8 : 18; + n = (n < alloc_len) ? n : alloc_len; + n = (n < (uint32_t)ptp->dxfer_len) ? n : (uint32_t)ptp->dxfer_len; + ptp->resid = ptp->dxfer_len - (int)n; + if (n > 0) + memcpy((uint8_t *)ptp->dxferp, rs_dout, n); + return 0; +} + +static int +sntl_mode_ss(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) +{ + bool is_msense = (SCSI_MODE_SENSE10_OPC == cdbp[0]); + int res, n, len; + uint8_t * bp; + struct freebsd_dev_channel * fdc_p; + struct sg_sntl_result_t sntl_result; + + if (vb > 3) + pr2ws("%s: mse%s\n", __func__, (is_msense ? "nse" : "lect")); + fdc_p = get_fdc_p(ptp); + if (NULL == fdc_p) { + pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__); + return -EINVAL; + } + if (NULL == fdc_p->nvme_id_ctlp) { + res = sntl_cache_identity(fdc_p, vb); + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb); + return 0; + } else if (res) + return res; + } + if (is_msense) { /* MODE SENSE(10) */ + len = ptp->dxfer_len; + bp = ptp->dxferp; + n = sntl_resp_mode_sense10(&fdc_p->dev_stat, cdbp, bp, len, + &sntl_result); + ptp->resid = (n >= 0) ? len - n : len; + } else { /* MODE SELECT(10) */ + uint8_t pre_enc_ov = fdc_p->dev_stat.enclosure_override; + + len = ptp->dxfer_len; + bp = ptp->dxferp; + n = sntl_resp_mode_select10(&fdc_p->dev_stat, cdbp, bp, len, + &sntl_result); + if (pre_enc_ov != fdc_p->dev_stat.enclosure_override) + sntl_check_enclosure_override(fdc_p, vb); /* ENC_OV has changed */ + } + if (n < 0) { + int in_bit = (255 == sntl_result.in_bit) ? (int)sntl_result.in_bit : + -1; + if ((SAM_STAT_CHECK_CONDITION == sntl_result.sstatus) && + (SPC_SK_ILLEGAL_REQUEST == sntl_result.sk)) { + if (INVALID_FIELD_IN_CDB == sntl_result.asc) + mk_sense_invalid_fld(ptp, true, sntl_result.in_byte, in_bit, + vb); + else if (INVALID_FIELD_IN_PARAM_LIST == sntl_result.asc) + mk_sense_invalid_fld(ptp, false, sntl_result.in_byte, in_bit, + vb); + else + mk_sense_asc_ascq(ptp, sntl_result.sk, sntl_result.asc, + sntl_result.ascq, vb); + } else + pr2ws("%s: error but no sense?? n=%d\n", __func__, n); + } + return 0; +} + +/* This is not really a SNTL. For SCSI SEND DIAGNOSTIC(PF=1) NVMe-MI + * has a special command (SES Send) to tunnel through pages to an + * enclosure. The NVMe enclosure is meant to understand the SES + * (SCSI Enclosure Services) use of diagnostics pages that are + * related to SES. */ +static int +sntl_senddiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) +{ + bool pf, self_test; + int err; + uint8_t st_cd, dpg_cd; + uint32_t alloc_len, n, dout_len, dpg_len, nvme_dst; + const uint8_t * dop; + struct nvme_pt_command npc; + uint8_t * npc_up = (uint8_t *)&npc; + struct freebsd_dev_channel * fdc_p; + + st_cd = 0x7 & (cdbp[1] >> 5); + pf = !! (0x4 & cdbp[1]); + self_test = !! (0x10 & cdbp[1]); + if (vb > 3) + pr2ws("%s: pf=%d, self_test=%d, st_code=%d\n", __func__, (int)pf, + (int)self_test, (int)st_cd); + fdc_p = get_fdc_p(ptp); + if (NULL == fdc_p) { + pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__); + return -EINVAL; + } + if (self_test || st_cd) { + memset(npc_up, 0, sizeof(npc)); + npc_up[SG_NVME_PT_OPCODE] = 0x14; /* Device self-test */ + /* just this namespace (if there is one) and controller */ + sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID); + switch (st_cd) { + case 0: /* Here if self_test is set, do short self-test */ + case 1: /* Background short */ + case 5: /* Foreground short */ + nvme_dst = 1; + break; + case 2: /* Background extended */ + case 6: /* Foreground extended */ + nvme_dst = 2; + break; + case 4: /* Abort self-test */ + nvme_dst = 0xf; + break; + default: + pr2ws("%s: bad self-test code [0x%x]\n", __func__, st_cd); + mk_sense_invalid_fld(ptp, true, 1, 7, vb); + return 0; + } + sg_put_unaligned_le32(nvme_dst, npc_up + SG_NVME_PT_CDW10); + err = nvme_pt_low(fdc_p, NULL, 0x0, false, &npc, vb); + goto do_low; + } + alloc_len = sg_get_unaligned_be16(cdbp + 3); /* parameter list length */ + dout_len = ptp->dxfer_len; + if (pf) { + if (0 == alloc_len) { + mk_sense_invalid_fld(ptp, true, 3, 7, vb); + if (vb) + pr2ws("%s: PF bit set bit param_list_len=0\n", __func__); + return 0; + } + } else { /* PF bit clear */ + if (alloc_len) { + mk_sense_invalid_fld(ptp, true, 3, 7, vb); + if (vb) + pr2ws("%s: param_list_len>0 but PF clear\n", __func__); + return 0; + } else + return 0; /* nothing to do */ + if (dout_len > 0) { + if (vb) + pr2ws("%s: dout given but PF clear\n", __func__); + return SCSI_PT_DO_BAD_PARAMS; + } + } + if (dout_len < 4) { + if (vb) + pr2ws("%s: dout length (%u bytes) too short\n", __func__, + dout_len); + return SCSI_PT_DO_BAD_PARAMS; + } + n = dout_len; + n = (n < alloc_len) ? n : alloc_len; + dop = (const uint8_t *)ptp->dxferp; + if (! sg_is_aligned(dop, 0)) { + if (vb) + pr2ws("%s: dout [0x%" PRIx64 "] not page aligned\n", __func__, + (uint64_t)ptp->dxferp); + return SCSI_PT_DO_BAD_PARAMS; + } + dpg_cd = dop[0]; + dpg_len = sg_get_unaligned_be16(dop + 2) + 4; + /* should we allow for more than one D_PG is dout ?? */ + n = (n < dpg_len) ? n : dpg_len; /* not yet ... */ + + if (vb) + pr2ws("%s: passing through d_pg=0x%x, len=%u to NVME_MI SES send\n", + __func__, dpg_cd, dpg_len); + memset(npc_up, 0, sizeof(npc)); + npc_up[SG_NVME_PT_OPCODE] = 0x1d; /* MI send; same opcode as SEND DIAG */ + sg_put_unaligned_le64((sg_uintptr_t)ptp->dxferp, + npc_up + SG_NVME_PT_ADDR); + /* NVMe 4k page size. Maybe determine this? */ + /* dout_len > 0x1000, is this a problem?? */ + sg_put_unaligned_le32(0x1000, npc_up + SG_NVME_PT_DATA_LEN); + /* NVMe Message Header */ + sg_put_unaligned_le32(0x0804, npc_up + SG_NVME_PT_CDW10); + /* nvme_mi_ses_send; (0x8 -> mi_ses_recv) */ + sg_put_unaligned_le32(0x9, npc_up + SG_NVME_PT_CDW11); + /* data-out length I hope */ + sg_put_unaligned_le32(n, npc_up + SG_NVME_PT_CDW13); + err = nvme_pt_low(fdc_p, ptp->dxferp, 0x1000, false, &npc, vb); +do_low: + if (err) { + if (err < 0) { + if (vb > 1) + pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", + __func__, strerror(-err), -err); + return err; + } else { + fdc_p->nvme_status = err; + mk_sense_from_nvme_status(ptp, err, vb); + return 0; + } + } + return 0; +} + +/* This is not really a SNTL. For SCSI RECEIVE DIAGNOSTIC RESULTS(PCV=1) + * NVMe-MI has a special command (SES Receive) to read pages through a + * tunnel from an enclosure. The NVMe enclosure is meant to understand the + * SES (SCSI Enclosure Services) use of diagnostics pages that are + * related to SES. */ +static int +sntl_recvdiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) +{ + bool pcv; + int err; + uint8_t dpg_cd; + uint32_t alloc_len, n, din_len; + const uint8_t * dip; + struct nvme_pt_command npc; + uint8_t * npc_up = (uint8_t *)&npc; + struct freebsd_dev_channel * fdc_p; + + pcv = !! (0x1 & cdbp[1]); + dpg_cd = cdbp[2]; + alloc_len = sg_get_unaligned_be16(cdbp + 3); /* parameter list length */ + if (vb > 3) + pr2ws("%s: dpg_cd=0x%x, pcv=%d, alloc_len=0x%x\n", __func__, + dpg_cd, (int)pcv, alloc_len); + fdc_p = get_fdc_p(ptp); + if (NULL == fdc_p) { + pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__); + return -EINVAL; + } + din_len = ptp->dxfer_len; + if (pcv) { + if (0 == alloc_len) { + /* T10 says not an error, hmmm */ + mk_sense_invalid_fld(ptp, true, 3, 7, vb); + if (vb) + pr2ws("%s: PCV bit set bit but alloc_len=0\n", __func__); + return 0; + } + } else { /* PCV bit clear */ + if (alloc_len) { + mk_sense_invalid_fld(ptp, true, 3, 7, vb); + if (vb) + pr2ws("%s: alloc_len>0 but PCV clear\n", __func__); + return 0; + } else + return 0; /* nothing to do */ + if (din_len > 0) { + if (vb) + pr2ws("%s: din given but PCV clear\n", __func__); + return SCSI_PT_DO_BAD_PARAMS; + } + } + n = din_len; + n = (n < alloc_len) ? n : alloc_len; + dip = (const uint8_t *)ptp->dxferp; + if (! sg_is_aligned(dip, 0)) { + if (vb) + pr2ws("%s: din [0x%" PRIx64 "] not page aligned\n", __func__, + (uint64_t)ptp->dxferp); + return SCSI_PT_DO_BAD_PARAMS; + } + + if (vb) + pr2ws("%s: expecting d_pg=0x%x from NVME_MI SES receive\n", __func__, + dpg_cd); + memset(npc_up, 0, sizeof(npc)); + npc_up[SG_NVME_PT_OPCODE] = 0x1e; /* MI receive */ + sg_put_unaligned_le64((sg_uintptr_t)ptp->dxferp, + npc_up + SG_NVME_PT_ADDR); + /* NVMe 4k page size. Maybe determine this? */ + /* dout_len > 0x1000, is this a problem?? */ + sg_put_unaligned_le32(0x1000, npc_up + SG_NVME_PT_DATA_LEN); + /* NVMe Message Header */ + sg_put_unaligned_le32(0x0804, npc_up + SG_NVME_PT_CDW10); + /* nvme_mi_ses_receive */ + sg_put_unaligned_le32(0x8, npc_up + SG_NVME_PT_CDW11); + sg_put_unaligned_le32(dpg_cd, npc_up + SG_NVME_PT_CDW12); + /* data-in length I hope */ + sg_put_unaligned_le32(n, npc_up + SG_NVME_PT_CDW13); + err = nvme_pt_low(fdc_p, ptp->dxferp, 0x1000, true, &npc, vb); + if (err) { + if (err < 0) { + if (vb > 1) + pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", + __func__, strerror(-err), -err); + return err; + } else { + fdc_p->nvme_status = err; + mk_sense_from_nvme_status(ptp, err, vb); + return 0; + } + } + ptp->resid = din_len - n; + return 0; +} + +#define F_SA_LOW 0x80 /* cdb byte 1, bits 4 to 0 */ +#define F_SA_HIGH 0x100 /* as used by variable length cdbs */ +#define FF_SA (F_SA_HIGH | F_SA_LOW) +#define F_INV_OP 0x200 + +static int +sntl_rep_opcodes(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, + int vb) +{ + bool rctd; + uint8_t reporting_opts, req_opcode, supp; + uint16_t req_sa, u; + uint32_t alloc_len, offset, a_len; + uint32_t pg_sz = sg_get_page_size(); + int k, len, count, bump; + const struct sg_opcode_info_t *oip; + uint8_t *arr; + uint8_t *free_arr; + + if (vb > 3) + pr2ws("%s: start\n", __func__); + rctd = !!(cdbp[2] & 0x80); /* report command timeout desc. */ + reporting_opts = cdbp[2] & 0x7; + req_opcode = cdbp[3]; + req_sa = sg_get_unaligned_be16(cdbp + 4); + alloc_len = sg_get_unaligned_be32(cdbp + 6); + if (alloc_len < 4 || alloc_len > 0xffff) { + mk_sense_invalid_fld(ptp, true, 6, -1, vb); + return 0; + } + a_len = pg_sz - 72; + arr = sg_memalign(pg_sz, pg_sz, &free_arr, vb > 3); + if (NULL == arr) { + pr2ws("%s: calloc() failed to get memory\n", __func__); + return -ENOMEM; + } + switch (reporting_opts) { + case 0: /* all commands */ + count = 0; + bump = rctd ? 20 : 8; + for (offset = 4, oip = sg_get_opcode_translation(); + (oip->flags != 0xffff) && (offset < a_len); ++oip) { + if (F_INV_OP & oip->flags) + continue; + ++count; + arr[offset] = oip->opcode; + sg_put_unaligned_be16(oip->sa, arr + offset + 2); + if (rctd) + arr[offset + 5] |= 0x2; + if (FF_SA & oip->flags) + arr[offset + 5] |= 0x1; + sg_put_unaligned_be16(oip->len_mask[0], arr + offset + 6); + if (rctd) + sg_put_unaligned_be16(0xa, arr + offset + 8); + offset += bump; + } + sg_put_unaligned_be32(count * bump, arr + 0); + break; + case 1: /* one command: opcode only */ + case 2: /* one command: opcode plus service action */ + case 3: /* one command: if sa==0 then opcode only else opcode+sa */ + for (oip = sg_get_opcode_translation(); oip->flags != 0xffff; ++oip) { + if ((req_opcode == oip->opcode) && (req_sa == oip->sa)) + break; + } + if ((0xffff == oip->flags) || (F_INV_OP & oip->flags)) { + supp = 1; + offset = 4; + } else { + if (1 == reporting_opts) { + if (FF_SA & oip->flags) { + mk_sense_invalid_fld(ptp, true, 2, 2, vb); + free(free_arr); + return 0; + } + req_sa = 0; + } else if ((2 == reporting_opts) && 0 == (FF_SA & oip->flags)) { + mk_sense_invalid_fld(ptp, true, 4, -1, vb); + free(free_arr); + return 0; + } + if ((0 == (FF_SA & oip->flags)) && (req_opcode == oip->opcode)) + supp = 3; + else if (0 == (FF_SA & oip->flags)) + supp = 1; + else if (req_sa != oip->sa) + supp = 1; + else + supp = 3; + if (3 == supp) { + u = oip->len_mask[0]; + sg_put_unaligned_be16(u, arr + 2); + arr[4] = oip->opcode; + for (k = 1; k < u; ++k) + arr[4 + k] = (k < 16) ? + oip->len_mask[k] : 0xff; + offset = 4 + u; + } else + offset = 4; + } + arr[1] = (rctd ? 0x80 : 0) | supp; + if (rctd) { + sg_put_unaligned_be16(0xa, arr + offset); + offset += 12; + } + break; + default: + mk_sense_invalid_fld(ptp, true, 2, 2, vb); + free(free_arr); + return 0; + } + offset = (offset < a_len) ? offset : a_len; + len = (offset < alloc_len) ? offset : alloc_len; + ptp->resid = ptp->dxfer_len - (int)len; + if (len > 0) + memcpy((uint8_t *)ptp->dxferp, arr, len); + free(free_arr); + return 0; +} + +static int +sntl_rep_tmfs(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) +{ + bool repd; + uint32_t alloc_len, len; + uint8_t arr[16]; + + if (vb > 3) + pr2ws("%s: start\n", __func__); + memset(arr, 0, sizeof(arr)); + repd = !!(cdbp[2] & 0x80); + alloc_len = sg_get_unaligned_be32(cdbp + 6); + if (alloc_len < 4) { + mk_sense_invalid_fld(ptp, true, 6, -1, vb); + return 0; + } + arr[0] = 0xc8; /* ATS | ATSS | LURS */ + arr[1] = 0x1; /* ITNRS */ + if (repd) { + arr[3] = 0xc; + len = 16; + } else + len = 4; + + len = (len < alloc_len) ? len : alloc_len; + ptp->resid = ptp->dxfer_len - (int)len; + if (len > 0) + memcpy((uint8_t *)ptp->dxferp, arr, len); + return 0; +} + +/* Note that the "Returned logical block address" (RLBA) field in the SCSI + * READ CAPACITY (10+16) command's response provides the address of the _last_ + * LBA (counting origin 0) which will be one less that the "size" in the + * NVMe Identify command response's NSZE field. One problem is that in + * some situations NSZE can be zero: temporarily set RLBA field to 0 + * (implying a 1 LB logical units size) pending further research. The LBLIB + * is the "Logical Block Length In Bytes" field in the RCAP response. */ +static int +sntl_readcap(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) +{ + bool is_rcap10 = (SCSI_READ_CAPACITY10_OPC == cdbp[0]); + int res, n, len, alloc_len, dps; + uint8_t flbas, index, lbads; /* NVMe: 2**LBADS --> Logical Block size */ + uint32_t lbafx; /* NVME: LBAF0...LBAF15, each 16 bytes */ + uint32_t pg_sz = sg_get_page_size(); + uint64_t nsze; + uint8_t * bp; + uint8_t * up; + uint8_t * free_up = NULL; + struct freebsd_dev_channel * fdc_p; + uint8_t resp[32]; + + if (vb > 3) + pr2ws("%s: RCAP%d\n", __func__, (is_rcap10 ? 10 : 16)); + fdc_p = get_fdc_p(ptp); + if (NULL == fdc_p) { + pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__); + return -EINVAL; + } + up = sg_memalign(pg_sz, pg_sz, &free_up, false); + if (NULL == up) { + pr2ws("%s: sg_memalign() failed to get memory\n", __func__); + return sg_convert_errno(ENOMEM); + } + res = sntl_do_identify(fdc_p, 0x0 /* CNS */, fdc_p->nsid, pg_sz, up, + vb); + if (res < 0) { + res = sg_convert_errno(-res); + goto fini; + } + memset(resp, 0, sizeof(resp)); + nsze = sg_get_unaligned_le64(up + 0); + flbas = up[26]; /* NVME FLBAS field from Identify, want LBAF[flbas] */ + index = 128 + (4 * (flbas & 0xf)); + lbafx = sg_get_unaligned_le32(up + index); + lbads = (lbafx >> 16) & 0xff; /* bits 16 to 23 inclusive, pow2 */ + if (is_rcap10) { + alloc_len = 8; /* implicit, not in cdb */ + if (nsze > 0xffffffff) + sg_put_unaligned_be32(0xffffffff, resp + 0); + else if (0 == nsze) /* no good answer here */ + sg_put_unaligned_be32(0, resp + 0); /* SCSI RLBA field */ + else + sg_put_unaligned_be32((uint32_t)(nsze - 1), resp + 0); + sg_put_unaligned_be32(1 << lbads, resp + 4); /* SCSI LBLIB field */ + } else { + alloc_len = sg_get_unaligned_be32(cdbp + 10); + dps = up[29]; + if (0x7 & dps) { + resp[12] = 0x1; + n = (0x7 & dps) - 1; + if (n > 0) + resp[12] |= (n + n); + } + if (0 == nsze) /* no good answer here */ + sg_put_unaligned_be64(0, resp + 0); + else + sg_put_unaligned_be64(nsze - 1, resp + 0); + sg_put_unaligned_be32(1 << lbads, resp + 8); /* SCSI LBLIB field */ + } + len = ptp->dxfer_len; + bp = ptp->dxferp; + n = 32; + n = (n < alloc_len) ? n : alloc_len; + n = (n < len) ? n : len; + ptp->resid = len - n; + if (n > 0) + memcpy(bp, resp, n); +fini: + if (free_up) + free(free_up); + return res; +} + +/* 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. */ +static int +sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb) +{ + bool scsi_cdb, in_xfer; + int n, err, len, io_len; + uint16_t sct_sc, sa; + uint8_t * dxferp; + uint8_t * npc_up; + struct freebsd_dev_channel * fdc_p; + struct sg_pt_freebsd_scsi * ptp = &vp->impl; + const uint8_t * cdbp; + struct nvme_pt_command npc; + + npc_up = (uint8_t *)&npc; + if (vb > 3) + pr2ws("%s: fd=%d\n", __func__, fd); + if (! ptp->cdb) { + if (vb) + pr2ws("%s: No NVMe command given (set_scsi_pt_cdb())\n", + __func__); + return SCSI_PT_DO_BAD_PARAMS; + } + fdc_p = get_fdc_p(ptp); + if (fd < 0) { + if (NULL == fdc_p) { + pr2ws("%s: no device handle in object or fd ?\n", __func__); + return -EINVAL; + } + } else { + int han = fd - FREEBSD_FDOFFSET; + + if ((han < 0) || (han >= FREEBSD_MAXDEV)) { + pr2ws("%s: argument 'fd' is bad\n", __func__); + return SCSI_PT_DO_BAD_PARAMS; + } + if (NULL == devicetable[han]) { + pr2ws("%s: argument 'fd' is bad (2)\n", __func__); + return SCSI_PT_DO_BAD_PARAMS; + } + if (fdc_p && (fdc_p != devicetable[han])) { + pr2ws("%s: different device handle in object and fd ?\n", + __func__); + return SCSI_PT_DO_BAD_PARAMS; + } + if (NULL == fdc_p) { + ptp->dev_han = fd; + fdc_p = devicetable[han]; + } + } + + n = ptp->cdb_len; + cdbp = (const uint8_t *)ptp->cdb; + if (vb > 3) + pr2ws("%s: opcode=0x%x, fd=%d\n", __func__, cdbp[0], fd); + scsi_cdb = sg_is_scsi_cdb(cdbp, n); + /* nvme_our_sntl is false when NVMe command (64 byte) has been given */ + ptp->nvme_our_sntl = scsi_cdb; + fdc_p->nvme_our_sntl = ptp->nvme_our_sntl; + if (scsi_cdb) { + switch (cdbp[0]) { + case SCSI_INQUIRY_OPC: + return sntl_inq(ptp, cdbp, vb); + case SCSI_REPORT_LUNS_OPC: + return sntl_rluns(ptp, cdbp, vb); + case SCSI_TEST_UNIT_READY_OPC: + return sntl_tur(ptp, vb); + case SCSI_REQUEST_SENSE_OPC: + return sntl_req_sense(ptp, cdbp, vb); + case SCSI_SEND_DIAGNOSTIC_OPC: + return sntl_senddiag(ptp, cdbp, vb); + case SCSI_RECEIVE_DIAGNOSTIC_OPC: + return sntl_recvdiag(ptp, cdbp, vb); + case SCSI_MODE_SENSE10_OPC: + case SCSI_MODE_SELECT10_OPC: + return sntl_mode_ss(ptp, cdbp, vb); + case SCSI_READ_CAPACITY10_OPC: + return sntl_readcap(ptp, cdbp, vb); + case SCSI_SERVICE_ACT_IN_OPC: + if (SCSI_READ_CAPACITY16_SA == (cdbp[1] & SCSI_SA_MSK)) + return sntl_readcap(ptp, cdbp, vb); + goto fini; + case SCSI_MAINT_IN_OPC: + sa = 0x1f & cdbp[1]; /* service action */ + if (SCSI_REP_SUP_OPCS_OPC == sa) + return sntl_rep_opcodes(ptp, cdbp, vb); + else if (SCSI_REP_SUP_TMFS_OPC == sa) + return sntl_rep_tmfs(ptp, cdbp, vb); + /* fall through */ + default: +fini: + if (vb > 2) { + char b[64]; + + sg_get_command_name(cdbp, -1, sizeof(b), b); + pr2ws("%s: no translation to NVMe for SCSI %s command\n", + __func__, b); + } + mk_sense_asc_ascq(ptp, SPC_SK_ILLEGAL_REQUEST, INVALID_OPCODE, + 0, vb); + return 0; + } + } + /* NVMe command given to pass-through */ + len = (int)sizeof(npc.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(npc_up, (const uint8_t *)ptp->cdb, n); + if (n < len) /* zero out rest of 'npc' */ + memset(npc_up + n, 0, len - n); + in_xfer = false; + io_len = 0; + dxferp = NULL; + if (ptp->dxfer_ilen > 0) { + in_xfer = true; + io_len = ptp->dxfer_ilen; + dxferp = ptp->dxferip; + sg_put_unaligned_le32(ptp->dxfer_ilen, npc_up + SG_NVME_PT_DATA_LEN); + sg_put_unaligned_le64((sg_uintptr_t)ptp->dxferip, + npc_up + SG_NVME_PT_ADDR); + } else if (ptp->dxfer_olen > 0) { + in_xfer = false; + io_len = ptp->dxfer_olen; + dxferp = ptp->dxferop; + sg_put_unaligned_le32(ptp->dxfer_olen, npc_up + SG_NVME_PT_DATA_LEN); + sg_put_unaligned_le64((sg_uintptr_t)ptp->dxferop, + npc_up + SG_NVME_PT_ADDR); + } + err = nvme_pt_low(fdc_p, dxferp, io_len, in_xfer, &npc, vb); + if (err < 0) { + if (vb > 1) + pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", + __func__, strerror(-err), -err); + return err; + } + sct_sc = err; /* ((SCT << 8) | SC) which may be 0 */ + fdc_p->nvme_status = sct_sc; + if (ptp->sense && (ptp->sense_len > 0)) { + uint32_t k = sizeof(fdc_p->cq_dw0_3); + + if ((int)k < ptp->sense_len) + ptp->sense_resid = ptp->sense_len - (int)k; + else { + k = ptp->sense_len; + ptp->sense_resid = 0; + } + memcpy(ptp->sense, fdc_p->cq_dw0_3, k); + } + if (in_xfer) + ptp->resid = 0; /* Just hoping ... */ + return sct_sc ? SG_LIB_NVME_STATUS : 0; +} + +#else /* if not(HAVE_NVME && (! IGNORE_NVME)) */ + +static int +sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb) +{ + if (vb) { + pr2ws("%s: not supported, ", __func__); +#ifdef HAVE_NVME + pr2ws("HAVE_NVME, "); +#else + pr2ws("don't HAVE_NVME, "); +#endif + +#ifdef IGNORE_NVME + pr2ws("IGNORE_NVME"); +#else + pr2ws("don't IGNORE_NVME"); +#endif + pr2ws("\n"); + if (NULL == vp) + pr2ws("%s: object pointer NULL; fd=%d\n", __func__, fd); + } + return -ENOTTY; /* inappropriate ioctl error */ +} + +#endif /* (HAVE_NVME && (! IGNORE_NVME)) */ + +#if (HAVE_NVME && (! IGNORE_NVME)) + +int +do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int vb) +{ + if (vb) + pr2ws("%s: not supported, ", __func__); + if (vp) { } + if (submq) { } + if (timeout_secs) { } + return SCSI_PT_DO_NOT_SUPPORTED; +} + +#else /* (HAVE_NVME && (! IGNORE_NVME)) */ + +int +do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int verbose) +{ + if (vb) { + pr2ws("%s: not supported, ", __func__); +#ifdef HAVE_NVME + pr2ws("HAVE_NVME, "); +#else + pr2ws("don't HAVE_NVME, "); +#endif + +#ifdef IGNORE_NVME + pr2ws("IGNORE_NVME"); +#else + pr2ws("don't IGNORE_NVME"); +#endif + } + if (vp) { } + if (submq) { } + if (timeout_secs) { } + return SCSI_PT_DO_NOT_SUPPORTED; +} + +#endif /* (HAVE_NVME && (! IGNORE_NVME)) */ diff -Nru sdparm-1.10/lib/sg_pt_linux.c sdparm-1.12/lib/sg_pt_linux.c --- sdparm-1.10/lib/sg_pt_linux.c 2015-12-20 16:23:44.000000000 +0000 +++ sdparm-1.12/lib/sg_pt_linux.c 2021-01-04 06:17:04.000000000 +0000 @@ -1,36 +1,66 @@ /* - * Copyright (c) 2005-2015 Douglas Gilbert. + * Copyright (c) 2005-2021 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. + * + * SPDX-License-Identifier: BSD-2-Clause */ -/* sg_pt_linux version 1.25 20151217 */ +/* sg_pt_linux version 1.51 20210102 */ #include #include #include +#include #include #include #include #include #include #include -#include #include +#include /* to define 'major' */ +#ifndef major +#include +#endif #ifdef HAVE_CONFIG_H #include "config.h" #endif +#include + #include "sg_pt.h" #include "sg_lib.h" #include "sg_linux_inc.h" +#include "sg_pt_linux.h" +#include "sg_pr2serr.h" + + +#ifdef major +#define SG_DEV_MAJOR major +#else +#ifdef HAVE_LINUX_KDEV_T_H +#include +#endif +#define SG_DEV_MAJOR MAJOR /* MAJOR() macro faulty if > 255 minors */ +#endif + +#ifndef BLOCK_EXT_MAJOR +#define BLOCK_EXT_MAJOR 259 +#endif #define DEF_TIMEOUT 60000 /* 60,000 millisecs (60 seconds) */ +/* sg driver displayed format: [x]xyyzz --> [x]x.[y]y.zz */ +#define SG_LINUX_SG_VER_V4_BASE 40000 /* lowest sg driver version with + * v4 interface */ +#define SG_LINUX_SG_VER_V4_FULL 40030 /* lowest version with full v4 + * interface */ + static const char * linux_host_bytes[] = { "DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT", "DID_BAD_TARGET", "DID_ABORT", "DID_PARITY", "DID_ERROR", @@ -43,27 +73,20 @@ "DID_MEDIUM_ERROR", }; -#define LINUX_HOST_BYTES_SZ \ - (int)(sizeof(linux_host_bytes) / sizeof(linux_host_bytes[0])) - static const char * linux_driver_bytes[] = { "DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA", "DRIVER_ERROR", "DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD", "DRIVER_SENSE" }; -#define LINUX_DRIVER_BYTES_SZ \ - (int)(sizeof(linux_driver_bytes) / sizeof(linux_driver_bytes[0])) - #if 0 + static const char * linux_driver_suggests[] = { "SUGGEST_OK", "SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP", "SUGGEST_DIE", "UNKNOWN","UNKNOWN","UNKNOWN", "SUGGEST_SENSE" }; -#define LINUX_DRIVER_SUGGESTS_SZ \ - (int)(sizeof(linux_driver_suggests) / sizeof(linux_driver_suggests[0])) #endif /* @@ -76,485 +99,41 @@ #define DRIVER_MASK 0x0f #endif #ifndef SUGGEST_MASK -#define SUGGEST_MASK 0xf0 +#define SUGGEST_MASK 0xf0 /* Suggest mask is obsolete */ #endif #ifndef DRIVER_SENSE #define DRIVER_SENSE 0x08 #endif #define SG_LIB_DRIVER_MASK DRIVER_MASK #define SG_LIB_SUGGEST_MASK SUGGEST_MASK -#define SG_LIB_DRIVER_SENSE DRIVER_SENSE - - -#ifdef __GNUC__ -static int pr2ws(const char * fmt, ...) - __attribute__ ((format (printf, 1, 2))); -#else -static int pr2ws(const char * fmt, ...); -#endif - - -static int -pr2ws(const char * fmt, ...) -{ - va_list args; - int n; - - va_start(args, fmt); - n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args); - va_end(args); - return n; -} - - -// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< -#if defined(IGNORE_LINUX_BSG) || ! defined(HAVE_LINUX_BSG_H) -/* - * sg(v3) via SG_IO ioctl on a sg node or other node that accepts that ioctl. - * Decision has been made at compile time because either: - * a) no /usr/include/linux/bsg.h header file was found, or - * b) the builder gave the '--enable-no-linux-bsg' option to ./configure - */ - - -struct sg_pt_linux_scsi { - struct sg_io_hdr io_hdr; - int in_err; - int os_err; -}; - -struct sg_pt_base { - struct sg_pt_linux_scsi impl; -}; - - -/* Returns >= 0 if successful. If error in Unix returns negated errno. */ -int -scsi_pt_open_device(const char * device_name, int read_only, int verbose) -{ - int oflags = O_NONBLOCK; - - oflags |= (read_only ? O_RDONLY : O_RDWR); - return scsi_pt_open_flags(device_name, oflags, verbose); -} - -/* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed */ -/* together. The 'flags' argument is advisory and may be ignored. */ -/* Returns >= 0 if successful, otherwise returns negated errno. */ -int -scsi_pt_open_flags(const char * device_name, int flags, int verbose) -{ - int fd; - - if (verbose > 1) { - pr2ws("open %s with flags=0x%x\n", device_name, flags); - } - fd = open(device_name, flags); - if (fd < 0) - fd = -errno; - return fd; -} - -/* Returns 0 if successful. If error in Unix returns negated errno. */ -int -scsi_pt_close_device(int device_fd) -{ - int res; - - res = close(device_fd); - if (res < 0) - res = -errno; - return res; -} - - -struct sg_pt_base * -construct_scsi_pt_obj() -{ - 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.interface_id = 'S'; - ptp->io_hdr.dxfer_direction = SG_DXFER_NONE; - } - return (struct sg_pt_base *)ptp; -} - -void -destruct_scsi_pt_obj(struct sg_pt_base * vp) -{ - struct sg_pt_linux_scsi * ptp = &vp->impl; - - if (ptp) - free(ptp); -} - -void -clear_scsi_pt_obj(struct sg_pt_base * vp) -{ - struct sg_pt_linux_scsi * ptp = &vp->impl; - - if (ptp) { - memset(ptp, 0, sizeof(struct sg_pt_linux_scsi)); - ptp->io_hdr.interface_id = 'S'; - ptp->io_hdr.dxfer_direction = SG_DXFER_NONE; - } -} - -void -set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb, - int cdb_len) -{ - struct sg_pt_linux_scsi * ptp = &vp->impl; - - if (ptp->io_hdr.cmdp) - ++ptp->in_err; - ptp->io_hdr.cmdp = (unsigned char *)cdb; - ptp->io_hdr.cmd_len = cdb_len; -} - -void -set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense, - int max_sense_len) -{ - struct sg_pt_linux_scsi * ptp = &vp->impl; - - if (ptp->io_hdr.sbp) - ++ptp->in_err; - memset(sense, 0, max_sense_len); - ptp->io_hdr.sbp = sense; - ptp->io_hdr.mx_sb_len = max_sense_len; -} - -/* Setup for data transfer from device */ -void -set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp, - int dxfer_len) -{ - struct sg_pt_linux_scsi * ptp = &vp->impl; - - if (ptp->io_hdr.dxferp) - ++ptp->in_err; - if (dxfer_len > 0) { - ptp->io_hdr.dxferp = dxferp; - ptp->io_hdr.dxfer_len = dxfer_len; - ptp->io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - } -} - -/* Setup for data transfer toward device */ -void -set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp, - int dxfer_len) -{ - struct sg_pt_linux_scsi * ptp = &vp->impl; - - if (ptp->io_hdr.dxferp) - ++ptp->in_err; - if (dxfer_len > 0) { - ptp->io_hdr.dxferp = (unsigned char *)dxferp; - ptp->io_hdr.dxfer_len = dxfer_len; - ptp->io_hdr.dxfer_direction = SG_DXFER_TO_DEV; - } -} - -void -set_scsi_pt_packet_id(struct sg_pt_base * vp, int pack_id) -{ - struct sg_pt_linux_scsi * ptp = &vp->impl; - - ptp->io_hdr.pack_id = pack_id; -} - -void -set_scsi_pt_tag(struct sg_pt_base * vp, uint64_t tag) -{ - struct sg_pt_linux_scsi * ptp = &vp->impl; - - ++ptp->in_err; - if (tag) { ; } /* unused, suppress warning */ -} - -/* Note that task management function codes are transport specific */ -void -set_scsi_pt_task_management(struct sg_pt_base * vp, int tmf_code) -{ - struct sg_pt_linux_scsi * ptp = &vp->impl; - - ++ptp->in_err; - if (tmf_code) { ; } /* unused, suppress warning */ -} - -void -set_scsi_pt_task_attr(struct sg_pt_base * vp, int attribute, int priority) -{ - struct sg_pt_linux_scsi * ptp = &vp->impl; - - ++ptp->in_err; - if (attribute) { ; } /* unused, suppress warning */ - if (priority) { ; } /* unused, suppress warning */ -} - -#ifndef SG_FLAG_Q_AT_TAIL -#define SG_FLAG_Q_AT_TAIL 0x10 -#endif -#ifndef SG_FLAG_Q_AT_HEAD -#define SG_FLAG_Q_AT_HEAD 0x20 -#endif - -void -set_scsi_pt_flags(struct sg_pt_base * vp, int flags) -{ - struct sg_pt_linux_scsi * ptp = &vp->impl; - - /* default action of sg driver [sg v3 interface] is QUEUE_AT_HEAD */ - /* default action of block layer SG_IO ioctl is QUEUE_AT_TAIL */ - if (SCSI_PT_FLAGS_QUEUE_AT_HEAD & flags) { /* favour AT_HEAD */ - ptp->io_hdr.flags |= SG_FLAG_Q_AT_HEAD; - ptp->io_hdr.flags &= ~SG_FLAG_Q_AT_TAIL; - } else if (SCSI_PT_FLAGS_QUEUE_AT_TAIL & flags) { - ptp->io_hdr.flags |= SG_FLAG_Q_AT_TAIL; - ptp->io_hdr.flags &= ~SG_FLAG_Q_AT_HEAD; - } -} - -/* Executes SCSI command (or at least forwards it to lower layers). - * Clears os_err field prior to active call (whose result may set it - * again). */ -int -do_scsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose) -{ - struct sg_pt_linux_scsi * ptp = &vp->impl; - - 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 (NULL == ptp->io_hdr.cmdp) { - if (verbose) - pr2ws("No SCSI command (cdb) given\n"); - return SCSI_PT_DO_BAD_PARAMS; - } - /* io_hdr.timeout is in milliseconds */ - ptp->io_hdr.timeout = ((time_secs > 0) ? (time_secs * 1000) : - DEF_TIMEOUT); - if (ptp->io_hdr.sbp && (ptp->io_hdr.mx_sb_len > 0)) - memset(ptp->io_hdr.sbp, 0, ptp->io_hdr.mx_sb_len); - if (ioctl(fd, SG_IO, &ptp->io_hdr) < 0) { - ptp->os_err = errno; - if (verbose > 1) - pr2ws("ioctl(SG_IO) failed: %s (errno=%d)\n", - strerror(ptp->os_err), ptp->os_err); - return -ptp->os_err; - } - return 0; -} - -int -get_scsi_pt_result_category(const struct sg_pt_base * vp) -{ - const struct sg_pt_linux_scsi * ptp = &vp->impl; - int dr_st = ptp->io_hdr.driver_status & SG_LIB_DRIVER_MASK; - int scsi_st = ptp->io_hdr.status & 0x7e; - - if (ptp->os_err) - return SCSI_PT_RESULT_OS_ERR; - else if (ptp->io_hdr.host_status) - return SCSI_PT_RESULT_TRANSPORT_ERR; - else if (dr_st && (SG_LIB_DRIVER_SENSE != dr_st)) - return SCSI_PT_RESULT_TRANSPORT_ERR; - else if ((SG_LIB_DRIVER_SENSE == dr_st) || - (SAM_STAT_CHECK_CONDITION == scsi_st) || - (SAM_STAT_COMMAND_TERMINATED == scsi_st)) - return SCSI_PT_RESULT_SENSE; - else if (scsi_st) - return SCSI_PT_RESULT_STATUS; - else - return SCSI_PT_RESULT_GOOD; -} - -int -get_scsi_pt_resid(const struct sg_pt_base * vp) -{ - const struct sg_pt_linux_scsi * ptp = &vp->impl; - - return ptp->io_hdr.resid; -} - -int -get_scsi_pt_status_response(const struct sg_pt_base * vp) -{ - const struct sg_pt_linux_scsi * ptp = &vp->impl; - - return ptp->io_hdr.status; -} - -int -get_scsi_pt_sense_len(const struct sg_pt_base * vp) -{ - const struct sg_pt_linux_scsi * ptp = &vp->impl; - - return ptp->io_hdr.sb_len_wr; -} - -int -get_scsi_pt_duration_ms(const struct sg_pt_base * vp) -{ - const struct sg_pt_linux_scsi * ptp = &vp->impl; - - return ptp->io_hdr.duration; -} - -int -get_scsi_pt_transport_err(const struct sg_pt_base * vp) -{ - const struct sg_pt_linux_scsi * ptp = &vp->impl; - - return (ptp->io_hdr.host_status << 8) + ptp->io_hdr.driver_status; -} - -int -get_scsi_pt_os_err(const struct sg_pt_base * vp) -{ - const struct sg_pt_linux_scsi * ptp = &vp->impl; - - return ptp->os_err; -} - -/* Returns b which will contain a null char terminated string (if - * max_b_len > 0). That string should decode Linux driver and host - * status values. */ -char * -get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len, - char * b) -{ - const struct sg_pt_linux_scsi * ptp = &vp->impl; - int ds = ptp->io_hdr.driver_status; - int hs = ptp->io_hdr.host_status; - int n, m; - char * cp = b; - int driv; - const char * driv_cp = "unknown"; - - if (max_b_len < 1) - return b; - m = max_b_len; - n = 0; - if (hs) { - if ((hs < 0) || (hs >= LINUX_HOST_BYTES_SZ)) - n = snprintf(cp, m, "Host_status=0x%02x is unknown\n", hs); - else - n = snprintf(cp, m, "Host_status=0x%02x [%s]\n", hs, - linux_host_bytes[hs]); - } - m -= n; - if (m < 1) { - b[max_b_len - 1] = '\0'; - return b; - } - cp += n; - driv = ds & SG_LIB_DRIVER_MASK; - if (driv < LINUX_DRIVER_BYTES_SZ) - driv_cp = linux_driver_bytes[driv]; -#if 0 - sugg = (ds & SG_LIB_SUGGEST_MASK) >> 4; - if (sugg < LINUX_DRIVER_SUGGESTS_SZ) - sugg_cp = linux_driver_suggests[sugg]; -#endif - n = snprintf(cp, m, "Driver_status=0x%02x [%s]\n", ds, driv_cp); - m -= n; - if (m < 1) - b[max_b_len - 1] = '\0'; - return b; -} - -char * -get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b) -{ - const struct sg_pt_linux_scsi * ptp = &vp->impl; - const char * cp; - - cp = safe_strerror(ptp->os_err); - strncpy(b, cp, max_b_len); - if ((int)strlen(cp) >= max_b_len) - b[max_b_len - 1] = '\0'; - return b; -} - - -// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< -#else /* allow for runtime selection of sg v3 or v4 (via bsg) */ -// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< -/* - * So bsg is an option. Thus we make a runtime decision. If all the following - * are true we use sg v4 which is only currently supported on bsg device - * nodes: - * a) there is a bsg entry in the /proc/devices file - * b) the device node given to scsi_pt_open() is a char device - * c) the char major number of the device node given to scsi_pt_open() - * matches the char major number of the bsg entry in /proc/devices - * Otherwise the sg v3 interface is used. - * - * Note that in either case we prepare the data in a sg v4 structure. If - * the runtime tests indicate that the v3 interface is needed then - * do_scsi_pt_v3() transfers the input data into a v3 structure and - * then the output data is transferred back into a sg v4 structure. - * That implementation detail could change in the future. - * - * [20120806] Only use MAJOR() macro in kdev_t.h if that header file is - * available and major() macro [N.B. lower case] is not available. - */ - - -#include -#include - -#ifdef major -#define SG_DEV_MAJOR major -#else -#ifdef HAVE_LINUX_KDEV_T_H -#include -#endif -#define SG_DEV_MAJOR MAJOR /* MAJOR() macro faulty if > 255 minors */ -#endif - - -struct sg_pt_linux_scsi { - struct sg_io_v4 io_hdr; /* use v4 header as it is more general */ - int in_err; - int os_err; - unsigned char tmf_request[4]; -}; +#define SG_LIB_DRIVER_SENSE DRIVER_SENSE -struct sg_pt_base { - struct sg_pt_linux_scsi impl; -}; +bool sg_bsg_nvme_char_major_checked = false; +int sg_bsg_major = 0; +volatile int sg_nvme_char_major = 0; -static int bsg_major_checked = 0; -static int bsg_major = 0; +bool sg_checked_version_num = false; +int sg_driver_version_num = 0; +bool sg_duration_set_nano = false; +long sg_lin_page_size = 4096; /* default, overridden with correct value */ -static void -find_bsg_major(int verbose) +/* 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). */ +void +sg_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]; - char * cp; - int n; + sg_lin_page_size = sysconf(_SC_PAGESIZE); if (NULL == (fp = fopen(proc_devices, "r"))) { if (verbose) pr2ws("fopen %s failed: %s\n", proc_devices, strerror(errno)); @@ -568,32 +147,187 @@ 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; + sg_bsg_major = n; + if (got_one) + break; + got_one = true; + } else if (0 == strcmp("nvme", a)) { + sg_nvme_char_major = n; + if (got_one) + break; + got_one = true; } } 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); + if (cp) { + if (sg_bsg_major > 0) + pr2ws("found sg_bsg_major=%d\n", sg_bsg_major); + if (sg_nvme_char_major > 0) + pr2ws("found sg_nvme_char_major=%d\n", sg_nvme_char_major); + } else + pr2ws("found no bsg not nvme char device in %s\n", proc_devices); } fclose(fp); } +/* Assumes that sg_find_bsg_nvme_char_major() has already been called. Returns + * true if dev_fd is a scsi generic pass-through device. If yields + * *is_nvme_p = true with *nsid_p = 0 then dev_fd is a NVMe char device. + * If yields *nsid_p > 0 then dev_fd is a NVMe block device. */ +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 (sg_bsg_major == major_num) + is_bsg = true; + else if (sg_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 (! sg_bsg_nvme_char_major_checked) { + sg_bsg_nvme_char_major_checked = true; + sg_find_bsg_nvme_char_major(verbose); + } + if (dev_fd >= 0) { + bool is_sg, is_bsg, is_nvme; + int err; + uint32_t nsid; + struct stat a_stat; -/* Returns >= 0 if successful. If error in Unix returns negated errno. */ -int -scsi_pt_open_device(const char * device_name, int read_only, int verbose) -{ - int oflags = O_NONBLOCK; - - oflags |= (read_only ? O_RDONLY : O_RDWR); - return scsi_pt_open_flags(device_name, oflags, verbose); + 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; } +/* + * We make a runtime decision whether to use the sg v3 interface or the sg + * v4 interface (currently exclusively used by the bsg driver). If all the + * following are true we use sg v4 which is only currently supported on bsg + * device nodes: + * a) there is a bsg entry in the /proc/devices file + * b) the device node given to scsi_pt_open() is a char device + * c) the char major number of the device node given to scsi_pt_open() + * matches the char major number of the bsg entry in /proc/devices + * Otherwise the sg v3 interface is used. + * + * Note that in either case we prepare the data in a sg v4 structure. If + * the runtime tests indicate that the v3 interface is needed then + * do_scsi_pt_v3() transfers the input data into a v3 structure and + * then the output data is transferred back into a sg v4 structure. + * That implementation detail could change in the future. + * + * [20120806] Only use MAJOR() macro in kdev_t.h if that header file is + * available and major() macro [N.B. lower case] is not available. + */ + + +#ifdef major +#define SG_DEV_MAJOR major +#else +#ifdef HAVE_LINUX_KDEV_T_H +#include +#endif +#define SG_DEV_MAJOR MAJOR /* MAJOR() macro faulty if > 255 minors */ +#endif + + /* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed */ /* together. The 'flags' argument is advisory and may be ignored. */ /* Returns >= 0 if successful, otherwise returns negated errno. */ @@ -602,18 +336,33 @@ { int fd; - if (! bsg_major_checked) { - bsg_major_checked = 1; - find_bsg_major(verbose); + if (! sg_bsg_nvme_char_major_checked) { + sg_bsg_nvme_char_major_checked = true; + sg_find_bsg_nvme_char_major(verbose); } - if (verbose > 1) + 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; } +/* Returns >= 0 if successful. If error in Unix returns negated errno. */ +int +scsi_pt_open_device(const char * device_name, bool read_only, int verbose) +{ + int oflags = O_NONBLOCK; + + oflags |= (read_only ? O_RDONLY : O_RDWR); + return scsi_pt_open_flags(device_name, oflags, verbose); +} + /* Returns 0 if successful. If error in Unix returns negated errno. */ int scsi_pt_close_device(int device_fd) @@ -626,41 +375,90 @@ return res; } +#if (HAVE_NVME && (! IGNORE_NVME)) +static bool checked_ev_dsense = false; +static bool ev_dsense = false; +#endif + +/* 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; ptp = (struct sg_pt_linux_scsi *) calloc(1, sizeof(struct sg_pt_linux_scsi)); if (ptp) { - ptp->io_hdr.guard = 'Q'; +#if (HAVE_NVME && (! IGNORE_NVME)) + sntl_init_dev_stat(&ptp->dev_stat); + if (! checked_ev_dsense) { + ev_dsense = sg_get_initial_dsense(); + checked_ev_dsense = true; + } + ptp->dev_stat.scsi_dsense = ev_dsense; +#endif + 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) { - struct sg_pt_linux_scsi * ptp = &vp->impl; - if (ptp) - free(ptp); + if (NULL == vp) + pr2ws(">>>>>>> Warning: %s called with NULL pointer\n", __func__); + else { + struct sg_pt_linux_scsi * ptp = &vp->impl; + + if (ptp->free_nvme_id_ctlp) { + free(ptp->free_nvme_id_ctlp); + ptp->free_nvme_id_ctlp = NULL; + ptp->nvme_id_ctlp = NULL; + } + if (ptp) + free(ptp); + } } +/* Remembers previous device file descriptor */ void clear_scsi_pt_obj(struct sg_pt_base * vp) { + bool is_sg, is_bsg, is_nvme; + int fd; + uint32_t nvme_nsid; + struct sg_sntl_dev_state_t dev_stat; 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; + dev_stat = ptp->dev_stat; + if (ptp->free_nvme_id_ctlp) + free(ptp->free_nvme_id_ctlp); memset(ptp, 0, sizeof(struct sg_pt_linux_scsi)); ptp->io_hdr.guard = 'Q'; #ifdef BSG_PROTOCOL_SCSI @@ -669,62 +467,251 @@ #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_our_sntl = false; + ptp->nvme_nsid = nvme_nsid; + ptp->dev_stat = dev_stat; + } +} + +void +partial_clear_scsi_pt_obj(struct sg_pt_base * vp) +{ + struct sg_pt_linux_scsi * ptp = &vp->impl; + + if (NULL == ptp) + return; + ptp->in_err = 0; + ptp->os_err = 0; + ptp->io_hdr.device_status = 0; + ptp->io_hdr.transport_status = 0; + ptp->io_hdr.driver_status = 0; + ptp->io_hdr.din_xferp = 0; + ptp->io_hdr.din_xfer_len = 0; + ptp->io_hdr.dout_xferp = 0; + ptp->io_hdr.dout_xfer_len = 0; + ptp->nvme_result = 0; +} + +#ifndef SG_SET_GET_EXTENDED + +/* If both sei_wr_mask and sei_rd_mask are 0, this ioctl does nothing */ +struct sg_extended_info { + uint32_t sei_wr_mask; /* OR-ed SG_SEIM_* user->driver values */ + uint32_t sei_rd_mask; /* OR-ed SG_SEIM_* driver->user values */ + uint32_t ctl_flags_wr_mask; /* OR-ed SG_CTL_FLAGM_* values */ + uint32_t ctl_flags_rd_mask; /* OR-ed SG_CTL_FLAGM_* values */ + uint32_t ctl_flags; /* bit values OR-ed, see SG_CTL_FLAGM_* */ + uint32_t read_value; /* write SG_SEIRV_*, read back related */ + + uint32_t reserved_sz; /* data/sgl size of pre-allocated request */ + uint32_t tot_fd_thresh; /* total data/sgat for this fd, 0: no limit */ + uint32_t minor_index; /* rd: kernel's sg device minor number */ + uint32_t share_fd; /* SHARE_FD and CHG_SHARE_FD use this */ + uint32_t sgat_elem_sz; /* sgat element size (must be power of 2) */ + uint8_t pad_to_96[52]; /* pad so struct is 96 bytes long */ +}; + +#define SG_IOCTL_MAGIC_NUM 0x22 + +#define SG_SET_GET_EXTENDED _IOWR(SG_IOCTL_MAGIC_NUM, 0x51, \ + struct sg_extended_info) + +#define SG_SEIM_CTL_FLAGS 0x1 + +#define SG_CTL_FLAGM_TIME_IN_NS 0x1 + +#endif + +/* 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 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 (! sg_bsg_nvme_char_major_checked) { + sg_bsg_nvme_char_major_checked = true; + sg_find_bsg_nvme_char_major(verbose); + } + ptp->dev_fd = dev_fd; + if (dev_fd >= 0) { + ptp->is_sg = check_file_type(dev_fd, &a_stat, &ptp->is_bsg, + &ptp->is_nvme, &ptp->nvme_nsid, + &ptp->os_err, verbose); + if (ptp->is_sg && (! sg_checked_version_num)) { + if (ioctl(dev_fd, SG_GET_VERSION_NUM, &ptp->sg_version) < 0) { + ptp->sg_version = 0; + if (verbose > 3) + pr2ws("%s: ioctl(SG_GET_VERSION_NUM) failed: errno: %d " + "[%s]\n", __func__, errno, safe_strerror(errno)); + } else { /* got version number */ + sg_driver_version_num = ptp->sg_version; + sg_checked_version_num = true; + } + if (verbose > 4) { + int ver = ptp->sg_version; + + if (ptp->sg_version >= SG_LINUX_SG_VER_V4_BASE) { +#ifdef IGNORE_LINUX_SGV4 + pr2ws("%s: sg driver version %d.%02d.%02d but config " + "override back to v3\n", __func__, ver / 10000, + (ver / 100) % 100, ver % 100); +#else + pr2ws("%s: sg driver version %d.%02d.%02d so choose v4\n", + __func__, ver / 10000, (ver / 100) % 100, + ver % 100); +#endif + } else if (verbose > 5) + pr2ws("%s: sg driver version %d.%02d.%02d so choose v3\n", + __func__, ver / 10000, (ver / 100) % 100, + ver % 100); + } + } else if (ptp->is_sg) + ptp->sg_version = sg_driver_version_num; + + if (ptp->is_sg && (ptp->sg_version >= SG_LINUX_SG_VER_V4_FULL) && + getenv("SG3_UTILS_LINUX_NANO")) { + struct sg_extended_info sei; + struct sg_extended_info * seip = &sei; + + memset(seip, 0, sizeof(*seip)); + /* try to override default of milliseconds */ + seip->sei_wr_mask |= SG_SEIM_CTL_FLAGS; + seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_TIME_IN_NS; + seip->ctl_flags |= SG_CTL_FLAGM_TIME_IN_NS; + if (ioctl(dev_fd, SG_SET_GET_EXTENDED, seip) < 0) { + if (verbose > 2) + pr2ws("%s: unable to override milli --> nanoseconds: " + "%s\n", __func__, safe_strerror(errno)); + } else { + if (! sg_duration_set_nano) + sg_duration_set_nano = true; + if (verbose > 5) + pr2ws("%s: dev_fd=%d, succeeding in setting durations " + "to nanoseconds\n", __func__, dev_fd); + } + } else if (ptp->is_sg && (ptp->sg_version >= SG_LINUX_SG_VER_V4_BASE) + && getenv("SG3_UTILS_LINUX_NANO")) { + if (verbose > 2) + pr2ws("%s: dev_fd=%d, ignored SG3_UTILS_LINUX_NANO\nbecause " + "base version sg version 4 driver\n", __func__, dev_fd); + } + } else { + ptp->is_sg = false; + ptp->is_bsg = false; + ptp->is_nvme = false; + ptp->nvme_our_sntl = false; + ptp->nvme_nsid = 0; + ptp->os_err = 0; } + return ptp->os_err; +} + +int +sg_linux_get_sg_version(const struct sg_pt_base * vp) +{ + const struct sg_pt_linux_scsi * ptp = &vp->impl; + + return ptp->sg_version; +} + +/* 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, +set_scsi_pt_cdb(struct sg_pt_base * vp, const uint8_t * cdb, int cdb_len) { struct sg_pt_linux_scsi * ptp = &vp->impl; - 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_uintptr_t)cdb; ptp->io_hdr.request_len = cdb_len; } +int +get_scsi_pt_cdb_len(const struct sg_pt_base * vp) +{ + const struct sg_pt_linux_scsi * ptp = &vp->impl; + + return ptp->io_hdr.request_len; +} + +uint8_t * +get_scsi_pt_cdb_buf(const struct sg_pt_base * vp) +{ + const struct sg_pt_linux_scsi * ptp = &vp->impl; + + return (uint8_t *)(sg_uintptr_t)ptp->io_hdr.request; +} + void -set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense, +set_scsi_pt_sense(struct sg_pt_base * vp, uint8_t * sense, int max_sense_len) { struct sg_pt_linux_scsi * ptp = &vp->impl; - if (ptp->io_hdr.response) - ++ptp->in_err; - memset(sense, 0, max_sense_len); - ptp->io_hdr.response = (__u64)(long)sense; + if (sense) { + if (max_sense_len > 0) + memset(sense, 0, max_sense_len); + } + ptp->io_hdr.response = (__u64)(sg_uintptr_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) +set_scsi_pt_data_in(struct sg_pt_base * vp, uint8_t * dxferp, + 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_uintptr_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) +set_scsi_pt_data_out(struct sg_pt_base * vp, const uint8_t * dxferp, + int dxfer_olen) { struct sg_pt_linux_scsi * ptp = &vp->impl; if (ptp->io_hdr.dout_xferp) ++ptp->in_err; + if (dxfer_olen > 0) { + ptp->io_hdr.dout_xferp = (__u64)(sg_uintptr_t)dxferp; + ptp->io_hdr.dout_xfer_len = dxfer_olen; + } +} + +void +set_pt_metadata_xfer(struct sg_pt_base * vp, uint8_t * dxferp, + uint32_t dxfer_len, bool out_true) +{ + struct sg_pt_linux_scsi * ptp = &vp->impl; + if (dxfer_len > 0) { - ptp->io_hdr.dout_xferp = (__u64)(long)dxferp; - ptp->io_hdr.dout_xfer_len = dxfer_len; + ptp->mdxferp = dxferp; + ptp->mdxfer_len = dxfer_len; + ptp->mdxfer_out = out_true; } } @@ -733,7 +720,7 @@ { struct sg_pt_linux_scsi * ptp = &vp->impl; - ptp->io_hdr.spare_in = pack_id; + ptp->io_hdr.request_extra = pack_id; /* was placed in spare_in */ } void @@ -751,8 +738,8 @@ struct sg_pt_linux_scsi * ptp = &vp->impl; 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->tmf_request[0] = (uint8_t)tmf_code; /* assume it fits */ + ptp->io_hdr.request = (__u64)(sg_uintptr_t)(&(ptp->tmf_request[0])); ptp->io_hdr.request_len = 1; } @@ -796,21 +783,89 @@ } } +/* If supported it is the number of bytes requested to transfer less the + * number actually transferred. This it typically important for data-in + * transfers. For data-out (only) transfers, the 'dout_req_len - + * dout_act_len' is returned. For bidi transfer the "din" residual is + * returned. */ /* N.B. Returns din_resid and ignores dout_resid */ int get_scsi_pt_resid(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; + if ((NULL == ptp) || (ptp->is_nvme && ! ptp->nvme_our_sntl)) + return 0; + else if ((ptp->io_hdr.din_xfer_len > 0) && + (ptp->io_hdr.dout_xfer_len > 0)) + return ptp->io_hdr.din_resid; + else if (ptp->io_hdr.dout_xfer_len > 0) + return ptp->io_hdr.dout_resid; return ptp->io_hdr.din_resid; } +void +get_pt_req_lengths(const struct sg_pt_base * vp, int * req_dinp, + int * req_doutp) +{ + const struct sg_pt_linux_scsi * ptp = &vp->impl; + + if (req_dinp) { + if (ptp->io_hdr.din_xfer_len > 0) + *req_dinp = ptp->io_hdr.din_xfer_len; + else + *req_dinp = 0; + } + if (req_doutp) { + if (ptp->io_hdr.dout_xfer_len > 0) + *req_doutp = ptp->io_hdr.dout_xfer_len; + else + *req_doutp = 0; + } +} + +void +get_pt_actual_lengths(const struct sg_pt_base * vp, int * act_dinp, + int * act_doutp) +{ + const struct sg_pt_linux_scsi * ptp = &vp->impl; + + if (act_dinp) { + if (ptp->io_hdr.din_xfer_len > 0) { + int res = ptp->io_hdr.din_xfer_len - ptp->io_hdr.din_resid; + + *act_dinp = (res > 0) ? res : 0; + } else + *act_dinp = 0; + } + if (act_doutp) { + if (ptp->io_hdr.dout_xfer_len > 0) + *act_doutp = ptp->io_hdr.dout_xfer_len - ptp->io_hdr.dout_resid; + else + *act_doutp = 0; + } +} + int get_scsi_pt_status_response(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; - return ptp->io_hdr.device_status; + if (NULL == ptp) + return 0; + return (int)((ptp->is_nvme && ! ptp->nvme_our_sntl) ? + ptp->nvme_status : ptp->io_hdr.device_status); +} + +uint32_t +get_pt_result(const struct sg_pt_base * vp) +{ + const struct sg_pt_linux_scsi * ptp = &vp->impl; + + if (NULL == ptp) + return 0; + return (ptp->is_nvme && ! ptp->nvme_our_sntl) ? + ptp->nvme_result : ptp->io_hdr.device_status; } int @@ -821,12 +876,31 @@ return ptp->io_hdr.response_len; } +uint8_t * +get_scsi_pt_sense_buf(const struct sg_pt_base * vp) +{ + const struct sg_pt_linux_scsi * ptp = &vp->impl; + + return (uint8_t *)(sg_uintptr_t)ptp->io_hdr.response; +} + int get_scsi_pt_duration_ms(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; - return ptp->io_hdr.duration; + return sg_duration_set_nano ? (ptp->io_hdr.duration / 1000) : + ptp->io_hdr.duration; +} + +/* If not available return 0 otherwise return number of nanoseconds that the + * lower layers (and hardware) took to execute the command just completed. */ +uint64_t +get_pt_duration_ns(const struct sg_pt_base * vp) +{ + const struct sg_pt_linux_scsi * ptp = &vp->impl; + + return sg_duration_set_nano ? (uint32_t)ptp->io_hdr.duration : 0; } int @@ -837,6 +911,14 @@ return ptp->io_hdr.transport_status; } +void +set_scsi_pt_transport_err(struct sg_pt_base * vp, int err) +{ + struct sg_pt_linux_scsi * ptp = &vp->impl; + + ptp->io_hdr.transport_status = err; +} + /* Returns b which will contain a null char terminated string (if * max_b_len > 0). Combined driver and transport (called "host" in Linux * kernel) statuses */ @@ -857,7 +939,7 @@ m = max_b_len; n = 0; if (hs) { - if ((hs < 0) || (hs >= LINUX_HOST_BYTES_SZ)) + if ((hs < 0) || (hs >= (int)SG_ARRAY_SIZE(linux_host_bytes))) n = snprintf(cp, m, "Host_status=0x%02x is invalid\n", hs); else n = snprintf(cp, m, "Host_status=0x%02x [%s]\n", hs, @@ -870,11 +952,11 @@ } cp += n; driv = ds & SG_LIB_DRIVER_MASK; - if (driv < LINUX_DRIVER_BYTES_SZ) + if (driv < (int)SG_ARRAY_SIZE(linux_driver_bytes)) driv_cp = linux_driver_bytes[driv]; #if 0 sugg = (ds & SG_LIB_SUGGEST_MASK) >> 4; - if (sugg < LINUX_DRIVER_SUGGESTS_SZ) + if (sugg < SG_ARRAY_SIZE(linux_driver_suggests) sugg_cp = linux_driver_suggests[sugg]; #endif n = snprintf(cp, m, "Driver_status=0x%02x [%s]\n", ds, driv_cp); @@ -928,6 +1010,25 @@ return b; } +bool +pt_device_is_nvme(const struct sg_pt_base * vp) +{ + const struct sg_pt_linux_scsi * ptp = &vp->impl; + + return ptp->is_nvme; +} + +/* If a NVMe block device (which includes the NSID) handle is associated + * with 'vp', then its NSID is returned (values range from 0x1 to + * 0xffffffe). Otherwise 0 is returned. */ +uint32_t +get_pt_nvme_nsid(const struct sg_pt_base * vp) +{ + const struct sg_pt_linux_scsi * ptp = &vp->impl; + + return ptp->nvme_nsid; +} + /* Executes SCSI command using sg v3 interface */ static int do_scsi_pt_v3(struct sg_pt_linux_scsi * ptp, int fd, int time_secs, @@ -939,8 +1040,8 @@ /* convert v4 to v3 header */ v3_hdr.interface_id = 'S'; v3_hdr.dxfer_direction = SG_DXFER_NONE; - v3_hdr.cmdp = (unsigned char *)(long)ptp->io_hdr.request; - v3_hdr.cmd_len = (unsigned char)ptp->io_hdr.request_len; + v3_hdr.cmdp = (uint8_t *)(sg_uintptr_t)ptp->io_hdr.request; + v3_hdr.cmd_len = (uint8_t)ptp->io_hdr.request_len; if (ptp->io_hdr.din_xfer_len > 0) { if (ptp->io_hdr.dout_xfer_len > 0) { if (verbose) @@ -956,10 +1057,10 @@ v3_hdr.dxfer_direction = SG_DXFER_TO_DEV; } if (ptp->io_hdr.response && (ptp->io_hdr.max_response_len > 0)) { - v3_hdr.sbp = (unsigned char *)(long)ptp->io_hdr.response; - v3_hdr.mx_sb_len = (unsigned char)ptp->io_hdr.max_response_len; + v3_hdr.sbp = (uint8_t *)(sg_uintptr_t)ptp->io_hdr.response; + v3_hdr.mx_sb_len = (uint8_t)ptp->io_hdr.max_response_len; } - v3_hdr.pack_id = (int)ptp->io_hdr.spare_in; + v3_hdr.pack_id = (int)ptp->io_hdr.request_extra; if (BSG_FLAG_Q_AT_HEAD & ptp->io_hdr.flags) v3_hdr.flags |= SG_FLAG_Q_AT_HEAD; /* favour AT_HEAD */ else if (BSG_FLAG_Q_AT_TAIL & ptp->io_hdr.flags) @@ -967,7 +1068,7 @@ if (NULL == v3_hdr.cmdp) { if (verbose) - pr2ws("No SCSI command (cdb) given\n"); + pr2ws("No SCSI command (cdb) given [v3]\n"); return SCSI_PT_DO_BAD_PARAMS; } /* io_hdr.timeout is in milliseconds, if greater than zero */ @@ -977,7 +1078,7 @@ 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; @@ -990,67 +1091,89 @@ return 0; } +/* Executes SCSI command using sg v4 interface */ +static int +do_scsi_pt_v4(struct sg_pt_linux_scsi * ptp, int fd, int time_secs, + int verbose) +{ + if (0 == ptp->io_hdr.request) { + if (verbose) + pr2ws("No SCSI command (cdb) given [v4]\n"); + return SCSI_PT_DO_BAD_PARAMS; + } + /* io_hdr.timeout is in milliseconds, if greater than zero */ + ptp->io_hdr.timeout = ((time_secs > 0) ? (time_secs * 1000) : DEF_TIMEOUT); + if (ioctl(fd, SG_IO, &ptp->io_hdr) < 0) { + ptp->os_err = errno; + if (verbose > 1) + pr2ws("ioctl(SG_IO v4) failed: %s (errno=%d)\n", + safe_strerror(ptp->os_err), ptp->os_err); + return -ptp->os_err; + } + 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 = 1; - find_bsg_major(verbose); + if (! sg_bsg_nvme_char_major_checked) { + sg_bsg_nvme_char_major_checked = true; + sg_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); - } - - if (! ptp->io_hdr.request) { + ptp->dev_fd = fd; + } else if (ptp->dev_fd < 0) { if (verbose) - pr2ws("No SCSI command (cdb) given (v4)\n"); + pr2ws("%s: invalid file descriptors\n", __func__); return SCSI_PT_DO_BAD_PARAMS; + } else + fd = ptp->dev_fd; + if (! have_checked_for_type) { + err = set_pt_file_handle(vp, ptp->dev_fd, verbose); + if (err) + return -ptp->os_err; } - /* io_hdr.timeout is in milliseconds */ - ptp->io_hdr.timeout = ((time_secs > 0) ? (time_secs * 1000) : - DEF_TIMEOUT); -#if 0 - /* sense buffer already zeroed */ - if (ptp->io_hdr.response && (ptp->io_hdr.max_response_len > 0)) { - void * p; - - p = (void *)(long)ptp->io_hdr.response; - memset(p, 0, ptp->io_hdr.max_response_len); - } -#endif - if (ioctl(fd, SG_IO, &ptp->io_hdr) < 0) { - ptp->os_err = errno; - if (verbose > 1) - pr2ws("ioctl(SG_IO v4) failed: %s (errno=%d)\n", - strerror(ptp->os_err), ptp->os_err); + if (ptp->os_err) return -ptp->os_err; - } + if (verbose > 5) + pr2ws("%s: is_nvme=%d, is_sg=%d, is_bsg=%d\n", __func__, + (int)ptp->is_nvme, (int)ptp->is_sg, (int)ptp->is_bsg); + if (ptp->is_nvme) + return sg_do_nvme_pt(vp, -1, time_secs, verbose); + else if (ptp->is_sg) { +#ifdef IGNORE_LINUX_SGV4 + return do_scsi_pt_v3(ptp, fd, time_secs, verbose); +#else + if (ptp->sg_version >= SG_LINUX_SG_VER_V4_BASE) + return do_scsi_pt_v4(ptp, fd, time_secs, verbose); + else + return do_scsi_pt_v3(ptp, fd, time_secs, verbose); +#endif + } else if (sg_bsg_major <= 0) + return do_scsi_pt_v3(ptp, fd, time_secs, verbose); + else if (ptp->is_bsg) + return do_scsi_pt_v4(ptp, fd, time_secs, verbose); + else + return do_scsi_pt_v3(ptp, fd, time_secs, verbose); + + pr2ws("%s: Should never reach this point\n", __func__); return 0; } - -#endif -// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< diff -Nru sdparm-1.10/lib/sg_pt_linux_nvme.c sdparm-1.12/lib/sg_pt_linux_nvme.c --- sdparm-1.10/lib/sg_pt_linux_nvme.c 1970-01-01 00:00:00.000000000 +0000 +++ sdparm-1.12/lib/sg_pt_linux_nvme.c 2021-01-04 06:17:04.000000000 +0000 @@ -0,0 +1,1932 @@ +/* + * Copyright (c) 2017-2021 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. + * + * SPDX-License-Identifier: BSD-2-Clause + * + * The code to use the NVMe Management Interface (MI) SES pass-through + * was provided by WDC in November 2017. + */ + +/* + * Copyright 2017, Western Digital Corporation + * + * Written by Berck Nash + * + * Use of this source code is governed by a BSD-style + * license that can be found in the BSD_LICENSE file. + * + * Based on the NVM-Express command line utility, which bore the following + * notice: + * + * Copyright (c) 2014-2015, Intel Corporation. + * + * Written by Keith Busch + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +/* sg_pt_linux_nvme version 1.15 20210102 */ + +/* This file contains a small "SPC-only" SNTL to support the SES pass-through + * of SEND DIAGNOSTIC and RECEIVE DIAGNOSTIC RESULTS through NVME-MI + * SES Send and SES Receive. */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define __STDC_FORMAT_MACROS 1 +#include +#include +#include +#include /* to define 'major' */ +#ifndef major +#include +#endif + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "sg_pt.h" +#include "sg_lib.h" +#include "sg_linux_inc.h" +#include "sg_pt_linux.h" +#include "sg_unaligned.h" +#include "sg_pr2serr.h" + +#define SCSI_INQUIRY_OPC 0x12 +#define SCSI_REPORT_LUNS_OPC 0xa0 +#define SCSI_TEST_UNIT_READY_OPC 0x0 +#define SCSI_REQUEST_SENSE_OPC 0x3 +#define SCSI_SEND_DIAGNOSTIC_OPC 0x1d +#define SCSI_RECEIVE_DIAGNOSTIC_OPC 0x1c +#define SCSI_MAINT_IN_OPC 0xa3 +#define SCSI_READ10_OPC 0x28 +#define SCSI_READ16_OPC 0x88 +#define SCSI_REP_SUP_OPCS_OPC 0xc +#define SCSI_REP_SUP_TMFS_OPC 0xd +#define SCSI_MODE_SENSE10_OPC 0x5a +#define SCSI_MODE_SELECT10_OPC 0x55 +#define SCSI_READ_CAPACITY10_OPC 0x25 +#define SCSI_START_STOP_OPC 0x1b +#define SCSI_SYNC_CACHE10_OPC 0x35 +#define SCSI_SYNC_CACHE16_OPC 0x91 +#define SCSI_VERIFY10_OPC 0x2f +#define SCSI_VERIFY16_OPC 0x8f +#define SCSI_WRITE10_OPC 0x2a +#define SCSI_WRITE16_OPC 0x8a +#define SCSI_WRITE_SAME10_OPC 0x41 +#define SCSI_WRITE_SAME16_OPC 0x93 +#define SCSI_SERVICE_ACT_IN_OPC 0x9e +#define SCSI_READ_CAPACITY16_SA 0x10 +#define SCSI_SA_MSK 0x1f + +/* Additional Sense Code (ASC) */ +#define NO_ADDITIONAL_SENSE 0x0 +#define LOGICAL_UNIT_NOT_READY 0x4 +#define LOGICAL_UNIT_COMMUNICATION_FAILURE 0x8 +#define UNRECOVERED_READ_ERR 0x11 +#define PARAMETER_LIST_LENGTH_ERR 0x1a +#define INVALID_OPCODE 0x20 +#define LBA_OUT_OF_RANGE 0x21 +#define INVALID_FIELD_IN_CDB 0x24 +#define INVALID_FIELD_IN_PARAM_LIST 0x26 +#define UA_RESET_ASC 0x29 +#define UA_CHANGED_ASC 0x2a +#define TARGET_CHANGED_ASC 0x3f +#define LUNS_CHANGED_ASCQ 0x0e +#define INSUFF_RES_ASC 0x55 +#define INSUFF_RES_ASCQ 0x3 +#define LOW_POWER_COND_ON_ASC 0x5e /* ASCQ=0 */ +#define POWER_ON_RESET_ASCQ 0x0 +#define BUS_RESET_ASCQ 0x2 /* scsi bus reset occurred */ +#define MODE_CHANGED_ASCQ 0x1 /* mode parameters changed */ +#define CAPACITY_CHANGED_ASCQ 0x9 +#define SAVING_PARAMS_UNSUP 0x39 +#define TRANSPORT_PROBLEM 0x4b +#define THRESHOLD_EXCEEDED 0x5d +#define LOW_POWER_COND_ON 0x5e +#define MISCOMPARE_VERIFY_ASC 0x1d +#define MICROCODE_CHANGED_ASCQ 0x1 /* with TARGET_CHANGED_ASC */ +#define MICROCODE_CHANGED_WO_RESET_ASCQ 0x16 +#define PCIE_ERR_ASC 0x4b +#define PCIE_UNSUPP_REQ_ASCQ 0x13 + +/* NVMe Admin commands */ +#define SG_NVME_AD_GET_FEATURE 0xa +#define SG_NVME_AD_SET_FEATURE 0x9 +#define SG_NVME_AD_IDENTIFY 0x6 /* similar to SCSI INQUIRY */ +#define SG_NVME_AD_MI_RECEIVE 0x1e /* MI: Management Interface */ +#define SG_NVME_AD_MI_SEND 0x1d /* hmmm, same opcode as SEND DIAG */ + +/* NVMe NVM (Non-Volatile Memory) commands */ +#define SG_NVME_NVM_FLUSH 0x0 /* SCSI SYNCHRONIZE CACHE */ +#define SG_NVME_NVM_COMPARE 0x5 /* SCSI VERIFY(BYTCHK=1) */ +#define SG_NVME_NVM_READ 0x2 +#define SG_NVME_NVM_VERIFY 0xc /* SCSI VERIFY(BYTCHK=0) */ +#define SG_NVME_NVM_WRITE 0x1 +#define SG_NVME_NVM_WRITE_ZEROES 0x8 /* SCSI WRITE SAME */ + +#define SG_NVME_NVM_CDW12_FUA (1 << 30) /* Force Unit Access bit */ + + +#if (HAVE_NVME && (! IGNORE_NVME)) + +/* This trims given NVMe block device name in Linux (e.g. /dev/nvme0n1p5) + * to the name of its associated char device (e.g. /dev/nvme0). If this + * occurs true is returned and the char device name is placed in 'b' (as + * long as b_len is sufficient). Otherwise false is returned. */ +bool +sg_get_nvme_char_devname(const char * nvme_block_devname, uint32_t b_len, + char * b) +{ + uint32_t n, tlen; + const char * cp; + char buff[8]; + + if ((NULL == b) || (b_len < 5)) + return false; /* degenerate cases */ + cp = strstr(nvme_block_devname, "nvme"); + if (NULL == cp) + return false; /* expected to find "nvme" in given name */ + if (1 != sscanf(cp, "nvme%u", &n)) + return false; /* didn't find valid "nvme" */ + snprintf(buff, sizeof(buff), "%u", n); + tlen = (cp - nvme_block_devname) + 4 + strlen(buff); + if ((tlen + 1) > b_len) + return false; /* b isn't long enough to fit output */ + memcpy(b, nvme_block_devname, tlen); + b[tlen] = '\0'; + return true; +} + +static void +mk_sense_asc_ascq(struct sg_pt_linux_scsi * ptp, int sk, int asc, int ascq, + int vb) +{ + bool dsense = !! ptp->dev_stat.scsi_dsense; + int n; + uint8_t * sbp = (uint8_t *)(sg_uintptr_t)ptp->io_hdr.response; + + ptp->io_hdr.device_status = SAM_STAT_CHECK_CONDITION; + n = ptp->io_hdr.max_response_len; + if ((n < 8) || ((! dsense) && (n < 14))) { + if (vb) + pr2ws("%s: max_response_len=%d too short, want 14 or more\n", + __func__, n); + return; + } else + ptp->io_hdr.response_len = dsense ? n : ((n < 18) ? n : 18); + memset(sbp, 0, n); + sg_build_sense_buffer(dsense, sbp, sk, asc, ascq); + if (vb > 3) + pr2ws("%s: [sense_key,asc,ascq]: [0x%x,0x%x,0x%x]\n", __func__, sk, + asc, ascq); +} + +static void +mk_sense_from_nvme_status(struct sg_pt_linux_scsi * ptp, int vb) +{ + bool ok; + bool dsense = !! ptp->dev_stat.scsi_dsense; + int n; + uint8_t sstatus, sk, asc, ascq; + uint8_t * sbp = (uint8_t *)(sg_uintptr_t)ptp->io_hdr.response; + + ok = sg_nvme_status2scsi(ptp->nvme_status, &sstatus, &sk, &asc, &ascq); + if (! ok) { /* can't find a mapping to a SCSI error, so ... */ + sstatus = SAM_STAT_CHECK_CONDITION; + sk = SPC_SK_ILLEGAL_REQUEST; + asc = 0xb; + ascq = 0x0; /* asc: "WARNING" purposely vague */ + } + + ptp->io_hdr.device_status = sstatus; + n = ptp->io_hdr.max_response_len; + if ((n < 8) || ((! dsense) && (n < 14))) { + pr2ws("%s: sense_len=%d too short, want 14 or more\n", __func__, n); + return; + } else + ptp->io_hdr.response_len = dsense ? n : ((n < 18) ? n : 18); + memset(sbp, 0, n); + sg_build_sense_buffer(dsense, sbp, sk, asc, ascq); + if (dsense && (ptp->nvme_status > 0)) + sg_nvme_desc2sense(sbp, ptp->nvme_stat_dnr, ptp->nvme_stat_more, + ptp->nvme_status); + if (vb > 3) + pr2ws("%s: [status, sense_key,asc,ascq]: [0x%x, 0x%x,0x%x,0x%x]\n", + __func__, sstatus, sk, asc, ascq); +} + +/* Set in_bit to -1 to indicate no bit position of invalid field */ +static void +mk_sense_invalid_fld(struct sg_pt_linux_scsi * ptp, bool in_cdb, int in_byte, + int in_bit, int vb) +{ + bool dsense = !! ptp->dev_stat.scsi_dsense; + int sl, asc, n; + uint8_t * sbp = (uint8_t *)(sg_uintptr_t)ptp->io_hdr.response; + uint8_t sks[4]; + + ptp->io_hdr.device_status = SAM_STAT_CHECK_CONDITION; + asc = in_cdb ? INVALID_FIELD_IN_CDB : INVALID_FIELD_IN_PARAM_LIST; + n = ptp->io_hdr.max_response_len; + if ((n < 8) || ((! dsense) && (n < 14))) { + if (vb) + pr2ws("%s: max_response_len=%d too short, want 14 or more\n", + __func__, n); + return; + } else + ptp->io_hdr.response_len = dsense ? n : ((n < 18) ? n : 18); + + memset(sbp, 0, n); + sg_build_sense_buffer(dsense, sbp, SPC_SK_ILLEGAL_REQUEST, asc, 0); + memset(sks, 0, sizeof(sks)); + sks[0] = 0x80; + if (in_cdb) + sks[0] |= 0x40; + if (in_bit >= 0) { + sks[0] |= 0x8; + sks[0] |= (0x7 & in_bit); + } + sg_put_unaligned_be16(in_byte, sks + 1); + if (dsense) { + sl = sbp[7] + 8; + sbp[7] = sl; + sbp[sl] = 0x2; + sbp[sl + 1] = 0x6; + memcpy(sbp + sl + 4, sks, 3); + } else + memcpy(sbp + 15, sks, 3); + if (vb > 3) + pr2ws("%s: [sense_key,asc,ascq]: [0x5,0x%x,0x0] %c byte=%d, bit=%d\n", + __func__, asc, in_cdb ? 'C' : 'D', in_byte, + ((in_bit > 0) ? (0x7 & in_bit) : 0)); +} + +/* Returns 0 for success. Returns SG_LIB_NVME_STATUS if there is non-zero + * NVMe status (from the completion queue) with the value placed in + * ptp->nvme_status. If Unix error from ioctl then return negated value + * (equivalent -errno from basic Unix system functions like open()). + * CDW0 from the completion queue is placed in ptp->nvme_result in the + * absence of a Unix error. If time_secs is negative it is treated as + * a timeout in milliseconds (of abs(time_secs) ). */ +static int +sg_nvme_admin_cmd(struct sg_pt_linux_scsi * ptp, + struct sg_nvme_passthru_cmd *cmdp, void * dp, bool is_read, + int time_secs, int vb) +{ + const uint32_t cmd_len = sizeof(struct sg_nvme_passthru_cmd); + int res; + uint32_t n; + uint16_t sct_sc; + const uint8_t * up = ((const uint8_t *)cmdp) + SG_NVME_PT_OPCODE; + char nam[64]; + + if (vb) + sg_get_nvme_opcode_name(*up, true /* ADMIN */, sizeof(nam), nam); + else + nam[0] = '\0'; + cmdp->timeout_ms = (time_secs < 0) ? (-time_secs) : (1000 * time_secs); + ptp->os_err = 0; + if (vb > 2) { + pr2ws("NVMe Admin command: %s\n", nam); + hex2stderr((const uint8_t *)cmdp, cmd_len, 1); + if ((vb > 3) && (! is_read) && dp) { + uint32_t len = sg_get_unaligned_le32(up + SG_NVME_PT_DATA_LEN); + + if (len > 0) { + n = len; + if ((len < 512) || (vb > 5)) + pr2ws("\nData-out buffer (%u bytes):\n", n); + else { + pr2ws("\nData-out buffer (first 512 of %u bytes):\n", n); + n = 512; + } + hex2stderr((const uint8_t *)dp, n, 0); + } + } + } + res = ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, cmdp); + if (res < 0) { /* OS error (errno negated) */ + ptp->os_err = -res; + if (vb > 1) { + pr2ws("%s: ioctl for %s [0x%x] failed: %s " + "(errno=%d)\n", __func__, nam, *up, strerror(-res), -res); + } + return res; + } + + /* Now res contains NVMe completion queue CDW3 31:17 (15 bits) */ + ptp->nvme_result = cmdp->result; + if ((! ptp->nvme_our_sntl) && ptp->io_hdr.response && + (ptp->io_hdr.max_response_len > 3)) { + /* build 32 byte "sense" buffer */ + uint8_t * sbp = (uint8_t *)(sg_uintptr_t)ptp->io_hdr.response; + uint16_t st = (uint16_t)res; + + n = ptp->io_hdr.max_response_len; + n = (n < 32) ? n : 32; + memset(sbp, 0 , n); + ptp->io_hdr.response_len = n; + sg_put_unaligned_le32(cmdp->result, + sbp + SG_NVME_PT_CQ_RESULT); + if (n > 15) /* LSBit will be 0 (Phase bit) after (st << 1) */ + sg_put_unaligned_le16(st << 1, sbp + SG_NVME_PT_CQ_STATUS_P); + } + /* clear upper bits (DNR and More) leaving ((SCT << 8) | SC) */ + sct_sc = 0x7ff & res; /* 11 bits */ + ptp->nvme_status = sct_sc; + ptp->nvme_stat_dnr = !!(0x4000 & res); + ptp->nvme_stat_more = !!(0x2000 & res); + if (sct_sc) { /* when non-zero, treat as command error */ + if (vb > 1) { + char b[80]; + + pr2ws("%s: ioctl for %s [0x%x] failed, status: %s [0x%x]\n", + __func__, nam, *up, + sg_get_nvme_cmd_status_str(sct_sc, sizeof(b), b), sct_sc); + } + return SG_LIB_NVME_STATUS; /* == SCSI_PT_DO_NVME_STATUS */ + } + if ((vb > 3) && is_read && dp) { + uint32_t len = sg_get_unaligned_le32(up + SG_NVME_PT_DATA_LEN); + + if (len > 0) { + n = len; + if ((len < 1024) || (vb > 5)) + pr2ws("\nData-in buffer (%u bytes):\n", n); + else { + pr2ws("\nData-in buffer (first 1024 of %u bytes):\n", n); + n = 1024; + } + hex2stderr((const uint8_t *)dp, n, 0); + } + } + return 0; +} + +/* see NVME MI document, NVMSR is NVM Subsystem Report */ +static void +sntl_check_enclosure_override(struct sg_pt_linux_scsi * ptp, int vb) +{ + uint8_t * up = ptp->nvme_id_ctlp; + uint8_t nvmsr; + + if (NULL == up) + return; + nvmsr = up[253]; + if (vb > 3) + pr2ws("%s: enter, nvmsr=%u\n", __func__, nvmsr); + ptp->dev_stat.id_ctl253 = nvmsr; + switch (ptp->dev_stat.enclosure_override) { + case 0x0: /* no override */ + if (0x3 & nvmsr) { + ptp->dev_stat.pdt = PDT_DISK; + ptp->dev_stat.enc_serv = 1; + } else if (0x2 & nvmsr) { + ptp->dev_stat.pdt = PDT_SES; + ptp->dev_stat.enc_serv = 1; + } else if (0x1 & nvmsr) { + ptp->dev_stat.pdt = PDT_DISK; + ptp->dev_stat.enc_serv = 0; + } else { + uint32_t nn = sg_get_unaligned_le32(up + 516); + + ptp->dev_stat.pdt = nn ? PDT_DISK : PDT_UNKNOWN; + ptp->dev_stat.enc_serv = 0; + } + break; + case 0x1: /* override to SES device */ + ptp->dev_stat.pdt = PDT_SES; + ptp->dev_stat.enc_serv = 1; + break; + case 0x2: /* override to disk with attached SES device */ + ptp->dev_stat.pdt = PDT_DISK; + ptp->dev_stat.enc_serv = 1; + break; + case 0x3: /* override to SAFTE device (PDT_PROCESSOR) */ + ptp->dev_stat.pdt = PDT_PROCESSOR; + ptp->dev_stat.enc_serv = 1; + break; + case 0xff: /* override to normal disk */ + ptp->dev_stat.pdt = PDT_DISK; + ptp->dev_stat.enc_serv = 0; + break; + default: + pr2ws("%s: unknown enclosure_override value: %d\n", __func__, + ptp->dev_stat.enclosure_override); + break; + } +} + +static int +sntl_do_identify(struct sg_pt_linux_scsi * ptp, int cns, int nsid, + int time_secs, int u_len, uint8_t * up, int vb) +{ + struct sg_nvme_passthru_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.opcode = SG_NVME_AD_IDENTIFY; + cmd.nsid = nsid; + cmd.cdw10 = cns; + cmd.addr = (uint64_t)(sg_uintptr_t)up; + cmd.data_len = u_len; + return sg_nvme_admin_cmd(ptp, &cmd, up, true, time_secs, vb); +} + +/* Currently only caches associated identify controller response (4096 bytes). + * Returns 0 on success; otherwise a positive value is returned */ +static int +sntl_cache_identify(struct sg_pt_linux_scsi * ptp, int time_secs, int vb) +{ + int ret; + uint32_t pg_sz = sg_get_page_size(); + uint8_t * up; + + up = sg_memalign(pg_sz, pg_sz, &ptp->free_nvme_id_ctlp, false); + ptp->nvme_id_ctlp = up; + if (NULL == up) { + pr2ws("%s: sg_memalign() failed to get memory\n", __func__); + return sg_convert_errno(ENOMEM); + } + ret = sntl_do_identify(ptp, 0x1 /* CNS */, 0 /* nsid */, time_secs, + pg_sz, up, vb); + if (0 == ret) + sntl_check_enclosure_override(ptp, vb); + return (ret < 0) ? sg_convert_errno(-ret) : ret; +} + +/* If nsid==0 then set cmdp->nsid to SG_NVME_BROADCAST_NSID. */ +static int +sntl_get_features(struct sg_pt_linux_scsi * ptp, int feature_id, int select, + uint32_t nsid, uint64_t din_addr, int time_secs, int vb) +{ + int res; + struct sg_nvme_passthru_cmd cmd; + struct sg_nvme_passthru_cmd * cmdp = &cmd; + + if (vb > 4) + pr2ws("%s: feature_id=0x%x, select=%d\n", __func__, feature_id, + select); + memset(cmdp, 0, sizeof(*cmdp)); + cmdp->opcode = SG_NVME_AD_GET_FEATURE; + cmdp->nsid = nsid ? nsid : SG_NVME_BROADCAST_NSID; + select &= 0x7; + feature_id &= 0xff; + cmdp->cdw10 = (select << 8) | feature_id; + if (din_addr) + cmdp->addr = din_addr; + cmdp->timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs); + res = sg_nvme_admin_cmd(ptp, cmdp, NULL, false, time_secs, vb); + if (res) + return res; + ptp->os_err = 0; + ptp->nvme_status = 0; + return 0; +} + +static const char * nvme_scsi_vendor_str = "NVMe "; +static const uint16_t inq_resp_len = 36; + +static int +sntl_inq(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs, + int vb) +{ + bool evpd; + bool cp_id_ctl = false; + int res; + uint16_t n, alloc_len, pg_cd; + uint32_t pg_sz = sg_get_page_size(); + uint8_t * nvme_id_ns = NULL; + uint8_t * free_nvme_id_ns = NULL; + uint8_t inq_dout[256]; + + if (vb > 3) + pr2ws("%s: time_secs=%d\n", __func__, time_secs); + + if (0x2 & cdbp[1]) { /* Reject CmdDt=1 */ + mk_sense_invalid_fld(ptp, true, 1, 1, vb); + return 0; + } + if (NULL == ptp->nvme_id_ctlp) { + res = sntl_cache_identify(ptp, time_secs, vb); + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, vb); + return 0; + } else if (res) /* should be negative errno */ + return res; + } + memset(inq_dout, 0, sizeof(inq_dout)); + alloc_len = sg_get_unaligned_be16(cdbp + 3); + evpd = !!(0x1 & cdbp[1]); + pg_cd = cdbp[2]; + if (evpd) { /* VPD page responses */ + switch (pg_cd) { + case 0: + /* inq_dout[0] = (PQ=0)<<5 | (PDT=0); prefer pdt=0xd --> SES */ + inq_dout[1] = pg_cd; + n = 11; + sg_put_unaligned_be16(n - 4, inq_dout + 2); + inq_dout[4] = 0x0; + inq_dout[5] = 0x80; + inq_dout[6] = 0x83; + inq_dout[7] = 0x86; + inq_dout[8] = 0x87; + inq_dout[9] = 0x92; + inq_dout[n - 1] = SG_NVME_VPD_NICR; /* last VPD number */ + break; + case 0x80: + /* inq_dout[0] = (PQ=0)<<5 | (PDT=0); prefer pdt=0xd --> SES */ + inq_dout[1] = pg_cd; + n = 24; + sg_put_unaligned_be16(n - 4, inq_dout + 2); + memcpy(inq_dout + 4, ptp->nvme_id_ctlp + 4, 20); /* SN */ + break; + case 0x83: + if ((ptp->nvme_nsid > 0) && + (ptp->nvme_nsid < SG_NVME_BROADCAST_NSID)) { + nvme_id_ns = sg_memalign(pg_sz, pg_sz, &free_nvme_id_ns, + false); + if (nvme_id_ns) { + /* CNS=0x0 Identify namespace */ + res = sntl_do_identify(ptp, 0x0, ptp->nvme_nsid, + time_secs, pg_sz, nvme_id_ns, vb); + if (res) { + free(free_nvme_id_ns); + free_nvme_id_ns = NULL; + nvme_id_ns = NULL; + } + } + } + n = sg_make_vpd_devid_for_nvme(ptp->nvme_id_ctlp, nvme_id_ns, + 0 /* pdt */, -1 /*tproto */, + inq_dout, sizeof(inq_dout)); + if (n > 3) + sg_put_unaligned_be16(n - 4, inq_dout + 2); + if (free_nvme_id_ns) { + free(free_nvme_id_ns); + free_nvme_id_ns = NULL; + nvme_id_ns = NULL; + } + break; + case 0x86: /* Extended INQUIRY (per SFS SPC Discovery 2016) */ + inq_dout[1] = pg_cd; + n = 64; + sg_put_unaligned_be16(n - 4, inq_dout + 2); + inq_dout[5] = 0x1; /* SIMPSUP=1 */ + inq_dout[7] = 0x1; /* LUICLR=1 */ + inq_dout[13] = 0x40; /* max supported sense data length */ + break; + case 0x87: /* Mode page policy (per SFS SPC Discovery 2016) */ + inq_dout[1] = pg_cd; + n = 8; + sg_put_unaligned_be16(n - 4, inq_dout + 2); + inq_dout[4] = 0x3f; /* all mode pages */ + inq_dout[5] = 0xff; /* and their sub-pages */ + inq_dout[6] = 0x80; /* MLUS=1, policy=shared */ + break; + case 0x92: /* SCSI Feature set: only SPC Discovery 2016 */ + inq_dout[1] = pg_cd; + n = 10; + sg_put_unaligned_be16(n - 4, inq_dout + 2); + inq_dout[9] = 0x1; /* SFS SPC Discovery 2016 */ + break; + case SG_NVME_VPD_NICR: /* 0xde (vendor (sg3_utils) specific) */ + inq_dout[1] = pg_cd; + sg_put_unaligned_be16((16 + 4096) - 4, inq_dout + 2); + n = 16 + 4096; + cp_id_ctl = true; + break; + default: /* Point to page_code field in cdb */ + mk_sense_invalid_fld(ptp, true, 2, 7, vb); + return 0; + } + if (alloc_len > 0) { + n = (alloc_len < n) ? alloc_len : n; + n = (n < ptp->io_hdr.din_xfer_len) ? n : ptp->io_hdr.din_xfer_len; + ptp->io_hdr.din_resid = ptp->io_hdr.din_xfer_len - n; + if (n > 0) { + uint8_t * dp = (uint8_t *)(sg_uintptr_t)ptp->io_hdr.din_xferp; + + if (cp_id_ctl) { + memcpy(dp, inq_dout, (n < 16 ? n : 16)); + if (n > 16) + memcpy(dp + 16, ptp->nvme_id_ctlp, n - 16); + } else + memcpy(dp, inq_dout, n); + } + } + } else { /* Standard INQUIRY response */ + /* pdt=0 --> disk; pdt=0xd --> SES; pdt=3 --> processor (safte) */ + inq_dout[0] = (0x1f & ptp->dev_stat.pdt); /* (PQ=0)<<5 */ + /* inq_dout[1] = (RMD=0)<<7 | (LU_CONG=0)<<6 | (HOT_PLUG=0)<<4; */ + inq_dout[2] = 6; /* version: SPC-4 */ + inq_dout[3] = 2; /* NORMACA=0, HISUP=0, response data format: 2 */ + inq_dout[4] = 31; /* so response length is (or could be) 36 bytes */ + inq_dout[6] = ptp->dev_stat.enc_serv ? 0x40 : 0; + inq_dout[7] = 0x2; /* CMDQUE=1 */ + memcpy(inq_dout + 8, nvme_scsi_vendor_str, 8); /* NVMe not Intel */ + memcpy(inq_dout + 16, ptp->nvme_id_ctlp + 24, 16); /* Prod <-- MN */ + memcpy(inq_dout + 32, ptp->nvme_id_ctlp + 64, 4); /* Rev <-- FR */ + if (alloc_len > 0) { + n = (alloc_len < inq_resp_len) ? alloc_len : inq_resp_len; + n = (n < ptp->io_hdr.din_xfer_len) ? n : ptp->io_hdr.din_xfer_len; + ptp->io_hdr.din_resid = ptp->io_hdr.din_xfer_len - n; + if (n > 0) + memcpy((uint8_t *)(sg_uintptr_t)ptp->io_hdr.din_xferp, + inq_dout, n); + } + } + return 0; +} + +static int +sntl_rluns(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs, + int vb) +{ + int res; + uint16_t sel_report; + uint32_t alloc_len, k, n, num, max_nsid; + uint8_t * rl_doutp; + uint8_t * up; + + if (vb > 3) + pr2ws("%s: time_secs=%d\n", __func__, time_secs); + + sel_report = cdbp[2]; + alloc_len = sg_get_unaligned_be32(cdbp + 6); + if (NULL == ptp->nvme_id_ctlp) { + res = sntl_cache_identify(ptp, time_secs, vb); + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, vb); + return 0; + } else if (res) + return res; + } + max_nsid = sg_get_unaligned_le32(ptp->nvme_id_ctlp + 516); + switch (sel_report) { + case 0: + case 2: + num = max_nsid; + break; + case 1: + case 0x10: + case 0x12: + num = 0; + break; + case 0x11: + num = (1 == ptp->nvme_nsid) ? max_nsid : 0; + break; + default: + if (vb > 1) + pr2ws("%s: bad select_report value: 0x%x\n", __func__, + sel_report); + mk_sense_invalid_fld(ptp, true, 2, 7, vb); + return 0; + } + rl_doutp = (uint8_t *)calloc(num + 1, 8); + if (NULL == rl_doutp) { + pr2ws("%s: calloc() failed to get memory\n", __func__); + return sg_convert_errno(ENOMEM); + } + for (k = 0, up = rl_doutp + 8; k < num; ++k, up += 8) + sg_put_unaligned_be16(k, up); + n = num * 8; + sg_put_unaligned_be32(n, rl_doutp); + n+= 8; + if (alloc_len > 0) { + n = (alloc_len < n) ? alloc_len : n; + n = (n < ptp->io_hdr.din_xfer_len) ? n : ptp->io_hdr.din_xfer_len; + ptp->io_hdr.din_resid = ptp->io_hdr.din_xfer_len - n; + if (n > 0) + memcpy((uint8_t *)(sg_uintptr_t)ptp->io_hdr.din_xferp, rl_doutp, + n); + } + res = 0; + free(rl_doutp); + return res; +} + +static int +sntl_tur(struct sg_pt_linux_scsi * ptp, int time_secs, int vb) +{ + int res; + uint32_t pow_state; + + if (vb > 4) + pr2ws("%s: start\n", __func__); + if (NULL == ptp->nvme_id_ctlp) { + res = sntl_cache_identify(ptp, time_secs, vb); + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, vb); + return 0; + } else if (res) + return res; + } + res = sntl_get_features(ptp, 2 /* Power Management */, 0 /* current */, + 0, 0, time_secs, vb); + if (0 != res) { + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, vb); + return 0; + } else + return res; + } + pow_state = (0x1f & ptp->nvme_result); + if (vb > 3) + pr2ws("%s: pow_state=%u\n", __func__, pow_state); +#if 0 /* pow_state bounces around too much on laptop */ + if (pow_state) + mk_sense_asc_ascq(ptp, SPC_SK_NOT_READY, LOW_POWER_COND_ON_ASC, 0, + vb); +#endif + return 0; +} + +static int +sntl_req_sense(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) +{ + bool desc; + int res; + uint32_t pow_state, alloc_len, n; + uint8_t rs_dout[64]; + + if (vb > 3) + pr2ws("%s: time_secs=%d\n", __func__, time_secs); + if (NULL == ptp->nvme_id_ctlp) { + res = sntl_cache_identify(ptp, time_secs, vb); + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, vb); + return 0; + } else if (res) + return res; + } + desc = !!(0x1 & cdbp[1]); + alloc_len = cdbp[4]; + res = sntl_get_features(ptp, 0x2 /* Power Management */, 0 /* current */, + 0, 0, time_secs, vb); + if (0 != res) { + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, vb); + return 0; + } else + return res; + } + ptp->io_hdr.response_len = 0; + pow_state = (0x1f & ptp->nvme_result); + if (vb > 3) + pr2ws("%s: pow_state=%u\n", __func__, pow_state); + memset(rs_dout, 0, sizeof(rs_dout)); + if (pow_state) + sg_build_sense_buffer(desc, rs_dout, SPC_SK_NO_SENSE, + LOW_POWER_COND_ON_ASC, 0); + else + sg_build_sense_buffer(desc, rs_dout, SPC_SK_NO_SENSE, + NO_ADDITIONAL_SENSE, 0); + n = desc ? 8 : 18; + n = (n < alloc_len) ? n : alloc_len; + n = (n < ptp->io_hdr.din_xfer_len) ? n : ptp->io_hdr.din_xfer_len; + ptp->io_hdr.din_resid = ptp->io_hdr.din_xfer_len - n; + if (n > 0) + memcpy((uint8_t *)(sg_uintptr_t)ptp->io_hdr.din_xferp, rs_dout, n); + return 0; +} + +static uint8_t pc_t10_2_select[] = {0, 3, 1, 2}; + +/* For MODE SENSE(10) and MODE SELECT(10). 6 byte variants not supported */ +static int +sntl_mode_ss(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) +{ + bool is_msense = (SCSI_MODE_SENSE10_OPC == cdbp[0]); + int res, n, len; + uint8_t * bp; + struct sg_sntl_result_t sntl_result; + + if (vb > 3) + pr2ws("%s: mode se%s\n", __func__, (is_msense ? "nse" : "lect")); + if (NULL == ptp->nvme_id_ctlp) { + res = sntl_cache_identify(ptp, time_secs, vb); + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, vb); + return 0; + } else if (res) + return res; + } + if (is_msense) { /* MODE SENSE(10) */ + uint8_t pc_t10 = (cdbp[2] >> 6) & 0x3; + int mp_t10 = (cdbp[2] & 0x3f); + + if ((0x3f == mp_t10) || (0x8 /* caching mpage */ == mp_t10)) { + /* 0x6 is "Volatile write cache" feature id */ + res = sntl_get_features(ptp, 0x6, pc_t10_2_select[pc_t10], 0, + 0, time_secs, vb); + if (0 != res) { + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, vb); + return 0; + } else + return res; + } + ptp->dev_stat.wce = !!(0x1 & ptp->nvme_result); + } + len = ptp->io_hdr.din_xfer_len; + bp = (uint8_t *)(sg_uintptr_t)ptp->io_hdr.din_xferp; + n = sntl_resp_mode_sense10(&ptp->dev_stat, cdbp, bp, len, + &sntl_result); + ptp->io_hdr.din_resid = (n >= 0) ? len - n : len; + } else { /* MODE SELECT(10) */ + bool sp = !!(0x1 & cdbp[1]); /* Save Page indication */ + uint8_t pre_enc_ov = ptp->dev_stat.enclosure_override; + + len = ptp->io_hdr.dout_xfer_len; + bp = (uint8_t *)(sg_uintptr_t)ptp->io_hdr.dout_xferp; + ptp->dev_stat.wce_changed = false; + n = sntl_resp_mode_select10(&ptp->dev_stat, cdbp, bp, len, + &sntl_result); + if (ptp->dev_stat.wce_changed) { + uint32_t nsid = ptp->nvme_nsid; + struct sg_nvme_passthru_cmd cmd; + struct sg_nvme_passthru_cmd * cmdp = &cmd; + + ptp->dev_stat.wce_changed = false; + memset(cmdp, 0, sizeof(*cmdp)); + cmdp->opcode = SG_NVME_AD_SET_FEATURE; + cmdp->nsid = nsid ? nsid : SG_NVME_BROADCAST_NSID; + cmdp->cdw10 = 0x6; /* "Volatile write cache" feature id */ + if (sp) + cmdp->cdw10 |= (1 << 31); + cmdp->cdw11 = (uint32_t)ptp->dev_stat.wce; + cmdp->timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs); + res = sg_nvme_admin_cmd(ptp, cmdp, NULL, false, time_secs, vb); + if (0 != res) { + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, vb); + return 0; + } else + return res; + } + ptp->os_err = 0; + ptp->nvme_status = 0; + } + if (pre_enc_ov != ptp->dev_stat.enclosure_override) + sntl_check_enclosure_override(ptp, vb); /* ENC_OV has changed */ + } + if (n < 0) { + int in_bit = (255 == sntl_result.in_bit) ? (int)sntl_result.in_bit : + -1; + if ((SAM_STAT_CHECK_CONDITION == sntl_result.sstatus) && + (SPC_SK_ILLEGAL_REQUEST == sntl_result.sk)) { + if (INVALID_FIELD_IN_CDB == sntl_result.asc) + mk_sense_invalid_fld(ptp, true, sntl_result.in_byte, in_bit, + vb); + else if (INVALID_FIELD_IN_PARAM_LIST == sntl_result.asc) + mk_sense_invalid_fld(ptp, false, sntl_result.in_byte, in_bit, + vb); + else + mk_sense_asc_ascq(ptp, sntl_result.sk, sntl_result.asc, + sntl_result.ascq, vb); + } else + pr2ws("%s: error but no sense?? n=%d\n", __func__, n); + } + return 0; +} + +/* This is not really a SNTL. For SCSI SEND DIAGNOSTIC(PF=1) NVMe-MI + * has a special command (SES Send) to tunnel through pages to an + * enclosure. The NVMe enclosure is meant to understand the SES + * (SCSI Enclosure Services) use of diagnostics pages that are + * related to SES. */ +static int +sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) +{ + bool pf, self_test; + int res; + uint8_t st_cd, dpg_cd; + uint32_t alloc_len, n, dout_len, dpg_len, nvme_dst; + const uint32_t pg_sz = sg_get_page_size(); + uint8_t * dop; + struct sg_nvme_passthru_cmd cmd; + uint8_t * cmd_up = (uint8_t *)&cmd; + + st_cd = 0x7 & (cdbp[1] >> 5); + self_test = !! (0x4 & cdbp[1]); + pf = !! (0x10 & cdbp[1]); + if (vb > 3) + pr2ws("%s: pf=%d, self_test=%d (st_code=%d)\n", __func__, (int)pf, + (int)self_test, (int)st_cd); + if (self_test || st_cd) { + memset(cmd_up, 0, sizeof(cmd)); + cmd_up[SG_NVME_PT_OPCODE] = 0x14; /* Device self-test */ + /* just this namespace (if there is one) and controller */ + sg_put_unaligned_le32(ptp->nvme_nsid, cmd_up + SG_NVME_PT_NSID); + switch (st_cd) { + case 0: /* Here if self_test is set, do short self-test */ + case 1: /* Background short */ + case 5: /* Foreground short */ + nvme_dst = 1; + break; + case 2: /* Background extended */ + case 6: /* Foreground extended */ + nvme_dst = 2; + break; + case 4: /* Abort self-test */ + nvme_dst = 0xf; + break; + default: + pr2ws("%s: bad self-test code [0x%x]\n", __func__, st_cd); + mk_sense_invalid_fld(ptp, true, 1, 7, vb); + return 0; + } + sg_put_unaligned_le32(nvme_dst, cmd_up + SG_NVME_PT_CDW10); + res = sg_nvme_admin_cmd(ptp, &cmd, NULL, false, time_secs, vb); + if (0 != res) { + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, vb); + return 0; + } else + return res; + } + } + alloc_len = sg_get_unaligned_be16(cdbp + 3); /* parameter list length */ + dout_len = ptp->io_hdr.dout_xfer_len; + if (pf) { + if (0 == alloc_len) { + mk_sense_invalid_fld(ptp, true, 3, 7, vb); + if (vb) + pr2ws("%s: PF bit set bit param_list_len=0\n", __func__); + return 0; + } + } else { /* PF bit clear */ + if (alloc_len) { + mk_sense_invalid_fld(ptp, true, 3, 7, vb); + if (vb) + pr2ws("%s: param_list_len>0 but PF clear\n", __func__); + return 0; + } else + return 0; /* nothing to do */ + } + if (dout_len < 4) { + if (vb) + pr2ws("%s: dout length (%u bytes) too short\n", __func__, + dout_len); + return SCSI_PT_DO_BAD_PARAMS; + } + n = dout_len; + n = (n < alloc_len) ? n : alloc_len; + dop = (uint8_t *)(sg_uintptr_t)ptp->io_hdr.dout_xferp; + if (! sg_is_aligned(dop, pg_sz)) { /* is dop page aligned ? */ + if (vb) + pr2ws("%s: dout [0x%" PRIx64 "] not page aligned\n", __func__, + (uint64_t)ptp->io_hdr.dout_xferp); + return SCSI_PT_DO_BAD_PARAMS; + } + dpg_cd = dop[0]; + dpg_len = sg_get_unaligned_be16(dop + 2) + 4; + /* should we allow for more than one D_PG is dout ?? */ + n = (n < dpg_len) ? n : dpg_len; /* not yet ... */ + + if (vb) + pr2ws("%s: passing through d_pg=0x%x, len=%u to NVME_MI SES send\n", + __func__, dpg_cd, dpg_len); + memset(&cmd, 0, sizeof(cmd)); + cmd.opcode = SG_NVME_AD_MI_SEND; + cmd.addr = (uint64_t)(sg_uintptr_t)dop; + cmd.data_len = 0x1000; /* NVMe 4k page size. Maybe determine this? */ + /* dout_len > 0x1000, is this a problem?? */ + cmd.cdw10 = 0x0804; /* NVMe Message Header */ + cmd.cdw11 = 0x9; /* nvme_mi_ses_send; (0x8 -> mi_ses_recv) */ + cmd.cdw13 = n; + res = sg_nvme_admin_cmd(ptp, &cmd, dop, false, time_secs, vb); + if (0 != res) { + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, vb); + return 0; + } + } + return res; +} + +/* This is not really a SNTL. For SCSI RECEIVE DIAGNOSTIC RESULTS(PCV=1) + * NVMe-MI has a special command (SES Receive) to read pages through a + * tunnel from an enclosure. The NVMe enclosure is meant to understand the + * SES (SCSI Enclosure Services) use of diagnostics pages that are + * related to SES. */ +static int +sntl_recvdiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) +{ + bool pcv; + int res; + uint8_t dpg_cd; + uint32_t alloc_len, n, din_len; + uint32_t pg_sz = sg_get_page_size(); + uint8_t * dip; + struct sg_nvme_passthru_cmd cmd; + + pcv = !! (0x1 & cdbp[1]); + dpg_cd = cdbp[2]; + alloc_len = sg_get_unaligned_be16(cdbp + 3); /* parameter list length */ + if (vb > 3) + pr2ws("%s: dpg_cd=0x%x, pcv=%d, alloc_len=0x%x\n", __func__, + dpg_cd, (int)pcv, alloc_len); + din_len = ptp->io_hdr.din_xfer_len; + n = din_len; + n = (n < alloc_len) ? n : alloc_len; + dip = (uint8_t *)(sg_uintptr_t)ptp->io_hdr.din_xferp; + if (! sg_is_aligned(dip, pg_sz)) { + if (vb) + pr2ws("%s: din [0x%" PRIx64 "] not page aligned\n", __func__, + (uint64_t)ptp->io_hdr.din_xferp); + return SCSI_PT_DO_BAD_PARAMS; + } + + if (vb) + pr2ws("%s: expecting d_pg=0x%x from NVME_MI SES receive\n", __func__, + dpg_cd); + memset(&cmd, 0, sizeof(cmd)); + cmd.opcode = SG_NVME_AD_MI_RECEIVE; + cmd.addr = (uint64_t)(sg_uintptr_t)dip; + cmd.data_len = 0x1000; /* NVMe 4k page size. Maybe determine this? */ + /* din_len > 0x1000, is this a problem?? */ + cmd.cdw10 = 0x0804; /* NVMe Message Header */ + cmd.cdw11 = 0x8; /* nvme_mi_ses_receive */ + cmd.cdw12 = dpg_cd; + cmd.cdw13 = n; + res = sg_nvme_admin_cmd(ptp, &cmd, dip, true, time_secs, vb); + if (0 != res) { + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, vb); + return 0; + } else + return res; + } + ptp->io_hdr.din_resid = din_len - n; + return res; +} + +#define F_SA_LOW 0x80 /* cdb byte 1, bits 4 to 0 */ +#define F_SA_HIGH 0x100 /* as used by variable length cdbs */ +#define FF_SA (F_SA_HIGH | F_SA_LOW) +#define F_INV_OP 0x200 + +static int +sntl_rep_opcodes(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) +{ + bool rctd; + uint8_t reporting_opts, req_opcode, supp; + uint16_t req_sa, u; + uint32_t alloc_len, offset, a_len; + uint32_t pg_sz = sg_get_page_size(); + int k, len, count, bump; + const struct sg_opcode_info_t *oip; + uint8_t *arr; + uint8_t *free_arr; + + if (vb > 3) + pr2ws("%s: time_secs=%d\n", __func__, time_secs); + rctd = !!(cdbp[2] & 0x80); /* report command timeout desc. */ + reporting_opts = cdbp[2] & 0x7; + req_opcode = cdbp[3]; + req_sa = sg_get_unaligned_be16(cdbp + 4); + alloc_len = sg_get_unaligned_be32(cdbp + 6); + if (alloc_len < 4 || alloc_len > 0xffff) { + mk_sense_invalid_fld(ptp, true, 6, -1, vb); + return 0; + } + a_len = pg_sz - 72; + arr = sg_memalign(pg_sz, pg_sz, &free_arr, false); + if (NULL == arr) { + pr2ws("%s: calloc() failed to get memory\n", __func__); + return sg_convert_errno(ENOMEM); + } + switch (reporting_opts) { + case 0: /* all commands */ + count = 0; + bump = rctd ? 20 : 8; + for (offset = 4, oip = sg_get_opcode_translation(); + (oip->flags != 0xffff) && (offset < a_len); ++oip) { + if (F_INV_OP & oip->flags) + continue; + ++count; + arr[offset] = oip->opcode; + sg_put_unaligned_be16(oip->sa, arr + offset + 2); + if (rctd) + arr[offset + 5] |= 0x2; + if (FF_SA & oip->flags) + arr[offset + 5] |= 0x1; + sg_put_unaligned_be16(oip->len_mask[0], arr + offset + 6); + if (rctd) + sg_put_unaligned_be16(0xa, arr + offset + 8); + offset += bump; + } + sg_put_unaligned_be32(count * bump, arr + 0); + break; + case 1: /* one command: opcode only */ + case 2: /* one command: opcode plus service action */ + case 3: /* one command: if sa==0 then opcode only else opcode+sa */ + for (oip = sg_get_opcode_translation(); oip->flags != 0xffff; ++oip) { + if ((req_opcode == oip->opcode) && (req_sa == oip->sa)) + break; + } + if ((0xffff == oip->flags) || (F_INV_OP & oip->flags)) { + supp = 1; + offset = 4; + } else { + if (1 == reporting_opts) { + if (FF_SA & oip->flags) { + mk_sense_invalid_fld(ptp, true, 2, 2, vb); + free(free_arr); + return 0; + } + req_sa = 0; + } else if ((2 == reporting_opts) && 0 == (FF_SA & oip->flags)) { + mk_sense_invalid_fld(ptp, true, 4, -1, vb); + free(free_arr); + return 0; + } + if ((0 == (FF_SA & oip->flags)) && (req_opcode == oip->opcode)) + supp = 3; + else if (0 == (FF_SA & oip->flags)) + supp = 1; + else if (req_sa != oip->sa) + supp = 1; + else + supp = 3; + if (3 == supp) { + u = oip->len_mask[0]; + sg_put_unaligned_be16(u, arr + 2); + arr[4] = oip->opcode; + for (k = 1; k < u; ++k) + arr[4 + k] = (k < 16) ? + oip->len_mask[k] : 0xff; + offset = 4 + u; + } else + offset = 4; + } + arr[1] = (rctd ? 0x80 : 0) | supp; + if (rctd) { + sg_put_unaligned_be16(0xa, arr + offset); + offset += 12; + } + break; + default: + mk_sense_invalid_fld(ptp, true, 2, 2, vb); + free(free_arr); + return 0; + } + offset = (offset < a_len) ? offset : a_len; + len = (offset < alloc_len) ? offset : alloc_len; + ptp->io_hdr.din_resid = ptp->io_hdr.din_xfer_len - len; + if (len > 0) + memcpy((uint8_t *)(sg_uintptr_t)ptp->io_hdr.din_xferp, arr, len); + free(free_arr); + return 0; +} + +static int +sntl_rep_tmfs(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) +{ + bool repd; + uint32_t alloc_len, len; + uint8_t arr[16]; + + if (vb > 3) + pr2ws("%s: time_secs=%d\n", __func__, time_secs); + memset(arr, 0, sizeof(arr)); + repd = !!(cdbp[2] & 0x80); + alloc_len = sg_get_unaligned_be32(cdbp + 6); + if (alloc_len < 4) { + mk_sense_invalid_fld(ptp, true, 6, -1, vb); + return 0; + } + arr[0] = 0xc8; /* ATS | ATSS | LURS */ + arr[1] = 0x1; /* ITNRS */ + if (repd) { + arr[3] = 0xc; + len = 16; + } else + len = 4; + + len = (len < alloc_len) ? len : alloc_len; + ptp->io_hdr.din_resid = ptp->io_hdr.din_xfer_len - len; + if (len > 0) + memcpy((uint8_t *)(sg_uintptr_t)ptp->io_hdr.din_xferp, arr, len); + return 0; +} + +/* Note that the "Returned logical block address" (RLBA) field in the SCSI + * READ CAPACITY (10+16) command's response provides the address of the _last_ + * LBA (counting origin 0) which will be one less that the "size" in the + * NVMe Identify command response's NSZE field. One problem is that in + * some situations NSZE can be zero: temporarily set RLBA field to 0 + * (implying a 1 LB logical units size) pending further research. The LBLIB + * is the "Logical Block Length In Bytes" field in the RCAP response. */ +static int +sntl_readcap(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) +{ + bool is_rcap10 = (SCSI_READ_CAPACITY10_OPC == cdbp[0]); + int res, n, len, alloc_len, dps; + uint8_t flbas, index, lbads; /* NVMe: 2**LBADS --> Logical Block size */ + uint32_t lbafx; /* NVME: LBAF0...LBAF15, each 16 bytes */ + uint32_t pg_sz = sg_get_page_size(); + uint64_t nsze; + uint8_t * bp; + uint8_t * up; + uint8_t * free_up = NULL; + uint8_t resp[32]; + + if (vb > 3) + pr2ws("%s: RCAP%d, time_secs=%d\n", __func__, + (is_rcap10 ? 10 : 16), time_secs); + up = sg_memalign(pg_sz, pg_sz, &free_up, false); + if (NULL == up) { + pr2ws("%s: sg_memalign() failed to get memory\n", __func__); + return sg_convert_errno(ENOMEM); + } + res = sntl_do_identify(ptp, 0x0 /* CNS */, ptp->nvme_nsid, time_secs, + pg_sz, up, vb); + if (res < 0) { + res = sg_convert_errno(-res); + goto fini; + } + memset(resp, 0, sizeof(resp)); + nsze = sg_get_unaligned_le64(up + 0); + flbas = up[26]; /* NVME FLBAS field from Identify, want LBAF[flbas] */ + index = 128 + (4 * (flbas & 0xf)); + lbafx = sg_get_unaligned_le32(up + index); + lbads = (lbafx >> 16) & 0xff; /* bits 16 to 23 inclusive, pow2 */ + if (is_rcap10) { + alloc_len = 8; /* implicit, not in cdb */ + if (nsze > 0xffffffff) + sg_put_unaligned_be32(0xffffffff, resp + 0); + else if (0 == nsze) /* no good answer here */ + sg_put_unaligned_be32(0, resp + 0); /* SCSI RLBA field */ + else + sg_put_unaligned_be32((uint32_t)(nsze - 1), resp + 0); + sg_put_unaligned_be32(1 << lbads, resp + 4); /* SCSI LBLIB field */ + } else { + alloc_len = sg_get_unaligned_be32(cdbp + 10); + dps = up[29]; + if (0x7 & dps) { + resp[12] = 0x1; + n = (0x7 & dps) - 1; + if (n > 0) + resp[12] |= (n + n); + } + if (0 == nsze) /* no good answer here */ + sg_put_unaligned_be64(0, resp + 0); + else + sg_put_unaligned_be64(nsze - 1, resp + 0); + sg_put_unaligned_be32(1 << lbads, resp + 8); /* SCSI LBLIB field */ + } + len = ptp->io_hdr.din_xfer_len; + bp = (uint8_t *)(sg_uintptr_t)ptp->io_hdr.din_xferp; + n = 32; + n = (n < alloc_len) ? n : alloc_len; + n = (n < len) ? n : len; + ptp->io_hdr.din_resid = len - n; + if (n > 0) + memcpy(bp, resp, n); +fini: + if (free_up) + free(free_up); + return res; +} + +static int +do_nvm_pt_low(struct sg_pt_linux_scsi * ptp, + struct sg_nvme_passthru_cmd *cmdp, void * dp, int dlen, + bool is_read, int time_secs, int vb) +{ + const uint32_t cmd_len = sizeof(struct sg_nvme_passthru_cmd); + int res; + uint32_t n; + uint16_t sct_sc; + const uint8_t * up = ((const uint8_t *)cmdp) + SG_NVME_PT_OPCODE; + char nam[64]; + + if (vb) + sg_get_nvme_opcode_name(*up, false /* NVM */ , sizeof(nam), nam); + else + nam[0] = '\0'; + cmdp->timeout_ms = (time_secs < 0) ? (-time_secs) : (1000 * time_secs); + ptp->os_err = 0; + if (vb > 2) { + pr2ws("NVMe NVM command: %s\n", nam); + hex2stderr((const uint8_t *)cmdp, cmd_len, 1); + if ((vb > 3) && (! is_read) && dp) { + if (dlen > 0) { + n = dlen; + if ((dlen < 512) || (vb > 5)) + pr2ws("\nData-out buffer (%u bytes):\n", n); + else { + pr2ws("\nData-out buffer (first 512 of %u bytes):\n", n); + n = 512; + } + hex2stderr((const uint8_t *)dp, n, 0); + } + } + } + res = ioctl(ptp->dev_fd, NVME_IOCTL_IO_CMD, cmdp); + if (res < 0) { /* OS error (errno negated) */ + ptp->os_err = -res; + if (vb > 1) { + pr2ws("%s: ioctl for %s [0x%x] failed: %s " + "(errno=%d)\n", __func__, nam, *up, strerror(-res), -res); + } + return res; + } + + /* Now res contains NVMe completion queue CDW3 31:17 (15 bits) */ + ptp->nvme_result = cmdp->result; + if ((! ptp->nvme_our_sntl) && ptp->io_hdr.response && + (ptp->io_hdr.max_response_len > 3)) { + /* build 32 byte "sense" buffer */ + uint8_t * sbp = (uint8_t *)(sg_uintptr_t)ptp->io_hdr.response; + uint16_t st = (uint16_t)res; + + n = ptp->io_hdr.max_response_len; + n = (n < 32) ? n : 32; + memset(sbp, 0 , n); + ptp->io_hdr.response_len = n; + sg_put_unaligned_le32(cmdp->result, + sbp + SG_NVME_PT_CQ_RESULT); + if (n > 15) /* LSBit will be 0 (Phase bit) after (st << 1) */ + sg_put_unaligned_le16(st << 1, sbp + SG_NVME_PT_CQ_STATUS_P); + } + /* clear upper bits (DNR and More) leaving ((SCT << 8) | SC) */ + sct_sc = 0x7ff & res; /* 11 bits */ + ptp->nvme_status = sct_sc; + ptp->nvme_stat_dnr = !!(0x4000 & res); + ptp->nvme_stat_more = !!(0x2000 & res); + if (sct_sc) { /* when non-zero, treat as command error */ + if (vb > 1) { + char b[80]; + + pr2ws("%s: ioctl for %s [0x%x] failed, status: %s [0x%x]\n", + __func__, nam, *up, + sg_get_nvme_cmd_status_str(sct_sc, sizeof(b), b), sct_sc); + } + return SG_LIB_NVME_STATUS; /* == SCSI_PT_DO_NVME_STATUS */ + } + if ((vb > 3) && is_read && dp) { + if (dlen > 0) { + n = dlen; + if ((dlen < 1024) || (vb > 5)) + pr2ws("\nData-in buffer (%u bytes):\n", n); + else { + pr2ws("\nData-in buffer (first 1024 of %u bytes):\n", n); + n = 1024; + } + hex2stderr((const uint8_t *)dp, n, 0); + } + } + return 0; +} + +/* Since ptp can be a char device (e.g. /dev/nvme0) or a blocks device + * (e.g. /dev/nvme0n1 or /dev/nvme0n1p3) use NVME_IOCTL_IO_CMD which is + * common to both (and takes a timeout). The difficult is that + * NVME_IOCTL_IO_CMD takes a nvme_passthru_cmd object point. */ +static int +sntl_do_nvm_cmd(struct sg_pt_linux_scsi * ptp, struct sg_nvme_user_io * iop, + uint32_t dlen, bool is_read, int time_secs, int vb) +{ + + struct sg_nvme_passthru_cmd nvme_pt_cmd; + struct sg_nvme_passthru_cmd *cmdp = &nvme_pt_cmd; + void * dp = (void *)(sg_uintptr_t)iop->addr; + + memset(cmdp, 0, sizeof(*cmdp)); + cmdp->opcode = iop->opcode; + cmdp->flags = iop->flags; + cmdp->nsid = ptp->nvme_nsid; + cmdp->addr = iop->addr; + cmdp->data_len = dlen; + cmdp->cdw10 = iop->slba & 0xffffffff; + cmdp->cdw11 = (iop->slba >> 32) & 0xffffffff; + cmdp->cdw12 = iop->nblocks; /* lower 16 bits already "0's based" count */ + + return do_nvm_pt_low(ptp, cmdp, dp, dlen, is_read, time_secs, vb); +} + +static int +sntl_read(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) +{ + bool is_read10 = (SCSI_READ10_OPC == cdbp[0]); + bool have_fua = !!(cdbp[1] & 0x8); + int res; + int nblks_t10 = 0; + struct sg_nvme_user_io io; + struct sg_nvme_user_io * iop = &io; + + if (vb > 3) + pr2ws("%s: fua=%d, time_secs=%d\n", __func__, (int)have_fua, + time_secs); + memset(iop, 0, sizeof(*iop)); + iop->opcode = SG_NVME_NVM_READ; + if (is_read10) { + iop->slba = sg_get_unaligned_be32(cdbp + 2); + nblks_t10 = sg_get_unaligned_be16(cdbp + 7); + } else { + uint32_t num = sg_get_unaligned_be32(cdbp + 10); + + iop->slba = sg_get_unaligned_be64(cdbp + 2); + if (num > (UINT16_MAX + 1)) { + mk_sense_invalid_fld(ptp, true, 11, -1, vb); + return 0; + } else + nblks_t10 = num; + } + if (0 == nblks_t10) { /* NOP in SCSI */ + if (vb > 4) + pr2ws("%s: nblks_t10 is 0, a NOP in SCSI, can't map to NVMe\n", + __func__); + return 0; + } + iop->nblocks = nblks_t10 - 1; /* crazy "0's based" */ + if (have_fua) + iop->nblocks |= SG_NVME_NVM_CDW12_FUA; + iop->addr = (uint64_t)ptp->io_hdr.din_xferp; + res = sntl_do_nvm_cmd(ptp, iop, ptp->io_hdr.din_xfer_len, + true /* is_read */, time_secs, vb); + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, vb); + return 0; + } + return res; +} + +static int +sntl_write(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) +{ + bool is_write10 = (SCSI_WRITE10_OPC == cdbp[0]); + bool have_fua = !!(cdbp[1] & 0x8); + int res; + int nblks_t10 = 0; + struct sg_nvme_user_io io; + struct sg_nvme_user_io * iop = &io; + + if (vb > 3) + pr2ws("%s: fua=%d, time_secs=%d\n", __func__, (int)have_fua, + time_secs); + memset(iop, 0, sizeof(*iop)); + iop->opcode = SG_NVME_NVM_WRITE; + if (is_write10) { + iop->slba = sg_get_unaligned_be32(cdbp + 2); + nblks_t10 = sg_get_unaligned_be16(cdbp + 7); + } else { + uint32_t num = sg_get_unaligned_be32(cdbp + 10); + + iop->slba = sg_get_unaligned_be64(cdbp + 2); + if (num > (UINT16_MAX + 1)) { + mk_sense_invalid_fld(ptp, true, 11, -1, vb); + return 0; + } else + nblks_t10 = num; + } + if (0 == nblks_t10) { /* NOP in SCSI */ + if (vb > 4) + pr2ws("%s: nblks_t10 is 0, a NOP in SCSI, can't map to NVMe\n", + __func__); + return 0; + } + iop->nblocks = nblks_t10 - 1; + if (have_fua) + iop->nblocks |= SG_NVME_NVM_CDW12_FUA; + iop->addr = (uint64_t)ptp->io_hdr.dout_xferp; + res = sntl_do_nvm_cmd(ptp, iop, ptp->io_hdr.dout_xfer_len, false, + time_secs, vb); + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, vb); + return 0; + } + return res; +} + +static int +sntl_verify(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) +{ + bool is_verify10 = (SCSI_VERIFY10_OPC == cdbp[0]); + uint8_t bytchk = (cdbp[1] >> 1) & 0x3; + uint32_t dlen = 0; + int res; + int nblks_t10 = 0; + struct sg_nvme_user_io io; + struct sg_nvme_user_io * iop = &io; + + if (vb > 3) + pr2ws("%s: bytchk=%d, time_secs=%d\n", __func__, bytchk, time_secs); + if (bytchk > 1) { + mk_sense_invalid_fld(ptp, true, 1, 2, vb); + return 0; + } + memset(iop, 0, sizeof(*iop)); + iop->opcode = bytchk ? SG_NVME_NVM_COMPARE : SG_NVME_NVM_WRITE; + if (is_verify10) { + iop->slba = sg_get_unaligned_be32(cdbp + 2); + nblks_t10 = sg_get_unaligned_be16(cdbp + 7); + } else { + uint32_t num = sg_get_unaligned_be32(cdbp + 10); + + iop->slba = sg_get_unaligned_be64(cdbp + 2); + if (num > (UINT16_MAX + 1)) { + mk_sense_invalid_fld(ptp, true, 11, -1, vb); + return 0; + } else + nblks_t10 = num; + } + if (0 == nblks_t10) { /* NOP in SCSI */ + if (vb > 4) + pr2ws("%s: nblks_t10 is 0, a NOP in SCSI, can't map to NVMe\n", + __func__); + return 0; + } + iop->nblocks = nblks_t10 - 1; + if (bytchk) { + iop->addr = (uint64_t)ptp->io_hdr.dout_xferp; + dlen = ptp->io_hdr.dout_xfer_len; + } + res = sntl_do_nvm_cmd(ptp, iop, dlen, false, time_secs, vb); + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, vb); + return 0; + } + return res; +} + +static int +sntl_write_same(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) +{ + bool is_ws10 = (SCSI_WRITE_SAME10_OPC == cdbp[0]); + bool ndob = is_ws10 ? false : !!(0x1 & cdbp[1]); + int res; + int nblks_t10 = 0; + struct sg_nvme_user_io io; + struct sg_nvme_user_io * iop = &io; + + if (vb > 3) + pr2ws("%s: ndob=%d, time_secs=%d\n", __func__, (int)ndob, time_secs); + if (! ndob) { + int flbas, index, lbafx, lbads, lbsize; + uint8_t * up; + uint8_t * dp; + + dp = (uint8_t *)(sg_uintptr_t)ptp->io_hdr.dout_xferp; + if (dp == NULL) + return sg_convert_errno(ENOMEM); + if (NULL == ptp->nvme_id_ctlp) { + res = sntl_cache_identify(ptp, time_secs, vb); + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, vb); + return 0; + } else if (res) + return res; + } + up = ptp->nvme_id_ctlp; + flbas = up[26]; /* NVME FLBAS field from Identify */ + index = 128 + (4 * (flbas & 0xf)); + lbafx = sg_get_unaligned_le32(up + index); + lbads = (lbafx >> 16) & 0xff; /* bits 16 to 23 inclusive, pow2 */ + lbsize = 1 << lbads; + if (! sg_all_zeros(dp, lbsize)) { + mk_sense_asc_ascq(ptp, SPC_SK_ILLEGAL_REQUEST, PCIE_ERR_ASC, + PCIE_UNSUPP_REQ_ASCQ, vb); + return 0; + } + /* so given single LB full of zeros, can translate .... */ + } + memset(iop, 0, sizeof(*iop)); + iop->opcode = SG_NVME_NVM_WRITE_ZEROES; + if (is_ws10) { + iop->slba = sg_get_unaligned_be32(cdbp + 2); + nblks_t10 = sg_get_unaligned_be16(cdbp + 7); + } else { + uint32_t num = sg_get_unaligned_be32(cdbp + 10); + + iop->slba = sg_get_unaligned_be64(cdbp + 2); + if (num > (UINT16_MAX + 1)) { + mk_sense_invalid_fld(ptp, true, 11, -1, vb); + return 0; + } else + nblks_t10 = num; + } + if (0 == nblks_t10) { /* NOP in SCSI */ + if (vb > 4) + pr2ws("%s: nblks_t10 is 0, a NOP in SCSI, can't map to NVMe\n", + __func__); + return 0; + } + iop->nblocks = nblks_t10 - 1; + res = sntl_do_nvm_cmd(ptp, iop, 0, false, time_secs, vb); + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, vb); + return 0; + } + return res; +} + +static int +sntl_sync_cache(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) +{ + bool immed = !!(0x2 & cdbp[1]); + struct sg_nvme_user_io io; + struct sg_nvme_user_io * iop = &io; + int res; + + if (vb > 3) + pr2ws("%s: immed=%d, time_secs=%d\n", __func__, (int)immed, + time_secs); + memset(iop, 0, sizeof(*iop)); + iop->opcode = SG_NVME_NVM_FLUSH; + if (vb > 4) + pr2ws("%s: immed bit, lba and num_lbs fields ignored\n", __func__); + res = sntl_do_nvm_cmd(ptp, iop, 0, false, time_secs, vb); + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(ptp, vb); + return 0; + } + return res; +} + +static int +sntl_start_stop(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) +{ + bool immed = !!(0x1 & cdbp[1]); + + if (vb > 3) + pr2ws("%s: immed=%d, time_secs=%d, ignore\n", __func__, (int)immed, + time_secs); + if (ptp) { } /* suppress warning */ + return 0; +} + +/* Executes NVMe Admin command (or at least forwards it to lower layers). + * Returns 0 for success, negative numbers are negated 'errno' values from + * OS system calls. Positive return values are errors from this package. + * When time_secs is 0 the Linux NVMe Admin command default of 60 seconds + * is used. */ +int +sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb) +{ + bool scsi_cdb; + bool is_read = false; + int n, len, hold_dev_fd; + uint16_t sa; + struct sg_pt_linux_scsi * ptp = &vp->impl; + struct sg_nvme_passthru_cmd cmd; + const uint8_t * cdbp; + void * dp = NULL; + + if (! ptp->io_hdr.request) { + if (vb) + pr2ws("No NVMe command given (set_scsi_pt_cdb())\n"); + return SCSI_PT_DO_BAD_PARAMS; + } + hold_dev_fd = ptp->dev_fd; + if (fd >= 0) { + if ((ptp->dev_fd >= 0) && (fd != ptp->dev_fd)) { + if (vb) + pr2ws("%s: file descriptor given to create() and here " + "differ\n", __func__); + return SCSI_PT_DO_BAD_PARAMS; + } + ptp->dev_fd = fd; + } else if (ptp->dev_fd < 0) { + if (vb) + pr2ws("%s: invalid file descriptors\n", __func__); + return SCSI_PT_DO_BAD_PARAMS; + } + n = ptp->io_hdr.request_len; + cdbp = (const uint8_t *)(sg_uintptr_t)ptp->io_hdr.request; + if (vb > 3) + pr2ws("%s: opcode=0x%x, fd=%d (dev_fd=%d), time_secs=%d\n", __func__, + cdbp[0], fd, hold_dev_fd, time_secs); + scsi_cdb = sg_is_scsi_cdb(cdbp, n); + /* direct NVMe command (i.e. 64 bytes long) or SNTL */ + ptp->nvme_our_sntl = scsi_cdb; + if (scsi_cdb) { + switch (cdbp[0]) { + case SCSI_INQUIRY_OPC: + return sntl_inq(ptp, cdbp, time_secs, vb); + case SCSI_REPORT_LUNS_OPC: + return sntl_rluns(ptp, cdbp, time_secs, vb); + case SCSI_TEST_UNIT_READY_OPC: + return sntl_tur(ptp, time_secs, vb); + case SCSI_REQUEST_SENSE_OPC: + return sntl_req_sense(ptp, cdbp, time_secs, vb); + case SCSI_READ10_OPC: + case SCSI_READ16_OPC: + return sntl_read(ptp, cdbp, time_secs, vb); + case SCSI_WRITE10_OPC: + case SCSI_WRITE16_OPC: + return sntl_write(ptp, cdbp, time_secs, vb); + case SCSI_START_STOP_OPC: + return sntl_start_stop(ptp, cdbp, time_secs, vb); + case SCSI_SEND_DIAGNOSTIC_OPC: + return sntl_senddiag(ptp, cdbp, time_secs, vb); + case SCSI_RECEIVE_DIAGNOSTIC_OPC: + return sntl_recvdiag(ptp, cdbp, time_secs, vb); + case SCSI_MODE_SENSE10_OPC: + case SCSI_MODE_SELECT10_OPC: + return sntl_mode_ss(ptp, cdbp, time_secs, vb); + case SCSI_READ_CAPACITY10_OPC: + return sntl_readcap(ptp, cdbp, time_secs, vb); + case SCSI_VERIFY10_OPC: + case SCSI_VERIFY16_OPC: + return sntl_verify(ptp, cdbp, time_secs, vb); + case SCSI_WRITE_SAME10_OPC: + case SCSI_WRITE_SAME16_OPC: + return sntl_write_same(ptp, cdbp, time_secs, vb); + case SCSI_SYNC_CACHE10_OPC: + case SCSI_SYNC_CACHE16_OPC: + return sntl_sync_cache(ptp, cdbp, time_secs, vb); + case SCSI_SERVICE_ACT_IN_OPC: + if (SCSI_READ_CAPACITY16_SA == (cdbp[1] & SCSI_SA_MSK)) + return sntl_readcap(ptp, cdbp, time_secs, vb); + goto fini; + case SCSI_MAINT_IN_OPC: + sa = SCSI_SA_MSK & cdbp[1]; /* service action */ + if (SCSI_REP_SUP_OPCS_OPC == sa) + return sntl_rep_opcodes(ptp, cdbp, time_secs, vb); + else if (SCSI_REP_SUP_TMFS_OPC == sa) + return sntl_rep_tmfs(ptp, cdbp, time_secs, vb); + /* fall through */ + default: +fini: + if (vb > 2) { + char b[64]; + + sg_get_command_name(cdbp, -1, sizeof(b), b); + pr2ws("%s: no translation to NVMe for SCSI %s command\n", + __func__, b); + } + mk_sense_asc_ascq(ptp, SPC_SK_ILLEGAL_REQUEST, INVALID_OPCODE, + 0, vb); + return 0; + } + } + 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, (const uint8_t *)(sg_uintptr_t)ptp->io_hdr.request, n); + if (n < len) /* zero out rest of 'cmd' */ + memset((uint8_t *)&cmd + n, 0, len - n); + if (ptp->io_hdr.din_xfer_len > 0) { + cmd.data_len = ptp->io_hdr.din_xfer_len; + dp = (void *)(sg_uintptr_t)ptp->io_hdr.din_xferp; + cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.din_xferp; + is_read = true; + } else if (ptp->io_hdr.dout_xfer_len > 0) { + cmd.data_len = ptp->io_hdr.dout_xfer_len; + dp = (void *)(sg_uintptr_t)ptp->io_hdr.dout_xferp; + cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.dout_xferp; + is_read = false; + } + return sg_nvme_admin_cmd(ptp, &cmd, dp, is_read, time_secs, vb); +} + +#else /* (HAVE_NVME && (! IGNORE_NVME)) [around line 140] */ + +int +sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb) +{ + if (vb) { + pr2ws("%s: not supported, ", __func__); +#ifdef HAVE_NVME + pr2ws("HAVE_NVME, "); +#else + pr2ws("don't HAVE_NVME, "); +#endif + +#ifdef IGNORE_NVME + pr2ws("IGNORE_NVME"); +#else + pr2ws("don't IGNORE_NVME"); +#endif + pr2ws("\n"); + } + if (vp) { ; } /* suppress warning */ + if (fd) { ; } /* suppress warning */ + if (time_secs) { ; } /* suppress warning */ + return -ENOTTY; /* inappropriate ioctl error */ +} + +#endif /* (HAVE_NVME && (! IGNORE_NVME)) */ + +#if (HAVE_NVME && (! IGNORE_NVME)) + +int +do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int vb) +{ + bool is_read = false; + int dlen; + struct sg_pt_linux_scsi * ptp = &vp->impl; + struct sg_nvme_passthru_cmd cmd; + uint8_t * cmdp = (uint8_t *)&cmd; + void * dp = NULL; + + if (vb && (submq != 0)) + pr2ws("%s: warning, uses submit queue 0\n", __func__); + if (ptp->dev_fd < 0) { + if (vb > 1) + pr2ws("%s: no NVMe file descriptor given\n", __func__); + return SCSI_PT_DO_BAD_PARAMS; + } + if (! ptp->is_nvme) { + if (vb > 1) + pr2ws("%s: file descriptor is not NVMe device\n", __func__); + return SCSI_PT_DO_BAD_PARAMS; + } + if ((! ptp->io_hdr.request) || (64 != ptp->io_hdr.request_len)) { + if (vb > 1) + pr2ws("%s: no NVMe 64 byte command present\n", __func__); + return SCSI_PT_DO_BAD_PARAMS; + } + if (sizeof(cmd) > 64) + memset(cmdp + 64, 0, sizeof(cmd) - 64); + memcpy(cmdp, (uint8_t *)(sg_uintptr_t)ptp->io_hdr.request, 64); + ptp->nvme_our_sntl = false; + + dlen = ptp->io_hdr.din_xfer_len; + if (dlen > 0) { + is_read = true; + dp = (void *)(sg_uintptr_t)ptp->io_hdr.din_xferp; + } else { + dlen = ptp->io_hdr.dout_xfer_len; + if (dlen > 0) + dp = (void *)(sg_uintptr_t)ptp->io_hdr.dout_xferp; + } + return do_nvm_pt_low(ptp, &cmd, dp, dlen, is_read, timeout_secs, vb); +} + +#else /* (HAVE_NVME && (! IGNORE_NVME)) */ + +int +do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int verbose) +{ + if (vb) { + pr2ws("%s: not supported, ", __func__); +#ifdef HAVE_NVME + pr2ws("HAVE_NVME, "); +#else + pr2ws("don't HAVE_NVME, "); +#endif + +#ifdef IGNORE_NVME + pr2ws("IGNORE_NVME"); +#else + pr2ws("don't IGNORE_NVME"); +#endif + if (vp) { } + if (submq) { } + if (timeout_secs) { } + return SCSI_PT_DO_NOT_SUPPORTED; +} + +#endif /* (HAVE_NVME && (! IGNORE_NVME)) */ diff -Nru sdparm-1.10/lib/sg_pt_osf1.c sdparm-1.12/lib/sg_pt_osf1.c --- sdparm-1.10/lib/sg_pt_osf1.c 2015-05-12 04:21:05.000000000 +0000 +++ sdparm-1.12/lib/sg_pt_osf1.c 2020-08-03 04:16:31.000000000 +0000 @@ -1,8 +1,10 @@ /* - * Copyright (c) 2005-2015 Douglas Gilbert. + * Copyright (c) 2005-2020 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. + * + * SPDX-License-Identifier: BSD-2-Clause */ #include @@ -20,15 +22,22 @@ #include #include #include +#include #include #include #include "sg_pt.h" #include "sg_lib.h" +#include "sg_pr2serr.h" +/* Version 2.03 20200722 */ #define OSF1_MAXDEV 64 +#ifndef CAM_DIR_BOTH +#define CAM_DIR_BOTH 0x0 /* copy value from FreeBSD */ +#endif + struct osf1_dev_channel { int bus; int tgt; @@ -43,11 +52,11 @@ static int camopened = 0; struct sg_pt_osf1_scsi { - unsigned char * cdb; + uint8_t * cdb; int cdb_len; - unsigned char * sense; + uint8_t * sense; int sense_len; - unsigned char * dxferp; + uint8_t * dxferp; int dxfer_len; int dxfer_dir; int scsi_status; @@ -56,36 +65,18 @@ int in_err; int os_err; int transport_err; + bool is_nvme; + int dev_fd; }; struct sg_pt_base { struct sg_pt_osf1_scsi impl; }; -#ifdef __GNUC__ -static int pr2ws(const char * fmt, ...) - __attribute__ ((format (printf, 1, 2))); -#else -static int pr2ws(const char * fmt, ...); -#endif - - -static int -pr2ws(const char * fmt, ...) -{ - va_list args; - int n; - - va_start(args, fmt); - n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args); - va_end(args); - return n; -} - /* Returns >= 0 if successful. If error in Unix returns negated errno. */ int -scsi_pt_open_device(const char * device_name, int read_only, int verbose) +scsi_pt_open_device(const char * device_name, bool read_only, int verbose) { int oflags = 0 /* O_NONBLOCK*/ ; @@ -180,18 +171,27 @@ } 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) { @@ -204,42 +204,79 @@ void clear_scsi_pt_obj(struct sg_pt_base * vp) { + bool is_nvme; + int dev_fd; struct sg_pt_osf1_scsi * ptp = &vp->impl; if (ptp) { + is_nvme = ptp->is_nvme; + dev_fd = ptp->dev_fd; bzero(ptp, sizeof(struct sg_pt_osf1_scsi)); + ptp->dev_fd = dev_fd; + ptp->is_nvme = is_nvme; ptp->dxfer_dir = CAM_DIR_NONE; } } void -set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb, +partial_clear_scsi_pt_obj(struct sg_pt_base * vp) +{ + struct sg_pt_osf1_scsi * ptp = &vp->impl; + + if (NULL == ptp) + return; + ptp->in_err = 0; + ptp->os_err = 0; + ptp->transport_err = 0; + ptp->scsi_status = 0; + ptp->dxfer_dir = CAM_DIR_NONE; + ptp->dxferp = NULL; + ptp->dxfer_len = 0; +} + +void +set_scsi_pt_cdb(struct sg_pt_base * vp, const uint8_t * cdb, int cdb_len) { struct sg_pt_osf1_scsi * ptp = &vp->impl; - if (ptp->cdb) - ++ptp->in_err; - ptp->cdb = (unsigned char *)cdb; + ptp->cdb = (uint8_t *)cdb; ptp->cdb_len = cdb_len; } +int +get_scsi_pt_cdb_len(const struct sg_pt_base * vp) +{ + const struct sg_pt_osf1_scsi * ptp = &vp->impl; + + return ptp->cdb_len; +} + +uint8_t * +get_scsi_pt_cdb_buf(const struct sg_pt_base * vp) +{ + const struct sg_pt_osf1_scsi * ptp = &vp->impl; + + return ptp->cdb; +} + void -set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense, +set_scsi_pt_sense(struct sg_pt_base * vp, uint8_t * sense, int max_sense_len) { struct sg_pt_osf1_scsi * ptp = &vp->impl; - if (ptp->sense) - ++ptp->in_err; - bzero(sense, max_sense_len); + if (sense) { + if (max_sense_len > 0) + bzero(sense, max_sense_len); + } ptp->sense = sense; ptp->sense_len = max_sense_len; } /* from device */ void -set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp, +set_scsi_pt_data_in(struct sg_pt_base * vp, uint8_t * dxferp, int dxfer_len) { struct sg_pt_osf1_scsi * ptp = &vp->impl; @@ -255,7 +292,7 @@ /* to device */ void -set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp, +set_scsi_pt_data_out(struct sg_pt_base * vp, const uint8_t * dxferp, int dxfer_len) { struct sg_pt_osf1_scsi * ptp = &vp->impl; @@ -263,7 +300,7 @@ if (ptp->dxferp) ++ptp->in_err; if (dxfer_len > 0) { - ptp->dxferp = (unsigned char *)dxferp; + ptp->dxferp = (uint8_t *)dxferp; ptp->dxfer_len = dxfer_len; ptp->dxfer_dir = CAM_DIR_OUT; } @@ -343,7 +380,7 @@ int len, retval; CCB_SCSIIO ccb; UAGT_CAM_CCB uagt; - unsigned char sensep[ADDL_SENSE_LENGTH]; + uint8_t sensep[ADDL_SENSE_LENGTH]; ptp->os_err = 0; @@ -352,19 +389,36 @@ 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"); @@ -418,7 +472,7 @@ /* 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; } @@ -449,6 +503,49 @@ return ptp->resid; } +void +get_pt_req_lengths(const struct sg_pt_base * vp, int * req_dinp, + int * req_doutp) +{ + const struct sg_pt_osf1_scsi * ptp = &vp->impl; + bool bidi = (ptp->dxfer_dir == CAM_DIR_BOTH); + + if (req_dinp) { + if (ptp->dxfer_len > 0) + *req_dinp = ptp->dxfer_len; + else + *req_dinp = 0; + } + if (req_doutp) { + if ((!bidi) && (ptp->dxfer_len > 0)) + *req_doutp = ptp->dxfer_len; + else + *req_doutp = 0; + } +} + +void +get_pt_actual_lengths(const struct sg_pt_base * vp, int * act_dinp, + int * act_doutp) +{ + const struct sg_pt_osf1_scsi * ptp = &vp->impl; + bool bidi = (ptp->dxfer_dir == CAM_DIR_BOTH); + + if (act_dinp) { + if (ptp->dxfer_len > 0) + *act_dinp = ptp->dxfer_len - ptp->resid; + else + *act_dinp = 0; + } + if (act_doutp) { + if ((!bidi) && (ptp->dxfer_len > 0)) + *act_doutp = ptp->dxfer_len - ptp->resid; + else + *act_doutp = 0; + } +} + + int get_scsi_pt_status_response(const struct sg_pt_base * vp) { @@ -467,6 +564,14 @@ return (len > 0) ? len : 0; } +uint8_t * +get_scsi_pt_sense_buf(const struct sg_pt_base * vp) +{ + const struct sg_pt_osf1_scsi * ptp = &vp->impl; + + return ptp->sense; +} + int get_scsi_pt_duration_ms(const struct sg_pt_base * vp) { @@ -475,6 +580,14 @@ return -1; } +/* If not available return 0 otherwise return number of nanoseconds that the + * lower layers (and hardware) took to execute the command just completed. */ +uint64_t +get_pt_duration_ns(const struct sg_pt_base * vp __attribute__ ((unused))) +{ + return 0; +} + int get_scsi_pt_transport_err(const struct sg_pt_base * vp) { @@ -491,6 +604,14 @@ return ptp->os_err; } +bool +pt_device_is_nvme(const struct sg_pt_base * vp) +{ + const struct sg_pt_osf1_scsi * ptp = &vp->impl; + + return ptp ? ptp->is_nvme : false; +} + char * get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len, char * b) @@ -519,3 +640,13 @@ b[max_b_len - 1] = '\0'; return b; } + +int +do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int verbose) +{ + if (vp) { } + if (submq) { } + if (timeout_secs) { } + if (verbose) { } + return SCSI_PT_DO_NOT_SUPPORTED; +} diff -Nru sdparm-1.10/lib/sg_pt_solaris.c sdparm-1.12/lib/sg_pt_solaris.c --- sdparm-1.10/lib/sg_pt_solaris.c 2015-12-20 16:23:44.000000000 +0000 +++ sdparm-1.12/lib/sg_pt_solaris.c 2020-08-03 04:16:31.000000000 +0000 @@ -1,14 +1,18 @@ /* - * Copyright (c) 2007-2015 Douglas Gilbert. + * Copyright (c) 2007-2020 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. + * + * SPDX-License-Identifier: BSD-2-Clause */ -/* sg_pt_solaris version 1.04 20151220 */ +/* sg_pt_solaris version 1.14 20200724 */ #include #include +#include +#include #include #include #include @@ -22,13 +26,13 @@ #include #include -#include "sg_pt.h" -#include "sg_lib.h" - #ifdef HAVE_CONFIG_H #include "config.h" #endif +#include "sg_pt.h" +#include "sg_lib.h" + #define DEF_TIMEOUT 60 /* 60 seconds */ @@ -37,6 +41,8 @@ int max_sense_len; int in_err; int os_err; + bool is_nvme; + int dev_fd; }; struct sg_pt_base { @@ -46,7 +52,7 @@ /* Returns >= 0 if successful. If error in Unix returns negated errno. */ int -scsi_pt_open_device(const char * device_name, int read_only, int verbose) +scsi_pt_open_device(const char * device_name, bool read_only, int verbose) { int oflags = 0 /* O_NONBLOCK*/ ; @@ -87,20 +93,32 @@ } struct sg_pt_base * -construct_scsi_pt_obj() +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 : dev_fd; + 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; - } + /* Comment in Illumos suggest USCSI_ISOLATE and USCSI_DIAGNOSE (both) + * seem to mean "don't retry" which is what we want. */ + ptp->uscsi.uscsi_flags = USCSI_ISOLATE | USCSI_DIAGNOSE | + USCSI_RQENABLE; + } 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() +{ + return construct_scsi_pt_obj_with_fd(-1, 0); +} + void destruct_scsi_pt_obj(struct sg_pt_base * vp) { @@ -113,37 +131,72 @@ void clear_scsi_pt_obj(struct sg_pt_base * vp) { + bool is_nvme; + int dev_fd; struct sg_pt_solaris_scsi * ptp = &vp->impl; if (ptp) { + is_nvme = ptp->is_nvme; + dev_fd = ptp->dev_fd; memset(ptp, 0, sizeof(struct sg_pt_solaris_scsi)); + ptp->dev_fd = dev_fd; + ptp->is_nvme = is_nvme; ptp->uscsi.uscsi_timeout = DEF_TIMEOUT; - ptp->uscsi.uscsi_flags = USCSI_READ | USCSI_ISOLATE | USCSI_RQENABLE; - ptp->uscsi.uscsi_timeout = DEF_TIMEOUT; + ptp->uscsi.uscsi_flags = USCSI_ISOLATE | USCSI_DIAGNOSE | + USCSI_RQENABLE; } } void -set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb, +partial_clear_scsi_pt_obj(struct sg_pt_base * vp) +{ + struct sg_pt_solaris_scsi * ptp = &vp->impl; + + if (ptp) { + ptp->in_err = 0; + ptp->os_err = 0; + ptp->uscsi.uscsi_status = 0; + ptp->uscsi.uscsi_bufaddr = NULL; + ptp->uscsi.uscsi_buflen = 0; + ptp->uscsi.uscsi_flags = USCSI_ISOLATE | USCSI_DIAGNOSE | + USCSI_RQENABLE; + } +} + +void +set_scsi_pt_cdb(struct sg_pt_base * vp, const uint8_t * cdb, int cdb_len) { struct sg_pt_solaris_scsi * ptp = &vp->impl; - if (ptp->uscsi.uscsi_cdb) - ++ptp->in_err; ptp->uscsi.uscsi_cdb = (char *)cdb; ptp->uscsi.uscsi_cdblen = cdb_len; } +int +get_scsi_pt_cdb_len(const struct sg_pt_base * vp) +{ + const struct sg_pt_solaris_scsi * ptp = &vp->impl; + + return ptp->uscsi.uscsi_cdblen; +} + +uint8_t * +get_scsi_pt_cdb_buf(const struct sg_pt_base * vp) +{ + const struct sg_pt_solaris_scsi * ptp = &vp->impl; + + return (uint8_t *)ptp->uscsi.uscsi_cdb; +} + void -set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense, +set_scsi_pt_sense(struct sg_pt_base * vp, uint8_t * sense, int max_sense_len) { struct sg_pt_solaris_scsi * ptp = &vp->impl; - if (ptp->uscsi.uscsi_rqbuf) - ++ptp->in_err; - memset(sense, 0, max_sense_len); + if (sense && (max_sense_len > 0)) + memset(sense, 0, max_sense_len); ptp->uscsi.uscsi_rqbuf = (char *)sense; ptp->uscsi.uscsi_rqlen = max_sense_len; ptp->max_sense_len = max_sense_len; @@ -151,7 +204,7 @@ /* from device */ void -set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp, +set_scsi_pt_data_in(struct sg_pt_base * vp, uint8_t * dxferp, int dxfer_len) { struct sg_pt_solaris_scsi * ptp = &vp->impl; @@ -161,13 +214,14 @@ if (dxfer_len > 0) { ptp->uscsi.uscsi_bufaddr = (char *)dxferp; ptp->uscsi.uscsi_buflen = dxfer_len; - ptp->uscsi.uscsi_flags = USCSI_READ | USCSI_ISOLATE | USCSI_RQENABLE; + ptp->uscsi.uscsi_flags = USCSI_READ | USCSI_ISOLATE | USCSI_DIAGNOSE | + USCSI_RQENABLE; } } /* to device */ void -set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp, +set_scsi_pt_data_out(struct sg_pt_base * vp, const uint8_t * dxferp, int dxfer_len) { struct sg_pt_solaris_scsi * ptp = &vp->impl; @@ -177,7 +231,8 @@ if (dxfer_len > 0) { ptp->uscsi.uscsi_bufaddr = (char *)dxferp; ptp->uscsi.uscsi_buflen = dxfer_len; - ptp->uscsi.uscsi_flags = USCSI_WRITE | USCSI_ISOLATE | USCSI_RQENABLE; + ptp->uscsi.uscsi_flags = USCSI_WRITE | USCSI_ISOLATE | USCSI_DIAGNOSE | + USCSI_RQENABLE; } } @@ -234,33 +289,49 @@ 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; @@ -283,6 +354,14 @@ return SCSI_PT_RESULT_GOOD; } +uint32_t +get_pt_result(const struct sg_pt_base * vp) +{ + const struct sg_pt_solaris_scsi * ptp = &vp->impl; + + return (uint32_t)ptp->uscsi.uscsi_status; +} + int get_scsi_pt_resid(const struct sg_pt_base * vp) { @@ -291,6 +370,50 @@ return ptp->uscsi.uscsi_resid; } +void +get_pt_req_lengths(const struct sg_pt_base * vp, int * req_dinp, + int * req_doutp) +{ + const struct sg_pt_solaris_scsi * ptp = &vp->impl; + int dxfer_len = ptp->uscsi.uscsi_buflen; + int flags = ptp->uscsi.uscsi_flags; + + if (req_dinp) { + if ((dxfer_len > 0) && (USCSI_READ & flags)) + *req_dinp = dxfer_len; + else + *req_dinp = 0; + } + if (req_doutp) { + if ((dxfer_len > 0) && (USCSI_WRITE & flags)) + *req_doutp = dxfer_len; + else + *req_doutp = 0; + } +} + +void +get_pt_actual_lengths(const struct sg_pt_base * vp, int * act_dinp, + int * act_doutp) +{ + const struct sg_pt_solaris_scsi * ptp = &vp->impl; + int dxfer_len = ptp->uscsi.uscsi_buflen; + int flags = ptp->uscsi.uscsi_flags; + + if (act_dinp) { + if ((dxfer_len > 0) && (USCSI_READ & flags)) + *act_dinp = dxfer_len - ptp->uscsi.uscsi_resid; + else + *act_dinp = 0; + } + if (act_doutp) { + if ((dxfer_len > 0) && (USCSI_WRITE & flags)) + *act_doutp = dxfer_len - ptp->uscsi.uscsi_resid; + else + *act_doutp = 0; + } +} + int get_scsi_pt_status_response(const struct sg_pt_base * vp) { @@ -312,6 +435,14 @@ return 0; } +uint8_t * +get_scsi_pt_sense_buf(const struct sg_pt_base * vp) +{ + const struct sg_pt_solaris_scsi * ptp = &vp->impl; + + return (uint8_t *)ptp->uscsi.uscsi_rqbuf; +} + int get_scsi_pt_duration_ms(const struct sg_pt_base * vp) { @@ -321,15 +452,32 @@ return -1; /* not available */ } +/* If not available return 0 otherwise return number of nanoseconds that the + * lower layers (and hardware) took to execute the command just completed. */ +uint64_t +get_pt_duration_ns(const struct sg_pt_base * vp __attribute__ ((unused))) +{ + return 0; +} + int get_scsi_pt_transport_err(const struct sg_pt_base * vp) { // const struct sg_pt_solaris_scsi * ptp = &vp->impl; - vp = vp; /* ignore and suppress warning */ + if (vp) { ; } /* ignore and suppress warning */ return 0; } +void +set_scsi_pt_transport_err(struct sg_pt_base * vp, int err) +{ + // const struct sg_pt_solaris_scsi * ptp = &vp->impl; + + if (vp) { ; } /* ignore and suppress warning */ + if (err) { ; } /* ignore and suppress warning */ +} + int get_scsi_pt_os_err(const struct sg_pt_base * vp) { @@ -338,6 +486,14 @@ return ptp->os_err; } +bool +pt_device_is_nvme(const struct sg_pt_base * vp) +{ + const struct sg_pt_solaris_scsi * ptp = &vp->impl; + + return ptp ? ptp->is_nvme : false; +} + char * get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len, char * b) @@ -363,3 +519,13 @@ b[max_b_len - 1] = '\0'; return b; } + +int +do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int verbose) +{ + if (vp) { } + if (submq) { } + if (timeout_secs) { } + if (verbose) { } + return SCSI_PT_DO_NOT_SUPPORTED; +} diff -Nru sdparm-1.10/lib/sg_pt_win32.c sdparm-1.12/lib/sg_pt_win32.c --- sdparm-1.10/lib/sg_pt_win32.c 2015-05-12 04:21:05.000000000 +0000 +++ sdparm-1.12/lib/sg_pt_win32.c 2021-01-04 06:17:04.000000000 +0000 @@ -1,11 +1,13 @@ /* - * Copyright (c) 2006-2015 Douglas Gilbert. + * Copyright (c) 2006-2021 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. + * + * SPDX-License-Identifier: BSD-2-Clause */ -/* sg_pt_win32 version 1.16 20150511 */ +/* sg_pt_win32 version 1.33 20210103 */ #include #include @@ -15,15 +17,24 @@ #include #include #include - -#include "sg_pt.h" -#include "sg_lib.h" -#include "sg_pt_win32.h" +#define __STDC_FORMAT_MACROS 1 +#include #ifdef HAVE_CONFIG_H #include "config.h" #endif +#include "sg_lib.h" +#include "sg_unaligned.h" +#include "sg_pt.h" +#include "sg_pt_win32.h" +#include "sg_pt_nvme.h" +#include "sg_pr2serr.h" + + +/* Comment the following line out to use the pre-W10 NVMe pass-through */ +#define W10_NVME_NON_PASSTHRU 1 + #ifndef O_EXCL // #define O_EXCL 0x80 // cygwin ?? // #define O_EXCL 0x80 // Linux @@ -31,6 +42,47 @@ #warning "O_EXCL not defined" #endif +#define SCSI_INQUIRY_OPC 0x12 +#define SCSI_REPORT_LUNS_OPC 0xa0 +#define SCSI_TEST_UNIT_READY_OPC 0x0 +#define SCSI_REQUEST_SENSE_OPC 0x3 +#define SCSI_SEND_DIAGNOSTIC_OPC 0x1d +#define SCSI_RECEIVE_DIAGNOSTIC_OPC 0x1c +#define SCSI_MAINT_IN_OPC 0xa3 +#define SCSI_REP_SUP_OPCS_OPC 0xc +#define SCSI_REP_SUP_TMFS_OPC 0xd +#define SCSI_MODE_SENSE10_OPC 0x5a +#define SCSI_MODE_SELECT10_OPC 0x55 + +/* Additional Sense Code (ASC) */ +#define NO_ADDITIONAL_SENSE 0x0 +#define LOGICAL_UNIT_NOT_READY 0x4 +#define LOGICAL_UNIT_COMMUNICATION_FAILURE 0x8 +#define UNRECOVERED_READ_ERR 0x11 +#define PARAMETER_LIST_LENGTH_ERR 0x1a +#define INVALID_OPCODE 0x20 +#define LBA_OUT_OF_RANGE 0x21 +#define INVALID_FIELD_IN_CDB 0x24 +#define INVALID_FIELD_IN_PARAM_LIST 0x26 +#define UA_RESET_ASC 0x29 +#define UA_CHANGED_ASC 0x2a +#define TARGET_CHANGED_ASC 0x3f +#define LUNS_CHANGED_ASCQ 0x0e +#define INSUFF_RES_ASC 0x55 +#define INSUFF_RES_ASCQ 0x3 +#define LOW_POWER_COND_ON_ASC 0x5e /* ASCQ=0 */ +#define POWER_ON_RESET_ASCQ 0x0 +#define BUS_RESET_ASCQ 0x2 /* scsi bus reset occurred */ +#define MODE_CHANGED_ASCQ 0x1 /* mode parameters changed */ +#define CAPACITY_CHANGED_ASCQ 0x9 +#define SAVING_PARAMS_UNSUP 0x39 +#define TRANSPORT_PROBLEM 0x4b +#define THRESHOLD_EXCEEDED 0x5d +#define LOW_POWER_COND_ON 0x5e +#define MISCOMPARE_VERIFY_ASC 0x1d +#define MICROCODE_CHANGED_ASCQ 0x1 /* with TARGET_CHANGED_ASC */ +#define MICROCODE_CHANGED_WO_RESET_ASCQ 0x16 + /* Use the Microsoft SCSI Pass Through (SPT) interface. It has two * variants: "SPT" where data is double buffered; and "SPTD" where data * pointers to the user space are passed to the OS. Only Windows @@ -58,22 +110,45 @@ #define MAX_OPEN_SIMULT 8 #define WIN32_FDOFFSET 32 +union STORAGE_DEVICE_DESCRIPTOR_DATA { + STORAGE_DEVICE_DESCRIPTOR desc; + char raw[256]; +}; + +union STORAGE_DEVICE_UID_DATA { + STORAGE_DEVICE_UNIQUE_IDENTIFIER desc; + char raw[1060]; +}; + + struct sg_pt_handle { - int in_use; + bool in_use; + bool not_claimed; + bool checked_handle; + bool bus_type_failed; + bool is_nvme; + bool got_physical_drive; HANDLE fh; - char adapter[32]; - int bus; + char adapter[32]; /* for example: '\\.\scsi3' */ + int bus; /* a.k.a. PathId in MS docs */ int target; int lun; + int scsi_pdt; /* Peripheral Device Type, -1 if not known */ + // uint32_t nvme_nsid; /* how do we find this given file handle ?? */ int verbose; /* tunnel verbose through to scsi_pt_close_device */ + char dname[20]; + struct sg_sntl_dev_state_t dev_stat; // owner }; +/* Start zeroed but need to zeroed before use because could be re-use */ static struct sg_pt_handle handle_arr[MAX_OPEN_SIMULT]; struct sg_pt_win32_scsi { - unsigned char * dxferp; - int dxfer_len; - unsigned char * sensep; + bool is_nvme; + bool nvme_direct; /* false: our SNTL; true: received NVMe command */ + bool mdxfer_out; /* direction of metadata xfer, true->data-out */ + bool have_nvme_cmd; + bool is_read; int sense_len; int scsi_status; int resid; @@ -81,6 +156,25 @@ int in_err; int os_err; /* pseudo unix error */ int transport_err; /* windows error number */ + int dev_fd; /* -1 for no "file descriptor" given */ + uint32_t nvme_nsid; /* 1 to 0xfffffffe are possibly valid, 0 + * implies dev_fd is not a NVMe device + * (is_nvme=false) or has no storage (e.g. + * enclosure rather than disk) */ + uint32_t nvme_result; /* DW0 from completion queue */ + uint32_t nvme_status; /* SCT|SC: DW3 27:17 from completion queue, + * note: the DNR+More bit are not there. + * The whole 16 byte completion q entry is + * sent back as sense data */ + uint32_t dxfer_len; + uint32_t mdxfer_len; + uint8_t * dxferp; + uint8_t * mdxferp; /* NVMe has metadata buffer */ + uint8_t * sensep; + uint8_t * nvme_id_ctlp; + uint8_t * free_nvme_id_ctlp; + struct sg_sntl_dev_state_t * dev_statp; /* points to handle's dev_stat */ + uint8_t nvme_cmd[64]; union { SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER swb_d; /* Last entry in structure so data buffer can be extended */ @@ -100,25 +194,8 @@ static int spt_direct = 0; #endif -#ifdef __GNUC__ -static int pr2ws(const char * fmt, ...) - __attribute__ ((format (printf, 1, 2))); -#else -static int pr2ws(const char * fmt, ...); -#endif - - -static int -pr2ws(const char * fmt, ...) -{ - va_list args; - int n; - - va_start(args, fmt); - n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args); - va_end(args); - return n; -} +static int nvme_pt(struct sg_pt_win32_scsi * psp, struct sg_pt_handle * shp, + int time_secs, int vb); /* Request SPT direct interface when state_direct is 1, state_direct set @@ -136,15 +213,141 @@ return spt_direct; } +static const char * +bus_type_str(int bt) +{ + switch (bt) + { + case BusTypeUnknown: + return "Unknown"; + case BusTypeScsi: + return "Scsi"; + case BusTypeAtapi: + return "Atapi"; + case BusTypeAta: + return "Ata"; + case BusType1394: + return "1394"; + case BusTypeSsa: + return "Ssa"; + case BusTypeFibre: + return "Fibre"; + case BusTypeUsb: + return "Usb"; + case BusTypeRAID: + return "RAID"; + case BusTypeiScsi: + return "iScsi"; + case BusTypeSas: + return "Sas"; + case BusTypeSata: + return "Sata"; + case BusTypeSd: + return "Sd"; + case BusTypeMmc: + return "Mmc"; + case BusTypeVirtual: + return "Virt"; + case BusTypeFileBackedVirtual: + return "FBVir"; +#ifdef BusTypeSpaces + case BusTypeSpaces: +#else + case 0x10: +#endif + return "Spaces"; +#ifdef BusTypeNvme + case BusTypeNvme: +#else + case 0x11: +#endif + return "NVMe"; +#ifdef BusTypeSCM + case BusTypeSCM: +#else + case 0x12: +#endif + return "SCM"; +#ifdef BusTypeUfs + case BusTypeUfs: +#else + case 0x13: +#endif + return "Ufs"; + case 0x14: + return "Max"; + case 0x7f: + return "Max Reserved"; + default: + return "_unknown"; + } +} + +static char * +get_err_str(DWORD err, int max_b_len, char * b) +{ + LPVOID lpMsgBuf; + int k, num, ch; + + memset(b, 0, max_b_len); + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, NULL ); + num = lstrlen((LPCTSTR)lpMsgBuf); + if (num < 1) + return b; + num = (num < max_b_len) ? num : (max_b_len - 1); + for (k = 0; k < num; ++k) { + ch = *((LPCTSTR)lpMsgBuf + k); + if ((ch >= 0x0) && (ch < 0x7f)) + b[k] = ch & 0x7f; + else + b[k] = '?'; + } + return b; +} + +/* Returns pointer to sg_pt_handle object given Unix like device_fd. If + * device_fd is invalid or not open returns NULL. If psp is non-NULL and + * NULL is returned then ENODEV is placed in psp->os_err. */ +static struct sg_pt_handle * +get_open_pt_handle(struct sg_pt_win32_scsi * psp, int device_fd, bool vbb) +{ + int index = device_fd - WIN32_FDOFFSET; + struct sg_pt_handle * shp; + + if ((index < 0) || (index >= WIN32_FDOFFSET)) { + if (vbb) + pr2ws("Bad file descriptor\n"); + if (psp) + psp->os_err = EBADF; + return NULL; + } + shp = handle_arr + index; + if (! shp->in_use) { + if (vbb) + pr2ws("File descriptor closed??\n"); + if (psp) + psp->os_err = ENODEV; + return NULL; + } + return shp; +} + /* Returns >= 0 if successful. If error in Unix returns negated errno. */ int -scsi_pt_open_device(const char * device_name, int read_only, int verbose) +scsi_pt_open_device(const char * device_name, bool read_only, int vb) { int oflags = 0 /* O_NONBLOCK*/ ; oflags |= (read_only ? 0 : 0); /* was ... ? O_RDONLY : O_RDWR) */ - return scsi_pt_open_flags(device_name, oflags, verbose); + return scsi_pt_open_flags(device_name, oflags, vb); } /* @@ -160,34 +363,47 @@ * form. */ int -scsi_pt_open_flags(const char * device_name, int flags, int verbose) +scsi_pt_open_flags(const char * device_name, int flags, int vb) { - int len, k, adapter_num, bus, target, lun, off, got_scsi_name; - int index, num, got_pd_name, pd_num, share_mode; + bool got_scsi_name = false; + int len, k, adapter_num, bus, target, lun, off, index, num, pd_num; + int share_mode; struct sg_pt_handle * shp; char buff[8]; share_mode = (O_EXCL & flags) ? 0 : (FILE_SHARE_READ | FILE_SHARE_WRITE); /* lock */ for (k = 0; k < MAX_OPEN_SIMULT; k++) - if (0 == handle_arr[k].in_use) + if (! handle_arr[k].in_use) break; if (k == MAX_OPEN_SIMULT) { - if (verbose) + if (vb) pr2ws("too many open handles (%d)\n", MAX_OPEN_SIMULT); return -EMFILE; - } else - handle_arr[k].in_use = 1; + } else { + /* clear any previous contents */ + memset(handle_arr + k, 0, sizeof(struct sg_pt_handle)); + handle_arr[k].in_use = true; + } /* unlock */ index = k; shp = handle_arr + index; +#if (HAVE_NVME && (! IGNORE_NVME)) + sntl_init_dev_stat(&shp->dev_stat); +#endif adapter_num = 0; bus = 0; /* also known as 'PathId' in MS docs */ target = 0; lun = 0; - got_pd_name = 0; - got_scsi_name = 0; - len = strlen(device_name); + len = (int)strlen(device_name); + k = (int)sizeof(shp->dname); + if (len < k) + strcpy(shp->dname, device_name); + else if (len == k) + memcpy(shp->dname, device_name, k - 1); + else /* trim on left */ + memcpy(shp->dname, device_name + (len - k), k - 1); + shp->dname[k - 1] = '\0'; if ((len > 4) && (0 == strncmp("\\\\.\\", device_name, 4))) off = 4; else @@ -198,32 +414,33 @@ if (0 == strncmp("PD", buff, 2)) { num = sscanf(device_name + off + 2, "%d", &pd_num); if (1 == num) - got_pd_name = 1; + shp->got_physical_drive = true; } - if (0 == got_pd_name) { + if (! shp->got_physical_drive) { buff[2] = toupper((int)device_name[off + 2]); buff[3] = toupper((int)device_name[off + 3]); if (0 == strncmp("SCSI", buff, 4)) { num = sscanf(device_name + off + 4, "%d:%d,%d,%d", &adapter_num, &bus, &target, &lun); if (num < 3) { - if (verbose) + if (vb) pr2ws("expected format like: " - "'SCSI:.[.]'\n"); - shp->in_use = 0; + "'SCSI:,[,]'\n"); + shp->in_use = false; return -EINVAL; } - got_scsi_name = 1; + got_scsi_name = true; } } } shp->bus = bus; shp->target = target; shp->lun = lun; - shp->verbose = verbose; + shp->scsi_pdt = -1; + shp->verbose = vb; memset(shp->adapter, 0, sizeof(shp->adapter)); - strncpy(shp->adapter, "\\\\.\\", 4); - if (got_pd_name) + memcpy(shp->adapter, "\\\\.\\", 4); + if (shp->got_physical_drive) snprintf(shp->adapter + 4, sizeof(shp->adapter) - 5, "PhysicalDrive%d", pd_num); else if (got_scsi_name) @@ -232,19 +449,36 @@ else snprintf(shp->adapter + 4, sizeof(shp->adapter) - 5, "%s", device_name + off); + if (vb > 4) + pr2ws("%s: CreateFile('%s'), bus=%d, target=%d, lun=%d\n", __func__, + shp->adapter, bus, target, lun); +#if 1 shp->fh = CreateFile(shp->adapter, GENERIC_READ | GENERIC_WRITE, share_mode, NULL, OPEN_EXISTING, 0, NULL); +#endif + +#if 0 + shp->fh = CreateFileA(shp->adapter, GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, + (SECURITY_ATTRIBUTES *)0, OPEN_EXISTING, 0, 0); + // No GENERIC_READ/WRITE access required, works without admin rights (W10) + shp->fh = CreateFileA(shp->adapter, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, + (SECURITY_ATTRIBUTES *)0, OPEN_EXISTING, 0, (HANDLE)0); +#endif if (shp->fh == INVALID_HANDLE_VALUE) { - if (verbose) - pr2ws("Windows CreateFile error=%u\n", - (unsigned int)GetLastError()); - shp->in_use = 0; + if (vb) { + uint32_t err = (uint32_t)GetLastError(); + char b[128]; + + pr2ws("%s: CreateFile error: %s [%u]\n", __func__, + get_err_str(err, sizeof(b), b), err); + } + shp->in_use = false; return -ENODEV; } return index + WIN32_FDOFFSET; } - /* Returns 0 if successful. If device_id seems wild returns -ENODEV, * other errors return 0. If CloseHandle() fails and verbose > 0 then * outputs warning with value from GetLastError(). The verbose value @@ -253,41 +487,232 @@ int scsi_pt_close_device(int device_fd) { - struct sg_pt_handle * shp; - int index; - - index = device_fd - WIN32_FDOFFSET; + struct sg_pt_handle * shp = get_open_pt_handle(NULL, device_fd, false); - if ((index < 0) || (index >= WIN32_FDOFFSET)) + if (NULL == shp) return -ENODEV; - shp = handle_arr + index; if ((! CloseHandle(shp->fh)) && shp->verbose) pr2ws("Windows CloseHandle error=%u\n", (unsigned int)GetLastError()); shp->bus = 0; shp->target = 0; shp->lun = 0; memset(shp->adapter, 0, sizeof(shp->adapter)); - shp->in_use = 0; + shp->in_use = false; shp->verbose = 0; + shp->dname[0] = '\0'; + return 0; +} + +/* Attempt to return device's SCSI peripheral device type (pdt), a number + * between 0 (disks) and 31 (not given) by calling IOCTL_SCSI_GET_INQUIRY_DATA + * on the adapter. Returns -EIO on error and -999 if not found. */ +static int +get_scsi_pdt(struct sg_pt_handle *shp, int vb) +{ + const int alloc_sz = 8192; + int j; + int ret = -999; + BOOL ok; + ULONG dummy; + DWORD err; + BYTE wbus; + uint8_t * inqBuf; + uint8_t * free_inqBuf; + char b[128]; + + if (vb > 2) + pr2ws("%s: enter, adapter: %s\n", __func__, shp->adapter); + inqBuf = sg_memalign(alloc_sz, 0 /* page size */, &free_inqBuf, false); + if (NULL == inqBuf) { + pr2ws("%s: unable to allocate %d bytes\n", __func__, alloc_sz); + return -ENOMEM; + } + ok = DeviceIoControl(shp->fh, IOCTL_SCSI_GET_INQUIRY_DATA, + NULL, 0, inqBuf, alloc_sz, &dummy, NULL); + if (ok) { + PSCSI_ADAPTER_BUS_INFO ai; + PSCSI_BUS_DATA pbd; + PSCSI_INQUIRY_DATA pid; + int num_lus, off; + + ai = (PSCSI_ADAPTER_BUS_INFO)inqBuf; + for (wbus = 0; wbus < ai->NumberOfBusses; ++wbus) { + pbd = ai->BusData + wbus; + num_lus = pbd->NumberOfLogicalUnits; + off = pbd->InquiryDataOffset; + for (j = 0; j < num_lus; ++j) { + if ((off < (int)sizeof(SCSI_ADAPTER_BUS_INFO)) || + (off > (alloc_sz - (int)sizeof(SCSI_INQUIRY_DATA)))) + break; + pid = (PSCSI_INQUIRY_DATA)(inqBuf + off); + if ((shp->bus == pid->PathId) && + (shp->target == pid->TargetId) && + (shp->lun == pid->Lun)) { /* got match */ + shp->scsi_pdt = pid->InquiryData[0] & 0x3f; + shp->not_claimed = ! pid->DeviceClaimed; + shp->checked_handle = true; + shp->bus_type_failed = false; + if (vb > 3) + pr2ws("%s: found, scsi_pdt=%d, claimed=%d, " + "target=%d, lun=%d\n", __func__, shp->scsi_pdt, + pid->DeviceClaimed, shp->target, shp->lun); + ret = shp->scsi_pdt; + goto fini; + } + off = pid->NextInquiryDataOffset; + } + } + } else { + err = GetLastError(); + if (vb > 1) + pr2ws("%s: IOCTL_SCSI_GET_INQUIRY_DATA failed err=%u\n\t%s", + shp->adapter, (unsigned int)err, + get_err_str(err, sizeof(b), b)); + ret = -EIO; + } +fini: + if (free_inqBuf) + free(free_inqBuf); + return ret; /* no match after checking all PathIds, Targets and LUs */ +} + +/* Returns 0 on success, negated errno if error */ +static int +get_bus_type(struct sg_pt_handle *shp, const char *dname, + STORAGE_BUS_TYPE * btp, int vb) +{ + DWORD num_out, err; + STORAGE_BUS_TYPE bt; + union STORAGE_DEVICE_DESCRIPTOR_DATA sddd; + STORAGE_PROPERTY_QUERY query = {StorageDeviceProperty, + PropertyStandardQuery, {0} }; + char b[256]; + + memset(&sddd, 0, sizeof(sddd)); + if (! DeviceIoControl(shp->fh, IOCTL_STORAGE_QUERY_PROPERTY, + &query, sizeof(query), &sddd, sizeof(sddd), + &num_out, NULL)) { + if (vb > 2) { + err = GetLastError(); + pr2ws("%s IOCTL_STORAGE_QUERY_PROPERTY(Devprop) failed, " + "Error: %s [%u]\n", dname, get_err_str(err, sizeof(b), b), + (uint32_t)err); + } + shp->bus_type_failed = true; + return -EIO; + } + bt = sddd.desc.BusType; + if (vb > 2) { + pr2ws("%s: Bus type: %s\n", __func__, bus_type_str((int)bt)); + if (vb > 3) { + pr2ws("Storage Device Descriptor Data:\n"); + hex2stderr((const uint8_t *)&sddd, num_out, 0); + } + } + if (shp) { + shp->checked_handle = true; + shp->bus_type_failed = false; + shp->is_nvme = (BusTypeNvme == bt); + } + if (btp) + *btp = bt; return 0; } +/* 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 device_fd, const char * device_name, int vb) +{ + int res; + STORAGE_BUS_TYPE bt; + const char * dnp = device_name; + struct sg_pt_handle * shp; + + if (vb > 3) + pr2ws("%s: device_name: %s\n", __func__, dnp); + shp = get_open_pt_handle(NULL, device_fd, vb > 1); + if (NULL == shp) { + pr2ws("%s: device_fd (%s) bad or not in_use ??\n", __func__, + dnp ? dnp : ""); + return -ENODEV; + } + if (shp->bus_type_failed) { + if (vb > 2) + pr2ws("%s: skip because get_bus_type() has failed\n", __func__); + return 0; + } + dnp = dnp ? dnp : shp->dname; + res = get_bus_type(shp, dnp, &bt, vb); + if (res < 0) { + if (! shp->got_physical_drive) { + res = get_scsi_pdt(shp, vb); + if (res >= 0) + return 1; + } + return res; + } + return (BusTypeNvme == bt) ? 3 : 1; + /* NVMe "char" ?? device, could be enclosure: 3 */ + /* SCSI generic pass-though device: 1 */ +} + +#if (HAVE_NVME && (! IGNORE_NVME)) +static bool checked_ev_dsense = false; +static bool ev_dsense = false; +#endif + struct sg_pt_base * -construct_scsi_pt_obj() +construct_scsi_pt_obj_with_fd(int dev_fd, int vb) { + int res; struct sg_pt_win32_scsi * psp; struct sg_pt_base * vp = NULL; + struct sg_pt_handle * shp = NULL; - /* 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; - + if (dev_fd >= 0) { + shp = get_open_pt_handle(NULL, dev_fd, vb > 1); + if (NULL == shp) { + if (vb) + pr2ws("%s: dev_fd is not open\n", __func__); + return NULL; + } + if (! (shp->bus_type_failed || shp->checked_handle)) { + res = get_bus_type(shp, shp->dname, NULL, vb); + if (res < 0) { + if (! shp->got_physical_drive) + res = get_scsi_pdt(shp, vb); + if ((res < 0) && (vb > 1)) + pr2ws("%s: get_bus_type() errno=%d, continue\n", __func__, + -res); + } + } + } psp = (struct sg_pt_win32_scsi *)calloc(sizeof(struct sg_pt_win32_scsi), 1); if (psp) { - if (spt_direct) { + psp->dev_fd = (dev_fd < 0) ? -1 : dev_fd; + if (shp) { + psp->is_nvme = shp->is_nvme; + psp->dev_statp = &shp->dev_stat; +#if (HAVE_NVME && (! IGNORE_NVME)) + sntl_init_dev_stat(psp->dev_statp); + if (! checked_ev_dsense) { + ev_dsense = sg_get_initial_dsense(); + checked_ev_dsense = true; + } + shp->dev_stat.scsi_dsense = ev_dsense; +#endif + } + if (psp->is_nvme) { + ; /* should be 'psp->nvme_nsid = shp->nvme_nsid' */ + } else if (spt_direct) { psp->swb_d.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED; psp->swb_d.spt.SenseInfoLength = SCSI_MAX_SENSE_LEN; psp->swb_d.spt.SenseInfoOffset = @@ -300,15 +725,24 @@ offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf); psp->swb_i.spt.TimeOutValue = DEF_TIMEOUT; } - vp = malloc(sizeof(struct sg_pt_win32_scsi *)); // yes a pointer + vp = (struct sg_pt_base *)malloc(sizeof(struct sg_pt_win32_scsi *)); + /* yes, allocating the size of a pointer (4 or 8 bytes) */ if (vp) vp->implp = psp; else free(psp); } + if ((NULL == vp) && vb) + pr2ws("%s: about to return NULL, space problem\n", __func__); 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) { @@ -322,12 +756,92 @@ } } +/* 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 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 vb) +{ + int res; + struct sg_pt_win32_scsi * psp; + + if (NULL == vp) { + if (vb) + pr2ws(">>>> %s: pointer to object is NULL\n", __func__); + return EINVAL; + } + if ((psp = vp->implp)) { + struct sg_pt_handle * shp; + + if (dev_han < 0) { + psp->dev_fd = -1; + psp->is_nvme = false; + psp->nvme_nsid = 0; + return 0; + } + shp = get_open_pt_handle(psp, dev_han, vb > 1); + if (NULL == shp) { + if (vb) + pr2ws("%s: dev_han (%d) is invalid\n", __func__, dev_han); + psp->os_err = EINVAL; + return psp->os_err; + } + psp->os_err = 0; + psp->transport_err = 0; + psp->in_err = 0; + psp->scsi_status = 0; + psp->dev_fd = dev_han; + if (! (shp->bus_type_failed || shp->checked_handle)) { + res = get_bus_type(shp, shp->dname, NULL, vb); + if (res < 0) { + res = get_scsi_pdt(shp, vb); + if (res >= 0) /* clears shp->bus_type_failed on success */ + psp->os_err = 0; + } + if ((res < 0) && (vb > 2)) + pr2ws("%s: get_bus_type() errno=%d\n", __func__, -res); + } + if (shp->bus_type_failed) + psp->os_err = EIO; + if (psp->os_err) + return psp->os_err; + psp->is_nvme = shp->is_nvme; + psp->nvme_nsid = 0; /* should be 'psp->nvme_nsid = shp->nvme_nsid' */ + psp->dev_statp = &shp->dev_stat; + } + 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_win32_scsi * psp; + + if (vp) { + psp = vp->implp; + return psp ? psp->dev_fd : -1; + } + return -1; +} + +/* Keep state information such as dev_fd and nvme_nsid */ void clear_scsi_pt_obj(struct sg_pt_base * vp) { + bool is_nvme; + int dev_fd; + uint32_t nvme_nsid; struct sg_pt_win32_scsi * psp = vp->implp; + struct sg_sntl_dev_state_t * dsp; if (psp) { + dev_fd = psp->dev_fd; + is_nvme = psp->is_nvme; + nvme_nsid = psp->nvme_nsid; + dsp = psp->dev_statp; memset(psp, 0, sizeof(struct sg_pt_win32_scsi)); if (spt_direct) { psp->swb_d.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED; @@ -342,18 +856,50 @@ offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf); psp->swb_i.spt.TimeOutValue = DEF_TIMEOUT; } + psp->dev_fd = dev_fd; + psp->is_nvme = is_nvme; + psp->nvme_nsid = nvme_nsid; + psp->dev_statp = dsp; } } void -set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb, - int cdb_len) +partial_clear_scsi_pt_obj(struct sg_pt_base * vp) { struct sg_pt_win32_scsi * psp = vp->implp; + if (NULL == psp) + return; + psp->in_err = 0; + psp->os_err = 0; + psp->transport_err = 0; + psp->scsi_status = 0; if (spt_direct) { - if (psp->swb_d.spt.CdbLength > 0) - ++psp->in_err; + psp->swb_d.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED; + psp->swb_d.spt.SenseInfoLength = SCSI_MAX_SENSE_LEN; + psp->swb_d.spt.SenseInfoOffset = + offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf); + psp->swb_d.spt.TimeOutValue = DEF_TIMEOUT; + } else { + psp->swb_i.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED; + psp->swb_i.spt.SenseInfoLength = SCSI_MAX_SENSE_LEN; + psp->swb_i.spt.SenseInfoOffset = + offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf); + psp->swb_i.spt.TimeOutValue = DEF_TIMEOUT; + } +} + +void +set_scsi_pt_cdb(struct sg_pt_base * vp, const uint8_t * cdb, + int cdb_len) +{ + bool scsi_cdb = sg_is_scsi_cdb(cdb, cdb_len); + struct sg_pt_win32_scsi * psp = vp->implp; + + if (! scsi_cdb) { + psp->have_nvme_cmd = true; + memcpy(psp->nvme_cmd, cdb, cdb_len); + } else if (spt_direct) { if (cdb_len > (int)sizeof(psp->swb_d.spt.Cdb)) { ++psp->in_err; return; @@ -361,8 +907,6 @@ memcpy(psp->swb_d.spt.Cdb, cdb, cdb_len); psp->swb_d.spt.CdbLength = cdb_len; } else { - if (psp->swb_i.spt.CdbLength > 0) - ++psp->in_err; if (cdb_len > (int)sizeof(psp->swb_i.spt.Cdb)) { ++psp->in_err; return; @@ -372,22 +916,36 @@ } } +int +get_scsi_pt_cdb_len(const struct sg_pt_base * vp) +{ + const struct sg_pt_win32_scsi * psp = vp->implp; + + return spt_direct ? psp->swb_d.spt.CdbLength : psp->swb_i.spt.CdbLength; +} + +uint8_t * +get_scsi_pt_cdb_buf(const struct sg_pt_base * vp) +{ + const struct sg_pt_win32_scsi * psp = vp->implp; + + return (uint8_t *)(spt_direct ? psp->swb_d.spt.Cdb : psp->swb_i.spt.Cdb); +} + void -set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense, - int sense_len) +set_scsi_pt_sense(struct sg_pt_base * vp, uint8_t * sense, int sense_len) { struct sg_pt_win32_scsi * psp = vp->implp; - if (psp->sensep) - ++psp->in_err; - memset(sense, 0, sense_len); + if (sense && (sense_len > 0)) + memset(sense, 0, sense_len); psp->sensep = sense; psp->sense_len = sense_len; } /* from device */ void -set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp, +set_scsi_pt_data_in(struct sg_pt_base * vp, uint8_t * dxferp, int dxfer_len) { struct sg_pt_win32_scsi * psp = vp->implp; @@ -396,7 +954,8 @@ ++psp->in_err; if (dxfer_len > 0) { psp->dxferp = dxferp; - psp->dxfer_len = dxfer_len; + psp->dxfer_len = (uint32_t)dxfer_len; + psp->is_read = true; if (spt_direct) psp->swb_d.spt.DataIn = SCSI_IOCTL_DATA_IN; else @@ -406,7 +965,7 @@ /* to device */ void -set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp, +set_scsi_pt_data_out(struct sg_pt_base * vp, const uint8_t * dxferp, int dxfer_len) { struct sg_pt_win32_scsi * psp = vp->implp; @@ -414,8 +973,8 @@ if (psp->dxferp) ++psp->in_err; if (dxfer_len > 0) { - psp->dxferp = (unsigned char *)dxferp; - psp->dxfer_len = dxfer_len; + psp->dxferp = (uint8_t *)dxferp; + psp->dxfer_len = (uint32_t)dxfer_len; if (spt_direct) psp->swb_d.spt.DataIn = SCSI_IOCTL_DATA_OUT; else @@ -424,6 +983,21 @@ } void +set_pt_metadata_xfer(struct sg_pt_base * vp, uint8_t * mdxferp, + uint32_t mdxfer_len, bool out_true) +{ + struct sg_pt_win32_scsi * psp = vp->implp; + + if (psp->mdxferp) + ++psp->in_err; + if (mdxfer_len > 0) { + psp->mdxferp = mdxferp; + psp->mdxfer_len = mdxfer_len; + psp->mdxfer_out = out_true; + } +} + +void set_scsi_pt_packet_id(struct sg_pt_base * vp __attribute__ ((unused)), int pack_id __attribute__ ((unused))) { @@ -468,49 +1042,25 @@ * using direct interface. Clears os_err field prior to active call (whose * result may set it again). */ static int -do_scsi_pt_direct(struct sg_pt_base * vp, int device_fd, int time_secs, - int verbose) +scsi_pt_direct(struct sg_pt_win32_scsi * psp, struct sg_pt_handle * shp, + int time_secs, int vb) { - int index = device_fd - WIN32_FDOFFSET; - struct sg_pt_win32_scsi * psp = vp->implp; - struct sg_pt_handle * shp; BOOL status; DWORD returned; psp->os_err = 0; - if (psp->in_err) { - if (verbose) - pr2ws("Replicated or unused set_scsi_pt...\n"); - return SCSI_PT_DO_BAD_PARAMS; - } if (0 == psp->swb_d.spt.CdbLength) { - if (verbose) + if (vb) pr2ws("No command (cdb) given\n"); return SCSI_PT_DO_BAD_PARAMS; } - - index = device_fd - WIN32_FDOFFSET; - if ((index < 0) || (index >= WIN32_FDOFFSET)) { - if (verbose) - pr2ws("Bad file descriptor\n"); - psp->os_err = ENODEV; - return -psp->os_err; - } - shp = handle_arr + index; - if (0 == shp->in_use) { - if (verbose) - pr2ws("File descriptor closed??\n"); - psp->os_err = ENODEV; - return -psp->os_err; - } - shp->verbose = verbose; psp->swb_d.spt.Length = sizeof (SCSI_PASS_THROUGH_DIRECT); psp->swb_d.spt.PathId = shp->bus; psp->swb_d.spt.TargetId = shp->target; psp->swb_d.spt.Lun = shp->lun; psp->swb_d.spt.TimeOutValue = time_secs; psp->swb_d.spt.DataTransferLength = psp->dxfer_len; - if (verbose > 4) { + if (vb > 4) { pr2ws(" spt_direct, adapter: %s Length=%d ScsiStatus=%d PathId=%d " "TargetId=%d Lun=%d\n", shp->adapter, (int)psp->swb_d.spt.Length, (int)psp->swb_d.spt.ScsiStatus, @@ -538,8 +1088,12 @@ unsigned int u; u = (unsigned int)GetLastError(); - if (verbose) - pr2ws("Windows DeviceIoControl error=%u\n", u); + if (vb) { + char b[128]; + + pr2ws("%s: DeviceIoControl: %s [%u]\n", __func__, + get_err_str(u, sizeof(b), b), u); + } psp->transport_err = (int)u; psp->os_err = EIO; return 0; /* let app find transport error */ @@ -564,54 +1118,30 @@ * indirect interface. Clears os_err field prior to active call (whose * result may set it again). */ static int -do_scsi_pt_indirect(struct sg_pt_base * vp, int device_fd, int time_secs, - int verbose) +scsi_pt_indirect(struct sg_pt_base * vp, struct sg_pt_handle * shp, + int time_secs, int vb) { - int index = device_fd - WIN32_FDOFFSET; - struct sg_pt_win32_scsi * psp = vp->implp; - struct sg_pt_handle * shp; BOOL status; DWORD returned; + struct sg_pt_win32_scsi * psp = vp->implp; - psp->os_err = 0; - if (psp->in_err) { - if (verbose) - pr2ws("Replicated or unused set_scsi_pt...\n"); - return SCSI_PT_DO_BAD_PARAMS; - } if (0 == psp->swb_i.spt.CdbLength) { - if (verbose) + if (vb) pr2ws("No command (cdb) given\n"); return SCSI_PT_DO_BAD_PARAMS; } - - index = device_fd - WIN32_FDOFFSET; - if ((index < 0) || (index >= WIN32_FDOFFSET)) { - if (verbose) - pr2ws("Bad file descriptor\n"); - psp->os_err = ENODEV; - return -psp->os_err; - } - shp = handle_arr + index; - if (0 == shp->in_use) { - if (verbose) - pr2ws("File descriptor closed??\n"); - psp->os_err = ENODEV; - return -psp->os_err; - } - shp->verbose = verbose; if (psp->dxfer_len > (int)sizeof(psp->swb_i.ucDataBuf)) { int extra = psp->dxfer_len - (int)sizeof(psp->swb_i.ucDataBuf); struct sg_pt_win32_scsi * epsp; - if (verbose > 4) + if (vb > 4) pr2ws("spt_indirect: dxfer_len (%d) too large for initial data\n" " buffer (%d bytes), try enlarging\n", psp->dxfer_len, (int)sizeof(psp->swb_i.ucDataBuf)); epsp = (struct sg_pt_win32_scsi *) calloc(sizeof(struct sg_pt_win32_scsi) + extra, 1); if (NULL == epsp) { - pr2ws("do_scsi_pt: failed to enlarge data buffer to %d bytes\n", + pr2ws("%s: failed to enlarge data buffer to %d bytes\n", __func__, psp->dxfer_len); psp->os_err = ENOMEM; return -psp->os_err; @@ -629,7 +1159,7 @@ psp->swb_i.spt.Lun = shp->lun; psp->swb_i.spt.TimeOutValue = time_secs; psp->swb_i.spt.DataTransferLength = psp->dxfer_len; - if (verbose > 4) { + if (vb > 4) { pr2ws(" spt_indirect, adapter: %s Length=%d ScsiStatus=%d PathId=%d " "TargetId=%d Lun=%d\n", shp->adapter, (int)psp->swb_i.spt.Length, (int)psp->swb_i.spt.ScsiStatus, @@ -658,11 +1188,14 @@ &returned, NULL); if (! status) { - unsigned int u; + uint32_t u = (uint32_t)GetLastError(); - u = (unsigned int)GetLastError(); - if (verbose) - pr2ws("Windows DeviceIoControl error=%u\n", u); + if (vb) { + char b[128]; + + pr2ws("%s: DeviceIoControl: %s [%u]\n", __func__, + get_err_str(u, sizeof(b), b), u); + } psp->transport_err = (int)u; psp->os_err = EIO; return 0; /* let app find transport error */ @@ -685,16 +1218,65 @@ return 0; } -/* Executes SCSI command (or at least forwards it to lower layers). +/* Executes SCSI or NVME command (or at least forwards it to lower layers). * Clears os_err field prior to active call (whose result may set it - * again). */ + * again). Returns 0 on success, positive SCSI_PT_DO_* errors for syntax + * like errors and negated errnos for OS errors. For Windows its errors + * are placed in psp->transport_err and a errno is simulated. */ 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_fd, int time_secs, int vb) { - if (spt_direct) - return do_scsi_pt_direct(vp, device_fd, time_secs, verbose); + int res; + struct sg_pt_win32_scsi * psp = vp->implp; + struct sg_pt_handle * shp; + + if (! (vp && ((psp = vp->implp)))) { + if (vb) + pr2ws("%s: NULL 1st argument to this function\n", __func__); + return SCSI_PT_DO_BAD_PARAMS; + } + psp->os_err = 0; + if (dev_fd >= 0) { + if ((psp->dev_fd >= 0) && (dev_fd != psp->dev_fd)) { + if (vb) + pr2ws("%s: file descriptor given to create() and here " + "differ\n", __func__); + return SCSI_PT_DO_BAD_PARAMS; + } + psp->dev_fd = dev_fd; + } else if (psp->dev_fd < 0) { /* so no dev_fd in ctor */ + if (vb) + pr2ws("%s: missing device file descriptor\n", __func__); + return SCSI_PT_DO_BAD_PARAMS; + } else + dev_fd = psp->dev_fd; + shp = get_open_pt_handle(psp, dev_fd, vb > 3); + if (NULL == shp) + return -psp->os_err; + + if (! (shp->bus_type_failed || shp->checked_handle)) { + res = get_bus_type(shp, shp->dname, NULL, vb); + if (res < 0) { + res = get_scsi_pdt(shp, vb); + if (res >= 0) /* clears shp->bus_type_failed on success */ + psp->os_err = 0; + } + if ((res < 0) && (vb > 2)) + pr2ws("%s: get_bus_type() errno=%d\n", __func__, -res); + } + if (shp->bus_type_failed) + psp->os_err = EIO; + if (psp->os_err) + return -psp->os_err; + psp->is_nvme = shp->is_nvme; + psp->dev_statp = &shp->dev_stat; + + if (psp->is_nvme) + return nvme_pt(psp, shp, time_secs, vb); + else if (spt_direct) + return scsi_pt_direct(psp, shp, time_secs, vb); else - return do_scsi_pt_indirect(vp, device_fd, time_secs, verbose); + return scsi_pt_indirect(vp, shp, time_secs, vb); } int @@ -723,32 +1305,102 @@ return psp->resid; } -int -get_scsi_pt_status_response(const struct sg_pt_base * vp) +void +get_pt_req_lengths(const struct sg_pt_base * vp, int * req_dinp, + int * req_doutp) { const struct sg_pt_win32_scsi * psp = vp->implp; - return psp->scsi_status; + if (req_dinp) { + if (psp->is_read && (psp->dxfer_len > 0)) + *req_dinp = psp->dxfer_len; + else + *req_dinp = 0; + } + if (req_doutp) { + if ((! psp->is_read) && (psp->dxfer_len > 0)) + *req_doutp = psp->dxfer_len; + else + *req_doutp = 0; + } } -int -get_scsi_pt_sense_len(const struct sg_pt_base * vp) +void +get_pt_actual_lengths(const struct sg_pt_base * vp, int * act_dinp, + int * act_doutp) { const struct sg_pt_win32_scsi * psp = vp->implp; - int len; - len = psp->sense_len - psp->sense_resid; - return (len > 0) ? len : 0; + if (act_dinp) { + if (psp->is_read && (psp->dxfer_len > 0)) + *act_dinp = psp->dxfer_len - psp->resid; + else + *act_dinp = 0; + } + if (act_doutp) { + if ((! psp->is_read) && (psp->dxfer_len > 0)) + *act_doutp = psp->dxfer_len - psp->resid; + else + *act_doutp = 0; + } } + int -get_scsi_pt_duration_ms(const struct sg_pt_base * vp __attribute__ ((unused))) +get_scsi_pt_status_response(const struct sg_pt_base * vp) +{ + const struct sg_pt_win32_scsi * psp = vp->implp; + + if (NULL == psp) + return 0; + return psp->nvme_direct ? (int)psp->nvme_status : psp->scsi_status; +} + +uint32_t +get_pt_result(const struct sg_pt_base * vp) +{ + const struct sg_pt_win32_scsi * psp = vp->implp; + + if (NULL == psp) + return 0; + return psp->nvme_direct ? psp->nvme_result : (uint32_t)psp->scsi_status; +} + +int +get_scsi_pt_sense_len(const struct sg_pt_base * vp) +{ + const struct sg_pt_win32_scsi * psp = vp->implp; + int len; + + len = psp->sense_len - psp->sense_resid; + return (len > 0) ? len : 0; +} + +uint8_t * +get_scsi_pt_sense_buf(const struct sg_pt_base * vp) +{ + const struct sg_pt_win32_scsi * psp = vp->implp; + + return psp->sensep; +} + + +int +get_scsi_pt_duration_ms(const struct sg_pt_base * vp __attribute__ ((unused))) { - // const struct sg_pt_freebsd_scsi * psp = vp->implp; + // const struct sg_pt_win32_scsi * psp = vp->implp; return -1; } +/* If not available return 0 otherwise return number of nanoseconds that the + * lower layers (and hardware) took to execute the command just completed. */ +uint64_t +get_pt_duration_ns(const struct sg_pt_base * vp __attribute__ ((unused))) +{ + return 0; +} + int get_scsi_pt_transport_err(const struct sg_pt_base * vp) { @@ -757,6 +1409,14 @@ return psp->transport_err; } +void +set_scsi_pt_transport_err(struct sg_pt_base * vp, int err) +{ + struct sg_pt_win32_scsi * psp = vp->implp; + + psp->transport_err = err; +} + int get_scsi_pt_os_err(const struct sg_pt_base * vp) { @@ -765,41 +1425,38 @@ return psp->os_err; } +bool +pt_device_is_nvme(const struct sg_pt_base * vp) +{ + const struct sg_pt_win32_scsi * psp = vp->implp; + + return psp ? psp->is_nvme : false; +} + +/* If a NVMe block device (which includes the NSID) handle is associated + * * with 'vp', then its NSID is returned (values range from 0x1 to + * * 0xffffffe). Otherwise 0 is returned. */ +uint32_t +get_pt_nvme_nsid(const struct sg_pt_base * vp) +{ + const struct sg_pt_win32_scsi * psp = vp->implp; + + return psp->nvme_nsid; +} +/* Use the transport_err for Windows errors. */ char * get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len, char * b) { struct sg_pt_win32_scsi * psp = (struct sg_pt_win32_scsi *)vp->implp; - LPVOID lpMsgBuf; - int k, num, ch; - if (max_b_len < 2) { - if (1 == max_b_len) + if ((max_b_len < 2) || (NULL == psp) || (NULL == b)) { + if (b && (max_b_len > 0)) b[0] = '\0'; return b; } - memset(b, 0, max_b_len); - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - psp->transport_err, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &lpMsgBuf, - 0, NULL ); - num = lstrlen((LPCTSTR)lpMsgBuf); - if (num < 1) - return b; - num = (num < max_b_len) ? num : (max_b_len - 1); - for (k = 0; k < num; ++k) { - ch = *((LPCTSTR)lpMsgBuf + k); - if ((ch >= 0x0) && (ch < 0x7f)) - b[k] = ch & 0x7f; - else - b[k] = '?'; - } - return b; + return get_err_str(psp->transport_err, max_b_len, b); } char * @@ -814,3 +1471,1674 @@ b[max_b_len - 1] = '\0'; return b; } + +#if (HAVE_NVME && (! IGNORE_NVME)) + +static void +mk_sense_asc_ascq(struct sg_pt_win32_scsi * psp, int sk, int asc, int ascq, + int vb) +{ + bool dsense = psp->dev_statp->scsi_dsense; + int slen = psp->sense_len; + int n; + uint8_t * sbp = (uint8_t *)psp->sensep; + + psp->scsi_status = SAM_STAT_CHECK_CONDITION; + if ((slen < 8) || ((! dsense) && (slen < 14))) { + if (vb) + pr2ws("%s: sense_len=%d too short, want 14 or more\n", + __func__, slen); + return; + } + if (dsense) + n = (slen > 32) ? 32 : slen; + else + n = (slen < 18) ? slen : 18; + psp->sense_resid = (slen > n) ? (slen - n) : 0; + memset(sbp, 0, slen); + sg_build_sense_buffer(dsense, sbp, sk, asc, ascq); + if (vb > 3) + pr2ws("%s: [sense_key,asc,ascq]: [0x%x,0x%x,0x%x]\n", __func__, sk, + asc, ascq); +} + +static void +mk_sense_from_nvme_status(struct sg_pt_win32_scsi * psp, int vb) +{ + bool ok; + bool dsense = psp->dev_statp->scsi_dsense; + int n; + int slen = psp->sense_len; + uint8_t sstatus, sk, asc, ascq; + uint8_t * sbp = (uint8_t *)psp->sensep; + + ok = sg_nvme_status2scsi(psp->nvme_status, &sstatus, &sk, &asc, &ascq); + if (! ok) { /* can't find a mapping to a SCSI error, so ... */ + sstatus = SAM_STAT_CHECK_CONDITION; + sk = SPC_SK_ILLEGAL_REQUEST; + asc = 0xb; + ascq = 0x0; /* asc: "WARNING" purposely vague */ + } + + psp->scsi_status = sstatus; + if ((slen < 8) || ((! dsense) && (slen < 14))) { + if (vb) + pr2ws("%s: sense_len=%d too short, want 14 or more\n", __func__, + slen); + return; + } + if (dsense) + n = (slen > 32) ? 32 : slen; + else + n = (slen < 18) ? slen : 18; + psp->sense_resid = (slen > n) ? slen - n : 0; + memset(sbp, 0, slen); + sg_build_sense_buffer(dsense, sbp, sk, asc, ascq); + if (dsense && (psp->nvme_status > 0)) + sg_nvme_desc2sense(sbp, false /* dnr */, false /* more */, + psp->nvme_status); + if (vb > 3) + pr2ws("%s: [status, sense_key,asc,ascq]: [0x%x, 0x%x,0x%x,0x%x]\n", + __func__, sstatus, sk, asc, ascq); +} + +/* Set in_bit to -1 to indicate no bit position of invalid field */ +static void +mk_sense_invalid_fld(struct sg_pt_win32_scsi * psp, bool in_cdb, int in_byte, + int in_bit, int vb) +{ + bool dsense = psp->dev_statp->scsi_dsense; + int sl, asc, n; + int slen = psp->sense_len; + uint8_t * sbp = (uint8_t *)psp->sensep; + uint8_t sks[4]; + + psp->scsi_status = SAM_STAT_CHECK_CONDITION; + asc = in_cdb ? INVALID_FIELD_IN_CDB : INVALID_FIELD_IN_PARAM_LIST; + if ((slen < 8) || ((! dsense) && (slen < 14))) { + if (vb) + pr2ws("%s: max_response_len=%d too short, want 14 or more\n", + __func__, slen); + return; + } + if (dsense) + n = (slen > 32) ? 32 : slen; + else + n = (slen < 18) ? slen : 18; + psp->sense_resid = (slen > n) ? (slen - n) : 0; + memset(sbp, 0, slen); + sg_build_sense_buffer(dsense, sbp, SPC_SK_ILLEGAL_REQUEST, asc, 0); + memset(sks, 0, sizeof(sks)); + sks[0] = 0x80; + if (in_cdb) + sks[0] |= 0x40; + if (in_bit >= 0) { + sks[0] |= 0x8; + sks[0] |= (0x7 & in_bit); + } + sg_put_unaligned_be16(in_byte, sks + 1); + if (dsense) { + sl = sbp[7] + 8; + sbp[7] = sl; + sbp[sl] = 0x2; + sbp[sl + 1] = 0x6; + memcpy(sbp + sl + 4, sks, 3); + } else + memcpy(sbp + 15, sks, 3); + if (vb > 3) + pr2ws("%s: [sense_key,asc,ascq]: [0x5,0x%x,0x0] %c byte=%d, bit=%d\n", + __func__, asc, in_cdb ? 'C' : 'D', in_byte, + ((in_bit > 0) ? (0x7 & in_bit) : 0)); +} + +#if W10_NVME_NON_PASSTHRU /* W10 and later, no real pass-through ?? */ + +#ifndef NVME_MAX_LOG_SIZE +#define NVME_MAX_LOG_SIZE 4096 +#endif + +static int +nvme_identify(struct sg_pt_win32_scsi * psp, struct sg_pt_handle * shp, + const uint8_t * cmdp, uint8_t * dp, uint32_t dlen, int vb) +{ + bool id_ctrl; + int res = 0; + const uint32_t pg_sz = sg_get_page_size(); + uint32_t cdw10, nsid, n; + const uint8_t * bp; + BOOL result; + PVOID buffer = NULL; + uint8_t * free_buffer = NULL; + ULONG bufferLength = 0; + ULONG returnedLength = 0; + STORAGE_PROPERTY_QUERY * query = NULL; + STORAGE_PROTOCOL_SPECIFIC_DATA * protocolData = NULL; + STORAGE_PROTOCOL_DATA_DESCRIPTOR * protocolDataDescr = NULL; + + nsid = sg_get_unaligned_le32(cmdp + SG_NVME_PT_NSID); + cdw10 = sg_get_unaligned_le32(cmdp + SG_NVME_PT_CDW10); + id_ctrl = (0x1 == cdw10); + n = dlen < NVME_MAX_LOG_SIZE ? NVME_MAX_LOG_SIZE : dlen; + bufferLength = offsetof(STORAGE_PROPERTY_QUERY, AdditionalParameters) + + sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA) + n; + buffer = sg_memalign(bufferLength, pg_sz, &free_buffer, false); + if (buffer == NULL) { + res = sg_convert_errno(ENOMEM); + if (vb > 1) + pr2ws("%s: unable to allocate memory\n", __func__); + psp->os_err = res; + return -res; + } + query = (STORAGE_PROPERTY_QUERY *)buffer; + + query->PropertyId = id_ctrl ? StorageAdapterProtocolSpecificProperty : + StorageDeviceProtocolSpecificProperty; + query->QueryType = PropertyStandardQuery; + protocolDataDescr = (STORAGE_PROTOCOL_DATA_DESCRIPTOR *)buffer; + protocolData = (STORAGE_PROTOCOL_SPECIFIC_DATA *) + query->AdditionalParameters; + + protocolData->ProtocolType = ProtocolTypeNvme; + protocolData->DataType = NVMeDataTypeIdentify; + protocolData->ProtocolDataRequestValue = cdw10; + if (! id_ctrl) + protocolData->ProtocolDataRequestSubValue = nsid; + protocolData->ProtocolDataOffset = sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA); + protocolData->ProtocolDataLength = dlen; + + result = DeviceIoControl(shp->fh, IOCTL_STORAGE_QUERY_PROPERTY, + buffer, bufferLength, buffer, bufferLength, + &returnedLength, (OVERLAPPED*)0); + if ((! result) || (0 == returnedLength)) { + n = (uint32_t)GetLastError(); + psp->transport_err = n; + psp->os_err = EIO; /* simulate Unix error, */ + if (vb > 2) { + char b[128]; + + pr2ws("%s: IOCTL_STORAGE_QUERY_PROPERTY(id_%s) failed: %s " + "[%u]\n", __func__, (id_ctrl ? "ctrl" : "ns"), + get_err_str(n, sizeof(b), b), n); + } + res = -psp->os_err; + goto err_out; + } + if (dlen > 0) { + protocolData = &protocolDataDescr->ProtocolSpecificData; + bp = (const uint8_t *)protocolData + protocolData->ProtocolDataOffset; + memcpy(dp, bp, dlen); + if (0 == psp->nvme_nsid) { + uint32_t nn = sg_get_unaligned_le32(bp + 516); + + if (1 == nn) /* if physical drive has only 1 namespace */ + psp->nvme_nsid = 1; /* then its nsid must be 1 */ + /* N.B. Need better get_nsid_from _handle technique when 2 or + * more namespaces. Suggestions? */ + } + } + psp->nvme_status = 0; + psp->nvme_result = + protocolDataDescr->ProtocolSpecificData.FixedProtocolReturnData; + if (vb > 3) + pr2ws("%s: IOCTL_STORAGE_QUERY_PROPERTY(id_ctrl) success, " + "returnedLength=%u\n", __func__, (uint32_t)returnedLength); + res = 0; +err_out: + if (free_buffer) + free(free_buffer); + return res; +} + +static int +nvme_get_features(struct sg_pt_win32_scsi * psp, struct sg_pt_handle * shp, + const uint8_t * cmdp, uint8_t * dp, uint32_t dlen, int vb) +{ + int res = 0; + const uint32_t pg_sz = sg_get_page_size(); + uint32_t cdw10, nsid, n; + const uint8_t * bp; + BOOL result; + PVOID buffer = NULL; + uint8_t * free_buffer = NULL; + ULONG bufferLength = 0; + ULONG returnedLength = 0; + STORAGE_PROPERTY_QUERY * query = NULL; + STORAGE_PROTOCOL_SPECIFIC_DATA * protocolData = NULL; + STORAGE_PROTOCOL_DATA_DESCRIPTOR * protocolDataDescr = NULL; + + nsid = sg_get_unaligned_le32(cmdp + SG_NVME_PT_NSID); + cdw10 = sg_get_unaligned_le32(cmdp + SG_NVME_PT_CDW10); + n = dlen < NVME_MAX_LOG_SIZE ? NVME_MAX_LOG_SIZE : dlen; + bufferLength = offsetof(STORAGE_PROPERTY_QUERY, AdditionalParameters) + + sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA) + n; + buffer = sg_memalign(bufferLength, pg_sz, &free_buffer, false); + if (buffer == NULL) { + res = sg_convert_errno(ENOMEM); + if (vb > 1) + pr2ws("%s: unable to allocate memory\n", __func__); + psp->os_err = res; + return -res; + } + query = (STORAGE_PROPERTY_QUERY *)buffer; + + query->PropertyId = StorageDeviceProtocolSpecificProperty; + query->QueryType = PropertyStandardQuery; + protocolDataDescr = (STORAGE_PROTOCOL_DATA_DESCRIPTOR *)buffer; + protocolData = (STORAGE_PROTOCOL_SPECIFIC_DATA *) + query->AdditionalParameters; + + protocolData->ProtocolType = ProtocolTypeNvme; + protocolData->DataType = NVMeDataTypeFeature; /* Get Features */ + protocolData->ProtocolDataRequestValue = cdw10; + protocolData->ProtocolDataRequestSubValue = nsid; + protocolData->ProtocolDataOffset = sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA); + protocolData->ProtocolDataLength = dlen; + + result = DeviceIoControl(shp->fh, IOCTL_STORAGE_QUERY_PROPERTY, + buffer, bufferLength, buffer, bufferLength, + &returnedLength, (OVERLAPPED*)0); + if ((! result) || (0 == returnedLength)) { + n = (uint32_t)GetLastError(); + psp->transport_err = n; + psp->os_err = EIO; /* simulate Unix error, */ + if (vb > 2) { + char b[128]; + + pr2ws("%s: IOCTL_STORAGE_QUERY_PROPERTY(id_ctrl) failed: %s " + "[%u]\n", __func__, get_err_str(n, sizeof(b), b), n); + } + res = -psp->os_err; + goto err_out; + } + if (dlen > 0) { + protocolData = &protocolDataDescr->ProtocolSpecificData; + bp = (const uint8_t *)protocolData + protocolData->ProtocolDataOffset; + memcpy(dp, bp, dlen); + } + psp->nvme_status = 0; + psp->nvme_result = + protocolDataDescr->ProtocolSpecificData.FixedProtocolReturnData; + if (vb > 3) + pr2ws("%s: IOCTL_STORAGE_QUERY_PROPERTY(id_ctrl) success, " + "returnedLength=%u\n", __func__, (uint32_t)returnedLength); + res = 0; +err_out: + if (free_buffer) + free(free_buffer); + return res; +} + +static int +nvme_get_log_page(struct sg_pt_win32_scsi * psp, struct sg_pt_handle * shp, + const uint8_t * cmdp, uint8_t * dp, uint32_t dlen, int vb) +{ + int res = 0; + const uint32_t pg_sz = sg_get_page_size(); + uint32_t cdw10, nsid, n; + const uint8_t * bp; + BOOL result; + PVOID buffer = NULL; + uint8_t * free_buffer = NULL; + ULONG bufferLength = 0; + ULONG returnedLength = 0; + STORAGE_PROPERTY_QUERY * query = NULL; + STORAGE_PROTOCOL_SPECIFIC_DATA * protocolData = NULL; + STORAGE_PROTOCOL_DATA_DESCRIPTOR * protocolDataDescr = NULL; + + nsid = sg_get_unaligned_le32(cmdp + SG_NVME_PT_NSID); + cdw10 = sg_get_unaligned_le32(cmdp + SG_NVME_PT_CDW10); + n = dlen < NVME_MAX_LOG_SIZE ? NVME_MAX_LOG_SIZE : dlen; + bufferLength = offsetof(STORAGE_PROPERTY_QUERY, AdditionalParameters) + + sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA) + n; + buffer = sg_memalign(bufferLength, pg_sz, &free_buffer, false); + if (buffer == NULL) { + res = sg_convert_errno(ENOMEM); + if (vb > 1) + pr2ws("%s: unable to allocate memory\n", __func__); + psp->os_err = res; + return -res; + } + query = (STORAGE_PROPERTY_QUERY *)buffer; + + query->PropertyId = StorageDeviceProtocolSpecificProperty; + query->QueryType = PropertyStandardQuery; + protocolDataDescr = (STORAGE_PROTOCOL_DATA_DESCRIPTOR *)buffer; + protocolData = (STORAGE_PROTOCOL_SPECIFIC_DATA *) + query->AdditionalParameters; + + protocolData->ProtocolType = ProtocolTypeNvme; + protocolData->DataType = NVMeDataTypeLogPage; /* Get Log Page */ + protocolData->ProtocolDataRequestValue = cdw10; + protocolData->ProtocolDataRequestSubValue = nsid; + protocolData->ProtocolDataOffset = sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA); + protocolData->ProtocolDataLength = dlen; + + result = DeviceIoControl(shp->fh, IOCTL_STORAGE_QUERY_PROPERTY, + buffer, bufferLength, buffer, bufferLength, + &returnedLength, (OVERLAPPED*)0); + if ((! result) || (0 == returnedLength)) { + n = (uint32_t)GetLastError(); + psp->transport_err = n; + psp->os_err = EIO; /* simulate Unix error, */ + if (vb > 2) { + char b[128]; + + pr2ws("%s: IOCTL_STORAGE_QUERY_PROPERTY(id_ctrl) failed: %s " + "[%u]\n", __func__, get_err_str(n, sizeof(b), b), n); + } + res = -psp->os_err; + goto err_out; + } + if (dlen > 0) { + protocolData = &protocolDataDescr->ProtocolSpecificData; + bp = (const uint8_t *)protocolData + protocolData->ProtocolDataOffset; + memcpy(dp, bp, dlen); + } + psp->nvme_status = 0; + psp->nvme_result = + protocolDataDescr->ProtocolSpecificData.FixedProtocolReturnData; + if (vb > 3) + pr2ws("%s: IOCTL_STORAGE_QUERY_PROPERTY(id_ctrl) success, " + "returnedLength=%u\n", __func__, (uint32_t)returnedLength); + res = 0; +err_out: + if (free_buffer) + free(free_buffer); + return res; +} + +static int +nvme_real_pt(struct sg_pt_win32_scsi * psp, struct sg_pt_handle * shp, + const uint8_t * cmdp, uint8_t * dp, uint32_t dlen, bool is_read, + int time_secs, int vb) +{ + int res = 0; + const uint32_t cmd_len = 64; + const uint32_t pg_sz = sg_get_page_size(); + uint32_t n, k; + uint32_t rd_off = 0; + uint32_t slen = psp->sense_len; + uint8_t * bp; + uint8_t * sbp = psp->sensep; + BOOL ok; + PVOID buffer = NULL; + uint8_t * free_buffer = NULL; + ULONG bufferLength = 0; + ULONG returnLength = 0; + STORAGE_PROTOCOL_COMMAND * protoCmdp; + const NVME_ERROR_INFO_LOG * neilp; + + n = dlen < NVME_MAX_LOG_SIZE ? NVME_MAX_LOG_SIZE : dlen; + bufferLength = offsetof(STORAGE_PROTOCOL_COMMAND, Command) + + cmd_len + + sizeof(NVME_ERROR_INFO_LOG) + n; + buffer = sg_memalign(bufferLength, pg_sz, &free_buffer, false); + if (buffer == NULL) { + res = sg_convert_errno(ENOMEM); + if (vb > 1) + pr2ws("%s: unable to allocate memory\n", __func__); + psp->os_err = res; + return -res; + } + protoCmdp = (STORAGE_PROTOCOL_COMMAND *)buffer; + protoCmdp->Version = STORAGE_PROTOCOL_STRUCTURE_VERSION; + protoCmdp->Length = sizeof(STORAGE_PROTOCOL_COMMAND); + protoCmdp->ProtocolType = ProtocolTypeNvme; + /* without *_ADAPTER_REQUEST flag, goes to device */ + protoCmdp->Flags = STORAGE_PROTOCOL_COMMAND_FLAG_ADAPTER_REQUEST; + /* protoCmdp->Flags = 0; */ + protoCmdp->CommandLength = cmd_len; + protoCmdp->ErrorInfoLength = sizeof(NVME_ERROR_INFO_LOG); + if (dlen > 0) { + if (is_read) + protoCmdp->DataFromDeviceTransferLength = dlen; + else + protoCmdp->DataToDeviceTransferLength = dlen; + } + protoCmdp->TimeOutValue = (time_secs > 0) ? time_secs : DEF_TIMEOUT; + protoCmdp->ErrorInfoOffset = + offsetof(STORAGE_PROTOCOL_COMMAND, Command) + cmd_len; + n = protoCmdp->ErrorInfoOffset + protoCmdp->ErrorInfoLength; + if (is_read) { + protoCmdp->DataFromDeviceBufferOffset = n; + rd_off = n; + } else + protoCmdp->DataToDeviceBufferOffset = n; + protoCmdp->CommandSpecific = + STORAGE_PROTOCOL_SPECIFIC_NVME_ADMIN_COMMAND; + memcpy(protoCmdp->Command, cmdp, cmd_len); + if ((dlen > 0) && (! is_read)) { + bp = (uint8_t *)protoCmdp + n; + memcpy(bp, dp, dlen); + } + + ok = DeviceIoControl(shp->fh, IOCTL_STORAGE_PROTOCOL_COMMAND, + buffer, bufferLength, buffer, bufferLength, + &returnLength, (OVERLAPPED*)0); + if (! ok) { + n = (uint32_t)GetLastError(); + psp->transport_err = n; + psp->os_err = EIO; /* simulate Unix error, */ + if (vb > 2) { + char b[128]; + + pr2ws("%s: IOCTL_STORAGE_PROTOCOL_COMMAND failed: %s " + "[%u]\n", __func__, get_err_str(n, sizeof(b), b), n); + pr2ws(" ... ReturnStatus=0x%x, ReturnLength=%u\n", + (uint32_t)protoCmdp->ReturnStatus, (uint32_t)returnLength); + } + res = -psp->os_err; + goto err_out; + } + bp = (uint8_t *)protoCmdp + protoCmdp->ErrorInfoOffset; + neilp = (const NVME_ERROR_INFO_LOG *)bp; + /* Shift over top of Phase tag bit */ + psp->nvme_status = 0x3ff & (neilp->Status.AsUshort >> 1); + if ((dlen > 0) && is_read) { + bp = (uint8_t *)protoCmdp + rd_off; + memcpy(dp, bp, dlen); + } + psp->nvme_result = protoCmdp->FixedProtocolReturnData; + if (psp->nvme_direct && sbp && (slen > 3)) { + /* build 16 byte "sense" buffer from completion queue entry */ + n = (slen < 16) ? slen : 16; + memset(sbp, 0 , n); + psp->sense_resid = (slen > 16) ? (slen - 16) : 0; + sg_put_unaligned_le32(psp->nvme_result, sbp + SG_NVME_PT_CQ_DW0); + if (n > 11) { + k = neilp->SQID; + sg_put_unaligned_le32((k << 16), sbp + SG_NVME_PT_CQ_DW2); + if (n > 15) { + k = ((uint32_t)neilp->Status.AsUshort << 16) | neilp->CMDID; + sg_put_unaligned_le32(k, sbp + SG_NVME_PT_CQ_DW3); + } + } + } + if (vb > 3) + pr2ws("%s: opcode=0x%x, status=0x%x, result=0x%x\n", + __func__, cmdp[0], psp->nvme_status, psp->nvme_result); + res = psp->nvme_status ? SG_LIB_NVME_STATUS : 0; +err_out: + if (free_buffer) + free(free_buffer); + return res; +} + +static int +do_nvme_admin_cmd(struct sg_pt_win32_scsi * psp, struct sg_pt_handle * shp, + const uint8_t * cmdp, uint8_t * dp, uint32_t dlen, + bool is_read, int time_secs, int vb) +{ + const uint32_t cmd_len = 64; + int res; + uint32_t n; + uint8_t opcode; + + psp->os_err = 0; + psp->transport_err = 0; + if (NULL == cmdp) { + if (! psp->have_nvme_cmd) + return SCSI_PT_DO_BAD_PARAMS; + cmdp = psp->nvme_cmd; + is_read = psp->is_read; + dlen = psp->dxfer_len; + dp = psp->dxferp; + } + if (vb > 2) { + pr2ws("NVMe is_read=%s, dlen=%u, command:\n", + (is_read ? "true" : "false"), dlen); + hex2stderr((const uint8_t *)cmdp, cmd_len, 1); + if ((vb > 3) && (! is_read) && dp) { + if (dlen > 0) { + n = dlen; + if ((dlen < 512) || (vb > 5)) + pr2ws("\nData-out buffer (%u bytes):\n", n); + else { + pr2ws("\nData-out buffer (first 512 of %u bytes):\n", n); + n = 512; + } + hex2stderr((const uint8_t *)dp, n, 0); + } + } + } + opcode = cmdp[0]; + switch (opcode) { /* The matches below are cached by W10 */ + case 0x6: /* Identify (controller + namespace */ + res = nvme_identify(psp, shp, cmdp, dp, dlen, vb); + if (res) + goto err_out; + break; + case 0xa: /* Get features */ + res = nvme_get_features(psp, shp, cmdp, dp, dlen, vb); + if (res) + goto err_out; + break; + case 0x2: /* Get Log Page */ + res = nvme_get_log_page(psp, shp, cmdp, dp, dlen, vb); + if (res) + goto err_out; + break; + default: + res = nvme_real_pt(psp, shp, cmdp, dp, dlen, is_read, time_secs, vb); + if (res) + goto err_out; + break; + /* IOCTL_STORAGE_PROTOCOL_COMMAND base pass-through goes here */ + res = -EINVAL; + goto err_out; + } + + if ((vb > 3) && is_read && dp && (dlen > 0)) { + n = dlen; + if ((dlen < 1024) || (vb > 5)) + pr2ws("\nData-in buffer (%u bytes):\n", n); + else { + pr2ws("\nData-in buffer (first 1024 of %u bytes):\n", n); + n = 1024; + } + hex2stderr((const uint8_t *)dp, n, 0); + } +err_out: + return res; +} + +#else /* W10_NVME_NON_PASSTHRU */ + +/* If cmdp is NULL then dp, dlen and is_read are ignored, those values are + * obtained from psp. Returns 0 for success. Returns SG_LIB_NVME_STATUS if + * there is non-zero NVMe status (SCT|SC from the completion queue) with the + * value placed in psp->nvme_status. If Unix error from ioctl then return + * negated value (equivalent -errno from basic Unix system functions like + * open()). CDW0 from the completion queue is placed in psp->nvme_result in + * the absence of an error. + * The following code is based on os_win32.cpp in smartmontools: + * Copyright (C) 2004-17 Christian Franke + * The code is licensed with a GPL-2. */ +static int +do_nvme_admin_cmd(struct sg_pt_win32_scsi * psp, struct sg_pt_handle * shp, + const uint8_t * cmdp, uint8_t * dp, uint32_t dlen, + bool is_read, int time_secs, int vb) +{ + const uint32_t cmd_len = 64; + int res; + uint32_t n, alloc_len; + const uint32_t pg_sz = sg_get_page_size(); + uint32_t slen = psp->sense_len; + uint8_t * sbp = psp->sensep; + NVME_PASS_THROUGH_IOCTL * pthru; + uint8_t * free_pthru; + DWORD num_out = 0; + BOOL ok; + + psp->os_err = 0; + psp->transport_err = 0; + if (NULL == cmdp) { + if (! psp->have_nvme_cmd) + return SCSI_PT_DO_BAD_PARAMS; + cmdp = psp->nvme_cmd; + is_read = psp->is_read; + dlen = psp->dxfer_len; + dp = psp->dxferp; + } + if (vb > 2) { + pr2ws("NVMe is_read=%s, dlen=%u, command:\n", + (is_read ? "true" : "false"), dlen); + hex2stderr((const uint8_t *)cmdp, cmd_len, 1); + if ((vb > 3) && (! is_read) && dp) { + if (dlen > 0) { + n = dlen; + if ((dlen < 512) || (vb > 5)) + pr2ws("\nData-out buffer (%u bytes):\n", n); + else { + pr2ws("\nData-out buffer (first 512 of %u bytes):\n", n); + n = 512; + } + hex2stderr((const uint8_t *)dp, n, 0); + } + } + } + alloc_len = sizeof(NVME_PASS_THROUGH_IOCTL) + dlen; + pthru = (NVME_PASS_THROUGH_IOCTL *)sg_memalign(alloc_len, pg_sz, + &free_pthru, false); + if (NULL == pthru) { + res = sg_convert_errno(ENOMEM); + if (vb > 1) + pr2ws("%s: unable to allocate memory\n", __func__); + psp->os_err = res; + return -res; + } + if (dp && (dlen > 0) && (! is_read)) + memcpy(pthru->DataBuffer, dp, dlen); /* dout-out buffer */ + /* Set NVMe command */ + pthru->SrbIoCtrl.HeaderLength = sizeof(SRB_IO_CONTROL); + memcpy(pthru->SrbIoCtrl.Signature, NVME_SIG_STR, sizeof(NVME_SIG_STR)-1); + pthru->SrbIoCtrl.Timeout = (time_secs > 0) ? time_secs : DEF_TIMEOUT; + pthru->SrbIoCtrl.ControlCode = NVME_PASS_THROUGH_SRB_IO_CODE; + pthru->SrbIoCtrl.ReturnCode = 0; + pthru->SrbIoCtrl.Length = alloc_len - sizeof(SRB_IO_CONTROL); + + memcpy(pthru->NVMeCmd, cmdp, cmd_len); + if (dlen > 0) + pthru->Direction = is_read ? 2 : 1; + else + pthru->Direction = 0; + pthru->ReturnBufferLen = alloc_len; + shp = get_open_pt_handle(psp, psp->dev_fd, vb > 1); + if (NULL == shp) { + res = -psp->os_err; /* -ENODEV */ + goto err_out; + } + + ok = DeviceIoControl(shp->fh, IOCTL_SCSI_MINIPORT, pthru, alloc_len, + pthru, alloc_len, &num_out, (OVERLAPPED*)0); + if (! ok) { + n = (uint32_t)GetLastError(); + psp->transport_err = n; + psp->os_err = EIO; /* simulate Unix error, */ + if (vb > 2) { + char b[128]; + + pr2ws("%s: IOCTL_SCSI_MINIPORT failed: %s [%u]\n", __func__, + get_err_str(n, sizeof(b), b), n); + } + } + /* nvme_status is SCT|SC, therefore it excludes DNR+More */ + psp->nvme_status = 0x3ff & (pthru->CplEntry[3] >> 17); + if (psp->nvme_status && (vb > 1)) { + uint16_t s = psp->nvme_status; + char b[80]; + + pr2ws("%s: opcode=0x%x failed: NVMe status: %s [0x%x]\n", __func__, + cmdp[0], sg_get_nvme_cmd_status_str(s, sizeof(b), b), s); + } + psp->nvme_result = sg_get_unaligned_le32(pthru->CplEntry + 0); + + psp->sense_resid = 0; + if (psp->nvme_direct && sbp && (slen > 3)) { + /* build 16 byte "sense" buffer */ + n = (slen < 16) ? slen : 16; + memset(sbp, 0 , n); + psp->sense_resid = (slen > 16) ? (slen - 16) : 0; + sg_put_unaligned_le32(pthru->CplEntry[0], sbp + SG_NVME_PT_CQ_DW0); + if (n > 7) { + sg_put_unaligned_le32(pthru->CplEntry[1], + sbp + SG_NVME_PT_CQ_DW1); + if (n > 11) { + sg_put_unaligned_le32(pthru->CplEntry[2], + sbp + SG_NVME_PT_CQ_DW2); + if (n > 15) + sg_put_unaligned_le32(pthru->CplEntry[3], + sbp + SG_NVME_PT_CQ_DW3); + } + } + } + if (! ok) { + res = -psp->os_err; + goto err_out; + } else if (psp->nvme_status) { + res = SG_LIB_NVME_STATUS; + goto err_out; + } + + if (dp && (dlen > 0) && is_read) { + memcpy(dp, pthru->DataBuffer, dlen); /* data-in buffer */ + if (vb > 3) { + n = dlen; + if ((dlen < 1024) || (vb > 5)) + pr2ws("\nData-in buffer (%u bytes):\n", n); + else { + pr2ws("\nData-in buffer (first 1024 of %u bytes):\n", n); + n = 1024; + } + hex2stderr((const uint8_t *)dp, n, 0); + } + } + res = 0; +err_out: + if (free_pthru) + free(free_pthru); + return res; +} + +#endif /* W10_NVME_NON_PASSTHRU */ + + +static void +sntl_check_enclosure_override(struct sg_pt_win32_scsi * psp, + struct sg_pt_handle * shp, int vb) +{ + uint8_t * up = psp->nvme_id_ctlp; + uint8_t nvmsr; + + if (NULL == up) + return; + nvmsr = up[253]; + if (vb > 3) + pr2ws("%s: enter, nvmsr=%u\n", __func__, nvmsr); + shp->dev_stat.id_ctl253 = nvmsr; + switch (shp->dev_stat.enclosure_override) { + case 0x0: /* no override */ + if (0x3 & nvmsr) { + shp->dev_stat.pdt = PDT_DISK; + shp->dev_stat.enc_serv = 1; + } else if (0x2 & nvmsr) { + shp->dev_stat.pdt = PDT_SES; + shp->dev_stat.enc_serv = 1; + } else if (0x1 & nvmsr) { + shp->dev_stat.pdt = PDT_DISK; + shp->dev_stat.enc_serv = 0; + } else { + uint32_t nn = sg_get_unaligned_le32(up + 516); + + shp->dev_stat.pdt = nn ? PDT_DISK : PDT_UNKNOWN; + shp->dev_stat.enc_serv = 0; + } + break; + case 0x1: /* override to SES device */ + shp->dev_stat.pdt = PDT_SES; + shp->dev_stat.enc_serv = 1; + break; + case 0x2: /* override to disk with attached SES device */ + shp->dev_stat.pdt = PDT_DISK; + shp->dev_stat.enc_serv = 1; + break; + case 0x3: /* override to SAFTE device (PDT_PROCESSOR) */ + shp->dev_stat.pdt = PDT_PROCESSOR; + shp->dev_stat.enc_serv = 1; + break; + case 0xff: /* override to normal disk */ + shp->dev_stat.pdt = PDT_DISK; + shp->dev_stat.enc_serv = 0; + break; + default: + pr2ws("%s: unknown enclosure_override value: %d\n", __func__, + shp->dev_stat.enclosure_override); + break; + } +} + +/* Returns 0 on success; otherwise a positive value is returned */ +static int +sntl_cache_identity(struct sg_pt_win32_scsi * psp, struct sg_pt_handle * shp, + int time_secs, int vb) +{ + static const bool is_read = true; + const uint32_t pg_sz = sg_get_page_size(); + int ret; + uint8_t * up; + uint8_t * cmdp; + + up = sg_memalign(((pg_sz < 4096) ? 4096 : pg_sz), pg_sz, + &psp->free_nvme_id_ctlp, false); + psp->nvme_id_ctlp = up; + if (NULL == up) { + pr2ws("%s: sg_memalign() failed to get memory\n", __func__); + return -ENOMEM; + } + cmdp = psp->nvme_cmd; + memset(cmdp, 0, sizeof(psp->nvme_cmd)); + cmdp[0] = 0x6; /* Identify */ + /* leave nsid as 0, should it be broadcast (0xffffffff) ? */ + /* CNS=0x1 Identify controller: */ + sg_put_unaligned_le32(0x1, cmdp + SG_NVME_PT_CDW10); + sg_put_unaligned_le64((uint64_t)(sg_uintptr_t)up, cmdp + SG_NVME_PT_ADDR); + sg_put_unaligned_le32(pg_sz, cmdp + SG_NVME_PT_DATA_LEN); + ret = do_nvme_admin_cmd(psp, shp, cmdp, up, 4096, is_read, time_secs, + vb); + if (0 == ret) + sntl_check_enclosure_override(psp, shp, vb); + return ret; +} + + +static const char * nvme_scsi_vendor_str = "NVMe "; +static const uint16_t inq_resp_len = 36; + +static int +sntl_inq(struct sg_pt_win32_scsi * psp, struct sg_pt_handle * shp, + const uint8_t * cdbp, int time_secs, int vb) +{ + bool evpd; + bool cp_id_ctl = false; + int res; + uint16_t n, alloc_len, pg_cd; + const uint32_t pg_sz = sg_get_page_size(); + uint8_t * nvme_id_ns = NULL; + uint8_t * free_nvme_id_ns = NULL; + uint8_t inq_dout[256]; + uint8_t * cmdp; + + if (vb > 3) + pr2ws("%s: time_secs=%d\n", __func__, time_secs); + if (0x2 & cdbp[1]) { /* Reject CmdDt=1 */ + mk_sense_invalid_fld(psp, true, 1, 1, vb); + return 0; + } + if (NULL == psp->nvme_id_ctlp) { + res = sntl_cache_identity(psp, shp, time_secs, vb); + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(psp, vb); + return 0; + } else if (res) /* should be negative errno */ + return res; + } + memset(inq_dout, 0, sizeof(inq_dout)); + alloc_len = sg_get_unaligned_be16(cdbp + 3); + evpd = !!(0x1 & cdbp[1]); + pg_cd = cdbp[2]; + if (evpd) { /* VPD page responses */ + switch (pg_cd) { + case 0: + /* inq_dout[0] = (PQ=0)<<5 | (PDT=0); prefer pdt=0xd --> SES */ + inq_dout[1] = pg_cd; + n = 11; + sg_put_unaligned_be16(n - 4, inq_dout + 2); + inq_dout[4] = 0x0; + inq_dout[5] = 0x80; + inq_dout[6] = 0x83; + inq_dout[7] = 0x86; + inq_dout[8] = 0x87; + inq_dout[9] = 0x92; + inq_dout[n - 1] = SG_NVME_VPD_NICR; /* last VPD number */ + break; + case 0x80: + /* inq_dout[0] = (PQ=0)<<5 | (PDT=0); prefer pdt=0xd --> SES */ + inq_dout[1] = pg_cd; + n = 24; + sg_put_unaligned_be16(n - 4, inq_dout + 2); + memcpy(inq_dout + 4, psp->nvme_id_ctlp + 4, 20); /* SN */ + break; + case 0x83: + if ((psp->nvme_nsid > 0) && + (psp->nvme_nsid < SG_NVME_BROADCAST_NSID)) { + nvme_id_ns = sg_memalign(pg_sz, pg_sz, &free_nvme_id_ns, + false); + if (nvme_id_ns) { + cmdp = psp->nvme_cmd; + memset(cmdp, 0, sizeof(psp->nvme_cmd)); + cmdp[SG_NVME_PT_OPCODE] = 0x6; /* Identify */ + sg_put_unaligned_le32(psp->nvme_nsid, + cmdp + SG_NVME_PT_NSID); + /* CNS=0x0 Identify controller: */ + sg_put_unaligned_le32(0x0, cmdp + SG_NVME_PT_CDW10); + sg_put_unaligned_le64((uint64_t)(sg_uintptr_t)nvme_id_ns, + cmdp + SG_NVME_PT_ADDR); + sg_put_unaligned_le32(pg_sz, cmdp + SG_NVME_PT_DATA_LEN); + res = do_nvme_admin_cmd(psp, shp, cmdp, nvme_id_ns, pg_sz, + true, time_secs, vb > 3); + if (res) { + free(free_nvme_id_ns); + free_nvme_id_ns = NULL; + nvme_id_ns = NULL; + } + } + } + n = sg_make_vpd_devid_for_nvme(psp->nvme_id_ctlp, nvme_id_ns, + 0 /* pdt */, -1 /*tproto */, + inq_dout, sizeof(inq_dout)); + if (n > 3) + sg_put_unaligned_be16(n - 4, inq_dout + 2); + if (free_nvme_id_ns) { + free(free_nvme_id_ns); + free_nvme_id_ns = NULL; + nvme_id_ns = NULL; + } + break; + case 0x86: /* Extended INQUIRY (per SFS SPC Discovery 2016) */ + inq_dout[1] = pg_cd; + n = 64; + sg_put_unaligned_be16(n - 4, inq_dout + 2); + inq_dout[5] = 0x1; /* SIMPSUP=1 */ + inq_dout[7] = 0x1; /* LUICLR=1 */ + inq_dout[13] = 0x40; /* max supported sense data length */ + break; + case 0x87: /* Mode page policy (per SFS SPC Discovery 2016) */ + inq_dout[1] = pg_cd; + n = 8; + sg_put_unaligned_be16(n - 4, inq_dout + 2); + inq_dout[4] = 0x3f; /* all mode pages */ + inq_dout[5] = 0xff; /* and their sub-pages */ + inq_dout[6] = 0x80; /* MLUS=1, policy=shared */ + break; + case 0x92: /* SCSI Feature set: only SPC Discovery 2016 */ + inq_dout[1] = pg_cd; + n = 10; + sg_put_unaligned_be16(n - 4, inq_dout + 2); + inq_dout[9] = 0x1; /* SFS SPC Discovery 2016 */ + break; + case SG_NVME_VPD_NICR: /* 0xde */ + inq_dout[1] = pg_cd; + sg_put_unaligned_be16((16 + 4096) - 4, inq_dout + 2); + n = 16 + 4096; + cp_id_ctl = true; + break; + default: /* Point to page_code field in cdb */ + mk_sense_invalid_fld(psp, true, 2, 7, vb); + return 0; + } + if (alloc_len > 0) { + n = (alloc_len < n) ? alloc_len : n; + n = (n < psp->dxfer_len) ? n : psp->dxfer_len; + psp->resid = psp->dxfer_len - n; + if (n > 0) { + if (cp_id_ctl) { + memcpy(psp->dxferp, inq_dout, (n < 16 ? n : 16)); + if (n > 16) + memcpy(psp->dxferp + 16, + psp->nvme_id_ctlp, n - 16); + } else + memcpy(psp->dxferp, inq_dout, n); + } + } + } else { /* Standard INQUIRY response */ + /* pdt=0 --> disk; pdt=0xd --> SES; pdt=3 --> processor (safte) */ + inq_dout[0] = (0x1f & shp->dev_stat.pdt); /* (PQ=0)<<5 */ + /* inq_dout[1] = (RMD=0)<<7 | (LU_CONG=0)<<6; rest reserved */ + inq_dout[2] = 6; /* version: SPC-4 */ + inq_dout[3] = 2; /* NORMACA=0, HISUP=0, response data format: 2 */ + inq_dout[4] = 31; /* so response length is (or could be) 36 bytes */ + inq_dout[6] = shp->dev_stat.enc_serv ? 0x40 : 0; + inq_dout[7] = 0x2; /* CMDQUE=1 */ + memcpy(inq_dout + 8, nvme_scsi_vendor_str, 8); /* NVMe not Intel */ + memcpy(inq_dout + 16, psp->nvme_id_ctlp + 24, 16); /* Prod <-- MN */ + memcpy(inq_dout + 32, psp->nvme_id_ctlp + 64, 4); /* Rev <-- FR */ + if (alloc_len > 0) { + n = (alloc_len < inq_resp_len) ? alloc_len : inq_resp_len; + n = (n < psp->dxfer_len) ? n : psp->dxfer_len; + psp->resid = psp->dxfer_len - n; + if (n > 0) + memcpy(psp->dxferp, inq_dout, n); + } + } + return 0; +} + +static int +sntl_rluns(struct sg_pt_win32_scsi * psp, struct sg_pt_handle * shp, + const uint8_t * cdbp, int time_secs, int vb) +{ + int res; + uint16_t sel_report; + uint32_t alloc_len, k, n, num, max_nsid; + uint8_t * rl_doutp; + uint8_t * up; + + if (vb > 3) + pr2ws("%s: time_secs=%d\n", __func__, time_secs); + + sel_report = cdbp[2]; + alloc_len = sg_get_unaligned_be32(cdbp + 6); + if (NULL == psp->nvme_id_ctlp) { + res = sntl_cache_identity(psp, shp, time_secs, vb); + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(psp, vb); + return 0; + } else if (res) + return res; + } + max_nsid = sg_get_unaligned_le32(psp->nvme_id_ctlp + 516); + switch (sel_report) { + case 0: + case 2: + num = max_nsid; + break; + case 1: + case 0x10: + case 0x12: + num = 0; + break; + case 0x11: + num = (1 == psp->nvme_nsid) ? max_nsid : 0; + break; + default: + if (vb > 1) + pr2ws("%s: bad select_report value: 0x%x\n", __func__, + sel_report); + mk_sense_invalid_fld(psp, true, 2, 7, vb); + return 0; + } + rl_doutp = (uint8_t *)calloc(num + 1, 8); + if (NULL == rl_doutp) { + pr2ws("%s: calloc() failed to get memory\n", __func__); + return -ENOMEM; + } + for (k = 0, up = rl_doutp + 8; k < num; ++k, up += 8) + sg_put_unaligned_be16(k, up); + n = num * 8; + sg_put_unaligned_be32(n, rl_doutp); + n+= 8; + if (alloc_len > 0) { + n = (alloc_len < n) ? alloc_len : n; + n = (n < psp->dxfer_len) ? n : psp->dxfer_len; + psp->resid = psp->dxfer_len - n; + if (n > 0) + memcpy(psp->dxferp, rl_doutp, n); + } + res = 0; + free(rl_doutp); + return res; +} + +static int +sntl_tur(struct sg_pt_win32_scsi * psp, struct sg_pt_handle * shp, + int time_secs, int vb) +{ + int res; + uint32_t pow_state; + uint8_t * cmdp; + + if (vb > 4) + pr2ws("%s: enter\n", __func__); + if (NULL == psp->nvme_id_ctlp) { + res = sntl_cache_identity(psp, shp, time_secs, vb); + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(psp, vb); + return 0; + } else if (res) + return res; + } + cmdp = psp->nvme_cmd; + memset(cmdp, 0, sizeof(psp->nvme_cmd)); + cmdp[SG_NVME_PT_OPCODE] = 0xa; /* Get features */ + sg_put_unaligned_le32(SG_NVME_BROADCAST_NSID, cmdp + SG_NVME_PT_NSID); + /* SEL=0 (current), Feature=2 Power Management */ + sg_put_unaligned_le32(0x2, cmdp + SG_NVME_PT_CDW10); + res = do_nvme_admin_cmd(psp, shp, cmdp, NULL, 0, false, time_secs, vb); + if (0 != res) { + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(psp, vb); + return 0; + } else + return res; + } else { + psp->os_err = 0; + psp->nvme_status = 0; + } + pow_state = (0x1f & psp->nvme_result); + if (vb > 3) + pr2ws("%s: pow_state=%u\n", __func__, pow_state); +#if 0 /* pow_state bounces around too much on laptop */ + if (pow_state) + mk_sense_asc_ascq(psp, SPC_SK_NOT_READY, LOW_POWER_COND_ON_ASC, 0, + vb); +#endif + return 0; +} + +static int +sntl_req_sense(struct sg_pt_win32_scsi * psp, struct sg_pt_handle * shp, + const uint8_t * cdbp, int time_secs, int vb) +{ + bool desc; + int res; + uint32_t pow_state, alloc_len, n; + uint8_t rs_dout[64]; + uint8_t * cmdp; + + if (vb > 3) + pr2ws("%s: time_secs=%d\n", __func__, time_secs); + if (NULL == psp->nvme_id_ctlp) { + res = sntl_cache_identity(psp, shp, time_secs, vb); + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(psp, vb); + return 0; + } else if (res) + return res; + } + desc = !!(0x1 & cdbp[1]); + alloc_len = cdbp[4]; + cmdp = psp->nvme_cmd; + memset(cmdp, 0, sizeof(psp->nvme_cmd)); + cmdp[SG_NVME_PT_OPCODE] = 0xa; /* Get features */ + sg_put_unaligned_le32(SG_NVME_BROADCAST_NSID, cmdp + SG_NVME_PT_NSID); + /* SEL=0 (current), Feature=2 Power Management */ + sg_put_unaligned_le32(0x2, cmdp + SG_NVME_PT_CDW10); + res = do_nvme_admin_cmd(psp, shp, cmdp, NULL, 0, false, time_secs, vb); + if (0 != res) { + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(psp, vb); + return 0; + } else + return res; + } else { + psp->os_err = 0; + psp->nvme_status = 0; + } + psp->sense_resid = psp->sense_len; + pow_state = (0x1f & psp->nvme_result); + if (vb > 3) + pr2ws("%s: pow_state=%u\n", __func__, pow_state); + memset(rs_dout, 0, sizeof(rs_dout)); + if (pow_state) + sg_build_sense_buffer(desc, rs_dout, SPC_SK_NO_SENSE, + LOW_POWER_COND_ON_ASC, 0); + else + sg_build_sense_buffer(desc, rs_dout, SPC_SK_NO_SENSE, + NO_ADDITIONAL_SENSE, 0); + n = desc ? 8 : 18; + n = (n < alloc_len) ? n : alloc_len; + n = (n < psp->dxfer_len) ? n : psp->dxfer_len; + psp->resid = psp->dxfer_len - n; + if (n > 0) + memcpy(psp->dxferp, rs_dout, n); + return 0; +} + +static int +sntl_mode_ss(struct sg_pt_win32_scsi * psp, struct sg_pt_handle * shp, + const uint8_t * cdbp, int time_secs, int vb) +{ + bool is_msense = (SCSI_MODE_SENSE10_OPC == cdbp[0]); + int res, n, len; + uint8_t * bp; + struct sg_sntl_result_t sntl_result; + + if (vb > 3) + pr2ws("%s: mse%s, time_secs=%d\n", __func__, + (is_msense ? "nse" : "lect"), time_secs); + if (NULL == psp->nvme_id_ctlp) { + res = sntl_cache_identity(psp, shp, time_secs, vb); + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(psp, vb); + return 0; + } else if (res) + return res; + } + if (is_msense) { /* MODE SENSE(10) */ + len = psp->dxfer_len; + bp = psp->dxferp; + n = sntl_resp_mode_sense10(&shp->dev_stat, cdbp, bp, len, + &sntl_result); + psp->resid = (n >= 0) ? len - n : len; + } else { /* MODE SELECT(10) */ + uint8_t pre_enc_ov = shp->dev_stat.enclosure_override; + + len = psp->dxfer_len; + bp = psp->dxferp; + n = sntl_resp_mode_select10(&shp->dev_stat, cdbp, bp, len, + &sntl_result); + if (pre_enc_ov != shp->dev_stat.enclosure_override) + sntl_check_enclosure_override(psp, shp, vb); /* ENC_OV changed */ + } + if (n < 0) { + int in_bit = (255 == sntl_result.in_bit) ? (int)sntl_result.in_bit : + -1; + if ((SAM_STAT_CHECK_CONDITION == sntl_result.sstatus) && + (SPC_SK_ILLEGAL_REQUEST == sntl_result.sk)) { + if (INVALID_FIELD_IN_CDB == sntl_result.asc) + mk_sense_invalid_fld(psp, true, sntl_result.in_byte, in_bit, + vb); + else if (INVALID_FIELD_IN_PARAM_LIST == sntl_result.asc) + mk_sense_invalid_fld(psp, false, sntl_result.in_byte, in_bit, + vb); + else + mk_sense_asc_ascq(psp, sntl_result.sk, sntl_result.asc, + sntl_result.ascq, vb); + } else + pr2ws("%s: error but no sense?? n=%d\n", __func__, n); + } + return 0; +} + +/* This is not really a SNTL. For SCSI SEND DIAGNOSTIC(PF=1) NVMe-MI + * has a special command (SES Send) to tunnel through pages to an + * enclosure. The NVMe enclosure is meant to understand the SES + * (SCSI Enclosure Services) use of diagnostics pages that are + * related to SES. */ +static int +sntl_senddiag(struct sg_pt_win32_scsi * psp, struct sg_pt_handle * shp, + const uint8_t * cdbp, int time_secs, int vb) +{ + bool pf, self_test; + int res; + uint8_t st_cd, dpg_cd; + uint32_t alloc_len, n, dout_len, dpg_len, nvme_dst; + uint8_t * dop; + uint8_t * cmdp; + + st_cd = 0x7 & (cdbp[1] >> 5); + self_test = !! (0x4 & cdbp[1]); + pf = !! (0x10 & cdbp[1]); + if (vb > 3) + pr2ws("%s: pf=%d, self_test=%d (st_code=%d)\n", __func__, (int)pf, + (int)self_test, (int)st_cd); + cmdp = psp->nvme_cmd; + if (self_test || st_cd) { + memset(cmdp, 0, sizeof(psp->nvme_cmd)); + cmdp[SG_NVME_PT_OPCODE] = 0x14; /* Device self-test */ + /* just this namespace (if there is one) and controller */ + sg_put_unaligned_le32(psp->nvme_nsid, cmdp + SG_NVME_PT_NSID); + switch (st_cd) { + case 0: /* Here if self_test is set, do short self-test */ + case 1: /* Background short */ + case 5: /* Foreground short */ + nvme_dst = 1; + break; + case 2: /* Background extended */ + case 6: /* Foreground extended */ + nvme_dst = 2; + break; + case 4: /* Abort self-test */ + nvme_dst = 0xf; + break; + default: + pr2ws("%s: bad self-test code [0x%x]\n", __func__, st_cd); + mk_sense_invalid_fld(psp, true, 1, 7, vb); + return 0; + } + sg_put_unaligned_le32(nvme_dst, cmdp + SG_NVME_PT_CDW10); + res = do_nvme_admin_cmd(psp, shp, cmdp, NULL, 0, false, time_secs, + vb); + if (0 != res) { + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(psp, vb); + return 0; + } else + return res; + } + } + alloc_len = sg_get_unaligned_be16(cdbp + 3); /* parameter list length */ + dout_len = psp->dxfer_len; + if (pf) { + if (0 == alloc_len) { + mk_sense_invalid_fld(psp, true, 3, 7, vb); + if (vb) + pr2ws("%s: PF bit set bit param_list_len=0\n", __func__); + return 0; + } + } else { /* PF bit clear */ + if (alloc_len) { + mk_sense_invalid_fld(psp, true, 3, 7, vb); + if (vb) + pr2ws("%s: param_list_len>0 but PF clear\n", __func__); + return 0; + } else + return 0; /* nothing to do */ + if (dout_len > 0) { + if (vb) + pr2ws("%s: dout given but PF clear\n", __func__); + return SCSI_PT_DO_BAD_PARAMS; + } + } + if (dout_len < 4) { + if (vb) + pr2ws("%s: dout length (%u bytes) too short\n", __func__, + dout_len); + return SCSI_PT_DO_BAD_PARAMS; + } + n = dout_len; + n = (n < alloc_len) ? n : alloc_len; + dop = psp->dxferp; + if (! sg_is_aligned(dop, 0)) { /* page aligned ? */ + if (vb) + pr2ws("%s: dout [0x%" PRIx64 "] not page aligned\n", __func__, + (uint64_t)(sg_uintptr_t)psp->dxferp); + return SCSI_PT_DO_BAD_PARAMS; + } + dpg_cd = dop[0]; + dpg_len = sg_get_unaligned_be16(dop + 2) + 4; + /* should we allow for more than one D_PG is dout ?? */ + n = (n < dpg_len) ? n : dpg_len; /* not yet ... */ + + if (vb) + pr2ws("%s: passing through d_pg=0x%x, len=%u to NVME_MI SES send\n", + __func__, dpg_cd, dpg_len); + memset(cmdp, 0, sizeof(psp->nvme_cmd)); + cmdp[SG_NVME_PT_OPCODE] = 0x1d; /* MI Send */ + /* And 0x1d is same opcode as the SCSI SEND DIAGNOSTIC command */ + sg_put_unaligned_le64((uint64_t)(sg_uintptr_t)dop, + cmdp + SG_NVME_PT_ADDR); + /* NVMe 4k page size. Maybe determine this? */ + /* N.B. Maybe n > 0x1000, is this a problem?? */ + sg_put_unaligned_le32(0x1000, cmdp + SG_NVME_PT_DATA_LEN); + /* NVMe Message Header */ + sg_put_unaligned_le32(0x0804, cmdp + SG_NVME_PT_CDW10); + /* NVME-MI SES Send; (0x8 -> NVME-MI SES Receive) */ + sg_put_unaligned_le32(0x9, cmdp + SG_NVME_PT_CDW11); + /* 'n' is number of bytes SEND DIAGNOSTIC dpage */ + sg_put_unaligned_le32(n, cmdp + SG_NVME_PT_CDW13); + res = do_nvme_admin_cmd(psp, shp, cmdp, dop, n, false, time_secs, vb); + if (0 != res) { + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(psp, vb); + return 0; + } + } + return res; +} + +/* This is not really a SNTL. For SCSI RECEIVE DIAGNOSTIC RESULTS(PCV=1) + * NVMe-MI has a special command (SES Receive) to read pages through a + * tunnel from an enclosure. The NVMe enclosure is meant to understand the + * SES (SCSI Enclosure Services) use of diagnostics pages that are + * related to SES. */ +static int +sntl_recvdiag(struct sg_pt_win32_scsi * psp, struct sg_pt_handle * shp, + const uint8_t * cdbp, int time_secs, int vb) +{ + bool pcv; + int res; + uint8_t dpg_cd; + uint32_t alloc_len, n, din_len; + uint8_t * dip; + uint8_t * cmdp; + + pcv = !! (0x1 & cdbp[1]); + dpg_cd = cdbp[2]; + alloc_len = sg_get_unaligned_be16(cdbp + 3); /* parameter list length */ + if (vb > 3) + pr2ws("%s: dpg_cd=0x%x, pcv=%d, alloc_len=0x%x\n", __func__, + dpg_cd, (int)pcv, alloc_len); + din_len = psp->dxfer_len; + n = (din_len < alloc_len) ? din_len : alloc_len; + dip = psp->dxferp; + if (! sg_is_aligned(dip, 0)) { /* page aligned ? */ + if (vb) + pr2ws("%s: din [0x%" PRIx64 "] not page aligned\n", __func__, + (uint64_t)(sg_uintptr_t)psp->dxferp); + return SCSI_PT_DO_BAD_PARAMS; + } + + if (vb) + pr2ws("%s: expecting d_pg=0x%x from NVME_MI SES receive\n", __func__, + dpg_cd); + cmdp = psp->nvme_cmd; + memset(cmdp, 0, sizeof(psp->nvme_cmd)); + cmdp[SG_NVME_PT_OPCODE] = 0x1e; /* MI Receive */ + sg_put_unaligned_le64((uint64_t)(sg_uintptr_t)dip, + cmdp + SG_NVME_PT_ADDR); + /* NVMe 4k page size. Maybe determine this? */ + /* N.B. Maybe n > 0x1000, is this a problem?? */ + sg_put_unaligned_le32(0x1000, cmdp + SG_NVME_PT_DATA_LEN); + /* NVMe Message Header */ + sg_put_unaligned_le32(0x0804, cmdp + SG_NVME_PT_CDW10); + /* NVME-MI SES Receive */ + sg_put_unaligned_le32(0x8, cmdp + SG_NVME_PT_CDW11); + /* Diagnostic page code */ + sg_put_unaligned_le32(dpg_cd, cmdp + SG_NVME_PT_CDW12); + /* 'n' is number of bytes expected in diagnostic page */ + sg_put_unaligned_le32(n, cmdp + SG_NVME_PT_CDW13); + res = do_nvme_admin_cmd(psp, shp, cmdp, dip, n, true, time_secs, vb); + if (0 != res) { + if (SG_LIB_NVME_STATUS == res) { + mk_sense_from_nvme_status(psp, vb); + return 0; + } else + return res; + } + psp->resid = din_len - n; + return res; +} + +#define F_SA_LOW 0x80 /* cdb byte 1, bits 4 to 0 */ +#define F_SA_HIGH 0x100 /* as used by variable length cdbs */ +#define FF_SA (F_SA_HIGH | F_SA_LOW) +#define F_INV_OP 0x200 + +static int +sntl_rep_opcodes(struct sg_pt_win32_scsi * psp, struct sg_pt_handle * shp, + const uint8_t * cdbp, int time_secs, int vb) +{ + bool rctd; + uint8_t reporting_opts, req_opcode, supp; + uint16_t req_sa, u; + uint32_t alloc_len, offset, a_len; + const uint32_t pg_sz = sg_get_page_size(); + int k, len, count, bump; + const struct sg_opcode_info_t *oip; + uint8_t *arr; + uint8_t *free_arr; + + if (vb > 3) + pr2ws("%s: time_secs=%d\n", __func__, time_secs); + if (shp) { ; } /* suppress warning */ + rctd = !!(cdbp[2] & 0x80); /* report command timeout desc. */ + reporting_opts = cdbp[2] & 0x7; + req_opcode = cdbp[3]; + req_sa = sg_get_unaligned_be16(cdbp + 4); + alloc_len = sg_get_unaligned_be32(cdbp + 6); + if (alloc_len < 4 || alloc_len > 0xffff) { + mk_sense_invalid_fld(psp, true, 6, -1, vb); + return 0; + } + a_len = pg_sz - 72; + arr = sg_memalign(pg_sz, pg_sz, &free_arr, false); + if (NULL == arr) { + pr2ws("%s: sg_memalign() failed to get memory\n", __func__); + return -ENOMEM; + } + switch (reporting_opts) { + case 0: /* all commands */ + count = 0; + bump = rctd ? 20 : 8; + for (offset = 4, oip = sg_get_opcode_translation(); + (oip->flags != 0xffff) && (offset < a_len); ++oip) { + if (F_INV_OP & oip->flags) + continue; + ++count; + arr[offset] = oip->opcode; + sg_put_unaligned_be16(oip->sa, arr + offset + 2); + if (rctd) + arr[offset + 5] |= 0x2; + if (FF_SA & oip->flags) + arr[offset + 5] |= 0x1; + sg_put_unaligned_be16(oip->len_mask[0], arr + offset + 6); + if (rctd) + sg_put_unaligned_be16(0xa, arr + offset + 8); + offset += bump; + } + sg_put_unaligned_be32(count * bump, arr + 0); + break; + case 1: /* one command: opcode only */ + case 2: /* one command: opcode plus service action */ + case 3: /* one command: if sa==0 then opcode only else opcode+sa */ + for (oip = sg_get_opcode_translation(); oip->flags != 0xffff; ++oip) { + if ((req_opcode == oip->opcode) && (req_sa == oip->sa)) + break; + } + if ((0xffff == oip->flags) || (F_INV_OP & oip->flags)) { + supp = 1; + offset = 4; + } else { + if (1 == reporting_opts) { + if (FF_SA & oip->flags) { + mk_sense_invalid_fld(psp, true, 2, 2, vb); + free(free_arr); + return 0; + } + req_sa = 0; + } else if ((2 == reporting_opts) && 0 == (FF_SA & oip->flags)) { + mk_sense_invalid_fld(psp, true, 4, -1, vb); + free(free_arr); + return 0; + } + if ((0 == (FF_SA & oip->flags)) && (req_opcode == oip->opcode)) + supp = 3; + else if (0 == (FF_SA & oip->flags)) + supp = 1; + else if (req_sa != oip->sa) + supp = 1; + else + supp = 3; + if (3 == supp) { + u = oip->len_mask[0]; + sg_put_unaligned_be16(u, arr + 2); + arr[4] = oip->opcode; + for (k = 1; k < u; ++k) + arr[4 + k] = (k < 16) ? + oip->len_mask[k] : 0xff; + offset = 4 + u; + } else + offset = 4; + } + arr[1] = (rctd ? 0x80 : 0) | supp; + if (rctd) { + sg_put_unaligned_be16(0xa, arr + offset); + offset += 12; + } + break; + default: + mk_sense_invalid_fld(psp, true, 2, 2, vb); + free(free_arr); + return 0; + } + offset = (offset < a_len) ? offset : a_len; + len = (offset < alloc_len) ? offset : alloc_len; + psp->resid = psp->dxfer_len - len; + if (len > 0) + memcpy(psp->dxferp, arr, len); + free(free_arr); + return 0; +} + +static int +sntl_rep_tmfs(struct sg_pt_win32_scsi * psp, struct sg_pt_handle * shp, + const uint8_t * cdbp, int time_secs, int vb) +{ + bool repd; + uint32_t alloc_len, len; + uint8_t arr[16]; + + if (vb > 3) + pr2ws("%s: time_secs=%d\n", __func__, time_secs); + if (shp) { ; } /* suppress warning */ + memset(arr, 0, sizeof(arr)); + repd = !!(cdbp[2] & 0x80); + alloc_len = sg_get_unaligned_be32(cdbp + 6); + if (alloc_len < 4) { + mk_sense_invalid_fld(psp, true, 6, -1, vb); + return 0; + } + arr[0] = 0xc8; /* ATS | ATSS | LURS */ + arr[1] = 0x1; /* ITNRS */ + if (repd) { + arr[3] = 0xc; + len = 16; + } else + len = 4; + + len = (len < alloc_len) ? len : alloc_len; + psp->resid = psp->dxfer_len - len; + if (len > 0) + memcpy(psp->dxferp, arr, len); + 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 +nvme_pt(struct sg_pt_win32_scsi * psp, struct sg_pt_handle * shp, + int time_secs, int vb) +{ + bool scsi_cdb = false; + uint32_t cmd_len = 0; + uint16_t sa; + const uint8_t * cdbp = NULL; + + if (psp->have_nvme_cmd) { + cdbp = psp->nvme_cmd; + cmd_len = 64; + psp->nvme_direct = true; + } else if (spt_direct) { + if (psp->swb_d.spt.CdbLength > 0) { + cdbp = psp->swb_d.spt.Cdb; + cmd_len = psp->swb_d.spt.CdbLength; + scsi_cdb = true; + psp->nvme_direct = false; + } + } else { + if (psp->swb_i.spt.CdbLength > 0) { + cdbp = psp->swb_i.spt.Cdb; + cmd_len = psp->swb_i.spt.CdbLength; + scsi_cdb = true; + psp->nvme_direct = false; + } + } + if (NULL == cdbp) { + if (vb) + pr2ws("%s: Missing NVMe or SCSI command (set_scsi_pt_cdb())" + " cmd_len=%u\n", __func__, cmd_len); + return SCSI_PT_DO_BAD_PARAMS; + } + if (vb > 3) + pr2ws("%s: opcode=0x%x, cmd_len=%u, fdev_name: %s, dlen=%u\n", + __func__, cdbp[0], cmd_len, shp->dname, psp->dxfer_len); + /* direct NVMe command (i.e. 64 bytes long) or SNTL */ + if (scsi_cdb) { + switch (cdbp[0]) { + case SCSI_INQUIRY_OPC: + return sntl_inq(psp, shp, cdbp, time_secs, vb); + case SCSI_REPORT_LUNS_OPC: + return sntl_rluns(psp, shp, cdbp, time_secs, vb); + case SCSI_TEST_UNIT_READY_OPC: + return sntl_tur(psp, shp, time_secs, vb); + case SCSI_REQUEST_SENSE_OPC: + return sntl_req_sense(psp, shp, cdbp, time_secs, vb); + case SCSI_SEND_DIAGNOSTIC_OPC: + return sntl_senddiag(psp, shp, cdbp, time_secs, vb); + case SCSI_RECEIVE_DIAGNOSTIC_OPC: + return sntl_recvdiag(psp, shp, cdbp, time_secs, vb); + case SCSI_MODE_SENSE10_OPC: + case SCSI_MODE_SELECT10_OPC: + return sntl_mode_ss(psp, shp, cdbp, time_secs, vb); + case SCSI_MAINT_IN_OPC: + sa = 0x1f & cdbp[1]; /* service action */ + if (SCSI_REP_SUP_OPCS_OPC == sa) + return sntl_rep_opcodes(psp, shp, cdbp, time_secs, + vb); + else if (SCSI_REP_SUP_TMFS_OPC == sa) + return sntl_rep_tmfs(psp, shp, cdbp, time_secs, vb); + /* fall through */ + default: + if (vb > 2) { + char b[64]; + + sg_get_command_name(cdbp, -1, sizeof(b), b); + pr2ws("%s: no translation to NVMe for SCSI %s command\n", + __func__, b); + } + mk_sense_asc_ascq(psp, SPC_SK_ILLEGAL_REQUEST, INVALID_OPCODE, + 0, vb); + return 0; + } + } + if(psp->dxfer_len > 0) { + uint8_t * cmdp = psp->nvme_cmd; + + sg_put_unaligned_le32(psp->dxfer_len, cmdp + SG_NVME_PT_DATA_LEN); + sg_put_unaligned_le64((uint64_t)(sg_uintptr_t)psp->dxferp, + cmdp + SG_NVME_PT_ADDR); + if (vb > 2) + pr2ws("%s: NVMe command, dlen=%u, dxferp=0x%p\n", __func__, + psp->dxfer_len, psp->dxferp); + } + return do_nvme_admin_cmd(psp, shp, NULL, NULL, 0, true, time_secs, vb); +} + +#else /* (HAVE_NVME && (! IGNORE_NVME)) */ + +static int +nvme_pt(struct sg_pt_win32_scsi * psp, struct sg_pt_handle * shp, + int time_secs, int vb) +{ + if (vb) + pr2ws("%s: not supported [time_secs=%d]\n", __func__, time_secs); + if (psp) { ; } /* suppress warning */ + if (shp) { ; } /* suppress warning */ + return -ENOTTY; /* inappropriate ioctl error */ +} + +#endif /* (HAVE_NVME && (! IGNORE_NVME)) */ + +int +do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int verbose) +{ + if (vp) { } + if (submq) { } + if (timeout_secs) { } + if (verbose) { } + return SCSI_PT_DO_NOT_SUPPORTED; +} diff -Nru sdparm-1.10/Makefile.in sdparm-1.12/Makefile.in --- sdparm-1.10/Makefile.in 2015-12-02 00:45:13.000000000 +0000 +++ sdparm-1.12/Makefile.in 2020-12-25 01:49:20.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2014 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -134,7 +134,7 @@ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ - cscope distdir dist dist-all distcheck + cscope distdir distdir-am dist dist-all distcheck am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ $(LISP)config.h.in # Read a list of newline-separated strings from the standard input, @@ -158,8 +158,8 @@ CSCOPE = cscope DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in AUTHORS \ - COPYING ChangeLog INSTALL NEWS README compile config.guess \ - config.sub depcomp install-sh missing + COPYING ChangeLog INSTALL NEWS README ar-lib compile \ + config.guess config.sub depcomp install-sh missing DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) @@ -205,6 +205,7 @@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -212,7 +213,6 @@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ -CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ @@ -254,6 +254,7 @@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ @@ -288,13 +289,12 @@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ -os_cflags = @os_cflags@ os_deps = @os_deps@ -os_libs = @os_libs@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ @@ -315,23 +315,23 @@ @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ - echo ' cd $(srcdir) && $(AUTOMAKE) --gnu'; \ - $(am__cd) $(srcdir) && $(AUTOMAKE) --gnu \ + echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ + $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ && exit 0; \ exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu Makefile + $(AUTOMAKE) --foreign Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -464,7 +464,10 @@ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -rm -f cscope.out cscope.in.out cscope.po.out cscope.files -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ @@ -529,7 +532,7 @@ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir - tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz $(am__post_remove_distdir) dist-bzip2: distdir @@ -555,7 +558,7 @@ @echo WARNING: "Support for shar distribution archives is" \ "deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 - shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz $(am__post_remove_distdir) dist-zip: distdir @@ -573,7 +576,7 @@ distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ - GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ + eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lz*) \ @@ -583,7 +586,7 @@ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ - GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ + eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ esac diff -Nru sdparm-1.10/missing sdparm-1.12/missing --- sdparm-1.10/missing 2015-12-02 00:45:13.000000000 +0000 +++ sdparm-1.12/missing 2020-12-25 01:49:20.000000000 +0000 @@ -1,9 +1,9 @@ #! /bin/sh # Common wrapper for a few potentially missing GNU programs. -scriptversion=2013-10-28.13; # UTC +scriptversion=2018-03-07.03; # UTC -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify @@ -17,7 +17,7 @@ # GNU General Public License for more details. # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -101,9 +101,9 @@ exit $st fi -perl_URL=http://www.perl.org/ -flex_URL=http://flex.sourceforge.net/ -gnu_software_URL=http://www.gnu.org/software +perl_URL=https://www.perl.org/ +flex_URL=https://github.com/westes/flex +gnu_software_URL=https://www.gnu.org/software program_details () { @@ -207,9 +207,9 @@ exit $st # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" +# time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: diff -Nru sdparm-1.10/README sdparm-1.12/README --- sdparm-1.10/README 2016-02-22 22:45:10.000000000 +0000 +++ sdparm-1.12/README 2021-04-21 22:25:59.000000000 +0000 @@ -1,26 +1,26 @@ Introduction ------------ sdparm is a utility for listing and potentially changing SCSI disk -parameters. More generally it can be used on any device that uses -a SCSI command set. Apart form SCSI disks, examples of devices that -use SCSI command sets are ATAPI CD/DVD drives, SCSI and ATAPI tape -drives and SCSI enclosures. +parameters. More generally it can be used on any device that uses a SCSI +command set. Apart form SCSI disks, examples of devices that use SCSI command +sets are ATAPI CD/DVD drives, SCSI and ATAPI tape drives and SCSI enclosures. -This utility was originally written for Linux. It has been ported -to FreeBSD, Solaris, Tru64, and Windows. +This utility was originally written for Linux. It has been ported to FreeBSD, +Solaris, Tru64, and Windows. Relationship to sg3_utils ------------------------- -This package shares code with sg3_utils (version 1.33). With the subversion -revision control system this is done by having sdparm's "include/" and "lib/" -subdirectories pointing to the correspondingly named directories in the -sg3_utils package using the "svn:externals" property. These two "external" -directories include more files than sdparm uses. The excess files include -"lib/Makefile.am" and "lib/Makefile.in". The "Makefile.am" in sdparm's "src/" -directory does the main part of the build. When the tarball is generated for -this utility, various files are "exported" out of the subversion repository -and "svn:externals" redirection is no longer visible (but the unused files -are visible). +Starting with sdparm version 1.02 this package shares code with sg3_utils +(start with sg3_utils version 1.25). With the subversion revision control +system this is done by having sdparm's "include/" and "lib/" subdirectories +pointing to the correspondingly named directories in the sg3_utils package +using the "svn:externals" property. These two "external" directories include +more files than sdparm uses. The excess files include "lib/Makefile.am" and +"lib/Makefile.in". The "Makefile.am" in sdparm's "src/" directory does the +main part of the build. When the tarball is generated for this utility, +various files are "exported" out of the subversion repository and +"svn:externals" redirection is no longer visible (but the unused files are +visible). The sdparm executable may or may not be built depending on the libsgutils (shared) library. Currently the ./configure rules are looking for a library @@ -40,11 +40,11 @@ installed. Prior to installation the man page can be viewed from this package's main directory with "man doc/sdparm.8". -There is a web page about this utility at http://sg.danny.cz/sg/sdparm.html . +There is a web page about this utility at https://sg.danny.cz/sg/sdparm.html . Build infrastructure -------------------- -This packages uses the automake and autoconf tools. The generating files +This package uses the automake and autoconf tools. The generating files (scripts) are configure.ac, Makefile.am, src/Makefile.am and autogen.sh . The autogen.sh script only needs to be executed if one of the other generating files in the above list is changed. @@ -79,9 +79,9 @@ Naturally credit and improvement/bug feedback are encouraged. The part of this code that others may be able to re-use is the information in the tables in sdparm_data.c , sg_lib.c and sg_lib_data.c . This is -information garnered from SCSI drafts and standards at http://www.t10.org +information garnered from SCSI drafts and standards at https://www.t10.org (plus some information from the ATA drafts and standards at -http://www.t13.org ). Vendor specific mode page information is found in the +https://www.t13.org ). Vendor specific mode page information is found in the sdparm_data_vendor.c file and is derived from vendor product manuals. Notes @@ -246,7 +246,7 @@ SCSI1:0,0,0 claimed=1 pdt=5h MATSHITA DVD/CDRW UJDA775 CB03 And finally here is a more interesting example showing disks with no -Windows (2000) volumes, a tape drive and a wierd unclaimed SCSI +Windows (2000) volumes, a tape drive and a weird unclaimed SCSI pseudo device with BCC (Bridge Controller Commands) peripheral device type. $ sdparm -www @@ -267,4 +267,4 @@ Douglas Gilbert -22nd February 2016 +16th April 2021 diff -Nru sdparm-1.10/scripts/Makefile.in sdparm-1.12/scripts/Makefile.in --- sdparm-1.10/scripts/Makefile.in 2015-12-02 00:45:13.000000000 +0000 +++ sdparm-1.12/scripts/Makefile.in 2020-12-25 01:49:20.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2014 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -152,6 +152,7 @@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -159,7 +160,6 @@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ -CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ @@ -201,6 +201,7 @@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ @@ -235,13 +236,12 @@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ -os_cflags = @os_cflags@ os_deps = @os_deps@ -os_libs = @os_libs@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ @@ -268,16 +268,16 @@ exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu scripts/Makefile'; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign scripts/Makefile'; \ $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu scripts/Makefile + $(AUTOMAKE) --foreign scripts/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -330,7 +330,10 @@ cscope cscopelist: -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ diff -Nru sdparm-1.10/scripts/sas_disk_blink sdparm-1.12/scripts/sas_disk_blink --- sdparm-1.10/scripts/sas_disk_blink 2013-05-14 18:27:22.000000000 +0000 +++ sdparm-1.12/scripts/sas_disk_blink 2016-02-25 04:10:03.000000000 +0000 @@ -9,7 +9,7 @@ # Uses sdparm rather than sg3_utils as the former is simpler to # use for setting mode page value. # -# Douglas Gilbert 20130513 +# Douglas Gilbert 20160224 seconds=30 @@ -35,7 +35,7 @@ opt=${opt#-} case "$opt" in h|-help) usage ; exit 0 ;; - s|-set) shift ; let seconds=$1 ;; + s|-set) shift ; let seconds="$1" ;; v|-verbose) verbose="-v" ;; vv) verbose="-vv" ;; vvv) verbose="-vvv" ;; @@ -58,50 +58,50 @@ sdparm fi -if [ $seconds = "0" ] +if [ "$seconds" = "0" ] then - sdparm ${verbose} -t sas -c RLM $1 + sdparm ${verbose} -t sas -c RLM "$1" exit $? -elif [ $seconds = "1" ] +elif [ "$seconds" = "1" ] then - sdparm ${verbose} -t sas -s RLM $1 + sdparm ${verbose} -t sas -s RLM "$1" exit $? -elif [ $seconds -gt 1 ] +elif [ "$seconds" -gt 1 ] then - outt=$(sdparm ${verbose} -t sas -g RLM -H $1) + outt=$(sdparm ${verbose} -t sas -g RLM -H "$1") let res=$? if [ $res -ne 0 ] then exit $res fi - if [ ${outt:0:4} = "0x00" ] + if [ "${outt:0:4}" = "0x00" ] then let start=0 else let start=1 fi echo "start blinking for $seconds seconds" - for (( times = 1; times < $seconds; times=$times+2 )); do + for (( times = 1; times < seconds; times=times+2 )); do if [ $start -eq 0 ] then - sdparm ${verbose} -q -t sas -s RLM $1 + sdparm ${verbose} -q -t sas -s RLM "$1" let res=$? if [ $res -ne 0 ] then exit $res fi sleep 1 - sdparm ${verbose} -q -t sas -c RLM $1 + sdparm ${verbose} -q -t sas -c RLM "$1" sleep 1 else - sdparm ${verbose} -q -t sas -c RLM $1 + sdparm ${verbose} -q -t sas -c RLM "$1" let res=$? if [ $res -ne 0 ] then exit $res fi sleep 1 - sdparm ${verbose} -q -t sas -s RLM $1 + sdparm ${verbose} -q -t sas -s RLM "$1" sleep 1 fi done diff -Nru sdparm-1.10/scripts/scsi_ch_swp sdparm-1.12/scripts/scsi_ch_swp --- sdparm-1.10/scripts/scsi_ch_swp 2013-06-06 13:59:59.000000000 +0000 +++ sdparm-1.12/scripts/scsi_ch_swp 2016-02-25 04:10:03.000000000 +0000 @@ -6,7 +6,7 @@ # # Uses sdparm and blockdev utilities. # -# Douglas Gilbert 20130606 +# Douglas Gilbert 20160224 rdonly="--readonly" @@ -40,7 +40,7 @@ opt=${opt#-} case "$opt" in h|-help) usage ; exit 0 ;; - s|-set) shift ; let swp=$1 ;; + s|-set) shift ; let swp="$1" ;; v|-verbose) verbose="-v" ;; w|-wr) rdonly="" ;; vv) verbose="-vv" ;; @@ -76,29 +76,29 @@ blockdev fi -if [ $swp -lt 0 ] ; then +if [ "$swp" -lt 0 ] ; then if [ ${verbose} ] ; then - echo sdparm --quiet ${rdonly} ${verbose} --get=SWP=1 $1 + echo sdparm --quiet ${rdonly} ${verbose} --get=SWP=1 "$1" fi - sdparm --quiet ${rdonly} ${verbose} --get=SWP=1 $1 + sdparm --quiet ${rdonly} ${verbose} --get=SWP=1 "$1" if [ ${bdverbose} ] ; then echo "blockdev --getro ${bdverbose} $1" fi echo -n "blockdev's RO flag: " - blockdev --getro ${bdverbose} $1 -elif [ $swp = "0" ] ; then + blockdev --getro ${bdverbose} "$1" +elif [ "$swp" = "0" ] ; then if [ ${verbose} ] ; then - echo sdparm --quiet ${rdonly} ${verbose} --clear=SWP $1 + echo sdparm --quiet ${rdonly} ${verbose} --clear=SWP "$1" fi - sdparm --quiet ${rdonly} ${verbose} --clear=SWP $1 + sdparm --quiet ${rdonly} ${verbose} --clear=SWP "$1" res=$? if [ $res -ne 0 ] ; then exit $res fi if [ ${bdverbose} ] ; then - echo blockdev --setrw ${bdverbose} $1 + echo blockdev --setrw ${bdverbose} "$1" fi - blockdev --setrw ${bdverbose} $1 + blockdev --setrw ${bdverbose} "$1" # res=$? # if [ $res -ne 0 ] ; then # exit $res @@ -110,19 +110,19 @@ # echo blockdev --rereadpt ${bdverbose} $1 # fi # blockdev --rereadpt ${bdverbose} $1 -elif [ $swp = "1" ] ; then +elif [ "$swp" = "1" ] ; then if [ ${verbose} ] ; then - echo sdparm --quiet ${verbose} --set=SWP $1 + echo sdparm --quiet ${verbose} --set=SWP "$1" fi - sdparm --quiet ${verbose} --set=SWP $1 + sdparm --quiet ${verbose} --set=SWP "$1" res=$? if [ $res -ne 0 ] ; then exit $res fi if [ ${bdverbose} ] ; then - echo blockdev --setro ${bdverbose} $1 + echo blockdev --setro ${bdverbose} "$1" fi - blockdev --setro ${bdverbose} $1 + blockdev --setro ${bdverbose} "$1" # res=$? # if [ $res -ne 0 ] ; then # exit $res diff -Nru sdparm-1.10/sdparm.spec sdparm-1.12/sdparm.spec --- sdparm-1.10/sdparm.spec 2016-02-22 22:45:10.000000000 +0000 +++ sdparm-1.12/sdparm.spec 2021-04-21 22:25:59.000000000 +0000 @@ -1,5 +1,5 @@ %define name sdparm -%define version 1.10 +%define version 1.12 %define release 1 Summary: List or change SCSI disk parameters @@ -8,8 +8,8 @@ Release: %{release} License: FreeBSD Group: Utilities/System -URL: http://sg.danny.cz/sg/sdparm.html -Source0: http://sg.danny.cz/sg/p/%{name}-%{version}.tgz +URL: https://sg.danny.cz/sg/sdparm.html +Source0: https://sg.danny.cz/sg/p/%{name}-%{version}.tgz BuildRoot: %{_tmppath}/%{name}-%{version}-root Packager: Douglas Gilbert @@ -56,9 +56,15 @@ %{_mandir}/man8/* %changelog +* Wed Apr 21 2021 - dgilbert at interlog dot com +- track recent spc6 and sbc4 drafts + * sdparm-1.12 +* Sun Mar 01 2020 - dgilbert at interlog dot com +- track recent spc6 and sbc4 drafts + * sdparm-1.11 * Mon Feb 22 2016 - dgilbert at interlog dot com - track recent spc5 and sbc4 drafts - * sdparm-1.09 + * sdparm-1.10 * Fri Dec 26 2014 - dgilbert at interlog dot com - track recent spc5 and sbc4 drafts * sdparm-1.09 diff -Nru sdparm-1.10/src/chk_sdparm_data.c sdparm-1.12/src/chk_sdparm_data.c --- sdparm-1.10/src/chk_sdparm_data.c 2016-02-22 22:45:10.000000000 +0000 +++ sdparm-1.12/src/chk_sdparm_data.c 2021-03-20 18:29:34.000000000 +0000 @@ -1,54 +1,56 @@ /* - * Copyright (c) 2005-2016 Douglas Gilbert. + * Copyright (c) 2005-2021, Douglas Gilbert * 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. + * 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. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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. - * - * Version 1.8 20160220 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. */ + /* Version 1.15 20210320 */ + #include #include +#include +#include #include #include "sdparm.h" +#include "sg_lib.h" /* * This is a maintenance program for checking the integrity of * data in the sdparm_data.c tables. * * Build with a line like: - * gcc -Wall -o chk_sdparm_data sdparm_data.o sdparm_data_vendor.o - * chk_sdparm_data.c + * gcc -I ../include -Wall -o chk_sdparm_data sdparm_data.o + * sg_lib.o sg_lib_data.o sdparm_data_vendor.o + * chk_sdparm_data.c * */ #define MAX_MP_LEN 1024 #define MAX_PDT 0x1f -static unsigned char cl_pdt_arr[MAX_PDT + 1][MAX_MP_LEN]; -static unsigned char cl_common_arr[MAX_MP_LEN]; +static uint8_t cl_pdt_arr[MAX_PDT + 1][MAX_MP_LEN]; +static uint8_t cl_common_arr[MAX_MP_LEN]; static void clear_cl() { @@ -58,13 +60,14 @@ /* result: 0 -> good, 1 -> clash at given pdt, 2 -> clash * other than given pdt, -1 -> bad input */ -static int check_cl(int off, int pdt, unsigned char mask) +static int check_cl(int off, int pdt_s, uint8_t mask) { int k; + int a_pdt = (pdt_s & 0xff); if (off >= MAX_MP_LEN) return -1; - if (pdt < 0) { + if (pdt_s < 0) { if (cl_common_arr[off] & mask) return 1; for (k = 0; k <= MAX_PDT; ++k) { @@ -72,36 +75,45 @@ return 2; } return 0; - } else if (pdt <= MAX_PDT) { - if (cl_pdt_arr[pdt][off] & mask) + } else if (a_pdt <= MAX_PDT) { + if (cl_pdt_arr[a_pdt][off] & mask) return 1; if (cl_common_arr[off] & mask) return 2; + if (pdt_s & ~0xff) + return check_cl(off, (pdt_s >> 8), mask); return 0; } return -1; } -static void set_cl(int off, int pdt, unsigned char mask) +static void set_cl(int off, int pdt_s, uint8_t mask) { + int a_pdt = (pdt_s & 0xff); + if (off < MAX_MP_LEN) { - if (pdt < 0) + if (pdt_s < 0) cl_common_arr[off] |= mask; - else if (pdt <= MAX_PDT) - cl_pdt_arr[pdt][off] |= mask; + else if (a_pdt <= MAX_PDT) { + cl_pdt_arr[a_pdt][off] |= mask; + if (pdt_s & ~0xff) + return set_cl(off, (pdt_s >> 8), mask); + } } } -static void check(const struct sdparm_mode_page_item * mpi, - const struct sdparm_mode_page_item * mpi_b) +static int check(const struct sdparm_mode_page_item * mpi, + const struct sdparm_mode_page_item * mpi_b) { - unsigned char mask; + bool second_k = false; + bool second_j = false; + bool k_clash_ok; + int res, prev_mp, prev_msp, prev_pdt, sbyte, sbit, nbits; + int bad_count = 0; + uint8_t mask; const struct sdparm_mode_page_item * kp = mpi; const struct sdparm_mode_page_item * jp = mpi; const char * acron; - int res, prev_mp, prev_msp, prev_pdt, sbyte, sbit, nbits; - int second_k = 0; - int second_j = 0; clear_cl(); for (prev_mp = 0, prev_msp = 0, prev_pdt = -1; ; ++kp) { @@ -111,88 +123,111 @@ prev_mp = 0; prev_msp = 0; kp = mpi_b; - second_k = 1; + second_k = true; } + k_clash_ok = !! (kp->flags & MF_CLASH_OK); acron = kp->acron ? kp->acron : "?"; - if ((prev_mp != kp->page_num) || (prev_msp != kp->subpage_num)) { - if (prev_mp > kp->page_num) - printf(" mode page 0x%x,0x%x out of order\n", kp->page_num, - kp->subpage_num); - if ((prev_mp == kp->page_num) && (prev_msp > kp->subpage_num)) + if ((prev_mp != kp->pg_num) || (prev_msp != kp->subpg_num)) { + if (prev_mp > kp->pg_num) { + printf(" mode page 0x%x,0x%x out of order\n", kp->pg_num, + kp->subpg_num); + ++bad_count; + } + if ((prev_mp == kp->pg_num) && (prev_msp > kp->subpg_num)) { printf(" mode subpage 0x%x,0x%x out of order, previous msp " - "was 0x%x\n", kp->page_num, kp->subpage_num, prev_msp); - prev_mp = kp->page_num; - prev_msp = kp->subpage_num; - prev_pdt = kp->pdt; + "was 0x%x\n", kp->pg_num, kp->subpg_num, prev_msp); + ++bad_count; + } + prev_mp = kp->pg_num; + prev_msp = kp->subpg_num; + prev_pdt = kp->pdt_s; clear_cl(); - } else if ((prev_pdt >= 0) && (prev_pdt != kp->pdt)) { - if (prev_pdt > kp->pdt) + } else if ((prev_pdt >= 0) && (prev_pdt != kp->pdt_s)) { + if (prev_pdt > kp->pdt_s) { printf(" mode page 0x%x,0x%x pdt out of order, pdt was " - "%d, now %d\n", kp->page_num, kp->subpage_num, - prev_pdt, kp->pdt); - prev_pdt = kp->pdt; + "%d, now %d\n", kp->pg_num, kp->subpg_num, + prev_pdt, kp->pdt_s); + ++bad_count; + } + prev_pdt = kp->pdt_s; } for (jp = kp + 1, second_j = second_k; ; ++jp) { if (NULL == jp->acron) { if ((NULL == mpi_b) || second_j) break; jp = mpi_b; - second_j = 1; + second_j = true; } if ((0 == strcmp(acron, jp->acron)) && - (! (jp->flags & MF_CLASH_OK))) + (! ((jp->flags & MF_CLASH_OK) || k_clash_ok))) { printf(" acronym '%s' with this description: '%s'\n " "clashes with '%s'\n", acron, kp->description, jp->description); + ++bad_count; + } } sbyte = kp->start_byte; if ((unsigned)sbyte + 8 > MAX_MP_LEN) { printf(" acronym: %s start byte too large: %d\n", kp->acron, sbyte); + ++bad_count; continue; } sbit = kp->start_bit; if ((unsigned)sbit > 7) { printf(" acronym: %s start bit too large: %d\n", kp->acron, sbit); + ++bad_count; continue; } nbits = kp->num_bits; if (nbits > 64) { printf(" acronym: %s number of bits too large: %d\n", kp->acron, nbits); + ++bad_count; continue; } if (nbits < 1) { printf(" acronym: %s number of bits too small: %d\n", kp->acron, nbits); + ++bad_count; continue; } + + /* now mark up bit map and check for overwrites */ + if (kp->flags & MF_CLASH_OK) + continue; /* MF_CLASH_OK flag implies clash expected */ mask = (1 << (sbit + 1)) - 1; if ((nbits - 1) < sbit) mask &= ~((1 << (sbit + 1 - nbits)) - 1); - res = check_cl(sbyte, kp->pdt, mask); + res = check_cl(sbyte, kp->pdt_s, mask); if (res) { - if (1 == res) + if (1 == res) { printf(" 0x%x,0x%x: clash at start_byte: %d, bit: %d " "[latest acron: %s, this pdt]\n", prev_mp, prev_msp, sbyte, sbit, acron); - else if (2 == res) + ++bad_count; + } else if (2 == res) { printf(" 0x%x,0x%x: clash at start_byte: %d, bit: %d " "[latest acron: %s, another pdt]\n", prev_mp, prev_msp, sbyte, sbit, acron); - else + ++bad_count; + } else { printf(" 0x%x,0x%x: clash, bad data at start_byte: %d, " "bit: %d [latest acron: %s]\n", prev_mp, prev_msp, sbyte, sbit, acron); + ++bad_count; + } } - set_cl(sbyte, kp->pdt, mask); + set_cl(sbyte, kp->pdt_s, mask); if ((nbits - 1) > sbit) { nbits -= (sbit + 1); - if ((nbits > 7) && (0 != (nbits % 8))) + if ((nbits > 7) && (0 != (nbits % 8))) { printf(" 0x%x,0x%x: check nbits: %d, start_byte: %d, bit: " "%d [acron: %s]\n", prev_mp, prev_msp, kp->num_bits, sbyte, sbit, acron); + ++bad_count; + } do { ++sbyte; mask = 0xff; @@ -202,33 +237,38 @@ mask &= ~((1 << (8 - nbits)) - 1); nbits = 0; } - res = check_cl(sbyte, kp->pdt, mask); + res = check_cl(sbyte, kp->pdt_s, mask); if (res) { - if (1 == res) + if (1 == res) { printf(" 0x%x,0x%x: clash at start_byte: %d, " "bit: %d [latest acron: %s, this pdt]\n", prev_mp, prev_msp, sbyte, sbit, acron); - else if (2 == res) + ++bad_count; + } else if (2 == res) { printf(" 0x%x,0x%x: clash at start_byte: %d, " "bit: %d [latest acron: %s, another pdt]\n", prev_mp, prev_msp, sbyte, sbit, acron); - else + ++bad_count; + } else { printf(" 0x%x,0x%x: clash, bad at start_byte: " "%d, bit: %d [latest acron: %s]\n", prev_mp, prev_msp, sbyte, sbit, acron); + ++bad_count; + } } - set_cl(sbyte, kp->pdt, mask); + set_cl(sbyte, kp->pdt_s, mask); } while (nbits > 0); } } + return bad_count; } -static const char * get_vendor_name(int vendor_num) +static const char * get_vendor_name(int vendor_id) { const struct sdparm_vendor_name_t * vnp; for (vnp = sdparm_vendor_id; vnp->acron; ++vnp) { - if (vendor_num == vnp->vendor_num) + if (vendor_id == vnp->vendor_id) return vnp->name; } return NULL; @@ -236,10 +276,11 @@ int main(int argc, char ** argv) { - int k; + int k, bad_count; const struct sdparm_transport_pair * tp; const struct sdparm_vendor_pair * vp; const char * ccp; + char b[128]; if (argc) { ; /* suppress unused warning */ @@ -250,14 +291,22 @@ printf(" Check integrity of mode page item tables in sdparm\n"); printf(" ==================================================\n\n"); printf("Generic (i.e. non-transport specific) mode page items:\n"); - check(sdparm_mitem_arr, NULL); - printf("\n"); + bad_count = check(sdparm_mitem_arr, NULL); + if (bad_count) + printf("%d problem%s\n", bad_count, (1 == bad_count) ? "" : "s"); + else + printf("Pass\n"); tp = sdparm_transport_mp; for (k = 0; k < 16; ++k, ++tp) { if (tp->mitem) { - printf("%s mode page items:\n", sdparm_transport_id[k].name); - check(tp->mitem, NULL); - printf("\n"); + printf("%s mode page items:\n", + sg_get_trans_proto_str(k, sizeof(b), b)); + bad_count = check(tp->mitem, NULL); + if (bad_count) + printf("%d problem%s\n", bad_count, + (1 == bad_count) ? "" : "s"); + else + printf("Pass\n"); } } vp = sdparm_vendor_mp; @@ -268,11 +317,19 @@ printf("%s mode page items:\n", ccp); else printf("0x%x mode page items:\n", k); - check(vp->mitem, NULL); - printf("\n"); + bad_count = check(vp->mitem, NULL); + if (bad_count) + printf("%d problem%s\n", bad_count, + (1 == bad_count) ? "" : "s"); + else + printf("Pass\n"); } } printf("Cross check Generic with SAS mode page items:\n"); - check(sdparm_mitem_arr, sdparm_transport_mp[6].mitem); + bad_count = check(sdparm_mitem_arr, sdparm_transport_mp[6].mitem); + if (bad_count) + printf("%d problem%s\n", bad_count, (1 == bad_count) ? "" : "s"); + else + printf("Pass\n"); return 0; } diff -Nru sdparm-1.10/src/Makefile.am sdparm-1.12/src/Makefile.am --- sdparm-1.10/src/Makefile.am 2015-02-07 20:29:11.000000000 +0000 +++ sdparm-1.12/src/Makefile.am 2021-02-20 18:04:11.000000000 +0000 @@ -1,15 +1,27 @@ bin_PROGRAMS = sdparm # for C++/clang testing -## CC = g++ +## CC = gcc-8 ## CC = g++ ## CC = clang ## CC = clang++ -# -std= can be c99, c11, gnu11, etc. Default is gnu89 (gnu90 is the same) -AM_CFLAGS = -iquote $(top_srcdir)/include -Wall -W @os_cflags@ -# AM_CFLAGS = -iquote $(top_srcdir)/include -Wall -W @os_cflags@ -pedantic -std=c11 -# AM_CFLAGS = -iquote $(top_srcdir)/include -Wall -W @os_cflags@ -pedantic -std=c++11 +if DEBUG +DBG_CFLAGS = -Wextra -Wmisleading-indentation -Wduplicated-cond -Wlogical-op -Wnull-dereference -Wshadow -Wjump-misses-init +DBG_CPPFLAGS = -DDEBUG +else +DBG_CFLAGS = +DBG_CPPFLAGS = +endif + +# -std= can be c99, c11, gnu11, etc. Default is gnu11 +AM_CPPFLAGS = -iquote ${top_srcdir}/include $(DBG_CPPFLAGS) +AM_CFLAGS = -Wall -W $(DBG_CFLAGS) +# AM_CFLAGS = -Wall -W -Wextra -Wmisleading-indentation -Wduplicated-cond -Wduplicated-branches -Wlogical-op -Wnull-dereference -Wshadow -Wjump-misses-init +# AM_CFLAGS = -Wall -W -Werror=misleading-indentation +# AM_CFLAGS = -Wall -W -pedantic -std=c11 +# AM_CFLAGS = -Wall -W -pedantic -std=c++14 +# AM_CFLAGS = -Wall -W -pedantic -std=c++20 sdparm_SOURCES = sdparm.c \ sdparm.h \ @@ -38,20 +50,23 @@ ../lib/sg_cmds_mmc.c \ ../include/sg_cmds_mmc.h \ ../include/sg_pt.h \ + ../include/sg_pt_nvme.h \ ../lib/sg_pt_common.c if HAVE_SGUTILS INCLUDES = -I/scsi -sdparm_LDADD = @GETOPT_O_FILES@ @os_libs@ @SGUTILS_LIBS@ +sdparm_LDADD = @GETOPT_O_FILES@ @SGUTILS_LIBS@ sdparm_DEPENDENCIES = @GETOPT_O_FILES@ else INCLUDES = -I$(top_srcdir)/include sdparm_SOURCES += $(sglib_SOURCES) -sdparm_LDADD = @os_deps@ @GETOPT_O_FILES@ @os_libs@ @SGUTILS_LIBS@ +sdparm_LDADD = @os_deps@ @GETOPT_O_FILES@ @SGUTILS_LIBS@ sdparm_DEPENDENCIES = @os_deps@ @GETOPT_O_FILES@ endif EXTRA_sdparm_SOURCES = ../lib/sg_pt_linux.c \ + ../lib/sg_pt_linux_nvme.c \ + ../include/sg_pt_linux.h \ ../include/sg_linux_inc.h \ ../lib/sg_pt_freebsd.c \ ../lib/sg_pt_osf1.c \ @@ -60,3 +75,6 @@ ../include/sg_pt_win32.h \ getopt_long.c \ port_getopt.h + +distclean-local: + rm -rf .deps diff -Nru sdparm-1.10/src/Makefile.chk_sdparm_data sdparm-1.12/src/Makefile.chk_sdparm_data --- sdparm-1.10/src/Makefile.chk_sdparm_data 2007-10-13 20:16:31.000000000 +0000 +++ sdparm-1.12/src/Makefile.chk_sdparm_data 2017-11-03 18:56:34.000000000 +0000 @@ -36,7 +36,7 @@ .c.o: $(CC) $(INCLUDES) $(CFLAGS) $(S_CFLAGS) -c -o $@ $< -chk_sdparm_data: chk_sdparm_data.o sdparm_data.o sdparm_data_vendor.o +chk_sdparm_data: chk_sdparm_data.o sdparm_data.o sdparm_data_vendor.o sg_lib.o sg_lib_data.o $(LD) -o $@ $(LDFLAGS) $^ install: $(EXECS) diff -Nru sdparm-1.10/src/Makefile.in sdparm-1.12/src/Makefile.in --- sdparm-1.10/src/Makefile.in 2015-12-02 00:45:13.000000000 +0000 +++ sdparm-1.12/src/Makefile.in 2021-02-20 18:04:11.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2014 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -111,7 +111,7 @@ ../lib/sg_cmds_basic.c ../lib/sg_cmds_basic2.c \ ../include/sg_cmds_basic.h ../lib/sg_cmds_mmc.c \ ../include/sg_cmds_mmc.h ../include/sg_pt.h \ - ../lib/sg_pt_common.c + ../include/sg_pt_nvme.h ../lib/sg_pt_common.c @OS_WIN32_MINGW_TRUE@am__objects_1 = sdparm_wscan.$(OBJEXT) @OS_WIN32_CYGWIN_TRUE@am__objects_2 = sdparm_wscan.$(OBJEXT) am__objects_3 = sg_lib.$(OBJEXT) sg_lib_data.$(OBJEXT) \ @@ -137,7 +137,17 @@ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp -am__depfiles_maybe = depfiles +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/getopt_long.Po ./$(DEPDIR)/sdparm.Po \ + ./$(DEPDIR)/sdparm_access.Po ./$(DEPDIR)/sdparm_cmd.Po \ + ./$(DEPDIR)/sdparm_data.Po ./$(DEPDIR)/sdparm_data_vendor.Po \ + ./$(DEPDIR)/sdparm_vpd.Po ./$(DEPDIR)/sdparm_wscan.Po \ + ./$(DEPDIR)/sg_cmds_basic.Po ./$(DEPDIR)/sg_cmds_basic2.Po \ + ./$(DEPDIR)/sg_cmds_mmc.Po ./$(DEPDIR)/sg_lib.Po \ + ./$(DEPDIR)/sg_lib_data.Po ./$(DEPDIR)/sg_pt_common.Po \ + ./$(DEPDIR)/sg_pt_freebsd.Po ./$(DEPDIR)/sg_pt_linux.Po \ + ./$(DEPDIR)/sg_pt_linux_nvme.Po ./$(DEPDIR)/sg_pt_osf1.Po \ + ./$(DEPDIR)/sg_pt_solaris.Po ./$(DEPDIR)/sg_pt_win32.Po am__mv = mv -f AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -186,6 +196,7 @@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -193,7 +204,6 @@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ -CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ @@ -235,6 +245,7 @@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ @@ -269,13 +280,12 @@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ -os_cflags = @os_cflags@ os_deps = @os_deps@ -os_libs = @os_libs@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ @@ -284,13 +294,21 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ +@DEBUG_FALSE@DBG_CFLAGS = # for C++/clang testing - -# -std= can be c99, c11, gnu11, etc. Default is gnu89 (gnu90 is the same) -AM_CFLAGS = -iquote $(top_srcdir)/include -Wall -W @os_cflags@ -# AM_CFLAGS = -iquote $(top_srcdir)/include -Wall -W @os_cflags@ -pedantic -std=c11 -# AM_CFLAGS = -iquote $(top_srcdir)/include -Wall -W @os_cflags@ -pedantic -std=c++11 +@DEBUG_TRUE@DBG_CFLAGS = -Wextra -Wmisleading-indentation -Wduplicated-cond -Wlogical-op -Wnull-dereference -Wshadow -Wjump-misses-init +@DEBUG_FALSE@DBG_CPPFLAGS = +@DEBUG_TRUE@DBG_CPPFLAGS = -DDEBUG + +# -std= can be c99, c11, gnu11, etc. Default is gnu11 +AM_CPPFLAGS = -iquote ${top_srcdir}/include $(DBG_CPPFLAGS) +AM_CFLAGS = -Wall -W $(DBG_CFLAGS) +# AM_CFLAGS = -Wall -W -Wextra -Wmisleading-indentation -Wduplicated-cond -Wduplicated-branches -Wlogical-op -Wnull-dereference -Wshadow -Wjump-misses-init +# AM_CFLAGS = -Wall -W -Werror=misleading-indentation +# AM_CFLAGS = -Wall -W -pedantic -std=c11 +# AM_CFLAGS = -Wall -W -pedantic -std=c++14 +# AM_CFLAGS = -Wall -W -pedantic -std=c++20 sdparm_SOURCES = sdparm.c sdparm.h sdparm_data.c sdparm_data_vendor.c \ sdparm_access.c sdparm_vpd.c sdparm_cmd.c $(am__append_1) \ $(am__append_2) $(am__append_3) @@ -304,15 +322,18 @@ ../lib/sg_cmds_mmc.c \ ../include/sg_cmds_mmc.h \ ../include/sg_pt.h \ + ../include/sg_pt_nvme.h \ ../lib/sg_pt_common.c @HAVE_SGUTILS_FALSE@INCLUDES = -I$(top_srcdir)/include @HAVE_SGUTILS_TRUE@INCLUDES = -I/scsi -@HAVE_SGUTILS_FALSE@sdparm_LDADD = @os_deps@ @GETOPT_O_FILES@ @os_libs@ @SGUTILS_LIBS@ -@HAVE_SGUTILS_TRUE@sdparm_LDADD = @GETOPT_O_FILES@ @os_libs@ @SGUTILS_LIBS@ +@HAVE_SGUTILS_FALSE@sdparm_LDADD = @os_deps@ @GETOPT_O_FILES@ @SGUTILS_LIBS@ +@HAVE_SGUTILS_TRUE@sdparm_LDADD = @GETOPT_O_FILES@ @SGUTILS_LIBS@ @HAVE_SGUTILS_FALSE@sdparm_DEPENDENCIES = @os_deps@ @GETOPT_O_FILES@ @HAVE_SGUTILS_TRUE@sdparm_DEPENDENCIES = @GETOPT_O_FILES@ EXTRA_sdparm_SOURCES = ../lib/sg_pt_linux.c \ + ../lib/sg_pt_linux_nvme.c \ + ../include/sg_pt_linux.h \ ../include/sg_linux_inc.h \ ../lib/sg_pt_freebsd.c \ ../lib/sg_pt_osf1.c \ @@ -335,16 +356,16 @@ exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu src/Makefile + $(AUTOMAKE) --foreign src/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -408,25 +429,32 @@ distclean-compile: -rm -f *.tab.c -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt_long.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdparm.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdparm_access.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdparm_cmd.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdparm_data.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdparm_data_vendor.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdparm_vpd.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdparm_wscan.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_cmds_basic.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_cmds_basic2.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_cmds_mmc.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_lib.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_lib_data.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_common.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_freebsd.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_linux.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_osf1.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_solaris.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_win32.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt_long.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdparm.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdparm_access.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdparm_cmd.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdparm_data.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdparm_data_vendor.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdparm_vpd.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdparm_wscan.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_cmds_basic.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_cmds_basic2.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_cmds_mmc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_lib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_lib_data.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_common.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_freebsd.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_linux.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_linux_nvme.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_osf1.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_solaris.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_win32.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -540,6 +568,20 @@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sg_pt_linux.obj `if test -f '../lib/sg_pt_linux.c'; then $(CYGPATH_W) '../lib/sg_pt_linux.c'; else $(CYGPATH_W) '$(srcdir)/../lib/sg_pt_linux.c'; fi` +sg_pt_linux_nvme.o: ../lib/sg_pt_linux_nvme.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sg_pt_linux_nvme.o -MD -MP -MF $(DEPDIR)/sg_pt_linux_nvme.Tpo -c -o sg_pt_linux_nvme.o `test -f '../lib/sg_pt_linux_nvme.c' || echo '$(srcdir)/'`../lib/sg_pt_linux_nvme.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sg_pt_linux_nvme.Tpo $(DEPDIR)/sg_pt_linux_nvme.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../lib/sg_pt_linux_nvme.c' object='sg_pt_linux_nvme.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sg_pt_linux_nvme.o `test -f '../lib/sg_pt_linux_nvme.c' || echo '$(srcdir)/'`../lib/sg_pt_linux_nvme.c + +sg_pt_linux_nvme.obj: ../lib/sg_pt_linux_nvme.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sg_pt_linux_nvme.obj -MD -MP -MF $(DEPDIR)/sg_pt_linux_nvme.Tpo -c -o sg_pt_linux_nvme.obj `if test -f '../lib/sg_pt_linux_nvme.c'; then $(CYGPATH_W) '../lib/sg_pt_linux_nvme.c'; else $(CYGPATH_W) '$(srcdir)/../lib/sg_pt_linux_nvme.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sg_pt_linux_nvme.Tpo $(DEPDIR)/sg_pt_linux_nvme.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../lib/sg_pt_linux_nvme.c' object='sg_pt_linux_nvme.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sg_pt_linux_nvme.obj `if test -f '../lib/sg_pt_linux_nvme.c'; then $(CYGPATH_W) '../lib/sg_pt_linux_nvme.c'; else $(CYGPATH_W) '$(srcdir)/../lib/sg_pt_linux_nvme.c'; fi` + sg_pt_freebsd.o: ../lib/sg_pt_freebsd.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sg_pt_freebsd.o -MD -MP -MF $(DEPDIR)/sg_pt_freebsd.Tpo -c -o sg_pt_freebsd.o `test -f '../lib/sg_pt_freebsd.c' || echo '$(srcdir)/'`../lib/sg_pt_freebsd.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sg_pt_freebsd.Tpo $(DEPDIR)/sg_pt_freebsd.Po @@ -648,7 +690,10 @@ distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ @@ -720,10 +765,29 @@ clean-am: clean-binPROGRAMS clean-generic mostlyclean-am distclean: distclean-am - -rm -rf ./$(DEPDIR) + -rm -f ./$(DEPDIR)/getopt_long.Po + -rm -f ./$(DEPDIR)/sdparm.Po + -rm -f ./$(DEPDIR)/sdparm_access.Po + -rm -f ./$(DEPDIR)/sdparm_cmd.Po + -rm -f ./$(DEPDIR)/sdparm_data.Po + -rm -f ./$(DEPDIR)/sdparm_data_vendor.Po + -rm -f ./$(DEPDIR)/sdparm_vpd.Po + -rm -f ./$(DEPDIR)/sdparm_wscan.Po + -rm -f ./$(DEPDIR)/sg_cmds_basic.Po + -rm -f ./$(DEPDIR)/sg_cmds_basic2.Po + -rm -f ./$(DEPDIR)/sg_cmds_mmc.Po + -rm -f ./$(DEPDIR)/sg_lib.Po + -rm -f ./$(DEPDIR)/sg_lib_data.Po + -rm -f ./$(DEPDIR)/sg_pt_common.Po + -rm -f ./$(DEPDIR)/sg_pt_freebsd.Po + -rm -f ./$(DEPDIR)/sg_pt_linux.Po + -rm -f ./$(DEPDIR)/sg_pt_linux_nvme.Po + -rm -f ./$(DEPDIR)/sg_pt_osf1.Po + -rm -f ./$(DEPDIR)/sg_pt_solaris.Po + -rm -f ./$(DEPDIR)/sg_pt_win32.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ - distclean-tags + distclean-local distclean-tags dvi: dvi-am @@ -766,7 +830,26 @@ installcheck-am: maintainer-clean: maintainer-clean-am - -rm -rf ./$(DEPDIR) + -rm -f ./$(DEPDIR)/getopt_long.Po + -rm -f ./$(DEPDIR)/sdparm.Po + -rm -f ./$(DEPDIR)/sdparm_access.Po + -rm -f ./$(DEPDIR)/sdparm_cmd.Po + -rm -f ./$(DEPDIR)/sdparm_data.Po + -rm -f ./$(DEPDIR)/sdparm_data_vendor.Po + -rm -f ./$(DEPDIR)/sdparm_vpd.Po + -rm -f ./$(DEPDIR)/sdparm_wscan.Po + -rm -f ./$(DEPDIR)/sg_cmds_basic.Po + -rm -f ./$(DEPDIR)/sg_cmds_basic2.Po + -rm -f ./$(DEPDIR)/sg_cmds_mmc.Po + -rm -f ./$(DEPDIR)/sg_lib.Po + -rm -f ./$(DEPDIR)/sg_lib_data.Po + -rm -f ./$(DEPDIR)/sg_pt_common.Po + -rm -f ./$(DEPDIR)/sg_pt_freebsd.Po + -rm -f ./$(DEPDIR)/sg_pt_linux.Po + -rm -f ./$(DEPDIR)/sg_pt_linux_nvme.Po + -rm -f ./$(DEPDIR)/sg_pt_osf1.Po + -rm -f ./$(DEPDIR)/sg_pt_solaris.Po + -rm -f ./$(DEPDIR)/sg_pt_win32.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic @@ -786,23 +869,26 @@ .MAKE: install-am install-strip -.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ clean-binPROGRAMS clean-generic cscopelist-am ctags ctags-am \ - distclean distclean-compile distclean-generic distclean-tags \ - distdir dvi dvi-am html html-am info info-am install \ - install-am install-binPROGRAMS install-data install-data-am \ - install-dvi install-dvi-am install-exec install-exec-am \ - install-html install-html-am install-info install-info-am \ - install-man install-pdf install-pdf-am install-ps \ - install-ps-am install-strip installcheck installcheck-am \ - installdirs maintainer-clean maintainer-clean-generic \ - mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \ - ps ps-am tags tags-am uninstall uninstall-am \ - uninstall-binPROGRAMS + distclean distclean-compile distclean-generic distclean-local \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-binPROGRAMS install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-binPROGRAMS .PRECIOUS: Makefile +distclean-local: + rm -rf .deps + # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: diff -Nru sdparm-1.10/src/sdparm_access.c sdparm-1.12/src/sdparm_access.c --- sdparm-1.10/src/sdparm_access.c 2016-02-02 04:28:24.000000000 +0000 +++ sdparm-1.12/src/sdparm_access.c 2021-03-22 04:44:26.000000000 +0000 @@ -1,45 +1,79 @@ /* - * Copyright (c) 2005-2016 Douglas Gilbert. + * Copyright (c) 2005-2021, Douglas Gilbert * 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. + * 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. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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. - * + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. */ #include #include +#include +#include #include #include #include +#define __STDC_FORMAT_MACROS 1 +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif #include "sdparm.h" +#include "sg_lib.h" #include "sg_unaligned.h" +#include "sg_pr2serr.h" /* sdparm_access.c : helpers for sdparm to access tables in * sdparm_data.c */ +/* Returns true if left argument is "equal" to the right argument. l_pdt_s + * is a compound PDT (SCSI Peripheral Device Type) or a negative number + * which represents a wildcard (i.e. match anything). r_pdt_s has a similar + * form. PDT values are 5 bits long (0 to 31) and a compound pdt_s is + * formed by shifting the second (upper) PDT by eight bits to the left and + * OR-ing it with the first PDT. The pdt_s values must be defined so + * PDT_DISK (0) is _not_ the upper value in a compound pdt_s. */ +bool +pdt_s_eq(int l_pdt_s, int r_pdt_s) +{ + bool upper_l = !!(l_pdt_s & PDT_UPPER_MASK); + bool upper_r = !!(r_pdt_s & PDT_UPPER_MASK); + + if ((l_pdt_s < 0) || (r_pdt_s < 0)) + return true; + if (!upper_l && !upper_r) + return l_pdt_s == r_pdt_s; + else if (upper_l && upper_r) + return (((PDT_UPPER_MASK & l_pdt_s) == (PDT_UPPER_MASK & r_pdt_s)) || + ((PDT_LOWER_MASK & l_pdt_s) == (PDT_LOWER_MASK & r_pdt_s))); + else if (upper_l) + return (((PDT_LOWER_MASK & l_pdt_s) == r_pdt_s) || + ((PDT_UPPER_MASK & l_pdt_s) >> 8) == r_pdt_s); + return (((PDT_LOWER_MASK & r_pdt_s) == l_pdt_s) || + ((PDT_UPPER_MASK & r_pdt_s) >> 8) == l_pdt_s); +} + /* Returns 1 if strings equal (same length, characters same or only differ * by case), else returns 0. Assumes 7 bit ASCII (English alphabet). */ int @@ -91,22 +125,25 @@ } +/* Returns length of mode page. Assumes mp pointing at start of a mode + * page (not the start of a MODE SENSE response). */ int -sdp_get_mp_len(unsigned char * mp) +sdp_mpage_len(const uint8_t * mp) { + /* if SPF (byte 0, bit 6) is set then 4 byte header, else 2 byte header */ return (mp[0] & 0x40) ? (sg_get_unaligned_be16(mp + 2) + 4) : (mp[1] + 2); } const struct sdparm_mode_page_t * -sdp_get_mode_detail(int page_num, int subpage_num, int pdt, int transp_proto, - int vendor_num) +sdp_get_mpage_t(int page_num, int subpage_num, int pdt, int transp_proto, + int vendor_id) { const struct sdparm_mode_page_t * mpp; - if (vendor_num >= 0) { + if (vendor_id >= 0) { const struct sdparm_vendor_pair * vpp; - vpp = sdp_get_vendor_pair(vendor_num); + vpp = sdp_get_vendor_pair(vendor_id); mpp = (vpp ? vpp->mpage : NULL); } else if ((transp_proto >= 0) && (transp_proto < 16)) mpp = sdparm_transport_mp[transp_proto].mpage; @@ -117,7 +154,7 @@ for ( ; mpp->acron; ++mpp) { if ((page_num == mpp->page) && (subpage_num == mpp->subpage)) { - if ((pdt < 0) || (mpp->pdt < 0) || (mpp->pdt == pdt)) + if ((pdt < 0) || (mpp->pdt_s < 0) || pdt_s_eq(mpp->pdt_s, pdt)) return mpp; } } @@ -125,22 +162,23 @@ } const struct sdparm_mode_page_t * -sdp_get_mpage_name(int page_num, int subpage_num, int pdt, int transp_proto, - int vendor_num, int plus_acron, int hex, char * bp, - int max_b_len) +sdp_get_mpt_with_str(int page_num, int subpage_num, int pdt, int transp_proto, + int vendor_id, bool plus_acron, bool hex, int b_len, + char * bp) { - int len = max_b_len - 1; + int len = b_len - 1; const struct sdparm_mode_page_t * mpp = NULL; const char * cp; if (len < 0) return mpp; bp[len] = '\0'; - mpp = sdp_get_mode_detail(page_num, subpage_num, pdt, transp_proto, - vendor_num); - if (NULL == mpp) - mpp = sdp_get_mode_detail(page_num, subpage_num, -1, transp_proto, - vendor_num); + /* first try to match given pdt */ + mpp = sdp_get_mpage_t(page_num, subpage_num, pdt, transp_proto, + vendor_id); + if (NULL == mpp) /* didn't match specific pdt so try -1 (ie. SPC) */ + mpp = sdp_get_mpage_t(page_num, subpage_num, -1, transp_proto, + vendor_id); if (mpp && mpp->name) { cp = mpp->acron; if (NULL == cp) @@ -176,14 +214,14 @@ } const struct sdparm_mode_page_t * -sdp_find_mp_by_acron(const char * ap, int transp_proto, int vendor_num) +sdp_find_mpt_by_acron(const char * ap, int transp_proto, int vendor_id) { const struct sdparm_mode_page_t * mpp; - if (vendor_num >= 0) { + if (vendor_id >= 0) { const struct sdparm_vendor_pair * vpp; - vpp = sdp_get_vendor_pair(vendor_num); + vpp = sdp_get_vendor_pair(vendor_id); mpp = (vpp ? vpp->mpage : NULL); } else if ((transp_proto >= 0) && (transp_proto < 16)) mpp = sdparm_transport_mp[transp_proto].mpage; @@ -210,7 +248,7 @@ for (vpp = sdparm_vpd_pg; vpp->acron; ++vpp) { if ((page_num == vpp->vpd_num) && (sv || (subvalue == vpp->subvalue)) && - (ty || (pdt == vpp->pdt))) + (ty || (pdt == vpp->pdt_s))) return vpp; } if (! ty) @@ -232,37 +270,47 @@ return NULL; } -const char * -sdp_get_transport_name(int proto_num) +char * +sdp_get_transport_name(int proto_num, int b_len, char * b) { - const struct sdparm_transport_id_t * tip; + char d[128]; - for (tip = sdparm_transport_id; tip->acron; ++tip) { - if (proto_num == tip->proto_num) - return tip->name; - } - return NULL; + if (NULL == b) + ; + else if (b_len < 2) { + if (1 == b_len) + b[0] = '\0'; + } else + snprintf(b, b_len, "%s", sg_get_trans_proto_str(proto_num, sizeof(d), + d)); + return b; } -const struct sdparm_transport_id_t * -sdp_find_transport_by_acron(const char * ap) +int +sdp_find_transport_id_by_acron(const char * ap) { - const struct sdparm_transport_id_t * tip; + const struct sdparm_val_desc_t * t_vdp; + const struct sdparm_val_desc_t * t_addp; - for (tip = sdparm_transport_id; tip->acron; ++tip) { - if (sdp_strcase_eq(tip->acron, ap)) - return tip; + for (t_vdp = sdparm_transport_id; t_vdp->desc; ++t_vdp) { + if (sdp_strcase_eq(t_vdp->desc, ap)) + return t_vdp->val; } - return NULL; + /* No match ... try additional transport acronyms */ + for (t_addp = sdparm_add_transport_acron; t_addp->desc; ++t_addp) { + if (sdp_strcase_eq(t_addp->desc, ap)) + return t_addp->val; + } + return -1; } const char * -sdp_get_vendor_name(int vendor_num) +sdp_get_vendor_name(int vendor_id) { const struct sdparm_vendor_name_t * vnp; for (vnp = sdparm_vendor_id; vnp->acron; ++vnp) { - if (vendor_num == vnp->vendor_num) + if (vendor_id == vnp->vendor_id) return vnp->name; } return NULL; @@ -281,28 +329,38 @@ } const struct sdparm_vendor_pair * -sdp_get_vendor_pair(int vendor_num) +sdp_get_vendor_pair(int vendor_id) { - return ((vendor_num >= 0) && (vendor_num < sdparm_vendor_mp_len)) - ? (sdparm_vendor_mp + vendor_num) : NULL; + return ((vendor_id >= 0) && (vendor_id < sdparm_vendor_mp_len)) + ? (sdparm_vendor_mp + vendor_id) : NULL; } +/* Searches mpage items table from (and including) the current position + * looking for the first match on 'ap' (pointer to acromym). Checks + * against the inbuilt table (in sdparm_data.c) of generic (when both + * transp_proto and vendor_id are -1), transport (when transp_proto is + * >= 0) or vendor (when vendor_id is >= 0) mode page items (fields). + * If found a pointer to that mitem is returned and *from_p is set to + * the offset after the match. If not found then NULL is returned and + * *from_p is set to the offset of the sentinel at the end of the + * selected mitem array. Start iteration by setting from_p to NULL or + * point it at -1. */ const struct sdparm_mode_page_item * -sdp_find_mitem_by_acron(const char * ap, int * from, int transp_proto, - int vendor_num) +sdp_find_mitem_by_acron(const char * ap, int * from_p, int transp_proto, + int vendor_id) { int k = 0; const struct sdparm_mode_page_item * mpi; - if (from) { - k = *from; + if (from_p) { + k = *from_p; if (k < 0) k = 0; } - if (vendor_num >= 0) { + if (vendor_id >= 0) { const struct sdparm_vendor_pair * vpp; - vpp = sdp_get_vendor_pair(vendor_num); + vpp = sdp_get_vendor_pair(vendor_id); mpi = (vpp ? vpp->mitem : NULL); } else if ((transp_proto >= 0) && (transp_proto < 16)) mpi = sdparm_transport_mp[transp_proto].mitem; @@ -317,96 +375,129 @@ } if (NULL == mpi->acron) mpi = NULL; - if (from) - *from = (mpi ? (k + 1) : k); + if (from_p) + *from_p = (mpi ? (k + 1) : k); return mpi; } -/* Does similar job to sg_get_unaligned_be*() but this function starts at - * a given start_bit offset. Maximum number of num_bits is 64. For example - * sdp_get_big_endian(from,7,16)==sg_get_unaligned_be16(from) */ uint64_t -sdp_get_big_endian(const unsigned char * from, int start_bit, int num_bits) +sdp_mitem_get_value(const struct sdparm_mode_page_item *mpi, + const uint8_t * mp) +{ + return sg_get_big_endian(mp + mpi->start_byte, mpi->start_bit, + mpi->num_bits); +} + +/* Gets a mode page item's value given a pointer to the mode page response + * (mp). If all_setp is non-NULL then checks 8, 16, 24, 32, 48 and 64 bit + * quantities for all bits set (e.g. for 8 bits that would be 0xff) and if + * so sets the bool addressed by all_setp to true. Otherwise if all_setp + * is non-NULL then it sets the bool addressed by all_setp to false. + * Returns the value in an unsigned 64 bit integer. To print a value as a + * signed quantity use sdp_print_signed_decimal(). */ +uint64_t +sdp_mitem_get_value_check(const struct sdparm_mode_page_item *mpi, + const uint8_t * mp, bool * all_setp) { uint64_t res; - int sbit_o1 = start_bit + 1; - res = (*from++ & ((1 << sbit_o1) - 1)); - num_bits -= sbit_o1; - while (num_bits > 0) { - res <<= 8; - res |= *from++; - num_bits -= 8; + res = sg_get_big_endian(mp + mpi->start_byte, mpi->start_bit, + mpi->num_bits); + if (all_setp) { + switch (mpi->num_bits) { + case 8: + if (0xff == res) + *all_setp = true; + break; + case 16: + if (0xffff == res) + *all_setp = true; + break; + case 24: + if (0xffffff == res) + *all_setp = true; + break; + case 32: + if (0xffffffff == res) + *all_setp = true; + break; + case 48: + if (0xffffffffffffULL == res) + *all_setp = true; + break; + case 64: + if (0xffffffffffffffffULL == res) + *all_setp = true; + break; + default: + *all_setp = false; + break; + } } - if (num_bits < 0) - res >>= (-num_bits); return res; } -/* Does similar job to sg_put_unaligned_be*() but this function starts at - * a given start_bit offset. Maximum number of num_bits is 64. Preserves - * residual bits in partially written bytes. */ void -sdp_set_big_endian(uint64_t val, unsigned char * to, int start_bit, - int num_bits) +sdp_print_signed_decimal(uint64_t u, int num_bits, bool leading_zeros) { - int sbit_o1 = start_bit + 1; - int mask, num, k, x; + unsigned int ui; + uint8_t uc; - mask = (8 != sbit_o1) ? ((1 << sbit_o1) - 1) : 0xff; - k = start_bit - ((num_bits - 1) % 8); - if (0 != k) - val <<= ((k > 0) ? k : (8 + k)); - num = (num_bits + 15 - sbit_o1) / 8; - for (k = 0; k < num; ++k) { - if ((sbit_o1 - num_bits) > 0) - mask &= ~((1 << (sbit_o1 - num_bits)) - 1); - if (k < (num - 1)) - x = (val >> ((num - k - 1) * 8)) & 0xff; + switch (num_bits) { + /* could support other num_bits, as required */ + case 4: /* -8 to 7 */ + uc = u & 0xf; + if (0x8 & uc) + uc |= 0xf0; /* sign extend */ + if (leading_zeros) + printf("%02hhd", (signed char)uc); else - x = val & 0xff; - to[k] = (to[k] & ~mask) | (x & mask); - mask = 0xff; - num_bits -= sbit_o1; - sbit_o1 = 8; - } -} - -uint64_t -sdp_mp_get_value(const struct sdparm_mode_page_item *mpi, - const unsigned char * mp) -{ - return sdp_get_big_endian(mp + mpi->start_byte, mpi->start_bit, - mpi->num_bits); -} - -uint64_t -sdp_mp_get_value_check(const struct sdparm_mode_page_item *mpi, - const unsigned char * mp, int * all_set) -{ - uint64_t res; - - res = sdp_get_big_endian(mp + mpi->start_byte, mpi->start_bit, - mpi->num_bits); - if (all_set) { - if ((16 == mpi->num_bits) && (0xffff == res)) - *all_set = 1; - else if ((32 == mpi->num_bits) && (0xffffffff == res)) - *all_set = 1; - else if ((64 == mpi->num_bits) && (0xffffffffffffffffULL == res)) - *all_set = 1; + printf("%hhd", (signed char)uc); + break; + case 8: /* -128 to 127 */ + if (leading_zeros) + printf("%02hhd", (signed char)u); + else + printf("%hhd", (signed char)u); + break; + case 16: /* -32768 to 32767 */ + if (leading_zeros) + printf("%02hd", (short int)u); + else + printf("%hd", (short int)u); + break; + case 24: + ui = 0xffffff & u; + if (0x800000 & ui) + ui |= 0xff000000; + if (leading_zeros) + printf("%02d", (int)ui); + else + printf("%d", (int)ui); + break; + case 32: + if (leading_zeros) + printf("%02d", (int)u); else - *all_set = 0; + printf("%d", (int)u); + break; + case 64: + default: + if (leading_zeros) + printf("%02" PRId64 , (int64_t)u); + else + printf("%" PRId64 , (int64_t)u); + break; } - return res; } +/* Place 'val' at an offset to 'mp' as indicated by mpi. */ void -sdp_mp_set_value(uint64_t val, const struct sdparm_mode_page_item * mpi, - unsigned char * mp) +sdp_mitem_set_value(uint64_t val, const struct sdparm_mode_page_item * mpi, + uint8_t * mp) { - sdp_set_big_endian(val, mp + mpi->start_byte, mpi->start_bit, - mpi->num_bits); + sg_set_big_endian(val, mp + mpi->start_byte, mpi->start_bit, + mpi->num_bits); } char * @@ -417,3 +508,9 @@ strncpy(buff, sdparm_ansi_version_arr[version], buff_len - 1); return buff; } + +int +sdp_get_desc_id(int flags) +{ + return (MF_DESC_ID_MASK & flags) >> MF_DESC_ID_SHIFT; +} diff -Nru sdparm-1.10/src/sdparm.c sdparm-1.12/src/sdparm.c --- sdparm-1.10/src/sdparm.c 2016-02-22 22:45:10.000000000 +0000 +++ sdparm-1.12/src/sdparm.c 2021-04-21 22:25:59.000000000 +0000 @@ -1,46 +1,48 @@ /* - * Copyright (c) 2005-2016 Douglas Gilbert. + * Copyright (c) 2005-2021, Douglas Gilbert * 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. + * 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. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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. - * + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. */ /* * sdparm is a utility program for getting and setting parameters on devices * that use one of the SCSI command sets. In some cases commands can be sent - * to the device (e.g. eject removable media). + * to the device (e.g. eject removable media). The parameters are usually + * arranged into pages. The pages are know as 'mode pages' (for parameters + * that may be altered) and 'Vital Product Data (VPD) pages (for parameters + * that seldom, if ever, change). Each page contains parameters that are + * logically similar (e.g. the Caching mode page contains both the Write + * Cache Enable (WCE) and Read Cache Disable (RCD) parameters). + * * - * Note that some devices, such as CD/DVD drives, use a SCSI command set - * (i.e. MMC-4 and SPC-3) but are not normally categorized as "SCSI" since - * most use the packet interface over the ATA transport (ATAPI). */ #include #include #include #include +#include +#include #include #include #include @@ -67,7 +69,7 @@ #include #include -static int map_if_lk24(int sg_fd, const char * device_name, int rw, +static int map_if_lk24(int sg_fd, const char * device_name, bool rw, int verbose); #endif /* SG_LIB_LINUX */ @@ -78,13 +80,24 @@ #include "sg_pr2serr.h" #include "sdparm.h" -static const char * version_str = "1.10 20160222 [svn: r279]"; +static const char * version_str = "1.12 20210421 [svn: r347]"; #define MAX_DEV_NAMES 256 #define INHEX_BUFF_SZ 4096 -static unsigned char inhex_buff[INHEX_BUFF_SZ]; +static uint8_t inhex_buff[INHEX_BUFF_SZ]; + +static uint8_t * cur_aligned_mp; +static uint8_t * cha_aligned_mp; +static uint8_t * def_aligned_mp; +static uint8_t * sav_aligned_mp; +static uint8_t * oth_aligned_mp; +static uint8_t * free_cur_aligned_mp; +static uint8_t * free_cha_aligned_mp; +static uint8_t * free_def_aligned_mp; +static uint8_t * free_sav_aligned_mp; +static uint8_t * free_oth_aligned_mp; static struct option long_options[] = { @@ -96,6 +109,7 @@ {"defaults", no_argument, 0, 'D'}, {"dummy", no_argument, 0, 'd'}, {"enumerate", no_argument, 0, 'e'}, + {"examine", no_argument, 0, 'E'}, {"flexible", no_argument, 0, 'f'}, {"get", required_argument, 0, 'g'}, {"help", no_argument, 0, 'h'}, @@ -104,6 +118,10 @@ {"inhex", required_argument, 0, 'I'}, {"long", no_argument, 0, 'l'}, {"num-desc", no_argument, 0, 'n'}, + {"num_desc", no_argument, 0, 'n'}, + {"numdesc", no_argument, 0, 'n'}, + {"out-mask", required_argument, 0, 'o'}, + {"out_mask", required_argument, 0, 'o'}, {"page", required_argument, 0, 'p'}, {"pdt", required_argument, 0, 'P'}, {"quiet", no_argument, 0, 'q'}, @@ -121,19 +139,24 @@ {0, 0, 0, 0}, }; +static const char * ms_s = "MODE SENSE"; + +static int print_mode_pages(int sg_fd, int pn, int spn, int pdt, + const struct sdparm_opt_coll * op); + static void usage(int do_help) { - if (1 != do_help) - goto secondary_help; - pr2serr("Usage: sdparm [--all] [--dbd] [--flexible] [--get=STR] [--hex] " - "[--long]\n" - " [--num-desc] [--page=PG[,SPG]] [--quiet] " - "[--readonly]\n" - " [--six] [--transport=TN] [--vendor=VN] " - "[--verbose]\n" - " DEVICE [DEVICE...]\n" + if (do_help < 2) { + pr2serr( + "Usage: sdparm [--all] [--dbd] [--examine] [--flexible] " + "[--get=STR] [--hex]\n" + " [--long] [--num-desc] [--out-mask=OM] " + "[--page=PG[,SPG]]\n" + " [--quiet] [--readonly] [--six] [--transport=TN] " + "[--vendor=VN]\n" + " [--verbose] DEVICE [DEVICE...]\n\n" " sdparm [--clear=STR] [--defaults] [--dummy] " "[--flexible]\n" " [--page=PG[,SPG]] [--quiet] [--readonly] " @@ -141,23 +164,82 @@ " [--six] [--transport=TN] [--vendor=VN] " "[--verbose]\n" " DEVICE [DEVICE...]\n\n" + ); + if (do_help < 1) { + pr2serr( + " sdparm --command=CMD [--hex] [--long] [--readonly] " + "[--verbose]\n" + " DEVICE [DEVICE...]\n\n" + " sdparm --inquiry [--all] [--examine] [--flexible] " + "[--hex]\n" + " [--num-desc] [--page=PG[,SPG]] [--quiet] " + "[--read‐only]\n" + " [--transport=TN] [--vendor=VN] [--verbose]\n" + " DEVICE [DEVICE...]\n\n" + " sdparm --enumerate [--all] [--inquiry] [--long] " + "[--page=PG[,SPG]]\n" + " [--transport=TN] [--vendor=VN]\n\n" + " sdparm --inhex=FN [--all] [--flexible] [--hex] " + "[--inquiry] [--long]\n" + " [--pdt=DT] [--raw] [--six] [--transport=TN] " + "[--vendor=VN]\n" + " [--verbose]\n\n" + "Or the corresponding short option usage: \n" + " sdparm [-a] [-B] [-E] [-f] [-g STR] [-H] [-l] [-n] " + "[-o OM] [-p PG[,SPG]]\n" + " [-q] [-r] [-6] [-t TN] [-M VN] [-v] DEVICE " + "[DEVICE...]\n" + "\n" + " sdparm [-c STR] [-D] [-d] [-f] [-p PG[,SPG]] [-q] [-S] " + "[-s STR] [-6] [-t TN]\n" + " [-M VN] [-v] DEVICE [DEVICE...]\n" + "\n" + " sdparm -c CMD [-H] [-l] [-r] [-v] DEVICE [DEVICE...]\n" + "\n" + " sdparm -i [-a] [-E] [-f] [-H] [-n] [-p PG[,SPG]] [-q] " + "[-r] [-t TN]\n" + " [-M VN] [-v] DEVICE [DEVICE...]\n" + "\n" + " sdparm -e [-a] [-i] [-l] [-p PG[,SPG]] [-t TN] [-M VN]\n" + "\n" + " sdparm -I FN [-a] [-f] [-H] [-i] [-l] [-P PDT] [-R] [-6] " + "[-t TN] [-M VN] [-v]\n" + ); + pr2serr("\nFor help use '-h' one or more times\n"); + return; + } + pr2serr( + " sdparm << for more usages, see 'sdparm -hh' >>\n\n" + " where mode page read (1st usage) and change (2nd usage) " "options are:\n" - " --all | -a list all known fields for given " - "DEVICE\n" - " --clear=STR | -c STR clear (zero) field value(s)\n" + " --all | -a list all known pages and fields for " + "given DEVICE\n" + " --clear=STR | -c STR clear (zero) field value(s), or " + "set to 'val'\n" " --dbd | -B set DBD bit in mode sense cdb\n" " --defaults | -D set a mode page to its default " "values\n" + " when use twice set all pages to " + "their defaults\n" " --dummy | -d don't write back modified mode page\n" + " --examine | -E cycle through mode or vpd page " + "numbers (default\n" + " with '-a': only check pages with " + "known fields)\n" " --flexible | -f compensate for common errors, " "relax some checks\n" - " --get=STR | -g STR get (fetch) field value(s)\n" + " --get=STR | -g STR get (fetch) field value(s), by " + "acronym or pos\n" " --hex | -H output in hex rather than name/value " "pairs\n" " --long | -l add description to field output\n" " --num-desc | -n report number of mode page " "descriptors\n" + " --out-mask=OM | -o OM select whether current, " + "changeable, default\n" + " and/or saveable values out, " + "(def: all(0xf)\n" " --page=PG[,SPG] | -p PG[,SPG] page (and optionally " "subpage) number\n" " [or abbrev] to output, change or " @@ -169,7 +251,8 @@ " on operation). Mainly for ATA disks\n" " --save | -S place mode changes in saved page as " "well\n" - " --set=STR | -s STR set field value(s)\n" + " --set=STR | -s STR set field value(s) to 1, or to " + "'val'\n" " --six | -6 use 6 byte SCSI mode cdbs (def: 10 " "byte)\n" " --transport=TN | -t TN transport protocol number " @@ -186,24 +269,24 @@ "decoding response data supplied in a\nfile or stdin (rather " "than from a DEVICE).\n" ); - return; -secondary_help: - pr2serr("Further usages of the sdparm utility:\n" - " sdparm --command=CMD [-hex] [--readonly] [--verbose]\n" - " DEVICE [DEVICE...]\n" + } else { + pr2serr("Further usages of the sdparm utility:\n" + " sdparm --command=CMD [-hex] [--long] [--readonly] " + "[--verbose]\n" + " DEVICE [DEVICE...]\n\n" " sdparm --inquiry [--all] [--flexible] [--hex]\n" " [--page=PG[,SPG]] [--quiet] [--readonly] " "[--transport=TN]\n" - " [--vendor=VN] [--verbose] DEVICE [DEVICE...]\n"); - pr2serr(" sdparm --enumerate [--all] [--inquiry] [--long] " + " [--vendor=VN] [--verbose] DEVICE [DEVICE...]\n\n"); + pr2serr(" sdparm --enumerate [--all] [--inquiry] [--long] " "[--page=PG[,SPG]]\n" - " [--transport=TN] [--vendor=VN]\n" + " [--transport=TN] [--vendor=VN]\n\n" " sdparm --inhex=FN [--all] [--flexible] [--hex] " "[--inquiry]\n" " [--long] [--pdt=PDT] [--raw] [--six] " "[--transport=TN]\n" - " [--vendor=VN]\n" - " sdparm --wscan [--verbose]\n" + " [--vendor=VN]\n\n" + " sdparm --wscan [--verbose]\n\n" " sdparm [--help] [--version]\n\n" " where the additional options are:\n" " --command=CMD | -C CMD perform CMD (e.g. 'eject')\n" @@ -233,211 +316,20 @@ "'--wscan' form is for listing Windows\ndevices and is only " "available on Windows machines.\n" ); -} - -/* Read ASCII hex bytes or binary from fname (a file named '-' taken as - * stdin). If reading ASCII hex then there should be either one entry per - * line or a comma, space or tab separated list of bytes. If no_space is - * set then a string of ACSII hex digits is expected, 2 per byte. Everything - * from and including a '#' on a line is ignored. Returns 0 if ok, or 1 if - * error. */ -static int -f2hex_arr(const char * fname, int as_binary, int no_space, - unsigned char * mp_arr, int * mp_arr_len, int max_arr_len) -{ - int fn_len, in_len, k, j, m, split_line, fd, has_stdin; - unsigned int h; - const char * lcp; - FILE * fp; - char line[512]; - char carry_over[4]; - int off = 0; - struct stat a_stat; - - if ((NULL == fname) || (NULL == mp_arr) || (NULL == mp_arr_len)) - return 1; - fn_len = strlen(fname); - if (0 == fn_len) - return 1; - has_stdin = ((1 == fn_len) && ('-' == fname[0])); /* read from stdin */ - if (as_binary) { - if (has_stdin) - fd = STDIN_FILENO; - else { - fd = open(fname, O_RDONLY); - if (fd < 0) { - pr2serr("unable to open binary file %s: %s\n", fname, - safe_strerror(errno)); - return 1; - } - } - k = read(fd, mp_arr, max_arr_len); - if (k <= 0) { - if (0 == k) - pr2serr("read 0 bytes from binary file %s\n", fname); - else - pr2serr("read from binary file %s: %s\n", fname, - safe_strerror(errno)); - if (! has_stdin) - close(fd); - return 1; - } - if ((0 == fstat(fd, &a_stat)) && S_ISFIFO(a_stat.st_mode)) { - /* pipe; keep reading till error or 0 read */ - while (k < max_arr_len) { - m = read(fd, mp_arr + k, max_arr_len - k); - if (0 == m) - break; - if (m < 0) { - pr2serr("read from binary pipe %s: %s\n", fname, - safe_strerror(errno)); - if (! has_stdin) - close(fd); - return 1; - } - k += m; - } - } - *mp_arr_len = k; - if (! has_stdin) - close(fd); - return 0; - } else { /* So read the file as ASCII hex */ - if (has_stdin) - fp = stdin; - else { - fp = fopen(fname, "r"); - if (NULL == fp) { - pr2serr("Unable to open %s for reading\n", fname); - return 1; - } - } - } - - carry_over[0] = 0; - for (j = 0; j < 512; ++j) { - if (NULL == fgets(line, sizeof(line), fp)) - break; - in_len = strlen(line); - if (in_len > 0) { - if ('\n' == line[in_len - 1]) { - --in_len; - line[in_len] = '\0'; - split_line = 0; - } else - split_line = 1; - } - if (in_len < 1) { - carry_over[0] = 0; - continue; - } - if (carry_over[0]) { - if (isxdigit(line[0])) { - carry_over[1] = line[0]; - carry_over[2] = '\0'; - if (1 == sscanf(carry_over, "%x", &h)) - mp_arr[off - 1] = h; /* back up and overwrite */ - else { - pr2serr("%s: carry_over error ['%s'] around line %d\n", - __func__, carry_over, j + 1); - goto bad; - } - lcp = line + 1; - --in_len; - } else - lcp = line; - carry_over[0] = 0; - } else - lcp = line; - - m = strspn(lcp, " \t"); - if (m == in_len) - continue; - lcp += m; - in_len -= m; - if ('#' == *lcp) - continue; - k = strspn(lcp, "0123456789aAbBcCdDeEfF ,\t"); - if ((k < in_len) && ('#' != lcp[k]) && ('\r' != lcp[k])) { - pr2serr("%s: syntax error at line %d, pos %d\n", __func__, - j + 1, m + k + 1); - goto bad; - } - if (no_space) { - for (k = 0; isxdigit(*lcp) && isxdigit(*(lcp + 1)); - ++k, lcp += 2) { - if (1 != sscanf(lcp, "%2x", &h)) { - pr2serr("%s: bad hex number in line %d, pos %d\n", - __func__, j + 1, (int)(lcp - line + 1)); - goto bad; - } - if ((off + k) >= max_arr_len) { - pr2serr("%s: array length exceeded\n", __func__); - goto bad; - } - mp_arr[off + k] = h; - } - if (isxdigit(*lcp) && (! isxdigit(*(lcp + 1)))) - carry_over[0] = *lcp; - off += k; - } else { - for (k = 0; k < 1024; ++k) { - if (1 == sscanf(lcp, "%x", &h)) { - if (h > 0xff) { - pr2serr("%s: hex number larger than 0xff in line " - "%d, pos %d\n", __func__, j + 1, - (int)(lcp - line + 1)); - goto bad; - } - if (split_line && (1 == strlen(lcp))) { - /* single trailing hex digit might be a split pair */ - carry_over[0] = *lcp; - } - if ((off + k) >= max_arr_len) { - pr2serr("%s: array length exceeded\n", __func__); - goto bad; - } - mp_arr[off + k] = h; - lcp = strpbrk(lcp, " ,\t"); - if (NULL == lcp) - break; - lcp += strspn(lcp, " ,\t"); - if ('\0' == *lcp) - break; - } else { - if (('#' == *lcp) || ('\r' == *lcp)) { - --k; - break; - } - pr2serr("%s: error in line %d, at pos %d\n", __func__, - j + 1, (int)(lcp - line + 1)); - goto bad; - } - } - off += (k + 1); - } } - *mp_arr_len = off; - if (stdin != fp) - fclose(fp); - return 0; -bad: - if (stdin != fp) - fclose(fp); - return 1; } static void -enumerate_mpages(int transport, int vendor) +enumerate_mpages(int transport, int vendor_id) { const struct sdparm_mode_page_t * mpp; - if (vendor >= 0) { + if (vendor_id >= 0) { const struct sdparm_vendor_pair * vpp; - vpp = sdp_get_vendor_pair(vendor); + vpp = sdp_get_vendor_pair(vendor_id); if (NULL == vpp) { - pr2serr("Bad vendor number\n"); + pr2serr("Bad vendor identifier (number)\n"); return; } mpp = vpp->mpage; @@ -475,14 +367,30 @@ } static void -enumerate_transports() +enumerate_transports(bool multiple_acrons, const struct sdparm_opt_coll * op) { - const struct sdparm_transport_id_t * tip; - - for (tip = sdparm_transport_id; tip->acron; ++tip) { - if (tip->name) - printf(" %-6s 0x%02x %s\n", tip->acron, tip->proto_num, - tip->name); + int len; + const struct sdparm_val_desc_t * addp; + const struct sdparm_val_desc_t * t_vdp; + char b[64]; + char d[128]; + + for (t_vdp = sdparm_transport_id; t_vdp->desc; ++t_vdp) { + if (op->do_long || multiple_acrons) { + snprintf(d, sizeof(d), "%s", t_vdp->desc); + len = strlen(d); + for (addp = sdparm_add_transport_acron; addp->desc; ++addp) { + if ((addp->val == t_vdp->val) && + ((int)sizeof(d) >= (len - 1))) { + snprintf(d + len, sizeof(d) - len, ",%s", addp->desc); + len = strlen(d); + } + } + printf(" %-24s 0x%02x %s\n", d, t_vdp->val, + sg_get_trans_proto_str(t_vdp->val, sizeof(b), b)); + } else + printf(" %-6s 0x%02x %s\n", t_vdp->desc, t_vdp->val, + sg_get_trans_proto_str(t_vdp->val, sizeof(b), b)); } } @@ -492,8 +400,8 @@ const struct sdparm_vendor_name_t * vnp; for (vnp = sdparm_vendor_id; vnp->acron; ++vnp) { - if (vnp->name) - printf(" %-6s 0x%02x %s\n", vnp->acron, vnp->vendor_num, + if (vnp->name && (VENDOR_NONE != vnp->vendor_id)) + printf(" %-6s 0x%02x %s\n", vnp->acron, vnp->vendor_id, vnp->name); } } @@ -522,23 +430,27 @@ enumerate_mitems(int pn, int spn, int pdt, const struct sdparm_opt_coll * op) { - int t_pn, t_spn, t_pdt, vendor, transp, long_o; + bool found = false; + bool have_desc = false; + bool have_desc_id = false; + int t_pn, t_spn, t_pdt_s, vendor_id, transp, long_o, e_num; const struct sdparm_mode_page_t * mpp = NULL; + const struct sdparm_mode_descriptor_t * mdp = NULL; const struct sdparm_mode_page_item * mpi; - char buff[128]; + char d[128]; char b[128]; - int found = 0; t_pn = -1; t_spn = -1; - t_pdt = -2; - vendor = op->vendor; + t_pdt_s = -2; + vendor_id = op->vendor_id; transp = op->transport; long_o = op->do_long; - if (vendor >= 0) { + e_num = op->do_enum; + if (vendor_id >= 0) { const struct sdparm_vendor_pair * vpp; - vpp = sdp_get_vendor_pair(vendor); + vpp = sdp_get_vendor_pair(vendor_id); mpi = (vpp ? vpp->mitem : NULL); } else if ((transp >= 0) && (transp < 16)) mpi = sdparm_transport_mp[transp].mitem; @@ -548,42 +460,57 @@ return; for ( ; mpi->acron; ++mpi) { - if ((pdt >= 0) && (mpi->pdt >= 0) && (pdt != mpi->pdt)) + if (! pdt_s_eq(pdt, mpi->pdt_s)) continue; - if ((t_pn != mpi->page_num) || (t_spn != mpi->subpage_num) || - (t_pdt != mpi->pdt)) { - t_pn = mpi->page_num; - t_spn = mpi->subpage_num; - t_pdt = mpi->pdt; + if ((t_pn == mpi->pg_num) && (t_spn == mpi->subpg_num) && + pdt_s_eq(t_pdt_s, mpi->pdt_s)) { if ((pn >= 0) && ((pn != t_pn) || (spn != t_spn))) continue; - if ((pdt >= 0) && (pdt != t_pdt)) - continue; - mpp = sdp_get_mpage_name(t_pn, t_spn, t_pdt, transp, vendor, - long_o, 1, buff, sizeof(buff)); - if (long_o && (transp < 0) && (vendor < 0)) - printf("%s [%s] mode page:\n", buff, - sg_get_pdt_str(t_pdt, sizeof(b), b)); - else - printf("%s mode page:\n", buff); } else { + t_pn = mpi->pg_num; + t_spn = mpi->subpg_num; + t_pdt_s = mpi->pdt_s; if ((pn >= 0) && ((pn != t_pn) || (spn != t_spn))) continue; + if (! pdt_s_eq(pdt, t_pdt_s)) + continue; + mpp = sdp_get_mpt_with_str(t_pn, t_spn, t_pdt_s, transp, + vendor_id, long_o, true, sizeof(d), d); + if (long_o && (transp < 0) && (vendor_id < 0)) + printf("%s [%s] mode page:\n", d, + sg_get_pdt_str(t_pdt_s, sizeof(b), b)); + else + printf("%s mode page:\n", d); + if (mpp && ((mdp = mpp->mp_desc))) { + have_desc = true; + have_desc_id = mdp->have_desc_id; + } else { + have_desc = false; + have_desc_id = false; + } + } + if (have_desc_id && (MF_CLASH_OK & mpi->flags)) + printf(" %-10s [0x%02x:%d:%-2d %d] %s\n", mpi->acron, + mpi->start_byte, mpi->start_bit, mpi->num_bits, + sdp_get_desc_id(mpi->flags), mpi->description); + else + printf(" %-10s [0x%02x:%d:%-2d] %s\n", mpi->acron, + mpi->start_byte, mpi->start_bit, mpi->num_bits, + mpi->description); + if (mpi->extra) { + if ((long_o > 1) || (e_num > 1)) + print_mp_extra(mpi->extra); } - printf(" %-10s [0x%02x:%d:%-2d] %s\n", mpi->acron, mpi->start_byte, - mpi->start_bit, mpi->num_bits, mpi->description); - if ((long_o > 1) && mpi->extra) - print_mp_extra(mpi->extra); - found = 1; + found = true; } if ((! found) && (pn >= 0)) { - sdp_get_mpage_name(pn, spn, pdt, transp, vendor, long_o, 1, buff, - sizeof(buff)); - pr2serr("%s mode page: no items found\n", buff); - } - if (found && mpp && mpp->mp_desc && long_o) { - const struct sdparm_mode_descriptor_t * mdp = mpp->mp_desc; - + sdp_get_mpt_with_str(pn, spn, pdt, transp, vendor_id, + long_o, true, sizeof(d), d); + pr2serr("%s mode page: no items found\n", d); + } + if (found && have_desc && (long_o || (e_num > 1))) { + if ((-1 == mdp->num_descs_off) && (-1 == mdp->num_descs_bytes)) + return; /* Nothing to warn about in this case */ if (mdp->name) printf(" <<%s mode descriptor>>\n", mdp->name); else @@ -593,28 +520,79 @@ mdp->num_descs_off, mdp->num_descs_bytes, mdp->num_descs_inc, mdp->first_desc_off); if (mdp->desc_len > 0) - printf(" descriptor_len=%d\n", mdp->desc_len); + printf(" descriptor_len=%d, ", mdp->desc_len); else - printf(" desc_len_off=%d, desc_len_bytes=%d\n", + printf(" desc_len_off=%d, desc_len_bytes=%d, ", mdp->desc_len_off, mdp->desc_len_bytes); + printf("desc_id=%s\n", (have_desc_id ? "true" : "false")); + } +} + +#define SDP_HIGHEST_MPAGE_NUM 0x3e +#define SDP_HIGHEST_MSUBPAGE_NUM 0xfe + +static int +examine_mode_page(int sg_fd, int pn, const struct sdparm_opt_coll * op, + int req_pdt) +{ + bool first = true; + bool not_subpages = (pn < 0); + int k, n, epn, espn, res; + + if (pn > SDP_HIGHEST_MPAGE_NUM) { + pr2serr("No mode page numbers higher than 0x%x are allowed\n", + SDP_HIGHEST_MPAGE_NUM); + return SG_LIB_SYNTAX_ERROR; + } + n = not_subpages ? SDP_HIGHEST_MPAGE_NUM : SDP_HIGHEST_MSUBPAGE_NUM; + for (k = 0, res = 0; k <= n; ++k) { + epn = not_subpages ? k : pn; + espn = not_subpages ? 0 : k; + if (first) + first = false; + else if (0 == res) + printf("\n"); + res = print_mode_pages(sg_fd, epn, espn, req_pdt, op); } + return 0; +} + +#define SDP_MAX_T10_VPD_NUM 0xbf + +static int +examine_vpd_page(int sg_fd, int pn, int spn, + const struct sdparm_opt_coll * op, int req_pdt, bool protect) +{ + bool first = true; + int k, n, res; + + n = (spn < 0) ? SDP_MAX_T10_VPD_NUM : spn; + for (k = (pn < 0) ? 0 : pn, res = 0; k <= n; ++k) { + if (first) + first = false; + else if (0 == res) + printf("\n"); + res = sdp_process_vpd_page(sg_fd, k, 0, op, req_pdt, protect, NULL, + 0, NULL, 0); + } + return 0; } static void -list_mp_settings(const struct sdparm_mode_page_settings * mps, int get) +list_mp_settings(const struct sdparm_mode_page_settings * mps, bool get) { const struct sdparm_mode_page_item * mpip; int k; printf("mp_settings: page,subpage=0x%x,0x%x num=%d\n", - mps->page_num, mps->subpage_num, mps->num_it_vals); + mps->pg_num, mps->subpg_num, mps->num_it_vals); for (k = 0; k < mps->num_it_vals; ++k) { mpip = &mps->it_vals[k].mpi; if (get) - printf(" [0x%x,0x%x]", mpip->page_num, mpip->subpage_num); + printf(" [0x%x,0x%x]", mpip->pg_num, mpip->subpg_num); printf(" pdt=%d start_byte=0x%x start_bit=%d num_bits=%d val=%" - PRId64 "", mpip->pdt, mpip->start_byte, mpip->start_bit, + PRId64 "", mpip->pdt_s, mpip->start_byte, mpip->start_bit, mpip->num_bits, mps->it_vals[k].val); if (mpip->acron) { printf(" acronym: %s", mpip->acron); @@ -628,65 +606,91 @@ } } -/* Print prefix and acronymn plus current, changeable [smask & 2], default - * [smask & 4] and saved [smask & 8] values for an item */ +/* Print prefix and acronymn plus current, changeable [smask & MP_OM_CHA], + * default [smask & MP_OM_DEF] and saved [smask & MP_OM_SAV] values for an + * item */ static void -print_mp_entry(const char * pre, int smask, - const struct sdparm_mode_page_item *mpi, - const unsigned char * cur_mp, - const unsigned char * cha_mp, - const unsigned char * def_mp, - const unsigned char * sav_mp, - int force_decimal, - const struct sdparm_opt_coll * op) -{ - int sep = 0; - int all_set; +print_mitem(const char * pre, int smask, + const struct sdparm_mode_page_item *mpi, + const uint8_t * cur_mp, + const uint8_t * cha_mp, + const uint8_t * def_mp, + const uint8_t * sav_mp, + bool force_decimal, + const struct sdparm_opt_coll * op) +{ + bool all_set = false; + bool prt_pre = false; + bool sep = false; uint64_t u; const char * acron; - all_set = 0; acron = (mpi->acron ? mpi->acron : ""); - u = sdp_mp_get_value_check(mpi, cur_mp, &all_set); - printf("%s%-14s", pre, acron); - if (force_decimal) - printf("%" PRId64 "", (int64_t)u); - else if (mpi->flags & MF_HEX) - printf("0x%" PRIx64 "", u); - else if (all_set) - printf("-1"); - else - printf("%" PRIu64 "", u); - if ((smask & 0xe) && (op->do_quiet < 2)) { - printf(" ["); - if (cha_mp && (smask & 2)) { + if (MP_OM_CUR & smask) { + u = sdp_mitem_get_value_check(mpi, cur_mp, &all_set); + printf("%s%-14s", pre, acron); + prt_pre = true; + if (force_decimal || (mpi->flags & MF_TWOS_COMP)) + sdp_print_signed_decimal(u, mpi->num_bits, false); + else if (mpi->flags & MF_HEX) { + if ((mpi->flags & MF_ALL_1S) && all_set) + printf("-1"); + else + printf("0x%" PRIx64 "", u); + } else if (all_set) + printf("-1"); + else + printf("%" PRIu64 "", u); + } + if ((smask & (MP_OM_CHA | MP_OM_DEF | MP_OM_SAV)) && (op->do_quiet < 2)) { + if (prt_pre) + printf(" ["); + if (cha_mp && (smask & MP_OM_CHA)) { + if (! prt_pre) { + printf("%s%-14s [", pre, acron); + prt_pre = true; + } printf("cha: %s", - (sdp_mp_get_value(mpi, cha_mp) ? "y" : "n")); - sep = 1; + (sdp_mitem_get_value(mpi, cha_mp) ? "y" : "n")); + sep = true; } - if (def_mp && (smask & 4)) { - all_set = 0; - u = sdp_mp_get_value_check(mpi, def_mp, &all_set); + if (def_mp && (smask & MP_OM_DEF)) { + if (! prt_pre) { + printf("%s%-14s [", pre, acron); + prt_pre = true; + } + all_set = false; + u = sdp_mitem_get_value_check(mpi, def_mp, &all_set); printf("%sdef:", (sep ? ", " : " ")); - if (force_decimal) - printf(" %" PRId64 "", (int64_t)u); - else if (mpi->flags & MF_HEX) - printf(" 0x%" PRIx64 "", u); - else if (all_set) + if (force_decimal || (mpi->flags & MF_TWOS_COMP)) + sdp_print_signed_decimal(u, mpi->num_bits, false); + else if (mpi->flags & MF_HEX) { + if ((mpi->flags & MF_ALL_1S) && all_set) + printf("-1"); + else + printf("0x%" PRIx64 "", u); + } else if (all_set) printf(" -1"); else printf("%3" PRIu64 "", u); - sep = 1; + sep = true; } - if (sav_mp && (smask & 8)) { - all_set = 0; - u = sdp_mp_get_value_check(mpi, sav_mp, &all_set); + if (sav_mp && (smask & MP_OM_SAV)) { + if (! prt_pre) { + printf("%s%-14s [", pre, acron); + prt_pre = true; + } + all_set = false; + u = sdp_mitem_get_value_check(mpi, sav_mp, &all_set); printf("%ssav:", (sep ? ", " : " ")); - if (force_decimal) - printf(" %" PRId64 "", (int64_t)u); - else if (mpi->flags & MF_HEX) - printf(" 0x%" PRIx64 "", u); - else if (all_set) + if (force_decimal || (mpi->flags & MF_TWOS_COMP)) + sdp_print_signed_decimal(u, mpi->num_bits, false); + else if (mpi->flags & MF_HEX) { + if ((mpi->flags & MF_ALL_1S) && all_set) + printf("-1"); + else + printf("0x%" PRIx64 "", u); + } else if (all_set) printf(" -1"); else printf("%3" PRIu64 "", u); @@ -701,36 +705,69 @@ } static void -print_mp_arr_entry(const char * pre, int smask, +print_mitem_pc_arr(const char * pre, int smask, const struct sdparm_mode_page_item *mpi, - void ** pc_arr, int force_decimal, + void ** pc_arr, bool force_decimal, const struct sdparm_opt_coll * op) { - const unsigned char * cur_mp = (const unsigned char *)pc_arr[0]; - const unsigned char * cha_mp = (const unsigned char *)pc_arr[1]; - const unsigned char * def_mp = (const unsigned char *)pc_arr[2]; - const unsigned char * sav_mp = (const unsigned char *)pc_arr[3]; + const uint8_t * cur_mp = (const uint8_t *)pc_arr[0]; + const uint8_t * cha_mp = (const uint8_t *)pc_arr[1]; + const uint8_t * def_mp = (const uint8_t *)pc_arr[2]; + const uint8_t * sav_mp = (const uint8_t *)pc_arr[3]; - print_mp_entry(pre, smask, mpi, cur_mp, cha_mp, def_mp, sav_mp, - force_decimal, op); + print_mitem(pre, smask, mpi, cur_mp, cha_mp, def_mp, sav_mp, + force_decimal, op); } static int ll_mode_sense(int fd, const struct sdparm_opt_coll * op, int pn, int spn, - unsigned char * resp, int mx_resp_len, int verb) + uint8_t * resp, int mx_resp_len, int * residp, int verb) { - if (op->mode_6) + if (op->mode_6) { + if (residp) + *residp = 0; return sg_ll_mode_sense6(fd, op->dbd, 0 /*current */, pn, spn, - resp, mx_resp_len, 1 /* noisy */, verb); - else - return sg_ll_mode_sense10(fd, 0 /* llbaa */, op->dbd, 0, pn, - spn, resp, mx_resp_len, 1 /* noisy */, - verb); + resp, mx_resp_len, true /* noisy */, verb); + } else + return sg_ll_mode_sense10_v2(fd, false /* llbaa */, op->dbd, 0, pn, + spn, resp, mx_resp_len, 0, residp, + true /* noisy */, verb); +} + +static int +report_error(int res, bool mode6) +{ + const char * cdbLenS = mode6 ? "6" : "10"; + char b[96]; + + switch (res) { + case SG_LIB_CAT_INVALID_OP: + pr2serr("%s(%s) cdb not supported, try again with%s '-6' " + "option\n", ms_s, cdbLenS, mode6 ? "out" : ""); + break; + case SG_LIB_CAT_NOT_READY: + pr2serr("%s(%s) failed, device not ready\n", ms_s, + cdbLenS); + break; + case SG_LIB_CAT_UNIT_ATTENTION: + pr2serr("%s(%s) failed, unit attention\n", ms_s, cdbLenS); + break; + case SG_LIB_CAT_ABORTED_COMMAND: + pr2serr("%s(%s), aborted command\n", ms_s, cdbLenS); + break; + case 0: + break; + default: + if (sg_exit2str(res, false, sizeof(b), b)) + pr2serr("%s(%s): %s\n", ms_s, cdbLenS, b); + break; + } + return res; } /* Make some noise if the mode page response seems badly formed */ static void -check_mode_page(unsigned char * cur_mp, int pn, int rep_len, +check_mode_page(uint8_t * cur_mp, int pn, int rep_len, const struct sdparm_opt_coll * op) { int const pn_in_page = cur_mp[0] & 0x3f; @@ -758,26 +795,32 @@ } } -/* When mode page has descriptor, print_mode_pages() will print the first */ -/* descriptor. This function is called to print subsequent descriptors */ +/* When mode page has descriptor, print_mode_pages() and + * print_mode_page_inhex() will print the items associated with the first + * descriptor. This function is called to print items associated with + * subsequent descriptors */ static void -print_mpage_extra_desc(void ** pc_arr, int rep_len, - const struct sdparm_mode_page_t * mpp, - const struct sdparm_mode_page_item * fdesc_mpi, - const struct sdparm_mode_page_item * last_mpi, - const struct sdparm_opt_coll * op, - int smask) -{ +print_mitem_desc_after1(void ** pc_arr, int rep_len, + const struct sdparm_mode_page_t * mpp, + const struct sdparm_mode_page_item * fdesc_mpi, + const struct sdparm_mode_page_item * last_mpi, + const struct sdparm_opt_coll * op, + int smask, int desc_len1) +{ + bool broke = false; + bool have_desc_id = false; + int k, len, d_off, n; + int desc_id = -1; + int num = 0; const struct sdparm_mode_descriptor_t * mdp = mpp->mp_desc; const struct sdparm_mode_page_item * mpi; - const unsigned char * cur_mp = (const unsigned char *)pc_arr[0]; - const unsigned char * cha_mp = (const unsigned char *)pc_arr[1]; - const unsigned char * def_mp = (const unsigned char *)pc_arr[2]; - const unsigned char * sav_mp = (const unsigned char *)pc_arr[3]; - const unsigned char * ucp; + const uint8_t * cur_mp = (const uint8_t *)pc_arr[0]; + const uint8_t * cha_mp = (const uint8_t *)pc_arr[1]; + const uint8_t * def_mp = (const uint8_t *)pc_arr[2]; + const uint8_t * sav_mp = (const uint8_t *)pc_arr[3]; + const uint8_t * bp; struct sdparm_mode_page_item ampi; uint64_t u; - int k, num, len, d_off, n, bad; char b[32]; if ((NULL == mdp) || (NULL == cur_mp) || (rep_len < 4) || @@ -785,28 +828,68 @@ return; if ((mdp->num_descs_inc < 0) && (mdp->desc_len > 0)) { k = mdp->first_desc_off - (mdp->num_descs_off + mdp->num_descs_bytes); - u = (sdp_get_big_endian(cur_mp + mdp->num_descs_off, 7, - mdp->num_descs_bytes * 8) - k) / + u = (sg_get_big_endian(cur_mp + mdp->num_descs_off, 7, + mdp->num_descs_bytes * 8) - k) / mdp->desc_len; - } else - u = sdp_get_big_endian(cur_mp + mdp->num_descs_off, 7, + num = (int)u; + } else if (mdp->num_descs_bytes > 0) { + u = sg_get_big_endian(cur_mp + mdp->num_descs_off, 7, mdp->num_descs_bytes * 8) + mdp->num_descs_inc; - num = (int)u; + num = (int)u; + } else { + int pg_len = sdp_mpage_len(cur_mp); + int off = mdp->first_desc_off; + int d_len = mdp->desc_len_off + mdp->desc_len_bytes; + + have_desc_id = true; + bp = cur_mp + mdp->desc_len_off; + while (off + d_len < pg_len) { + ++num; + u = sg_get_big_endian(bp + off, 7, mdp->desc_len_bytes * 8); + if (u > 1024) { + pr2serr(">> mpage descriptor too large=%" + PRIu64 ", stop counting " + "descriptors\n", u); + break; + } + off += (d_len + (int)u); + } + } if (op->verbose > 1) - pr2serr(" >>> mode page says it has %d descriptors\n", num); - if ((u < 2) || (u > 256)) + pr2serr(" >>> %d descriptors in mode page\n", num); + + if (((num >= 0) && (num < 2)) || (num > 512)) return; - if (mdp->desc_len <= 0) { - ucp = cur_mp + mdp->first_desc_off + mdp->desc_len_off; - u = sdp_get_big_endian(ucp, 7, mdp->desc_len_bytes * 8); + if (have_desc_id) { + if (desc_len1 <= 0) { + pr2serr("%s: desc_id logic fails for acron: %s\n", __func__, + fdesc_mpi->acron ? fdesc_mpi->acron : "??"); + return; + } + len = desc_len1; + } else if (mdp->desc_len <= 0) { + bp = cur_mp + mdp->first_desc_off + mdp->desc_len_off; + u = sg_get_big_endian(bp, 7, mdp->desc_len_bytes * 8); len = mdp->desc_len_off + mdp->desc_len_bytes + u; } else len = mdp->desc_len; d_off = mdp->first_desc_off + len; for (k = 1; k < num; ++k, d_off += len) { - bad = 0; + if (op->verbose > 3) + pr2serr("%s: desc_len1=%d, d_off=%d, len=%d\n", __func__, + desc_len1, d_off, len); + broke = false; for (mpi = fdesc_mpi; mpi <= last_mpi; ++mpi) { - strncpy(b, mpi->acron, sizeof(b)); + if (have_desc_id) { + if (mpi->flags & MF_CLASH_OK) { + if (desc_id != sdp_get_desc_id(mpi->flags)) + continue; /* skip this item due to desc_id mismatch */ + } else { + desc_id = 0xf & *(cur_mp + d_off); + len = sg_get_unaligned_be16(cur_mp + d_off + 2) + 4; + } + } + sg_scnpr(b, sizeof(b), "%s", mpi->acron); ampi = *mpi; b[sizeof(b) - 8] = '\0'; n = strlen(b); @@ -816,17 +899,18 @@ if (ampi.start_byte >= rep_len) { pr2serr("descriptor overflows reply len (%d) for %s\n", rep_len, ampi.acron); - bad = 1; + broke = true; break; } - print_mp_entry(" ", smask, &i, cur_mp, cha_mp, def_mp, - sav_mp, 0, op); + print_mitem(" ", smask, &i, cur_mp, cha_mp, def_mp, sav_mp, + false, op); } - if (bad) + if (broke) break; - if (mdp->desc_len <= 0) { - ucp = cur_mp + d_off + mdp->desc_len_off; - u = sdp_get_big_endian(ucp, 7, mdp->desc_len_bytes * 8); + if ((mdp->desc_len <= 0) && (mdp->num_descs_bytes > 0) && + (! have_desc_id)) { + bp = cur_mp + d_off + mdp->desc_len_off; + u = sg_get_big_endian(bp, 7, mdp->desc_len_bytes * 8); len = mdp->desc_len_off + mdp->desc_len_bytes + u; } } @@ -838,208 +922,284 @@ print_direct_access_info(int sg_fd, const struct sdparm_opt_coll * op, int verb) { - int res, v; - unsigned char cur_mp[DEF_MODE_RESP_LEN]; - - memset(cur_mp, 0, sizeof(cur_mp)); - res = ll_mode_sense(sg_fd, op, ALL_MPAGES, 0, cur_mp, 8, verb); + bool for_in_hex = (op->do_hex > 2); + int res, v, req_len, resp_len, resid; + uint8_t * cur_mp = oth_aligned_mp; + + memset(cur_mp, 0, op->mode_6 ? DEF_MODE_6_RESP_LEN : DEF_MODE_RESP_LEN); + req_len = 8 + (10 * 16); /* allow for 10 'long' block descriptors */ + res = ll_mode_sense(sg_fd, op, ALL_MPAGES, 0, cur_mp, req_len, &resid, + verb); if (0 == res) { - v = cur_mp[op->mode_6 ? 2 : 3]; - printf(" Direct access device specific parameters: WP=%d " - "DPOFUA=%d\n", !!(v & 0x80), !!(v & 0x10)); - } else if (SG_LIB_CAT_NOT_READY == res) { - pr2serr("mode sense command failed, device not ready\n"); - return res; - } else if (SG_LIB_CAT_INVALID_OP == res) { - if (op->mode_6) - pr2serr("6 byte MODE SENSE cdb not supported, try again without " - "'-6' option\n"); - else - pr2serr("10 byte MODE SENSE cdb not supported, try again with " - "'-6' option\n"); - return res; - } else if (SG_LIB_CAT_UNIT_ATTENTION == res) { - pr2serr("mode sense command failed, unit attention\n"); - return res; - } else if (SG_LIB_CAT_ABORTED_COMMAND == res) { - pr2serr("mode sense command failed, aborted command\n"); - return res; - } + bool mode6 = op->mode_6; + + resp_len = req_len - resid; + if (resp_len < 4) { + if (verb > 0) + pr2serr("%s: resid=%d too large, implies truncated " + "response\n", __func__, resid); + } else if (for_in_hex) { + uint16_t bd_len = mode6 ? cur_mp[3] : + sg_get_unaligned_be16(cur_mp + 6); + + printf("# Mode parameter header(%d)%s:\n", (mode6 ? 6 : 10), + (bd_len ? " and block descriptor(s)" : "")); + hex2stdout(cur_mp, bd_len + (mode6 ? 4 : 8), -1); + printf("\n"); + } else { + v = cur_mp[mode6 ? 2 : 3]; + printf(" Direct access device specific parameters: WP=%d " + "DPOFUA=%d\n", !!(v & 0x80), !!(v & 0x10)); + } + } else if (SG_LIB_CAT_ILLEGAL_REQ != res) + return report_error(res, op->mode_6); return 0; } +static void +report_number_of_descriptors(uint8_t * cur_mp, + const struct sdparm_mode_descriptor_t * mdp, + const struct sdparm_opt_coll * op) +{ + int num = 0; + uint64_t u; + + if ((mdp->num_descs_inc < 0) && (mdp->desc_len > 0)) { + u = sg_get_big_endian(cur_mp + mdp->num_descs_off, 7, + mdp->num_descs_bytes * 8) / + mdp->desc_len; + num = (int)u; + } else if (mdp->num_descs_bytes > 0) { + u = sg_get_big_endian(cur_mp + mdp->num_descs_off, 7, + mdp->num_descs_bytes * 8) + + mdp->num_descs_inc; + num = (int)u; + } else { /* no descriptor count, so walk them */ + int pg_len = sdp_mpage_len(cur_mp); + int d_len = mdp->desc_len_off + mdp->desc_len_bytes; + int off = mdp->first_desc_off; + const uint8_t * bp = cur_mp + mdp->desc_len_off; + + if (pg_len < 4) + return; + while (off + d_len < pg_len) { + ++num; + u = sg_get_big_endian(bp + off, 7, mdp->desc_len_bytes * 8); + if (u > 1024) { + pr2serr(">> mpage descriptor too large=%" + PRIu64 ", stop counting " + "descriptors\n", u); + break; + } + off += (d_len + (int)u); + } + } + if (op->do_long) + printf("number of descriptors=%d\n", num); + else + printf("%d\n", num); +} + +static int +print_mode_page_hex(int sg_fd, int pn, int spn, + const struct sdparm_opt_coll * op) +{ + int res, len, resid, vb; + uint8_t * mdpg = oth_aligned_mp; + + vb = (op->verbose > 2) ? op->verbose - 2 : 0; + len = (op->mode_6) ? DEF_MODE_6_RESP_LEN : MAX_MODE_DATA_LEN; + res = ll_mode_sense(sg_fd, op, pn, spn, mdpg, len, &resid, vb); + if (res) + return res; + printf("Mode page [0x%x,0x%x] current values in hex:\n", pn, spn); + len -= resid; + if (len > 0) + hex2stdout(mdpg, len, ((op->do_long > 0) ? 0 : -1)); + return res; +} + /* Print one or more mode pages. Returns 0 if ok else IO error number. */ static int print_mode_pages(int sg_fd, int pn, int spn, int pdt, const struct sdparm_opt_coll * op) { - int res, pg_len, verb, smask, single_pg, fetch_pg, rep_len, orig_pn; - int warned, first_desc_off; + bool single_pg, fetch_pg, desc_part, warned, have_desc_id; + bool for_in_hex = (op->do_hex > 2); + bool mode6 = op->mode_6; + int res, pg_len, verb, smask, rep_len, req_len, orig_pn; + int desc_id, desc_len; + int desc0_off = 0; const struct sdparm_mode_page_item * mpi; - const struct sdparm_mode_page_item * hmpi; + const struct sdparm_mode_page_item * last_mpi; const struct sdparm_mode_page_item * fdesc_mpi = NULL; const struct sdparm_mode_page_t * mpp = NULL; const struct sdparm_mode_descriptor_t * mdp; - unsigned char cur_mp[DEF_MODE_RESP_LEN]; - unsigned char cha_mp[DEF_MODE_RESP_LEN]; - unsigned char def_mp[DEF_MODE_RESP_LEN]; - unsigned char sav_mp[DEF_MODE_RESP_LEN]; void * pc_arr[4]; - char buff[128]; + uint8_t * cur_mp; + uint8_t * cha_mp; + uint8_t * def_mp; + uint8_t * sav_mp; + uint8_t * first_mp; + char b[128]; - buff[0] = '\0'; + cur_mp = (MP_OM_CUR & op->out_mask) ? cur_aligned_mp : NULL; + cha_mp = (MP_OM_CHA & op->out_mask) ? cha_aligned_mp : NULL; + def_mp = (MP_OM_DEF & op->out_mask) ? def_aligned_mp : NULL; + sav_mp = (MP_OM_SAV & op->out_mask) ? sav_aligned_mp : NULL; + if (cur_mp) + first_mp = cur_mp; + else if (cha_mp) + first_mp = cha_mp; + else if (def_mp) + first_mp = def_mp; + else if (sav_mp) + first_mp = sav_mp; + else + first_mp = NULL; + b[0] = '\0'; + res = 0; verb = (op->verbose > 0) ? op->verbose - 1 : 0; - if ((0 == pdt) && (op->do_long > 0) && (0 == op->do_quiet)) { + if (((0 == pdt) && (op->do_long > 0) && (0 == op->do_quiet)) || + for_in_hex) { if (0 != (res = print_direct_access_info(sg_fd, op, verb))) return res; } + if (NULL == first_mp) { + if (for_in_hex) + printf("# "); + printf("No page control selected by --out_mask=OM\n"); + return 0; + } orig_pn = pn; /* choose a mode page item namespace (vendor, transport or generic) */ - if (op->vendor >= 0) { - const struct sdparm_vendor_pair * vpp; + if (op->vendor_id >= 0) { + const struct sdparm_vendor_pair * svpp; - vpp = sdp_get_vendor_pair(op->vendor); - mpi = (vpp ? vpp->mitem : NULL); + svpp = sdp_get_vendor_pair(op->vendor_id); + mpi = (svpp ? svpp->mitem : NULL); } else if ((op->transport >= 0) && (op->transport < 16)) mpi = sdparm_transport_mp[op->transport].mitem; - else + else /* generic */ mpi = sdparm_mitem_arr; if (NULL == mpi) return SG_LIB_CAT_OTHER; - hmpi = mpi; + last_mpi = mpi; if (pn >= 0) { /* step to first item of given page */ - single_pg = 1; - fetch_pg = 1; + single_pg = true; + fetch_pg = true; for ( ; mpi->acron; ++mpi) { - if ((pn == mpi->page_num) && (spn == mpi->subpage_num)) { - if ((pdt < 0) || (mpi->pdt < 0) || - (pdt == mpi->pdt) || op->flexible) + if ((pn == mpi->pg_num) && (spn == mpi->subpg_num)) { + if (pdt_s_eq(pdt, mpi->pdt_s) || op->flexible) break; } } if (NULL == mpi->acron) { /* page has no known fields */ if (op->do_hex) - mpi = hmpi; /* trick to enter main loop once */ + mpi = last_mpi; /* trick to enter main loop once */ + else if (op->examine) + return print_mode_page_hex(sg_fd, pn, spn, op); else { - sdp_get_mpage_name(pn, spn, pdt, op->transport, - op->vendor, 0, 0, buff, sizeof(buff)); - if ((op->vendor < 0) && ((0 == pn) || (pn >= 0x20))) - pr2serr("%s mode page seems to be vendor specific, try " - "'--vendor=VN'.\nOtherwise add '-H' to see page " - "in hex.\n", buff); + sdp_get_mpt_with_str(pn, spn, pdt, op->transport, + op->vendor_id, 0, false, sizeof(b), b); + if ((op->vendor_id < 0) && ((0 == pn) || (0x18 == pn) || + (0x19 == pn) || (pn >= 0x20))) + pr2serr("%s mode page may be transport or vendor " + "specific,\ntry '--transport=TN' or '--vendor=VN'" + ". Otherwise add '-H' to\nsee page in hex.\n", b); else pr2serr("%s mode page, no fields found, add '-H' to see " - "page in hex.\n", buff); + "page in hex.\n", b); } } - } else { /* want all so check all items in given namespace */ - single_pg = 0; - fetch_pg = 0; - mpi = hmpi; + } else { /* want all, so check all items in given namespace */ + single_pg = false; + fetch_pg = false; + mpi = last_mpi; } mdp = NULL; pg_len = 0; + have_desc_id = false; + desc_id = -1; + desc_len = -1; + req_len = mode6 ? DEF_MODE_6_RESP_LEN : DEF_MODE_RESP_LEN; /* starting at first match, loop over each mode page item in given * namespace */ - for (smask = 0, warned = 0 ; mpi->acron; ++mpi, fetch_pg = 0) { - if (0 == fetch_pg) { - if ((pn != mpi->page_num) || (spn != mpi->subpage_num)) { - if (fdesc_mpi) { - hmpi = mpi - 1; - if ((pn == hmpi->page_num) && (spn == hmpi->subpage_num)) - print_mpage_extra_desc(pc_arr, pg_len, mpp, - fdesc_mpi, hmpi, op, smask); - } - if (single_pg) - break; - fetch_pg = 1; - pn = mpi->page_num; - spn = mpi->subpage_num; - } else { - if ((pdt >=0) && (mpi->pdt >= 0) && - (pdt != mpi->pdt) && (0 == op->flexible)) + for (smask = 0, warned = false ; mpi->acron; ++mpi, fetch_pg = false) { + if (! fetch_pg) { + if ((pn == mpi->pg_num) && (spn == mpi->subpg_num)) { + if (! pdt_s_eq(pdt, mpi->pdt_s) && ! op->flexible) continue; if (! (((orig_pn >= 0) ? 1 : op->do_all) || (MF_COMMON & mpi->flags))) continue; + /* here if matches everything at the mpage level */ + } else { /* page changed? some clean up to do */ + if (fdesc_mpi) { + last_mpi = mpi - 1; + if ((pn == last_mpi->pg_num) && + (spn == last_mpi->subpg_num) && + (single_pg || op->do_all)) + print_mitem_desc_after1(pc_arr, pg_len, mpp, + fdesc_mpi, last_mpi, op, + smask, desc_len); + } + if (single_pg) + break; + if (! pdt_s_eq(pdt, mpi->pdt_s)) + continue; /* ... but pdt_s didn't match */ + fetch_pg = true; + pn = mpi->pg_num; + spn = mpi->subpg_num; } } if (fetch_pg) { pg_len = 0; /* Only fetch mode page when needed (e.g. item page changed) */ - mpp = sdp_get_mpage_name(pn, spn, pdt, op->transport, - op->vendor, op->do_long, op->do_hex, - buff, sizeof(buff)); + mpp = sdp_get_mpt_with_str(pn, spn, pdt, op->transport, + op->vendor_id, op->do_long, + (op->do_hex > 0), sizeof(b), b); smask = 0; - warned = 0; + warned = false; fdesc_mpi = NULL; pc_arr[0] = cur_mp; pc_arr[1] = cha_mp; pc_arr[2] = def_mp; pc_arr[3] = sav_mp; - res = sg_get_mode_page_controls(sg_fd, op->mode_6, pn, spn, - op->dbd, op->flexible, - DEF_MODE_RESP_LEN, &smask, + res = sg_get_mode_page_controls(sg_fd, mode6, pn, spn, op->dbd, + op->flexible, req_len, &smask, pc_arr, &rep_len, verb); - if (SG_LIB_CAT_INVALID_OP == res) { - if (op->mode_6) - pr2serr("6 byte MODE SENSE cdb not supported, try again " - "without '-6' option\n"); - else - pr2serr("10 byte MODE SENSE cdb not supported, try again " - "with '-6' option\n"); - return res; - } else if (SG_LIB_CAT_NOT_READY == res) { - pr2serr("MODE SENSE failed, device not ready\n"); - return res; - } else if (SG_LIB_CAT_UNIT_ATTENTION == res) { - pr2serr("mode sense command failed, unit attention\n"); - return res; - } else if (SG_LIB_CAT_ABORTED_COMMAND == res) { - pr2serr("mode sense command failed, aborted command\n"); - return res; - } + if (res && (SG_LIB_CAT_ILLEGAL_REQ != res)) + return report_error(res, mode6); /* check for mode page descriptors */ mdp = (mpp && (! op->do_hex)) ? mpp->mp_desc : NULL; - first_desc_off = mdp ? mdp->first_desc_off : 0; - if (first_desc_off > 1) { - for (res = 0, fdesc_mpi = mpi; - fdesc_mpi && (pn == fdesc_mpi->page_num) && - (spn == fdesc_mpi->subpage_num); ++fdesc_mpi) { - if (fdesc_mpi->start_byte >= first_desc_off) { - res = 1; + have_desc_id = mdp ? mdp->have_desc_id : false; + desc0_off = mdp ? mdp->first_desc_off : 0; + if (desc0_off > 1) { + for (desc_part = false, fdesc_mpi = mpi; + fdesc_mpi && (pn == fdesc_mpi->pg_num) && + (spn == fdesc_mpi->subpg_num); ++fdesc_mpi) { + if (fdesc_mpi->start_byte >= desc0_off) { + desc_part = true; break; } } - if (0 == res) + if (! desc_part) fdesc_mpi = NULL; } - if (op->num_desc) { - int num = 0; - uint64_t u; - - if (fdesc_mpi && (smask & 1)) { - if ((mdp->num_descs_inc < 0) && (mdp->desc_len > 0)) - u = sdp_get_big_endian(cur_mp + mdp->num_descs_off, 7, - mdp->num_descs_bytes * 8) / - mdp->desc_len; - else - u = sdp_get_big_endian(cur_mp + mdp->num_descs_off, 7, - mdp->num_descs_bytes * 8) + - mdp->num_descs_inc; - num = (int)u; - } - if (op->do_long) - printf("number of descriptors=%d\n", num); - else - printf("%d\n", num); + if (op->num_desc) { /* report number of descriptors */ + if (mdp && fdesc_mpi && (op->out_mask & smask)) + report_number_of_descriptors(first_mp, mdp, op); return 0; } - if (smask & 1) { - pg_len = sdp_get_mp_len(cur_mp); - printf("%s ", buff); + if ((smask & MP_OM_CUR) || (! (MP_OM_CUR & op->out_mask))) { + if (for_in_hex) + printf("# "); + + pg_len = sdp_mpage_len(first_mp); + printf("%s ", b); if (op->verbose) { if (spn) printf("[0x%x,0x%x] ", pn, spn); @@ -1048,34 +1208,44 @@ } printf("mode page"); if ((op->do_long > 1) || op->verbose) - printf(" [PS=%d]:\n", !!(cur_mp[0] & 0x80)); + printf(" [PS=%d]:\n", !!(first_mp[0] & 0x80)); else printf(":\n"); - check_mode_page(cur_mp, pn, pg_len, op); + check_mode_page(first_mp, pn, pg_len, op); if (op->do_hex) { - if (pg_len > (int)sizeof(cur_mp)) { + if (pg_len > req_len) { pr2serr(">> decoded page length too large=%d, trim\n", pg_len); - pg_len = sizeof(cur_mp); + pg_len = req_len; } - printf(" Current:\n"); - dStrHex((const char *)cur_mp, pg_len, 1); - if (smask & 2) { + if (smask & MP_OM_CUR) { + if (for_in_hex) + printf("#"); + printf(" Current:\n"); + hex2stdout(cur_mp, pg_len, for_in_hex ? -1 : 1); + } + if (smask & MP_OM_CHA) { + if (for_in_hex) + printf("#"); printf(" Changeable:\n"); - dStrHex((const char *)cha_mp, pg_len, 1); + hex2stdout(cha_mp, pg_len, for_in_hex ? -1 : 1); } - if (smask & 4) { + if (smask & MP_OM_DEF) { + if (for_in_hex) + printf("#"); printf(" Default:\n"); - dStrHex((const char *)def_mp, pg_len, 1); + hex2stdout(def_mp, pg_len, for_in_hex ? -1 : 1); } - if (smask & 8) { + if (smask & MP_OM_SAV) { + if (for_in_hex) + printf("#"); printf(" Saved:\n"); - dStrHex((const char *)sav_mp, pg_len, 1); + hex2stdout(sav_mp, pg_len, for_in_hex ? -1 : 1); } } - } else { - if (op->verbose || single_pg) { - pr2serr(">> %s mode %spage ", buff, (spn ? "sub" : "")); + } else { /* asked for current values and didn't get them */ + if (op->verbose || (single_pg && (! op->examine))) { + pr2serr(">> %s mode %spage ", b, (spn ? "sub" : "")); if (op->verbose) { if (spn) pr2serr("[0x%x,0x%x] ", pn, spn); @@ -1093,10 +1263,10 @@ } /* end of fetch_pg */ if (smask && (! op->do_hex)) { if (mpi->start_byte >= pg_len) { - if ((0 == op->flexible) && (0 == op->verbose)) + if ((! op->flexible) && (0 == op->verbose)) continue; // step over - if (0 == warned) { - warned = 1; + if (! warned) { + warned = true; if (op->flexible) pr2serr(" >> hereafter field position exceeds mode " "page length=%d\n", pg_len); @@ -1106,81 +1276,94 @@ continue; } } - if (0 == op->flexible) + if (! op->flexible) + continue; + } + if (have_desc_id && (mpi->start_byte == desc0_off) && + (! (mpi->flags & MF_CLASH_OK))) { + desc_id = 0xf & *(first_mp + mpi->start_byte); + desc_len = sg_get_unaligned_be16(first_mp + desc0_off + 2) + 4; + } + if (have_desc_id && (desc_id >= 0) && + (mpi->flags & MF_CLASH_OK)) { + if (desc_id != sdp_get_desc_id(mpi->flags)) continue; } - print_mp_arr_entry(" ", smask, mpi, pc_arr, 0, op); + print_mitem_pc_arr(" ", smask, mpi, pc_arr, false, op); } } /* end of mode page item loop */ - if (mpi && (NULL == mpi->acron) && fdesc_mpi) { + if ((0 == res) && mpi && (NULL == mpi->acron) && fdesc_mpi) { --mpi; - if ((pn == mpi->page_num) && (spn == mpi->subpage_num)) - print_mpage_extra_desc(pc_arr, pg_len, mpp, fdesc_mpi, mpi, - op, smask); + if ((pn == mpi->pg_num) && (spn == mpi->subpg_num)) + print_mitem_desc_after1(pc_arr, pg_len, mpp, fdesc_mpi, mpi, + op, smask, desc_len); } - return 0; + return res; } /* Print a mode page provided as argument. Returns 0 if ok else error * number. */ static int -print_mode_page_given(uint8_t * cur_mp, int cur_mp_len, +print_mode_page_inhex(uint8_t * msense_resp, int msense_resp_len, const struct sdparm_opt_coll * op) { - int res, smask, first_time, rep_len, orig_pn, warned, v, off; - int first_desc_off, pn, spn, pg_len, transport, vendor; + bool first_time, desc_part, warned, spf, have_desc_id; + bool mode6 = op->mode_6; + int smask, resp_len, orig_pn, v, off, desc0_off, pn, spn; + int pg_len, transport, vendor_id, desc_id, desc_len; const struct sdparm_mode_page_item * mpi; - const struct sdparm_mode_page_item * hmpi; + const struct sdparm_mode_page_item * last_mpi; const struct sdparm_mode_page_item * fdesc_mpi = NULL; const struct sdparm_mode_page_t * mpp = NULL; const struct sdparm_mode_descriptor_t * mdp; uint8_t * pg_p; void * pc_arr[4]; - char buff[128]; - char ebuff[128]; + char b[128]; - rep_len = op->mode_6 ? (cur_mp[0] + 1) : - (sg_get_unaligned_be16(cur_mp) + 2); - if (cur_mp_len < rep_len) { - pr2serr("given mode page is too short, given %d bytes, expected " - "%d\n", cur_mp_len, rep_len); - if (op->inhex_fn) - pr2serr("perhaps %s is a VPD page, if so add '-i'\n", - op->inhex_fn); + resp_len = sg_msense_calc_length(msense_resp, msense_resp_len, mode6, + NULL); + off = sg_mode_page_offset(msense_resp, msense_resp_len, mode6, NULL, 0); + if ((resp_len < 2) || (off < 2)) { + pr2serr("Couldn't decode %s as a %s(%d) command reponse\n", + (op->inhex_fn ? op->inhex_fn : "??"), ms_s, (mode6 ? 6 : 10)); + pr2serr("perhaps it is a VPD page, if so add '-i'\n"); return SG_LIB_CAT_OTHER; } - off = sg_mode_page_offset(cur_mp, cur_mp_len, op->mode_6, ebuff, - sizeof(ebuff)); - if (off < 0) { - pr2serr("%s\n", ebuff); + if (msense_resp_len < resp_len) { + pr2serr("given %s(%d) response is too short, given %d bytes, expected " + "%d\n", ms_s, (mode6 ? 6 : 10), msense_resp_len, resp_len); return SG_LIB_CAT_OTHER; } if ((0 == op->pdt) && (op->do_long > 0) && (0 == op->do_quiet)) { - v = cur_mp[op->mode_6 ? 2 : 3]; + v = msense_resp[mode6 ? 2 : 3]; printf(" Direct access device specific parameters: WP=%d " "DPOFUA=%d\n", !!(v & 0x80), !!(v & 0x10)); } and_again: - pg_p = cur_mp + off; + pg_p = msense_resp + off; pn = pg_p[0] & 0x3f; - pg_len = sdp_get_mp_len(pg_p); - spn = (pg_p[0] & 0x40) ? pg_p[1] : 0; + pg_len = sdp_mpage_len(pg_p); + spf = !! (pg_p[0] & 0x40); + spn = spf ? pg_p[1] : 0; pc_arr[0] = pg_p; pc_arr[1] = NULL; pc_arr[2] = NULL; pc_arr[3] = NULL; smask = 0x1; /* treat as single "current" page */ + have_desc_id = false; + desc_id = -1; + desc_len = -1; - buff[0] = '\0'; + b[0] = '\0'; orig_pn = pn; /* choose a mode page item namespace (vendor, transport or generic) */ transport = op->transport; - vendor = op->vendor; - if (vendor >= 0) { + vendor_id = op->vendor_id; + if (vendor_id >= 0) { const struct sdparm_vendor_pair * vpp; - vpp = sdp_get_vendor_pair(vendor); + vpp = sdp_get_vendor_pair(vendor_id); mpi = (vpp ? vpp->mitem : NULL); } else if ((transport >= 0) && (transport < 16)) mpi = sdparm_transport_mp[transport].mitem; @@ -1190,82 +1373,66 @@ return SG_LIB_CAT_OTHER; now_try_generic: - hmpi = mpi; + last_mpi = mpi; for ( ; mpi->acron; ++mpi) { - if ((pn == mpi->page_num) && (spn == mpi->subpage_num)) { - if ((op->pdt < 0) || (mpi->pdt < 0) || - (op->pdt == mpi->pdt) || op->flexible) + if ((pn == mpi->pg_num) && (spn == mpi->subpg_num)) { + if (pdt_s_eq(op->pdt, mpi->pdt_s) || op->flexible) break; } } if (NULL == mpi->acron) { /* page has no known fields */ - if (op->do_all && ((transport >= 0) || (vendor >= 0))) { + if (op->do_all && ((transport >= 0) || (vendor_id >= 0))) { if (transport >= 0) transport = -1; - if (vendor >= 0) - vendor = -1; + if (vendor_id >= 0) + vendor_id = -1; mpi = sdparm_mitem_arr; goto now_try_generic; } - sdp_get_mpage_name(pn, spn, op->pdt, transport, vendor, 0, 0, - buff, sizeof(buff)); - if ((vendor < 0) && ((0 == pn) || (pn >= 0x20))) - pr2serr("%s mode page seems to be vendor specific, try " - "'--vendor=VN'.\nOtherwise add '-H' to see page " - "in hex.\n", buff); + sdp_get_mpt_with_str(pn, spn, op->pdt, transport, vendor_id, 0, false, + sizeof(b), b); + if ((vendor_id < 0) && ((0 == pn) || (0x18 == pn) || (0x19 == pn) || + (pn >= 0x20))) + pr2serr("%s mode page may be transport or vendor specific,\n" + "try '--transport=TN' or '--vendor=VN'.\n", b); else - pr2serr("%s mode page, no fields found, add '-H' to see " - "page in hex.\n", buff); + pr2serr("%s mode page, no fields found. Add '-v' or '--v' " + "to\nsee more debug.\n", b); } mdp = NULL; /* starting at first match, loop over each mode page item in given * namespace */ - for (warned = 0, first_time = 1 ; mpi->acron; ++mpi) { + for (warned = false, first_time = true ; mpi->acron; ++mpi) { if (first_time) { - first_time = 0; - mpp = sdp_get_mpage_name(pn, spn, op->pdt, transport, vendor, - op->do_long, op->do_hex, buff, - sizeof(buff)); - warned = 0; + first_time = false; + mpp = sdp_get_mpt_with_str(pn, spn, op->pdt, transport, vendor_id, + op->do_long, (op->do_hex > 0), + sizeof(b), b); + warned = false; fdesc_mpi = NULL; /* check for mode page descriptors */ mdp = (mpp && (! op->do_hex)) ? mpp->mp_desc : NULL; - first_desc_off = mdp ? mdp->first_desc_off : 0; - if (first_desc_off > 1) { - for (res = 0, fdesc_mpi = mpi; - fdesc_mpi && (pn == fdesc_mpi->page_num) && - (spn == fdesc_mpi->subpage_num); ++fdesc_mpi) { - if (fdesc_mpi->start_byte >= first_desc_off) { - res = 1; + have_desc_id = mdp ? mdp->have_desc_id : false; + desc0_off = mdp ? mdp->first_desc_off : 0; + if (desc0_off > 1) { + for (desc_part = false, fdesc_mpi = mpi; + fdesc_mpi && (pn == fdesc_mpi->pg_num) && + (spn == fdesc_mpi->subpg_num); ++fdesc_mpi) { + if (fdesc_mpi->start_byte >= desc0_off) { + desc_part = true; break; } } - if (0 == res) + if (! desc_part) fdesc_mpi = NULL; } - if (op->num_desc) { - int num = 0; - uint64_t u; - - if (fdesc_mpi && (smask & 1)) { - if ((mdp->num_descs_inc < 0) && (mdp->desc_len > 0)) - u = sdp_get_big_endian(pg_p + mdp->num_descs_off, 7, - mdp->num_descs_bytes * 8) / - mdp->desc_len; - else - u = sdp_get_big_endian(pg_p + mdp->num_descs_off, 7, - mdp->num_descs_bytes * 8) + - mdp->num_descs_inc; - num = (int)u; - } - if (op->do_long) - printf("number of descriptors=%d\n", num); - else - printf("%d\n", num); + if (op->num_desc) { /* report number of descriptors */ + if (mdp && fdesc_mpi && (1 & smask)) + report_number_of_descriptors(pg_p, mdp, op); return 0; } - printf("%s ", buff); + printf("%s ", b); if (op->verbose) { if (spn) printf("[0x%x,0x%x] ", pn, spn); @@ -1279,29 +1446,30 @@ printf(":\n"); check_mode_page(pg_p, pn, pg_len, op); } else { /* if not first_time */ - if ((pn != mpi->page_num) || (spn != mpi->subpage_num)) { - if (fdesc_mpi) { - hmpi = mpi - 1; - if ((pn == hmpi->page_num) && (spn == hmpi->subpage_num)) - print_mpage_extra_desc(pc_arr, pg_len, mpp, - fdesc_mpi, hmpi, op, smask); - } - break; - } else { - if ((op->pdt >=0) && (mpi->pdt >= 0) && - (op->pdt != mpi->pdt) && (0 == op->flexible)) + if ((pn == mpi->pg_num) && (spn == mpi->subpg_num)) { + if (! pdt_s_eq(op->pdt, mpi->pdt_s) && ! op->flexible) continue; - if (! (((orig_pn >= 0) ? 1 : op->do_all) || + if (! (((orig_pn >= 0) ? true : op->do_all) || (MF_COMMON & mpi->flags))) continue; + } else { /* mode page or subpage changed */ + if (fdesc_mpi) { + last_mpi = mpi - 1; /* last_mpi that matched ... */ + if ((pn == last_mpi->pg_num) && + (spn == last_mpi->subpg_num)) + print_mitem_desc_after1(pc_arr, pg_len, mpp, + fdesc_mpi, last_mpi, op, + smask, desc_len); + } + break; } } if (smask && (! op->do_hex)) { if (mpi->start_byte >= pg_len) { - if ((0 == op->flexible) && (0 == op->verbose)) + if ((! op->flexible) && (0 == op->verbose)) continue; // step over - if (0 == warned) { - warned = 1; + if (! warned) { + warned = true; if (op->flexible) pr2serr(" >> hereafter field position exceeds mode " "page length=%d\n", pg_len); @@ -1311,72 +1479,82 @@ continue; } } - if (0 == op->flexible) + if (! op->flexible) + continue; + } + if (have_desc_id && (mpi->start_byte == desc0_off) && + (! (mpi->flags & MF_CLASH_OK))) { + desc_id = 0xf & *(pg_p + mpi->start_byte); + desc_len = sg_get_unaligned_be16(pg_p + desc0_off + 2) + 4; + } + if (have_desc_id && (desc_id >= 0) && + (mpi->flags & MF_CLASH_OK)) { + if (desc_id != sdp_get_desc_id(mpi->flags)) continue; } - print_mp_arr_entry(" ", smask, mpi, pc_arr, 0, op); + print_mitem_pc_arr(" ", smask, mpi, pc_arr, false, op); } } /* end of mode page item loop */ if (mpi && (NULL == mpi->acron) && fdesc_mpi) { --mpi; - if ((pn == mpi->page_num) && (spn == mpi->subpage_num)) - print_mpage_extra_desc(pc_arr, pg_len, mpp, fdesc_mpi, mpi, - op, smask); + if ((pn == mpi->pg_num) && (spn == mpi->subpg_num)) + print_mitem_desc_after1(pc_arr, pg_len, mpp, fdesc_mpi, mpi, + op, smask, desc_len); } if (op->do_all) { off += pg_len; - if ((off + 4) < rep_len) + if ((off + 4) < resp_len) goto and_again; } return 0; } -/* returns 1 when ok, else 0 */ -static int +/* returns true when ok, else false */ +static bool check_desc_convert_mpi(int desc_num, const struct sdparm_mode_page_t * mpp, const struct sdparm_mode_page_item * ref_mpi, struct sdparm_mode_page_item * out_mpi, - char * b, int b_len) + int b_len, char * b) { int n; - if (mpp && mpp->mp_desc && ref_mpi->acron) { + if (mpp && mpp->mp_desc && ref_mpi->acron && b) { *out_mpi = *ref_mpi; - strncpy(b, ref_mpi->acron, b_len); + sg_scnpr(b, b_len, "%s", ref_mpi->acron); b[(b_len > 10) ? (b_len - 8) : 4] = '\0'; n = strlen(b); snprintf(b + n, b_len - n, ".%d", desc_num); out_mpi->acron = b; - return 1; + return true; } else - return 0; + return false; } -/* returns 1 when ok, else 0 */ -static int +/* returns true when ok, else false */ +static bool desc_adjust_start_byte(int desc_num, const struct sdparm_mode_page_t * mpp, - unsigned char * cur_mp, int rep_len, + const uint8_t * cur_mp, int rep_len, struct sdparm_mode_page_item * mpi, const struct sdparm_opt_coll * op) { const struct sdparm_mode_descriptor_t * mdp; uint64_t u; - const unsigned char * ucp; + const uint8_t * bp; int d_off, sb_off, j; mdp = mpp->mp_desc; if ((mdp->num_descs_off < rep_len) && (mdp->num_descs_off < 64)) { if ((mdp->num_descs_inc < 0) && (mdp->desc_len > 0)) - u = sdp_get_big_endian(cur_mp + mdp->num_descs_off, 7, + u = sg_get_big_endian(cur_mp + mdp->num_descs_off, 7, mdp->num_descs_bytes * 8) / mdp->desc_len; else - u = sdp_get_big_endian(cur_mp + mdp->num_descs_off, 7, + u = sg_get_big_endian(cur_mp + mdp->num_descs_off, 7, mdp->num_descs_bytes * 8) + mdp->num_descs_inc; if ((uint64_t)desc_num < u) { if (mdp->desc_len > 0) { mpi->start_byte += (mdp->desc_len * desc_num); if (mpi->start_byte < rep_len) - return 1; + return true; } else if (mdp->desc_len_off > 0) { /* need to walk through variable length descriptors */ @@ -1390,15 +1568,14 @@ if (j == desc_num) { mpi->start_byte = d_off + sb_off; if (mpi->start_byte < rep_len) - return 1; + return true; else pr2serr(">> new start_byte exceeds current page " "...\n"); break; } - ucp = cur_mp + d_off + mdp->desc_len_off; - u = sdp_get_big_endian(ucp, 7, - mdp->desc_len_bytes * 8); + bp = cur_mp + d_off + mdp->desc_len_off; + u = sg_get_big_endian(bp, 7, mdp->desc_len_bytes * 8); d_off += mdp->desc_len_off + mdp->desc_len_bytes + u; if (d_off >= rep_len) { @@ -1412,75 +1589,88 @@ pr2serr(" >> mode page says it has only %d descriptors\n", (int)u); } - return 0; + return false; } /* Print one or more mode page items (from '--get='). Returns 0 if ok. */ static int -print_mode_items(int sg_fd, const struct sdparm_mode_page_settings * mps, - int pdt, const struct sdparm_opt_coll * op) +print_mitems(int sg_fd, const struct sdparm_mode_page_settings * mps, + int pdt, const struct sdparm_opt_coll * op) { - int k, res, verb, smask, pn, spn, warned, rep_len, len, desc_num, adapt; + bool adapt, warned; + bool mode6 = op->mode_6; + int k, verb, smask, pn, spn, rep_len, req_len, len, desc_num; + int res = 0; uint64_t u; int64_t val; const struct sdparm_mode_page_item * mpi; struct sdparm_mode_page_item ampi; const struct sdparm_mode_page_t * mpp = NULL; - unsigned char cur_mp[DEF_MODE_RESP_LEN]; - unsigned char cha_mp[DEF_MODE_RESP_LEN]; - unsigned char def_mp[DEF_MODE_RESP_LEN]; - unsigned char sav_mp[DEF_MODE_RESP_LEN]; + uint8_t * cur_mp; + uint8_t * cha_mp; + uint8_t * def_mp; + uint8_t * sav_mp; + uint8_t * free_cur_mp = NULL; + uint8_t * free_cha_mp = NULL; + uint8_t * free_def_mp = NULL; + uint8_t * free_sav_mp = NULL; const struct sdparm_mode_page_it_val * ivp; - char buff[128]; + char b[128]; char b_tmp[32]; void * pc_arr[4]; - warned = 0; + req_len = mode6 ? DEF_MODE_6_RESP_LEN : DEF_MODE_RESP_LEN; + cur_mp = sg_memalign(req_len, 0, &free_cur_mp, false); + cha_mp = sg_memalign(req_len, 0, &free_cha_mp, false); + def_mp = sg_memalign(req_len, 0, &free_def_mp, false); + sav_mp = sg_memalign(req_len, 0, &free_sav_mp, false); + if ((NULL == cur_mp) || (NULL == cha_mp) || (NULL == def_mp) || + (NULL == sav_mp)) { + pr2serr("%s: unable to allocate heap\n", __func__); + res = sg_convert_errno(ENOMEM); + goto out; + } + warned = false; verb = (op->verbose > 0) ? op->verbose - 1 : 0; for (k = 0, pn = 0, spn = 0; k < mps->num_it_vals; ++k) { ivp = &mps->it_vals[k]; val = ivp->val; desc_num = ivp->descriptor_num; mpi = &ivp->mpi; - mpp = sdp_get_mpage_name(mpi->page_num, mpi->subpage_num, mpi->pdt, - op->transport, op->vendor, op->do_long, - op->do_hex, buff, sizeof(buff)); + mpp = sdp_get_mpt_with_str(mpi->pg_num, mpi->subpg_num, mpi->pdt_s, + op->transport, op->vendor_id, op->do_long, + (op->do_hex > 0), sizeof(b), b); if (desc_num > 0) { - if (check_desc_convert_mpi(desc_num, mpp, mpi, &i, b_tmp, - sizeof(b_tmp))) { - adapt = 1; + if (check_desc_convert_mpi(desc_num, mpp, mpi, &i, + sizeof(b_tmp), b_tmp)) { + adapt = true; mpi = &i; } else { - pr2serr("can't decode descriptors for %s in %s mode page\n", - (mpi->acron ? mpi->acron : ""), buff); - return SG_LIB_SYNTAX_ERROR; + pr2serr("can't decode descriptors for %s in %s mode page\n", + (mpi->acron ? mpi->acron : ""), b); + res = SG_LIB_SYNTAX_ERROR; + goto out; } } else - adapt = 0; - if ((0 == k) || (pn != mpi->page_num) || (spn != mpi->subpage_num)) { - pn = mpi->page_num; - spn = mpi->subpage_num; + adapt = false; + if ((0 == k) || (pn != mpi->pg_num) || (spn != mpi->subpg_num)) { + pn = mpi->pg_num; + spn = mpi->subpg_num; smask = 0; res = 0; - switch (val) { + switch (val) { /* for --get==0|1|2 */ case 0: pc_arr[0] = cur_mp; pc_arr[1] = cha_mp; pc_arr[2] = def_mp; pc_arr[3] = sav_mp; - res = sg_get_mode_page_controls(sg_fd, op->mode_6, pn, spn, - op->dbd, op->flexible, DEF_MODE_RESP_LEN, - &smask, pc_arr, &rep_len, verb); break; case 1: - case 2: + case 2: /* signed integer */ pc_arr[0] = cur_mp; pc_arr[1] = NULL; pc_arr[2] = NULL; pc_arr[3] = NULL; - res = sg_get_mode_page_controls(sg_fd, op->mode_6, pn, spn, - op->dbd, op->flexible, DEF_MODE_RESP_LEN, - &smask, pc_arr, &rep_len, verb); break; default: if (mpi->acron) @@ -1488,25 +1678,15 @@ else pr2serr("bad value given to 0x%x:%d:%d\n", mpi->start_byte, mpi->start_bit, mpi->num_bits); - return SG_LIB_SYNTAX_ERROR; + res = SG_LIB_SYNTAX_ERROR; + goto out; } - if (SG_LIB_CAT_INVALID_OP == res) { - if (op->mode_6) - pr2serr("6 byte MODE SENSE cdb not supported, try again " - "without '-6' option\n"); - else - pr2serr("10 byte MODE SENSE cdb not supported, try again " - "with '-6' option\n"); - return res; - } else if (SG_LIB_CAT_NOT_READY == res) { - pr2serr("MODE SENSE failed, device not ready\n"); - return res; - } else if (SG_LIB_CAT_UNIT_ATTENTION == res) { - pr2serr("MODE SENSE failed, unit attention\n"); - return res; - } else if (SG_LIB_CAT_ABORTED_COMMAND == res) { - pr2serr("MODE SENSE failed, aborted command\n"); - return res; + res = sg_get_mode_page_controls(sg_fd, mode6, pn, spn, op->dbd, + op->flexible, req_len, &smask, + pc_arr, &rep_len, verb); + if (res && (SG_LIB_CAT_ILLEGAL_REQ != res)) { + res = report_error(res, mode6); + goto out; } if ((0 == smask) && res) { if (mpi->acron) @@ -1519,8 +1699,8 @@ else pr2serr("error %sin ", (verb ? "" : "(try adding '-vv') ")); - pr2serr("%s mode page\n", buff); - return res; + pr2serr("%s mode page\n", b); + goto out; } if (smask & 1) check_mode_page(cur_mp, pn, rep_len, op); @@ -1530,17 +1710,17 @@ &i, op)) { pr2serr(">> failed to find field acronym: %s in current " "page\n", mpi->acron); - return SG_LIB_CAT_OTHER; + res = SG_LIB_CAT_OTHER; + goto out; } } - if ((pdt >= 0) && (0 == warned) && mpi->acron && - (mpi->pdt >= 0) && (pdt != mpi->pdt)) { - warned = 1; + if (! warned && mpi->acron && ! pdt_s_eq(pdt, mpi->pdt_s)) { + warned = true; pr2serr(">> warning: peripheral device type (pdt) is 0x%x but " "acronym %s\n is associated with pdt 0x%x.\n", pdt, - ivp->mpi.acron, ivp->mpi.pdt); + ivp->mpi.acron, ivp->mpi.pdt_s); } - len = (smask & 1) ? sdp_get_mp_len(cur_mp) : 0; + len = (smask & 1) ? sdp_mpage_len(cur_mp) : 0; if (mpi->start_byte >= len) { pr2serr(">> warning: "); if (mpi->acron) @@ -1552,140 +1732,161 @@ if (! op->flexible) continue; } - if (0 == val) { + if (0 == val) { /* current, changed, default and saved; in hex */ if (op->do_hex) { - if (smask & 1) { - u = sdp_mp_get_value(mpi, cur_mp); + if (smask & MP_OM_CUR) { + u = sdp_mitem_get_value(mpi, cur_mp); printf("0x%02" PRIx64 " ", u); } else printf("- "); - if (smask & 2) { - u = sdp_mp_get_value(mpi, cha_mp); + if (smask & MP_OM_CHA) { + u = sdp_mitem_get_value(mpi, cha_mp); printf("0x%02" PRIx64 " ", u); } else printf("- "); - if (smask & 4) { - u = sdp_mp_get_value(mpi, def_mp); + if (smask & MP_OM_DEF) { + u = sdp_mitem_get_value(mpi, def_mp); printf("0x%02" PRIx64 " ", u); } else printf("- "); - if (smask & 8) { - u = sdp_mp_get_value(mpi, sav_mp); + if (smask & MP_OM_SAV) { + u = sdp_mitem_get_value(mpi, sav_mp); printf("0x%02" PRIx64 " ", u); } else printf("- "); printf("\n"); } else - print_mp_arr_entry("", smask, mpi, pc_arr, 0, op); - } else if (1 == val) { + print_mitem_pc_arr("", smask, mpi, pc_arr, false, op); + } else if (1 == val) { /* just current in hex */ if (op->do_hex) { - if (smask & 1) { - u = sdp_mp_get_value(mpi, cur_mp); + if (smask & MP_OM_CUR) { + u = sdp_mitem_get_value(mpi, cur_mp); printf("0x%02" PRIx64 " ", u); } else printf("- "); printf("\n"); } else - print_mp_arr_entry("", smask & 1, mpi, pc_arr, 0, op); - } else if (2 == val) { + print_mitem_pc_arr("", smask & 1, mpi, pc_arr, false, op); + } else if (2 == val) { /* just current in signed decimal */ if (op->do_hex) { - if (smask & 1) { - u = sdp_mp_get_value(mpi, cur_mp); - printf("%02" PRId64 " ", (int64_t)u); + if (smask & MP_OM_CUR) { + u = sdp_mitem_get_value(mpi, cur_mp); + sdp_print_signed_decimal(u, mpi->num_bits, true); } else printf("- "); printf("\n"); } else - print_mp_arr_entry("", smask & 1, mpi, pc_arr, 1, op); + print_mitem_pc_arr("", smask & 1, mpi, pc_arr, true, op); } } - return 0; +out: + if (free_cur_mp) + free(free_cur_mp); + if (free_cha_mp) + free(free_cha_mp); + if (free_def_mp) + free(free_def_mp); + if (free_sav_mp) + free(free_sav_mp); + return res; } -/* Return of 0 -> success, various SG_LIB_CAT_* positive values, - * -1 -> other failure */ +/* Return of 0 -> success, most errors indicated by various SG_LIB_CAT_* + * positive values, -1 -> other failures */ static int change_mode_page(int sg_fd, int pdt, const struct sdparm_mode_page_settings * mps, const struct sdparm_opt_coll * op) { + bool mode6 = op->mode_6; int k, off, md_len, len, res, desc_num, pn, spn; - char ebuff[EBUFF_SZ]; + int resid = 0; + int vb = op->verbose; const struct sdparm_mode_page_t * mpp = NULL; - unsigned char mdpg[MAX_MODE_DATA_LEN]; const struct sdparm_mode_page_it_val * ivp; const struct sdparm_mode_page_item * mpi; - struct sdparm_mode_page_item ampi; + const char * cdbLenS = mode6 ? "6": "10"; char b[128]; char b_tmp[32]; + char ebuff[EBUFF_SZ]; + uint8_t * mdpg = oth_aligned_mp; + struct sdparm_mode_page_item ampi; if (pdt >= 0) { /* sanity check: check acronym's pdt matches device's pdt */ for (k = 0; k < mps->num_it_vals; ++k) { ivp = &mps->it_vals[k]; - if (ivp->mpi.acron && (ivp->mpi.pdt >= 0) && - (pdt != ivp->mpi.pdt)) { + if (ivp->mpi.acron && ! pdt_s_eq(pdt, ivp->mpi.pdt_s)) { pr2serr("%s: peripheral device type (pdt) is 0x%x but " "acronym %s\n is associated with pdt 0x%x. To " "bypass use numeric addressing mode.\n", __func__, - pdt, ivp->mpi.acron, ivp->mpi.pdt); + pdt, ivp->mpi.acron, ivp->mpi.pdt_s); return SG_LIB_SYNTAX_ERROR; } } } - pn = mps->page_num; - spn = mps->subpage_num; - mpp = sdp_get_mpage_name(pn, spn, pdt, op->transport, op->vendor, - 0, 0, b, sizeof(b)); - memset(mdpg, 0, sizeof(mdpg)); - res = ll_mode_sense(sg_fd, op, pn, spn, mdpg, 4, op->verbose); + pn = mps->pg_num; + spn = mps->subpg_num; + mpp = sdp_get_mpt_with_str(pn, spn, pdt, op->transport, op->vendor_id, + 0, false, sizeof(b), b); + memset(mdpg, 0, MAX_MODE_DATA_LEN); + res = ll_mode_sense(sg_fd, op, pn, spn, mdpg, 4, &resid, vb); if (0 != res) { if (SG_LIB_CAT_INVALID_OP == res) { - if (op->mode_6) - pr2serr("6 byte MODE SENSE cdb not supported, try again " - "without '-6' option\n"); - else - pr2serr("10 byte MODE SENSE cdb not supported, try again " - "with '-6' option\n"); + pr2serr("%s byte %s cdb not supported, try again with%s '-6' " + "option\n", cdbLenS, ms_s, mode6 ? "out" : ""); } else { - char b[80]; + char bb[80]; - sg_get_category_sense_str(res, sizeof(b), b, op->verbose); - pr2serr("mode sense command: %s\n", b); + sg_get_category_sense_str(res, sizeof(bb), bb, vb); + pr2serr("%s command: %s\n", ms_s, bb); } pr2serr("%s: failed fetching page: %s\n", __func__, b); return res; } - md_len = op->mode_6 ? (mdpg[0] + 1) : (sg_get_unaligned_be16(mdpg) + 2); - if (md_len > (int)sizeof(mdpg)) { + if (resid > 0) { + pr2serr("%s: resid=%d implies even short mpage truncated\n", + __func__, resid); + return SG_LIB_CAT_MALFORMED; + } + md_len = mode6 ? (mdpg[0] + 1) : (sg_get_unaligned_be16(mdpg) + 2); + if (md_len > MAX_MODE_DATA_LEN) { pr2serr("%s: mode data length=%d exceeds allocation length=%d\n", - __func__, md_len, (int)sizeof(mdpg)); + __func__, md_len, MAX_MODE_DATA_LEN); return SG_LIB_CAT_MALFORMED; } - res = ll_mode_sense(sg_fd, op, pn, spn, mdpg, md_len, op->verbose); + res = ll_mode_sense(sg_fd, op, pn, spn, mdpg, md_len, &resid, vb); if (0 != res) { pr2serr("%s: failed fetching page: %s\n", __func__, b); return res; } - off = sg_mode_page_offset(mdpg, md_len, op->mode_6, ebuff, EBUFF_SZ); + if (resid > 0) { + md_len -= resid; + if (md_len < 4) { + pr2serr("%s: resid=%d implies mpage truncated\n", __func__, + resid); + return SG_LIB_CAT_MALFORMED; + } + } + off = sg_mode_page_offset(mdpg, md_len, mode6, ebuff, EBUFF_SZ); if (off < 0) { pr2serr("%s: page offset failed: %s\n", __func__, ebuff); return SG_LIB_CAT_MALFORMED; } - len = sdp_get_mp_len(mdpg + off); + len = sdp_mpage_len(mdpg + off); mdpg[0] = 0; /* mode data length reserved for mode select */ - if (! op->mode_6) + if (! mode6) mdpg[1] = 0; /* mode data length reserved for mode select */ if (PDT_DISK == pdt) /* entire disk specific parameters is ... */ - mdpg[op->mode_6 ? 2 : 3] = 0x00; /* reserved for mode select */ + mdpg[mode6 ? 2 : 3] = 0x00; /* reserved for mode select */ for (k = 0; k < mps->num_it_vals; ++k) { ivp = &mps->it_vals[k]; mpi = &ivp->mpi; desc_num = ivp->descriptor_num; if (desc_num > 0) { - if (check_desc_convert_mpi(desc_num, mpp, mpi, &i, b_tmp, - sizeof(b_tmp))) { + if (check_desc_convert_mpi(desc_num, mpp, mpi, &i, + sizeof(b_tmp), b_tmp)) { mpi = &i; if (! desc_adjust_start_byte(desc_num, mpp, mdpg + off, len, &i, op)) { @@ -1724,26 +1925,26 @@ ivp->val, mpi->num_bits); pr2serr(" applying anyway\n"); } - sdp_mp_set_value(ivp->val, mpi, mdpg + off); + sdp_mitem_set_value(ivp->val, mpi, mdpg + off); } if ((! (mdpg[off] & 0x80)) && op->save) { - pr2serr("%s: mode page indicates it is not savable but\n" + pr2serr("%s: mode page indicates it is not saveable but\n" " '--save' option given (try without it)\n", __func__); return SG_LIB_CAT_MALFORMED; } mdpg[off] &= 0x7f; /* mask out PS bit, reserved in mode select */ if (op->dummy) { pr2serr("Mode data that would have been written:\n"); - dStrHexErr((const char *)mdpg, md_len, 1); + hex2stderr(mdpg, md_len, 1); return 0; } - if (op->mode_6) - res = sg_ll_mode_select6(sg_fd, 1 /* PF */, op->save, mdpg, md_len, - 1, op->verbose); + if (mode6) + res = sg_ll_mode_select6(sg_fd, true /* PF */, op->save, mdpg, md_len, + true, vb); else - res = sg_ll_mode_select10(sg_fd, 1, op->save, mdpg, md_len, 1, - op->verbose); + res = sg_ll_mode_select10_v2(sg_fd, true /* PF */, false /* RTD */, + op->save, mdpg, md_len, true, vb); if (0 != res) { pr2serr("%s: failed setting page: %s\n", __func__, b); return res; @@ -1754,51 +1955,69 @@ /* Return of 0 -> success, various SG_LIB_CAT_* positive values, * -1 -> other failure */ static int -set_def_mode_page(int sg_fd, int pn, int spn, unsigned char * mode_pg, - int mode_pg_len, const struct sdparm_opt_coll * op) +set_def_mode_page(int sg_fd, int pn, int spn, + const uint8_t * mode_pg, int mode_pg_len, + const struct sdparm_opt_coll * op) { + bool mode6 = op->mode_6; int len, off, md_len; - unsigned char * mdp; - char ebuff[EBUFF_SZ]; + int resid = 0; int ret = -1; - char buff[128]; + uint8_t * mdp; + char ebuff[EBUFF_SZ]; + char b[128]; len = mode_pg_len + MODE_DATA_OVERHEAD; - mdp = (unsigned char *)malloc(len); + mdp = (uint8_t *)malloc(len); if (NULL ==mdp) { pr2serr("%s: malloc failed, out of memory\n", __func__); - return SG_LIB_FILE_ERROR; + return sg_convert_errno(ENOMEM); } memset(mdp, 0, len); - ret = ll_mode_sense(sg_fd, op, pn, spn, mdp, 4, op->verbose); + ret = ll_mode_sense(sg_fd, op, pn, spn, mdp, 4, &resid, op->verbose); if (0 != ret) { - sdp_get_mpage_name(pn, spn, -1, op->transport, op->vendor, 0, 0, - buff, sizeof(buff)); - pr2serr("%s: failed fetching page: %s\n", __func__, buff); + sdp_get_mpt_with_str(pn, spn, -1, op->transport, op->vendor_id, 0, + false, sizeof(b), b); + pr2serr("%s: failed fetching page: %s\n", __func__, b); goto err_out; } - md_len = op->mode_6 ? (mdp[0] + 1) : (sg_get_unaligned_be16(mdp) + 2); + if (resid > 0) { + pr2serr("%s: resid=%d implies even short mpage truncated\n", + __func__, resid); + ret = SG_LIB_CAT_MALFORMED; + goto err_out; + } + md_len = mode6 ? (mdp[0] + 1) : (sg_get_unaligned_be16(mdp) + 2); if (md_len > len) { pr2serr("%s: mode data length=%d exceeds allocation length=%d\n", __func__, md_len, len); ret = SG_LIB_CAT_MALFORMED; goto err_out; } - ret = ll_mode_sense(sg_fd, op, pn, spn, mdp, md_len, op->verbose); + ret = ll_mode_sense(sg_fd, op, pn, spn, mdp, md_len, &resid, op->verbose); if (0 != ret) { - sdp_get_mpage_name(pn, spn, -1, op->transport, op->vendor, - 0, 0, buff, sizeof(buff)); - pr2serr("%s: failed fetching page: %s\n", __func__, buff); + sdp_get_mpt_with_str(pn, spn, -1, op->transport, op->vendor_id, + 0, false, sizeof(b), b); + pr2serr("%s: failed fetching page: %s\n", __func__, b); goto err_out; } - off = sg_mode_page_offset(mdp, len, op->mode_6, ebuff, EBUFF_SZ); + if (resid > 0) { + md_len -= resid; + if (md_len < 4) { + pr2serr("%s: resid=%d implies mpage truncated\n", __func__, + resid); + ret = SG_LIB_CAT_MALFORMED; + goto err_out; + } + } + off = sg_mode_page_offset(mdp, len, mode6, ebuff, EBUFF_SZ); if (off < 0) { pr2serr("%s: page offset failed: %s\n", __func__, ebuff); ret = SG_LIB_CAT_MALFORMED; goto err_out; } mdp[0] = 0; /* mode data length reserved for mode select */ - if (! op->mode_6) + if (! mode6) mdp[1] = 0; /* mode data length reserved for mode select */ if ((md_len - off) > mode_pg_len) { pr2serr("%s: mode length length=%d exceeds new contents length=%d\n", @@ -1810,20 +2029,20 @@ mdp[off] &= 0x7f; /* mask out PS bit, reserved in mode select */ if (op->dummy) { pr2serr("Mode data that would have been written:\n"); - dStrHexErr((const char *)mdp, md_len, 1); + hex2stderr(mdp, md_len, 1); ret = 0; goto err_out; } - if (op->mode_6) - ret = sg_ll_mode_select6(sg_fd, 1 /* PF */, op->save, mdp, md_len, 1, - op->verbose); + if (mode6) + ret = sg_ll_mode_select6(sg_fd, true /* PF */, op->save, mdp, md_len, + true, op->verbose); else - ret = sg_ll_mode_select10(sg_fd, 1, op->save, mdp, md_len, 1, - op->verbose); + ret = sg_ll_mode_select10_v2(sg_fd, true, false /* RTD */, op->save, + mdp, md_len, true, op->verbose); if (0 != ret) { - sdp_get_mpage_name(pn, spn, -1, op->transport, op->vendor, - 0, 0, buff, sizeof(buff)); - pr2serr("%s: failed setting page: %s\n", __func__, buff); + sdp_get_mpt_with_str(pn, spn, -1, op->transport, op->vendor_id, + 0, false, sizeof(b), b); + pr2serr("%s: failed setting page: %s\n", __func__, b); goto err_out; } @@ -1836,93 +2055,110 @@ set_mp_defaults(int sg_fd, int pn, int spn, int pdt, const struct sdparm_opt_coll * op) { - int smask, res, len, rep_len; - unsigned char cur_mp[DEF_MODE_RESP_LEN]; - unsigned char def_mp[DEF_MODE_RESP_LEN]; - char buff[128]; + bool mode6 = op->mode_6; + int smask, req_len, len, rep_len; + int res = 0; + const char * cdbLenS = mode6 ? "6" : "10"; + uint8_t * cur_mp; + uint8_t * def_mp; + uint8_t * free_cur_mp = NULL; + uint8_t * free_def_mp = NULL; + char b[128]; void * pc_arr[4]; + req_len = mode6 ? DEF_MODE_6_RESP_LEN : DEF_MODE_RESP_LEN; + cur_mp = sg_memalign(req_len, 0, &free_cur_mp, false); + def_mp = sg_memalign(req_len, 0, &free_def_mp, false); + if ((NULL == cur_mp) || (NULL == def_mp)) { + pr2serr("%s: unable to allocate heap\n", __func__); + res = sg_convert_errno(ENOMEM); + goto out; + } smask = 0; pc_arr[0] = cur_mp; pc_arr[1] = NULL; pc_arr[2] = def_mp; pc_arr[3] = NULL; res = sg_get_mode_page_controls(sg_fd, op->mode_6, pn, spn, op->dbd, - op->flexible, DEF_MODE_RESP_LEN, &smask, pc_arr, - &rep_len, op->verbose); + op->flexible, req_len, &smask, pc_arr, + &rep_len, op->verbose); if (SG_LIB_CAT_INVALID_OP == res) { - if (op->mode_6) - pr2serr("6 byte MODE SENSE cdb not supported, try again without " - "'-6' option\n"); - else - pr2serr("10 byte MODE SENSE cdb not supported, try again with " - "'-6' option\n"); - return res; - } - else if (SG_LIB_CAT_NOT_READY == res) { - pr2serr("MODE SENSE failed, device not ready\n"); - return res; + pr2serr("%s byte %s cdb not supported, try again with%s '-6' " + "option\n", cdbLenS, ms_s, mode6 ? "out" : ""); + goto out; + } else if (SG_LIB_CAT_NOT_READY == res) { + pr2serr("%s(%s) failed, device not ready\n", ms_s, cdbLenS); + goto out; } else if (SG_LIB_CAT_ABORTED_COMMAND == res) { - pr2serr("MODE SENSE failed, aborted command\n"); - return res; + pr2serr("%s(%s) failed, aborted command\n", ms_s, cdbLenS); + goto out; } - if (op->verbose && (0 == op->flexible) && (rep_len > 0xa00)) { - sdp_get_mpage_name(pn, spn, pdt, op->transport, op->vendor, - 0, 0, buff, sizeof(buff)); + if (op->verbose && (! op->flexible) && (rep_len > 0xa00)) { + sdp_get_mpt_with_str(pn, spn, pdt, op->transport, op->vendor_id, + 0, false, sizeof(b), b); pr2serr("%s mode page length=%d too long, perhaps try '--flexible'\n", - buff, rep_len); + b, rep_len); } - if ((smask & 1)) { - len = sdp_get_mp_len(cur_mp); - if ((smask & 4)) { - return set_def_mode_page(sg_fd, pn, spn, def_mp, len, op); + if ((smask & MP_OM_CUR)) { + len = sdp_mpage_len(cur_mp); + if ((smask & MP_OM_SAV)) { + res = set_def_mode_page(sg_fd, pn, spn, def_mp, len, op); + goto out; } else { - sdp_get_mpage_name(pn, spn, pdt, op->transport, op->vendor, - 0, 0, buff, sizeof(buff)); - pr2serr(">> %s mode page (default) not supported\n", buff); - return SG_LIB_CAT_ILLEGAL_REQ; + sdp_get_mpt_with_str(pn, spn, pdt, op->transport, op->vendor_id, + 0, false, sizeof(b), b); + pr2serr(">> %s mode page (default) not supported\n", b); + res = SG_LIB_CAT_ILLEGAL_REQ; + goto out; } } else { - sdp_get_mpage_name(pn, spn, pdt, op->transport, op->vendor, - 0, 0, buff, sizeof(buff)); - pr2serr(">> %s mode page not supported\n", buff); - return SG_LIB_CAT_ILLEGAL_REQ; - } + sdp_get_mpt_with_str(pn, spn, pdt, op->transport, op->vendor_id, + 0, false, sizeof(b), b); + pr2serr(">> %s mode page not supported\n", b); + res = SG_LIB_CAT_ILLEGAL_REQ; + goto out; + } +out: + if (free_cur_mp) + free(free_cur_mp); + if (free_def_mp) + free(free_def_mp); + return res; } -/* Returns 64 bit signed integer given in either decimal or in hex. The - * hex number is either preceded by "0x" or followed by "h". Returns -1 - * on error (so check for "-1" string before using this function). */ -static int64_t -get_llnum(const char * buf) -{ - int res, len; - int64_t num; - uint64_t unum; - - if ((NULL == buf) || ('\0' == buf[0])) - return -1; - len = strlen(buf); - if (('0' == buf[0]) && (('x' == buf[1]) || ('X' == buf[1]))) { - res = sscanf(buf + 2, "%" SCNx64 "", &unum); - num = unum; - } else if ('H' == toupper(buf[len - 1])) { - res = sscanf(buf, "%" SCNx64 "", &unum); - num = unum; - } else - res = sscanf(buf, "%" SCNd64 "", &num); - return (1 == res) ? num : -1; +static int +set_all_page_defaults(int sg_fd, const struct sdparm_opt_coll * op) +{ + int ret; + + if (op->mode_6) + ret = sg_ll_mode_select6_v2(sg_fd, false /* PF */, true /* RTD */, + op->save, NULL, 0, true, op->verbose); + else + ret = sg_ll_mode_select10_v2(sg_fd, false /* PF */, true /* RTD */, + op->save, NULL, 0, true, op->verbose); + if (0 != ret) + pr2serr("%s: setting RTD (reset to defaults) bit failed\n", + __func__); + return ret; } -static int +/* Parse 'arg' given to command line --get=, --clear= or set= into 'mps' + * which contains an array ('mps->it_vals[]') used for output. 'arg' may be + * a comma separated list of item value=pairs. This function places + * 'mps->num_it_vals' elements in that array. If 'clear','get' are + * false,false then assume "--set="; true,true is invalid. Returns true if + * successful, else false. */ +static bool build_mp_settings(const char * arg, struct sdparm_mode_page_settings * mps, - struct sdparm_opt_coll * op, int clear, int get) + struct sdparm_opt_coll * op, bool clear, bool get) { - int len, b_sz, num, from, cont, colon; + bool cont; + int len, b_sz, num, from, colon; unsigned int u; int64_t ll; - char buff[64]; + char b[64]; char acron[64]; char vb[64]; const char * cp; @@ -1932,10 +2168,10 @@ const struct sdparm_mode_page_item * mpi; const struct sdparm_mode_page_item * prev_mpi; - b_sz = sizeof(buff) - 1; + b_sz = sizeof(b) - 1; cp = arg; while (mps->num_it_vals < MAX_MP_IT_VAL) { - memset(buff, 0, sizeof(buff)); + memset(b, 0, sizeof(b)); ivp = &mps->it_vals[mps->num_it_vals]; if ('\0' == *cp) break; @@ -1946,29 +2182,29 @@ ++cp; continue; } - strncpy(buff, cp, (len < b_sz ? len : b_sz)); + strncpy(b, cp, (len < b_sz ? len : b_sz)); } else - strncpy(buff, cp, b_sz); - colon = strchr(buff, ':') ? 1 : 0; - if ((isalpha(buff[0]) && (! colon)) || - (isdigit(buff[0]) && ('_' == buff[1]))) { /* expect acronym */ - ecp = strchr(buff, '='); + strncpy(b, cp, b_sz); + colon = strchr(b, ':') ? 1 : 0; + if ((isalpha(b[0]) && (! colon)) || + (isdigit(b[0]) && ('_' == b[1]))) { /* expect acronym */ + ecp = strchr(b, '='); if (ecp) { - strncpy(acron, buff, ecp - buff); - acron[ecp - buff] = '\0'; + strncpy(acron, b, ecp - b); + acron[ecp - b] = '\0'; strcpy(vb, ecp + 1); if (0 == strcmp("-1", vb)) ivp->val = -1; else { - ivp->val = get_llnum(vb); + ivp->val = sg_get_llnum_nomult(vb); if (-1 == ivp->val) { - pr2serr("unable to decode: %s value\n", buff); + pr2serr("unable to decode: %s value\n", b); pr2serr(" expected: [=]\n"); - return -1; + return false; } } } else { - strcpy(acron, buff); + strcpy(acron, b); ivp->val = ((clear || get) ? 0 : -1); } if ((ecp = strchr(acron, '.'))) { @@ -1976,29 +2212,28 @@ strncpy(acron, vb, ecp - acron); acron[ecp - acron] = '\0'; strcpy(vb, ecp + 1); - ivp->descriptor_num = get_llnum(vb); + ivp->descriptor_num = sg_get_llnum_nomult(vb); if (ivp->descriptor_num < 0) { - pr2serr("unable to decode: %s descriptor number\n", buff); + pr2serr("unable to decode: %s descriptor number\n", b); pr2serr(" expected: " "[.][=]\n"); - return -1; + return false; } } ivp->orig_val = ivp->val; from = 0; - cont = 0; + cont = false; prev_mpi = NULL; if (get) { do { - mpi = sdp_find_mitem_by_acron(acron, &from, - op->transport, - op->vendor); + mpi = sdp_find_mitem_by_acron(acron, &from, op->transport, + op->vendor_id); if (NULL == mpi) { if (cont) { mpi = prev_mpi; break; } - if ((op->vendor < 0) && (op->transport < 0)) { + if ((op->vendor_id < 0) && (op->transport < 0)) { from = 0; mpi = sdp_find_mitem_by_acron(acron, &from, DEF_TRANSPORT_PROTOCOL, -1); @@ -2008,43 +2243,43 @@ pr2serr(" [perhaps a '--transport=' " "or '--vendor=' option is " "needed]\n"); - return -1; + return false; } else /* keep going in this case */ op->transport = DEF_TRANSPORT_PROTOCOL; } else { pr2serr("couldn't find field acronym: %s\n", acron); - return -1; + return false; } } - if (mps->page_num < 0) { - mps->page_num = mpi->page_num; - mps->subpage_num = mpi->subpage_num; + if (mps->pg_num < 0) { + mps->pg_num = mpi->pg_num; + mps->subpg_num = mpi->subpg_num; break; } - cont = 1; + cont = true; prev_mpi = mpi; /* got acronym match but if not at specified pn,spn */ /* then keep searching */ - } while ((mps->page_num != mpi->page_num) || - (mps->subpage_num != mpi->subpage_num)); + } while ((mps->pg_num != mpi->pg_num) || + (mps->subpg_num != mpi->subpg_num)); } else { /* --set or --clear */ do { mpi = sdp_find_mitem_by_acron(acron, &from, - op->transport, op->vendor); + op->transport, op->vendor_id); if (NULL == mpi) { if (cont) { pr2serr("mode page of acronym: %s [0x%x,0x%x] " "doesn't match prior\n", acron, - prev_mpi->page_num, - prev_mpi->subpage_num); + prev_mpi->pg_num, + prev_mpi->subpg_num); pr2serr(" mode page: 0x%x,0x%x\n", - mps->page_num, mps->subpage_num); + mps->pg_num, mps->subpg_num); pr2serr("For '--set' and '--clear' all fields " "must be in the same mode page\n"); - return -1; + return false; } - if ((op->vendor < 0) && (op->transport < 0)) { + if ((op->vendor_id < 0) && (op->transport < 0)) { from = 0; mpi = sdp_find_mitem_by_acron(acron, &from, DEF_TRANSPORT_PROTOCOL, -1); @@ -2054,26 +2289,26 @@ pr2serr(" [perhaps a '--transport=' " "or '--vendor=' option is " "needed]\n"); - return -1; + return false; } else /* keep going in this case */ op->transport = DEF_TRANSPORT_PROTOCOL; } else { pr2serr("couldn't find field acronym: %s\n", acron); - return -1; + return false; } } - if (mps->page_num < 0) { - mps->page_num = mpi->page_num; - mps->subpage_num = mpi->subpage_num; + if (mps->pg_num < 0) { + mps->pg_num = mpi->pg_num; + mps->subpg_num = mpi->subpg_num; break; } - cont = 1; + cont = true; prev_mpi = mpi; /* got acronym match but if not at specified pn,spn */ /* then keep searching */ - } while ((mps->page_num != mpi->page_num) || - (mps->subpage_num != mpi->subpage_num)); + } while ((mps->pg_num != mpi->pg_num) || + (mps->subpg_num != mpi->subpg_num)); } if (mpi->num_bits < 64) { ll = 1; @@ -2082,29 +2317,29 @@ ivp->mpi = *mpi; /* struct assignment */ } else { /* expect "start_byte:start_bit:num_bits[=]" */ /* start_byte may be in hex ('0x' prefix or 'h' suffix) */ - if ((0 == strncmp("0x", buff, 2)) || - (0 == strncmp("0X", buff, 2))) { - num = sscanf(buff + 2, "%x:%d:%d=%62s", &u, + if ((0 == strncmp("0x", b, 2)) || + (0 == strncmp("0X", b, 2))) { + num = sscanf(b + 2, "%x:%d:%d=%62s", &u, &ivp->mpi.start_bit, &ivp->mpi.num_bits, vb); ivp->mpi.start_byte = u; } else { - if (strstr(buff, "h:")) { - num = sscanf(buff, "%xh:%d:%d=%62s", &u, + if (strstr(b, "h:")) { + num = sscanf(b, "%xh:%d:%d=%62s", &u, &ivp->mpi.start_bit, &ivp->mpi.num_bits, vb); ivp->mpi.start_byte = u; - } else if (strstr(buff, "H:")) { - num = sscanf(buff, "%xH:%d:%d=%62s", &u, + } else if (strstr(b, "H:")) { + num = sscanf(b, "%xH:%d:%d=%62s", &u, &ivp->mpi.start_bit, &ivp->mpi.num_bits, vb); ivp->mpi.start_byte = u; } else - num = sscanf(buff, "%d:%d:%d=%62s", &ivp->mpi.start_byte, + num = sscanf(b, "%d:%d:%d=%62s", &ivp->mpi.start_byte, &ivp->mpi.start_bit, &ivp->mpi.num_bits, vb); } if (num < 3) { - pr2serr("unable to decode: %s\n", buff); + pr2serr("unable to decode: %s\n", b); pr2serr(" expected: start_byte:start_bit:num_bits[=]" "\n"); - return -1; + return false; } if (3 == num) ivp->val = ((clear || get) ? 0 : -1); @@ -2112,40 +2347,40 @@ if (0 == strcmp("-1", vb)) ivp->val = -1; else { - ivp->val = get_llnum(vb); + ivp->val = sg_get_llnum_nomult(vb); if (-1 == ivp->val) { pr2serr("unable to decode start_byte:start_bit:" "num_bits value\n"); - return -1; + return false; } } } - ivp->mpi.pdt = -1; /* don't known pdt now, so don't restrict */ + ivp->mpi.pdt_s = -1; /* don't known pdt now, so don't restrict */ if (ivp->mpi.start_byte < 0) { pr2serr("need positive start byte offset\n"); - return -1; + return false; } if ((ivp->mpi.start_bit < 0) || (ivp->mpi.start_bit > 7)) { pr2serr("need start bit in 0..7 range (inclusive)\n"); - return -1; + return false; } if ((ivp->mpi.num_bits < 1) || (ivp->mpi.num_bits > 64)) { pr2serr("need number of bits in 1..64 range (inclusive)\n"); - return -1; + return false; } - if (mps->page_num < 0) { + if (mps->pg_num < 0) { pr2serr("need '--page=' option for mode page name or " "number\n"); - return -1; + return false; } else if (get) { - ivp->mpi.page_num = mps->page_num; - ivp->mpi.subpage_num = mps->subpage_num; + ivp->mpi.pg_num = mps->pg_num; + ivp->mpi.subpg_num = mps->subpg_num; } ivp->orig_val = ivp->val; if (ivp->mpi.num_bits < 64) { - int64_t ll = 1; + int64_t ll1 = 1; - ivp->val &= (ll << ivp->mpi.num_bits) - 1; + ivp->val &= (ll1 << ivp->mpi.num_bits) - 1; } } ++mps->num_it_vals; @@ -2154,12 +2389,16 @@ else break; } - return 0; + if (mps->num_it_vals >= MAX_MP_IT_VAL) + pr2serr("%s: maximum number (%d) of command line itm/value pairs " + "reached,\nignore rest\n", __func__, MAX_MP_IT_VAL); + return true; } +/* Returns open file descriptor ( >= 0) or negated sg3_utils error code. */ static int -open_and_simple_inquiry(const char * device_name, int rw, int * pdt, - int * protect, const struct sdparm_opt_coll * op) +open_and_simple_inquiry(const char * device_name, bool rw, int * pdt, + bool * protect, const struct sdparm_opt_coll * op) { int res, verb, sg_fd, l_pdt; struct sg_simple_inquiry_resp sir; @@ -2170,7 +2409,7 @@ if (sg_fd < 0) { pr2serr("open error: %s [%s]: %s\n", device_name, (rw ? "read/write" : "read only"), safe_strerror(-sg_fd)); - return -1; + return -sg_convert_errno(-sg_fd); } res = sg_simple_inquiry(sg_fd, &sir, 0, verb); if (res) { @@ -2179,27 +2418,31 @@ int sg_sg_fd; sg_sg_fd = map_if_lk24(sg_fd, device_name, rw, op->verbose); - if (sg_sg_fd < 0) + if (sg_sg_fd < 0) { + res = -SG_LIB_SYNTAX_ERROR; goto err_out; + } sg_cmds_close_device(sg_fd); sg_fd = sg_sg_fd; res = sg_simple_inquiry(sg_fd, &sir, 0, verb); - if (sg_sg_fd < 0) + if (res < 0) goto err_out; } #endif /* SG_LIB_LINUX */ if (res) { pr2serr("SCSI INQUIRY command failed on %s\n", device_name); + if (res > 0) + res = -res; goto err_out; } } l_pdt = sir.peripheral_type; if ((PDT_WO == l_pdt) || (PDT_OPTICAL == l_pdt)) - *pdt = PDT_DISK; /* map disk-like pdt's to PDT_DOSK */ + *pdt = PDT_DISK; /* map disk-like pdt's to PDT_DISK */ else *pdt = l_pdt; if (protect) - *protect = sir.byte_5 & 0x1; /* PROTECT bit SPC-3 and later */ + *protect = !! (sir.byte_5 & 0x1); /* PROTECT bit SPC-3 and later */ if ((0 == op->do_hex) && (0 == op->do_quiet)) { printf(" %s: %.8s %.16s %.4s", device_name, sir.vendor, sir.product, sir.revision); @@ -2211,14 +2454,14 @@ err_out: sg_cmds_close_device(sg_fd); - return -1; + return res; } /* Process mode page(s) operation. Returns 0 if successful */ static int -process_mode(int sg_fd, const struct sdparm_mode_page_settings * mps, int pn, - int spn, int set_clear, int get, - const struct sdparm_opt_coll * op, int pdt) +process_mode_page(int sg_fd, const struct sdparm_mode_page_settings * mps, + int pn, int spn, bool set_clear, bool get, + const struct sdparm_opt_coll * op, int pdt) { int res; const struct sdparm_mode_page_t * mpp; @@ -2234,19 +2477,21 @@ return SG_LIB_SYNTAX_ERROR; } if ((pn > 0) && (pdt >= 0)) { - mpp = sdp_get_mode_detail(pn, spn, pdt, op->transport, op->vendor); + mpp = sdp_get_mpage_t(pn, spn, pdt, op->transport, op->vendor_id); if (NULL == mpp) - mpp = sdp_get_mode_detail(pn, spn, -1, op->transport, op->vendor); - if (mpp && mpp->name && (mpp->pdt >= 0) && (pdt != mpp->pdt)) { + mpp = sdp_get_mpage_t(pn, spn, -1, op->transport, op->vendor_id); + if (mpp && mpp->name && ! pdt_s_eq(pdt, mpp->pdt_s)) { pr2serr(">> Warning: %s mode page associated with\n", mpp->name); pr2serr(" peripheral device type 0x%x but device pdt is 0x%x\n", - mpp->pdt, pdt); + mpp->pdt_s, pdt); if (! op->flexible) pr2serr(" may need '--flexible' option to override\n"); } } - if (op->defaults) + if (1 == op->defaults) res = set_mp_defaults(sg_fd, pn, spn, pdt, op); + else if (op->defaults > 1) + res = set_all_page_defaults(sg_fd, op); else if (set_clear) { if (mps->num_it_vals < 1) { pr2serr("no fields found to set or clear\n"); @@ -2260,7 +2505,7 @@ pr2serr("no fields found to get\n"); return SG_LIB_CAT_OTHER; } - res = print_mode_items(sg_fd, mps, pdt, op); + res = print_mitems(sg_fd, mps, pdt, op); } else res = print_mode_pages(sg_fd, pn, spn, pdt, op); return res; @@ -2270,8 +2515,19 @@ int main(int argc, char * argv[]) { +#ifdef SG_LIB_WIN32 + int do_wscan = 0; +#endif + bool protect = false; + bool rw = false; /* true: requires RDWR, false: perhaps RDONLY ok */ + bool set_clear = false; int sg_fd, res, c, pdt, req_pdt, k, orig_transport, r; - struct sdparm_opt_coll opts; + int cmd_arg = -1; + int do_help = 0; + int num_devices = 0; + int pn = -1; + int spn = -1; + int ret = 0; struct sdparm_opt_coll * op; const char * clear_str = NULL; const char * cmd_str = NULL; @@ -2279,32 +2535,23 @@ const char * set_str = NULL; const char * page_str = NULL; const char * device_name_arr[MAX_DEV_NAMES]; - int num_devices = 0; - int pn = -1; - int spn = -1; - int rw = 0; /* 1: requires RDWR, 0: perhaps RDONLY ok */ - int set_clear = 0; - int protect = 0; - int do_help = 0; - int cmd_arg = -1; const struct sdparm_mode_page_t * mpp = NULL; - const struct sdparm_transport_id_t * tip; + int t_proto; const struct sdparm_vpd_page_t * vpp = NULL; const struct sdparm_vendor_name_t * vnp; - struct sdparm_mode_page_settings mp_settings; char * cp; const char * ccp; const struct sdparm_command_t * scmdp = NULL; - int ret = 0; -#ifdef SG_LIB_WIN32 - int do_wscan = 0; -#endif + struct sdparm_opt_coll opts; + struct sdparm_mode_page_settings mp_settings; + char b[256]; op = &opts; memset(op, 0, sizeof(opts)); - op->transport = -1; - op->vendor = -1; + op->out_mask = MP_OM_ALL; /* 0xf */ op->pdt = -1; + op->transport = -1; + op->vendor_id = -1; memset(device_name_arr, 0, sizeof(device_name_arr)); memset(&mp_settings, 0, sizeof(mp_settings)); pdt = -1; @@ -2312,10 +2559,11 @@ int option_index = 0; #ifdef SG_LIB_WIN32 - c = getopt_long(argc, argv, "6aBc:C:dDefg:hHiI:lM:np:P:qrRs:St:vVw", + c = getopt_long(argc, argv, + "6aBc:C:dDEefg:hHiI:lM:no:p:P:qrRs:St:vVw", long_options, &option_index); #else - c = getopt_long(argc, argv, "6aBc:C:dDefg:hHiI:lM:np:P:qrRs:St:vV", + c = getopt_long(argc, argv, "6aBc:C:dDeEfg:hHiI:lM:no:p:P:qrRs:St:vV", long_options, &option_index); #endif if (c == -1) @@ -2323,46 +2571,53 @@ switch (c) { case '6': - ++op->mode_6; + op->mode_6 = true; break; case 'a': ++op->do_all; break; case 'B': - ++op->dbd; + op->dbd = true; break; case 'c': clear_str = optarg; - set_clear = 1; - rw = 1; + set_clear = true; + rw = true; break; case 'C': cmd_str = optarg; + break; case 'd': - ++op->dummy; + op->dummy = true; break; case 'D': ++op->defaults; - rw = 1; + rw = true; break; case 'e': ++op->do_enum; break; + case 'E': + op->examine = true; + break; case 'f': - ++op->flexible; + op->flexible = true; break; case 'g': get_str = optarg; break; case 'h': - case '?': ++do_help; break; + case '?': + pr2serr("\n"); + usage((do_help > 0) ? do_help : 0); + return SG_LIB_SYNTAX_ERROR; case 'H': ++op->do_hex; break; case 'i': - ++op->inquiry; + op->inquiry = true; break; case 'I': op->inhex_fn = optarg; @@ -2379,24 +2634,32 @@ enumerate_vendors(); return SG_LIB_SYNTAX_ERROR; } else - op->vendor = vnp->vendor_num; + op->vendor_id = vnp->vendor_id; } else { - const struct sdparm_vendor_pair * vpp; + const struct sdparm_vendor_pair * svpp; res = sg_get_num_nomult(optarg); - vpp = sdp_get_vendor_pair(res); - if (NULL == vpp) { + svpp = sdp_get_vendor_pair(res); + if (NULL == svpp) { pr2serr("Bad vendor value after '-M' (or '--vendor=') " "option\n"); printf("Available vendors:\n"); enumerate_vendors(); return SG_LIB_SYNTAX_ERROR; } - op->vendor = res; + op->vendor_id = res; } break; case 'n': - ++op->num_desc; + op->num_desc = true; + break; + case 'o': + res = sg_get_num_nomult(optarg); + if ((res < 0) || (res > 15)) { + pr2serr("Bad out-mask value, expect 0 to 15 (or 0xf)\n"); + return SG_LIB_SYNTAX_ERROR; + } + op->out_mask = res; break; case 'q': ++op->do_quiet; @@ -2404,8 +2667,8 @@ case 'p': if (page_str) { pr2serr("only one '--page=' option permitted\n"); - usage(1); - return SG_LIB_SYNTAX_ERROR; + usage((do_help > 0) ? do_help : 0); + return SG_LIB_CONTRADICT; } else page_str = optarg; break; @@ -2421,47 +2684,48 @@ } break; case 'r': - ++op->read_only; + op->read_only = true; break; case 'R': - ++op->do_raw; + op->do_raw = true; break; case 's': set_str = optarg; - rw = 1; - set_clear = 1; + rw = true; + set_clear = true; break; case 'S': - ++op->save; + op->save = true; break; case 't': if (isalpha(optarg[0])) { - tip = sdp_find_transport_by_acron(optarg); - if (NULL == tip) { + t_proto = sdp_find_transport_id_by_acron(optarg); + if (t_proto < 0) { pr2serr("abbreviation does not match a transport " "protocol\n"); printf("Available transport protocols:\n"); - enumerate_transports(); + enumerate_transports(true, op); return SG_LIB_SYNTAX_ERROR; } else - op->transport = tip->proto_num; + op->transport = t_proto; } else { res = sg_get_num_nomult(optarg); if ((res < 0) || (res > 15)) { pr2serr("Bad transport value after '-t' option\n"); printf("Available transport protocols:\n"); - enumerate_transports(); + enumerate_transports(false, op); return SG_LIB_SYNTAX_ERROR; } op->transport = res; } break; case 'v': + op->verbose_given = true; ++op->verbose; break; case 'V': - pr2serr("version: %s\n", version_str); - return 0; + op->version_given = true; + break; #ifdef SG_LIB_WIN32 case 'w': ++do_wscan; @@ -2469,7 +2733,7 @@ #endif default: pr2serr("unrecognised option code 0x%x ??\n", c); - usage(1); + usage((do_help > 0) ? do_help : 0); return SG_LIB_SYNTAX_ERROR; } } @@ -2480,22 +2744,43 @@ } else { for (; optind < argc; ++optind) pr2serr("Unexpected extra argument: %s\n", argv[optind]); - usage(1); + usage((do_help > 0) ? do_help : 0); return SG_LIB_SYNTAX_ERROR; } } - if (do_help) { +#ifdef DEBUG + pr2serr("In DEBUG mode, "); + if (op->verbose_given && op->version_given) { + pr2serr("but override: '-vV' given, zero verbose and continue\n"); + op->verbose_given = false; + op->version_given = false; + op->verbose = 0; + } else if (! op->verbose_given) { + pr2serr("set '-vv'\n"); + op->verbose = 2; + } else + pr2serr("keep verbose=%d\n", op->verbose); +#else + if (op->verbose_given && op->version_given) + pr2serr("Not in DEBUG mode, so '-vV' has no special action\n"); +#endif + if (op->version_given) { + pr2serr("version: %s\n", version_str); + return 0; + } + + if (do_help > 0) { usage(do_help); return 0; } if (op->read_only) - rw = 0; // override any read-write settings + rw = false; // override any read-write settings if ((!!get_str + !!set_str + !!clear_str) > 1) { pr2serr("Can only give one of '--get=', '--set=' and '--clear='\n"); - return SG_LIB_SYNTAX_ERROR; + return SG_LIB_CONTRADICT; } #ifdef SG_LIB_WIN32 if (do_wscan) @@ -2504,26 +2789,26 @@ if (page_str) { if ((0 == strcmp("-1", page_str)) || (0 == strcmp("-2", page_str))) { - op->inquiry = 1; + op->inquiry = true; pn = VPD_NOT_STD_INQ; } else if (isalpha(page_str[0])) { while ( 1 ) { /* dummy loop, just using break */ - mpp = sdp_find_mp_by_acron(page_str, op->transport, - op->vendor); + mpp = sdp_find_mpt_by_acron(page_str, op->transport, + op->vendor_id); if (mpp) break; vpp = sdp_find_vpd_by_acron(page_str); if (vpp) break; orig_transport = op->transport; - if ((op->vendor < 0) && (op->transport < 0)) { + if ((op->vendor_id < 0) && (op->transport < 0)) { op->transport = DEF_TRANSPORT_PROTOCOL; - mpp = sdp_find_mp_by_acron(page_str, op->transport, - op->vendor); + mpp = sdp_find_mpt_by_acron(page_str, op->transport, + op->vendor_id); if (mpp) break; } - if ((op->vendor < 0) && (orig_transport < 0)) + if ((op->vendor_id < 0) && (orig_transport < 0)) pr2serr("abbreviation matches neither a mode page nor " "a VPD page\n" " [perhaps a '--transport=' or " @@ -2537,30 +2822,30 @@ return SG_LIB_SYNTAX_ERROR; } else { printf("available mode pages"); - if (op->vendor >= 0) + if (op->vendor_id >= 0) printf(" (for given vendor):\n"); else if (orig_transport >= 0) printf(" (for given transport):\n"); else printf(":\n"); - enumerate_mpages(orig_transport, op->vendor); + enumerate_mpages(orig_transport, op->vendor_id); return SG_LIB_SYNTAX_ERROR; } } if (vpp) { pn = vpp->vpd_num; spn = vpp->subvalue; - op->inquiry = 1; - pdt = vpp->pdt; + op->inquiry = true; + pdt = vpp->pdt_s; } else { if (op->inquiry) { pr2serr("matched mode page acronym but given '-i' so " "expecting a VPD page\n"); - return SG_LIB_SYNTAX_ERROR; + return SG_LIB_CONTRADICT; } pn = mpp->page; spn = mpp->subpage; - pdt = mpp->pdt; + pdt = mpp->pdt_s; } } else { /* got page_str and first char probably numeric */ cp = (char *)strchr(page_str, ','); @@ -2573,13 +2858,13 @@ return SG_LIB_SYNTAX_ERROR; } else { printf("available mode pages"); - if (op->vendor >= 0) + if (op->vendor_id >= 0) printf(" (for given vendor):\n"); else if (op->transport >= 0) printf(" (for given transport):\n"); else printf(":\n"); - enumerate_mpages(op->transport, op->vendor); + enumerate_mpages(op->transport, op->vendor_id); return SG_LIB_SYNTAX_ERROR; } } @@ -2594,12 +2879,12 @@ } } - if (op->inquiry) { + if (op->inquiry) { /* VPD pages or standard INQUIRY response */ if (set_clear || get_str || cmd_str || op->defaults || op->save) { pr2serr("'--inquiry' option lists VPD pages so other options " "that are\nconcerned with mode pages are " "inappropriate\n"); - return SG_LIB_SYNTAX_ERROR; + return SG_LIB_CONTRADICT; } if (pn > 255) { pr2serr("VPD page numbers are from 0 to 255\n"); @@ -2611,10 +2896,10 @@ return 0; } } else if (cmd_str) { - if (set_clear || get_str || op->defaults || op->save) { + if (set_clear || get_str || op->defaults || op->save || op->examine) { pr2serr("'--command=' option is not valid with other " "options that are\nconcerned with mode pages\n"); - return SG_LIB_SYNTAX_ERROR; + return SG_LIB_CONTRADICT; } if (op->do_enum) { printf("Available commands:\n"); @@ -2629,23 +2914,22 @@ return SG_LIB_SYNTAX_ERROR; } if (op->read_only) - rw = 0; // override any read-write settings - } else { - /* assume mode pages */ + rw = false; // override any read-write settings + } else { /* assume mode page access */ if (pn < 0) { - mp_settings.page_num = -1; - mp_settings.subpage_num = -1; + mp_settings.pg_num = -1; + mp_settings.subpg_num = -1; } else { - mp_settings.page_num = pn; - mp_settings.subpage_num = spn; + mp_settings.pg_num = pn; + mp_settings.subpg_num = spn; } if (get_str) { if (set_clear) { pr2serr("'--get=' can't be used with '--set=' or " "'--clear='\n"); - return SG_LIB_SYNTAX_ERROR; + return SG_LIB_CONTRADICT; } - if (build_mp_settings(get_str, &mp_settings, op, 0, 1)) + if (! build_mp_settings(get_str, &mp_settings, op, false, true)) return SG_LIB_SYNTAX_ERROR; } if (op->do_enum) { @@ -2654,20 +2938,20 @@ printf(" as well as most options are ignored " "when '--enumerate' is given\n"); if (pn < 0) { - if (op->vendor >= 0) { - ccp = sdp_get_vendor_name(op->vendor); + if (op->vendor_id >= 0) { + ccp = sdp_get_vendor_name(op->vendor_id); if (ccp) printf("Mode pages for %s vendor:\n", ccp); else - printf("Mode pages for vendor 0x%x:\n", op->vendor); + printf("Mode pages for vendor 0x%x:\n", op->vendor_id); if (op->do_all) enumerate_mitems(pn, spn, pdt, op); else { - enumerate_mpages(op->transport, op->vendor); + enumerate_mpages(op->transport, op->vendor_id); } } else if (op->transport >= 0) { - ccp = sdp_get_transport_name(op->transport); - if (ccp) + ccp = sdp_get_transport_name(op->transport, sizeof(b), b); + if (strlen(ccp) > 0) printf("Mode pages for %s transport protocol:\n", ccp); else @@ -2676,7 +2960,7 @@ if (op->do_all) enumerate_mitems(pn, spn, pdt, op); else { - enumerate_mpages(op->transport, op->vendor); + enumerate_mpages(op->transport, op->vendor_id); } } else { /* neither vendor nor transport given */ if (op->do_long || (op->do_enum > 1)) { @@ -2685,7 +2969,7 @@ enumerate_mpages(-1, -1); printf("\n"); printf("Transport protocols:\n"); - enumerate_transports(); + enumerate_transports(false, op); printf("\n"); printf("Vendors:\n"); enumerate_vendors(); @@ -2695,31 +2979,35 @@ printf("\n"); t_opts = opts; enumerate_mitems(pn, spn, pdt, op); - for (k = 0; k < 16; ++k) { - ccp = sdp_get_transport_name(k); - if (NULL == ccp) + for (k = 0; k < 15; ++k) { + /* skip "no specific protocol" (0xf) */ + ccp = sdp_get_transport_name(k, sizeof(b), b); + if (0 == strlen(ccp)) continue; printf("\n"); printf("Mode pages for %s transport " "protocol:\n", ccp); t_opts.transport = k; - t_opts.vendor = -1; + t_opts.vendor_id = -1; enumerate_mitems(pn, spn, pdt, &t_opts); } for (k = 0; k < sdparm_vendor_mp_len; ++k) { + if (VENDOR_NONE == k) + continue; ccp = sdp_get_vendor_name(k); if (NULL == ccp) continue; printf("\n"); printf("Mode pages for %s vendor:\n", ccp); t_opts.transport = -1; - t_opts.vendor = k; + t_opts.vendor_id = k; enumerate_mitems(pn, spn, pdt, &t_opts); } } else { - for (k = 0; k < 16; ++k) { - ccp = sdp_get_transport_name(k); - if (NULL == ccp) + for (k = 0; k < 15; ++k) { + /* skip "no specific protocol" (0xf) */ + ccp = sdp_get_transport_name(k, sizeof(b), b); + if (0 == strlen(ccp)) continue; printf("\n"); printf("Mode pages for %s transport " @@ -2727,6 +3015,8 @@ enumerate_mpages(k, -1); } for (k = 0; k < sdparm_vendor_mp_len; ++k) { + if (VENDOR_NONE == k) + continue; ccp = sdp_get_vendor_name(k); if (NULL == ccp) continue; @@ -2739,7 +3029,7 @@ printf("Commands:\n"); sdp_enumerate_commands(); } else { - printf("Mode pages:\n"); + printf("Generic mode pages:\n"); enumerate_mpages(-1, -1); if (op->do_all) enumerate_mitems(pn, spn, pdt, op); @@ -2750,64 +3040,83 @@ return 0; } - if ((op->num_desc > 0) && (pn < 0)) { + if (op->num_desc && (pn < 0)) { pr2serr("when '--num-desc' is given an explicit mode page is " "required\n"); - return SG_LIB_SYNTAX_ERROR; + return SG_LIB_CONTRADICT; } if (op->defaults && (set_clear || get_str)) { pr2serr("'--get=', '--set=' or '--clear=' " "can't be used with '--defaults'\n"); - return SG_LIB_SYNTAX_ERROR; + return SG_LIB_CONTRADICT; } if (set_str) { - if (build_mp_settings(set_str, &mp_settings, op, 0, 0)) + if (! build_mp_settings(set_str, &mp_settings, op, false, false)) return SG_LIB_SYNTAX_ERROR; } if (clear_str) { - if (build_mp_settings(clear_str, &mp_settings, op, 1, 0)) + if (! build_mp_settings(clear_str, &mp_settings, op, true, false)) return SG_LIB_SYNTAX_ERROR; } if (op->verbose && (mp_settings.num_it_vals > 0)) list_mp_settings(&mp_settings, (NULL != get_str)); - if (op->defaults && (pn < 0)) { - pr2serr("to set defaults, the '--page=' option must be used\n"); - return SG_LIB_SYNTAX_ERROR; + if ((1 == op->defaults) && (pn < 0)) { + pr2serr("to set a page's defaults, the '--page=' option must be " + "used\n"); + return SG_LIB_CONTRADICT; } } if (op->inhex_fn) { - int inhex_len; + int inhex_len = 0; if (num_devices > 0) { pr2serr("Cannot have both a DEVICE and --inhex= option\n"); - return SG_LIB_SYNTAX_ERROR; + return SG_LIB_CONTRADICT; } - if (f2hex_arr(op->inhex_fn, op->do_raw, 0, inhex_buff, &inhex_len, - sizeof(inhex_buff))) - return SG_LIB_FILE_ERROR; + res = sg_f2hex_arr(op->inhex_fn, op->do_raw, false, inhex_buff, + &inhex_len, sizeof(inhex_buff)); + if (res) + return res; if (op->verbose > 2) pr2serr("Read %d bytes from user input\n", inhex_len); if (op->verbose > 3) - dStrHexErr((const char *)inhex_buff, inhex_len, 0); - op->do_raw = 0; + hex2stderr(inhex_buff, inhex_len, 0); + op->do_raw = false; if (op->inquiry) - return sdp_process_vpd_page(0, pn, ((spn < 0) ? 0: spn), op, - pdt, protect, inhex_buff, inhex_len); + return sdp_process_vpd_page(-1, pn, ((spn < 0) ? 0: spn), op, + pdt, protect, inhex_buff, inhex_len, + NULL, 0); else - return print_mode_page_given(inhex_buff, inhex_len, op); + return print_mode_page_inhex(inhex_buff, inhex_len, op); } if (0 == num_devices) { pr2serr("one or more device names required\n"); - usage(1); + if (! op->do_quiet) { + pr2serr("\n"); + usage((do_help > 0) ? do_help : 0); + } return SG_LIB_SYNTAX_ERROR; } + /* Page sized heap allocations aligned to a page */ + cur_aligned_mp = sg_memalign(0, 0, &free_cur_aligned_mp, false); + cha_aligned_mp = sg_memalign(0, 0, &free_cha_aligned_mp, false); + def_aligned_mp = sg_memalign(0, 0, &free_def_aligned_mp, false); + sav_aligned_mp = sg_memalign(0, 0, &free_sav_aligned_mp, false); + oth_aligned_mp = sg_memalign(0, 0, &free_oth_aligned_mp, false); + if ((NULL == cur_aligned_mp) || (NULL == cha_aligned_mp) || + (NULL == def_aligned_mp) || (NULL == sav_aligned_mp) || + (NULL == oth_aligned_mp)) { + ret = sg_convert_errno(ENOMEM); + goto no_mem_out; + } + req_pdt = pdt; ret = 0; for (k = 0; k < num_devices; ++k) { @@ -2819,32 +3128,51 @@ sg_fd = open_and_simple_inquiry(device_name_arr[k], rw, &pdt, &protect, op); if (sg_fd < 0) { - r = SG_LIB_FILE_ERROR; if (0 == ret) - ret = r; + ret = -sg_fd; continue; } - if (op->inquiry) - r = sdp_process_vpd_page(sg_fd, pn, ((spn < 0) ? 0: spn), op, - req_pdt, protect, NULL, 0); - else { + if (op->inquiry) { + if (op->examine) + r = examine_vpd_page(sg_fd, pn, spn, op, req_pdt, protect); + else + r = sdp_process_vpd_page(sg_fd, pn, ((spn < 0) ? 0: spn), op, + req_pdt, protect, NULL, 0, NULL, 0); + } else { if (cmd_str && scmdp) /* process command */ r = sdp_process_cmd(sg_fd, scmdp, cmd_arg, pdt, op); - else /* mode page */ - r = process_mode(sg_fd, &mp_settings, pn, spn, set_clear, - (NULL != get_str), op, pdt); + else { /* mode page */ + if (op->examine) + r = examine_mode_page(sg_fd, pn, op, req_pdt); + + else + r = process_mode_page(sg_fd, &mp_settings, pn, spn, + set_clear, (NULL != get_str), op, + pdt); + } } res = sg_cmds_close_device(sg_fd); if (res < 0) { - pr2serr("close error: %s\n", safe_strerror(-sg_fd)); + pr2serr("close error: %s\n", safe_strerror(-res)); if (0 == r) - r = SG_LIB_FILE_ERROR; + r = -res; } if (r && ((0 == ret) || (SG_LIB_FILE_ERROR == ret))) ret = r; } +no_mem_out: + if (free_cur_aligned_mp) + free(free_cur_aligned_mp); + if (free_cha_aligned_mp) + free(free_cha_aligned_mp); + if (free_def_aligned_mp) + free(free_def_aligned_mp); + if (free_sav_aligned_mp) + free(free_sav_aligned_mp); + if (free_oth_aligned_mp) + free(free_oth_aligned_mp); return (ret >= 0) ? ret : SG_LIB_CAT_OTHER; } @@ -2876,7 +3204,7 @@ * Assumes (and is currently only invoked for) lk 2.4. */ static int -find_corresponding_sg_fd(int oth_fd, const char * device_name, int rw, +find_corresponding_sg_fd(int oth_fd, const char * device_name, bool rw, int verbose) { int fd, err, bus, bbus, k, v; @@ -2966,7 +3294,7 @@ } static int -map_if_lk24(int sg_fd, const char * device_name, int rw, int verbose) +map_if_lk24(int sg_fd, const char * device_name, bool rw, int verbose) { /* could be lk 2.4 and not using a sg device */ struct utsname a_uts; diff -Nru sdparm-1.10/src/sdparm_cmd.c sdparm-1.12/src/sdparm_cmd.c --- sdparm-1.10/src/sdparm_cmd.c 2016-02-02 15:24:56.000000000 +0000 +++ sdparm-1.12/src/sdparm_cmd.c 2018-06-01 04:24:06.000000000 +0000 @@ -1,40 +1,43 @@ /* - * Copyright (c) 2005-2016 Douglas Gilbert. + * Copyright (c) 2005-2018, Douglas Gilbert * 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. + * 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. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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. - * + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. */ + #include #include +#include #include #include #include #define __STDC_FORMAT_MACROS 1 #include +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "sg_lib.h" #include "sg_cmds_basic.h" #include "sg_cmds_mmc.h" @@ -50,39 +53,70 @@ #define RCAP_REPLY_LEN 8 #define RCAP16_REPLY_LEN 32 +static uint8_t * aligned_buff; +static uint8_t * free_aligned_buff; +static int aligned_buff_sz; + +static uint8_t * allocate_if_needed(void) +{ + if (NULL == aligned_buff) { + aligned_buff = sg_memalign(0, 0, &free_aligned_buff, false); + if (NULL == aligned_buff) { + pr2serr("Unable to allocate aligned_buff\n"); + return NULL; + } + aligned_buff_sz = sg_get_page_size(); + } + return aligned_buff; +} + static int -do_cmd_read_capacity(int sg_fd, int verbose) +deallocate_res(int res) { - int res, do16; + if (free_aligned_buff) { + free(free_aligned_buff); + free_aligned_buff = NULL; + aligned_buff = NULL; + } + return res; +} + +static int +do_cmd_read_capacity(int sg_fd, bool do_long, int verbose) +{ + bool do16; + int res; unsigned int last_blk_addr, block_size; - unsigned char resp_buff[RCAP16_REPLY_LEN]; uint64_t llast_blk_addr; double sz_mib; + uint8_t * resp_buff = aligned_buff; - do16 = 0; - res = sg_ll_readcap_10(sg_fd, 0 /* pmi */, 0 /* lba */, resp_buff, - RCAP_REPLY_LEN, 1, verbose); - if (0 == res) { - last_blk_addr = sg_get_unaligned_be32(resp_buff + 0); - if (0xffffffff != last_blk_addr) { - block_size = sg_get_unaligned_be32(resp_buff + 4); - printf("blocks: %u\n", last_blk_addr + 1); - printf("block_length: %u\n", block_size); - sz_mib = ((double)(last_blk_addr + 1) * block_size) / - (double)(1048576); + do16 = do_long; + if (! do16) { + res = sg_ll_readcap_10(sg_fd, false /* pmi */, 0 /* lba */, + resp_buff, RCAP_REPLY_LEN, true, verbose); + if (0 == res) { + last_blk_addr = sg_get_unaligned_be32(resp_buff + 0); + if (0xffffffff != last_blk_addr) { + block_size = sg_get_unaligned_be32(resp_buff + 4); + printf("blocks: %u\n", last_blk_addr + 1); + printf("block_length: %u\n", block_size); + sz_mib = ((double)(last_blk_addr + 1) * block_size) / + (double)(1048576); #ifdef SG3_UTILS_MINGW - printf("capacity_mib: %g\n", sz_mib); + printf("capacity_mib: %g\n", sz_mib); #else - printf("capacity_mib: %.1f\n", sz_mib); + printf("capacity_mib: %.1f\n", sz_mib); #endif + } else + do16 = true; } else - do16 = 1; - } else - return res; + return res; + } if (do16) { /* within SERVICE ACTION IN. May need RW or root permissions. */ - res = sg_ll_readcap_16(sg_fd, 0 /* pmi */, 0 /* llba */, resp_buff, - RCAP16_REPLY_LEN, 1, verbose); + res = sg_ll_readcap_16(sg_fd, false /* pmi */, false /* llba */, + resp_buff, RCAP16_REPLY_LEN, true, verbose); if (0 == res) { llast_blk_addr = sg_get_unaligned_be64(resp_buff + 0); block_size = sg_get_unaligned_be32(resp_buff + 8); @@ -95,40 +129,57 @@ #else printf("capacity_mib: %.1f\n", sz_mib); #endif + if (do_long) { + uint8_t u[2]; + + printf("ZBC_rc_basis: %d\n", ((resp_buff[12] & 0x30) >> 4)); + printf("p_type: %d\n", ((resp_buff[12] & 0xe) >> 1)); + printf("prot_en: %d\n", (resp_buff[12] & 0x1)); + printf("p_i_exponent: %d\n", ((resp_buff[13] & 0xf0) >> 4)); + printf("lbs_per_physical_block_exponent: %d\n", + (resp_buff[13] & 0xf)); + printf("lbpme: %d\n", !!(resp_buff[14] & 0x80)); + printf("lbprz: %d\n", !!(resp_buff[14] & 0x40)); + memcpy(u, resp_buff + 14, 2); + u[0] &= 0x3f; + printf("lowest_aligned_lba: %u\n", sg_get_unaligned_be16(u)); + } } else return res; } return 0; } +#define MAX_REQ_SENSE_SZ 64 + static int -do_cmd_sense(int sg_fd, int hex, int do_quiet, int verbose) +do_cmd_sense(int sg_fd, bool hex, bool do_quiet, int verbose) { - int res, resp_len, sk, asc, ascq, progress, pr, rem, something; - unsigned char buff[32]; + bool something; + int res, resp_len, sk, asc, ascq, progress, pr, rem; + uint8_t * buff = aligned_buff; char b[128]; - memset(buff, 0, sizeof(buff)); - res = sg_ll_request_sense(sg_fd, 0 /* fixed format */, - buff, sizeof(buff), 1, verbose); + res = sg_ll_request_sense(sg_fd, false /* DESC so fixed format */, + buff, MAX_REQ_SENSE_SZ, true, verbose); if (0 == res) { resp_len = buff[7] + 8; - if (resp_len > (int)sizeof(buff)) - resp_len = sizeof(buff); + if (resp_len > aligned_buff_sz) + resp_len = aligned_buff_sz; sk = (0xf & buff[2]); if (hex) { - dStrHex((const char *)buff, resp_len, 1); + hex2stdout(buff, resp_len, 1); return 0; } - something = 0; + something = false; if (verbose) { pr2serr("Decode response as sense data:\n"); sg_print_sense(NULL, buff, resp_len, 0); if (verbose > 1) { pr2serr("\nOutput response in hex\n"); - dStrHexErr((const char *)buff, resp_len, 1); + hex2stderr(buff, resp_len, 1); } - something = 1; + something = true; } asc = (resp_len > 12) ? buff[12] : 0; ascq = (resp_len > 13) ? buff[13] : 0; @@ -136,7 +187,7 @@ pr = (progress * 100) / 65536; rem = ((progress * 100) % 65536) / 656; printf("Operation in progress: %d.%d%% done\n", pr, rem); - something = 1; + something = true; } if (0 == sk) { /* NO SENSE */ /* check for hardware threshold exceeded or warning */ @@ -164,6 +215,8 @@ return res; } +#define PERF_DESC_SZ 28 + /* cmd_arg is kBytes/sec if given (i.e. 1000 bytes per second */ static int do_cmd_speed(int sg_fd, int cmd_arg, const struct sdparm_opt_coll * op) @@ -174,7 +227,7 @@ unsigned int rw_time = 1000; if (cmd_arg >= 0) { - unsigned char perf_desc[28]; + uint8_t * perf_desc = aligned_buff; #if 0 int kbps; @@ -183,9 +236,8 @@ else kbps = cmd_arg; res = sg_ll_set_cd_speed(sg_fd, 0 /* rot_control */, kbps, - 0 /* drv_write_speed */, 1, op->verbose); + 0 /* drv_write_speed */, true, op->verbose); #else - memset(perf_desc, 0, sizeof(perf_desc)); if (0 == cmd_arg) perf_desc[0] |= 0x4; /* set RDD bit: restore drive defaults */ else { @@ -197,7 +249,7 @@ } /* performance (type=0), tolerance 10% nominal, read speed */ res = sg_ll_set_streaming(sg_fd, 0x0 /* type */, perf_desc, - sizeof(perf_desc), 1, op->verbose); + PERF_DESC_SZ, true, op->verbose); if (res) { if (SG_LIB_CAT_NOT_READY == res) pr2serr("Set Streaming failed, device not ready\n"); @@ -207,15 +259,15 @@ } } else { const int max_num_desc = 16; - unsigned char buff[8 + (16 * 16)]; + uint8_t * buff = aligned_buff; + int buff_sz = 8 + (16 * 16); unsigned int lba; /* performance (type=0), tolerance 10% nominal, read speed */ res = sg_ll_get_performance(sg_fd, 0x10 /* data_type */, 0 /* starting_lba */, - max_num_desc, - 0 /* type */, buff, sizeof(buff), - 1, op->verbose); + max_num_desc, 0 /* type */, buff, + buff_sz, true, op->verbose); if (0 == res) { if (op->verbose) { lba = sg_get_unaligned_be32(buff + 8); @@ -259,7 +311,7 @@ } static void -decode_get_config_feature(int feature, unsigned char * ucp, int len) +decode_get_config_feature(int feature, uint8_t * bp, int len) { int k, profile; char buff[128]; @@ -269,9 +321,9 @@ printf("Available profiles, profile of current media marked " "with * \n"); for (k = 4; k < len; k += 4) { - profile = sg_get_unaligned_be16(ucp + k); + profile = sg_get_unaligned_be16(bp + k); printf(" %s %s\n", get_profile_str(profile, buff), - ((ucp[k + 2] & 1) ? "*" : "")); + ((bp[k + 2] & 1) ? "*" : "")); } break; default: @@ -282,10 +334,10 @@ static void -decode_get_config(unsigned char * resp, int max_resp_len, int len) +decode_get_config(uint8_t * resp, int max_resp_len, int len) { int k, extra, feature; - unsigned char * ucp; + uint8_t * bp; if (max_resp_len < len) { printf("get_config: response to long for buffer, resp_len=%d>>>\n", @@ -296,16 +348,16 @@ printf("get_config: response length too short: %d\n", len); return; } - ucp = resp + 8; + bp = resp + 8; len -= 8; - for (k = 0; k < len; k += extra, ucp += extra) { - extra = 4 + ucp[3]; - feature = sg_get_unaligned_be16(ucp + 0); + for (k = 0; k < len; k += extra, bp += extra) { + extra = 4 + bp[3]; + feature = sg_get_unaligned_be16(bp + 0); if (0 != (extra % 4)) printf(" get_config: additional length [%d] not a multiple " "of 4, ignore\n", extra - 4); else - decode_get_config_feature(feature, ucp, extra); + decode_get_config_feature(feature, bp, extra); } } @@ -315,38 +367,38 @@ do_cmd_profile(int sg_fd, const struct sdparm_opt_coll * op) { int res, len; - unsigned char resp[MAX_CONFIG_RESPLEN]; + uint8_t * resp = aligned_buff; /* performance (type=0), tolerance 10% nominal, read speed */ res = sg_ll_get_config(sg_fd, 0x0 /* rt */, 0 /* starting_lba */, - resp, sizeof(resp), 1, op->verbose); + resp, MAX_CONFIG_RESPLEN, true, op->verbose); if (0 == res) { len = sg_get_unaligned_be32(resp + 0); - decode_get_config(resp, sizeof(resp), len); + decode_get_config(resp, MAX_CONFIG_RESPLEN, len); } return res; } const struct sdparm_command_t * -sdp_build_cmd(const char * cmd_str, int * rwp, int * argp) +sdp_build_cmd(const char * cmd_str, bool * rwp, int * argp) { + int arg = -1; + int len; const struct sdparm_command_t * scmdp; const char * eq_cp; const char * cp; - char buff[16]; - int len; - int arg = -1; + char cbuff[16]; eq_cp = strchr(cmd_str, '='); if (eq_cp) { len = eq_cp - cmd_str; - if (len >= (int)sizeof(buff)) + if (len >= (int)sizeof(cbuff)) return NULL; - strncpy(buff, cmd_str, len); - buff[len] = '\0'; + strncpy(cbuff, cmd_str, len); + cbuff[len] = '\0'; if (1 != sscanf(eq_cp + 1, "%d", &arg)) return NULL; - cp = buff; + cp = cbuff; } else cp = cmd_str; if (argp) @@ -368,9 +420,9 @@ if ((CMD_READY == scmdp->cmd_num) || (CMD_SENSE == scmdp->cmd_num) || (CMD_CAPACITY == scmdp->cmd_num)) - *rwp = 0; + *rwp = false; else - *rwp = 1; + *rwp = true; } return scmdp; } else @@ -405,26 +457,29 @@ "'--flexible' to override\n"); return SG_LIB_SYNTAX_ERROR; } + if (NULL == allocate_if_needed()) + return sg_convert_errno(ENOMEM); switch (scmdp->cmd_num) { case CMD_CAPACITY: - res = do_cmd_read_capacity(sg_fd, op->verbose); + res = do_cmd_read_capacity(sg_fd, op->do_long, op->verbose); break; case CMD_EJECT: - res = sg_ll_start_stop_unit(sg_fd, 0 /* immed */, 0 /* fl_num */, - 0 /* power cond. */, 0 /* fl */, - 1 /*loej */, 0 /* start */, 1 /* noisy */, - op->verbose); + res = sg_ll_start_stop_unit(sg_fd, false /* immed */, 0 /* fl_num */, + 0 /* power cond. */, false /* fl */, + true /*loej */, false /* start */, + true /* noisy */, op->verbose); break; case CMD_LOAD: - res = sg_ll_start_stop_unit(sg_fd, 0, 0, 0, 0, 1, 1, 1, op->verbose); + res = sg_ll_start_stop_unit(sg_fd, false, 0, 0, false, true, true, + true, op->verbose); break; case CMD_PROFILE: res = do_cmd_profile(sg_fd, op); break; case CMD_READY: progress = -1; - res = sg_ll_test_unit_ready_progress(sg_fd, 0, &progress, 0, + res = sg_ll_test_unit_ready_progress(sg_fd, false, &progress, false, op->verbose); if (0 == res) printf("Ready\n"); @@ -437,26 +492,30 @@ } break; case CMD_SENSE: - res = do_cmd_sense(sg_fd, op->do_hex, op->do_quiet, op->verbose); + res = do_cmd_sense(sg_fd, (op->do_hex > 0), (op->do_quiet > 0), + op->verbose); break; case CMD_SPEED: res = do_cmd_speed(sg_fd, cmd_arg, op); break; case CMD_START: - res = sg_ll_start_stop_unit(sg_fd, 0, 0, 0, 0, 0, 1, 1, op->verbose); + res = sg_ll_start_stop_unit(sg_fd, false, 0, 0, false, false, true, + true, op->verbose); break; case CMD_STOP: - res = sg_ll_start_stop_unit(sg_fd, 0, 0, 0, 0, 0, 0, 1, op->verbose); + res = sg_ll_start_stop_unit(sg_fd, false, 0, 0, false, false, false, + true, op->verbose); break; case CMD_SYNC: - res = sg_ll_sync_cache_10(sg_fd, 0, 0, 0, 0, 0, 1, op->verbose); + res = sg_ll_sync_cache_10(sg_fd, false, false, 0, 0, 0, true, + op->verbose); break; case CMD_UNLOCK: - res = sg_ll_prevent_allow(sg_fd, 0, 1, op->verbose); + res = sg_ll_prevent_allow(sg_fd, 0, true, op->verbose); break; default: pr2serr("unknown cmd number [%d]\n", scmdp->cmd_num); - return SG_LIB_SYNTAX_ERROR; + res = SG_LIB_SYNTAX_ERROR; } - return res; + return deallocate_res(res); } diff -Nru sdparm-1.10/src/sdparm_data.c sdparm-1.12/src/sdparm_data.c --- sdparm-1.10/src/sdparm_data.c 2016-02-22 22:45:10.000000000 +0000 +++ sdparm-1.12/src/sdparm_data.c 2021-04-21 22:25:59.000000000 +0000 @@ -1,33 +1,35 @@ /* - * Copyright (c) 2005-2016 Douglas Gilbert. + * Copyright (c) 2005-2021, Douglas Gilbert * 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. + * 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. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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. - * + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. */ #include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "sg_lib.h" #include "sdparm.h" @@ -48,42 +50,42 @@ /* SSC's medium partition mode page has a variable number of partition size fields which are treated as descriptors here */ static struct sdparm_mode_descriptor_t ssc_mpa_desc = { - 3, 1, 1, 8, 2, -1, -1, "SSC medium partition" + 3, 1, 1, 8, 2, -1, -1, false, "SSC medium partition" }; /* SMC's transport geometry parameters mode page doesn't give the number of following descriptors but rather parameter length (in bytes). This is flagged by -1 in num_descs_inc (third) field */ static struct sdparm_mode_descriptor_t smc_tg_desc = { - 1, 1, -1, 2, 2, -1, -1, "SMC transport geometry" + 1, 1, -1, 2, 2, -1, -1, false, "SMC transport geometry" }; /* SBC's logical block provisioning mode page doesn't give the number of following descriptors but rather parameter length (in bytes). This is flagged by -1 in num_descs_inc (third) field */ static struct sdparm_mode_descriptor_t sbc_lbp_desc = { - 2, 2, -1, 16, 8, -1, -1, "SBC logical block provisioning" + 2, 2, -1, 16, 8, -1, -1, false, "SBC logical block provisioning" }; /* SBC's application tag mode page doesn't give the number of following descriptors but rather parameter length (in bytes). This is flagged by -1 in num_descs_inc (third) field */ static struct sdparm_mode_descriptor_t sbc_atag_desc = { - 2, 2, -1, 16, 24, -1, -1, "SBC application tag" + 2, 2, -1, 16, 24, -1, -1, false, "SBC application tag" }; /* SPC's command duration limit A/B mode pages don't give the number of following descriptors but rather parameter length (in bytes). This is flagged by -1 in num_descs_inc (third) field */ static struct sdparm_mode_descriptor_t spc_cdl_desc = { - 2, 2, -1, 8, 4, -1, -1, "command duration limit" + 2, 2, -1, 8, 4, -1, -1, false, "command duration limit" }; /* SBC's IO advice hints grouping mode page doesn't give the number of following descriptors but rather parameter length (in bytes). This is flagged by -1 in num_descs_inc (third) field */ static struct sdparm_mode_descriptor_t sbc_ioadvi_desc = { - 2, 2, -1, 16, 16, -1, -1, "IO advice hints group" + 2, 2, -1, 16, 16, -1, -1, false, "IO advice hints group" }; /* Mode pages that aren't specific to any transport protocol or vendor. @@ -93,22 +95,27 @@ {ADC_MP, MSP_ADC_DT_DPP, PDT_ADC, 0, "addp", "DT device primary port (ADC)", NULL}, {ADC_MP, MSP_ADC_LU, PDT_ADC, 0, "adlu", "logical unit (ADC)", NULL}, - {ADC_MP, MSP_ADC_TGT_DEV, PDT_ADC, 0, "adtd", "Targer device (ADC)", + {ADC_MP, MSP_ADC_TGT_DEV, PDT_ADC, 0, "adtd", "Target device (ADC)", NULL}, {ADC_MP, MSP_ADC_TD_SN, PDT_ADC, 0, "adts", - "Targer device serial number (ADC)", NULL}, + "Target device serial number (ADC)", NULL}, + {CONTROL_MP, MSP_SAT_AFC, -1, 0, "afc", "SAT ATA Feature control", NULL}, {POWER_MP, MSP_SAT_POWER, -1, 0, "apo", "SAT ATA Power condition", NULL}, - {CONTROL_MP, MSP_SBC_APP_TAG, PDT_DISK, 0, "atag", "Application tag " + {CONTROL_MP, MSP_SBC_APP_TAG, PDT_DISK_ZBC, 0, "atag", "Application tag " "(SBC)", &sbc_atag_desc}, - {IEC_MP, MSP_BACK_CTL, PDT_DISK, 0, "bc", "Background control (SBC)", + {IEC_MP, MSP_BACK_CTL, PDT_DISK_ZBC, 0, "bc", "Background control (SBC)", NULL}, - {CONTROL_MP, MSP_SBC_BACK_OP, PDT_DISK, 0, "bop", "Background operation " - "control (SBC)", NULL}, - {CACHING_MP, 0, PDT_DISK, 0, "ca", "Caching (SBC)", NULL}, + {CONTROL_MP, MSP_SBC_BACK_OP, PDT_DISK, 0, "bop", + "Background operation control (SBC)", NULL}, + {CACHING_MP, 0, PDT_DISK_ZBC, 0, "ca", "Caching (SBC)", NULL}, {CONTROL_MP, MSP_SPC_CDLA, -1, 0, "cdla", "Command duration limit A", &spc_cdl_desc}, {CONTROL_MP, MSP_SPC_CDLB, -1, 0, "cdlb", "Command duration limit B", &spc_cdl_desc}, + {CONTROL_MP, MSP_SPC_CDLT2A, -1, 0, "cdt2a", "Command duration limit T2A", + NULL /* spc6r01 */ }, + {CONTROL_MP, MSP_SPC_CDLT2B, -1, 0, "cdt2b", "Command duration limit T2B", + NULL /* spc6r01 */ }, {MMCMS_MP, 0, PDT_MMC, 1, "cms", "CD/DVD (MM) capabilities and " "mechanical status (MMC)", NULL}, /* read only */ {CONTROL_MP, 0, -1, 0, "co", "Control", NULL}, @@ -129,6 +136,7 @@ "Extended device capabilities (SMC)", NULL}, {ES_MAN_MP, 0, PDT_SES, 0, "esm", "Enclosure services management (SES)", NULL}, + {FLEX_DISK_MP, 0, PDT_DISK, 0, "fd", "Flexible disk (SBC)", NULL}, {FORMAT_MP, 0, PDT_DISK, 0, "fo", "Format (SBC)", NULL}, {IEC_MP, 0, -1, 0, "ie", "Informational exceptions control", NULL}, {CONTROL_MP, MSP_SBC_IO_ADVI, 0, 0, "ioad", "IO advice hints grouping", @@ -139,6 +147,7 @@ {MED_PART_MP, 0, PDT_TAPE, 0, "mpa", "Medium partition (SSC)", &ssc_mpa_desc}, {MRW_MP, 0, PDT_MMC, 0, "mrw", "Mount rainier reWritable (MMC)", NULL}, + {NOTCH_MP, 0, PDT_DISK, 0, "not", "Notch and partition (SBC)", NULL}, {CONTROL_MP, MSP_SAT_PATA, -1, 0, "pat", "SAT pATA control", NULL}, {PROT_SPEC_LU_MP, 0, -1, 0, "pl", "Protocol specific logical unit", NULL}, {POWER_MP, 0, -1, 0, "po", "Power condition", NULL}, @@ -154,38 +163,51 @@ {TRANS_GEO_PAR_MP, 0, PDT_MCHANGER, 0, "tgp", "Transport geometry " "parameters (SMC)", &smc_tg_desc}, {TIMEOUT_PROT_MP, 0, PDT_MMC, 0, "tp", "Timeout and protect (MMC)", NULL}, - {V_ERR_RECOVERY_MP, 0, PDT_DISK, 0, "ve", "Verify error recovery (SBC)", - NULL}, + {V_ERR_RECOVERY_MP, 0, PDT_DISK_ZBC, 0, "ve", + "Verify error recovery (SBC)", NULL}, {WRITE_PARAM_MP, 0, PDT_MMC, 0, "wp", "Write parameters (MMC)", NULL}, {XOR_MP, 0, PDT_DISK, 0, "xo", "XOR control (SBC)", NULL}, /* XOR control mode page made obsolete in sbc3r32 */ + {CONTROL_MP, MSP_ZB_D_CTL, PDT_DISK_ZBC, 0, "zbdct", + "Zoned block device control (ZBC)", NULL}, {0, 0, 0, 0, NULL, NULL, NULL}, }; -/* Array for transport id, acronym and name association. */ -/* Those transports commented with "none" don't have transport specific */ -/* mode pages. */ -struct sdparm_transport_id_t sdparm_transport_id[] = { - {TPROTO_FCP, "fcp", "Fibre channel (FCP)"}, - {TPROTO_SPI, "spi", "SCSI parallel interface (SPI)"}, - {TPROTO_SSA, "ssa", "Serial storage architecture (SSA)"}, - {TPROTO_1394, "sbp", "Serial bus (SBP)"}, /* none */ - {TPROTO_SRP, "srp", "SCSI remote DMA (SRP)"}, - {TPROTO_ISCSI, "iscsi", "Internet SCSI (iSCSI)"}, /* none */ - {TPROTO_SAS, "sas", "Serial attached SCSI (SAS, SPL) "}, - {TPROTO_ADT, "adt", "Automation/Drive interface (ADT)"}, - {TPROTO_ATA, "ata", "AT attachment interface (ATA, ACS)"}, /* none */ - {TPROTO_UAS, "uas", "USB attached SCSI (UAS)"}, /* none */ - {TPROTO_SOP, "sop", "SCSI over PCIe (SOP)"}, /* none */ - {0x9, "u0xb", NULL}, /* leading "u" so not number */ - {0xc, "u0xc", NULL}, - {0xd, "u0xd", NULL}, - {0xe, "u0xe", NULL}, - {TPROTO_NONE, "none", "No specific"}, - {0, NULL, NULL}, +/* Array for transport id and corresponding acronyms. The + * sg_get_trans_proto_str() function from the sg3_utils' library provides + * the full protocol (transport) name. Those transports commented with + * "none" don't have transport specific mode pages at this time. */ +struct sdparm_val_desc_t sdparm_transport_id[] = { + {TPROTO_FCP, "fcp"}, + {TPROTO_SPI, "spi"}, + {TPROTO_SSA, "ssa"}, + {TPROTO_1394, "sbp"}, /* none */ + {TPROTO_SRP, "srp"}, + {TPROTO_ISCSI, "iscsi"}, /* none */ + {TPROTO_SAS, "sas"}, + {TPROTO_ADT, "adt"}, + {TPROTO_ATA, "ata"}, /* none */ + {TPROTO_UAS, "uas"}, /* none */ + {TPROTO_SOP, "sop"}, /* none */ + {TPROTO_PCIE, "pcie"}, /* none */ + {0xc, "u0xc"}, /* leading "u" so not number */ + {0xd, "u0xd"}, + {0xe, "u0xe"}, + {TPROTO_NONE, "none"}, + {-1, NULL}, +}; + +struct sdparm_val_desc_t sdparm_add_transport_acron[] = { + {TPROTO_SPI, "para"}, + {TPROTO_SAS, "spl"}, + {TPROTO_PCIE, "nvme"}, + {TPROTO_ATA, "sata"}, + {TPROTO_SRP, "ib"}, /* InfiniBand */ + {TPROTO_UAS, "usb"}, + {-1, NULL}, }; -static struct sdparm_mode_page_t sdparm_fcp_mode_pg[] = { /* FCP-3 */ +static struct sdparm_mode_page_t sdparm_fcp_mode_pg[] = { /* FCP-3,5 */ {DISCONNECT_MP, 0, -1, 0, "dr", "Disconnect-reconnect (FCP)", NULL}, {PROT_SPEC_LU_MP, 0, -1, 0, "luc", "lu: control (FCP)", NULL}, {PROT_SPEC_PORT_MP, 0, -1, 0, "pc", "port: control (FCP)", NULL}, @@ -219,11 +241,17 @@ }; static struct sdparm_mode_descriptor_t sas_pcd_desc = { /* desc SAS/SPL */ - 7, 1, 0, 8, 48, -1, -1, "SAS phy" + 7, 1, 0, 8, 48, -1, -1, false, "SAS phy" }; static struct sdparm_mode_descriptor_t sas_e_phy_desc = { /* desc SAS/SPL */ - 7, 1, 0, 8, -1, 2, 2, "Enhanced phy" + 7, 1, 0, 8, -1, 2, 2, false, "Enhanced phy" +}; + +/* This one has a strange format, no number of descriptors and each + * descriptor can have a variable size. */ +static struct sdparm_mode_descriptor_t sas_oob_m_c_desc = { /* desc SAS/SPL */ + -1, -1, 0, 8, -1, 2, 2, true, "Attribute control" }; /* N.B. In SAS 2.1 the spec was split with the upper levels going into the @@ -231,6 +259,8 @@ * relevant SAS references. */ static struct sdparm_mode_page_t sdparm_sas_mode_pg[] = { /* SAS/SPL */ {DISCONNECT_MP, 0, -1, 0, "dr", "Disconnect-reconnect (SAS)", NULL}, + {PROT_SPEC_PORT_MP, MSP_SAS_OOB_M_C, -1, 0, "oobm", /* spl5r01 */ + "Out of band management control (SAS)", &sas_oob_m_c_desc}, {PROT_SPEC_LU_MP, 0, -1, 0, "pl", "Protocol specific logical unit (SAS)", NULL}, {PROT_SPEC_PORT_MP, MSP_SAS_PCD, -1, 0, "pcd", @@ -260,6 +290,7 @@ {VPD_BLOCK_LIMITS_EXT, 0, PDT_DISK, "ble", "Block limits extension (SBC)"}, {VPD_CFA_PROFILE_INFO, 0, -1, "cfa", "CFA profile information"}, + {VPD_CON_POS_RANGE, 0, 0, "cpr", "Concurrent positioning ranges (SBC)"}, {VPD_DEVICE_CONSTITUENTS, 0, -1, "dc", "Device constituents"}, {VPD_DEVICE_ID, 0, -1, "di", "Device identification"}, {VPD_DEVICE_ID, VPD_DI_SEL_AS_IS, -1, "di_asis", "Like 'di' " @@ -270,9 +301,10 @@ "identification, target port only"}, {VPD_DEVICE_ID, VPD_DI_SEL_TARGET, -1, "di_target", "Device " "identification, target device only"}, - {VPD_EXT_INQ, 0, -1, "ei", "Extended inquiry data"}, {VPD_DTDE_ADDRESS, 0, 1, "dtde", "Data transfer device element address (SSC)"}, + {VPD_EXT_INQ, 0, -1, "ei", "Extended inquiry data"}, + {VPD_FORMAT_PRESETS, 0, 0, "fp", "Format presets (SBC)"}, {VPD_IMP_OP_DEF, 0, -1, "iod", "Implemented operating definition (obs)"}, {VPD_LB_PROTECTION, 0, PDT_TAPE, "lbpro", "Logical block protection " @@ -285,6 +317,7 @@ "Manufacturer assigned serial number (ADC)"}, {VPD_MAN_NET_ADDR, 0, -1, "mna", "Management network addresses"}, {VPD_MODE_PG_POLICY, 0, -1, "mpp", "Mode page policy"}, + {SG_NVME_VPD_NICR, 0, -1, "nicr", "NVMe Identify controller response"}, {VPD_OSD_INFO, 0, PDT_OSD, "oi", "OSD information"}, {VPD_POWER_CONDITION, 0, -1, "pc", "Power condition"}, {VPD_POWER_CONSUMPTION, 0, -1, "psm", "Power consumption"}, @@ -294,6 +327,7 @@ {VPD_REFERRALS, 0, PDT_DISK, "ref", "Referrals (SBC)"}, {VPD_SA_DEV_CAP, 0, PDT_TAPE, "sad", "Sequential access device capabilities (SSC)"}, + {VPD_SCSI_FEATURE_SETS, 0, -1, "sfs", "SCSI Feature sets"}, {VPD_SOFTW_INF_ID, 0, -1, "sii", "Software interface identification"}, {VPD_NOT_STD_INQ, 0, -1, "sinq", "Standard inquiry response"}, {VPD_UNIT_SERIAL_NUM, 0, -1, "sn", "Unit serial number"}, @@ -303,7 +337,7 @@ {VPD_SUPPORTED_VPDS, 0, -1, "sv", "Supported VPD pages"}, {VPD_TA_SUPPORTED, 0, PDT_TAPE, "tas", "TapeAlert supported flags (SSC)"}, {VPD_3PARTY_COPY, 0, -1, "tpc", "Third party copy (SPC + SBC)"}, - {VPD_ZBC_DEV_CHARS, 0, -1, "zbdc", "Zoned block device characteristics " + {VPD_ZBC_DEV_CHARS, 0, -1, "zbdch", "Zoned block device characteristics " "(SBC + ZBC)"}, {0, 0, 0, NULL, NULL}, }; @@ -437,6 +471,20 @@ {"MRR", RIGID_DISK_MP, 0, PDT_DISK, 20, 7, 16, 0, "Medium rotation rate (rpm)", NULL}, + /* Flexible disk mode page [0x5] sbc (obsolete by sbc2r11) */ + {"XRATE", FLEX_DISK_MP, 0, PDT_DISK, 2, 7, 16, 0, + "Transfer rate", NULL}, + {"NUM_HD", FLEX_DISK_MP, 0, PDT_DISK, 4, 7, 8, 0, + "Number of heads", NULL}, + {"SECT_TR", FLEX_DISK_MP, 0, PDT_DISK, 5, 7, 8, 0, + "Sectors per track", NULL}, + {"BYTE_SECT", FLEX_DISK_MP, 0, PDT_DISK, 6, 7, 16, 0, + "Bytes per sector", NULL}, + {"NUM_CYL", FLEX_DISK_MP, 0, PDT_DISK, 8, 7, 16, 0, + "Number of cylinders", NULL}, + /* Surely the rest (starting with 'write precompensation') are no + * longer used. Some USB mass storage devices (flash) use this mpage. */ + /* Write parameters mode page [0x5] mmc5 */ {"BUFE", WRITE_PARAM_MP, 0, PDT_MMC, 2, 6, 1, MF_COMMON, "Buffer underrun free recording enable", NULL}, @@ -452,7 +500,7 @@ "Multi session", "0: next session not allowed (no BO pointer)\t" "1: next session not allowed\t" - "3: next seesion allowed (indicated by BO pointer)"}, + "3: next session allowed (indicated by BO pointer)"}, {"FP", WRITE_PARAM_MP, 0, PDT_MMC, 3, 5, 1, 0, "Fixed packet type", NULL}, {"COPY", WRITE_PARAM_MP, 0, PDT_MMC, 3, 4, 1, 0, @@ -491,81 +539,81 @@ "Lock disable", NULL}, /* Verify error recovery mode page [0x7] sbc2 */ - {"V_EER", V_ERR_RECOVERY_MP, 0, PDT_DISK, 2, 3, 1, 0, + {"V_EER", V_ERR_RECOVERY_MP, 0, PDT_DISK_ZBC, 2, 3, 1, 0, "Enable early recovery (obsolete) ", NULL}, /* in sbc4r02 */ - {"V_PER", V_ERR_RECOVERY_MP, 0, PDT_DISK, 2, 2, 1, 0, + {"V_PER", V_ERR_RECOVERY_MP, 0, PDT_DISK_ZBC, 2, 2, 1, 0, "Post error", NULL}, - {"V_DTE", V_ERR_RECOVERY_MP, 0, PDT_DISK, 2, 1, 1, 0, + {"V_DTE", V_ERR_RECOVERY_MP, 0, PDT_DISK_ZBC, 2, 1, 1, 0, "Data terminate on error", NULL}, - {"V_DCR", V_ERR_RECOVERY_MP, 0, PDT_DISK, 2, 0, 1, 0, + {"V_DCR", V_ERR_RECOVERY_MP, 0, PDT_DISK_ZBC, 2, 0, 1, 0, "Disable correction (obsolete)", NULL}, /* in sbc4r02 */ - {"V_RC", V_ERR_RECOVERY_MP, 0, PDT_DISK, 3, 7, 8, 0, + {"V_RC", V_ERR_RECOVERY_MP, 0, PDT_DISK_ZBC, 3, 7, 8, 0, "Verify retry count", NULL}, - {"V_COR_S", V_ERR_RECOVERY_MP, 0, PDT_DISK, 4, 7, 8, 0, + {"V_COR_S", V_ERR_RECOVERY_MP, 0, PDT_DISK_ZBC, 4, 7, 8, 0, "Verify correction span (obsolete)", NULL}, - {"V_RTL", V_ERR_RECOVERY_MP, 0, PDT_DISK, 10, 7, 16, 0, + {"V_RTL", V_ERR_RECOVERY_MP, 0, PDT_DISK_ZBC, 10, 7, 16, 0, "Verify recovery time limit (ms)", NULL}, /* Caching mode page [0x8] sbc2 */ - {"IC", CACHING_MP, 0, PDT_DISK, 2, 7, 1, 0, + {"IC", CACHING_MP, 0, PDT_DISK_ZBC, 2, 7, 1, 0, "Initiator control", "0: disk uses own adaptive caching algorithm\t" "1: disk caching algorithm controlled by NCS or CCS"}, - {"ABPF", CACHING_MP, 0, PDT_DISK, 2, 6, 1, 0, + {"ABPF", CACHING_MP, 0, PDT_DISK_ZBC, 2, 6, 1, 0, "Abort pre-fetch", NULL}, - {"CAP", CACHING_MP, 0, PDT_DISK, 2, 5, 1, 0, + {"CAP", CACHING_MP, 0, PDT_DISK_ZBC, 2, 5, 1, 0, "Caching analysis permitted", NULL}, - {"DISC", CACHING_MP, 0, PDT_DISK, 2, 4, 1, 0, + {"DISC", CACHING_MP, 0, PDT_DISK_ZBC, 2, 4, 1, 0, "Discontinuity", "0: pre-fetch truncated or wrapped at time discontinuity\t" "1: pre-fetch continues across time discontinuity"}, - {"SIZE", CACHING_MP, 0, PDT_DISK, 2, 3, 1, 0, + {"SIZE", CACHING_MP, 0, PDT_DISK_ZBC, 2, 3, 1, 0, "Size enable", "0: number of cache segments (NCS) controls cache segmentation\t" "1: the cache segment size (CCS) controls cache segmentation"}, - {"WCE", CACHING_MP, 0, PDT_DISK, 2, 2, 1, MF_COMMON, + {"WCE", CACHING_MP, 0, PDT_DISK_ZBC, 2, 2, 1, MF_COMMON, "Write cache enable", NULL}, - {"MF", CACHING_MP, 0, PDT_DISK, 2, 1, 1, 0, + {"MF", CACHING_MP, 0, PDT_DISK_ZBC, 2, 1, 1, 0, "Multiplication factor", "0: MIPF and MAPF specify blocks\t" "1: multiply MIPF and MAPF by blocks in read command"}, - {"RCD", CACHING_MP, 0, PDT_DISK, 2, 0, 1, MF_COMMON, + {"RCD", CACHING_MP, 0, PDT_DISK_ZBC, 2, 0, 1, MF_COMMON, "Read cache disable", NULL}, - {"DRRP", CACHING_MP, 0, PDT_DISK, 3, 7, 4, 0, + {"DRRP", CACHING_MP, 0, PDT_DISK_ZBC, 3, 7, 4, 0, "Demand read retention priority", "0: treat requested and other data equally\t" "1: replace requested data before other data\t" "15: replace other data before requested data"}, - {"WRP", CACHING_MP, 0, PDT_DISK, 3, 3, 4, 0, + {"WRP", CACHING_MP, 0, PDT_DISK_ZBC, 3, 3, 4, 0, "Write retention priority", "0: treat requested and other data equally\t" "1: replace requested data before other data\t" "15: replace other data before requested data"}, - {"DPTL", CACHING_MP, 0, PDT_DISK, 4, 7, 16, 0, + {"DPTL", CACHING_MP, 0, PDT_DISK_ZBC, 4, 7, 16, 0, "Disable pre-fetch transfer length", NULL}, - {"MIPF", CACHING_MP, 0, PDT_DISK, 6, 7, 16, 0, + {"MIPF", CACHING_MP, 0, PDT_DISK_ZBC, 6, 7, 16, 0, "Minimum pre-fetch", NULL}, - {"MAPF", CACHING_MP, 0, PDT_DISK, 8, 7, 16, 0, + {"MAPF", CACHING_MP, 0, PDT_DISK_ZBC, 8, 7, 16, 0, "Maximum pre-fetch", NULL}, - {"MAPFC", CACHING_MP, 0, PDT_DISK, 10, 7, 16, 0, + {"MAPFC", CACHING_MP, 0, PDT_DISK_ZBC, 10, 7, 16, 0, "Maximum pre-fetch ceiling", NULL}, - {"FSW", CACHING_MP, 0, PDT_DISK, 12, 7, 1, 0, + {"FSW", CACHING_MP, 0, PDT_DISK_ZBC, 12, 7, 1, 0, "Force sequential write", NULL}, - {"LBCSS", CACHING_MP, 0, PDT_DISK, 12, 6, 1, 0, + {"LBCSS", CACHING_MP, 0, PDT_DISK_ZBC, 12, 6, 1, 0, "Logical block cache segment size", "0: CSS unit is bytes; 1: CSS unit is blocks"}, - {"DRA", CACHING_MP, 0, PDT_DISK, 12, 5, 1, 0, + {"DRA", CACHING_MP, 0, PDT_DISK_ZBC, 12, 5, 1, 0, "Disable read ahead", NULL}, - {"SYNC_PROG", CACHING_MP, 0, PDT_DISK, 12, 2, 2, 0, /* sbc3r33 */ + {"SYNC_PROG", CACHING_MP, 0, PDT_DISK_ZBC, 12, 2, 2, 0, /* sbc3r33 */ "Synchronous cache progress indication", "0: no pollable sense data during sync\t" "1: allow pollable sense data, allow all commands during sync\t" "2: allow pollable sense data, allow some commands during sync"}, - {"NV_DIS", CACHING_MP, 0, PDT_DISK, 12, 0, 1, 0, + {"NV_DIS", CACHING_MP, 0, PDT_DISK_ZBC, 12, 0, 1, 0, "Non-volatile cache disable", NULL}, - {"NCS", CACHING_MP, 0, PDT_DISK, 13, 7, 8, 0, + {"NCS", CACHING_MP, 0, PDT_DISK_ZBC, 13, 7, 8, 0, "Number of cache segments", NULL}, - {"CSS", CACHING_MP, 0, PDT_DISK, 14, 7, 16, 0, + {"CSS", CACHING_MP, 0, PDT_DISK_ZBC, 14, 7, 16, 0, "Cache segment size", NULL}, /* Control mode page [0xa] spc3 */ @@ -592,6 +640,8 @@ "Queue error management", "0: only affected task gets CC; 1: affected tasks aborted\t" "3: affected tasks aborted on same I_T nexus"}, + {"VS_CTL", CONTROL_MP, 0, -1, 4, 7, 1, 0, + "Vendor specific [byte 4, bit 7]", NULL}, {"RAC", CONTROL_MP, 0, -1, 4, 6, 1, 0, "Report a check", NULL}, {"UA_INTLCK", CONTROL_MP, 0, -1, 4, 5, 2, 0, @@ -638,26 +688,31 @@ {"IALUAE", CONTROL_MP, MSP_SPC_CE, -1, 4, 0, 1, 0, "Implicit asymmetric logical unit access enabled", NULL}, {"INIT_PR", CONTROL_MP, MSP_SPC_CE, -1, 5, 3, 4, 0, - "Initial command priority", "0: none or vendor\t" - "1: highest\t15: lowest"}, + "Initial command priority", "0: none or vendor; 1: highest; " + "15: lowest"}, {"MSDL", CONTROL_MP, MSP_SPC_CE, -1, 6, 7, 8, 0, /* spc4r34 */ "Maximum sense data length", "0: unlimited"}, + {"NSQCC", CONTROL_MP, MSP_SPC_CE, -1, 7, 7, 8, 0, /* spc6r05 */ + "Non-sequestered command count", NULL}, + {"SQCO", CONTROL_MP, MSP_SPC_CE, -1, 8, 7, 8, 0, /* spc6r05 */ + "Sequestered command order", + "0: oldest\t1: best IOPS\t2: IOPS and other sources"}, - /* Application tag mode subpage [0xa,0x2] sbc4 */ + /* Application tag mode subpage: atag [0xa,0x2] sbc3r25 */ /* descriptor starts here, is relative to start of mode - * page (i.e. 16 more than shown in descriptor format table) */ - {"AT_LAST", CONTROL_MP, MSP_SBC_APP_TAG, PDT_DISK, 16, 7, 1, 0, + * page (i.e. 16 more than shown in t10's descriptor format table) */ + {"AT_LAST", CONTROL_MP, MSP_SBC_APP_TAG, PDT_DISK_ZBC, 16, 7, 1, 0, "Last", NULL}, - {"AT_LBAT", CONTROL_MP, MSP_SBC_APP_TAG, PDT_DISK, 22, 7, 16, MF_HEX, + {"AT_LBAT", CONTROL_MP, MSP_SBC_APP_TAG, PDT_DISK_ZBC, 22, 7, 16, MF_HEX, "Logical block application tag", NULL}, - {"AT_LBA", CONTROL_MP, MSP_SBC_APP_TAG, PDT_DISK, 24, 7, 64, MF_HEX, - "Logical block address", NULL}, - {"AT_COUNT", CONTROL_MP, MSP_SBC_APP_TAG, PDT_DISK, 32, 7, 64, MF_HEX, - "Logical block count", NULL}, + {"AT_LBA", CONTROL_MP, MSP_SBC_APP_TAG, PDT_DISK_ZBC, 24, 7, 64, MF_HEX, + "Logical block address", "start LBA for this application tag"}, + {"AT_COUNT", CONTROL_MP, MSP_SBC_APP_TAG, PDT_DISK_ZBC, 32, 7, 64, + MF_HEX | MF_ALL_1S, "Logical block count", NULL}, - /* Command duration limit A mode subpage [0xa,0x3] spc5 */ + /* Command duration limit A mode subpage: cdla [0xa,0x3] spc5 */ /* descriptor starts here, is relative to start of mode - * page (i.e. 8 more than shown in descriptor format table) */ + * page (i.e. 8 more than shown in t10's descriptor format table) */ {"CDA_UNIT", CONTROL_MP, MSP_SPC_CDLA, -1, 8, 7, 3, 0, "CDLA unit", "0: no duration limit\t" @@ -667,9 +722,9 @@ {"CDA_LIMIT", CONTROL_MP, MSP_SPC_CDLA, -1, 10, 7, 16, 0, "Command duration limit", NULL}, - /* Command duration limit B mode subpage [0xa,0x4] spc5 */ + /* Command duration limit B mode subpage: cdlb [0xa,0x4] spc5 */ /* descriptor starts here, is relative to start of mode - * page (i.e. 8 more than shown in descriptor format table) */ + * page (i.e. 8 more than shown in t10's descriptor format table) */ {"CDB_UNIT", CONTROL_MP, MSP_SPC_CDLB, -1, 8, 7, 3, 0, "CDL unit", "0: no duration limit\t" @@ -679,9 +734,9 @@ {"CDB_LIMIT", CONTROL_MP, MSP_SPC_CDLB, -1, 10, 7, 16, 0, "Command duration limit", NULL}, - /* IO advice hints grouping mode subpage [0xa,0x5] sbc4 */ + /* IO advice hints grouping mode subpage: ioad [0xa,0x5] sbc4 */ /* descriptor starts here, is relative to start of mode - * page (i.e. 16 more than shown in descriptor format table) */ + * page (i.e. 16 more than shown in t10's descriptor format table) */ {"IOA_MODE", CONTROL_MP, MSP_SBC_IO_ADVI, -1, 16, 7, 2, 0, "IO advice hints mode", "0: valid; 1: invalid"}, {"CS_EN", CONTROL_MP, MSP_SBC_IO_ADVI, -1, 16, 1, 1, 0, @@ -692,6 +747,10 @@ * patterns) */ {"ACDLU", CONTROL_MP, MSP_SBC_IO_ADVI, -1, 20, 7, 1, 0, "Access continue during low utilization", NULL}, + {"RLBSR", CONTROL_MP, MSP_SBC_IO_ADVI, -1, 20, 5, 2, 0, + "Related logical blocks and subsequent reads", + "0: no information; 1: LBs associated, no subsequent reads expected;\t" + "3: LBs associated, subsequent reads expected"}, {"LBM_DT", CONTROL_MP, MSP_SBC_IO_ADVI, -1, 20, 3, 4, 0, "LBM descriptor type", "0: access patterns; else trouble"}, {"OV_FR", CONTROL_MP, MSP_SBC_IO_ADVI, -1, 21, 7, 2, 0, @@ -714,13 +773,100 @@ "Operating System Initialization (OSI) proximity", "0: unknown; 1: improbable; 2: probable"}, - /* Background operation control mode subpage [0xa,0x6] sbc4 */ + /* Background operation control mode subpage: bop [0xa,0x6] sbc4 */ {"BO_MODE", CONTROL_MP, MSP_SBC_BACK_OP, PDT_DISK, 4, 7, 2, 0, - "Background operation mode", "0: advance background ops " - "suspended during IO\t" - "1: host initiated background operations continue during IO"}, + "Background operation mode", "host initiated advanced background " + "operations:\t" + "0: suspended during IO\t" + "1: continue during IO"}, + + /* Command duration limit T2A mode subpage: cdt2a [0xa,0x7] spc6 */ + {"PVCDG", CONTROL_MP, MSP_SPC_CDLT2A, -1, 7, 7, 4, 0, + "Perf versus command duration guidelines", + "Maximium percentage increase in average command completion times:\t" + "0: 0%\t1: 0.5%\t...\t6: 3%\t7: 4%\t8: 5%\t9: 8%\t10: 10%" + "11: 15%\t12: 20%"}, + /* descriptor starts here, is relative to start of mode + * page (i.e. 8 more than shown in t10's descriptor format table) */ + {"T2CDLU", CONTROL_MP, MSP_SPC_CDLT2A, -1, 8, 3, 4, MF_CLASH_OK, + "T2 command duration limit units", "0: none\t6: 500 nanoseconds\t" + "8: 1 microsecond\t10: 10 milliseconds\t14: 500 milliseconds"}, + {"MXINATI", CONTROL_MP, MSP_SPC_CDLT2A, -1, 10, 7, 16, MF_CLASH_OK, + "Max inactive time", NULL}, + {"MXACTTI", CONTROL_MP, MSP_SPC_CDLT2A, -1, 12, 7, 16, MF_CLASH_OK, + "Max active time", NULL}, + {"MXINATP", CONTROL_MP, MSP_SPC_CDLT2A, -1, 14, 7, 4, MF_CLASH_OK, + "Max inactive time policy", "0: asap\t" + "13: good, completed, data currently unavailable\t" + "15: terminate, aborted command, command timeout before processing"}, + {"MXACTTP", CONTROL_MP, MSP_SPC_CDLT2A, -1, 14, 3, 4, MF_CLASH_OK, + "Max active time policy", "0: asap\t" + "13: good, completed, data currently unavailable\t" + "14: as per 15, may report largest LBA processed" + "15: terminate, aborted command, command timeout before processing"}, + {"CDGUID", CONTROL_MP, MSP_SPC_CDLT2A, -1, 18, 7, 16, MF_CLASH_OK, + "Command duration guideline", + "0: ignore\t>0: preferred command duration"}, + {"CDGUPOL", CONTROL_MP, MSP_SPC_CDLT2A, -1, 22, 7, 16, MF_CLASH_OK, + "Command duration guideline policy", "0: asap\t" + "1: next highest CDL descriptor\t" + "2: continue as if no CDL\t" + "13: good, completed, data currently unavailable\t" + "15: terminate, aborted command, command timeout before processing"}, + {"BYP_SEQ", CONTROL_MP, MSP_SPC_CDLT2A, -1, 23, 0, 1, MF_CLASH_OK, + "Bypass sequestration", NULL}, + + /* Command duration limit T2B mode subpage: cdt2b [0xa,0x8] spc6 */ + {"PVLC", CONTROL_MP, MSP_SPC_CDLT2B, -1, 7, 3, 4, 0, + "Perf versus latency control", + "This field is not defined in SPC6r05??\t" + "Maximium percentage increase in average command completion times:\t" + "0: 0%\t1: 0.5%\t...\t6: 3%\t7: 4%\t8: 5%\t9: 8%\t10: 10%" + "11: 15%\t12: 20%"}, + /* descriptor starts here, is relative to start of mode + * page (i.e. 8 more than shown in t10's descriptor format table) */ + {"T2CDLU", CONTROL_MP, MSP_SPC_CDLT2B, -1, 8, 3, 4, MF_CLASH_OK, + "T2 command duration limit units", "0: none\t6: 500 nanoseconds\t" + "8: 1 microsecond\t10: 10 milliseconds\t14: 500 milliseconds"}, + {"MXINATI", CONTROL_MP, MSP_SPC_CDLT2B, -1, 10, 7, 16, MF_CLASH_OK, + "Max inactive time", NULL}, + {"MXACTTI", CONTROL_MP, MSP_SPC_CDLT2B, -1, 12, 7, 16, MF_CLASH_OK, + "Max active time", NULL}, + {"MXINATP", CONTROL_MP, MSP_SPC_CDLT2B, -1, 14, 7, 4, MF_CLASH_OK, + "Max inactive time policy", "0: asap\t" + "13: good, completed, data currently unavailable\t" + "15: terminate, aborted command, command timeout before processing"}, + {"MXACTTP", CONTROL_MP, MSP_SPC_CDLT2B, -1, 14, 3, 4, MF_CLASH_OK, + "Max active time policy", "0: asap\t" + "13: good, completed, data currently unavailable\t" + "14: as per 15, may report largest LBA processed" + "15: terminate, aborted command, command timeout before processing"}, + {"CDGUID", CONTROL_MP, MSP_SPC_CDLT2B, -1, 18, 7, 16, MF_CLASH_OK, + "Command duration guideline", + "0: ignore\t>0: preferred command duration"}, + {"CDGUPOL", CONTROL_MP, MSP_SPC_CDLT2B, -1, 22, 7, 16, MF_CLASH_OK, + "Command duration guideline policy", "0: asap\t" + "1: next highest CDL descriptor\t" + "2: continue as if no CDL\t" + "13: good, completed, data currently unavailable\t" + "15: terminate, aborted command, command timeout before processing"}, + {"BYP_SEQ", CONTROL_MP, MSP_SPC_CDLT2B, -1, 23, 0, 1, MF_CLASH_OK, + "Bypass sequestration", NULL}, + + /* Zoned Block device Control mode subpage: zbcc [0xa,0x9] zbc2r04a */ + /* Probably only applies to host-managed ZBC (pdt=0x14) but set pdt=-1 + * in these entries in case it could apply to host-aware (pdt=0x0) */ + {"URSWRZ_M", CONTROL_MP, MSP_ZB_D_CTL, PDT_DISK_ZBC, 4, 0, 1, 0, + "Unrestricted read in sequential write required management", + "0: not configure to support reading unwritten blocks\t" + "1: configure to support reading unwritten blocks"}, + {"U_UA_CTL", CONTROL_MP, MSP_ZB_D_CTL, PDT_DISK_ZBC, 5, 0, 1, 0, + "Unrestricted read in sequential write required zone unit " + "attention control", + "0: issue 'Mode parameters changed' UA when URSWRZ changed\t" + "1: issue 'Inquiry data has changed' UA when URSWRZ changed"}, - /* Control data protection mode subpage [0xa,0xf0] ssc4 */ + /* Control data protection mode subpage: cdp [0xa,0xf0] ssc4 */ {"LBPM", CONTROL_MP, MSP_SSC_CDP, PDT_TAPE, 4, 7, 8, 0, "Logical block protection method", "0: none\t" "1: Reed-Solomon CRC\t2: CRC32C (Castagnoli)\t>= 0xf0: vendor"}, @@ -733,7 +879,7 @@ {"RBDP", CONTROL_MP, MSP_SSC_CDP, PDT_TAPE, 6, 5, 1, 0, "Recover buffered data protected", NULL}, - /* SAT: pATA control mode subpage [0xa,0xf1] sat-r09 */ + /* SAT: pATA control mode subpage: pat [0xa,0xf1] sat-r09 */ /* treat as spc since could be disk or ATAPI */ {"MWD2", CONTROL_MP, MSP_SAT_PATA, -1, 4, 6, 1, 0, "Multi word DMA bit 2", NULL}, @@ -760,7 +906,32 @@ {"UDMA0", CONTROL_MP, MSP_SAT_PATA, -1, 5, 0, 1, 0, "Ultra DMA bit 0", NULL}, - /* Power condition mode page - obsolete block-device-only version */ + /* SAT: ATA feature control mode subpage: afc [0xa,0xf2] 20-085r4 */ + /* treat as spc since could be disk or ATAPI */ + {"CDL_CTRL", CONTROL_MP, MSP_SAT_AFC, -1, 4, 1, 2, 0, + "Command duration limits control", + "0: ATA 0->cdl_action, no CDL mpages supported\t" + "1: ATA 0->cdl_action, CDL A mpage supported, maybe CDL B\t" + "2: ATA 1->cdl_action, CDL T2A mpage supported, maybe CDL T2B"}, + + /* Notch and partition mode page [0xc] sbc2 (obsolete in sbc2r14) */ + {"ND", NOTCH_MP, 0, PDT_DISK, 2, 7, 1, 0, + "Notched device", NULL}, + {"LPN", NOTCH_MP, 0, PDT_DISK, 2, 6, 1, 0, + "Logical or physical notch", "0: physical; 1: logical"}, + {"MNN", NOTCH_MP, 0, PDT_DISK, 4, 7, 16, 0, + "Maximum number of notches", NULL}, + {"ANOT", NOTCH_MP, 0, PDT_DISK, 6, 7, 16, 0, + "Active notch", "origin 1, 0 for all"}, + {"SBOU", NOTCH_MP, 0, PDT_DISK, 8, 7, 32, MF_HEX, + "Starting boundary", NULL}, + {"EBOU", NOTCH_MP, 0, PDT_DISK, 12, 7, 32, MF_HEX, + "Ending boundary", NULL}, + {"PNOT", NOTCH_MP, 0, PDT_DISK, 16, 7, 64, MF_HEX, + "Pages notched", + "bit map of mpages altered by notching\tMSb: mpage 0x3f"}, + + /* Power condition mode page: poo, obsolete block-device-only version */ /* [0xd] sbc (replacement page now at 0x1a) */ {"IDLE-OLD", POWER_OLD_MP, 0, PDT_DISK, 3, 1, 1, 0, "Idle timer active", NULL}, @@ -771,7 +942,7 @@ {"SCT-OLD", POWER_OLD_MP, 0, PDT_DISK, 8, 7, 32, 0, "Standby condition timer (100 ms)", NULL}, - /* Data compression mode page [0xf] ssc3 */ + /* Data compression mode page: dac [0xf] ssc3 */ {"DCE", DATA_COMPR_MP, 0, PDT_TAPE, 2, 7, 1, MF_COMMON, "Data compression enable", NULL}, {"DCC", DATA_COMPR_MP, 0, PDT_TAPE, 2, 6, 1, MF_COMMON, @@ -787,13 +958,13 @@ "Decompression algorithm", "0: none; 1: default; 5: ALDC (2048 byte); 16: IDRC; 32: DCLZ"}, - /* XOR control mode page [0x10] sbc2 << obsolete in sbc3r32>> */ + /* XOR control mode page: xo [0x10] sbc2 << obsolete in sbc3r32>> */ {"XORDIS", XOR_MP, 0, PDT_DISK, 2, 1, 1, 0, "XOR disable", NULL}, {"MXWS", XOR_MP, 0, PDT_DISK, 4, 7, 32, 0, "Maximum XOR write size (blocks)", NULL}, - /* Device configuration mode page [0x10] ssc3 */ + /* Device configuration mode page: dc [0x10] ssc3 */ {"CAF", DEV_CONF_MP, 0, PDT_TAPE, 2, 5, 1, 0, "Change active format", NULL}, {"ACT_F", DEV_CONF_MP, 0, PDT_TAPE, 2, 4, 5, 0, @@ -854,7 +1025,9 @@ {"PRMWP", DEV_CONF_MP, 0, PDT_TAPE, 15, 0, 1, 0, "Permanent write protection", NULL}, - /* Device configuration extension mode subpage [0x10,1 ] ssc3 */ + /* Device configuration extension mode subpage: dce [0x10,1 ] ssc3 */ + {"PE_UN", DEV_CONF_MP, MSP_DEV_CONF_EXT, PDT_TAPE, 4, 7, 4, 0, + "PEWS units", "Units: 0: MB, 1: GB, 2: TB"}, {"TARPF", DEV_CONF_MP, MSP_DEV_CONF_EXT, PDT_TAPE, 4, 3, 1, 0, "TapeAlert respect parameter fields", NULL}, {"TASER", DEV_CONF_MP, MSP_DEV_CONF_EXT, PDT_TAPE, 4, 2, 1, 0, @@ -870,12 +1043,13 @@ "Short erase mode", "0: as per SSC-2; 1: erase has no effect; 2: record EOD indication"}, {"PEWS", DEV_CONF_MP, MSP_DEV_CONF_EXT, PDT_TAPE, 6, 7, 16, 0, - "Programmable early warning size [MB]", NULL}, + "Programmable early warning size", + "size units depend on PE_UN field; 0: MB, 1: GB, 2: TB"}, {"VCELBRE", DEV_CONF_MP, MSP_DEV_CONF_EXT, PDT_TAPE, 8, 0, 1, 0, "Volume containing encrypted logical blocks requires encryption", NULL}, - /* Medium partition mode page [0x11] ssc3 */ + /* Medium partition mode page: mpa [0x11] ssc3 */ {"MAX_AP", MED_PART_MP, 0, PDT_TAPE, 2, 7, 8, 0, "Maximum additional partitions", NULL}, {"APD", MED_PART_MP, 0, PDT_TAPE, 3, 7, 8, 0, @@ -911,25 +1085,27 @@ {"P_SZ", MED_PART_MP, 0, PDT_TAPE, 8, 7, 16, 0, "Partition size", NULL}, - /* Enclosure services management mode page [0x14] ses2 */ + /* Enclosure services management mode page: esm [0x14] ses2 */ {"ENBLTC", ES_MAN_MP, 0, PDT_SES, 5, 0, 1, MF_COMMON, "Enable timed completion", NULL}, {"MTCT", ES_MAN_MP, 0, PDT_SES, 6, 7, 16, MF_COMMON, "Maximum task completion time (100ms)", NULL}, - /* Protocol specific logical unit control mode page [0x18] spc3 */ + /* Protocol specific logical unit control mode page: pl [0x18] spc3 */ {"LUPID", PROT_SPEC_LU_MP, 0, -1, 2, 3, 4, 0, "Logical unit's (transport) protocol identifier", PROTO_IDENT_STR "\t" "[try adding '-t ' to get more fields]"}, - /* Protocol specific port control mode page [0x19] spc3 */ + /* Protocol specific port control mode page: pp [0x19] spc3 */ {"PPID", PROT_SPEC_PORT_MP, 0, -1, 2, 3, 4, 0, "Port's (transport) protocol identifier", PROTO_IDENT_STR "\t" "[try adding '-t ' to get more fields]"}, - /* Power condition mode page [0x1a] spc3 (expanded in spc4r18) */ + /* Power condition mode page: po [0x1a] spc3 (expanded in spc4r18) */ + /* In sdparm v1.11 changed IDLE->IDLE_A; STANDBY->STANDBY_Z; */ + /* ICT->IACT and SCT->SZCT */ {"PM_BG", POWER_MP, 0, -1, 2, 7, 2, 0, /* added spc4r24 */ "Power management, background functions, precedence", "0: vendor specific; 1: background function higher\t" @@ -940,18 +1116,14 @@ "Idle_c timer enable", NULL}, {"IDLE_B", POWER_MP, 0, -1, 3, 2, 1, 0, "Idle_b timer enable", NULL}, - {"IDLE", POWER_MP, 0, -1, 3, 1, 1, 0, /* IDLE_A in future ? */ - "Idle_a timer enable", - "named IDLE prior to spc4r18, thence IDLE_A"}, - {"STANDBY", POWER_MP, 0, -1, 3, 0, 1, 0, /* STANDBY_Z in future ? */ - "Standby_z timer enable", - "named STANDBY prior to spc4r18, thence STANDBY_Z"}, - {"ICT", POWER_MP, 0, -1, 4, 7, 32, 0, /* IACT in future ? */ - "Idle_a condition timer (100 ms)", - "named IDLE prior to spc4r18, thence IDLE_A"}, - {"SCT", POWER_MP, 0, -1, 8, 7, 32, 0, /* SZCT in future ? */ - "Standby_z condition timer (100 ms)", - "named STANDBY prior to spc4r18, thence STANDBY_Z"}, + {"IDLE_A", POWER_MP, 0, -1, 3, 1, 1, 0, + "Idle_a timer enable", NULL}, + {"STANDBY_Z", POWER_MP, 0, -1, 3, 0, 1, 0, + "Standby_z timer enable", NULL}, + {"IACT", POWER_MP, 0, -1, 4, 7, 32, 0, + "Idle_a condition timer (100 ms)", NULL}, + {"SZCT", POWER_MP, 0, -1, 8, 7, 32, 0, + "Standby_z condition timer (100 ms)", NULL}, {"IBCT", POWER_MP, 0, -1, 12, 7, 32, 0, "Idle_b condition timer (100 ms)", NULL}, {"ICCT", POWER_MP, 0, -1, 16, 7, 32, 0, @@ -960,30 +1132,31 @@ "Standby_y condition timer (100 ms)", NULL}, /* The "0: restricted (SAS-2)" became obsolete in spc5r01 */ {"CCF_IDLE", POWER_MP, 0, -1, 39, 7, 2, 0, /* changed spc4r35 */ - "check condition on transition from idle", /* was FIDCPC (spc4r25) */ - "0: restricted (SAS-2); 1: disabled; 2: enabled\n"}, - {"CCF_STAND", POWER_MP, 0, -1, 39, 5, 2, 0, /* changed spc4r35 */ - "check condition on transition from standby", /* was FSBCPC */ - "0: restricted (SAS-2); 1: disabled; 2: enabled\n"}, - {"CCF_STOPP", POWER_MP, 0, -1, 39, 3, 2, 0, /* changed spc4r35 */ - "check condition on transition from stopped", /* was FSTCPC */ - "0: restricted (SAS-2); 1: disabled; 2: enabled\n"}, + "check condition if from idle_c", /* was FIDCPC (spc4r25) */ + "0: restricted (SAS-2); 1: disabled; 2: enabled"}, + {"CCF_STAND", POWER_MP, 0, -1, 39, 5, 2, 0, /* changed spc4r35 */ + "check condition if from a standby", /* was FSBCPC */ + "0: restricted (SAS-2); 1: disabled; 2: enabled"}, + {"CCF_STOPP", POWER_MP, 0, -1, 39, 3, 2, 0, /* changed spc4r35 */ + "check condition if from stopped", /* was FSTCPC */ + "0: restricted (SAS-2); 1: disabled; 2: enabled"}, - /* Power consumption mode page [0x1a,1] added spc4r33 */ + /* Power consumption mode page: ps [0x1a,1] added spc4r33 */ {"ACT_LEV", POWER_MP, MSP_SPC_PS, -1, 6, 1, 2, 0, "Active level", - "1: highest; 2: intermediate; 3: lowest"}, + "0: per PC_ID field; 1: highest; 2: intermediate; 3: lowest"}, {"PC_ID", POWER_MP, MSP_SPC_PS, -1, 7, 7, 8, 0, "Power consumption identifier", "references Power consumption VPD page"}, - /* SAT ATA Power condition mode page [0x1a,0xf1] sat2 */ + /* SAT ATA Power condition mode page: apo [0x1a,0xf1] sat2 */ {"APMP", POWER_MP, MSP_SAT_POWER, -1, 5, 0, 1, 0, "Advanced Power Management (APM) enabled/change", NULL}, {"APM", POWER_MP, MSP_SAT_POWER, -1, 6, 7, 8, 0, - "Advanced Power Management (APM) value", NULL}, + "Advanced Power Management (APM) value", + "0: disable APM feature set; >0: enable"}, - /* Informational exception control mode page [0x1c] spc3 */ + /* Informational exception control mode page: ie [0x1c] sbc */ {"PERF", IEC_MP, 0, -1, 2, 7, 1, 0, "Performance (impact of ie operations)", "0: normal (some delays); 1: abridge ie operations"}, @@ -1009,29 +1182,29 @@ {"REPC", IEC_MP, 0, -1, 8, 7, 32, 0, "Report count (or Test flag number [SSC-3])", NULL}, - /* Background control mode subpage [0x1c,0x1] sbc3 */ - {"S_L_FULL", IEC_MP, MSP_BACK_CTL, PDT_DISK, 4, 2, 1, 0, + /* Background control mode subpage: bc [0x1c,0x1] sbc3 */ + {"S_L_FULL", IEC_MP, MSP_BACK_CTL, PDT_DISK_ZBC, 4, 2, 1, 0, "Suspend on log full", NULL}, - {"LOWIR", IEC_MP, MSP_BACK_CTL, PDT_DISK, 4, 1, 1, 0, + {"LOWIR", IEC_MP, MSP_BACK_CTL, PDT_DISK_ZBC, 4, 1, 1, 0, "Log only when intervention required", NULL}, - {"EN_BMS", IEC_MP, MSP_BACK_CTL, PDT_DISK, 4, 0, 1, 0, + {"EN_BMS", IEC_MP, MSP_BACK_CTL, PDT_DISK_ZBC, 4, 0, 1, 0, "Enable background medium scan", NULL}, - {"EN_PS", IEC_MP, MSP_BACK_CTL, PDT_DISK, 5, 0, 1, 0, + {"EN_PS", IEC_MP, MSP_BACK_CTL, PDT_DISK_ZBC, 5, 0, 1, 0, "Enable pre-scan", NULL}, - {"BMS_I", IEC_MP, MSP_BACK_CTL, PDT_DISK, 6, 7, 16, 0, + {"BMS_I", IEC_MP, MSP_BACK_CTL, PDT_DISK_ZBC, 6, 7, 16, 0, "Background medium scan interval time (hour)", NULL}, - {"BPS_TL", IEC_MP, MSP_BACK_CTL, PDT_DISK, 8, 7, 16, 0, - "Background pre-scan time limit (hour)", NULL}, - {"MIN_IDLE", IEC_MP, MSP_BACK_CTL, PDT_DISK, 10, 7, 16, 0, + {"BPS_TL", IEC_MP, MSP_BACK_CTL, PDT_DISK_ZBC, 8, 7, 16, 0, + "Background pre-scan time limit (hour)", "0: no limit"}, + {"MIN_IDLE", IEC_MP, MSP_BACK_CTL, PDT_DISK_ZBC, 10, 7, 16, 0, "Minumum idle time before background scan (ms)", NULL}, - {"MAX_SUSP", IEC_MP, MSP_BACK_CTL, PDT_DISK, 12, 7, 16, 0, + {"MAX_SUSP", IEC_MP, MSP_BACK_CTL, PDT_DISK_ZBC, 12, 7, 16, 0, "Maximum time to suspend background scan (ms)", NULL}, - /* Logical block provisioning mode subpage [0x1c,0x2] sbc3 */ + /* Logical block provisioning mode subpage: lbp [0x1c,0x2] sbc3 */ {"SITUA", IEC_MP, MSP_SBC_LB_PROV, PDT_DISK, 4, 0, 1, 0, "Single initiator threshold unit attention", NULL}, /* descriptor starts here, the is relative to the start - * of the mode page (i.e. 16 more than descriptor format table) */ + * of the mode page (i.e. 16 more than t10's descriptor format table) */ {"LBP_EN", IEC_MP, MSP_SBC_LB_PROV, PDT_DISK, 16, 7, 1, 0, "Threshold enabled", NULL}, {"LBP_TYPE", IEC_MP, MSP_SBC_LB_PROV, PDT_DISK, 16, 5, 3, 0, @@ -1044,19 +1217,19 @@ {"LBP_COUNT", IEC_MP, MSP_SBC_LB_PROV, PDT_DISK, 20, 7, 32, 0, "Threshold count", NULL}, - /* Medium configuration mode page [0x1d] ssc3 */ + /* Medium configuration mode page: mco [0x1d] ssc3 */ {"WORMM", MED_CONF_MP, 0, PDT_TAPE, 2, 0, 1, 0, "Worm mode", NULL}, {"WMLR", MED_CONF_MP, 0, PDT_TAPE, 4, 7, 8, 0, - "Worm volume label restrictions", /* mode->volume renaming */ + "Worm volume label restrictions", /* mode->volume renaming */ "0: disallow overwrite\t1: disallow some format labels overwrite\t" "2: allow all format labels to be overwritten"}, {"WMFR", MED_CONF_MP, 0, PDT_TAPE, 5, 7, 8, 0, - "Worm volume filemark restrictions", /* mode->volume renaming */ + "Worm volume filemark restrictions", /* mode->volume renaming */ "2: allow filemarks before EOD except closest to BOP\t" "3: allow any number of filemarks before EOD"}, - /* Timeout and protect mode page [0x1d] mmc5 */ + /* Timeout and protect mode page: tp [0x1d] mmc5 */ {"G3E", TIMEOUT_PROT_MP, 0, PDT_MMC, 4, 3, 1, 0, "Group 3 timeout capability enable", NULL}, {"TMOE", TIMEOUT_PROT_MP, 0, PDT_MMC, 4, 2, 1, 0, @@ -1071,7 +1244,7 @@ "Group 2 minimum timeout (sec)", NULL}, - /* Element address assignment mode page [0x1d] smc2 */ + /* Element address assignment mode page: eaa [0x1d] smc2 */ {"FMTEA", ELE_ADDR_ASS_MP, 0, PDT_MCHANGER, 2, 7, 16, 0, "First medium transport element address", NULL}, {"NMTE", ELE_ADDR_ASS_MP, 0, PDT_MCHANGER, 4, 7, 16, 0, @@ -1089,15 +1262,16 @@ {"NDTE", ELE_ADDR_ASS_MP, 0, PDT_MCHANGER, 16, 7, 16, 0, "Number of data transfer elements", NULL}, - /* Transport geometry parameters mode page [0x1e] smc2 */ + /* Transport geometry parameters mode page: tgp [0x1e] smc2 */ /* transport geometry descriptor starts here, is relative - * to start of mode page (i.e. 2 more than shown in descriptor table */ + * to start of mode page (i.e. 2 more than shown in t10's descriptor + * table */ {"ROTAT", TRANS_GEO_PAR_MP, 0, PDT_MCHANGER, 2, 0, 1, 0, "Rotation for double sided media handling", NULL}, {"MNTES", TRANS_GEO_PAR_MP, 0, PDT_MCHANGER, 3, 7, 8, 0, "Member number in transport element set", NULL}, - /* Device capabilities mode page [0x1f] smc3 */ + /* Device capabilities mode page: dca [0x1f] smc3 */ {"STORDT", DEV_CAP_MP, 0, PDT_MCHANGER, 2, 3, 1, 0, "Storage for data transfer element", NULL}, {"STORIE", DEV_CAP_MP, 0, PDT_MCHANGER, 2, 2, 1, 0, @@ -1193,7 +1367,7 @@ {"DTEMT", DEV_CAP_MP, 0, PDT_MCHANGER, 15, 0, 1, 0, "Data transfer -> medium transport; Exchange Medium", NULL}, - /* Extended device capabilities mode page [0x1f,0x41] smc3 */ + /* Extended device capabilities mode page: edc [0x1f,0x41] smc3 */ {"MVPRV", DEV_CAP_MP, MSP_EXT_DEV_CAP, PDT_MCHANGER, 4, 5, 1, 0, "Move prevented to import/export element", NULL}, {"MVCL", DEV_CAP_MP, MSP_EXT_DEV_CAP, PDT_MCHANGER, 4, 4, 1, 0, @@ -1231,7 +1405,7 @@ {"UCST", DEV_CAP_MP, MSP_EXT_DEV_CAP, PDT_MCHANGER, 8, 0, 1, 0, "Unassigned cleaning storage", NULL}, - /* CD/DVD (MM) capabilities and mechanical status mode page */ + /* CD/DVD (MM) capabilities and mechanical status mode page: cms */ /* [0x2a] obsolete in mmc4 and mmc5, last valid in mmc3 */ /* MRSS field was already obsolete in mmc3 */ {"D_RAM_R", MMCMS_MP, 0, PDT_MMC, 2, 5, 1, 0, @@ -1338,58 +1512,60 @@ /* << Transport protocol specific mode page items follow >> */ static struct sdparm_mode_page_item sdparm_mitem_fcp_arr[] = { - /* disconnect-reconnect mode page [0x2] fcp3 */ - {"BFR", DISCONNECT_MP, 0, -1, 2, 7, 8, 0, + /* disconnect-reconnect mode page [0x2] fcp3-5 */ + {"BFR", DISCONNECT_MP, 0, -1, 2, 7, 8, 0, /* obs fcp-5 */ "Buffer full ratio", NULL}, - {"BER", DISCONNECT_MP, 0, -1, 3, 7, 8, 0, + {"BER", DISCONNECT_MP, 0, -1, 3, 7, 8, 0, /* obs fcp-5 */ "Buffer empty ratio", NULL}, - {"BIL", DISCONNECT_MP, 0, -1, 4, 7, 16, MF_COMMON, + {"BIL", DISCONNECT_MP, 0, -1, 4, 7, 16, MF_COMMON, /* obs fcp-5 */ "Bus inactivity limit (transmission words)", NULL}, - {"DTL", DISCONNECT_MP, 0, -1, 6, 7, 16, MF_COMMON, + {"DTL", DISCONNECT_MP, 0, -1, 6, 7, 16, MF_COMMON, /* obs fcp-5 */ "Disconnect time limit (128 transmission words)", NULL}, - {"CTL", DISCONNECT_MP, 0, -1, 8, 7, 16, MF_COMMON, + {"CTL", DISCONNECT_MP, 0, -1, 8, 7, 16, MF_COMMON, /* obs fcp-5 */ "Connect time limit (128 transmission words)", NULL}, {"MBS", DISCONNECT_MP, 0, -1, 10, 7, 16, MF_COMMON | MF_CLASH_OK, "Maximum burst size (512 bytes)", NULL}, {"EMDP", DISCONNECT_MP, 0, -1, 12, 7, 1, MF_CLASH_OK, "Enable modify data pointers", NULL}, - {"FAA", DISCONNECT_MP, 0, -1, 12, 6, 1, 0, + {"FAA", DISCONNECT_MP, 0, -1, 12, 6, 1, 0, /* obs fcp-5 */ "Fairness access A [FCP_DATA]", NULL}, - {"FAB", DISCONNECT_MP, 0, -1, 12, 5, 1, 0, + {"FAB", DISCONNECT_MP, 0, -1, 12, 5, 1, 0, /* obs fcp-5 */ "Fairness access B [FCP_XFER]", NULL}, - {"FAC", DISCONNECT_MP, 0, -1, 12, 4, 1, 0, + {"FAC", DISCONNECT_MP, 0, -1, 12, 4, 1, 0, /* obs fcp-5 */ "Fairness access C [FCP_RSP]", NULL}, {"FBS", DISCONNECT_MP, 0, -1, 14, 7, 16, MF_CLASH_OK, - "First burst size (512 bytes)", NULL}, + "First burst size (512 bytes)", "0: no limit"}, - /* protocol specific logical unit mode page [0x18] fcp3 */ + /* protocol specific logical unit mode page [0x18] fcp3-5 */ {"LUPID", PROT_SPEC_LU_MP, 0, -1, 2, 3, 4, MF_COMMON | MF_CLASH_OK, "Logical unit's (transport) protocol identifier", PROTO_IDENT_STR }, {"EPDC", PROT_SPEC_LU_MP, 0, -1, 3, 0, 1, MF_COMMON, "Enable precise delivery checking", NULL}, - /* protocol specific port control page [0x19] fcp3 */ + /* protocol specific port control page [0x19] fcp3-5 */ {"PPID", PROT_SPEC_PORT_MP, 0, -1, 2, 3, 4, MF_COMMON | MF_CLASH_OK, "Port's (transport) protocol identifier", PROTO_IDENT_STR }, - {"DTFD", PROT_SPEC_PORT_MP, 0, -1, 3, 7, 1, MF_COMMON, + {"DTFD", PROT_SPEC_PORT_MP, 0, -1, 3, 7, 1, MF_COMMON, /* obs fcp-5 */ "Disable target fabric discovery", NULL}, - {"PLPB", PROT_SPEC_PORT_MP, 0, -1, 3, 6, 1, MF_COMMON, + {"PLPB", PROT_SPEC_PORT_MP, 0, -1, 3, 6, 1, MF_COMMON, /* obs fcp-5 */ "Prevent loop port bypass", NULL}, - {"DDIS", PROT_SPEC_PORT_MP, 0, -1, 3, 5, 1, 0, + {"DDIS", PROT_SPEC_PORT_MP, 0, -1, 3, 5, 1, 0, /* obs fcp-5 */ "Disable discovery", NULL}, - {"DLM", PROT_SPEC_PORT_MP, 0, -1, 3, 4, 1, 0, + {"DLM", PROT_SPEC_PORT_MP, 0, -1, 3, 4, 1, 0, /* obs fcp-5 */ "Disable loop master", NULL}, - {"RHA", PROT_SPEC_PORT_MP, 0, -1, 3, 3, 1, 0, + {"RHA", PROT_SPEC_PORT_MP, 0, -1, 3, 3, 1, 0, /* obs fcp-5 */ "Require hard address", NULL}, - {"ALWI", PROT_SPEC_PORT_MP, 0, -1, 3, 2, 1, 0, + {"ALWI", PROT_SPEC_PORT_MP, 0, -1, 3, 2, 1, 0, /* obs fcp-5 */ "Allow login without loop initialization", NULL}, - {"DTIPE", PROT_SPEC_PORT_MP, 0, -1, 3, 1, 1, 0, + {"DTIPE", PROT_SPEC_PORT_MP, 0, -1, 3, 1, 1, 0, /* obs fcp-5 */ "Disable target initialized port enable", NULL}, - {"DTOLI", PROT_SPEC_PORT_MP, 0, -1, 3, 0, 1, 0, + {"DTOLI", PROT_SPEC_PORT_MP, 0, -1, 3, 0, 1, 0, /* obs fcp-5 */ "Disable target originated loop initialization", NULL}, - {"RRTVU", PROT_SPEC_PORT_MP, 0, -1, 6, 2, 3, 0, + {"RRTVU", PROT_SPEC_PORT_MP, 0, -1, 6, 2, 3, MF_CLASH_OK, + "Resource recovery timeout value unit", NULL}, + {"RR_TOV", PROT_SPEC_PORT_MP, 0, -1, 6, 2, 3, MF_CLASH_OK, "Resource recovery timeout value unit", NULL}, {"SIRRTV", PROT_SPEC_PORT_MP, 0, -1, 7, 7, 8, 0, "Sequence initiative resource recovery timeout value", NULL}, @@ -1540,6 +1716,7 @@ {NULL, 0, 0, 0, 0, 0, 0, 0, NULL, NULL}, }; +/* SRP == SCSI RDMA protocol */ static struct sdparm_mode_page_item sdparm_mitem_srp_arr[] = { /* disconnect-reconnect mode page [0x2] srp */ {"MBS", DISCONNECT_MP, 0, -1, 10, 7, 16, MF_COMMON | MF_CLASH_OK, @@ -1552,15 +1729,16 @@ {NULL, 0, 0, 0, 0, 0, 0, 0, NULL, NULL}, }; +/* SAS == Serial Attached SCSI */ static struct sdparm_mode_page_item sdparm_mitem_sas_arr[] = { /* disconnect-reconnect mode page [0x2] sas/spl */ /* spl3r6 dropped the "time" from the name of BITL, keep acronym */ {"BITL", DISCONNECT_MP, 0, -1, 4, 7, 16, MF_COMMON, - "Bus inactivity (time) limit (100us)", + "Bus inactivity (time) limit (100us or see BILUNIT)", "0: no bus inactivity time limit\t" "1-65535: limit in units of 100 us"}, {"MCTL", DISCONNECT_MP, 0, -1, 8, 7, 16, MF_COMMON, - "Connect time limit (100us)", + "Connect time limit (100us or see CTLUNIT)", "0: no maximum connection time limit\t" "1-65535: limit in units of 100 us"}, {"MBS", DISCONNECT_MP, 0, -1, 10, 7, 16, MF_COMMON | MF_CLASH_OK, @@ -1569,7 +1747,15 @@ "1-65535: limit in units of 512 bytes\t" "Ignored by persistent connections"}, /* obsoleted spl3r2, re-instated spl3r3 */ - {"FBS", DISCONNECT_MP, 0, -1, 14, 7, 16, MF_CLASH_OK, + {"CTLUNIT", DISCONNECT_MP, 0, -1, 13, 3, 2, MF_CLASH_OK, + "Connect time limit unit", + "0: 100 microsecond unit\t" + "1: 1 microsecond unit\t"}, + {"BILUNIT", DISCONNECT_MP, 0, -1, 13, 1, 2, MF_CLASH_OK, /* 21-021r3 */ + "Bus inactivity (time) limit unit", + "0: 100 microsecond unit\t" + "1: 1 microsecond unit\t"}, + {"FBS", DISCONNECT_MP, 0, -1, 14, 7, 16, MF_CLASH_OK, /* 21-021r3 */ "First burst size (512 bytes)", "0: no first burst size (no data-out before xfer_ready)\t" "1-65535: maximum first burst size in units of 512 bytes"}, @@ -1578,7 +1764,7 @@ {"LUPID", PROT_SPEC_LU_MP, 0, -1, 2, 3, 4, MF_COMMON | MF_CLASH_OK, "Logical unit's (transport) protocol identifier", PROTO_IDENT_STR }, - {"TLR", PROT_SPEC_LU_MP, 0, -1, 2, 4, 1, 0, + {"TLR", PROT_SPEC_LU_MP, 0, -1, 2, 4, 1, MF_COMMON, "Transport layer retries", "0: disabled; 1: enabled (on target)"}, /* protocol specific port mode page [0x19] sas/spl */ @@ -1616,7 +1802,7 @@ {"NOP", PROT_SPEC_PORT_MP, MSP_SAS_PCD, -1, 7, 7, 8, MF_COMMON, "Number of phys", "one descriptor per phy"}, /* Phy mode descriptor starts here, relative to start of - * mode page (i.e. 8 more than phy mode descriptor table) */ + * mode page (i.e. 8 more than t10's phy mode descriptor table) */ {"PHID", PROT_SPEC_PORT_MP, MSP_SAS_PCD, -1, 9, 7, 8, 0, "Phy identifier", NULL}, {"ADT", PROT_SPEC_PORT_MP, MSP_SAS_PCD, -1, 12, 6, 3, 0, @@ -1655,6 +1841,8 @@ MF_HEX | MF_COMMON, "Attached SAS address", NULL}, {"APHID", PROT_SPEC_PORT_MP, MSP_SAS_PCD, -1, 32, 7, 8, 0, "Attached phy identifier", NULL}, + {"APERCAP", PROT_SPEC_PORT_MP, MSP_SAS_PCD, -1, 33, 7, 1, 0, + "Attached persistent capable", NULL}, {"APOWCAP", PROT_SPEC_PORT_MP, MSP_SAS_PCD, -1, 33, 6, 2, 0, "Attached power capable", "0: not; 1: can consume; 2: can source"}, {"ASLCAP", PROT_SPEC_PORT_MP, MSP_SAS_PCD, -1, 33, 4, 1, 0, @@ -1664,9 +1852,15 @@ {"AIZPER", PROT_SPEC_PORT_MP, MSP_SAS_PCD, -1, 33, 2, 1, 0, "Attached inside ZPSDS persistent", NULL}, {"AREQIZ", PROT_SPEC_PORT_MP, MSP_SAS_PCD, -1, 33, 1, 1, 0, - "Attached request inside ZPSDS", NULL}, + "Attached requested inside ZPSDS", NULL}, {"ABRCAP", PROT_SPEC_PORT_MP, MSP_SAS_PCD, -1, 33, 0, 1, 0, "Attached break reply capable", NULL}, + {"AAPTACAP", PROT_SPEC_PORT_MP, MSP_SAS_PCD, -1, 34, 2, 1, 0, + "Attached APTA capable", NULL}, + {"ASMPPCAP", PROT_SPEC_PORT_MP, MSP_SAS_PCD, -1, 34, 1, 1, 0, + "Attached SMP priority capable", NULL}, + {"APOWDCAP", PROT_SPEC_PORT_MP, MSP_SAS_PCD, -1, 34, 0, 1, 0, + "Attached power disable capable", NULL}, {"PMILR", PROT_SPEC_PORT_MP, MSP_SAS_PCD, -1, 40, 7, 4, 0, "Programmed minimum link rate", "0: not programmed; 8: 1.5 Gbps; 9: 3 Gbps; 10: 6 Gbps; 11: 12 Gbps\t" @@ -1690,6 +1884,18 @@ "Power loss timeout(ms)", NULL}, {"PGRATO", PROT_SPEC_PORT_MP, MSP_SAS_SPC, -1, 9, 7, 8, 0, "Power grant timeout(sec)", NULL}, + {"4PHYS", PROT_SPEC_PORT_MP, MSP_SAS_SPC, -1, 10, 2, 1, 0, + "4 phy wide port(s) when set", "If more than 4 phys, group adjacent " + "(by phy id) phys"}, /* start of spl5r07 addition */ + {"2PHYS", PROT_SPEC_PORT_MP, MSP_SAS_SPC, -1, 10, 1, 1, 0, + "2 phy wide port(s) when set", "If more than 2 phys, group adjacent " + "(by phy id) phys"}, + {"1PHY", PROT_SPEC_PORT_MP, MSP_SAS_SPC, -1, 10, 0, 1, 0, + "single phy (narrow) ports", "Each phy is a SCSI port with own SAS " + "address"}, + {"PMCDT", PROT_SPEC_PORT_MP, MSP_SAS_SPC, -1, 11, 7, 8, 0, + "port mode change delay time (unit: seconds)", "Minimum time device " + "remains offline after change"}, /* end of spl5r07 addition */ /* SAS-2 Enhanced phy mode page [0x19,0x3] sas/spl */ {"PPID_3", PROT_SPEC_PORT_MP, MSP_SAS_E_PHY, -1, 5, 3, 4, 0, @@ -1700,7 +1906,7 @@ {"NOP_1", PROT_SPEC_PORT_MP, MSP_SAS_E_PHY, -1, 7, 7, 8, 0, "Number of phys", "one descriptor per phy"}, /* Phy mode descriptor starts here, relative to start of - * mode page (i.e. 8 more than phy mode descriptor table) */ + * mode page (i.e. 8 more than t10's phy mode descriptor table) */ {"PHID_1", PROT_SPEC_PORT_MP, MSP_SAS_E_PHY, -1, 9, 7, 8, 0, "Phy identifier", NULL}, {"PPCAP", PROT_SPEC_PORT_MP, MSP_SAS_E_PHY, -1, 12, 7, 32, MF_HEX, @@ -1723,7 +1929,47 @@ {"EN_PA", PROT_SPEC_PORT_MP, MSP_SAS_E_PHY, -1, 27, 1, 1, 0, "Enable partial phy power condition", NULL}, {"HMS", PROT_SPEC_PORT_MP, MSP_SAS_E_PHY, -1, 27, 0, 1, 0, - "Hardware muxing supported", NULL}, + "Hardware muxing supported", NULL}, /* obsolete spl5r01 */ + + /* SPL-5 Out of band management control mode page [0x19,0x4] sas/spl */ + /* SFF-8609 related: Management Interface for drive thermal conditions */ + {"OOB_RE", PROT_SPEC_PORT_MP, MSP_SAS_OOB_M_C, -1, 4, 7, 1, 0, + "Out of band reporting enabled", "MSelect 1->0: send stopping " + "transmission packet\t0->1: send protocol revision code packet"}, + {"OOB_PRV", PROT_SPEC_PORT_MP, MSP_SAS_OOB_M_C, -1, 6, 7, 16, MF_HEX, + "Out of band protocol revision code", + "example: SFF-8609 revison 1.2 is code 0x102"}, + {"OOB_D_ID", PROT_SPEC_PORT_MP, MSP_SAS_OOB_M_C, -1, 8, 3, 4, + 0, "Out of band descriptor identifier", + "0: temperature attribute; 1-15: restricted for SFF-8209"}, + /* MF_DESC_ID_B0-3 bits are all zero for Temperature attribute */ + {"TA_TRE", PROT_SPEC_PORT_MP, MSP_SAS_OOB_M_C, -1, 12, 0, 1, MF_CLASH_OK, + "Temperature attribute, temperature reporting enabled", NULL}, + {"TA_RI", PROT_SPEC_PORT_MP, MSP_SAS_OOB_M_C, -1, 13, 7, 8, MF_CLASH_OK, + "Temperature attribute, reporting interval (seconds)", NULL}, + {"TA_MRI", PROT_SPEC_PORT_MP, MSP_SAS_OOB_M_C, -1, 14, 7, 8, MF_CLASH_OK, + "Temperature attribute, minimum reporting interval (seconds)", NULL}, + {"TA_C_UP", PROT_SPEC_PORT_MP, MSP_SAS_OOB_M_C, -1, 15, 7, 4, MF_CLASH_OK, + "Temperature attribute, change up (Celsius)", NULL}, + {"TA_C_DO", PROT_SPEC_PORT_MP, MSP_SAS_OOB_M_C, -1, 15, 3, 4, MF_CLASH_OK, + "Temperature attribute, change down (Celsius)", NULL}, + {"TA_TM", PROT_SPEC_PORT_MP, MSP_SAS_OOB_M_C, -1, 16, 1, 2, MF_CLASH_OK, + "Temperature attribute, test mode", + "0: test mode disabled, transfer actual temperature\t" + "1: TM enabled, send incrementing sequence of temps\t" + "2: TM enabled, send decrementing sequence of temps\t" + "3: TM enabled, send value in TA_TM_T every interval"}, + {"TA_TM_T", PROT_SPEC_PORT_MP, MSP_SAS_OOB_M_C, -1, 18, 7, 8, + MF_CLASH_OK | MF_TWOS_COMP, + "Temperature attribute test mode temperature", NULL}, +#if 0 + {"MY_TEST", PROT_SPEC_PORT_MP, MSP_SAS_OOB_M_C, -1, 13, 7, 8, + MF_CLASH_OK | MF_DESC_ID_B0, + "My test (dpg), 8 bit number, desc_id=1", NULL}, + {"MY_EXTRA", PROT_SPEC_PORT_MP, MSP_SAS_OOB_M_C, -1, 18, 7, 8, + MF_CLASH_OK | MF_DESC_ID_B0, + "My test (dpg), 8 bit number, desc_id=1", NULL}, +#endif {NULL, 0, 0, 0, 0, 0, 0, 0, NULL, NULL}, }; @@ -1738,14 +1984,14 @@ {NULL, NULL}, {sdparm_sas_mode_pg, sdparm_mitem_sas_arr}, {NULL, NULL}, - {NULL, NULL}, /* 8 */ + {NULL, NULL}, /* 8: ata (SAT mpages in generic) */ {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, - {NULL, NULL}, /* 15 */ + {sdparm_gen_mode_pg, sdparm_mitem_arr}, /* 15: none, treat as generic */ }; const char * sdparm_ansi_version_arr[] = diff -Nru sdparm-1.10/src/sdparm_data_vendor.c sdparm-1.12/src/sdparm_data_vendor.c --- sdparm-1.10/src/sdparm_data_vendor.c 2016-02-22 22:45:10.000000000 +0000 +++ sdparm-1.12/src/sdparm_data_vendor.c 2019-02-06 23:30:33.000000000 +0000 @@ -1,33 +1,35 @@ /* - * Copyright (c) 2007-2016 Douglas Gilbert. + * Copyright (c) 2005-2019, Douglas Gilbert * 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. + * 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. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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. - * + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. */ #include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "sg_lib.h" #include "sdparm.h" @@ -43,12 +45,16 @@ /* Vendor specific mode pages */ struct sdparm_vendor_name_t sdparm_vendor_id[] = { - {VENDOR_SEAGATE, "sea", "Seagate disk"}, + {VENDOR_SEAGATE, "sea", "Seagate disk"}, /* 0 */ {VENDOR_HITACHI, "hit", "Hitachi disk"}, + {VENDOR_HITACHI, "wdc", "Hitachi disk->HGST->WDC"}, {VENDOR_MAXTOR, "max", "Maxtor disk"}, {VENDOR_FUJITSU, "fuj", "Fujitsu disk"}, + {VENDOR_NONE, "none", "maps back to generic mode pages"}, {VENDOR_LTO5, "lto5", "LTO-5 tape drive (IBM, HP)"}, {VENDOR_LTO6, "lto6", "LTO-6 tape drive (IBM, HP)"}, + {VENDOR_NVME, "nvme", "NVMe, SNTL in library"}, + {VENDOR_SG, "sg", "sg3_utils package defined"}, /* 8 */ {0, NULL, NULL}, }; @@ -58,7 +64,7 @@ }; static struct sdparm_mode_page_item sdparm_mitem_v_seagate_arr[] = { - /* Unit attention page [0x0] Seagate */ + /* Unit attention page, ua [0x0] Seagate */ {"PM", UNIT_ATTENTION_MP, 0, 0, 2, 7, 1, MF_COMMON, "Performance Mode", "0: adaptive cache ('server mode')\t" @@ -120,6 +126,10 @@ "Just in time 0, fastest seek type", "0: can not use this seek type in seek speed algorithm\t" "1: can use this seek type in seek speed algorithm"}, + {"TTE", UNIT_ATTENTION_MP, 0, 0, 6, 0, 1, 0, + "Thermal throttle enable (SSD)", + "0: drive activity is not limited, based on temperature\t" + "1: drive activity is limited, based on temperature"}, {NULL, 0, 0, 0, 0, 0, 0, 0, NULL, NULL}, }; @@ -131,13 +141,19 @@ }; static struct sdparm_mode_page_item sdparm_mitem_v_hitachi_arr[] = { - /* Vendor unique parameters page [0x0] Hitachi */ + /* Vendor unique parameters page, vup [0x0] Hitachi/HGST/WDC */ {"MRG", UNIT_ATTENTION_MP, 0, 0, 2, 3, 1, 0, "Merge Glist into Plist (during format)", NULL}, {"VGMDE", UNIT_ATTENTION_MP, 0, 0, 3, 6, 1, MF_COMMON, "Veggie mode (do random seeks when idle)", NULL}, {"RRNDE", UNIT_ATTENTION_MP, 0, 0, 3, 1, 1, 0, "Report recovered non data errors (when PER set)", NULL}, + {"DNS", UNIT_ATTENTION_MP, 0, 0, 4, 2, 1, 0, + "Disable notify for standby", NULL}, + {"LRPMS", UNIT_ATTENTION_MP, 0, 0, 4, 1, 1, 0, + "Low RPM standby", NULL}, + {"LCS", UNIT_ATTENTION_MP, 0, 0, 4, 0, 1, 0, + "Limited current startup", NULL}, {"FDD", UNIT_ATTENTION_MP, 0, 0, 5, 4, 1, 0, "Format degraded disable (reporting for Test Unit Ready)", NULL}, {"CAEN", UNIT_ATTENTION_MP, 0, 0, 5, 1, 1, MF_COMMON, @@ -153,7 +169,7 @@ {"TT", UNIT_ATTENTION_MP, 0, 0, 9, 7, 8, 0, "Temperature threshold (celsius), 0 -> 85C", NULL}, {"CAL", UNIT_ATTENTION_MP, 0, 0, 10, 7, 16, 0, - "Command aging limit (50 ms), 0 -> 85C", NULL}, + "Command aging limit (50 ms)", NULL}, {"RRT", UNIT_ATTENTION_MP, 0, 0, 12, 7, 8, 0, "Read reporting threshold for read recovered errors when PER set", NULL}, @@ -180,7 +196,7 @@ }; static struct sdparm_mode_page_item sdparm_mitem_v_maxtor_arr[] = { - /* Unit attention page [0x0] Seagate */ + /* Unit attention page condition, uac [0x0] Maxtor */ {"DUA", UNIT_ATTENTION_MP, 0, 0, 2, 4, 1, MF_COMMON, "Disable unit attention", NULL}, @@ -195,7 +211,7 @@ }; static struct sdparm_mode_page_item sdparm_mitem_v_fujitsu_arr[] = { - /* Additional error recovery parameters page [0x21] Fujitsu */ + /* Additional error recovery parameters page, aerp [0x21] Fujitsu */ {"RDSE", 0x21, 0, 0, 2, 3, 4, MF_COMMON, "Retries during a seek error", "0: no repositioning retries"}, @@ -360,16 +376,35 @@ {NULL, 0, 0, 0, 0, 0, 0, 0, NULL, NULL}, }; +static struct sdparm_mode_page_t sdparm_v_nvme_mode_pg[] = { + {UNIT_ATTENTION_MP, 0, 0, 0, "nvme", "Unit attention (NVMe)", NULL}, + {0, 0, 0, 0, NULL, NULL, NULL}, +}; + +/* Only used by library's SNTL to override settings implied by NVMSR (byte + * 253 of Identify controller response) field, namely the NVMEE and NVMESD + * bits within that field */ +static struct sdparm_mode_page_item sdparm_mitem_v_nvme_arr[] = { + /* Unit attention page [0x0] NVMe */ + {"ENC_OV", UNIT_ATTENTION_MP, 0, 0, 2, 7, 8, MF_COMMON, + "Enclosure override", + "0: no override; 1: SES only; 2: SES+disk\t" + "3: pdt=processor SAFTE; 255: disk only"}, + {"NVME2", UNIT_ATTENTION_MP, 0, 0, 3, 7, 8, 0, + "Place holder, NVMe 2", NULL}, +}; + /* Indexed by VENDOR_* define */ struct sdparm_vendor_pair sdparm_vendor_mp[] = { {sdparm_v_seagate_mode_pg, sdparm_mitem_v_seagate_arr}, {sdparm_v_hitachi_mode_pg, sdparm_mitem_v_hitachi_arr}, {sdparm_v_maxtor_mode_pg, sdparm_mitem_v_maxtor_arr}, {sdparm_v_fujitsu_mode_pg, sdparm_mitem_v_fujitsu_arr}, - {NULL, NULL}, /* hole in sequence, re-use asap */ + {sdparm_gen_mode_pg, sdparm_mitem_arr}, /* VENDOR_NONE --> generic */ {sdparm_v_lto5_mode_pg, sdparm_mitem_v_lto5_arr}, {sdparm_v_lto6_mode_pg, sdparm_mitem_v_lto6_arr}, + {sdparm_v_nvme_mode_pg, sdparm_mitem_v_nvme_arr}, + {NULL, NULL}, /* no VENDOR_SG defined mode pages */ }; -int sdparm_vendor_mp_len = - sizeof(sdparm_vendor_mp) / sizeof(sdparm_vendor_mp[0]); +const int sdparm_vendor_mp_len = SG_ARRAY_SIZE(sdparm_vendor_mp); diff -Nru sdparm-1.10/src/sdparm.h sdparm-1.12/src/sdparm.h --- sdparm-1.10/src/sdparm.h 2016-02-18 19:24:07.000000000 +0000 +++ sdparm-1.12/src/sdparm.h 2021-03-22 04:44:26.000000000 +0000 @@ -7,15 +7,21 @@ * to the device (e.g. eject removable media). */ +#include #include #ifdef __cplusplus extern "C" { #endif -#define DEF_MODE_RESP_LEN 252 +#define DEF_MODE_6_RESP_LEN 252 +#define DEF_MODE_RESP_LEN 512 #define DEF_INQ_RESP_LEN 252 #define VPD_ATA_INFO_RESP_LEN 572 +#define VPD_XCOPY_RESP_LEN 572 +#define VPD_LARGE_RESP_LEN 1020 + +#define MP_DESC_DNC (-1) /* Do not care indicator */ /* Mode page numbers */ #define UNIT_ATTENTION_MP 0 @@ -24,11 +30,13 @@ #define FORMAT_MP 3 #define MRW_MP 3 #define RIGID_DISK_MP 4 +#define FLEX_DISK_MP 5 #define WRITE_PARAM_MP 5 #define RBC_DEV_PARAM_MP 6 #define V_ERR_RECOVERY_MP 7 #define CACHING_MP 8 #define CONTROL_MP 0xa +#define NOTCH_MP 0xc /* Notch and partition (obsolete in sbc4r14) */ #define POWER_OLD_MP 0xd /* #define CD_DEV_PARAMS 0xd */ #define ADC_MP 0xe @@ -37,6 +45,7 @@ #define XOR_MP 0x10 #define MED_PART_MP 0x11 #define ES_MAN_MP 0x14 +#define EXTENDED_MP 0x15 /* SUBPG > 0, for all device type */ #define PROT_SPEC_LU_MP 0x18 #define PROT_SPEC_PORT_MP 0x19 #define POWER_MP 0x1a @@ -49,7 +58,8 @@ #define MMCMS_MP 0x2a #define ALL_MPAGES 0x3f -/* Mode subpage numbers */ +/* Mode subpage numbers (when SPF bit is set in bit 6, byte 0 of response) */ +#define NO_MSP 0 /* SPF is clear (0) in this case */ #define MSP_SPC_CE 1 /* control extension */ #define MSP_SPI_MC 1 #define MSP_SPI_STC 2 @@ -58,9 +68,11 @@ #define MSP_SAS_PCD 1 #define MSP_SAS_SPC 2 #define MSP_SAS_E_PHY 3 +#define MSP_SAS_OOB_M_C 4 /* OOB Management Control */ #define MSP_BACK_CTL 1 -#define MSP_SAT_PATA 0xf1 /* SAT PATA Control */ -#define MSP_SAT_POWER 0xf1 /* SAT ATA Power condition */ +#define MSP_SAT_AFC 0xf2 /* SAT ATA Feature Control [a,f2] */ +#define MSP_SAT_PATA 0xf1 /* SAT PATA Control [a,f1] */ +#define MSP_SAT_POWER 0xf1 /* SAT ATA Power condition [1a,f1] */ #define MSP_DEV_CONF_EXT 1 /* device conf extension (ssc) */ #define MSP_EXT_DEV_CAP 0x41 /* extended device capabilities (smc) */ #define MSP_ADC_TGT_DEV 0x1 @@ -73,8 +85,11 @@ #define MSP_SPC_PS 0x1 /* power consumption */ #define MSP_SPC_CDLA 0x3 #define MSP_SPC_CDLB 0x4 +#define MSP_SPC_CDLT2A 0x7 /* spc6r01 */ +#define MSP_SPC_CDLT2B 0x8 /* spc6r01 */ #define MSP_SBC_IO_ADVI 0x5 #define MSP_SBC_BACK_OP 0x6 +#define MSP_ZB_D_CTL 0xf /* zbc2r04a */ #define MODE_DATA_OVERHEAD 128 #define EBUFF_SZ 256 @@ -90,7 +105,7 @@ #define VPD_DEVICE_ID 0x83 #define VPD_SOFTW_INF_ID 0x84 #define VPD_MAN_NET_ADDR 0x85 -#define VPD_EXT_INQ 0x86 +#define VPD_EXT_INQ 0x86 /* Extended Inquiry */ #define VPD_MODE_PG_POLICY 0x87 #define VPD_SCSI_PORTS 0x88 #define VPD_ATA_INFO 0x89 @@ -101,22 +116,25 @@ #define VPD_3PARTY_COPY 0x8f #define VPD_PROTO_LU 0x90 #define VPD_PROTO_PORT 0x91 -#define VPD_BLOCK_LIMITS 0xb0 /* SBC-3 */ -#define VPD_SA_DEV_CAP 0xb0 /* SSC-3 */ -#define VPD_OSD_INFO 0xb0 /* OSD */ -#define VPD_BLOCK_DEV_CHARS 0xb1 /* SBC-3 */ -#define VPD_MAN_ASS_SN 0xb1 /* SSC-3, ADC-2 */ -#define VPD_SECURITY_TOKEN 0xb1 /* OSD */ -#define VPD_TA_SUPPORTED 0xb2 /* SSC-3 */ -#define VPD_LB_PROVISIONING 0xb2 /* SBC-3 */ -#define VPD_REFERRALS 0xb3 -#define VPD_AUTOMATION_DEV_SN 0xb3 /* SSC-3 */ -#define VPD_SUP_BLOCK_LENS 0xb4 /* SBC-4 */ -#define VPD_DTDE_ADDRESS 0xb4 /* SSC-4 */ -#define VPD_BLOCK_DEV_C_EXTENS 0xb5 /* SBC-4 */ -#define VPD_LB_PROTECTION 0xb5 /* SSC-5 */ -#define VPD_ZBC_DEV_CHARS 0xb6 /* ZBC */ -#define VPD_BLOCK_LIMITS_EXT 0xb7 /* SBC-4 */ +#define VPD_SCSI_FEATURE_SETS 0x92 /* spc5r11 */ +#define VPD_BLOCK_LIMITS 0xb0 /* SBC-3 */ +#define VPD_SA_DEV_CAP 0xb0 /* SSC-3 */ +#define VPD_OSD_INFO 0xb0 /* OSD */ +#define VPD_BLOCK_DEV_CHARS 0xb1 /* SBC-3 */ +#define VPD_MAN_ASS_SN 0xb1 /* SSC-3, ADC-2 */ +#define VPD_SECURITY_TOKEN 0xb1 /* OSD */ +#define VPD_TA_SUPPORTED 0xb2 /* SSC-3 */ +#define VPD_LB_PROVISIONING 0xb2 /* SBC-3 */ +#define VPD_REFERRALS 0xb3 /* SBC-3 */ +#define VPD_AUTOMATION_DEV_SN 0xb3 /* SSC-3 */ +#define VPD_SUP_BLOCK_LENS 0xb4 /* sbc4r01 */ +#define VPD_DTDE_ADDRESS 0xb4 /* SSC-4 */ +#define VPD_BLOCK_DEV_C_EXTENS 0xb5 /* sbc4r02 */ +#define VPD_LB_PROTECTION 0xb5 /* SSC-5 */ +#define VPD_ZBC_DEV_CHARS 0xb6 /* zbc-r01b */ +#define VPD_BLOCK_LIMITS_EXT 0xb7 /* sbc4r08 */ +#define VPD_FORMAT_PRESETS 0xb8 /* sbc4r18 */ +#define VPD_CON_POS_RANGE 0xb9 /* 20-089r2 */ #define VPD_NOT_STD_INQ -2 /* request for standard inquiry */ #define VPD_ASSOC_LU 0 @@ -129,20 +147,43 @@ #define VPD_DI_SEL_TARGET 4 #define VPD_DI_SEL_AS_IS 32 +#ifndef SG_NVME_VPD_NICR +#define SG_NVME_VPD_NICR 0xde /* NVME Identify Controller Response */ +#endif + #define DEF_TRANSPORT_PROTOCOL TPROTO_SAS -/* Vendor identifiers */ +/* Vendor identifiers, only for mode pages, not VPD pages */ #define VENDOR_SEAGATE 0x0 #define VENDOR_HITACHI 0x1 #define VENDOR_MAXTOR 0x2 #define VENDOR_FUJITSU 0x3 +#define VENDOR_NONE 0x4 #define VENDOR_LTO5 0x5 #define VENDOR_LTO6 0x6 +#define VENDOR_NVME 0x7 +#define VENDOR_SG 0x8 /* bit flag settings for sdparm_mode_page_item::flags */ #define MF_COMMON 0x1 /* output in summary mode */ #define MF_HEX 0x2 #define MF_CLASH_OK 0x4 /* know this overlaps safely with generic */ +#define MF_TWOS_COMP 0x8 /* integer is two's complement */ +#define MF_ALL_1S 0x10 /* even with MF_HEX output all ones as -1 */ +#define MF_SAVE_PGS 0x20 /* advise/require --save option */ +#define MF_DESC_ID_B0 0x100 /* mpage descriptor ID, bit 0 */ +#define MF_DESC_ID_B1 0x200 +#define MF_DESC_ID_B2 0x400 +#define MF_DESC_ID_B3 0x800 +#define MF_DESC_ID_MASK 0xf00 +#define MF_DESC_ID_SHIFT 8 + +/* Output (bit) mask values */ +#define MP_OM_CUR 0x1 +#define MP_OM_CHA 0x2 +#define MP_OM_DEF 0x4 +#define MP_OM_SAV 0x8 +#define MP_OM_ALL 0xf /* enumerations for commands */ #define CMD_READY 1 @@ -157,37 +198,77 @@ #define CMD_SPEED 10 #define CMD_PROFILE 11 -#ifndef TPROTO_UAS -#define TPROTO_UAS 0x9 -#endif -#ifndef TPROTO_SOP -#define TPROTO_SOP 0xa -#endif +/* squeeze two PDTs into one field, must not use PDT_DISK as upper */ +#define PDT_DISK_ZBC (PDT_DISK | (PDT_ZBC << 8)) +/* Mainly command line options */ struct sdparm_opt_coll { - int do_all; - int dbd; - int defaults; - int dummy; + bool dbd; + bool dummy; + bool examine; + bool flexible; + bool inquiry; + bool mode_6; /* false (default) for Mode Sense or Select(10) */ + bool num_desc; /* report number of descriptors */ + bool do_raw; /* -R (usually '-r' but already used) */ + bool read_only; + bool save; + bool verbose_given; + bool version_given; + int defaults; /* set mode page to its default values, or when set + * twice set RTD bit to set defaults on all pages */ + int do_all; /* -iaa outputs all VPD pages found in the Supported + * VPD Pages VPD page (0x0) */ int do_enum; - int flexible; int do_hex; - int inquiry; int do_long; - int mode_6; - int num_desc; + int out_mask; /* OR-ed MP_OM_* values, default: MP_OM_ALL (0xf) */ int pdt; int do_quiet; - int do_raw; /* -R (usually '-r' but already used) */ - int read_only; - int save; int transport; /* -1 means not transport specific (def) */ - int vendor; /* -1 means not vendor specific (def) */ + int vendor_id; /* -1 means not vendor specific (def) */ int verbose; const char * inhex_fn; }; +/* Instances and arrays of the following templates are mainly found in the + * sdparm_data.c file. */ + +/* Template for mode pages that use descriptor format; forms in use: + * Fixed descriptor Length + * ======================= + * num_descs_off >=0 >=0 + * num_descs_inc >=0 -1 + * desc_len >0 >0 + * desc_len_off -1 -1 + * have_desc_id false false + * --------------------------------------------------- + * Notes number of descs length of descs + * in mpage in mpage + * ^ ^ + * | | + * Examples: pcd (SAS/SPL) lbp, atag (SBC) + * + * + * Variable descriptor Length + * ========================== + * num_descs_off >=0 -1 + * num_descs_inc >=0 0 + * desc_len -1 -1 + * desc_len_off >=0 >=0 + * have_desc_id false true + * --------------------------------------------------- + * Notes number of descs and only length of descs + * and length in mpage in mpage + * ^ ^ + * | | + * Examples: sep (SAS/SPL) oobm (SAS/SPL) + * + * The last one (i.e. have_desc_id=true) is the most complex. It can have + * many descriptors, one or more of one desc_id and one or more of another + * desc_id. And descriptors with different desc_id_s can have different + * lengths and different fields (items). */ struct sdparm_mode_descriptor_t { int num_descs_off; /* byte offset of start of num_descriptors */ int num_descs_bytes; /* number of bytes in num_descriptors field */ @@ -200,14 +281,20 @@ int desc_len_bytes; /* ... after start of descriptor */ /* Hence: = deref(base + d_len_off, d_len_bytes) + */ /* d_len_off + d_len_bytes */ + bool have_desc_id; /* descriptor has 4 bit ID, byte 0, bits 3 to 0 */ const char * name; }; +/* Template for each mode page, array populated in sdparm_data.c for generic + * and transport mpages. Vendor mode pages found in sdparm_data_vendor.c . */ struct sdparm_mode_page_t { int page; int subpage; - int pdt; /* peripheral device type id, -1 is the default */ - /* (not applicable) value */ + int pdt_s; /* compound peripheral device type id, -1 is the default + * for fields defined in SPC (common to all PDTs). + * Compound pdt_s may hold two PDTs. The most common + * example is: + * PDT_DISK | (PDT_ZBC << 8) */ int ro; /* read-only */ const char * acron; const char * name; @@ -215,33 +302,31 @@ /* non-NULL when mpage has descriptor format */ }; -struct sdparm_transport_id_t { - int proto_num; - const char * acron; - const char * name; -}; - +/* Template for each VPD page, array populated in sdparm_data.c */ struct sdparm_vpd_page_t { int vpd_num; int subvalue; - int pdt; /* peripheral device type id, -1 is the default */ - /* (not applicable) value */ + int pdt_s; /* see pdt_s explanation above */ const char * acron; const char * name; }; +/* Template for each mode/VPD page vendor, array populated in + * sdparm_data_vendor.c */ struct sdparm_vendor_name_t { - int vendor_num; + int vendor_id; const char * acron; const char * name; }; +/* Template for each mode page field (item), arrays for generic and each + * transport populated in sdparm_data.c . Arrays for vendors populated + * in sdparm_data_vendor.c */ struct sdparm_mode_page_item { const char * acron; - int page_num; - int subpage_num; - int pdt; /* peripheral device type or -1 (default) if not */ - /* applicable */ + int pg_num; + int subpg_num; + int pdt_s; /* see pdt_s explanation above */ int start_byte; int start_bit; int num_bits; @@ -250,30 +335,53 @@ const char * extra; }; +/* Command line arguments to --clear, --get= and --set= are parsed into + * one or more instances of this structure. On the command line each + * has the form: [.][=] + * struct sdparm_mode_page_settings contains an array of these. */ struct sdparm_mode_page_it_val { - struct sdparm_mode_page_item mpi; - int64_t val; - int64_t orig_val; - int descriptor_num; + struct sdparm_mode_page_item mpi; /* holds in above form */ + int64_t val; /* holds in above form. Defaults to 1 for + * for --set=, and to 0 for --clear= and --get= . + * The for --get= is for output format. */ + int64_t orig_val; /* what Mode sense indicates is currently in that + * [.] . If same, nothing to do */ + int descriptor_num; /* holds in the above form. Defaults to 0 + * which indicates the first descriptor. 1 corresponds + * to second descriptor, etc. */ }; +/* Instance holds the argument to --clear=, --get= or --set= plus the + * mode page number and sub-page number. That argument could be empty, a + * single [.][=] instance or a comma separated list of + * them. */ struct sdparm_mode_page_settings { - int page_num; - int subpage_num; + int pg_num; + int subpg_num; struct sdparm_mode_page_it_val it_vals[MAX_MP_IT_VAL]; - int num_it_vals; + int num_it_vals; /* number of active elements in it_vals[] */ }; +/* Template for a transport's mode pages and fields. Array of these in + * sdparm_data.c . Index of array corresponds to SCSI transport protocol id + * (which is a number from 0 to 15). Undefined or unsupported entries + * contain NULL, NULL. */ struct sdparm_transport_pair { - struct sdparm_mode_page_t * mpage; - struct sdparm_mode_page_item * mitem; + struct sdparm_mode_page_t * mpage; /* array of transport specific + mode pages */ + struct sdparm_mode_page_item * mitem; /* array of transport specific + mode page fields (items) */ }; +/* Template for a vendor's mode pages and fields. Array of these found in + * sdparm_data_vendor.c . */ struct sdparm_vendor_pair { struct sdparm_mode_page_t * mpage; struct sdparm_mode_page_item * mitem; }; +/* Template for a simple SCSI command supported by sdparm. Array of them + * found in sdparm_data.c */ struct sdparm_command_t { int cmd_num; const char * name; @@ -281,6 +389,7 @@ const char * extra_arg; }; +/* Simple value and description pair */ struct sdparm_val_desc_t { int val; const char * desc; @@ -288,11 +397,12 @@ extern struct sdparm_mode_page_t sdparm_gen_mode_pg[]; extern struct sdparm_vpd_page_t sdparm_vpd_pg[]; -extern struct sdparm_transport_id_t sdparm_transport_id[]; +extern struct sdparm_val_desc_t sdparm_transport_id[]; +extern struct sdparm_val_desc_t sdparm_add_transport_acron[]; extern struct sdparm_transport_pair sdparm_transport_mp[]; extern struct sdparm_vendor_name_t sdparm_vendor_id[]; extern struct sdparm_vendor_pair sdparm_vendor_mp[]; -extern int sdparm_vendor_mp_len; +extern const int sdparm_vendor_mp_len; extern struct sdparm_mode_page_item sdparm_mitem_arr[]; extern struct sdparm_command_t sdparm_command_arr[]; extern struct sdparm_val_desc_t sdparm_profile_arr[]; @@ -302,36 +412,34 @@ extern const char * sdparm_mode_page_policy_arr[]; -int sdp_get_mp_len(unsigned char * mp); -const struct sdparm_mode_page_t * sdp_get_mode_detail(int page_num, +int sdp_mpage_len(const uint8_t * mp); /* page, not MS response */ +const struct sdparm_mode_page_t * sdp_get_mpage_t(int page_num, int subpage_num, int pdt, int transp_proto, int vendor_num); -const struct sdparm_mode_page_t * sdp_get_mpage_name(int page_num, +const struct sdparm_mode_page_t * sdp_get_mpt_with_str(int page_num, int subpage_num, int pdt, int transp_proto, int vendor_num, - int plus_acron, int hex, char * bp, int max_b_len); -const struct sdparm_mode_page_t * sdp_find_mp_by_acron(const char * ap, + bool plus_acron, bool hex, int max_b_len, char * bp); +const struct sdparm_mode_page_t * sdp_find_mpt_by_acron(const char * ap, int transp_proto, int vendor_num); const struct sdparm_vpd_page_t * sdp_get_vpd_detail(int page_num, int subvalue, int pdt); const struct sdparm_vpd_page_t * sdp_find_vpd_by_acron(const char * ap); -const char * sdp_get_transport_name(int proto_num); -const struct sdparm_transport_id_t * sdp_find_transport_by_acron( - const char * ap); +char * sdp_get_transport_name(int proto_num, int b_len, char * b); +/* sdp_find_transport_id_by_acron() returns -1 for not found */ +int sdp_find_transport_id_by_acron(const char * ap); const char * sdp_get_vendor_name(int vendor_num); const struct sdparm_vendor_name_t * sdp_find_vendor_by_acron(const char * ap); const struct sdparm_vendor_pair * sdp_get_vendor_pair(int vendor_num); const struct sdparm_mode_page_item * sdp_find_mitem_by_acron(const char * ap, int * from, int transp_proto, int vendor_num); -uint64_t sdp_get_big_endian(const unsigned char * from, int start_bit, - int num_bits); -void sdp_set_big_endian(uint64_t val, unsigned char * to, int start_bit, - int num_bits); -uint64_t sdp_mp_get_value(const struct sdparm_mode_page_item *mpi, - const unsigned char * mp); -uint64_t sdp_mp_get_value_check(const struct sdparm_mode_page_item *mpi, - const unsigned char * mp, int * all_set); -void sdp_mp_set_value(uint64_t val, const struct sdparm_mode_page_item *mpi, - unsigned char * mp); +uint64_t sdp_mitem_get_value(const struct sdparm_mode_page_item *mpi, + const uint8_t * mp); +uint64_t sdp_mitem_get_value_check(const struct sdparm_mode_page_item *mpi, + const uint8_t * mp, bool * all_setp); +void sdp_print_signed_decimal(uint64_t u, int num_bits, bool leading_zeros); +void sdp_mitem_set_value(uint64_t val, const struct sdparm_mode_page_item *mpi, + uint8_t * mp); char * sdp_get_ansi_version_str(int version, int buff_len, char * buff); +int sdp_get_desc_id(int flags); int sdp_strcase_eq(const char * s1p, const char * s2p); int sdp_strcase_eq_upto(const char * s1p, const char * s2p, int n); @@ -340,22 +448,35 @@ */ int sdp_process_vpd_page(int sg_fd, int pn, int spn, - const struct sdparm_opt_coll * opts, int req_pdt, - int protect, const unsigned char * ihbp, - int ihb_len); + const struct sdparm_opt_coll * op, int req_pdt, + bool protect, const uint8_t * ihbp, int ihb_len, + uint8_t * alt_buf, int off); /* * Declarations for functions found in sdparm_cmd.c */ -const struct sdparm_command_t * sdp_build_cmd(const char * cmd_str, int * rwp, - int * argp); +const struct sdparm_command_t * sdp_build_cmd(const char * cmd_str, + bool * rwp, int * argp); void sdp_enumerate_commands(); int sdp_process_cmd(int sg_fd, const struct sdparm_command_t * scmdp, int cmd_arg, int pdt, const struct sdparm_opt_coll * opts); +/* Must not have PDT_DISK as upper byte of mask */ +#define PDT_LOWER_MASK 0xff +#define PDT_UPPER_MASK (~PDT_LOWER_MASK) + +/* Returns true if left argument is "equal" to the right argument. l_pdt_s + * is a compound PDT (SCSI Peripheral Device Type) or a negative number + * which represents a wildcard (i.e. match anything). r_pdt_s has a similar + * form. PDT values are 5 bits long (0 to 31) and a compound pdt_s is + * formed by shifting the second (upper) PDT by eight bits to the left and + * OR-ing it with the first PDT. The pdt_s values must be defined so + * PDT_DISK (0) is _not_ the upper value in a compound pdt_s. */ +bool pdt_s_eq(int l_pdt_s, int r_pdt_s); + /* - * Declarations for functions that are port dependant + * Declarations for functions that are port dependent */ #ifdef SG_LIB_WIN32 diff -Nru sdparm-1.10/src/sdparm_vpd.c sdparm-1.12/src/sdparm_vpd.c --- sdparm-1.10/src/sdparm_vpd.c 2016-02-18 19:24:07.000000000 +0000 +++ sdparm-1.12/src/sdparm_vpd.c 2021-03-20 18:29:34.000000000 +0000 @@ -1,30 +1,27 @@ /* - * Copyright (c) 2005-2016 Douglas Gilbert. + * Copyright (c) 2005-2021, Douglas Gilbert * 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. + * 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. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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. - * + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. */ #include @@ -36,6 +33,10 @@ #define __STDC_FORMAT_MACROS 1 #include +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "sg_lib.h" #include "sg_cmds_basic.h" #include "sg_unaligned.h" @@ -50,34 +51,35 @@ /* Prints outs an abridged set of device identification designators selected by association, designator type and/or code set. */ static int -decode_dev_ids_quiet(unsigned char * buff, int len, int m_assoc, +decode_dev_ids_quiet(uint8_t * buff, int len, int m_assoc, int m_desig_type, int m_code_set) { - int m, p_id, c_set, piv, assoc, desig_type, i_len, is_sas; - int naa, off, u, rtp; - const unsigned char * ucp; - const unsigned char * ip; - unsigned char sas_tport_addr[8]; + int m, p_id, c_set, assoc, desig_type, i_len, naa, off, u, rtp; + bool piv, is_sas; + const uint8_t * bp; + const uint8_t * ip; + uint8_t sas_tport_addr[8]; + static const int sas_tport_addr_sz = sizeof(sas_tport_addr); rtp = 0; - memset(sas_tport_addr, 0, sizeof(sas_tport_addr)); + memset(sas_tport_addr, 0, sas_tport_addr_sz); off = -1; while ((u = sg_vpd_dev_id_iter(buff, len, &off, m_assoc, m_desig_type, m_code_set)) == 0) { - ucp = buff + off; - i_len = ucp[3]; + bp = buff + off; + i_len = bp[3]; if ((off + i_len + 4) > len) { pr2serr(" VPD page error: designator length longer than\n" " remaining response length=%d\n", (len - off)); return SG_LIB_CAT_MALFORMED; } - ip = ucp + 4; - p_id = ((ucp[0] >> 4) & 0xf); - c_set = (ucp[0] & 0xf); - piv = ((ucp[1] & 0x80) ? 1 : 0); - is_sas = (piv && (6 == p_id)) ? 1 : 0; - assoc = ((ucp[1] >> 4) & 0x3); - desig_type = (ucp[1] & 0xf); + ip = bp + 4; + p_id = ((bp[0] >> 4) & 0xf); + c_set = (bp[0] & 0xf); + piv = !!(bp[1] & 0x80); + is_sas = (piv && (6 == p_id)); + assoc = ((bp[1] >> 4) & 0x3); + desig_type = (bp[1] & 0xf); switch (desig_type) { case 0: /* vendor specific */ break; @@ -97,7 +99,7 @@ if (1 != c_set) { pr2serr(" << unexpected code set %d for NAA=%d >>\n", c_set, naa); - dStrHexErr((const char *)ip, i_len, 0); + hex2stderr(ip, i_len, 0); break; } switch (naa) { @@ -105,7 +107,7 @@ if (8 != i_len) { pr2serr(" << unexpected NAA 2 identifier length: " "0x%x >>\n", i_len); - dStrHexErr((const char *)ip, i_len, 0); + hex2stderr(ip, i_len, 0); break; } printf("0x"); @@ -117,7 +119,7 @@ if (8 != i_len) { pr2serr(" << unexpected NAA 3 identifier " "length: 0x%x >>\n", i_len); - dStrHexErr((const char *)ip, i_len, 0); + hex2stderr(ip, i_len, 0); break; } printf("0x"); @@ -129,10 +131,10 @@ if (8 != i_len) { pr2serr(" << unexpected NAA 5 identifier " "length: 0x%x >>\n", i_len); - dStrHexErr((const char *)ip, i_len, 0); + hex2stderr(ip, i_len, 0); break; } - if ((0 == is_sas) || (1 != assoc)) { + if ((! is_sas) || (1 != assoc)) { printf("0x"); for (m = 0; m < 8; ++m) printf("%02x", (unsigned int)ip[m]); @@ -150,14 +152,14 @@ printf("%02x", (unsigned int)sas_tport_addr[m]); printf("\n"); } - memcpy(sas_tport_addr, ip, sizeof(sas_tport_addr)); + memcpy(sas_tport_addr, ip, sas_tport_addr_sz); } break; case 6: /* NAA 5: IEEE Registered Extended */ if (16 != i_len) { pr2serr(" << unexpected NAA 6 identifier length: " "0x%x >>\n", i_len); - dStrHexErr((const char *)ip, i_len, 0); + hex2stderr(ip, i_len, 0); break; } printf("0x"); @@ -168,12 +170,12 @@ default: pr2serr(" << expected NAA nibble of 2, 3, 5 or 6, got " "%d >>\n", naa); - dStrHexErr((const char *)ip, i_len, 0); + hex2stderr(ip, i_len, 0); break; } break; case 4: /* Relative target port */ - if ((0 == is_sas) || (1 != c_set) || (1 != assoc) || (4 != i_len)) + if ((! is_sas) || (1 != c_set) || (1 != assoc) || (4 != i_len)) break; rtp = sg_get_unaligned_be16(ip + 2); if (sas_tport_addr[0]) { @@ -181,7 +183,7 @@ for (m = 0; m < 8; ++m) printf("%02x", (unsigned int)sas_tport_addr[m]); printf(",0x%x\n", rtp); - memset(sas_tport_addr, 0, sizeof(sas_tport_addr)); + memset(sas_tport_addr, 0, sas_tport_addr_sz); rtp = 0; } break; @@ -192,20 +194,20 @@ case 7: /* MD5 logical unit identifier */ break; case 8: /* SCSI name string */ - if (3 != c_set) { + if (c_set < 2) { /* accept ASCII as subset of UTF-8 */ pr2serr(" << expected UTF-8 code_set >>\n"); - dStrHexErr((const char *)ip, i_len, 0); + hex2stderr(ip, i_len, 0); break; } /* does %s print out UTF-8 ok?? * Seems to depend on the locale. Looks ok here with my * locale setting: en_AU.UTF-8 */ - printf("%s\n", (const char *)ip); + printf("%.*s\n", i_len, (const char *)ip); break; case 9: /* Protocol specific port identifier */ break; - case 0xa: /* UUID identifier */ + case 0xa: /* UUID identifier [spc5r08] RFC 4122 */ if ((1 != c_set) || (18 != i_len) || (1 != ((ip[0] >> 4) & 0xf))) break; for (m = 0; m < 16; ++m) { @@ -232,15 +234,17 @@ return 0; } +#define SDPARM_DECODE_DESC_SZ 2048 + static void -decode_designation_descriptor(const unsigned char * ucp, int i_len, - int print_assoc, +decode_designation_descriptor(const uint8_t * bp, int i_len, + bool print_assoc, const struct sdparm_opt_coll * op) { - char b[2048]; + char b[SDPARM_DECODE_DESC_SZ]; - sg_get_designation_descriptor_str(NULL, ucp, i_len + 4, print_assoc, - op->do_long, sizeof(b), b); + sg_get_designation_descriptor_str(NULL, bp, i_len + 4, print_assoc, + op->do_long, SDPARM_DECODE_DESC_SZ, b); printf("%s", b); } @@ -248,35 +252,47 @@ /* Prints outs device identification designators selected by association, designator type and/or code set. */ static int -decode_dev_ids(const char * print_if_found, unsigned char * buff, int len, - int m_assoc, int m_desig_type, int m_code_set, +decode_dev_ids(const char * print_if_found, int num_leading, uint8_t * buff, + int len, int m_assoc, int m_desig_type, int m_code_set, const struct sdparm_opt_coll * op) { - int i_len, assoc, printed, off, u; - const unsigned char * ucp; + bool printed; + int i_len, assoc, off, u; + const uint8_t * bp; + char b[1024]; + char sp[82]; if (op->do_quiet) return decode_dev_ids_quiet(buff, len, m_assoc, m_desig_type, m_code_set); + if (num_leading > (int)(sizeof(sp) - 2)) + num_leading = sizeof(sp) - 2; + if (num_leading > 0) + snprintf(sp, sizeof(sp), "%*c", num_leading, ' '); + else + sp[0] = '\0'; off = -1; - printed = 0; + printed = false; while ((u = sg_vpd_dev_id_iter(buff, len, &off, m_assoc, m_desig_type, m_code_set)) == 0) { - ucp = buff + off; - i_len = ucp[3]; + bp = buff + off; + i_len = bp[3]; if ((off + i_len + 4) > len) { pr2serr(" VPD page error: designator length longer than\n" " remaining response length=%d\n", (len - off)); return SG_LIB_CAT_MALFORMED; } - assoc = ((ucp[1] >> 4) & 0x3); - if (print_if_found && (0 == printed)) { - printed = 1; - printf(" %s:\n", print_if_found); + assoc = ((bp[1] >> 4) & 0x3); + if (print_if_found && (! printed)) { + printed = true; + if (strlen(print_if_found) > 0) + printf(" %s:\n", print_if_found); } if (NULL == print_if_found) - printf(" %s:\n", sg_get_desig_assoc_str(assoc)); - decode_designation_descriptor(ucp, i_len, 0, op); + printf(" %s%s:\n", sp, sg_get_desig_assoc_str(assoc)); + sg_get_designation_descriptor_str(sp, bp, i_len + 4, false, + op->do_long, sizeof(b), b); + printf("%s", b); } if (-2 == u) { pr2serr("VPD page error: short designator around offset %d\n", off); @@ -287,83 +303,136 @@ /* VPD_MODE_PG_POLICY 0x87 */ static int -decode_mode_policy_vpd(unsigned char * buff, int len) +decode_mode_policy_vpd(uint8_t * buff, int len) { int k, bump; - unsigned char * ucp; + uint8_t * bp; if (len < 4) { pr2serr("Mode page policy VPD page length too short=%d\n", len); return SG_LIB_CAT_MALFORMED; } len -= 4; - ucp = buff + 4; - for (k = 0; k < len; k += bump, ucp += bump) { + bp = buff + 4; + for (k = 0; k < len; k += bump, bp += bump) { bump = 4; if ((k + bump) > len) { pr2serr("Mode page policy VPD page, short descriptor length=%d, " "left=%d\n", bump, (len - k)); return SG_LIB_CAT_MALFORMED; } - printf(" Policy page code: 0x%x", (ucp[0] & 0x3f)); - if (ucp[1]) - printf(", subpage code: 0x%x\n", ucp[1]); + printf(" Policy page code: 0x%x", (bp[0] & 0x3f)); + if (bp[1]) + printf(", subpage code: 0x%x\n", bp[1]); else printf("\n"); - printf(" MLUS=%d, Policy: %s\n", !!(ucp[2] & 0x80), - sdparm_mode_page_policy_arr[ucp[2] & 0x3]); + printf(" MLUS=%d (multiple LUs (in a target) share), Policy: " + "%s\n", !!(bp[2] & 0x80), + sdparm_mode_page_policy_arr[bp[2] & 0x3]); } return 0; } -/* VPD_DEVICE_CONSTITUENTS 0x8b */ +static const char * constituent_type_arr[] = { + "Reserved", + "Virtual tape library", + "Virtual tape drive", + "Direct access block device", +}; + +/* VPD_DEVICE_CONSTITUENTS 0x8b, can recurse at least one level */ static int -decode_dev_const_vpd(unsigned char * buff, int len) +decode_dev_constit_vpd(uint8_t * buff, int len, int req_pdt, bool protect, + const struct sdparm_opt_coll * op) { - int k, j, bump, cd_len; - unsigned char * ucp; + int k, j, res, bump, csd_len; + uint16_t constit_type; + const uint8_t * bp; + const char * dcp = "Device constituents VPD page"; + char b[64]; if (len < 4) { - pr2serr("Device constituents VPD page length too short=%d\n", - len); + pr2serr("%s length too short=%d\n", dcp, len); return SG_LIB_CAT_MALFORMED; } len -= 4; - ucp = buff + 4; - for (k = 0, j = 0; k < len; k += bump, ucp += bump, ++j) { + bp = buff + 4; + for (k = 0, j = 0; k < len; k += bump, bp += bump, ++j) { + if (j > 0) + printf("\n"); printf(" Constituent descriptor %d:\n", j + 1); if ((k + 36) > len) { - pr2serr("Device constituents VPD page, short descriptor " - "length=36, left=%d\n", (len - k)); + pr2serr("%s, short descriptor length=36, left=%d\n", dcp, + (len - k)); return SG_LIB_CAT_MALFORMED; } - printf(" Constituent type: 0x%x\n", - sg_get_unaligned_be16(ucp + 0)); - printf(" Constituent device type: 0x%x\n", ucp[2]); - printf(" Vendor_identification: %.8s\n", ucp + 4); - printf(" Product_identification: %.16s\n", ucp + 12); - printf(" Product_revision_level: %.4s\n", ucp + 28); - cd_len = sg_get_unaligned_be16(ucp + 34); - bump = 36 + cd_len; + constit_type = sg_get_unaligned_be16(bp + 0); + if (constit_type >= SG_ARRAY_SIZE(constituent_type_arr)) + printf(" Constituent type: unknown [0x%x]\n", constit_type); + else + printf(" Constituent type: %s [0x%x]\n", + constituent_type_arr[constit_type], constit_type); + printf(" Constituent device type: "); + if (0xff == bp[2]) + printf("Unknown [0xff]\n"); + else if (bp[2] >= 0x20) + printf("Reserved [0x%x]\n", bp[2]); + else + printf("%s [0x%x]\n", sg_get_pdt_str(0x1f & bp[2], sizeof(b), b), + bp[2]); + printf(" Vendor_identification: %.8s\n", bp + 4); + printf(" Product_identification: %.16s\n", bp + 12); + printf(" Product_revision_level: %.4s\n", bp + 28); + csd_len = sg_get_unaligned_be16(bp + 34); + bump = 36 + csd_len; if ((k + bump) > len) { - pr2serr("Device constituents VPD page, short descriptor " - "length=%d, left=%d\n", bump, (len - k)); + pr2serr("%s, short descriptor length=%d, left=%d\n", dcp, bump, + (len - k)); return SG_LIB_CAT_MALFORMED; } - if (cd_len > 0) { - printf(" Constituent specific descriptor list (in hex):\n"); - dStrHex((const char *)(ucp + 36), cd_len, 1); + if (csd_len > 0) { + int m, q, cs_bump; + uint8_t cs_type; + uint8_t cs_len; + const uint8_t * cs_bp; + + printf(" Constituent specific descriptors:\n"); + for (m = 0, q = 0, cs_bp = bp + 36; m < csd_len; + m += cs_bump, ++q, cs_bp += cs_bump) { + cs_type = cs_bp[0]; + cs_len = sg_get_unaligned_be16(cs_bp + 2); + cs_bump = cs_len + 4; + if (1 == cs_type) { /* VPD page */ + int off = cs_bp + 4 - buff; + + printf(" Constituent VPD page %d:\n", q + 1); + /* SPC-5 says these shall _not_ themselves be Device + * Constituent VPD pages. So no infinite recursion. */ + res = sdp_process_vpd_page(-1, 0 /* pn */, 0, op, + req_pdt, protect, NULL, 0, + buff, off); + if (res) + return res; + } else { + if (0xff == cs_type) + printf(" Vendor specific data (in hex):\n"); + else + printf(" Reserved [0x%x] specific data (in " + "hex):\n", cs_type); + hex2stdout(cs_bp + 4, cs_len, 0 /* plus ASCII */); + } + } /* end of Constituent specific descriptor loop */ } - } + } /* end Constituent descriptor loop */ return 0; } /* VPD_MAN_NET_ADDR 0x85 */ static int -decode_man_net_vpd(unsigned char * buff, int len) +decode_man_net_vpd(uint8_t * buff, int len) { int k, bump, na_len; - unsigned char * ucp; + uint8_t * bp; if (len < 4) { pr2serr("Management network addresses VPD page length too short=%d\n", @@ -371,12 +440,12 @@ return SG_LIB_CAT_MALFORMED; } len -= 4; - ucp = buff + 4; - for (k = 0; k < len; k += bump, ucp += bump) { + bp = buff + 4; + for (k = 0; k < len; k += bump, bp += bump) { printf(" %s, Service type: %s\n", - sg_get_desig_assoc_str((ucp[0] >> 5) & 0x3), - sdparm_network_service_type_arr[ucp[0] & 0x1f]); - na_len = sg_get_unaligned_be16(ucp + 2); + sg_get_desig_assoc_str((bp[0] >> 5) & 0x3), + sdparm_network_service_type_arr[bp[0] & 0x1f]); + na_len = sg_get_unaligned_be16(bp + 2); bump = 4 + na_len; if ((k + bump) > len) { pr2serr("Management network addresses VPD page, short descriptor " @@ -384,7 +453,7 @@ return SG_LIB_CAT_MALFORMED; } if (na_len > 0) { - printf(" %s\n", ucp + 4); + printf(" %s\n", bp + 4); } } return 0; @@ -393,51 +462,72 @@ /* This is xcopy(LID4) related: "ROD" == Representation Of Data * Used by VPD_3PARTY_COPY */ static void -decode_rod_descriptor(const unsigned char * buff, int len) +decode_rod_descriptor(const uint8_t * buff, int len) { - const unsigned char * ucp = buff; + const uint8_t * bp = buff; int k, bump; + uint64_t ul; - for (k = 0; k < len; k += bump, ucp += bump) { - bump = sg_get_unaligned_be16(ucp + 2) + 4; - switch (ucp[0]) { + for (k = 0; k < len; k += bump, bp += bump) { + bump = sg_get_unaligned_be16(bp + 2) + 4; + switch (bp[0]) { case 0: /* Block ROD device type specific descriptor */ printf(" Optimal block ROD length granularity: %d\n", - sg_get_unaligned_be16(ucp + 6)); + sg_get_unaligned_be16(bp + 6)); printf(" Maximum Bytes in block ROD: %" PRIu64 "\n", - sg_get_unaligned_be64(ucp + 8)); - printf(" Optimal Bytes in block ROD transfer: %" PRIu64 "\n", - sg_get_unaligned_be64(ucp + 16)); - printf(" Optimal Bytes to token per segment: %" PRIu64 "\n", - sg_get_unaligned_be64(ucp + 24)); - printf(" Optimal Bytes from token per segment:" - " %" PRIu64 "\n", sg_get_unaligned_be64(ucp + 32)); + sg_get_unaligned_be64(bp + 8)); + ul = sg_get_unaligned_be64(bp + 16); + printf(" Optimal Bytes in block ROD transfer: "); + if (SG_LIB_UNBOUNDED_64BIT == ul) + printf("-1 [no limit]\n"); + else + printf("%" PRIu64 "\n", ul); + ul = sg_get_unaligned_be64(bp + 24); + printf(" Optimal Bytes to token per segment: "); + if (SG_LIB_UNBOUNDED_64BIT == ul) + printf("-1 [no limit]\n"); + else + printf("%" PRIu64 "\n", ul); + ul = sg_get_unaligned_be64(bp + 32); + printf(" Optimal Bytes from token per segment: "); + if (SG_LIB_UNBOUNDED_64BIT == ul) + printf("-1 [no limit]\n"); + else + printf("%" PRIu64 "\n", ul); break; case 1: /* Stream ROD device type specific descriptor */ printf(" Maximum Bytes in stream ROD: %" PRIu64 "\n", - sg_get_unaligned_be64(ucp + 8)); - printf(" Optimal Bytes in stream ROD transfer:" - " %" PRIu64 "\n", sg_get_unaligned_be64(ucp + 16)); + sg_get_unaligned_be64(bp + 8)); + ul = sg_get_unaligned_be64(bp + 16); + printf(" Optimal Bytes in stream ROD transfer: "); + if (SG_LIB_UNBOUNDED_64BIT == ul) + printf("-1 [no limit]\n"); + else + printf("%" PRIu64 "\n", ul); break; case 3: /* Copy manager ROD device type specific descriptor */ printf(" Maximum Bytes in processor ROD: %" PRIu64 "\n", - sg_get_unaligned_be64(ucp + 8)); - printf(" Optimal Bytes in processor ROD transfer:" - " %" PRIu64 "\n", sg_get_unaligned_be64(ucp + 16)); + sg_get_unaligned_be64(bp + 8)); + ul = sg_get_unaligned_be64(bp + 16); + printf(" Optimal Bytes in processor ROD transfer: "); + if (SG_LIB_UNBOUNDED_64BIT == ul) + printf("-1 [no limit]\n"); + else + printf("%" PRIu64 "\n", ul); break; default: printf(" Unhandled descriptor (format %d, device type %d)\n", - ucp[0] >> 5, ucp[0] & 0x1F); + bp[0] >> 5, bp[0] & 0x1F); break; } } } struct tpc_desc_type { - unsigned char code; + uint8_t code; const char * name; }; @@ -487,7 +577,7 @@ }; static const char * -get_tpc_desc_name(unsigned char code) +get_tpc_desc_name(uint8_t code) { const struct tpc_desc_type * dtp; @@ -555,24 +645,26 @@ /* VPD_3PARTY_COPY (3PC, THIRD PARTY COPY, SPC-4, SBC-3) 0x8f */ static void -decode_3party_copy_vpd(unsigned char * buff, int len, int do_hex, int verbose) +decode_3party_copy_vpd(uint8_t * buff, int len, int do_hex, int pdt, + int verbose) { - int j, k, m, bump, desc_type, desc_len, sa_len; + int j, k, m, bump, desc_type, desc_len, sa_len, blen; unsigned int u; - const unsigned char * ucp; + const uint8_t * bp; const char * cp; uint64_t ull; - char b[80]; + char b[120]; if (len < 4) { pr2serr("Third-party Copy VPD page length too short=%d\n", len); return; } + blen = sizeof(b); len -= 4; - ucp = buff + 4; - for (k = 0; k < len; k += bump, ucp += bump) { - desc_type = sg_get_unaligned_be16(ucp); - desc_len = sg_get_unaligned_be16(ucp + 2); + bp = buff + 4; + for (k = 0; k < len; k += bump, bp += bump) { + desc_type = sg_get_unaligned_be16(bp); + desc_len = sg_get_unaligned_be16(bp + 2); if (verbose) printf("Descriptor type=%d, len %d\n", desc_type, desc_len); bump = 4 + desc_len; @@ -584,62 +676,99 @@ if (0 == desc_len) continue; if (2 == do_hex) - dStrHex((const char *)ucp + 4, desc_len, 1); + hex2stdout(bp + 4, desc_len, 1); else if (do_hex > 2) - dStrHex((const char *)ucp, bump, 1); + hex2stdout(bp, bump, 1); else { + int csll; + switch (desc_type) { case 0x0000: /* Required if POPULATE TOKEN (or friend) used */ printf(" Block Device ROD Token Limits:\n"); - printf(" Maximum Range Descriptors: %d\n", - sg_get_unaligned_be16(ucp + 10)); - u = sg_get_unaligned_be32(ucp + 12); - printf(" Maximum Inactivity Timeout: %u seconds\n", u); - u = sg_get_unaligned_be32(ucp + 16); - printf(" Default Inactivity Timeout: %u seconds\n", u); - ull = sg_get_unaligned_be64(ucp + 20); - printf(" Maximum Token Transfer Size: %" PRIu64 "\n", ull); - ull = sg_get_unaligned_be64(ucp + 28); - printf(" Optimal Transfer Count: %" PRIu64 "\n", ull); + u = sg_get_unaligned_be16(bp + 10); + printf(" Maximum range descriptors: "); + if (0 == u) + printf("0 [not reported]\n"); + else + printf("%u\n", u); + u = sg_get_unaligned_be32(bp + 12); + printf(" Maximum inactivity timeout: "); + if (0 == u) + printf("0 [not reported]\n"); + else if (SG_LIB_UNBOUNDED_32BIT == u) + printf("-1 [no maximum given]\n"); + else + printf("%u seconds\n", u); + u = sg_get_unaligned_be32(bp + 16); + printf(" Default inactivity timeout: "); + if (0 == u) + printf("0 [not reported]\n"); + else + printf("%u seconds\n", u); + ull = sg_get_unaligned_be64(bp + 20); + printf(" Maximum token transfer size: "); + if (0 == ull) + printf("0 [not reported]\n"); + else + printf("%" PRIu64 "\n", ull); + ull = sg_get_unaligned_be64(bp + 28); + printf(" Optimal transfer count: "); + if (0 == ull) + printf("0 [not reported]\n"); + else + printf("%" PRIu64 "\n", ull); break; case 0x0001: /* Mandatory (SPC-4) */ printf(" Supported Commands:\n"); j = 0; - while (j < ucp[4]) { - sa_len = ucp[6 + j]; - for (m = 0; m < sa_len; ++m) { - sg_get_opcode_sa_name(ucp[5 + j], ucp[7 + j + m], - 0, sizeof(b), b); + csll = bp[4]; + if (csll >= desc_len) { + pr2serr("Command supported list length (%d) >= " + "descriptor length (%d), wrong so trim\n", + csll, desc_len); + csll = desc_len - 1; + } + while (j < csll) { + sa_len = bp[6 + j]; + for (m = 0; (m < sa_len) && ((j + m) < csll); ++m) { + sg_get_opcode_sa_name(bp[5 + j], bp[7 + j + m], + pdt, blen, b); printf(" %s\n", b); } - j += sa_len + 2; + if (0 == sa_len) { + sg_get_opcode_name(bp[5 + j], pdt, blen, b); + printf(" %s\n", b); + } else if (m < sa_len) + pr2serr("Supported service actions list length (%d) " + "is too large\n", sa_len); + j += m + 2; } break; case 0x0004: - printf(" Parameter Data:\n"); - printf(" Maximum CSCD Descriptor Count: %d\n", - sg_get_unaligned_be16(ucp + 8)); - printf(" Maximum Segment Descriptor Count: %d\n", - sg_get_unaligned_be16(ucp + 10));; - u = sg_get_unaligned_be32(ucp + 12); - printf(" Maximum Descriptor List Length: %u\n", u); - u = sg_get_unaligned_be32(ucp + 16); - printf(" Maximum Inline Data Length: %u\n", u); + printf(" Parameter data:\n"); + printf(" Maximum CSCD descriptor count: %d\n", + sg_get_unaligned_be16(bp + 8)); + printf(" Maximum segment descriptor count: %d\n", + sg_get_unaligned_be16(bp + 10));; + u = sg_get_unaligned_be32(bp + 12); + printf(" Maximum descriptor list length: %u\n", u); + u = sg_get_unaligned_be32(bp + 16); + printf(" Maximum inline data length: %u\n", u); break; case 0x0008: - printf(" Supported Descriptors:\n"); - for (j = 0; j < ucp[4]; j++) { - cp = get_tpc_desc_name(ucp[5 + j]); + printf(" Supported descriptors:\n"); + for (j = 0; j < bp[4]; j++) { + cp = get_tpc_desc_name(bp[5 + j]); if (strlen(cp) > 0) - printf(" %s [0x%x]\n", cp, ucp[5 + j]); + printf(" %s [0x%x]\n", cp, bp[5 + j]); else - printf(" 0x%x\n", ucp[5 + j]); + printf(" 0x%x\n", bp[5 + j]); } break; case 0x000C: printf(" Supported CSCD IDs (above 0x7ff):\n"); - for (j = 0; j < sg_get_unaligned_be16(ucp + 4); j += 2) { - u = sg_get_unaligned_be16(ucp + 6 + j); + for (j = 0; j < sg_get_unaligned_be16(bp + 4); j += 2) { + u = sg_get_unaligned_be16(bp + 6 + j); cp = get_cscd_desc_id_name(u); if (strlen(cp) > 0) printf(" %s [0x%04x]\n", cp, u); @@ -647,65 +776,72 @@ printf(" 0x%04x\n", u); } break; + case 0x000D: + printf(" Copy group identifier:\n"); + u = bp[4]; + sg_t10_uuid_desig2str(bp + 5, u, 1 /* c_set */, false, + false, NULL, blen, b); + printf("%s", b); + break; case 0x0106: - printf(" ROD Token Features:\n"); - printf(" Remote Tokens: %d\n", ucp[4] & 0x0f); - u = sg_get_unaligned_be32(ucp + 16); - printf(" Minimum Token Lifetime: %u seconds\n", u); - u = sg_get_unaligned_be32(ucp + 20); - printf(" Maximum Token Lifetime: %u seconds\n", u); - u = sg_get_unaligned_be32(ucp + 24); - printf(" Maximum Token inactivity timeout: %d\n", u); - decode_rod_descriptor(ucp + 48, - sg_get_unaligned_be16(ucp + 46)); + printf(" ROD token features:\n"); + printf(" Remote tokens: %d\n", bp[4] & 0x0f); + u = sg_get_unaligned_be32(bp + 16); + printf(" Minimum token lifetime: %u seconds\n", u); + u = sg_get_unaligned_be32(bp + 20); + printf(" Maximum token lifetime: %u seconds\n", u); + u = sg_get_unaligned_be32(bp + 24); + printf(" Maximum token inactivity timeout: %u\n", u); + decode_rod_descriptor(bp + 48, + sg_get_unaligned_be16(bp + 46)); break; case 0x0108: - printf(" Supported ROD Token and ROD Types:\n"); - for (j = 0; j < sg_get_unaligned_be16(ucp + 6); j+= 64) { - u = sg_get_unaligned_be32(ucp + 8 + j); + printf(" Supported ROD token and ROD types:\n"); + for (j = 0; j < sg_get_unaligned_be16(bp + 6); j+= 64) { + u = sg_get_unaligned_be32(bp + 8 + j); cp = get_tpc_rod_name(u); if (strlen(cp) > 0) - printf(" ROD Type: %s [0x%x]\n", cp, u); + printf(" ROD type: %s [0x%x]\n", cp, u); else - printf(" ROD Type: 0x%x\n", u); + printf(" ROD type: 0x%x\n", u); printf(" Internal: %s\n", - (ucp[8 + j + 4] & 0x80) ? "yes" : "no"); - printf(" Token In: %s\n", - (ucp[8 + j + 4] & 0x02) ? "yes" : "no"); - printf(" Token Out: %s\n", - (ucp[8 + j + 4] & 0x01) ? "yes" : "no"); - printf(" Preference: %d\n", - sg_get_unaligned_be16(ucp + 8 + j + 6)); + (bp[8 + j + 4] & 0x80) ? "yes" : "no"); + printf(" Token in: %s\n", + (bp[8 + j + 4] & 0x02) ? "yes" : "no"); + printf(" Token out: %s\n", + (bp[8 + j + 4] & 0x01) ? "yes" : "no"); + printf(" preference: %d\n", + sg_get_unaligned_be16(bp + 8 + j + 6)); } break; case 0x8001: /* Mandatory (SPC-4) */ - printf(" General Copy Operations:\n"); - u = sg_get_unaligned_be32(ucp + 4); - printf(" Total Concurrent Copies: %u\n", u); - u = sg_get_unaligned_be32(ucp + 8); - printf(" Maximum Identified Concurrent Copies: %u\n", u); - u = sg_get_unaligned_be32(ucp + 12); - printf(" Maximum Segment Length: %u\n", u); - ull = (1 << ucp[16]); /* field is power of 2 */ - printf(" Data Segment Granularity: %" PRIu64 "\n", ull); - ull = (1 << ucp[17]); - printf(" Inline Data Granularity: %" PRIu64 "\n", ull); + printf(" General copy operations:\n"); + u = sg_get_unaligned_be32(bp + 4); + printf(" Total concurrent copies: %u\n", u); + u = sg_get_unaligned_be32(bp + 8); + printf(" Maximum identified concurrent copies: %u\n", u); + u = sg_get_unaligned_be32(bp + 12); + printf(" Maximum segment length: %u\n", u); + ull = (1 << bp[16]); /* field is power of 2 */ + printf(" Data segment granularity: %" PRIu64 "\n", ull); + ull = (1 << bp[17]); + printf(" Inline data granularity: %" PRIu64 "\n", ull); break; case 0x9101: - printf(" Stream Copy Operations:\n"); - u = sg_get_unaligned_be32(ucp + 4); - printf(" Maximum Stream Device Transfer Size: %u\n", u); + printf(" Stream copy operations:\n"); + u = sg_get_unaligned_be32(bp + 4); + printf(" Maximum stream device transfer size: %u\n", u); break; case 0xC001: - printf(" Held Data:\n"); - u = sg_get_unaligned_be32(ucp + 4); - printf(" Held Data Limit: %u\n", u); - ull = (1 << ucp[8]); - printf(" Held Data Granularity: %" PRIu64 "\n", ull); + printf(" Held data:\n"); + u = sg_get_unaligned_be32(bp + 4); + printf(" Held data limit: %u\n", u); + ull = (1 << bp[8]); + printf(" Held data granularity: %" PRIu64 "\n", ull); break; default: pr2serr("Unexpected type=%d\n", desc_type); - dStrHexErr((const char *)ucp, bump, 1); + hex2stderr(bp, bump, 1); break; } } @@ -715,10 +851,10 @@ /* VPD_PROTO_LU 0x90 */ static int -decode_proto_lu_vpd(unsigned char * buff, int len) +decode_proto_lu_vpd(uint8_t * buff, int len) { int k, bump, rel_port, desc_len, proto; - unsigned char * ucp; + uint8_t * bp; if (len < 4) { pr2serr("Protocol-specific logical unit information VPD page length " @@ -726,12 +862,12 @@ return SG_LIB_CAT_MALFORMED; } len -= 4; - ucp = buff + 4; - for (k = 0; k < len; k += bump, ucp += bump) { - rel_port = sg_get_unaligned_be16(ucp); + bp = buff + 4; + for (k = 0; k < len; k += bump, bp += bump) { + rel_port = sg_get_unaligned_be16(bp); printf("Relative port=%d\n", rel_port); - proto = ucp[2] & 0xf; - desc_len = sg_get_unaligned_be16(ucp + 6); + proto = bp[2] & 0xf; + desc_len = sg_get_unaligned_be16(bp + 6); bump = 8 + desc_len; if ((k + bump) > len) { pr2serr("Protocol-specific logical unit information VPD page, " @@ -742,11 +878,11 @@ switch (proto) { case TPROTO_SAS: printf(" Protocol identifier: SAS\n"); - printf(" TLR control supported: %d\n", !!(ucp[8] & 0x1)); + printf(" TLR control supported: %d\n", !!(bp[8] & 0x1)); break; default: pr2serr("Unexpected proto=%d\n", proto); - dStrHexErr((const char *)ucp, bump, 1); + hex2stderr(bp, bump, 1); break; } } @@ -756,11 +892,11 @@ /* VPD_PROTO_PORT 0x91 */ static int -decode_proto_port_vpd(unsigned char * buff, int len) +decode_proto_port_vpd(uint8_t * buff, int len) { int k, j, bump, rel_port, desc_len, proto; - unsigned char * ucp; - unsigned char * pidp; + uint8_t * bp; + uint8_t * pidp; if (len < 4) { pr2serr("Protocol-specific port information VPD page length too " @@ -768,12 +904,12 @@ return SG_LIB_CAT_MALFORMED; } len -= 4; - ucp = buff + 4; - for (k = 0; k < len; k += bump, ucp += bump) { - rel_port = sg_get_unaligned_be16(ucp); + bp = buff + 4; + for (k = 0; k < len; k += bump, bp += bump) { + rel_port = sg_get_unaligned_be16(bp); printf("Relative port=%d\n", rel_port); - proto = ucp[2] & 0xf; - desc_len = sg_get_unaligned_be16(ucp + 6); + proto = bp[2] & 0xf; + desc_len = sg_get_unaligned_be16(bp + 6); bump = 8 + desc_len; if ((k + bump) > len) { pr2serr("Protocol-specific port information VPD page, short " @@ -784,15 +920,15 @@ switch (proto) { case TPROTO_SAS: /* page added in spl3r02 */ printf(" power disable supported (pwr_d_s)=%d\n", - !!(ucp[3] & 0x1)); /* added spl3r03 */ - pidp = ucp + 8; + !!(bp[3] & 0x1)); /* added spl3r03 */ + pidp = bp + 8; for (j = 0; j < desc_len; j += 4, pidp += 4) printf(" phy id=%d, SSP persistent capable=%d\n", pidp[1], (0x1 & pidp[2])); break; default: pr2serr("Unexpected proto=%d\n", proto); - dStrHexErr((const char *)ucp, bump, 1); + hex2stderr(bp, bump, 1); break; } } @@ -800,24 +936,62 @@ return 0; } +/* VPD_SCSI_FEATURE_SETS [0x92] (sfs) */ +static int +decode_feature_sets_vpd(uint8_t * buff, int len, + const struct sdparm_opt_coll * op) +{ + int k, bump; + uint16_t sf_code; + bool found; + uint8_t * bp; + char b[64]; + + if (len < 4) { + pr2serr("SCSI Feature sets VPD page length too short=%d\n", len); + return SG_LIB_CAT_MALFORMED; + } + len -= 8; + bp = buff + 8; + for (k = 0; k < len; k += bump, bp += bump) { + sf_code = sg_get_unaligned_be16(bp); + bump = 2; + if ((k + bump) > len) { + pr2serr("SCSI Feature sets, short descriptor length=%d, " + "left=%d\n", bump, (len - k)); + return SG_LIB_CAT_MALFORMED; + } + printf(" %s", sg_get_sfs_str(sf_code, -2, sizeof(b), b, + &found, op->verbose)); + if (op->verbose == 1) + printf(" [0x%x]\n", (unsigned int)sf_code); + else if (op->verbose > 1) + printf(" [0x%x] found=%s\n", (unsigned int)sf_code, + found ? "true" : "false"); + else + printf("\n"); + } + return 0; +} + /* VPD_SCSI_PORTS 0x88 */ static int -decode_scsi_ports_vpd(unsigned char * buff, int len, +decode_scsi_ports_vpd(uint8_t * buff, int len, const struct sdparm_opt_coll * op) { int k, bump, rel_port, ip_tid_len, tpd_len, res; - unsigned char * ucp; + uint8_t * bp; if (len < 4) { pr2serr("SCSI Ports VPD page length too short=%d\n", len); return SG_LIB_CAT_MALFORMED; } len -= 4; - ucp = buff + 4; - for (k = 0; k < len; k += bump, ucp += bump) { - rel_port = sg_get_unaligned_be16(ucp + 2); - printf("Relative port=%d\n", rel_port); - ip_tid_len = sg_get_unaligned_be16(ucp + 6); + bp = buff + 4; + for (k = 0; k < len; k += bump, bp += bump) { + rel_port = sg_get_unaligned_be16(bp + 2); + printf(" Relative port=%d\n", rel_port); + ip_tid_len = sg_get_unaligned_be16(bp + 6); bump = 8 + ip_tid_len; if ((k + bump) > len) { pr2serr("SCSI Ports VPD page, short descriptor length=%d, " @@ -825,25 +999,21 @@ return SG_LIB_CAT_MALFORMED; } if (ip_tid_len > 0) { - /* - * SCSI devices that are both target and initiator are rare. - * Only target devices can receive this command, so if they - * are also initiators then print out the "Initiator port - * transport id" in hex. sg_inq in sg3_utils decodes it. - */ - printf(" Initiator port transport id:\n"); - dStrHex((const char *)(ucp + 8), ip_tid_len, 1); + char b[1024]; + + printf("%s", sg_decode_transportid_str(" ", bp + 8, + ip_tid_len, true, sizeof(b), b)); } - tpd_len = sg_get_unaligned_be16(ucp + bump + 2); + tpd_len = sg_get_unaligned_be16(bp + bump + 2); if ((k + bump + tpd_len + 4) > len) { pr2serr("SCSI Ports VPD page, short descriptor(tgt) length=%d, " "left=%d\n", bump, (len - k)); return SG_LIB_CAT_MALFORMED; } if (tpd_len > 0) { - res = decode_dev_ids(" Target port descriptor(s)", - ucp + bump + 4, tpd_len, VPD_ASSOC_TPORT, - -1, -1, op); + printf(" Target port descriptor(s):\n"); + res = decode_dev_ids("", 2, bp + bump + 4, tpd_len, + VPD_ASSOC_TPORT, -1, -1, op); if (res) return res; } @@ -854,7 +1024,7 @@ /* VPD_EXT_INQ Extended Inquiry page 0x86 */ static int -decode_ext_inq_vpd(unsigned char * b, int len, int do_long, int protect) +decode_ext_inq_vpd(uint8_t * b, int len, int do_long, bool protect) { int n; @@ -920,9 +1090,10 @@ printf(" CRD_SUP=%d\n", !!(b[6] & 0x4)); printf(" NV_SUP=%d\n", !!(b[6] & 0x2)); printf(" V_SUP=%d\n", !!(b[6] & 0x1)); - printf(" NO_PI_CHK=%d\n", !!(b[7] & 0x10)); /* spc5r02 */ + printf(" NO_PI_CHK=%d\n", !!(b[7] & 0x20)); /* spc5r02 */ printf(" P_I_I_SUP=%d\n", !!(b[7] & 0x10)); printf(" LUICLR=%d\n", !!(b[7] & 0x1)); + printf(" LU_COLL_TYPE=%d\n", (b[8] >> 5) & 0x7); /* spc5r09 */ printf(" R_SUP=%d\n", !!(b[8] & 0x10)); printf(" HSSRELEF=%d\n", !!(b[8] & 0x2)); /* spc5r02 */ printf(" CBCS=%d\n", !!(b[8] & 0x1)); /* obsolete in spc5r01 */ @@ -932,8 +1103,25 @@ printf(" POA_SUP=%d\n", !!(b[12] & 0x80)); /* spc4r32 */ printf(" HRA_SUP=%d\n", !!(b[12] & 0x40)); /* spc4r32 */ printf(" VSA_SUP=%d\n", !!(b[12] & 0x20)); /* spc4r32 */ + printf(" DMS_VALID=%d\n", !!(b[12] & 0x10)); /* spc5r20 */ printf(" Maximum supported sense data length=%d\n", b[13]); /* spc4r34 */ + printf(" IBS=%d\n", !!(b[14] & 0x80)); /* spc5r09 */ + printf(" IAS=%d\n", !!(b[14] & 0x40)); /* spc5r09 */ + printf(" SAC=%d\n", !!(b[14] & 0x4)); /* spc5r09 */ + printf(" NRD1=%d\n", !!(b[14] & 0x2)); /* spc5r09 */ + printf(" NRD0=%d\n", !!(b[14] & 0x1)); /* spc5r09 */ + printf(" Maximum inquiry change logs=%u\n", + sg_get_unaligned_be16(b + 15)); /* spc5r17 */ + printf(" Maximum mode page change logs=%u\n", + sg_get_unaligned_be16(b + 17)); /* spc5r17 */ + printf(" DM_MD_4=%d\n", !!(b[19] & 0x80)); /* spc5r20 */ + printf(" DM_MD_5=%d\n", !!(b[19] & 0x40)); /* spc5r20 */ + printf(" DM_MD_6=%d\n", !!(b[19] & 0x20)); /* spc5r20 */ + printf(" DM_MD_7=%d\n", !!(b[19] & 0x10)); /* spc5r20 */ + printf(" DM_MD_D=%d\n", !!(b[19] & 0x8)); /* spc5r20 */ + printf(" DM_MD_E=%d\n", !!(b[19] & 0x4)); /* spc5r20 */ + printf(" DM_MD_F=%d\n", !!(b[19] & 0x2)); /* spc5r20 */ } else { printf(" ACTIVATE_MICROCODE=%d SPT=%d GRD_CHK=%d APP_CHK=%d " "REF_CHK=%d\n", ((b[4] >> 6) & 0x3), ((b[4] >> 3) & 0x7), @@ -946,27 +1134,42 @@ printf(" WU_SUP=%d [CRD_SUP=%d] NV_SUP=%d V_SUP=%d\n", !!(b[6] & 0x8), !!(b[6] & 0x4), !!(b[6] & 0x2), !!(b[6] & 0x1)); - /* CBCS, capability-based command security, obsolete in spc5r01 */ - printf(" P_I_I_SUP=%d LUICLR=%d R_SUP=%d CBCS=%d\n", - !!(b[7] & 0x10), !!(b[7] & 0x1), - !!(b[8] & 0x10), !!(b[8] & 0x1)); + printf(" NO_PI_CHK=%d P_I_I_SUP=%d LUICLR=%d\n", !!(b[7] & 0x20), + !!(b[7] & 0x10), !!(b[7] & 0x1)); + /* LU_COLL_TYPE in spc5r09, CBCS obsolete in spc5r01 */ + printf(" LU_COLL_TYPE=%d R_SUP=%d HSSRELEF=%d [CBCS=%d]\n", + (b[8] >> 5) & 0x7, !!(b[8] & 0x10), !!(b[8] & 0x2), + !!(b[8] & 0x1)); printf(" Multi I_T nexus microcode download=%d\n", b[9] & 0xf); printf(" Extended self-test completion minutes=%d\n", sg_get_unaligned_be16(b + 10)); - printf(" POA_SUP=%d HRA_SUP=%d VSA_SUP=%d\n", /* spc4r32 */ - !!(b[12] & 0x80), !!(b[12] & 0x40), !!(b[12] & 0x20)); + printf(" POA_SUP=%d HRA_SUP=%d VSA_SUP=%d DMS_VALID=%d\n", + !!(b[12] & 0x80), !!(b[12] & 0x40), !!(b[12] & 0x20), + !!(b[12] & 0x10)); /* spc5r20 */ printf(" Maximum supported sense data length=%d\n", b[13]); /* spc4r34 */ + printf(" IBS=%d IAS=%d SAC=%d NRD1=%d NRD0=%d\n", !!(b[14] & 0x80), + !!(b[14] & 0x40), !!(b[14] & 0x4), !!(b[14] & 0x2), + !!(b[14] & 0x1)); /* added in spc5r09 */ + printf(" Maximum inquiry change logs=%u\n", + sg_get_unaligned_be16(b + 15)); /* spc5r17 */ + printf(" Maximum mode page change logs=%u\n", + sg_get_unaligned_be16(b + 17)); /* spc5r17 */ + printf(" DM_MD_4=%d DM_MD_5=%d DM_MD_6=%d DM_MD_7=%d\n", + !!(b[19] & 0x80), !!(b[19] & 0x40), !!(b[19] & 0x20), + !!(b[19] & 0x10)); /* spc5r20 */ + printf(" DM_MD_D=%d DM_MD_E=%d DM_MD_F=%d\n", + !!(b[19] & 0x8), !!(b[19] & 0x4), !!(b[19] & 0x2)); } return 0; } /* VPD_ATA_INFO 0x89 */ static int -decode_ata_info_vpd(unsigned char * buff, int len, int do_long, int do_hex) +decode_ata_info_vpd(uint8_t * buff, int len, int do_long, int do_hex) { char b[80]; - int num, is_be; + int num, is_be, cc; const char * cp; const char * ata_transp; @@ -988,14 +1191,22 @@ ata_transp = (0x34 == buff[36]) ? "SATA" : "PATA"; if (do_long) { printf(" Device signature [%s] (in hex):\n", ata_transp); - dStrHex((const char *)buff + 36, 20, 1); + hex2stdout(buff + 36, 20, 1); } else printf(" Device signature indicates %s transport\n", ata_transp); + cc = buff[56]; /* 0xec for IDENTIFY DEVICE and 0xa1 for IDENTIFY + * PACKET DEVICE (obsolete) */ + printf(" Command code: 0x%x\n", cc); if (len < 60) return SG_LIB_CAT_MALFORMED; + if (0xec == cc) + cp = ""; + else if (0xa1 == cc) + cp = "PACKET "; + else + cp = NULL; is_be = sg_is_big_endian(); - if ((0xec == buff[56]) || (0xa1 == buff[56])) { - cp = (0xa1 == buff[56]) ? "PACKET " : ""; + if (cp) { printf(" ATA command IDENTIFY %sDEVICE response summary:\n", cp); num = sg_ata_get_chars((const unsigned short *)(buff + 60), 27, 20, is_be, b); @@ -1017,7 +1228,7 @@ if (len < 572) return SG_LIB_CAT_MALFORMED; if (do_hex) - dStrHex((const char *)(buff + 60), 512, 0); + hex2stdout(buff + 60, 512, 0); else if (do_long) dWordHex((const unsigned short *)(buff + 60), 256, 0, is_be); return 0; @@ -1025,7 +1236,7 @@ /* VPD_POWER_CONDITION 0x8a */ static int -decode_power_condition(unsigned char * buff, int len) +decode_power_condition(uint8_t * buff, int len) { if (len < 18) { pr2serr("Power condition VPD page length too short=%d\n", len); @@ -1063,10 +1274,10 @@ /* VPD_POWER_CONSUMPTION 0x8d */ static int -decode_power_consumption_vpd(unsigned char * buff, int len) +decode_power_consumption_vpd(uint8_t * buff, int len) { int k, bump; - unsigned char * ucp; + uint8_t * bp; unsigned int value; if (len < 4) { @@ -1074,91 +1285,154 @@ return SG_LIB_CAT_MALFORMED; } len -= 4; - ucp = buff + 4; - for (k = 0; k < len; k += bump, ucp += bump) { + bp = buff + 4; + for (k = 0; k < len; k += bump, bp += bump) { bump = 4; if ((k + bump) > len) { pr2serr("Power consumption VPD page, short descriptor length=%d, " "left=%d\n", bump, (len - k)); return SG_LIB_CAT_MALFORMED; } - value = sg_get_unaligned_be16(ucp + 2); - printf(" Power consumption identifier: 0x%x", ucp[0]); - if (value >= 1000 && (ucp[1] & 0x7) > 0) + value = sg_get_unaligned_be16(bp + 2); + printf(" Power consumption identifier: 0x%x", bp[0]); + if (value >= 1000 && (bp[1] & 0x7) > 0) printf(" Maximum power consumption: %d.%03d %s\n", value / 1000, value % 1000, - power_unit_arr[(ucp[1] & 0x7) - 1]); + power_unit_arr[(bp[1] & 0x7) - 1]); else printf(" Maximum power consumption: %d %s\n", - value, power_unit_arr[ucp[1] & 0x7]); + value, power_unit_arr[bp[1] & 0x7]); } return 0; } /* VPD_BLOCK_LIMITS 0xb0 */ static int -decode_block_limits_vpd(unsigned char * buff, int len) +decode_block_limits_vpd(uint8_t * buff, int len) { + bool ugavalid; unsigned int u; - unsigned char b[4]; + uint64_t ull; if (len < 16) { pr2serr("Block limits VPD page length too short=%d\n", len); return SG_LIB_CAT_MALFORMED; } printf(" Write same non-zero (WSNZ): %d\n", !!(buff[4] & 0x1)); - printf(" Maximum compare and write length: %u blocks\n", buff[5]); + u = buff[5]; + printf(" Maximum compare and write length: "); + if (0 == u) + printf("0 blocks [Command not implemented]\n"); + else + printf("%u blocks\n", buff[5]); u = sg_get_unaligned_be16(buff + 6); - printf(" Optimal transfer length granularity: %u blocks\n", u); + printf(" Optimal transfer length granularity: "); + if (0 == u) + printf("0 blocks [not reported]\n"); + else + printf("%u blocks\n", u); u = sg_get_unaligned_be32(buff + 8); - printf(" Maximum transfer length: %u blocks\n", u); + printf(" Maximum transfer length: "); + if (0 == u) + printf("0 blocks [not reported]\n"); + else + printf("%u blocks\n", u); u = sg_get_unaligned_be32(buff + 12); - printf(" Optimal transfer length: %u blocks\n", u); + printf(" Optimal transfer length: "); + if (0 == u) + printf("0 blocks [not reported]\n"); + else + printf("%u blocks\n", u); if (len > 19) { /* added in sbc3r09 */ u = sg_get_unaligned_be32(buff + 16); - printf(" Maximum prefetch length: %u blocks\n", u); - /* was 'Maximum prefetch transfer length' prior to sbc3r33 */ + printf(" Maximum prefetch transfer length: "); + if (0 == u) + printf("0 blocks [ignored]\n"); + else + printf("%u blocks\n", u); } if (len > 27) { /* added in sbc3r18 */ - u = sg_get_unaligned_be32(buff + 29); - printf(" Maximum unmap LBA count: %u\n", u); + u = sg_get_unaligned_be32(buff + 20); + printf(" Maximum unmap LBA count: "); + if (0 == u) + printf("0 [Unmap command not implemented]\n"); + else if (SG_LIB_UNBOUNDED_32BIT == u) + printf("-1 [unbounded]\n"); + else + printf("%u\n", u); u = sg_get_unaligned_be32(buff + 24); - printf(" Maximum unmap block descriptor count: %u\n", u); + printf(" Maximum unmap block descriptor count: "); + if (0 == u) + printf("0 [Unmap command not implemented]\n"); + else if (SG_LIB_UNBOUNDED_32BIT == u) + printf("-1 [unbounded]\n"); + else + printf("%u\n", u); } if (len > 35) { /* added in sbc3r19 */ u = sg_get_unaligned_be32(buff + 28); - printf(" Optimal unmap granularity: %u\n", u); - printf(" Unmap granularity alignment valid: %u\n", - !!(buff[32] & 0x80)); - memcpy(b, buff + 32, 4); - b[0] &= 0x7f; /* mask off top bit */ - u = sg_get_unaligned_be32(b); - printf(" Unmap granularity alignment: %u\n", u); - } - if (len > 43) /* added in sbc3r26 */ - printf(" Maximum write same length: 0x%" PRIx64 " blocks\n", - sg_get_unaligned_be64(buff + 36)); + printf(" Optimal unmap granularity: "); + if (0 == u) + printf("0 blocks [not reported]\n"); + else + printf("%u blocks\n", u); + + ugavalid = !!(buff[32] & 0x80); + printf(" Unmap granularity alignment valid: %s\n", + ugavalid ? "true" : "false"); + u = 0x7fffffff & sg_get_unaligned_be32(buff + 32); + printf(" Unmap granularity alignment: %u%s\n", u, + ugavalid ? "" : " [invalid]"); + } + if (len > 43) { /* added in sbc3r26 */ + ull = sg_get_unaligned_be64(buff + 36); + printf(" Maximum write same length: "); + if (0 == ull) + printf("0 blocks [not reported]\n"); + else + printf("0x%" PRIx64 " blocks\n", ull); + } if (len > 44) { /* added in sbc4r02 */ u = sg_get_unaligned_be32(buff + 44); - printf(" Maximum atomic transfer length: %u\n", u); + printf(" Maximum atomic transfer length: "); + if (0 == u) + printf("0 blocks [not reported]\n"); + else + printf("%u blocks\n", u); u = sg_get_unaligned_be32(buff + 48); - printf(" Atomic alignment: %u\n", u); + printf(" Atomic alignment: "); + if (0 == u) + printf("0 [unaligned atomic writes permitted]\n"); + else + printf("%u\n", u); u = sg_get_unaligned_be32(buff + 52); - printf(" Atomic transfer length granularity: %u\n", u); + printf(" Atomic transfer length granularity: "); + if (0 == u) + printf("0 [no granularity requirement\n"); + else + printf("%u\n", u); } - if (len > 56) { /* added in sbc4r04 */ + if (len > 56) { u = sg_get_unaligned_be32(buff + 56); - printf(" Maximum atomic transfer length with atomic boundary: %u\n", - u); + printf(" Maximum atomic transfer length with atomic " + "boundary: "); + if (0 == u) + printf("0 blocks [not reported]\n"); + else + printf("%u blocks\n", u); u = sg_get_unaligned_be32(buff + 60); - printf(" Maximum atomic boundary size: %u\n", u); + printf(" Maximum atomic boundary size: "); + if (0 == u) + printf("0 blocks [can only write atomic 1 block]\n"); + else + printf("%u blocks\n", u); } return 0; } /* VPD_BLOCK_LIMITS_EXT 0xb7 */ static int -decode_block_limits_ext_vpd(unsigned char * buff, int len) +decode_block_limits_ext_vpd(uint8_t * buff, int len) { unsigned int u; @@ -1167,11 +1441,35 @@ return SG_LIB_CAT_MALFORMED; } u = sg_get_unaligned_be16(buff + 6); - printf(" Maximum number of streams: %u\n", u); + printf(" Maximum number of streams: "); + if (0 == u) + printf("0 [Stream control not supported]\n"); + else + printf("%u\n", u); u = sg_get_unaligned_be16(buff + 8); - printf(" Optimal stream write size: %u logical blocks\n", u); + printf(" Optimal stream write size: %u blocks\n", u); u = sg_get_unaligned_be32(buff + 10); printf(" Stream granularity size: %u\n", u); + if (len > 27) { + u = sg_get_unaligned_be32(buff + 16); + printf(" Maximum scattered LBA range transfer length: "); + if (0 == u) + printf("0 blocks [not reported]\n"); + else + printf("%u blocks\n", u); + u = sg_get_unaligned_be16(buff + 22); + printf(" Maximum scattered LBA range descriptor count: "); + if (0 == u) + printf("0 [not reported]\n"); + else + printf("%u\n", u); + u = sg_get_unaligned_be32(buff + 24); + printf(" Maximum scattered transfer length: "); + if (0 == u) + printf("0 blocks [not reported]\n"); + else + printf("%u blocks\n", u); + } return 0; } @@ -1187,14 +1485,22 @@ "Universal Flash Storage Card (UFS)", }; +static const char * zoned_strs[] = { + "", + " [host-aware]", + " [host-managed]", + "", +}; + /* VPD_BLOCK_DEV_CHARS 0xb1 */ static int -decode_block_dev_chars_vpd(unsigned char * buff, int len) +decode_block_dev_chars_vpd(uint8_t * buff, int len) { + int zoned; unsigned int u, k; if (len < 64) { - pr2serr("Block device capabilities VPD page length too short=%d\n", + pr2serr("Block device characteristics VPD page length too short=%d\n", len); return SG_LIB_CAT_MALFORMED; } @@ -1207,14 +1513,15 @@ printf(" Reserved [0x%x]\n", u); else printf(" Nominal rotation rate: %d rpm\n", u); - printf(" Product type=%d\n", buff[6]); - k = sizeof(product_type_arr) / sizeof(product_type_arr[0]); + u = buff[6]; + printf(" Product type: "); + k = SG_ARRAY_SIZE(product_type_arr); if (u < k) - printf(" Product type: %s\n", product_type_arr[u]); + printf("%s\n", product_type_arr[u]); else if (u < 0xf0) - printf(" Product type: Reserved [0x%x]\n", u); + printf("Reserved [0x%x]\n", u); else - printf(" Product type: Vendor specific [0x%x]\n", u); + printf("Vendor specific [0x%x]\n", u); u = buff[7] & 0xf; printf(" WABEREQ=%d\n", (buff[7] >> 6) & 0x3); printf(" WACEREQ=%d\n", (buff[7] >> 4) & 0x3); @@ -1242,16 +1549,19 @@ printf(": reserved\n"); break; } - printf(" ZONED=%d\n", (buff[8] >> 4) & 0x3); /* sbc4r04 */ + zoned = (buff[8] >> 4) & 0x3; /* added sbc4r04 */ + printf(" ZONED=%d%s\n", zoned, zoned_strs[zoned]); printf(" BOCS=%d\n", !!(buff[8] & 0x4)); printf(" FUAB=%d\n", !!(buff[8] & 0x2)); printf(" VBULS=%d\n", !!(buff[8] & 0x1)); + printf(" DEPOPULATION_TIME=%u (seconds)\n", + sg_get_unaligned_be32(buff + 12)); /* added sbc4r14 */ return 0; } /* VPD_SA_DEV_CAP 0xb0 */ static int -decode_tape_dev_caps_vpd(unsigned char * buff, int len) +decode_tape_dev_caps_vpd(uint8_t * buff, int len) { if (len < 6) { pr2serr("Sequential access device capabilities VPD page length too " @@ -1264,7 +1574,7 @@ /* VPD_MAN_ASS_SN 0xb1 */ static int -decode_tape_man_ass_sn_vpd(unsigned char * buff, int len) +decode_tape_man_ass_sn_vpd(uint8_t * buff, int len) { if (len < 4) { pr2serr("Manufacturer-assigned serial number VPD page length too " @@ -1276,94 +1586,102 @@ return 0; } +static const char * prov_type_arr[8] = { + "not known or fully provisioned", + "resource provisioned", + "thin provisioned", + "reserved [0x3]", + "reserved [0x4]", + "reserved [0x5]", + "reserved [0x6]", + "reserved [0x7]", +}; + /* VPD_LB_PROVISIONING 0xb2 */ static int -decode_block_lb_prov_vpd(unsigned char * b, int len, +decode_block_lb_prov_vpd(uint8_t * b, int len, const struct sdparm_opt_coll * op) { - int dp; + int dp, pt; + unsigned int u; if (len < 4) { pr2serr("Logical block provisioning page too short=%d\n", len); return SG_LIB_CAT_MALFORMED; } + pt = b[6] & 0x7; printf(" Unmap command supported (LBPU): %d\n", !!(0x80 & b[5])); - printf(" Write same (16) with unmap bit supported (LBWS): %d\n", + printf(" Write same (16) with unmap bit supported (LBPWS): %d\n", !!(0x40 & b[5])); - printf(" Write same (10) with unmap bit supported (LBWS10): %d\n", + printf(" Write same (10) with unmap bit supported (LBPWS10): %d\n", !!(0x20 & b[5])); printf(" Logical block provisioning read zeros (LBPRZ): %d\n", - (0x7 & (b[5] >> 2))); + (0x7 & (b[5] >> 2))); /* expanded from 1 to 3 bits in sbc4r07 */ printf(" Anchored LBAs supported (ANC_SUP): %d\n", !!(0x2 & b[5])); - printf(" Threshold exponent: %d\n", b[4]); + u = b[4]; + printf(" Threshold exponent: "); + if (0 == u) + printf("0 [threshold sets not supported]\n"); + else + printf("%u\n", u); dp = !!(b[5] & 0x1); printf(" Descriptor present: %d\n", dp); - printf(" Minimum percentage: %d\n", 0x1f & (b[6] >> 3)); - printf(" Provisioning type: %d\n", b[6] & 0x7); - printf(" Threshold percentage: %d\n", b[7]); + printf(" Minimum percentage: "); + u = 0x1f & (b[6] >> 3); + if (0 == u) + printf("0 [not reported]\n"); + else + printf("%d\n", u); + printf(" Provisioning type: %d (%s)\n", pt, prov_type_arr[pt]); + printf(" Threshold percentage: "); + if (0 == b[7]) + printf("0 [percentages not supported]\n"); + else + printf("%u\n", b[7]); if (dp) { - const unsigned char * ucp; + const uint8_t * bp; int i_len; - ucp = b + 8; - i_len = ucp[3]; + bp = b + 8; + i_len = bp[3]; if (0 == i_len) { pr2serr("Logical block provisioning page provisioning group " "descriptor too short=%d\n", i_len); return 0; } printf(" Provisioning group descriptor\n"); - decode_designation_descriptor(ucp, i_len, 1, op); + decode_designation_descriptor(bp, i_len, true, op); } return 0; } /* VPD_TA_SUPPORTED 0xb2 */ static int -decode_tapealert_supported_vpd(unsigned char * b, int len) +decode_tapealert_supported_vpd(uint8_t * b, int len) { + int k, mod, div; + if (len < 12) { pr2serr("TapeAlert supported flags length too short=%d\n", len); return SG_LIB_CAT_MALFORMED; } - printf(" Flag01h: %d 02h: %d 03h: %d 04h: %d 05h: %d 06h: %d " - "07h: %d 08h: %d\n", !!(b[4] & 0x80), !!(b[4] & 0x40), - !!(b[4] & 0x20), !!(b[4] & 0x10), !!(b[4] & 0x8), !!(b[4] & 0x4), - !!(b[4] & 0x2), !!(b[4] & 0x1)); - printf(" Flag09h: %d 0ah: %d 0bh: %d 0ch: %d 0dh: %d 0eh: %d " - "0fh: %d 10h: %d\n", !!(b[5] & 0x80), !!(b[5] & 0x40), - !!(b[5] & 0x20), !!(b[5] & 0x10), !!(b[5] & 0x8), !!(b[5] & 0x4), - !!(b[5] & 0x2), !!(b[5] & 0x1)); - printf(" Flag11h: %d 12h: %d 13h: %d 14h: %d 15h: %d 16h: %d " - "17h: %d 18h: %d\n", !!(b[6] & 0x80), !!(b[6] & 0x40), - !!(b[6] & 0x20), !!(b[6] & 0x10), !!(b[6] & 0x8), !!(b[6] & 0x4), - !!(b[6] & 0x2), !!(b[6] & 0x1)); - printf(" Flag19h: %d 1ah: %d 1bh: %d 1ch: %d 1dh: %d 1eh: %d " - "1fh: %d 20h: %d\n", !!(b[7] & 0x80), !!(b[7] & 0x40), - !!(b[7] & 0x20), !!(b[7] & 0x10), !!(b[7] & 0x8), !!(b[7] & 0x4), - !!(b[7] & 0x2), !!(b[7] & 0x1)); - printf(" Flag21h: %d 22h: %d 23h: %d 24h: %d 25h: %d 26h: %d " - "27h: %d 28h: %d\n", !!(b[8] & 0x80), !!(b[8] & 0x40), - !!(b[8] & 0x20), !!(b[8] & 0x10), !!(b[8] & 0x8), !!(b[8] & 0x4), - !!(b[8] & 0x2), !!(b[8] & 0x1)); - printf(" Flag29h: %d 2ah: %d 2bh: %d 2ch: %d 2dh: %d 2eh: %d " - "2fh: %d 30h: %d\n", !!(b[9] & 0x80), !!(b[9] & 0x40), - !!(b[9] & 0x20), !!(b[9] & 0x10), !!(b[9] & 0x8), !!(b[9] & 0x4), - !!(b[9] & 0x2), !!(b[9] & 0x1)); - printf(" Flag31h: %d 32h: %d 33h: %d 34h: %d 35h: %d 36h: %d " - "37h: %d 38h: %d\n", !!(b[10] & 0x80), !!(b[10] & 0x40), - !!(b[10] & 0x20), !!(b[10] & 0x10), !!(b[10] & 0x8), - !!(b[10] & 0x4), !!(b[10] & 0x2), !!(b[10] & 0x1)); - printf(" Flag39h: %d 3ah: %d 3bh: %d 3ch: %d 3dh: %d 3eh: %d " - "3fh: %d 40h: %d\n", !!(b[11] & 0x80), !!(b[11] & 0x40), - !!(b[11] & 0x20), !!(b[11] & 0x10), !!(b[11] & 0x8), - !!(b[11] & 0x4), !!(b[11] & 0x2), !!(b[11] & 0x1)); + for (k = 1; k < 0x41; ++k) { + mod = ((k - 1) % 8); + div = (k - 1) / 8; + if (0 == mod) { + if (div > 0) + printf("\n"); + printf(" Flag%02Xh: %d", k, !! (b[4 + div] & 0x80)); + } else + printf(" %02Xh: %d", k, !! (b[4 + div] & (1 << (7 - mod)))); + } + printf("\n"); return 0; } /* VPD_REFERRALS 0xb3 */ static int -decode_referrals_vpd(unsigned char * b, int len) +decode_referrals_vpd(uint8_t * b, int len) { unsigned int u; @@ -1372,7 +1690,11 @@ return SG_LIB_CAT_MALFORMED; } u = sg_get_unaligned_be32(b + 8); - printf(" User data segment size: %u\n", u); + printf(" User data segment size: "); + if (0 == u) + printf("0 [per sense descriptor]\n"); + else + printf("%u\n", u); u = sg_get_unaligned_be32(b + 12); printf(" User data segment multiplier: %u\n", u); return 0; @@ -1380,11 +1702,11 @@ /* VPD_SUP_BLOCK_LENS 0xb4 [added sbc4r01] */ static int -decode_sup_block_lens_vpd(unsigned char * buff, int len) +decode_sup_block_lens_vpd(uint8_t * buff, int len) { int k; unsigned int u; - unsigned char * ucp; + uint8_t * bp; if (len < 4) { pr2serr("Supported block lengths and protection types VPD page " @@ -1392,26 +1714,26 @@ return SG_LIB_CAT_MALFORMED; } len -= 4; - ucp = buff + 4; - for (k = 0; k < len; k += 8, ucp += 8) { - u = sg_get_unaligned_be32(ucp + 0); + bp = buff + 4; + for (k = 0; k < len; k += 8, bp += 8) { + u = sg_get_unaligned_be32(bp + 0); printf(" Logical block length: %u\n", u); - printf(" P_I_I_SUP: %d\n", !!(ucp[4] & 0x40)); - printf(" NO_PI_CHK: %d\n", !!(ucp[4] & 0x8)); /* sbc4r05 */ - printf(" GRD_CHK: %d\n", !!(ucp[4] & 0x4)); - printf(" APP_CHK: %d\n", !!(ucp[4] & 0x2)); - printf(" REF_CHK: %d\n", !!(ucp[4] & 0x1)); - printf(" T3PS_SUP: %d\n", !!(ucp[5] & 0x8)); - printf(" T2PS_SUP: %d\n", !!(ucp[5] & 0x4)); - printf(" T1PS_SUP: %d\n", !!(ucp[5] & 0x2)); - printf(" T0PS_SUP: %d\n", !!(ucp[5] & 0x1)); + printf(" P_I_I_SUP: %d\n", !!(bp[4] & 0x40)); + printf(" NO_PI_CHK: %d\n", !!(bp[4] & 0x8)); /* sbc4r05 */ + printf(" GRD_CHK: %d\n", !!(bp[4] & 0x4)); + printf(" APP_CHK: %d\n", !!(bp[4] & 0x2)); + printf(" REF_CHK: %d\n", !!(bp[4] & 0x1)); + printf(" T3PS: %d\n", !!(bp[5] & 0x8)); + printf(" T2PS: %d\n", !!(bp[5] & 0x4)); + printf(" T1PS: %d\n", !!(bp[5] & 0x2)); + printf(" T0PS: %d\n", !!(bp[5] & 0x1)); } return 0; } /* VPD_BLOCK_DEV_C_EXTENS 0xb5 [added sbc4r02] */ static int -decode_block_dev_char_ext_vpd(unsigned char * b, int len) +decode_block_dev_char_ext_vpd(uint8_t * b, int len) { if (len < 16) { pr2serr("Block device characteristics extension VPD page " @@ -1476,10 +1798,10 @@ /* VPD_LB_PROTECTION_ADDR 0xb5 [added ssc5r02a] */ static int -decode_lb_protection_vpd(unsigned char * buff, int len) +decode_lb_protection_vpd(uint8_t * buff, int len) { int k, bump; - unsigned char * ucp; + uint8_t * bp; if (len < 8) { pr2serr("Logical block protection VPD page length too short=%d\n", @@ -1487,12 +1809,12 @@ return SG_LIB_CAT_MALFORMED; } len -= 8; - ucp = buff + 8; - for (k = 0; k < len; k += bump, ucp += bump) { - bump = 1 + ucp[0]; + bp = buff + 8; + for (k = 0; k < len; k += bump, bp += bump) { + bump = 1 + bp[0]; printf(" method: %d, info_len: %d, LBP_W_C=%d, LBP_R_C=%d, " - "RBDP_C=%d\n", ucp[1], 0x3f & ucp[2], !!(0x80 & ucp[3]), - !!(0x40 & ucp[3]), !!(0x20 & ucp[3])); + "RBDP_C=%d\n", bp[1], 0x3f & bp[2], !!(0x80 & bp[3]), + !!(0x40 & bp[3]), !!(0x20 & bp[3])); if ((k + bump) > len) { pr2serr("Logical block protection VPD page, short descriptor " "length=%d, left=%d\n", bump, (len - k)); @@ -1502,9 +1824,77 @@ return 0; } +/* VPD_FORMAT_PRESETS 0xb8 (added sbc4r18) */ +static int +decode_format_presets_vpd(uint8_t * buff, int len) +{ + int k; + unsigned int sch_type; + uint8_t * bp; + + len -= 4; + bp = buff + 4; + for (k = 0; k < len; k += 64, bp += 64) { + printf(" Preset identifier: 0x%x\n", sg_get_unaligned_be32(bp)); + sch_type = bp[4]; + printf(" schema type: %u\n", sch_type); + printf(" logical blocks per physical block exponent type: %u\n", + 0xf & bp[7]); + printf(" logical block length: %u\n", + sg_get_unaligned_be32(bp + 8)); + printf(" designed last LBA: 0x%" PRIx64 "\n", + sg_get_unaligned_be64(bp + 16)); + printf(" FMPT_INFO: %u\n", (bp[38] >> 6) & 0x3); + printf(" protection field usage: %u\n", bp[38] & 0x7); + printf(" protection interval exponent: %u\n", bp[39] & 0xf); + if (2 == sch_type) + printf(" Defines zones for host aware device:\n"); + else if (3 == sch_type) + printf(" Defines zones for host managed device:\n"); + if ((2 == sch_type) || (3 == sch_type)) { + unsigned int u = bp[40 + 0]; + + printf(" low LBA conventional zones percentage: " + "%u.%u %%\n", u / 10, u % 10); + u = bp[40 + 1]; + printf(" high LBA conventional zones percentage: " + "%u.%u %%\n", u / 10, u % 10); + printf(" logical blocks per zone: %u\n", + sg_get_unaligned_be32(bp + 40 + 12)); + } + } + return 0; +} + +/* VPD_CON_POS_RANGE 0xb9 (added 20-089r2) */ +static int +decode_con_pos_range_vpd(uint8_t * buff, int len) +{ + int k; + uint64_t u; + uint8_t * bp; + + if (len < 64) { + pr2serr("Concurrent position ranges VPD page length too short=%d\n", + len); + return SG_LIB_CAT_MALFORMED; + } + len -= 64; + bp = buff + 64; + for (k = 0; k < len; k += 32, bp += 32) { + printf(" LBA range number: %u\n", bp[0]); + printf(" number of storage elements: %u\n", bp[1]); + printf(" starting LBA: 0x%" PRIx64 "\n", + sg_get_unaligned_be64(bp + 8)); + u = sg_get_unaligned_be64(bp + 16); + printf(" number of LBAs: 0x%" PRIx64 " [%" PRIu64 "]\n", u, u); + } + return 0; +} + /* VPD_ZBC_DEV_CHARS sbc or zbc */ static int -decode_zbdc_vpd(unsigned char * b, int len) +decode_zbdc_vpd(uint8_t * b, int len) { uint32_t u; @@ -1513,39 +1903,84 @@ "short=%d\n", len); return SG_LIB_CAT_MALFORMED; } + printf(" Zoned block device extension: "); + switch ((b[4] >> 4) & 0xf) { + case 0: + printf("not reported\n"); + break; + case 1: + printf("host aware zone block device model\n"); + break; + case 2: + printf("Domains and realms zone block device model\n"); + break; + default: + printf("Unknown [0x%x]\n", (b[4] >> 4) & 0xf); + break; + } + printf(" AAORB: %d\n", !!(b[4] & 0x2)); + /* URSWRZ: unrestricted read in sequential write required zone */ printf(" URSWRZ type: %d\n", !!(b[4] & 0x1)); u = sg_get_unaligned_be32(b + 8); printf(" Optimal number of open sequential write preferred zones: "); - if (0xffffffff == u) + if (SG_LIB_UNBOUNDED_32BIT == u) printf("not reported\n"); else printf("%" PRIu32 "\n", u); u = sg_get_unaligned_be32(b + 12); printf(" Optimal number of non-sequentially written sequential write " "preferred zones: "); - if (0xffffffff == u) + if (SG_LIB_UNBOUNDED_32BIT == u) printf("not reported\n"); else printf("%" PRIu32 "\n", u); u = sg_get_unaligned_be32(b + 16); printf(" Maximum number of open sequential write required zones: "); - if (0xffffffff == u) + if (SG_LIB_UNBOUNDED_32BIT == u) printf("no limit\n"); else printf("%" PRIu32 "\n", u); return 0; } +/* Called from end of 'case VPD_SUPPORTED_VPDS:' in sdp_process_vpd_page(). + * Must take care not to call that function to decode the VPD_SUPPORTED_VPDS + * page again. That will cause an infinite loop! + * Called when '--inquiry --all --all' or '-iaa' used on command line. */ +static int +decode_all_vpds(uint8_t * b, int len, int sg_fd, + const struct sdparm_opt_coll * op, int req_pdt, + bool protect) +{ + int k, ret; + uint8_t bb[256]; + + if (len > 256) + len = 256; + memcpy(bb, b, ((len < 256) ? len : 256)); + + for (k = 0; k < len; ++k) { + if (VPD_SUPPORTED_VPDS == bb[k]) + continue; /* this avoids infinite loop */ + printf("\n"); + ret = sdp_process_vpd_page(sg_fd, bb[k], 0, op, req_pdt, protect, + NULL, 0, NULL, 0); + if (ret) + return ret; + } + return 0; +} + /* Assume index is less than 16 */ const char * sg_ansi_version_arr[] = { "no conformance claimed", "SCSI-1", /* obsolete, ANSI X3.131-1986 */ "SCSI-2", /* obsolete, ANSI X3.131-1994 */ - "SPC", /* withdrawn */ - "SPC-2", - "SPC-3", - "SPC-4", + "SPC", /* withdrawn, ANSI INCITS 301-1997 */ + "SPC-2", /* ANSI INCITS 351-2001, ISO/IEC 14776-452 */ + "SPC-3", /* ANSI INCITS 408-2005, ISO/IEC 14776-453 */ + "SPC-4", /* ANSI INCITS 513-2015 */ "SPC-5", "ecma=1, [8h]", "ecma=1, [9h]", @@ -1562,38 +1997,51 @@ decode_std_inq(int sg_fd, const struct sdparm_opt_coll * op) { int res, verb, sz, len, pqual; - unsigned char b[DEF_INQ_RESP_LEN]; + int resid = 0; + int b_sz = DEF_INQ_RESP_LEN; + uint8_t * b = NULL; + uint8_t * free_b = NULL; verb = (op->verbose > 0) ? op->verbose - 1 : 0; - sz = sizeof(b); - memset(b, 0, sizeof(b)); - sz = op->do_long ? sizeof(b) : 36; - res = sg_ll_inquiry(sg_fd, 0, 0, 0, b, sz, 0, verb); + b = sg_memalign(b_sz, 0, &free_b, false); + if (NULL == b) { + pr2serr("%s: unalign to allocate ram\n", __func__); + return sg_convert_errno(ENOMEM); + } + sz = op->do_long ? b_sz : 36; + res = sg_ll_inquiry_v2(sg_fd, false, 0, b, sz, 0, &resid, false, verb); if (res) { - pr2serr("INQUIRY fetching standar response failed\n"); - return res; + pr2serr("INQUIRY fetching standard response failed\n"); + goto fini; + } + if (resid > 0) { + sz -= resid; + if (sz < 5) { + pr2serr("%s: after resid (%d) response size is too short (%d)\n", + __func__, resid, sz); + res = SG_LIB_WILD_RESID; + goto fini; + } } pqual = (b[0] & 0xe0) >> 5; + printf("standard INQUIRY:"); if (0 == pqual) - printf("standard INQUIRY:\n"); + printf("\n"); else if (1 == pqual) - printf("standard INQUIRY: [qualifier indicates no connected " - "LU]\n"); + printf(" [PQ indicates LU temporarily unavailable]\n"); else if (3 == pqual) - printf("standard INQUIRY: [qualifier indicates not capable " - "of supporting LU]\n"); + printf(" [PQ indicates LU not accessible via this port]\n"); else - printf("standard INQUIRY: [reserved or vendor specific " - "qualifier [%d]]\n", pqual); + printf(" [reserved or vendor specific qualifier [%d]]\n", pqual); len = b[4] + 5; len = (len <= sz) ? len : sz; if (op->do_hex) { - dStrHex((const char *)b, len, 0); + hex2stdout(b, len, 0); return 0; } - printf(" PQual=%d Device_type=%d RMB=%d LU_CONG=%d version=0x%02x ", - pqual, b[0] & 0x1f, !!(b[1] & 0x80), !!(b[1] & 0x40), - (unsigned int)b[2]); + printf(" PQual=%d PDT=%d RMB=%d LU_CONG=%d hot_pluggable=%d " + "version=0x%02x ", pqual, b[0] & 0x1f, !!(b[1] & 0x80), + !!(b[1] & 0x40), (b[1] >> 4) & 0x3, (unsigned int)b[2]); printf(" [%s]\n", sg_ansi_version_arr[b[2] & 0xf]); printf(" [AERC=%d] [TrmTsk=%d] NormACA=%d HiSUP=%d " " Resp_data_format=%d\n SCCS=%d ", @@ -1617,48 +2065,99 @@ printf(" Vendor_identification: %.8s\n", b + 8); printf(" Product_identification: %.16s\n", b + 16); printf(" Product_revision_level: %.4s\n", b + 32); - return 0; + res = 0; +fini: + if (free_b) + free(free_b); + return res; } -/* Returns 0 if successful, else error */ +/* If ihbp is NULL then need to send SCSI INQUIRY command to device referred + * to by sg_fd; then process the response received. If ihbp is non-NULL then + * sg_fd is ignored and the buffer pointed to by ihbp (with length no greater + * than ihb_len) is assumed to be the response to a SCSI INQUIRY command. + * Returns 0 if successful, else error. spn changes what is output, currently + * it only changes of the output of the Device Identification VPD page. */ int sdp_process_vpd_page(int sg_fd, int pn, int spn, const struct sdparm_opt_coll * op, int req_pdt, - int protect, const unsigned char * ihbp, int ihb_len) + bool protect, const uint8_t * ihbp, int ihb_len, + uint8_t * alt_buf, int off) { - int res, len, k, verb, dev_pdt, pdt, sz, hex_format; bool adc = false; bool sbc = false; bool ssc = false; - unsigned char * up; + int len, k, verb, dev_pdt, pdt, sz, hex_format; + int resid = 0; + int ret = 0; + uint8_t * up; const struct sdparm_vpd_page_t * vpp; const char * vpd_name; - unsigned char b[VPD_ATA_INFO_RESP_LEN]; + uint8_t * b = NULL; + uint8_t * free_b = NULL; + int b_sz = 2 * sg_get_page_size(); char c[80]; verb = (op->verbose > 0) ? op->verbose - 1 : 0; + if (verb > 3) + pr2serr("%s: sg_fd=%d, pn=%d, spn=%d, ihbp is %sgiven, alt_buff is " + "%sgiven, off=%d\n", __func__, sg_fd, pn, spn, + ((!! ihbp) ? "" : "not "), ((!! alt_buf) ? "" : "not "), off); hex_format = (op->do_hex > 2) ? -1 : 0; - sz = sizeof(b); - memset(b, 0, sz); - if (ihbp) { /* response data supplied by user as hex or binary */ - if (ihb_len < sz) - sz = ihb_len; - memcpy(b, ihbp, sz); - pn = b[1]; - } else { /* need to read from given device */ + sz = b_sz; + if (NULL == alt_buf) { + b = sg_memalign(b_sz, 0 /* page align */, &free_b, false); + if (NULL == b) { + pr2serr("Unable to allocate %d bytes on the heap\n", b_sz); + ret = sg_convert_errno(ENOMEM); + goto fini; + } + } + if (sg_fd < 0) { + if ((!! alt_buf) == (!! ihbp)) { + pr2serr("%s: logic error, if no sg_fd need either ihbp or " + "alt_buf, not both\n", __func__); + ret = sg_convert_errno(EINVAL); + goto fini; + } else if (alt_buf) { + b = alt_buf + off; + sz -= off; + pn = b[1]; + } else { /* so ihbp must be non-NULL */ + if (ihb_len < sz) + sz = ihb_len; + memcpy(b, ihbp, sz); + pn = b[1]; + } + } else { /* so (sg_fd >= 0) , need to read from device */ if (pn < 0) { - if (VPD_NOT_STD_INQ == pn) - return decode_std_inq(sg_fd, op); - else if (op->do_all) + if (VPD_NOT_STD_INQ == pn) { + ret = decode_std_inq(sg_fd, op); + goto fini; + } else if (op->do_all) pn = VPD_SUPPORTED_VPDS; /* if '--all' list supported vpds */ else pn = VPD_DEVICE_ID; /* default to device id page */ } sz = (VPD_ATA_INFO == pn) ? VPD_ATA_INFO_RESP_LEN : DEF_INQ_RESP_LEN; - res = sg_ll_inquiry(sg_fd, 0, 1, pn, b, sz, 0, verb); - if (res) { - pr2serr("INQUIRY fetching VPD page=0x%x failed\n", pn); - return res; +try_larger: + ret = sg_ll_inquiry_v2(sg_fd, true, pn, b, sz, 0, &resid, false, + verb); + if (ret) { + if (! op->examine) + pr2serr("INQUIRY fetching VPD page=0x%x failed\n", pn); + goto fini; + } + len = ((sz - resid) >= 4) ? (sg_get_unaligned_be16(b + 2) + 4) : 0; + if (len > sz) { + if (sz < VPD_LARGE_RESP_LEN) { + sz = VPD_LARGE_RESP_LEN; + goto try_larger; + } + pr2serr("%s: resid=%d implies response too short (%d)\n", + __func__, resid, len); + ret = SG_LIB_WILD_RESID; + goto fini; } } dev_pdt = b[0] & 0x1f; @@ -1677,8 +2176,9 @@ len = sg_get_unaligned_be16(b + 2); /* spc4r25 */ printf("Supported VPD pages VPD page:\n"); if (op->do_hex) { - dStrHex((const char *)b, len + 4, hex_format); - return 0; + hex2stdout(b, len + 4, hex_format); + ret = 0; + goto fini; } if (len > 0) { for (k = 0; k < len; ++k) { @@ -1692,6 +2192,13 @@ } else printf(" 0x%x\n", b[4 + k]); } + /* Take care not to decode this VPD page [0x0] again! */ + if ((op->do_all > 1) && (sg_fd >= 0)) { + ret = decode_all_vpds(b + 4, len, sg_fd, op, req_pdt, + protect); + if (ret) + goto fini; + } } else printf(" \n"); break; @@ -1706,19 +2213,19 @@ if (3 == op->do_hex) { /* output suitable for "hdparm --Istdin" */ dWordHex((const unsigned short *)(b + 60), 256, -2, sg_is_big_endian()); - return 0; + goto fini; } if (op->do_long) printf("ATA information [0x89] VPD page:\n"); else printf("ATA information VPD page:\n"); if (op->do_hex && (2 != op->do_hex)) { - dStrHex((const char *)b, len + 4, 0); - return 0; + hex2stdout(b, len + 4, 0); + goto fini; } - res = decode_ata_info_vpd(b, len + 4, op->do_long, op->do_hex); - if (res) - return res; + ret = decode_ata_info_vpd(b, len + 4, op->do_long, op->do_hex); + if (ret) + goto fini; break; case VPD_DEVICE_ID: /* 0x83 */ if (b[1] != pn) @@ -1733,23 +2240,23 @@ else if (! op->do_quiet) printf("Device identification VPD page:\n"); if (op->do_hex) { - dStrHex((const char *)b, len + 4, hex_format); - return 0; + hex2stdout(b, len + 4, hex_format); + goto fini; } - res = 0; + ret = 0; if ((0 == spn) || (VPD_DI_SEL_LU & spn)) - res = decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_LU), b + 4, - len, VPD_ASSOC_LU, -1, -1, op); + ret = decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_LU), 0, + b + 4, len, VPD_ASSOC_LU, -1, -1, op); if ((0 == spn) || (VPD_DI_SEL_TPORT & spn)) - res = decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TPORT), + ret = decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TPORT), 0, b + 4, len, VPD_ASSOC_TPORT, -1, -1, op); if ((0 == spn) || (VPD_DI_SEL_TARGET & spn)) - res = decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TDEVICE), + ret = decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TDEVICE), 0, b + 4, len, VPD_ASSOC_TDEVICE, -1, -1, op); if (VPD_DI_SEL_AS_IS & spn) - res = decode_dev_ids(NULL, b + 4, len, -1, -1, -1, op); - if (res) - return res; + ret = decode_dev_ids(NULL, 0, b + 4, len, -1, -1, -1, op); + if (ret) + goto fini; break; case VPD_EXT_INQ: /* 0x86 */ if (b[1] != pn) @@ -1764,12 +2271,12 @@ else if (! op->do_quiet) printf("Extended inquiry data VPD page:\n"); if (op->do_hex) { - dStrHex((const char *)b, len + 4, hex_format); - return 0; + hex2stdout(b, len + 4, hex_format); + goto fini; } - res = decode_ext_inq_vpd(b, len + 4, op->do_long, protect); - if (res) - return res; + ret = decode_ext_inq_vpd(b, len + 4, op->do_long, protect); + if (ret) + goto fini; break; case VPD_MAN_NET_ADDR: /* 0x85 */ if (b[1] != pn) @@ -1785,12 +2292,12 @@ else printf("Management network addresses VPD page:\n"); if (op->do_hex) { - dStrHex((const char *)b, len + 4, hex_format); - return 0; + hex2stdout(b, len + 4, hex_format); + goto fini; } - res = decode_man_net_vpd(b, len + 4); - if (res) - return res; + ret = decode_man_net_vpd(b, len + 4); + if (ret) + goto fini; break; case VPD_MODE_PG_POLICY: /* 0x87 */ if (b[1] != pn) @@ -1805,12 +2312,12 @@ else printf("mode page policy VPD page:\n"); if (op->do_hex) { - dStrHex((const char *)b, len + 4, hex_format); - return 0; + hex2stdout(b, len + 4, hex_format); + goto fini; } - res = decode_mode_policy_vpd(b, len + 4); - if (res) - return res; + ret = decode_mode_policy_vpd(b, len + 4); + if (ret) + goto fini; break; case VPD_POWER_CONDITION: /* 0x8a */ if (b[1] != pn) @@ -1825,12 +2332,12 @@ else if (! op->do_quiet) printf("Power condition VPD page:\n"); if (op->do_hex) { - dStrHex((const char *)b, len + 4, hex_format); - return 0; + hex2stdout(b, len + 4, hex_format); + goto fini; } - res = decode_power_condition(b, len + 4); - if (res) - return res; + ret = decode_power_condition(b, len + 4); + if (ret) + goto fini; break; case VPD_DEVICE_CONSTITUENTS: /* 0x8b */ if (b[1] != pn) @@ -1845,12 +2352,12 @@ else if (! op->do_quiet) printf("Device constituents VPD page:\n"); if (op->do_hex) { - dStrHex((const char *)b, len + 4, hex_format); - return 0; + hex2stdout(b, len + 4, hex_format); + goto fini; } - res = decode_dev_const_vpd(b, len + 4); - if (res) - return res; + ret = decode_dev_constit_vpd(b, len + 4, req_pdt, protect, op); + if (ret) + goto fini; break; case VPD_POWER_CONSUMPTION: /* 0x8d */ if (b[1] != pn) @@ -1865,12 +2372,12 @@ else printf("Power consumption VPD page:\n"); if (op->do_hex) { - dStrHex((const char *)b, len + 4, hex_format); - return 0; + hex2stdout(b, len + 4, hex_format); + goto fini; } - res = decode_power_consumption_vpd(b, len + 4); - if (res) - return res; + ret = decode_power_consumption_vpd(b, len + 4); + if (ret) + goto fini; break; case VPD_PROTO_LU: /* 0x90 */ if (b[1] != pn) @@ -1887,12 +2394,12 @@ else printf("Protocol-specific logical unit information VPD page:\n"); if (op->do_hex) { - dStrHex((const char *)b, len + 4, hex_format); - return 0; + hex2stdout(b, len + 4, hex_format); + goto fini; } - res = decode_proto_lu_vpd(b, len + 4); - if (res) - return res; + ret = decode_proto_lu_vpd(b, len + 4); + if (ret) + goto fini; break; case VPD_PROTO_PORT: /* 0x91 */ if (b[1] != pn) @@ -1909,12 +2416,32 @@ else printf("Protocol-specific port information VPD page:\n"); if (op->do_hex) { - dStrHex((const char *)b, len + 4, hex_format); - return 0; + hex2stdout(b, len + 4, hex_format); + goto fini; + } + ret = decode_proto_port_vpd(b, len + 4); + if (ret) + goto fini; + break; + case VPD_SCSI_FEATURE_SETS: /* 0x92 */ + if (b[1] != pn) + goto dumb_inq; + len = sg_get_unaligned_be16(b + 2); + if (len > sz) { + pr2serr("Response to SCSI Feature sets VPD page truncated\n"); + len = sz; } - res = decode_proto_port_vpd(b, len + 4); - if (res) - return res; + if (op->do_long) + printf("SCSI Feature sets [0x92] VPD page:\n"); + else + printf("SCSI Feature sets:\n"); + if (op->do_hex) { + hex2stdout(b, len + 4, hex_format); + goto fini; + } + ret = decode_feature_sets_vpd(b, len + 4, op); + if (ret) + goto fini; break; case VPD_SCSI_PORTS: /* 0x88 */ if (b[1] != pn) @@ -1929,12 +2456,12 @@ else printf("SCSI Ports VPD page:\n"); if (op->do_hex) { - dStrHex((const char *)b, len + 4, hex_format); - return 0; + hex2stdout(b, len + 4, hex_format); + goto fini; } - res = decode_scsi_ports_vpd(b, len + 4, op); - if (res) - return res; + ret = decode_scsi_ports_vpd(b, len + 4, op); + if (ret) + goto fini; break; case VPD_SOFTW_INF_ID: /* 0x84 */ if (b[1] != pn) @@ -1950,8 +2477,8 @@ else printf("Software interface identification VPD page:\n"); if (op->do_hex) { - dStrHex((const char *)b, len + 4, hex_format); - return 0; + hex2stdout(b, len + 4, hex_format); + goto fini; } up = b + 4; for ( ; len > 5; len -= 6, up += 6) @@ -1976,12 +2503,12 @@ else printf("Unit serial number VPD page:\n"); if (op->do_hex) - dStrHex((const char *)b, len + 4, hex_format); + hex2stdout(b, len + 4, hex_format); else if (len > 0) { - if (len + 4 < (int)sizeof(b)) + if (len + 4 < b_sz) b[len + 4] = '\0'; else - b[sizeof(b) - 1] = '\0'; + b[b_sz - 1] = '\0'; printf(" %s\n", b + 4); } else printf(" \n"); @@ -1992,11 +2519,21 @@ len = sg_get_unaligned_be16(b + 2); /* spc4r25 */ if ((NULL == ihbp) && (len > sz)) { /* Increase response size to maximum we can handle here */ - sz = VPD_ATA_INFO_RESP_LEN; - res = sg_ll_inquiry(sg_fd, 0, 1, pn, b, sz, 0, verb); - if (res) { + sz = VPD_XCOPY_RESP_LEN; + ret = sg_ll_inquiry_v2(sg_fd, true, pn, b, sz, 0, &resid, false, + verb); + if (ret) { pr2serr("INQUIRY fetching VPD page=0x%x failed\n", pn); - return res; + goto fini; + } + if (resid) { + sz += resid; + if (resid < 4) { + pr2serr("%s: resid=%d implies response too short (%d)\n", + __func__, resid, sz); + ret = SG_LIB_WILD_RESID; + goto fini; + } } len = sg_get_unaligned_be16(b + 2); if (len > sz) { @@ -2009,10 +2546,10 @@ else printf("Third party copy VPD page:\n"); if (1 == op->do_hex) { - dStrHex((const char *)b, len + 4, 0); - return 0; + hex2stdout(b, len + 4, 0); + goto fini; } - decode_3party_copy_vpd(b, len + 4, op->do_hex, op->verbose); + decode_3party_copy_vpd(b, len + 4, op->do_hex, pdt, op->verbose); break; case 0xb0: /* VPD page depends on pdt */ if (b[1] != pn) @@ -2044,18 +2581,18 @@ else printf("%s VPD page:\n", vpd_name); if (op->do_hex) { - dStrHex((const char *)b, len + 4, hex_format); - return 0; + hex2stdout(b, len + 4, hex_format); + goto fini; } - res = 0; + ret = 0; if (ssc) - res = decode_tape_dev_caps_vpd(b, len + 4); + ret = decode_tape_dev_caps_vpd(b, len + 4); else if (sbc) - res = decode_block_limits_vpd(b, len + 4); + ret = decode_block_limits_vpd(b, len + 4); else - dStrHex((const char *)b, len + 4, 0); - if (res) - return res; + hex2stdout(b, len + 4, 0); + if (ret) + goto fini; break; case 0xb1: /* VPD page depends on pdt */ if (b[1] != pn) @@ -2071,7 +2608,7 @@ break; case PDT_OSD: vpd_name = "Security token (OSD)"; - /* osd = 1; */ + /* osd = true; */ break; case PDT_ADC: vpd_name = "Manufactured assigned serial number (ADC)"; @@ -2091,18 +2628,18 @@ else printf("%s VPD page:\n", vpd_name); if (op->do_hex) { - dStrHex((const char *)b, len + 4, hex_format); - return 0; + hex2stdout(b, len + 4, hex_format); + goto fini; } - res = 0; + ret = 0; if (ssc || adc) - res = decode_tape_man_ass_sn_vpd(b, len + 4); + ret = decode_tape_man_ass_sn_vpd(b, len + 4); else if (sbc) - res = decode_block_dev_chars_vpd(b, len + 4); + ret = decode_block_dev_chars_vpd(b, len + 4); else - dStrHex((const char *)b, len + 4, 0); - if (res) - return res; + hex2stdout(b, len + 4, 0); + if (ret) + goto fini; break; case 0xb2: /* VPD page depends on pdt */ if (b[1] != pn) @@ -2127,18 +2664,18 @@ else printf("%s VPD page:\n", vpd_name); if (op->do_hex) { - dStrHex((const char *)b, len + 4, hex_format); - return 0; + hex2stdout(b, len + 4, hex_format); + goto fini; } - res = 0; + ret = 0; if (ssc) - res = decode_tapealert_supported_vpd(b, len + 4); + ret = decode_tapealert_supported_vpd(b, len + 4); else if (sbc) /* added in sbc3r20 */ - res = decode_block_lb_prov_vpd(b, len + 4, op); + ret = decode_block_lb_prov_vpd(b, len + 4, op); else - dStrHex((const char *)b, len + 4, 0); - if (res) - return res; + hex2stdout(b, len + 4, 0); + if (ret) + goto fini; break; case 0xb3: /* VPD page depends on pdt */ if (b[1] != pn) @@ -2162,26 +2699,26 @@ else printf("%s VPD page:\n", vpd_name); if (op->do_hex) { - dStrHex((const char *)b, len + 4, hex_format); - return 0; + hex2stdout(b, len + 4, hex_format); + goto fini; } - res = 0; + ret = 0; if (ssc) { /* VPD_AUTOMATION_DEV_SN ssc */ if (len > 0) { - if (len + 4 < (int)sizeof(b)) + if (len + 4 < b_sz) b[len + 4] = '\0'; else - b[sizeof(b) - 1] = '\0'; + b[b_sz - 1] = '\0'; printf(" serial number: %s\n", b + 4); } else printf(" serial number: \n"); } else if (sbc) /* added in sbc3r22 */ - res = decode_referrals_vpd(b, len + 4); + ret = decode_referrals_vpd(b, len + 4); else - dStrHex((const char *)b, len + 4, 0); - if (res) - return res; + hex2stdout(b, len + 4, 0); + if (ret) + goto fini; break; case 0xb4: /* VPD page depends on pdt */ if (b[1] != pn) @@ -2205,10 +2742,10 @@ else printf("%s VPD page:\n", vpd_name); if (op->do_hex) { - dStrHex((const char *)b, len + 4, hex_format); - return 0; + hex2stdout(b, len + 4, hex_format); + goto fini; } - res = 0; + ret = 0; if (ssc) { if (len > 0) { printf(" 0x"); @@ -2218,11 +2755,11 @@ } else printf(" \n"); } else if (sbc) /* added in sbc4r01 */ - res = decode_sup_block_lens_vpd(b, len + 4); + ret = decode_sup_block_lens_vpd(b, len + 4); else - dStrHex((const char *)b, len + 4, 0); - if (res) - return res; + hex2stdout(b, len + 4, 0); + if (ret) + goto fini; break; case 0xb5: /* VPD page depends on pdt */ if (b[1] != pn) @@ -2246,18 +2783,18 @@ else printf("%s VPD page:\n", vpd_name); if (op->do_hex) { - dStrHex((const char *)b, len + 4, hex_format); - return 0; + hex2stdout(b, len + 4, hex_format); + goto fini; } - res = 0; + ret = 0; if (sbc) /* added in sbc4r02 */ - res = decode_block_dev_char_ext_vpd(b, len + 4); + ret = decode_block_dev_char_ext_vpd(b, len + 4); else if (ssc) /* added in ssc5r02a */ - res = decode_lb_protection_vpd(b, len + 4); + ret = decode_lb_protection_vpd(b, len + 4); else - dStrHex((const char *)b, len + 4, 0); - if (res) - return res; + hex2stdout(b, len + 4, 0); + if (ret) + goto fini; break; case VPD_ZBC_DEV_CHARS: /* 0xb6 for both pdt=0 and pdt=0x14 */ if (b[1] != pn) @@ -2277,16 +2814,16 @@ else printf("%s VPD page:\n", vpd_name); if (op->do_hex) { - dStrHex((const char *)b, len + 4, hex_format); - return 0; + hex2stdout(b, len + 4, hex_format); + goto fini; } - res = 0; + ret = 0; if (sbc) /* added in zbc-r01c */ - res = decode_zbdc_vpd(b, len + 4); + ret = decode_zbdc_vpd(b, len + 4); else - dStrHex((const char *)b, len + 4, 0); - if (res) - return res; + hex2stdout(b, len + 4, 0); + if (ret) + goto fini; break; case 0xb7: /* VPD page depends on pdt */ if (b[1] != pn) @@ -2294,7 +2831,7 @@ len = sg_get_unaligned_be16(b + 2); switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: - vpd_name = "Block limits extension"; + vpd_name = "Block limits extension (SBC)"; sbc = true; break; default: @@ -2306,16 +2843,74 @@ else printf("%s VPD page:\n", vpd_name); if (op->do_hex) { - dStrHex((const char *)b, len + 4, hex_format); - return 0; + hex2stdout(b, len + 4, hex_format); + goto fini; } - res = 0; + ret = 0; if (sbc) /* added in sbc4r07 */ - res = decode_block_limits_ext_vpd(b, len + 4); + ret = decode_block_limits_ext_vpd(b, len + 4); else - dStrHex((const char *)b, len + 4, 0); - if (res) - return res; + hex2stdout(b, len + 4, 0); + if (ret) + goto fini; + break; + case 0xb8: /* VPD page depends on pdt */ + if (b[1] != pn) + goto dumb_inq; + len = sg_get_unaligned_be16(b + 2); + switch (pdt) { + case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: + vpd_name = "Format presets (SBC)"; + sbc = true; + break; + default: + vpd_name = "unexpected pdt for B8h"; + break; + } + if (op->do_long) + printf("%s [0xb8] VPD page:\n", vpd_name); + else + printf("%s VPD page:\n", vpd_name); + if (op->do_hex) { + hex2stdout(b, len + 4, hex_format); + goto fini; + } + ret = 0; + if (sbc) /* added in sbc4r18 */ + ret = decode_format_presets_vpd(b, len + 4); + else + hex2stdout(b, len + 4, 0); + if (ret) + goto fini; + break; + case 0xb9: /* VPD page depends on pdt */ + if (b[1] != pn) + goto dumb_inq; + len = sg_get_unaligned_be16(b + 2); + switch (pdt) { + case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: + vpd_name = "Concurrent positioning ranges (SBC)"; + sbc = true; + break; + default: + vpd_name = "unexpected pdt for B9h"; + break; + } + if (op->do_long) + printf("%s [0xb9] VPD page:\n", vpd_name); + else + printf("%s VPD page:\n", vpd_name); + if (op->do_hex) { + hex2stdout(b, len + 4, hex_format); + goto fini; + } + ret = 0; + if (sbc) + ret = decode_con_pos_range_vpd(b, len + 4); + else + hex2stdout(b, len + 4, 0); + if (ret) + goto fini; break; default: if (b[1] != pn) @@ -2330,20 +2925,26 @@ printf("%s in hex:\n", c); else pr2serr("%s in hex:\n", c); - if (len > (int)sizeof(b)) { + if (len > b_sz) { if (op->verbose) pr2serr("page length=%d too long, trim\n", len); - len = sizeof(b); + len = b_sz; } if (op->do_hex) - dStrHex((const char *)b, len, hex_format); + hex2stdout(b, len, hex_format); else - dStrHexErr((const char *)b, len, 0); + hex2stderr(b, len, 0); break; } - return 0; + ret = 0; + goto fini; dumb_inq: pr2serr("malformed VPD response, VPD pages probably not supported\n"); return SG_LIB_CAT_MALFORMED; + +fini: + if (free_b) + free(free_b); + return ret; } diff -Nru sdparm-1.10/src/sdparm_wscan.c sdparm-1.12/src/sdparm_wscan.c --- sdparm-1.10/src/sdparm_wscan.c 2014-08-29 21:51:54.000000000 +0000 +++ sdparm-1.12/src/sdparm_wscan.c 2020-03-04 04:03:39.000000000 +0000 @@ -1,30 +1,27 @@ /* - * Copyright (c) 2006-2014 Douglas Gilbert. + * Copyright (c) 2005-2018, Douglas Gilbert * 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. + * 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. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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. - * + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. */ #include @@ -52,6 +49,7 @@ #endif #include "sg_pt_win32.h" +#include "sg_pr2serr.h" /* * This is called when the '--wscan' option is given to sdparm. It is @@ -78,82 +76,9 @@ #define IOCTL_STORAGE_QUERY_PROPERTY \ CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#ifndef _DEVIOCTL_ -typedef enum _STORAGE_BUS_TYPE { - BusTypeUnknown = 0x00, - BusTypeScsi = 0x01, - BusTypeAtapi = 0x02, - BusTypeAta = 0x03, - BusType1394 = 0x04, - BusTypeSsa = 0x05, - BusTypeFibre = 0x06, - BusTypeUsb = 0x07, - BusTypeRAID = 0x08, - BusTypeiScsi = 0x09, - BusTypeSas = 0x0A, - BusTypeSata = 0x0B, - BusTypeSd = 0x0C, - BusTypeMmc = 0x0D, - BusTypeVirtual = 0xE, - BusTypeFileBackedVirtual = 0xF, - BusTypeMax, - BusTypeMaxReserved = 0x7F -} STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE; - -typedef struct _STORAGE_DEVICE_DESCRIPTOR { - ULONG Version; - ULONG Size; - UCHAR DeviceType; - UCHAR DeviceTypeModifier; - BOOLEAN RemovableMedia; - BOOLEAN CommandQueueing; - ULONG VendorIdOffset; /* 0 if not available */ - ULONG ProductIdOffset; /* 0 if not available */ - ULONG ProductRevisionOffset;/* 0 if not available */ - ULONG SerialNumberOffset; /* -1 if not available ?? */ - STORAGE_BUS_TYPE BusType; - ULONG RawPropertiesLength; - UCHAR RawDeviceProperties[1]; -} STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR; -#endif - -typedef struct _STORAGE_DEVICE_UNIQUE_IDENTIFIER { - ULONG Version; - ULONG Size; - ULONG StorageDeviceIdOffset; - ULONG StorageDeviceOffset; - ULONG DriveLayoutSignatureOffset; -} STORAGE_DEVICE_UNIQUE_IDENTIFIER, *PSTORAGE_DEVICE_UNIQUE_IDENTIFIER; - // Use CompareStorageDuids(PSTORAGE_DEVICE_UNIQUE_IDENTIFIER duid1, duid2) // to test for equality -#ifndef _DEVIOCTL_ -typedef enum _STORAGE_QUERY_TYPE { - PropertyStandardQuery = 0, - PropertyExistsQuery, - PropertyMaskQuery, - PropertyQueryMaxDefined -} STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE; - -typedef enum _STORAGE_PROPERTY_ID { - StorageDeviceProperty = 0, - StorageAdapterProperty, - StorageDeviceIdProperty, - StorageDeviceUniqueIdProperty, - StorageDeviceWriteCacheProperty, - StorageMiniportProperty, - StorageAccessAlignmentProperty -} STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID; - -typedef struct _STORAGE_PROPERTY_QUERY { - STORAGE_PROPERTY_ID PropertyId; - STORAGE_QUERY_TYPE QueryType; - UCHAR AdditionalParameters[1]; -} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY; -#endif - ///////////////////////////////////////////////////////////////////////////// @@ -178,6 +103,7 @@ static struct storage_elem * storage_arr; +static uint8_t * free_storage_arr; static int next_unused_elem = 0; static int verbose = 0; @@ -251,8 +177,34 @@ return "Virt "; case BusTypeFileBackedVirtual: return "FBVir"; - case BusTypeMax: - return "Max "; +#ifdef BusTypeSpaces + case BusTypeSpaces: +#else + case 0x10: +#endif + return "Spaces"; +#ifdef BusTypeNvme + case BusTypeNvme: +#else + case 0x11: +#endif + return "NVMe "; +#ifdef BusTypeSCM + case BusTypeSCM: +#else + case 0x12: +#endif + return "SCM "; +#ifdef BusTypeUfs + case BusTypeUfs: +#else + case 0x13: +#endif + return "Ufs "; + case 0x14: + return "Max "; + case 0x7f: + return "Max Reserved"; default: return "_unkn"; } @@ -273,7 +225,7 @@ &num_out, NULL)) { if (verbose > 2) { err = GetLastError(); - fprintf(stderr, " IOCTL_STORAGE_QUERY_PROPERTY(Devprop) failed, " + pr2serr(" IOCTL_STORAGE_QUERY_PROPERTY(Devprop) failed, " "Error=%u %s\n", (unsigned int)err, get_err_str(err, sizeof(b), b)); } @@ -281,8 +233,8 @@ } if (verbose > 3) - fprintf(stderr, " IOCTL_STORAGE_QUERY_PROPERTY(DevProp) " - "num_out=%u\n", (unsigned int)num_out); + pr2serr(" IOCTL_STORAGE_QUERY_PROPERTY(DevProp) num_out=%u\n", + (unsigned int)num_out); return 0; } @@ -301,12 +253,12 @@ &query, sizeof(query), NULL, 0, &num_out, NULL)) { if (verbose > 2) { err = GetLastError(); - fprintf(stderr, " IOCTL_STORAGE_QUERY_PROPERTY(DevUid(exists)) " - "failed, Error=%u %s\n", (unsigned int)err, + pr2serr(" IOCTL_STORAGE_QUERY_PROPERTY(DevUid(exists)) failed, " + "Error=%u %s\n", (unsigned int)err, get_err_str(err, sizeof(b), b)); } if (verbose > 3) - fprintf(stderr, " num_out=%u\n", (unsigned int)num_out); + pr2serr(" num_out=%u\n", (unsigned int)num_out); /* interpret any error to mean this property doesn't exist */ return 0; } @@ -317,20 +269,20 @@ &num_out, NULL)) { if (verbose > 2) { err = GetLastError(); - fprintf(stderr, " IOCTL_STORAGE_QUERY_PROPERTY(DevUid) failed, " + pr2serr(" IOCTL_STORAGE_QUERY_PROPERTY(DevUid) failed, " "Error=%u %s\n", (unsigned int)err, get_err_str(err, sizeof(b), b)); } return -ENOSYS; } if (verbose > 3) - fprintf(stderr, " IOCTL_STORAGE_QUERY_PROPERTY(DevUid) num_out=%u\n", + pr2serr(" IOCTL_STORAGE_QUERY_PROPERTY(DevUid) num_out=%u\n", (unsigned int)num_out); return 0; } /* 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) { @@ -424,8 +376,7 @@ } } else { err = GetLastError(); - fprintf(stderr, "%s: IOCTL_SCSI_GET_INQUIRY_DATA failed " - "err=%u\n\t%s", + pr2serr("%s: IOCTL_SCSI_GET_INQUIRY_DATA failed err=%u\n\t%s", adapter_name, (unsigned int)err, get_err_str(err, sizeof(b), b)); } @@ -433,12 +384,11 @@ } else { err = GetLastError(); if (ERROR_SHARING_VIOLATION == err) - fprintf(stderr, "%s: in use by other process (sharing " - "violation [34])\n", adapter_name); + pr2serr("%s: in use by other process (sharing violation " + "[34])\n", adapter_name); else if (verbose > 3) - fprintf(stderr, "%s: CreateFile failed err=%u\n\t%s", - adapter_name, (unsigned int)err, - get_err_str(err, sizeof(b), b)); + pr2serr("%s: CreateFile failed err=%u\n\t%s", adapter_name, + (unsigned int)err, get_err_str(err, sizeof(b), b)); if (++hole_count >= MAX_HOLE_COUNT) break; } @@ -455,7 +405,7 @@ struct storage_elem tmp_se; if (verbose > 2) - fprintf(stderr, "%s: enter\n", __func__); + pr2serr("%s: enter\n", __func__); for (k = 0; k < 24; ++k) { memset(&tmp_se, 0, sizeof(tmp_se)); snprintf(adapter_name, sizeof (adapter_name), "\\\\.\\%c:", 'C' + k); @@ -465,12 +415,12 @@ OPEN_EXISTING, 0, NULL); if (fh != INVALID_HANDLE_VALUE) { if (query_dev_property(fh, &tmp_se.qp_descriptor) < 0) - fprintf(stderr, "%s: query_dev_property failed\n", __func__); + pr2serr("%s: query_dev_property failed\n", __func__); else tmp_se.qp_descriptor_valid = 1; if (query_dev_uid(fh, &tmp_se.qp_uid) < 0) { if (verbose > 2) - fprintf(stderr, "%s: query_dev_uid failed\n", __func__); + pr2serr("%s: query_dev_uid failed\n", __func__); } else tmp_se.qp_uid_valid = 1; if (('\0' == letter) || (letter == tmp_se.name[0])) @@ -493,7 +443,7 @@ struct storage_elem tmp_se; if (verbose > 2) - fprintf(stderr, "%s: enter\n", __func__); + pr2serr("%s: enter\n", __func__); for (k = 0; k < MAX_PHYSICALDRIVE_NUM; ++k) { memset(&tmp_se, 0, sizeof(tmp_se)); snprintf(adapter_name, sizeof (adapter_name), @@ -504,12 +454,12 @@ OPEN_EXISTING, 0, NULL); if (fh != INVALID_HANDLE_VALUE) { if (query_dev_property(fh, &tmp_se.qp_descriptor) < 0) - fprintf(stderr, "%s: query_dev_property failed\n", __func__); + pr2serr("%s: query_dev_property failed\n", __func__); else tmp_se.qp_descriptor_valid = 1; if (query_dev_uid(fh, &tmp_se.qp_uid) < 0) { if (verbose > 2) - fprintf(stderr, "%s: query_dev_uid failed\n", __func__); + pr2serr("%s: query_dev_uid failed\n", __func__); } else tmp_se.qp_uid_valid = 1; hole_count = 0; @@ -517,13 +467,15 @@ CloseHandle(fh); } else { err = GetLastError(); + if ((0 == k) && (ERROR_ACCESS_DENIED == err)) + pr2serr("Access denied on %s, may need Administrator\n", + adapter_name); if (ERROR_SHARING_VIOLATION == err) - fprintf(stderr, "%s: in use by other process (sharing " - "violation [34])\n", adapter_name); + pr2serr("%s: in use by other process (sharing violation " + "[34])\n", adapter_name); else if (verbose > 3) - fprintf(stderr, "%s: CreateFile failed err=%u\n\t%s", - adapter_name, (unsigned int)err, - get_err_str(err, sizeof(b), b)); + pr2serr("%s: CreateFile failed err=%u\n\t%s", adapter_name, + (unsigned int)err, get_err_str(err, sizeof(b), b)); if (++hole_count >= MAX_HOLE_COUNT) break; } @@ -543,7 +495,7 @@ struct storage_elem tmp_se; if (verbose > 2) - fprintf(stderr, "%s: enter\n", __func__); + pr2serr("%s: enter\n", __func__); for (k = 0; k < MAX_CDROM_NUM; ++k) { memset(&tmp_se, 0, sizeof(tmp_se)); snprintf(adapter_name, sizeof (adapter_name), "\\\\.\\CDROM%d", k); @@ -553,12 +505,12 @@ OPEN_EXISTING, 0, NULL); if (fh != INVALID_HANDLE_VALUE) { if (query_dev_property(fh, &tmp_se.qp_descriptor) < 0) - fprintf(stderr, "%s: query_dev_property failed\n", __func__); + pr2serr("%s: query_dev_property failed\n", __func__); else tmp_se.qp_descriptor_valid = 1; if (query_dev_uid(fh, &tmp_se.qp_uid) < 0) { if (verbose > 2) - fprintf(stderr, "%s: query_dev_uid failed\n", __func__); + pr2serr("%s: query_dev_uid failed\n", __func__); } else tmp_se.qp_uid_valid = 1; hole_count = 0; @@ -567,12 +519,11 @@ } else { err = GetLastError(); if (ERROR_SHARING_VIOLATION == err) - fprintf(stderr, "%s: in use by other process (sharing " - "violation [34])\n", adapter_name); + pr2serr("%s: in use by other process (sharing violation " + "[34])\n", adapter_name); else if (verbose > 3) - fprintf(stderr, "%s: CreateFile failed err=%u\n\t%s", - adapter_name, (unsigned int)err, - get_err_str(err, sizeof(b), b)); + pr2serr("%s: CreateFile failed err=%u\n\t%s", adapter_name, + (unsigned int)err, get_err_str(err, sizeof(b), b)); if (++hole_count >= MAX_HOLE_COUNT) break; } @@ -592,7 +543,7 @@ struct storage_elem tmp_se; if (verbose > 2) - fprintf(stderr, "%s: enter\n", __func__); + pr2serr("%s: enter\n", __func__); for (k = 0; k < MAX_TAPE_NUM; ++k) { memset(&tmp_se, 0, sizeof(tmp_se)); snprintf(adapter_name, sizeof (adapter_name), "\\\\.\\TAPE%d", k); @@ -602,12 +553,12 @@ OPEN_EXISTING, 0, NULL); if (fh != INVALID_HANDLE_VALUE) { if (query_dev_property(fh, &tmp_se.qp_descriptor) < 0) - fprintf(stderr, "%s: query_dev_property failed\n", __func__); + pr2serr("%s: query_dev_property failed\n", __func__); else tmp_se.qp_descriptor_valid = 1; if (query_dev_uid(fh, &tmp_se.qp_uid) < 0) { if (verbose > 2) - fprintf(stderr, "%s: query_dev_uid failed\n", __func__); + pr2serr("%s: query_dev_uid failed\n", __func__); } else tmp_se.qp_uid_valid = 1; hole_count = 0; @@ -616,12 +567,11 @@ } else { err = GetLastError(); if (ERROR_SHARING_VIOLATION == err) - fprintf(stderr, "%s: in use by other process (sharing " - "violation [34])\n", adapter_name); + pr2serr("%s: in use by other process (sharing violation " + "[34])\n", adapter_name); else if (verbose > 3) - fprintf(stderr, "%s: CreateFile failed err=%u\n\t%s", - adapter_name, (unsigned int)err, - get_err_str(err, sizeof(b), b)); + pr2serr("%s: CreateFile failed err=%u\n\t%s", adapter_name, + (unsigned int)err, get_err_str(err, sizeof(b), b)); if (++hole_count >= MAX_HOLE_COUNT) break; } @@ -685,12 +635,13 @@ printf("%s", sp->qp_descriptor.raw + j); printf("\n"); if (verbose > 2) - dStrHexErr(sp->qp_descriptor.raw, 144, 0); + hex2stderr((uint8_t *)sp->qp_descriptor.raw, 144, 0); } else printf("\n"); if ((verbose > 3) && sp->qp_uid_valid) { printf(" UID valid, in hex:\n"); - dStrHexErr(sp->qp_uid.raw, sizeof(sp->qp_uid.raw), 1); + hex2stderr((uint8_t *)sp->qp_uid.raw, sizeof(sp->qp_uid.raw), + 1); } } } @@ -708,16 +659,19 @@ sg_do_wscan(char letter, int do_scan, int verb) { int ret, show_bt, scsi_scan; + const uint32_t pg_sz = sg_get_page_size(); verbose = verb; show_bt = (do_scan > 1); scsi_scan = (do_scan > 2) ? (do_scan - 2) : 0; - storage_arr = calloc(sizeof(struct storage_elem) * MAX_SCSI_ELEMS, 1); + storage_arr = (struct storage_elem *)sg_memalign( + sizeof(struct storage_elem) * MAX_SCSI_ELEMS, + pg_sz, &free_storage_arr, verb > 3); if (storage_arr) { ret = do_wscan(letter, show_bt, scsi_scan); - free(storage_arr); + free(free_storage_arr); } else { - fprintf(stderr, "Failed to allocate storage_arr on heap\n"); + pr2serr("Failed to allocate storage_arr on heap\n"); ret = SG_LIB_SYNTAX_ERROR; } return ret;