sg_z_act_query: new utility for sending either a Zone activate or Zone query command; sg_rep_zones: add Report zone starting LBA granularity field in REPORT ZONES response [zbc2r12]; sg_decode_sense: add --nodecode option; initialize all sense buffers to 0; rework main README file

git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@923 6180dd3e-e324-4e3e-922d-17de1ae2f315
diff --git a/COVERAGE b/COVERAGE
index 434ebc2..1ab56b1 100644
--- a/COVERAGE
+++ b/COVERAGE
@@ -53,7 +53,7 @@
 READ(10)            sg_dd, sgm_dd, sgp_dd, sg_read
 READ(12)            sg_dd, sgm_dd, sgp_dd, sg_read
 READ(16)            sg_dd, sgm_dd, sgp_dd, sg_read
-READ ATTRUBUTE      sg_read_attr
+READ ATTRIBUTE      sg_read_attr
 READ BLOCK LIMITS   sg_read_block_limits, ++
 READ BUFFER(10)     sg_rbuf, sg_test_rwbuf, sg_read_buffer, sg_safte, ++
 READ BUFFER(16)     sg_read_buffer
@@ -121,6 +121,8 @@
 WRITE STREAM(16)    sg_write_x
 WRITE STREAM(32)    sg_write_x
 WRITE USING TOKEN   ddpt, ddptctl, ++
+ZONE ACTIVATE       sg_z_act_query
+ZONE QUERY          sg_z_act_query
 <most commands>     sg_raw
 
 
@@ -180,4 +182,4 @@
 
 
 Douglas Gilbert
-30th August 2021
+20th November 2021
diff --git a/ChangeLog b/ChangeLog
index e570228..292794a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,7 +2,14 @@
 some description at the top of its ".c" file. All utilities in the main
 directory have their own "man" pages. There is also a sg3_utils man page.
 
-Changelog for pre-release sg3_utils-1.48 [20211116] [svn: r922]
+Changelog for pre-release sg3_utils-1.48 [20211120] [svn: r923]
+  - sg_z_act_query: new utility for sending either a
+    Zone activate or Zone query command
+  - sg_rep_zones: add Report zone starting LBA granularity
+    field in REPORT ZONES response [zbc2r12]
+  - sg_decode_sense: add --nodecode option
+  - initialize all sense buffers to 0
+  - rework main README file
   - rev 921+922 are bugfix revs on release 1.47 [r919,920]
   - configure.ac: map msys to mingw
     - repeat tweak to accept uclinux as linux
diff --git a/README b/README
index 26acbea..5b40bb0 100644
--- a/README
+++ b/README
@@ -1,544 +1,114 @@
-                      README for sg3_utils
-                      ====================
+                        README for sg3_utils
+                        ====================
 Introduction
-============
-This package contains low level command line utilities for devices that use
-the SCSI command set. Originally the SCSI command set was associated
-exclusively with the SCSI Parallel Interface (SPI) transport. SPI has now
-almost been completely replaced by the Serial Attached SCSI (SAS) transport
-which also accepts the SCSI command set. Additionally many other storage
-related transports use the SCSI command set (amongst others); examples are
-ATAPI devices (CD/DVDs and tapes), USB mass storage devices (including those
-using the newer UAS[P]), Fibre Channel disks, IEEE 1394 storage devices (SBP
-protocol), iSCSI, FCoE and SOP devices. Even NVMe which has its own command
-set accepts SCSI commands in some contexts; one example is for enclosure
-management where NVME-MI has SES Send and SES Receive commands. SES refers
-to the SCSI Enclosure Services command set.
+------------
+sg3_utils is a package of utilities originally written to send individual
+SCSI commands to storage devices that used one of the SCSI command sets.
+These utilities can be divided into three groups:
+   - sg_raw: the user supplies the cdb (command descriptor block) and
+     optionally the size of the data-in and data-out buffers
+   - one command utilities: the majority of the utilities in this package
+     send one SCSI command. Their names start with "sg_" while the
+     remaining part of the name alludes to the command which is sent. For
+     example, "sg_inq" sends the SCSI INQUIRY command. Some utilities in
+     this group send one of a selection of commands, typically those
+     commands have a lot it common.
+   - copy type utilities: sg_dd, sgp_dd and sgm_dd use the Unix dd command
+     as a template. sg_xcopy sends the SCSI EXTENDED COPY command which in
+     some cases can do offloaded copies. As well as copying some of these
+     utilities can compare if two data segments are the same.
 
-This package originally targeted the Linux SCSI subsystem. Since most
-operating systems contain a SCSI command pass-through mechanism, many
-utilities within this package have been ported. This README mainly
-concentrates on Linux: see the README.freebsd file for the FreeBSD port,
-README.solaris for the Solaris port, the README.tru64 file for the Tru64
-(OSF) port and README.win32 for the Windows ports (of which there are two
-variants).
+Platforms
+---------
+These utilities were written on Linux and should work from Linux kernel
+(lk) 2.4 through to the current series 5. The third group ("copy type")
+are only implemented on Linux, but a separate portable package/utility
+called ddpt implements similar functionality. The first two groups are
+implemented (i.e. ported) to Android, FreeBSD, Solaris and Windows. The
+Windows port uses either a Cygwin or MinGW (plus Msys) build environment
+(rather than Visual Studio).
 
-Most utilities within the sg3_utils package work at the SCSI command level.
-For example the sg_inq utility issues a SCSI INQUIRY command and decodes the
-response. The COVERAGE file has a table containing a row for each SCSI
-command issued by this package; to the right of each row is the utility
-(sometimes more than one) that issue that SCSI command. The COVERAGE file
-has a second table for ATA commands usage.
+Library
+-------
+Many of these utilities share a lot of code (e.g. SCSI error messages)
+so a lot of repetition (potentially error prone) is saved by having a
+library called libsgutils or some variation on that name. Distributions
+(especially of Linux) have differing policies on how a library (and a
+package) should be named. For that reason this package is sometimes
+known as "sg3-utils" (i.e. the underscore is turned into a hyphen).
+Various other packages use libsgutils. The library interface is not
+altered from one package release, to the next, but the library interface
+may be expanded. If a utility from one release is used with a libsgutils
+from an earlier release, the runtime linking may fail. Typically package
+managers take care of these details so that runtime linking errors
+should be rare.
 
-Some utilities interface at a slightly higher level, for example: sg_dd,
-sgm_dd and sgp_dd. These are closely related to the Unix dd command and
-typically issue a sequence of SCSI READ and WRITE commands to copy data.
-These utilities are relatively tightly bound to Linux and are not ported to
-other Operating Systems. A new utility called ddpt (in a package of the same
-name) is more generic while still allowing a copy to be done in terms of
-SCSI READ and WRITE commands. ddpt has been ported to other OSes.
+Command Sets
+------------
+SCSI command sets are not the only storage command sets in wide use, there
+are also ATA and NVMe command sets. There is a SCSI command set to
+translate SCSI commands to ATA commands (called SAT: SCSI to ATA
+Translation). SAT includes an ATA PASS-THROUGH SCSI command and
+sg_sat_* utilities (there are four) are examples of using SAT. The SAS
+transport (Serial Attached SCSI) can convey ATA commands through a
+SCSI/SAS domain via its Serial ATA Tunnelled Protocol (STP).
 
-License
-=======
-All utilities and libraries have either a "2 clause" BSD license or are
-"GPL-2ed". The "2 clause" BSD license is taken from the FreeBSD project but
-drops the last paragraph that directly refers to the "FreeBSD project".
-That BSD license was updated from the "3 clause" to the newer "2 clause"
-version on 20180119. To save space various source code files refer to a
-file called "BSD_LICENSE" in the main, src and lib directories. The author's
-intention is that users may incorporate all or part of the code in their work
-as they please. Attribution is encouraged. Please check the code as other
-contributors (apart from the author) may also have copyright notices. For a
-list of contributors see the CREDITS file.
+NVMe command sets (e.g. Admin, NVM and MI) are relatively new. There
+was an early paper on a SCSI to NVMe Translation Layer (SNTL) but it
+hasn't been standardized. The sg_inq utility will send (and decode
+the response of) a SCSI INQUIRY command if the underlying device is
+a SCSI device. If the underlying device is a NVMe controller or
+namespace, then sg_inq will send a NVMe Admin Identify command. The
+sg_ses utility (for SCSI Enclosure Services) also checks whether its
+underlying device is SCSI or NVME. In the NVMe case, sg_ses translates
+the SCSI SEND DIAGNOSTIC and READ DIAGNOSTIC RESULTS commands to the
+NVMe Management Interface (MI) SES Send and SES Receive commands
+respectively. The output of the sg_ses utility should be similar,
+irrespective of whether the "SES" device is SCSI or NVMe.
 
+The sg_raw utility may send NVMe Admin or NVM commands (as well as
+SCSI commands). One difficulty with a command-line utility invoking
+NVME commands is that those commands contain memory addresses for
+data-in (from the storage device) or data-out (toward the storage
+device) transfers. See the sg_raw manpage for how this difficulty is
+addressed.
 
-Description
-===========
-A web site supporting the sg3_utils package can be found at
-https://sg.danny.cz/sg/sg3_utils.html . That page has a table of released
-versions for download. The most recent release or beta of sg3_utils may
-be found on this page: https://sg.danny.cz/sg in the News section.
+Documentation
+-------------
+Manual pages ("manpages") are the primary method of utility documentation.
+All utilities and scripts that are installed by this package have a
+manpage. There are utilities in the examples, testing and utils
+directories that are not installed and do not have manpages. Nearly
+all utilities have runtime help, usually invoked with either the '-h'
+short option or the '--help' long option. There is also an overarching
+manpage called "sg3_utils". All manpages are in chapter 8 which is
+for system administration commands/utilities.
 
-The predecessor to this package was called sg_utils. It is described in
-https://sg.danny.cz/sg/uu_index.html and old versions can be downloaded
-from the Downloads section of https://sg.danny.cz/sg .
+The sg3_utils package and some more complex utilities have html pages:
+   sg3_utils: https://sg.danny.cz/sg/sg3_utils.html
+   sg_ses:    https://sg.danny.cz/sg/sg_ses.html
+   sg_dd:     https://sg.danny.cz/sg/sg_dd.html
 
-In the Linux 2.4 kernel series these utilities need to use the SCSI generic
-(sg) driver to access SCSI devices. The name of this package (i.e. sg3_utils)
-refers to version 3 of the SCSI generic (sg) driver which was introduced at
-the beginning of the 2.4 Linux kernel series. Significantly this added a new
-SCSI command interface structure (i.e. struct sg_io_hdr) that is more
-flexible than the older "sg_header" structure found in the sg driver in the
-2.2 and earlier Linux kernel series. The sg_io_hdr structure is also more
-flexible than the awkward (and limiting) interface to the
-SCSI_IOCTL_SEND_COMMAND ioctl supported by the Linux SCSI mid level. The
-version 3 sg driver also added the SG_IO ioctl that is synchronous (i.e. it
-issues the requested SCSI command and waits for the response (or a timeout)
-before the ioctl returns to the user space program that invoked it). The
-SG_IO ioctl is now supported in other parts of the Linux kernel in the 2.6
-series.
+A tarball (and zip) of all the manpages from the previous release are
+here:
+   https://sg.danny.cz/sg/p/sg3_utils_man_html.tgz
+   https://sg.danny.cz/sg/p/sg3_utils_man_html.zip
 
-In sg3_utils version 1.27 support has been added for the Linux bsg driver
-which use the sg version 4 interface. There seems no point in renaming
-this package sg4_utils. The existing utilities just silently support either.
-Currently the source build must be able to see the /usr/include/linux/bsg.h
-file. Then at run time the /proc/devices pseudo file needs to have an entry
-for the bsg driver (appeared around lk 2.6.28). With this in place each
-utility at run time checks the device it has been given and if it is a char
-device whose major number matches the bsg entry in /proc/devices then the
-sg v4 interface is used. Otherwise the sg v3 interface is used.
+There is a html rendering of the sg3_utils manpage in the same directory
+as this README file called sg3_utils.man8.html .
 
-Utilities that wish to use the asynchronous SCSI command interface (i.e. via
-a write() read() sequence) or issue special "commands" (e.g. bus and device
-resets) still need to use the Linux sg driver. Note that various
-drivers (e.g. cdrom/sr) have different open() flag and permissions policies
-that the user may need to take into account.
+The previous README file is now called README.details plus there are
+these OS specific files: README.freebsd , README.solaris , README.tru64
+and README.win32 . To know the current state of the package the ChangeLog
+file is the good reference.
 
-If users have problems or questions about them please contact the author.
-Documentation for the Linux sg device driver can be found at:
-https://sg.danny.cz/sg/p/sg_v3_ho.html . This is written in DocBook and the
-original xml can be found in the same directory with the ".xml" extension.
-Postscript and pdf renderings are also in that directory. Older documentation
-for the sg version 3 driver can be found at:
-https://sg.danny.cz/sg/p/scsi_generic_v3.txt .
+The author's primary source code repository uses subversion and is on
+the author's equipment (a RPi). One advantage of subversion is its
+revision numbers which are simply integers starting at 1 and ascending.
+For this package the current revision is 922 . The subversion repository
+is mirrored in git (using "git svn" tools) here:
+    https://github.com/doug-gilbert/sg3_utils
 
-To save the repetition of common code (e.g. SCSI error processing) and
-reduce the size of the executable files, a shared library called
-libsgutils<num>.so (its Linux name) is created during the build process.
-That library is built from the contents of the include and lib
-subdirectories. The header files in the include subdirectory can be seen
-as the API of libsgutils and are commented with that in mind. The SCSI
-pass-through code for the supported operating systems is found in the lib
-subdirectory with names like sg_pt_linux.c and sg_pt_win32.c .
 
-Various distributions (of Linux mainly) distribute sg3_utils as 3
-installable packages. One is a package containing the shared library
-discussed above (e.g. libsgutils2-2_1.33-0.1_i386.deb). A second package
-contains the utilities (e.g. sg3-utils_1.33-0.1_i386.deb) and depends on the
-first package). Finally there is an optional package that contains header
-files and a static library (e.g. libsgutils2-dev_1.33-0.1_i386.deb). This
-final package is only needed to build other packages (e.g. sdparm) that
-wish to use the sg3_utils shared library.
-
-All the utilities in the src subdirectory have "man" pages that are
-placed in the doc subdirectory. There is also a sg3_utils (8) man page that
-summarizes common facilities including exit statuses. Additional
-information (including each utility's version number) can be found towards
-the top of each ".c" file corresponding to the utility name.
-
-The sg driver in Linux can be seen as having 3 distinct versions:
-
-   v1   lk < 2.2.6     sg_header based relatively unchanged since 1992
-   v2   lk >= 2.2.6    enhanced sg_header interface structure [1999/4/16]
-   v3   lk >= 2.4      additional sg_io_hdr interface structure [2001/1/4]
-   v3   lk >= 2.6      same interface as found in lk 2.4 [2.6.0: 2003/12/18]
-
-and the bsg driver supports the sg v4 interface and was added around
-lk 2.6.28 . This package is targeted at "v3" and "v4". Another package called
-"sg_utils" is targeted at "v2" and to a lesser extent "v1". The "sg_utils"
-package has a subset of the utilities found in this package.
-
-In Linux some sg driver ioctls (notably SG_IO) are defined for many block
-devices in lk 2.6 series. In practice this means all SCSI block devices,
-ATAPI block devices (mainly CD, DVD and BD optical devices) but _not_ ATA
-disks, depending on which kernel configuration options, can be accessed by
-the utilities in this package. SATA disks that use the libata kernel library
-(or some other SCSI to ATA Translation (SAT) Layer (SATL)) accept SCSI
-commands and thus are supported. Support for the SG_IO as been added to the
-scsi tape driver (st) in lk 2.6.6 .
-
-In the src directory the bulk of the utilities are written in relatively
-clean POSIX compliant C code with Linux specific system calls and structures
-removed and placed in Linux specific files in the lib directory. A small
-number of utilities in the src directory do contain Linux specific logic
-and are not ported to other OSes (e.g. sg_dd). One utility, sg_scan, has
-two separate implementations, one for Linux (sg_scan_linux.c) and one for
-Windows (sg_scan_win32.c). The src-lib directory split approach allows
-FreeBSD, Solaris, Tru64 and Windows specific code to be isolated to a few
-files in the lib directory whose interfaces match those of the Linux
-specific code.
-
-Darwin is not supported because the Apple folks do not want to give their
-users a pass-through SCSI interface. The author has read about creative
-hackers using a VM containing a real OS to circumvent the Apple restriction.
-
-C standard is C11
-==================
-The C code in this package is written for portability rather than speed.
-It assumes a level of C99 compliance (the C standard prior to C11) and
-favours POSIX system and library calls over OS specific calls.
-
-The C code is written in a C++ friendly way and is checked from time to
-time that it compiles clean with C++. To accommodate C++ certain C99
-constructs such as designated initializers cannot be used.
-
-The author has not seriously attempted to build this code on MSVC (aka
-Visual Studio). There are a few roadblocks (that may be overcome in the
-future) that include MSVC being basically a C++ compiler, not a C/C++
-compiler. For some reason MSVC only claims C89 compliance (i.e. the first
-C standard from 1989). MSVC 2013 and 2015 are moving closer to C99
-compliance and may be sufficient to compile this package. Another problem
-is the assumption of the availability of basic Unix system calls such as
-open(). Nearly 20 years ago Microsoft indicated (promised ?) that it
-would move in the direction of POSIX compliance, but very little ever
-happened. "Talk is cheap, there should be a tax on it."
-
-Building
-========
-This package is designed to be built with the usual:
-    "./configure ; make ; make install"
-sequence. In some situations that may need to be prefixed by a call to
-the "./autogen.sh" script which invokes autoconf and automake. That in turn
-may require packages containing those utilities to be installed. The
-libtool utility is also required. Naturally a C compiler is required
-and due to the vagaries of libtool a C++ compiler also.
-
-The "./configure" takes many command line options with the defaults
-being usually sufficient to start with. One quirk is that the location
-of the installation is under the /usr/local directory. So the sg_inq
-utility will be installed at /usr/local/bin/sg_inq . This is controlled
-by the "--prefix=<directory>" option which defaults to
-"--prefix=/usr/local". As an example to install the executables in /usr/bin
-and disable the creation of the shared library (libsgutils<num>.so) this
-invocation could be used: "./configure --prefix=/usr --disable-shared".
-To reduce the size of an executable as well try this:
-"./configure --prefix=/usr --disable-shared --disable-scsistrings".
-Also --disable-shared will produce (relatively) "static" executables in
-the src directory that are easier to debug. And
-"./configure --enable-debug" will compile with more debug type options,
-including more compiler checks and defining "DEBUG" within the src and
-lib source files. Most utilities in the src directory set '-vv' (i.e.
-equivalent to calling "--verbose" twice) when "DEBUG" is set.
-
-In Linux there are package build files for "rpm" based and for "deb" based
-systems. The 'sg3_utils.spec' file in the main directory can be used like
-this: 'rpmbuild -ba sg3_utils.spec' in a rpmbuild tree SPECS directory.
-To cross build or make a more widely distributable package then the --target
-option may be useful: 'rpmbuild --target=i386 -ba sg3_utils.spec' or
-'rpmbuild --target=x86_64 -ba sg3_utils.spec' . The sg3_utils.spec file
-in the main directory targets Red Hat systems, an alternative "spec" file
-for Suse systems has been placed under the 'suse' directory.
-
-The 'build_debian.sh' script should build several "deb" packages and place
-them in the parent directory. In debian based systems doing
-a 'apt-get install build-essential' is one way to get most of build
-environment needed if it has not already been loaded. There are now some
-problems with this script and the superseded Debian 4.0 ("etch"). See
-debian/README.debian4 for a workaround. Amongst other things debian
-builds are sensitive to the value in the debian/compat file. If it
-contains "7" then it works on lenny and gives warning on squeeze (but
-fails on the earlier etch).
-
-Warning
-=======
-Many devices use SCSI command sets over transport protocols not normally
-associated with SCSI (as defined at https://www.t10.org ). Some of these
-devices react poorly (e.g. lock up) when sent SCSI commands that they don't
-support. Even sending a supported SCSI command with a field set to an
-unexpected value can cause problems. [The author is talking about billions
-of USB devices with horrible SCSI implementations.]
-
-For example, all "SCSI" devices must support the INQUIRY command which the
-SCSI-2 standard says should request a 36 byte response. However later SCSI
-standards (e.g. SPC-2) have increased that length but some SCSI devices lock
-up when they receive a request for anything other than a 36 byte response.
-
-Any well implemented "SCSI" device should react sensibly when a utility in
-sg3_utils sends a SCSI command that it doesn't support. Unfortunately this
-cannot be guaranteed.
-
-Prior to lk 2.6.29 USB mass storage limited sense data to 18 bytes which
-caused problems for certain types of descriptor based sense data. An
-example of this is the SCSI ATA PASS-THROUGH command with the CK_COND bit
-set.
-
-
-Utilities
-=========
-Here is list in alphabetical order of utilities found in the 'src'
-subdirectory of the sg3_utils package:
-    sginfo, sg_bt_ctl, sg_compare_and_write, sg_copy_results, sgm_dd, sgp_dd,
-    sg_dd, sg_decode_sense, sg_emc_trespass, sg_format, sg_get_config,
-    sg_get_elem_status, sg_get_lba_status, sg_ident, sg_inq, sg_logs,
-    sg_luns, sg_map, sg_map26, sg_modes, sg_opcodes, sg_persist, sg_prevent,
-    sg_raw, sg_rbuf, sg_rdac, sg_read, sg_read_attr, sg_readcap,
-    sg_read_block_limits, sg_read_buffer, sg_read_long, sg_reassign,
-    sg_referrals, sg_rep_pip, sg_rep_zones, sg_request, sg_reset, sg_rmsn,
-    sg_rtpg, sg_safte, sg_sanitize, sg_sat_identify, sg_sat_phy_event,
-    sg_sat_read_gplog, sg_sat_set_features, sg_scan, sg_seek, sg_senddiag,
-    sg_ses, sg_ses_microcode, sg_start, sg_stpg, sg_stream_ctl, sg_sync,
-    sg_test_rwbuff, sg_timestamp, sg_turs, sg_unmap, sg_verify, sg_vpd,
-    sg_write_buffer, sg_write_long, sg_write_same, sg_write_verify,
-    sg_write_x, sg_wr_mode, sg_xcopy, sg_zone
-
-Each of the above utilities depends on header files found in the 'include'
-subdirectory and library code found in the 'lib' subdirectory. Associated
-man pages are found in the 'doc' subdirectory. Additional programs found
-in the 'archive', 'examples' and 'utils' subdirectories in not build by the
-top level build infrastructure. Linux binary distributions of the sg3_utils
-package (e.g. "rpm" and debian packages) typically contain the shared
-library, the utilities found in the 'src' subdirectory, their associated man
-pages and some documentation files (e.g. README, INSTALL, CREDITS, COPYING
-and COVERAGE). See the INSTALL file for generic instructions about building
-with autotools (e.g. ./configure ).
-
-Man pages can be read (without building and installing the package) by
-going to the 'doc' subdirectory and executing something like this:
- $ man ./sg_dd.8
-
-To see which SCSI commands (and ATA commands) are used by these utilities
-refer to the COVERAGE file.
-
-Here is a list in alphabetical order of utilities found in the 'examples'
-subdirectory:
-  - sg_excl, scsi_inquiry, sg_sat_chk_power, sg__sat_identify,
-    sg__sat_phy_event, sg__sat_set_features, sg_sat_smart_rd_data,
-    sg_simple1, sg_simple2, sg_simple3, sg_simple4, sg_simple5,
-    sg_simple16
-
-Also in that subdirectory is a script to test sg_persist, an example data
-file for sg_persist (called "transport_ids.txt") and an example data file for
-sg_reassign (called "reassign_addr.txt"). There are several scripts
-for 'sg_senddiag -pf -raw=-' that will put some SAS disk phys into
-a "compliant jitter tolerance pattern" (CJTPAT).
-
-The 'testing' subdirectory contains source and a Makefiles to test
-kernel pass-through and associated drivers, mainly for Linux. There is
-both C code (with the extension ".c") and C++ code (with the extension
-".cpp"). There is a "Makefile" to build the C + C++ code. The Makefile
-depends on some object files from the "lib" subdirectory. So a sequence
-like this may be required prior to invoking make: "cd <top_of_package> ;
-./configure ; cd lib ; make ; cd ../testing".
-
-Here is a list in alphabetical order of utilities found in the 'testing'
-subdirectory:
-  - bsg_queue_tst, sgh_dd (C++), sg_iovec_tst, sg_queue_tst, sg_sense_tst,
-    sg_tst_async (C++), sg_tst_context (C++), sg_tst_excl (C++),
-    sg_tst_excl2 (C++), sg_tst_excl3 (C++)
-
-The 'utils' subdirectory contains source and a Makefile to build "hxascdmp"
-which accepts binary data from stdin (or a file on the command line) and
-outputs an ASCII-HEX and ASCII representation of it. It is similar to the
-Unix od command. There is also code to sg_chk_asc.c which checks a given
-text file (typically a copy of https://www.t10.org/lists/asc-num.txt ) and
-checks it against the asc/ascq text strings held in sg_lib_data.c .
-
-The 'doc' subdirectory contains a README file containing the urls of
-various related documents.
-
-The 'scripts' subdirectory contains some Bourne (bash) shell scripts that
-rely on utilities in the main directory. One script uses the sdparm utility.
-These scripts are described in the scripts/README file and have usage
-messages.
-
-
-Notes for utilities without man pages
-=====================================
-These utils are found in the 'examples' subdirectory.
-
-The "scsi_inquiry" program shows the use of the SCSI_IOCTL_SEND_COMMAND
-ioctl to send a SCSI INQUIRY command. That ioctl() is supported by the
-SCSI sub system mid level and so is common to all sd, sr, st and sg devices.
-That ioctl is deprecated in the lk 2.6 series. This program has been placed
-in the "examples" subdirectory.
-
-"sg_simple1" and "sg_simple2" are example programs demonstrating calls
-to the SCSI INQUIRY and TEST UNIT READY commands. They only differ in their
-error processing: sg_simple1 uses sg_lib.[hc] for error processing while
-sg_simple2 does its own more primitive checks.
-
-"sg_simple3" tests out user space scatter gather added to the version 3
-sg driver.
-
-"sg_simple4" shows the INQUIRY command using mmap-ed IO to obtain its
-response buffer.
-
-"sg_simple5" also sends and INQUIRY and TEST UNIT READY commands. It
-uses the generic pass through mechanism based on sg_pt.h . It will
-currently build in Linux and FreeBSD (with "make -f Makefile.freebsd").
-It has extensive error checking code.
-
-"sg_simple16" attempts to send a 16 byte SCSI command, READ_16, to the
-scsi device. This is only supported for lk >= 2.4.15 and for adapter
-drivers that indicate that they have 16 byte CDB capability (otherwise
-DID_ABORT will appear in the host_status).
-
-"sg_sat_chk_power" attempts to push an ATA CHECK POWER MODE command
-through the SAT-defined ATA PASS_THROUGH (16) SCSI command. That
-ATA command needs to read the "FIS" registers after the command is
-completed which involves using the ATA Status Return (sense data)
-descriptor (as defined in SAT).
-
-"sg_sat_smart_rd_data" attempts to push an ATA SMART/READ DATA command
-through the SAT-defined ATA PASS_THROUGH (16) SCSI command. If
-successful, the 256 word (512 byte) response is output.
-
-"sg_tst_excl" and "sg_tst_excl2" use multiple threads to bombard the
-given device with O_EXCL open flags, so only one should succeed at a
-time. While holding O_EXCL control a thread attempts a double increment
-on an integer in the given LBA. If the integer starts even (after the
-first read) then it should remain even if the O_EXCL flag is doing its job.
-The "sg_tst_excl" variant uses the Linux SG_IO v3 interface while the
-"sg_tst_excl2" uses the more generic sg_pt infrastructure.
-
-"sg_tst_excl3" is a variant of "sg_tst_excl2". "sg_tst_excl3" only does
-the double increment from the first thread, each time using O_EXCL on
-open. The remaining threads check the value is even, each time doing
-an open without the O_EXCL flag.
-
-"bsg_queue_tst" sends an INQUIRY command via the Linux SG_IO v4 interface
-which is used by the bsg driver. So it will take device names like
-"/dev/bsg/6:0:0:0". It tests if sending repeated INQUIRYs with
-the BSG_FLAG_Q_AT_HEAD or BSG_FLAG_Q_AT_TAIL flag makes any difference.
-
-"sg_tst_async" is a test harness for the Linux sg driver. It is multi
-threaded, submitting either TEST UNIT READY, READ(16) or WRITE(16) SCSI
-commands asynchronously. Each thread opens a file descriptor and submits
-those commands up to the queue limit (sg driver has a per file descriptor
-queue limit of 16). Multiple threads doing the same thing act as a
-multiplier to that queue limit.
-
-
-NVME Support
-============
-Firstly the author has no intention of extending this package to contain
-general purpose NVMe utilities. That leaves the areas where SCSI overlaps
-with NVMe. There was a SCSI to NVMe Translation Layer (SNTL) driver in the
-Linux kernel based on a white paper from NVM Express. Intel has withdrawn
-that driver and T10 (SCSI) and NVM Express have made no further attempts
-to standardize a SNTL. Given the SCSI to ATA Translation Layer (SATL) which
-is standardized by T10, it is pretty clear what a SNTL should do.
-
-The NVMe Management Interface (NVME-MI) committee have decided to use SES-3
-standard from T10 via the newly added SES Send and SES Receive MI commands.
-So the sg_ses utility and this package's library have been extended to use
-these commands when a NVMe device (typically a disk enclosure) is detected.
-This has been tested by a disk vendor who is happy with the results. Other
-user reports are welcome as the author does not have equipment to test
-this.
-
-Other utilities in this package that use the SES Send and Receive commands,
-or the SNTL in the library are sg_senddiag, sg_inq, sg_raw and sg_readcap.
-
-
-Command line processing
-=======================
-These utilities can be divided into 3 groups when their handling of command
-line arguments is considered:
-  - ad hoc, typically in a short form only, sometimes longer (e.g.
-    "sg_logs -pcb /dev/sdc")
-  - inspired by the dd Unix command (e.g. sg_dd, sgm_dd, sgp_dd, sg_read)
-  - recent utilities use "getopt_long" (see "man getopt_long")
-    type command lines. These have short form (starting with "-")
-    and corresponding longer form (starting with "--") options.
-
-The older utilities that use ad hoc options, in alphabetical order:
-  - sg_emc_trespass, sginfo(1/2), sg_inq, sg_logs, sg_map, sg_modes,
-    sg_opcodes, sg_rbuf, sg_rdac, sg_readcap, sg_reset, sg_scan (Linux),
-    sg_senddiag, sg_start, sg_test_rwbuf, sg_turs
-In sg3_utils version 1.23 the following utilities from this group were
-converted to have a dual getopt_long/ad_hoc interface, defaulting to
-the getop_long interface:
-  - sg_inq, sg_logs, sg_modes, sg_opcodes, sg_rbuf, sg_readcap,
-    sg_senddiag, sg_start, sg_turs
-These can be switched back to the older (backward compatible) ad hoc
-interface by defining the SG3_UTILS_OLD_OPTS environment variable
-or using '-O' as the first command line option.
-
-The more recent utilities that use "getopt_long" only are:
-  - sg_bt_ctl, sg_compare_and_write, sg_decode_sense, sg_format,
-    sg_get_config, sg_get_lba_status, sg_ident, sg_luns, sg_map26,
-    sg_persist, sg_prevent, sg_raw, sg_read_attr, sg_read_block_limits,
-    sg_read_buffer, sg_read_long, sg_reassign, sg_referrals, sg_rep_pip,
-    sg_rep_zones, sg_requests, sg_rmsn, sg_rtpg, sg_safte, sg_sanitize,
-    sg_sat_identify, sg_sat_phy_event, sg_sat_read_gplog,
-    sg_sat_set_features, sg_scan(w), sg_seek, sg_ses, sg_ses_microcode,
-    sg_stpg, sg_stream_ctl, sg_sync, sg_test_rwbuf, sg_timestamp, sg_unmap,
-    sg_verify, sg_vpd, sg_write_buffer, sg_write_long, sg_write_same,
-    sg_write_verify, sg_write_x, sg_wr_mode, sg_zone
-
-
-Dangerous code
-==============
-This C code snippet:
-    unsigned char uc = 0x80;
-    uint64_t ull;
-    ull = (uc << 24);
-Somewhat surprisingly sets ull to:
-    ull: 0xffffffff80000000
-This result is due to the 'unary conversion' of uc to a (32 bit signed)
-'int' before the shift. The resultant type from the shift is also an int
-and it has its top bit set so there is sign extension when it is assigned
-into a 64 bit unsigned integer. Making sure there is no conversion to 'int'
-solves the problem. In this case if uc is declared as unsigned int the
-result will be as expected (i.e. 0x80000000).
-
-
-Bypassing the somewhat dangerous shift operators
-================================================
-The shift operators in C are "<<" and ">>". They can be dangerous (as shown
-in the above section) or tedious and hence error prone to use. However they
-are often needed to cope with the translation of integers on the host OS to
-the corresponding representation within a SCSI command or parameter data
-moved to or from a SCSI device. The Logical Block Address (LBA) is a good
-example; it is either 32 or 64 bits long typically (i.e. 4 or 8 bytes
-respectively). The host machine representation may be big or little endian
-and may prefer or require alignment to a particular memory address boundary
-(e.g. module 4 (or in 'C' code: "(lba % 4) == 0")). For SCSI commands and
-the parameter data moved to or from a SCSI device, the integer
-representation is big endian and it is unaligned.
-
-Recent versions of this package have replaced the explicit use of the C
-shift operators with a group of functions modelled on those found in the
-Linux kernel. These functions contain either "get_unaligned" or
-"put_unaligned" in their names and are found in the asm/unaligned.h
-header. This package contains the sg_unaligned.h header that implements
-a similar set of functions. The current implementation favours correctness
-over speed. The functions in the package use a "sg_" prefix but otherwise
-use the same function name as the Linux kernel for the same action.
-
-An example of the change made to a snippet of sg_write_buffer.c may
-clarify this change. The old code was:
-
-   wbufCmdBlk[3] = (unsigned char)((buffer_offset >> 16) & 0xff);
-   wbufCmdBlk[4] = (unsigned char)((buffer_offset >> 8) & 0xff);
-   wbufCmdBlk[5] = (unsigned char)(buffer_offset & 0xff);
-
-and it has been replaced by:
-
-   sg_put_unaligned_be24(buffer_offset, wbufCmdBlk + 3);
-
-The Linux kernel only supplies "unaligned" functions for 16, 32 and 64
-bit quantities. SCSI commands also have cases of 24 and 48 bit numbers
-so sg_unaligned.h contains support for those plus a variant where the
-byte length is passed as an argument.
-
-The unaligned functions are inlined for speed (at the possible expense of
-space) and now have specializations depending whether the host is big or
-(more likely) little endian. These functions can be broken down to a
-memcpy() and optionally a byte-swap for 16, 32 and 64 bit operations.
-The memcpy() takes care of alignment while the byte-swap (bswap_16(),
-bswap_32() and bswap_64() ) addresses integer endianness. If the host is
-little endian and a little endian variant of the unaligned functions is
-requested, then no byte-swap is required. These specializations can be
-"compiled out" with this configure option: './configure
---disable-fast-lebe' in which case the classic "C shifting" technique is
-used to implement all the unaligned functions.
-
-Associated with the above change, fixed length integer types seem a better
-fit for SCSI command and parameter integers than the traditional integer
-types in the C language. Fixed length integer types were standardized in
-C99 and require the inclusion of <stdint.h>. For example this means for
-an integer that will represent a 64 bit LBA, to favour using "uint64_t"
-over the "unsigned long long" type. Also "unsigned char" has mostly been
-replaced by "uint8_t" as the 8 bit (unsigned) byte type; "char" is still
-used for ASCII text.
-
-
-Other SCSI and storage tools
-============================
-See https://sg.danny.cz/sg/tools.html
-
-
-Douglas Gilbert		dgilbert@interlog.com
-17th June 2021
+Douglas Gilbert
+20th November 2021
diff --git a/README.details b/README.details
new file mode 100644
index 0000000..ea976e7
--- /dev/null
+++ b/README.details
@@ -0,0 +1,544 @@
+                      README.details for sg3_utils
+                      ============================
+Introduction
+============
+This package contains low level command line utilities for devices that use
+the SCSI command set. Originally the SCSI command set was associated
+exclusively with the SCSI Parallel Interface (SPI) transport. SPI has now
+almost been completely replaced by the Serial Attached SCSI (SAS) transport
+which also accepts the SCSI command set. Additionally many other storage
+related transports use the SCSI command set (amongst others); examples are
+ATAPI devices (CD/DVDs and tapes), USB mass storage devices (including those
+using the newer UAS[P]), Fibre Channel disks, IEEE 1394 storage devices (SBP
+protocol), iSCSI, FCoE and SOP devices. Even NVMe which has its own command
+set accepts SCSI commands in some contexts; one example is for enclosure
+management where NVME-MI has SES Send and SES Receive commands. SES refers
+to the SCSI Enclosure Services command set.
+
+This package originally targeted the Linux SCSI subsystem. Since most
+operating systems contain a SCSI command pass-through mechanism, many
+utilities within this package have been ported. This README mainly
+concentrates on Linux: see the README.freebsd file for the FreeBSD port,
+README.solaris for the Solaris port, the README.tru64 file for the Tru64
+(OSF) port and README.win32 for the Windows ports (of which there are two
+variants).
+
+Most utilities within the sg3_utils package work at the SCSI command level.
+For example the sg_inq utility issues a SCSI INQUIRY command and decodes the
+response. The COVERAGE file has a table containing a row for each SCSI
+command issued by this package; to the right of each row is the utility
+(sometimes more than one) that issue that SCSI command. The COVERAGE file
+has a second table for ATA commands usage.
+
+Some utilities interface at a slightly higher level, for example: sg_dd,
+sgm_dd and sgp_dd. These are closely related to the Unix dd command and
+typically issue a sequence of SCSI READ and WRITE commands to copy data.
+These utilities are relatively tightly bound to Linux and are not ported to
+other Operating Systems. A new utility called ddpt (in a package of the same
+name) is more generic while still allowing a copy to be done in terms of
+SCSI READ and WRITE commands. ddpt has been ported to other OSes.
+
+License
+=======
+All utilities and libraries have either a "2 clause" BSD license or are
+"GPL-2ed". The "2 clause" BSD license is taken from the FreeBSD project but
+drops the last paragraph that directly refers to the "FreeBSD project".
+That BSD license was updated from the "3 clause" to the newer "2 clause"
+version on 20180119. To save space various source code files refer to a
+file called "BSD_LICENSE" in the main, src and lib directories. The author's
+intention is that users may incorporate all or part of the code in their work
+as they please. Attribution is encouraged. Please check the code as other
+contributors (apart from the author) may also have copyright notices. For a
+list of contributors see the CREDITS file.
+
+
+Description
+===========
+A web site supporting the sg3_utils package can be found at
+https://sg.danny.cz/sg/sg3_utils.html . That page has a table of released
+versions for download. The most recent release or beta of sg3_utils may
+be found on this page: https://sg.danny.cz/sg in the News section.
+
+The predecessor to this package was called sg_utils. It is described in
+https://sg.danny.cz/sg/uu_index.html and old versions can be downloaded
+from the Downloads section of https://sg.danny.cz/sg .
+
+In the Linux 2.4 kernel series these utilities need to use the SCSI generic
+(sg) driver to access SCSI devices. The name of this package (i.e. sg3_utils)
+refers to version 3 of the SCSI generic (sg) driver which was introduced at
+the beginning of the 2.4 Linux kernel series. Significantly this added a new
+SCSI command interface structure (i.e. struct sg_io_hdr) that is more
+flexible than the older "sg_header" structure found in the sg driver in the
+2.2 and earlier Linux kernel series. The sg_io_hdr structure is also more
+flexible than the awkward (and limiting) interface to the
+SCSI_IOCTL_SEND_COMMAND ioctl supported by the Linux SCSI mid level. The
+version 3 sg driver also added the SG_IO ioctl that is synchronous (i.e. it
+issues the requested SCSI command and waits for the response (or a timeout)
+before the ioctl returns to the user space program that invoked it). The
+SG_IO ioctl is now supported in other parts of the Linux kernel in the 2.6
+series.
+
+In sg3_utils version 1.27 support has been added for the Linux bsg driver
+which use the sg version 4 interface. There seems no point in renaming
+this package sg4_utils. The existing utilities just silently support either.
+Currently the source build must be able to see the /usr/include/linux/bsg.h
+file. Then at run time the /proc/devices pseudo file needs to have an entry
+for the bsg driver (appeared around lk 2.6.28). With this in place each
+utility at run time checks the device it has been given and if it is a char
+device whose major number matches the bsg entry in /proc/devices then the
+sg v4 interface is used. Otherwise the sg v3 interface is used.
+
+Utilities that wish to use the asynchronous SCSI command interface (i.e. via
+a write() read() sequence) or issue special "commands" (e.g. bus and device
+resets) still need to use the Linux sg driver. Note that various
+drivers (e.g. cdrom/sr) have different open() flag and permissions policies
+that the user may need to take into account.
+
+If users have problems or questions about them please contact the author.
+Documentation for the Linux sg device driver can be found at:
+https://sg.danny.cz/sg/p/sg_v3_ho.html . This is written in DocBook and the
+original xml can be found in the same directory with the ".xml" extension.
+Postscript and pdf renderings are also in that directory. Older documentation
+for the sg version 3 driver can be found at:
+https://sg.danny.cz/sg/p/scsi_generic_v3.txt .
+
+To save the repetition of common code (e.g. SCSI error processing) and
+reduce the size of the executable files, a shared library called
+libsgutils<num>.so (its Linux name) is created during the build process.
+That library is built from the contents of the include and lib
+subdirectories. The header files in the include subdirectory can be seen
+as the API of libsgutils and are commented with that in mind. The SCSI
+pass-through code for the supported operating systems is found in the lib
+subdirectory with names like sg_pt_linux.c and sg_pt_win32.c .
+
+Various distributions (of Linux mainly) distribute sg3_utils as 3
+installable packages. One is a package containing the shared library
+discussed above (e.g. libsgutils2-2_1.33-0.1_i386.deb). A second package
+contains the utilities (e.g. sg3-utils_1.33-0.1_i386.deb) and depends on the
+first package). Finally there is an optional package that contains header
+files and a static library (e.g. libsgutils2-dev_1.33-0.1_i386.deb). This
+final package is only needed to build other packages (e.g. sdparm) that
+wish to use the sg3_utils shared library.
+
+All the utilities in the src subdirectory have "man" pages that are
+placed in the doc subdirectory. There is also a sg3_utils (8) man page that
+summarizes common facilities including exit statuses. Additional
+information (including each utility's version number) can be found towards
+the top of each ".c" file corresponding to the utility name.
+
+The sg driver in Linux can be seen as having 3 distinct versions:
+
+   v1   lk < 2.2.6     sg_header based relatively unchanged since 1992
+   v2   lk >= 2.2.6    enhanced sg_header interface structure [1999/4/16]
+   v3   lk >= 2.4      additional sg_io_hdr interface structure [2001/1/4]
+   v3   lk >= 2.6      same interface as found in lk 2.4 [2.6.0: 2003/12/18]
+
+and the bsg driver supports the sg v4 interface and was added around
+lk 2.6.28 . This package is targeted at "v3" and "v4". Another package called
+"sg_utils" is targeted at "v2" and to a lesser extent "v1". The "sg_utils"
+package has a subset of the utilities found in this package.
+
+In Linux some sg driver ioctls (notably SG_IO) are defined for many block
+devices in lk 2.6 series. In practice this means all SCSI block devices,
+ATAPI block devices (mainly CD, DVD and BD optical devices) but _not_ ATA
+disks, depending on which kernel configuration options, can be accessed by
+the utilities in this package. SATA disks that use the libata kernel library
+(or some other SCSI to ATA Translation (SAT) Layer (SATL)) accept SCSI
+commands and thus are supported. Support for the SG_IO as been added to the
+scsi tape driver (st) in lk 2.6.6 .
+
+In the src directory the bulk of the utilities are written in relatively
+clean POSIX compliant C code with Linux specific system calls and structures
+removed and placed in Linux specific files in the lib directory. A small
+number of utilities in the src directory do contain Linux specific logic
+and are not ported to other OSes (e.g. sg_dd). One utility, sg_scan, has
+two separate implementations, one for Linux (sg_scan_linux.c) and one for
+Windows (sg_scan_win32.c). The src-lib directory split approach allows
+FreeBSD, Solaris, Tru64 and Windows specific code to be isolated to a few
+files in the lib directory whose interfaces match those of the Linux
+specific code.
+
+Darwin is not supported because the Apple folks do not want to give their
+users a pass-through SCSI interface. The author has read about creative
+hackers using a VM containing a real OS to circumvent the Apple restriction.
+
+C standard is C11
+==================
+The C code in this package is written for portability rather than speed.
+It assumes a level of C99 compliance (the C standard prior to C11) and
+favours POSIX system and library calls over OS specific calls.
+
+The C code is written in a C++ friendly way and is checked from time to
+time that it compiles clean with C++. To accommodate C++ certain C99
+constructs such as designated initializers cannot be used.
+
+The author has not seriously attempted to build this code on MSVC (aka
+Visual Studio). There are a few roadblocks (that may be overcome in the
+future) that include MSVC being basically a C++ compiler, not a C/C++
+compiler. For some reason MSVC only claims C89 compliance (i.e. the first
+C standard from 1989). MSVC 2013 and 2015 are moving closer to C99
+compliance and may be sufficient to compile this package. Another problem
+is the assumption of the availability of basic Unix system calls such as
+open(). Nearly 20 years ago Microsoft indicated (promised ?) that it
+would move in the direction of POSIX compliance, but very little ever
+happened. "Talk is cheap, there should be a tax on it."
+
+Building
+========
+This package is designed to be built with the usual:
+    "./configure ; make ; make install"
+sequence. In some situations that may need to be prefixed by a call to
+the "./autogen.sh" script which invokes autoconf and automake. That in turn
+may require packages containing those utilities to be installed. The
+libtool utility is also required. Naturally a C compiler is required
+and due to the vagaries of libtool a C++ compiler also.
+
+The "./configure" takes many command line options with the defaults
+being usually sufficient to start with. One quirk is that the location
+of the installation is under the /usr/local directory. So the sg_inq
+utility will be installed at /usr/local/bin/sg_inq . This is controlled
+by the "--prefix=<directory>" option which defaults to
+"--prefix=/usr/local". As an example to install the executables in /usr/bin
+and disable the creation of the shared library (libsgutils<num>.so) this
+invocation could be used: "./configure --prefix=/usr --disable-shared".
+To reduce the size of an executable as well try this:
+"./configure --prefix=/usr --disable-shared --disable-scsistrings".
+Also --disable-shared will produce (relatively) "static" executables in
+the src directory that are easier to debug. And
+"./configure --enable-debug" will compile with more debug type options,
+including more compiler checks and defining "DEBUG" within the src and
+lib source files. Most utilities in the src directory set '-vv' (i.e.
+equivalent to calling "--verbose" twice) when "DEBUG" is set.
+
+In Linux there are package build files for "rpm" based and for "deb" based
+systems. The 'sg3_utils.spec' file in the main directory can be used like
+this: 'rpmbuild -ba sg3_utils.spec' in a rpmbuild tree SPECS directory.
+To cross build or make a more widely distributable package then the --target
+option may be useful: 'rpmbuild --target=i386 -ba sg3_utils.spec' or
+'rpmbuild --target=x86_64 -ba sg3_utils.spec' . The sg3_utils.spec file
+in the main directory targets Red Hat systems, an alternative "spec" file
+for Suse systems has been placed under the 'suse' directory.
+
+The 'build_debian.sh' script should build several "deb" packages and place
+them in the parent directory. In debian based systems doing
+a 'apt-get install build-essential' is one way to get most of build
+environment needed if it has not already been loaded. There are now some
+problems with this script and the superseded Debian 4.0 ("etch"). See
+debian/README.debian4 for a workaround. Amongst other things debian
+builds are sensitive to the value in the debian/compat file. If it
+contains "7" then it works on lenny and gives warning on squeeze (but
+fails on the earlier etch).
+
+Warning
+=======
+Many devices use SCSI command sets over transport protocols not normally
+associated with SCSI (as defined at https://www.t10.org ). Some of these
+devices react poorly (e.g. lock up) when sent SCSI commands that they don't
+support. Even sending a supported SCSI command with a field set to an
+unexpected value can cause problems. [The author is talking about billions
+of USB devices with horrible SCSI implementations.]
+
+For example, all "SCSI" devices must support the INQUIRY command which the
+SCSI-2 standard says should request a 36 byte response. However later SCSI
+standards (e.g. SPC-2) have increased that length but some SCSI devices lock
+up when they receive a request for anything other than a 36 byte response.
+
+Any well implemented "SCSI" device should react sensibly when a utility in
+sg3_utils sends a SCSI command that it doesn't support. Unfortunately this
+cannot be guaranteed.
+
+Prior to lk 2.6.29 USB mass storage limited sense data to 18 bytes which
+caused problems for certain types of descriptor based sense data. An
+example of this is the SCSI ATA PASS-THROUGH command with the CK_COND bit
+set.
+
+
+Utilities
+=========
+Here is list in alphabetical order of utilities found in the 'src'
+subdirectory of the sg3_utils package:
+    sginfo, sg_bt_ctl, sg_compare_and_write, sg_copy_results, sgm_dd, sgp_dd,
+    sg_dd, sg_decode_sense, sg_emc_trespass, sg_format, sg_get_config,
+    sg_get_elem_status, sg_get_lba_status, sg_ident, sg_inq, sg_logs,
+    sg_luns, sg_map, sg_map26, sg_modes, sg_opcodes, sg_persist, sg_prevent,
+    sg_raw, sg_rbuf, sg_rdac, sg_read, sg_read_attr, sg_readcap,
+    sg_read_block_limits, sg_read_buffer, sg_read_long, sg_reassign,
+    sg_referrals, sg_rep_pip, sg_rep_zones, sg_request, sg_reset, sg_rmsn,
+    sg_rtpg, sg_safte, sg_sanitize, sg_sat_identify, sg_sat_phy_event,
+    sg_sat_read_gplog, sg_sat_set_features, sg_scan, sg_seek, sg_senddiag,
+    sg_ses, sg_ses_microcode, sg_start, sg_stpg, sg_stream_ctl, sg_sync,
+    sg_test_rwbuff, sg_timestamp, sg_turs, sg_unmap, sg_verify, sg_vpd,
+    sg_write_buffer, sg_write_long, sg_write_same, sg_write_verify,
+    sg_write_x, sg_wr_mode, sg_xcopy, sg_zone, sg_z_act_query
+
+Each of the above utilities depends on header files found in the 'include'
+subdirectory and library code found in the 'lib' subdirectory. Associated
+man pages are found in the 'doc' subdirectory. Additional programs found
+in the 'archive', 'examples' and 'utils' subdirectories in not build by the
+top level build infrastructure. Linux binary distributions of the sg3_utils
+package (e.g. "rpm" and debian packages) typically contain the shared
+library, the utilities found in the 'src' subdirectory, their associated man
+pages and some documentation files (e.g. README, INSTALL, CREDITS, COPYING
+and COVERAGE). See the INSTALL file for generic instructions about building
+with autotools (e.g. ./configure ).
+
+Man pages can be read (without building and installing the package) by
+going to the 'doc' subdirectory and executing something like this:
+ $ man ./sg_dd.8
+
+To see which SCSI commands (and ATA commands) are used by these utilities
+refer to the COVERAGE file.
+
+Here is a list in alphabetical order of utilities found in the 'examples'
+subdirectory:
+  - sg_excl, scsi_inquiry, sg_sat_chk_power, sg__sat_identify,
+    sg__sat_phy_event, sg__sat_set_features, sg_sat_smart_rd_data,
+    sg_simple1, sg_simple2, sg_simple3, sg_simple4, sg_simple5,
+    sg_simple16
+
+Also in that subdirectory is a script to test sg_persist, an example data
+file for sg_persist (called "transport_ids.txt") and an example data file for
+sg_reassign (called "reassign_addr.txt"). There are several scripts
+for 'sg_senddiag -pf -raw=-' that will put some SAS disk phys into
+a "compliant jitter tolerance pattern" (CJTPAT).
+
+The 'testing' subdirectory contains source and a Makefiles to test
+kernel pass-through and associated drivers, mainly for Linux. There is
+both C code (with the extension ".c") and C++ code (with the extension
+".cpp"). There is a "Makefile" to build the C + C++ code. The Makefile
+depends on some object files from the "lib" subdirectory. So a sequence
+like this may be required prior to invoking make: "cd <top_of_package> ;
+./configure ; cd lib ; make ; cd ../testing".
+
+Here is a list in alphabetical order of utilities found in the 'testing'
+subdirectory:
+  - bsg_queue_tst, sgh_dd (C++), sg_iovec_tst, sg_queue_tst, sg_sense_tst,
+    sg_tst_async (C++), sg_tst_context (C++), sg_tst_excl (C++),
+    sg_tst_excl2 (C++), sg_tst_excl3 (C++)
+
+The 'utils' subdirectory contains source and a Makefile to build "hxascdmp"
+which accepts binary data from stdin (or a file on the command line) and
+outputs an ASCII-HEX and ASCII representation of it. It is similar to the
+Unix od command. There is also code to sg_chk_asc.c which checks a given
+text file (typically a copy of https://www.t10.org/lists/asc-num.txt ) and
+checks it against the asc/ascq text strings held in sg_lib_data.c .
+
+The 'doc' subdirectory contains a README file containing the urls of
+various related documents.
+
+The 'scripts' subdirectory contains some Bourne (bash) shell scripts that
+rely on utilities in the main directory. One script uses the sdparm utility.
+These scripts are described in the scripts/README file and have usage
+messages.
+
+
+Notes for utilities without man pages
+=====================================
+These utils are found in the 'examples' subdirectory.
+
+The "scsi_inquiry" program shows the use of the SCSI_IOCTL_SEND_COMMAND
+ioctl to send a SCSI INQUIRY command. That ioctl() is supported by the
+SCSI sub system mid level and so is common to all sd, sr, st and sg devices.
+That ioctl is deprecated in the lk 2.6 series. This program has been placed
+in the "examples" subdirectory.
+
+"sg_simple1" and "sg_simple2" are example programs demonstrating calls
+to the SCSI INQUIRY and TEST UNIT READY commands. They only differ in their
+error processing: sg_simple1 uses sg_lib.[hc] for error processing while
+sg_simple2 does its own more primitive checks.
+
+"sg_simple3" tests out user space scatter gather added to the version 3
+sg driver.
+
+"sg_simple4" shows the INQUIRY command using mmap-ed IO to obtain its
+response buffer.
+
+"sg_simple5" also sends and INQUIRY and TEST UNIT READY commands. It
+uses the generic pass through mechanism based on sg_pt.h . It will
+currently build in Linux and FreeBSD (with "make -f Makefile.freebsd").
+It has extensive error checking code.
+
+"sg_simple16" attempts to send a 16 byte SCSI command, READ_16, to the
+scsi device. This is only supported for lk >= 2.4.15 and for adapter
+drivers that indicate that they have 16 byte CDB capability (otherwise
+DID_ABORT will appear in the host_status).
+
+"sg_sat_chk_power" attempts to push an ATA CHECK POWER MODE command
+through the SAT-defined ATA PASS_THROUGH (16) SCSI command. That
+ATA command needs to read the "FIS" registers after the command is
+completed which involves using the ATA Status Return (sense data)
+descriptor (as defined in SAT).
+
+"sg_sat_smart_rd_data" attempts to push an ATA SMART/READ DATA command
+through the SAT-defined ATA PASS_THROUGH (16) SCSI command. If
+successful, the 256 word (512 byte) response is output.
+
+"sg_tst_excl" and "sg_tst_excl2" use multiple threads to bombard the
+given device with O_EXCL open flags, so only one should succeed at a
+time. While holding O_EXCL control a thread attempts a double increment
+on an integer in the given LBA. If the integer starts even (after the
+first read) then it should remain even if the O_EXCL flag is doing its job.
+The "sg_tst_excl" variant uses the Linux SG_IO v3 interface while the
+"sg_tst_excl2" uses the more generic sg_pt infrastructure.
+
+"sg_tst_excl3" is a variant of "sg_tst_excl2". "sg_tst_excl3" only does
+the double increment from the first thread, each time using O_EXCL on
+open. The remaining threads check the value is even, each time doing
+an open without the O_EXCL flag.
+
+"bsg_queue_tst" sends an INQUIRY command via the Linux SG_IO v4 interface
+which is used by the bsg driver. So it will take device names like
+"/dev/bsg/6:0:0:0". It tests if sending repeated INQUIRYs with
+the BSG_FLAG_Q_AT_HEAD or BSG_FLAG_Q_AT_TAIL flag makes any difference.
+
+"sg_tst_async" is a test harness for the Linux sg driver. It is multi
+threaded, submitting either TEST UNIT READY, READ(16) or WRITE(16) SCSI
+commands asynchronously. Each thread opens a file descriptor and submits
+those commands up to the queue limit (sg driver has a per file descriptor
+queue limit of 16). Multiple threads doing the same thing act as a
+multiplier to that queue limit.
+
+
+NVME Support
+============
+Firstly the author has no intention of extending this package to contain
+general purpose NVMe utilities. That leaves the areas where SCSI overlaps
+with NVMe. There was a SCSI to NVMe Translation Layer (SNTL) driver in the
+Linux kernel based on a white paper from NVM Express. Intel has withdrawn
+that driver and T10 (SCSI) and NVM Express have made no further attempts
+to standardize a SNTL. Given the SCSI to ATA Translation Layer (SATL) which
+is standardized by T10, it is pretty clear what a SNTL should do.
+
+The NVMe Management Interface (NVME-MI) committee have decided to use SES-3
+standard from T10 via the newly added SES Send and SES Receive MI commands.
+So the sg_ses utility and this package's library have been extended to use
+these commands when a NVMe device (typically a disk enclosure) is detected.
+This has been tested by a disk vendor who is happy with the results. Other
+user reports are welcome as the author does not have equipment to test
+this.
+
+Other utilities in this package that use the SES Send and Receive commands,
+or the SNTL in the library are sg_senddiag, sg_inq, sg_raw and sg_readcap.
+
+
+Command line processing
+=======================
+These utilities can be divided into 3 groups when their handling of command
+line arguments is considered:
+  - ad hoc, typically in a short form only, sometimes longer (e.g.
+    "sg_logs -pcb /dev/sdc")
+  - inspired by the dd Unix command (e.g. sg_dd, sgm_dd, sgp_dd, sg_read)
+  - recent utilities use "getopt_long" (see "man getopt_long")
+    type command lines. These have short form (starting with "-")
+    and corresponding longer form (starting with "--") options.
+
+The older utilities that use ad hoc options, in alphabetical order:
+  - sg_emc_trespass, sginfo(1/2), sg_inq, sg_logs, sg_map, sg_modes,
+    sg_opcodes, sg_rbuf, sg_rdac, sg_readcap, sg_reset, sg_scan (Linux),
+    sg_senddiag, sg_start, sg_test_rwbuf, sg_turs
+In sg3_utils version 1.23 the following utilities from this group were
+converted to have a dual getopt_long/ad_hoc interface, defaulting to
+the getop_long interface:
+  - sg_inq, sg_logs, sg_modes, sg_opcodes, sg_rbuf, sg_readcap,
+    sg_senddiag, sg_start, sg_turs
+These can be switched back to the older (backward compatible) ad hoc
+interface by defining the SG3_UTILS_OLD_OPTS environment variable
+or using '-O' as the first command line option.
+
+The more recent utilities that use "getopt_long" only are:
+  - sg_bt_ctl, sg_compare_and_write, sg_decode_sense, sg_format,
+    sg_get_config, sg_get_lba_status, sg_ident, sg_luns, sg_map26,
+    sg_persist, sg_prevent, sg_raw, sg_read_attr, sg_read_block_limits,
+    sg_read_buffer, sg_read_long, sg_reassign, sg_referrals, sg_rep_pip,
+    sg_rep_zones, sg_requests, sg_rmsn, sg_rtpg, sg_safte, sg_sanitize,
+    sg_sat_identify, sg_sat_phy_event, sg_sat_read_gplog,
+    sg_sat_set_features, sg_scan(w), sg_seek, sg_ses, sg_ses_microcode,
+    sg_stpg, sg_stream_ctl, sg_sync, sg_test_rwbuf, sg_timestamp, sg_unmap,
+    sg_verify, sg_vpd, sg_write_buffer, sg_write_long, sg_write_same,
+    sg_write_verify, sg_write_x, sg_wr_mode, sg_zone, sg_z_act_query
+
+
+Dangerous code
+==============
+This C code snippet:
+    unsigned char uc = 0x80;
+    uint64_t ull;
+    ull = (uc << 24);
+Somewhat surprisingly sets ull to:
+    ull: 0xffffffff80000000
+This result is due to the 'unary conversion' of uc to a (32 bit signed)
+'int' before the shift. The resultant type from the shift is also an int
+and it has its top bit set so there is sign extension when it is assigned
+into a 64 bit unsigned integer. Making sure there is no conversion to 'int'
+solves the problem. In this case if uc is declared as unsigned int the
+result will be as expected (i.e. 0x80000000).
+
+
+Bypassing the somewhat dangerous shift operators
+================================================
+The shift operators in C are "<<" and ">>". They can be dangerous (as shown
+in the above section) or tedious and hence error prone to use. However they
+are often needed to cope with the translation of integers on the host OS to
+the corresponding representation within a SCSI command or parameter data
+moved to or from a SCSI device. The Logical Block Address (LBA) is a good
+example; it is either 32 or 64 bits long typically (i.e. 4 or 8 bytes
+respectively). The host machine representation may be big or little endian
+and may prefer or require alignment to a particular memory address boundary
+(e.g. module 4 (or in 'C' code: "(lba % 4) == 0")). For SCSI commands and
+the parameter data moved to or from a SCSI device, the integer
+representation is big endian and it is unaligned.
+
+Recent versions of this package have replaced the explicit use of the C
+shift operators with a group of functions modelled on those found in the
+Linux kernel. These functions contain either "get_unaligned" or
+"put_unaligned" in their names and are found in the asm/unaligned.h
+header. This package contains the sg_unaligned.h header that implements
+a similar set of functions. The current implementation favours correctness
+over speed. The functions in the package use a "sg_" prefix but otherwise
+use the same function name as the Linux kernel for the same action.
+
+An example of the change made to a snippet of sg_write_buffer.c may
+clarify this change. The old code was:
+
+   wbufCmdBlk[3] = (unsigned char)((buffer_offset >> 16) & 0xff);
+   wbufCmdBlk[4] = (unsigned char)((buffer_offset >> 8) & 0xff);
+   wbufCmdBlk[5] = (unsigned char)(buffer_offset & 0xff);
+
+and it has been replaced by:
+
+   sg_put_unaligned_be24(buffer_offset, wbufCmdBlk + 3);
+
+The Linux kernel only supplies "unaligned" functions for 16, 32 and 64
+bit quantities. SCSI commands also have cases of 24 and 48 bit numbers
+so sg_unaligned.h contains support for those plus a variant where the
+byte length is passed as an argument.
+
+The unaligned functions are inlined for speed (at the possible expense of
+space) and now have specializations depending whether the host is big or
+(more likely) little endian. These functions can be broken down to a
+memcpy() and optionally a byte-swap for 16, 32 and 64 bit operations.
+The memcpy() takes care of alignment while the byte-swap (bswap_16(),
+bswap_32() and bswap_64() ) addresses integer endianness. If the host is
+little endian and a little endian variant of the unaligned functions is
+requested, then no byte-swap is required. These specializations can be
+"compiled out" with this configure option: './configure
+--disable-fast-lebe' in which case the classic "C shifting" technique is
+used to implement all the unaligned functions.
+
+Associated with the above change, fixed length integer types seem a better
+fit for SCSI command and parameter integers than the traditional integer
+types in the C language. Fixed length integer types were standardized in
+C99 and require the inclusion of <stdint.h>. For example this means for
+an integer that will represent a 64 bit LBA, to favour using "uint64_t"
+over the "unsigned long long" type. Also "unsigned char" has mostly been
+replaced by "uint8_t" as the 8 bit (unsigned) byte type; "char" is still
+used for ASCII text.
+
+
+Other SCSI and storage tools
+============================
+See https://sg.danny.cz/sg/tools.html
+
+
+Douglas Gilbert		dgilbert@interlog.com
+18th November 2021
diff --git a/debian/changelog b/debian/changelog
index 13e8c88..8fcefd2 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,7 +2,7 @@
 
   * New upstream version
 
- -- Douglas Gilbert <dgilbert@interlog.com>  Mon, 15 Nov 2021 11:00:00 -0500
+ -- Douglas Gilbert <dgilbert@interlog.com>  Sat, 20 Nov 2021 11:00:00 -0500
 
 sg3-utils (1.47-0.1) unstable; urgency=low
 
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 22e659e..3cb12eb 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -14,7 +14,7 @@
 	sg_ses.8 sg_ses_microcode.8 sg_start.8 sg_stpg.8 sg_stream_ctl.8 \
 	sg_sync.8 sg_timestamp.8 sg_turs.8 sg_unmap.8 sg_verify.8 sg_vpd.8 \
 	sg_wr_mode.8 sg_write_buffer.8 sg_write_long.8 sg_write_same.8 \
-	sg_write_verify.8 sg_write_x.8 sg_zone.8
+	sg_write_verify.8 sg_write_x.8 sg_zone.8 sg_z_act_query.8
 CLEANFILES =
 
 if OS_LINUX
diff --git a/doc/Makefile.in b/doc/Makefile.in
index cce580f..c1e2bdb 100644
--- a/doc/Makefile.in
+++ b/doc/Makefile.in
@@ -301,7 +301,8 @@
 	sg_sync.8 sg_timestamp.8 sg_turs.8 sg_unmap.8 sg_verify.8 \
 	sg_vpd.8 sg_wr_mode.8 sg_write_buffer.8 sg_write_long.8 \
 	sg_write_same.8 sg_write_verify.8 sg_write_x.8 sg_zone.8 \
-	$(am__append_1) $(am__append_3) $(am__append_5)
+	sg_z_act_query.8 $(am__append_1) $(am__append_3) \
+	$(am__append_5)
 CLEANFILES = $(am__append_2) $(am__append_4) $(am__append_6)
 all: all-am
 
diff --git a/doc/sg3_utils.8 b/doc/sg3_utils.8
index 5fcbf75..abe07d8 100644
--- a/doc/sg3_utils.8
+++ b/doc/sg3_utils.8
@@ -1,4 +1,4 @@
-.TH SG3_UTILS "8" "November 2021" "sg3_utils\-1.47" SG3_UTILS
+.TH SG3_UTILS "8" "November 2021" "sg3_utils\-1.48" SG3_UTILS
 .SH NAME
 sg3_utils \- a package of utilities for sending SCSI commands
 .SH SYNOPSIS
@@ -34,8 +34,8 @@
 Architecture Model (SAM) with SAM\-5 being the most recent standard (ANSI
 INCITS 515\-2016) with the most recent draft being SAM\-6 revision 4 . SCSI
 commands in common with all device types can be found in SCSI Primary
-Commands (SPC) of which SPC\-4 is the most recent standard (ANSI INCITS
-513-2015). The most recent SPC draft is SPC\-5 revision 21. Block device
+Commands (SPC) of which SPC\-5 is the most recent standard (ANSI INCITS
+502-2020). The most recent SPC draft is SPC\-6 revision 6. Block device
 specific commands (e.g. as used by disks) are in SBC, those for tape drives
 in SSC, those for SCSI enclosures in SES and those for CD/DVD/BD drives in
 MMC.
@@ -64,7 +64,7 @@
 The \fIDEVICE\fR argument may appear after, between or prior to any options.
 .PP
 The older utilities, including as sg_inq, sg_logs, sg_modes, sg_opcode,
-sg_rbuff,  sg_readcap, sg_senddiag, sg_start and sg_turs had individual
+sg_rbuff, sg_readcap, sg_senddiag, sg_start and sg_turs had individual
 command line processing code typically based on a single "\-" followed by one
 or more characters. If an argument is needed then it follows a "=" (
 e.g. '\-p=1f' in sg_modes with its older interface). Various options can be
@@ -575,10 +575,14 @@
 output the usage message then exit.
 .TP
 \fB\-H\fR, \fB\-\-hex\fR
-for SCSI commands that yield a non\-trivial response, print out that
-response in ASCII hexadecimal. To produce hexadecimal that can be parsed
-by other utilities (e.g. without a relative address to the left and without
-trailing ASCII) use this option three or four times.
+for SCSI commands that yield a non\-trivial response, print out that response
+in ASCII hexadecimal. When used once, 16 bytes are printed on each line,
+prefixed by an relative address, starting at 0 (hex). When used twice, an
+ASCII rendering of the 16 bytes is appended to each line, with non printeble
+characters replaced by a '.' . When used three times only the 16 hex bytes
+are printed on each line (hence no address prefix nor ASCII appended). To
+produce hexadecimal that can be parsed by other utilities use this option
+three or four times.
 .TP
 \fB\-i\fR, \fB\-\-in\fR=\fIFN\fR
 many SCSI commands fetch a significant amount of data (returned in the
@@ -744,6 +748,12 @@
 line. Users are encouraged to use hash marks to introduce comments in hex
 files. The author uses the extension'.hex' on such files. Examples can be
 found in the 'inhex' directory.
+.PP
+The hexadecimal format described in the previous paragraph can be converted
+to binary using the sg_decode_sense utility with these
+options: "\fI\-\-inhex=HFN \-\-nodecode \-\-write=WFN\fR". The input (in
+hex) is in the \fIHFN\fR file while the output is placed in the \fIWFN\fR
+file.
 .SH MICROCODE AND FIRMWARE
 There are two standardized methods for downloading microcode (i.e. device
 firmware) to a SCSI device. The more general way is with the SCSI WRITE
@@ -777,7 +787,7 @@
 https://github.com/hreinecke/sg3_utils . The principle code repository
 uses subversion and is on the author's equipment. The author keeps track
 of this via the subversion revision number which is an ascending integer
-(currently at 774 for this package). The github mirror gets updated
+(currently at 922 for this package). The github mirror gets updated
 periodically from the author's repository. Depending on the time of
 update, the above Downloads section at sg.danny.cz may be more up to
 date than the github mirror.
@@ -796,4 +806,5 @@
 under a FreeBSD license. There is NO warranty; not even for MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.
 .SH "SEE ALSO"
-.B sdparm(sdparm), ddpt(ddpt), lsscsi(lsscsi), dmesg(1), mt(1)
+.B sg_decode_sense(sg3_utils), sdparm(sdparm), ddpt(ddpt), lsscsi(lsscsi),
+.B dmesg(1), mt(1)
diff --git a/doc/sg_decode_sense.8 b/doc/sg_decode_sense.8
index 62d4c54..7c952ed 100644
--- a/doc/sg_decode_sense.8
+++ b/doc/sg_decode_sense.8
@@ -1,18 +1,18 @@
-.TH SG_DECODE_SENSE "8" "November 2021" "sg3_utils\-1.47" SG3_UTILS
+.TH SG_DECODE_SENSE "8" "November 2021" "sg3_utils\-1.48" SG3_UTILS
 .SH NAME
 sg_decode_sense \- decode SCSI sense and related data
 .SH SYNOPSIS
 .B sg_decode_sense
 [\fI\-\-binary=BFN\fR] [\fI\-\-cdb\fR] [\fI\-\-err=ES\fR] [\fI\-\-file=HFN\fR]
-[\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-inhex=HFN\fR] [\fI\-\-nospace\fR]
-[\fI\-\-status=SS\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR]
-[\fI\-\-write=WFN\fR] [H1 H2 H3 ...]
+[\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-inhex=HFN\fR] [\fI\-\-nodecode\fR]
+[\fI\-\-nospace\fR] [\fI\-\-status=SS\fR] [\fI\-\-verbose\fR]
+[\fI\-\-version\fR] [\fI\-\-write=WFN\fR] [H1 H2 H3 ...]
 .SH DESCRIPTION
 .\" Add any additional description here
-This utility takes SCSI sense data in binary or as a sequence of
-ASCII hexadecimal bytes and decodes it. The primary reference for the
-decoding is SPC\-4 ANSI INCITS 513\-2015 and the most recent draft
-SPC\-5 revision 19 which can be found at https://www.t10.org and other
+This utility takes SCSI sense data in binary or as a sequence of ASCII
+hexadecimal bytes and decodes it. The primary reference for the
+decoding is SPC\-5 ANSI INCITS 502\-2020 and the most recent draft
+SPC\-6 revision 6 which can be found at https://www.t10.org and other
 locations on the internet.
 .PP
 SCSI sense data is often found in kernel log files as a result of
@@ -74,15 +74,23 @@
 output the usage message then exit.
 .TP
 \fB\-H\fR, \fB\-\-hex\fR
-this option is used in conjunction with \fI\-\-write=WFN\fR in order to
+this option is used once in conjunction with \fI\-\-write=WFN\fR in order to
 change the output written to \fIWFN\fR to lines of ASCII hex bytes suitable
 for a C language compiler. Each line contains up to 16 bytes (e.g. a line
 starting with "0x3b,0x07,0x00,0xff").
+.br
+In other cases (i.e. when \fI\-\-write=WFN\fR is not given, or this option
+is given more than once) then the output is as described in the sg3_utils(8)
+manpage.
 .TP
 \fB\-i\fR, \fB\-\-inhex\fR=\fIHFN\fR
 same action as \fI\-\-file=HFN\fR. This option was added for compatibility
 with other utilities in this package that have a \fI\-\-inhex=\fR option.
 .TP
+\fB\-N\fR, \fB\-\-nodecode\fR
+Do not decode the given data as sense or a cdb. Useful when arbitrary data
+is given (e.g. when converting hex to binary).
+.TP
 \fB\-n\fR, \fB\-\-nospace\fR
 expect ASCII hexadecimal to be a string of hexadecimal digits with no
 spaces between them. Bytes are decoded by taking two hexadecimal digits
@@ -121,6 +129,15 @@
 The sg_raw utility takes a ASCII hexadecimal sequence representing a SCSI
 CDB. When sg_raw is given the '\-vvv' option, it will attempt to decode the
 CDB name.
+.PP
+Using the option combination: "\fI\-\-inhex=HFN \-\-nodecode \-\-write=WFN\fR"
+may be used to convert hexadecimal (as produced by this and other utilities
+in this package) to binary where the output file is \fIWFN\fR.
+.PP
+Unlike many other utilities there is no \fI\-\-raw\fR option. However binary
+data can be input using the \fI\-\-binary=BFN\fR option while binary data
+can be output using the \fI\-\-write=WFN\fR option (in the absence of the
+\fI\-\-hex\fR option).
 .SH EXAMPLES
 Sense data is often printed out in kernel logs and sometimes on the
 command line when verbose or debug flags are given. It will be at least
diff --git a/doc/sg_rep_zones.8 b/doc/sg_rep_zones.8
index bd25fa4..d438971 100644
--- a/doc/sg_rep_zones.8
+++ b/doc/sg_rep_zones.8
@@ -1,13 +1,13 @@
-.TH SG_REP_ZONES "8" "June 2021" "sg3_utils\-1.47" SG3_UTILS
+.TH SG_REP_ZONES "8" "November 2021" "sg3_utils\-1.48" SG3_UTILS
 .SH NAME
 sg_rep_zones \- send SCSI REPORT ZONES, REALMS or ZONE DOMAINS command
 .SH SYNOPSIS
 .B sg_rep_zones
-[\fI\-\-domain\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-inhex=FN\fR]
-[\fI\-\-locator=LBA\fR] [\fI\-\-maxlen=LEN\fR] [\fI\-\-num=NUM\fR]
-[\fI\-\-partial\fR] [\fI\-\-raw\fR] [\fI\-\-readonly\fR] [\fI\-\-realm\fR]
-[\fI\-\-report=OPT\fR] [\fI\-\-start=LBA\fR] [\fI\-\-verbose\fR]
-[\fI\-\-version\fR] [\fI\-\-wp\fR] \fIDEVICE\fR
+[\fI\-\-domain\fR] [\fI\-\-force\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR]
+[\fI\-\-inhex=FN\fR] [\fI\-\-locator=LBA\fR] [\fI\-\-maxlen=LEN\fR]
+[\fI\-\-num=NUM\fR] [\fI\-\-partial\fR] [\fI\-\-raw\fR] [\fI\-\-readonly\fR]
+[\fI\-\-realm\fR] [\fI\-\-report=OPT\fR] [\fI\-\-start=LBA\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] [\fI\-\-wp\fR] \fIDEVICE\fR
 .SH DESCRIPTION
 .\" Add any additional description here
 .PP
@@ -33,6 +33,12 @@
 \fB\-d\fR, \fB\-\-domain\fR
 send or decode the SCSI REPORT ZONE DOMAINS command.
 .TP
+\fB\-f\fR, \fB\-\-force\fR
+when decoding the response to this command, certain sanity checks are
+done and if they fail a message is sent to stderr and a non\-zero
+exit status is set. If this option is given those sanity checks are
+bypassed.
+.TP
 \fB\-h\fR, \fB\-\-help\fR
 output the usage message then exit.
 .TP
diff --git a/doc/sg_z_act_query.8 b/doc/sg_z_act_query.8
new file mode 100644
index 0000000..a43ad07
--- /dev/null
+++ b/doc/sg_z_act_query.8
@@ -0,0 +1,115 @@
+.TH SG_Z_ACT_QUERY "8" "November 2021" "sg3_utils\-1.48" SG3_UTILS
+.SH NAME
+sg_z_act_query \- send a SCSI ZONE ACTIVATE or ZONE QUERY command
+.SH SYNOPSIS
+.B sg_z_act_query
+[\fI\-\-activate\fR] [\fI\-\-all\fR] [\fI\-\-force\fR] [\fI\-\-help\fR]
+[\fI\-\-hex\fR] [\fI\-\-inhex=FN\fR] [\fI\-\-maxlen=LEN\fR]
+[\fI\-\-num=ZS\fR] [\fI\-\-other=ZDID\fR] [\fI\-\-query\fR] [\fI\-\-raw\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] [\fI\-\-zone=ID\fR]
+\fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Sends a SCSI ZONE ACTIVATE or ZONE QUERY command to the \fIDEVICE\fR. If the
+\fI\-\-activate\fR option is not given, then a ZONE QUERY command is sent.
+These commands where added in the ZBC\-2 draft revision 4 (zbc2r04.pdf).
+.PP
+Both of these commands have similar cdb_s and responses hence they are both
+placed in this utility. The difference is that only the ZONE ACTIVATE command
+will potentially activate or deactivate zones. Both commands will perform
+a "Verify activations operation" as defined in ZBC\-2 .
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-A\fR, \fB\-\-activate\fR
+sends a ZONE ACTIVATE command to the \fIDEVICE\fR. The default (i.e. without
+this option) is to send a ZONE QUERY command.
+.TP
+\fB\-a\fR, \fB\-\-all\fR
+sets the ALL field in the cdb.
+.TP
+\fB\-f\fR, \fB\-\-force\fR
+when decoding the response to this command, certain sanity checks are
+done and if they fail a message is sent to stderr and a non\-zero
+exit status is set. If this option is given those sanity checks are
+bypassed.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+output the response in hexadecimal to stdout. When used once the whole
+response is output in ASCII hexadecimal with a leading address (starting at
+0) on each line. When used twice each zone activation descriptor in the
+response is output separately in hexadecimal. When used thrice the whole
+response is output in hexadecimal with no leading address (on each line).
+.br
+The output format when this option is given thrice is suitable contents
+for a later invocation with the \fI\-\-inhex=FN\fR option.
+.TP
+\fB\-i\fR, \fB\-\-inhex\fR=\fIFN\fR
+where \fIFN\fR is a file name whose contents are assumed to be ASCII
+hexadecimal. If \fIDEVICE\fR is also given then \fIDEVICE\fR is ignored,
+a warning is issued and the utility continues, decoding the file named
+\fIFN\fR. See the "FORMAT OF FILES CONTAINING ASCII HEX" section in the
+sg3_utils manpage for more information. If the \fI\-\-raw\fR option is
+also given then the contents of \fIFN\fR are treated as binary.
+.br
+By default it is assumed the response is from a ZONE QUERY command but
+that shouldn't matter because the response of the ZONE ACTIVATE and
+ZONE QUERY commands is of the same form.
+.TP
+\fB\-m\fR, \fB\-\-maxlen\fR=\fILEN\fR
+where \fILEN\fR is the (maximum) response length in bytes. It is placed in
+the cdb's "allocation length" field. If not given (or \fILEN\fR is zero)
+then 8192 is used. The maximum allowed value of \fILEN\fR is 1048576.
+.br
+The draft standard disallows allocation lengths less than 64.
+.TP
+\fB\-n\fR, \fB\-\-num\fR=\fIZS\fR
+where \fIZS\fR is placed in the "Number of zones" field in the cdb. This
+option is usually ignored if the \fI\-\-all\fR option is given. If the
+\fI\-\-all\fR option is not given, the default value of this field is 1 .
+.TP
+\fB\-o\fR, \fB\-\-other\fR=\fIZDID\fR
+where the \fIZDID\fR value will be placed in the "Other zone domain ID"
+field of the cdb to be sent to the \fIDEVICE\fR.
+.TP
+\fB\-q\fR, \fB\-\-query\fR
+causes the ZONE QUERY command to be sent to the \fIDEVICE\fR. Since this
+is the default action, this option is typically not needed. If both this
+option and the \fI\-\-activate\fR option are given, an error will be
+reported (and no command will be sent).
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+output response in binary (to stdout) unless the \fI\-\-inhex=FN\fR option
+is also given. In that case the input file name (\fIFN\fR) is decoded as
+binary (and the output is _not_ in binary (but may be hex)).
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.TP
+\fB\-z\fR, \fB\-\-zone\fR=\fIID\fR
+where \fIID\fR is placed in the cdb's ZONE ID field. A zone id is a zone
+start logical block address (LBA). The default value is 0. \fIID\fR is
+assumed to be in decimal unless prefixed with '0x' or has a trailing 'h'
+which indicate hexadecimal. The maximum value that can be given is
+2^64 - 2. In the unlikely event of wanting to give 2^64 - 1, enter "\-1".
+.SH EXIT STATUS
+The exit status of sg_z_act_query is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 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 "SEE ALSO"
+.B sg_zone,sg_rep_zones,sg_reset_wp(sg3_utils)
diff --git a/doc/sg_zone.8 b/doc/sg_zone.8
index bc91642..cf35c16 100644
--- a/doc/sg_zone.8
+++ b/doc/sg_zone.8
@@ -1,4 +1,4 @@
-.TH SG_ZONE "8" "January 2021" "sg3_utils\-1.43" SG3_UTILS
+.TH SG_ZONE "8" "November 2021" "sg3_utils\-1.48" SG3_UTILS
 .SH NAME
 sg_zone \- send a SCSI ZONE modifying command
 .SH SYNOPSIS
@@ -18,6 +18,11 @@
 .PP
 One and only one of the \fI\-\-open\fR, \fI\-\-close\fR, \fI\-\-finish\fR,
 \fI\-\-remove\fR and \fI\-\-sequentialize\fR options can be chosen.
+.PP
+The REPORT ZONES, REPORT REALMS and REPORT ZONE DOMAINS commands may be
+accessed via the sg_rep_zones utility. The ZONE ACTIVATE and ZONE QUERY
+commands may be accessed via the sg_z_act_query utility. The RESET WRITE
+POINTER command may be accessed via the sg_reset_wp utility.
 .SH OPTIONS
 Arguments to long options are mandatory for short options as well.
 .TP
@@ -78,4 +83,4 @@
 This software is distributed under a FreeBSD license. There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 .SH "SEE ALSO"
-.B sg_rep_zones,sg_reset_wp(sg3_utils)
+.B sg_rep_zones,sg_reset_wp,sg_z_act_query(sg3_utils)
diff --git a/include/sg_lib.h b/include/sg_lib.h
index 35b44d6..94c98fa 100644
--- a/include/sg_lib.h
+++ b/include/sg_lib.h
@@ -567,7 +567,8 @@
 /* Print (to stdout) 'str' of bytes in hex, 16 bytes per line optionally
  * followed at the right hand side of the line with an ASCII interpretation.
  * Each line is prefixed with an address, starting at 0 for str[0]..str[15].
- * All output numbers are in hex. 'no_ascii' allows for 3 output types:
+ * All output numbers are in hex.
+ * 'no_ascii' selects on of  3 output format types:
  *     > 0     each line has address then up to 16 ASCII-hex bytes
  *     = 0     in addition, the bytes are listed in ASCII to the right
  *     < 0     only the ASCII-hex bytes are listed (i.e. without address)
@@ -579,6 +580,12 @@
  * logic as dStrHex() with different output stream (i.e. stderr). */
 void dStrHexErr(const char * str, int len, int no_ascii);
 
+/* Read binary starting at 'str' for 'len' bytes and output as ASCII
+ * hexadecinal into file pointer (fp). 16 bytes per line are output with an
+ * additional space between 8th and 9th byte on each line (for readability).
+ * 'no_ascii' selects one of 3 output format types as shown in dStrHex() . */
+void dStrHexFp(const char* str, int len, int no_ascii, FILE * fp);
+
 /* 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
diff --git a/include/sg_lib_data.h b/include/sg_lib_data.h
index 01c132e..1e42704 100644
--- a/include/sg_lib_data.h
+++ b/include/sg_lib_data.h
@@ -2,7 +2,7 @@
 #define SG_LIB_DATA_H
 
 /*
- * Copyright (c) 2007-2019 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.
@@ -46,7 +46,9 @@
 #define SG_VARIABLE_LENGTH_CMD 0x7f
 #define SG_WRITE_BUFFER 0x3b
 #define SG_ZONING_OUT 0x94
+#define SG_ZBC_OUT SG_ZONING_OUT	/* as SPC calls them */
 #define SG_ZONING_IN 0x95
+#define SG_ZBC_IN SG_ZONING_IN		/* as SPC calls them */
 
 
 
diff --git a/inhex/README b/inhex/README
index cdc2e1a..42337e0 100644
--- a/inhex/README
+++ b/inhex/README
@@ -41,10 +41,42 @@
       SCSI name string:
       naa.5000C5003011CB28
 
+Binary <--> Hexadecimal
+-----------------------
+The vpd_zbdc.raw file is binary and was created by:
+    sg_decode_sense --inhex=vpd_zbdc.hex --nodecode --write=vpd_zbdc.raw
+as an example of converting a file in ASCII hexadecimal byte oriented
+format to binary.
 
+Turning binary output into hexadecimal can be done several ways. For
+viewing in byte oriented ASCII hex these Unix commands can be used:
+    od -t x1 vpd_zbdc.raw
+    hexdump -C vpd_zbdc.raw
+
+Each line starting with a "input offset" which is a running count of
+bytes, starting at zero. The hexdump examples shows an ASCII rendering
+of those 16 bytes to the right of each line. The sg_decode_sense utility
+may also be used:
+    sg_decode_sense --binary=vpd_zbdc.raw -H 
+    sg_decode_sense --binary=vpd_zbdc.raw -HH 
+
+The second form of sg_decode_sense appends an ASCII rendering of the 16
+bytes to the right of each line.
+
+When ASCII hexadecimal is being used as input to a utility in this
+package, the "input offset" at the start of each line must (and the
+optional ASCII rendering to the right of each line, must not be given.
+That can be done with hexdump:
+   hexdump -An -C -v vpd_zbdc.raw
+
+And the sg_decode_sense utility can do it with:
+   sg_decode_sense --binary=vpd_zbdc.raw -HHH
+
+Conclusion
+----------
 Users are encouraged to send the author any ASCII hex files for utilities
 that support --inhex and don't have hex data already. Special cases are
 also welcome. They help the author test this code.
 
 Douglas Gilbert
-25th September 2021
+20th November 2021
diff --git a/inhex/vpd_zbdc.raw b/inhex/vpd_zbdc.raw
new file mode 100644
index 0000000..249dbd5
--- /dev/null
+++ b/inhex/vpd_zbdc.raw
Binary files differ
diff --git a/inhex/z_act_query.hex b/inhex/z_act_query.hex
new file mode 100644
index 0000000..16d094e
--- /dev/null
+++ b/inhex/z_act_query.hex
@@ -0,0 +1,28 @@
+# This is the output (in hex) of a simulated SCSI ZONE QUERY command.
+#
+# The hex bytes in this file may be generated by:
+#     sg_z_act_query /dev/sg1 -HHH > /tmp/z_act_query.hex
+# where /dev/sg1 was a SCSI device implementing ZBC-2.
+
+# An example invocation:
+#    sg_z_act_query --inhex=z_act_query.hex
+
+
+# parameter data header (64 bytes)
+00 00 00 80 00 00 00 80  80 00 03 00 00 00 00 00
+00 00 00 04 00 00 00 00  00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+
+# first zone activation descriptor, zone type: conventional
+01 00 01 00 00 00 00 00  00 00 00 00 00 00 00 04
+00 00 00 20 00 00 00 00  00 00 00 00 00 00 00 00
+# second zone activation descriptor, zone type: sequential write required
+02 10 02 00 00 00 00 00  00 00 00 00 00 00 00 05
+00 00 00 40 00 00 00 00  00 00 00 00 00 00 00 00
+# third zone descriptor, zone type: sequential write required
+02 10 03 00 00 00 00 00  00 00 00 00 00 00 00 06
+00 00 00 60 00 00 00 00  00 00 00 00 00 00 00 00
+# fourth and last zone activation descriptor, zone type: sequential write required
+02 10 04 00 00 00 00 00  00 00 00 00 00 00 00 07
+00 00 00 80 00 00 00 00  00 00 00 00 00 00 00 00
diff --git a/lib/sg_cmds_basic.c b/lib/sg_cmds_basic.c
index 844018c..e177354 100644
--- a/lib/sg_cmds_basic.c
+++ b/lib/sg_cmds_basic.c
@@ -379,7 +379,7 @@
     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 sense_b[SENSE_BUFF_LEN] = {0};
     uint8_t * up;
 
     if (resp == NULL) {
@@ -738,7 +738,7 @@
     static const char * const rq_s = "request sense";
     uint8_t rs_cdb[REQUEST_SENSE_CMDLEN] =
         {REQUEST_SENSE_CMD, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
 
     if (desc)
         rs_cdb[1] |= 0x1;
@@ -837,7 +837,7 @@
     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};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
 
     rl_cdb[2] = select_report & 0xff;
     sg_put_unaligned_be32((uint32_t)mx_resp_len, rl_cdb + 6);
diff --git a/lib/sg_cmds_basic2.c b/lib/sg_cmds_basic2.c
index 06bbda6..7772aa4 100644
--- a/lib/sg_cmds_basic2.c
+++ b/lib/sg_cmds_basic2.c
@@ -93,7 +93,7 @@
     int res, ret, sense_cat;
     uint8_t sc_cdb[SYNCHRONIZE_CACHE_CMDLEN] =
                 {SYNCHRONIZE_CACHE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     if (sync_nv)
@@ -154,7 +154,7 @@
     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};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     if (pmi) { /* lbs only valid when pmi set */
@@ -209,7 +209,7 @@
     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};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     if (pmi) { /* lbs only valid when pmi set */
@@ -262,7 +262,7 @@
     int res, ret, sense_cat, resid;
     uint8_t modes_cdb[MODE_SENSE6_CMDLEN] =
         {MODE_SENSE6_CMD, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     modes_cdb[1] = (uint8_t)(dbd ? 0x8 : 0);
@@ -361,7 +361,7 @@
     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];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
 
     modes_cdb[1] = (uint8_t)((dbd ? 0x8 : 0) | (llbaa ? 0x10 : 0));
     modes_cdb[2] = (uint8_t)(((pc << 6) & 0xc0) | (pg_code & 0x3f));
@@ -448,7 +448,7 @@
     int res, ret, sense_cat;
     uint8_t modes_cdb[MODE_SELECT6_CMDLEN] =
         {MODE_SELECT6_CMD, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     modes_cdb[1] = (uint8_t)((pf ? 0x10 : 0x0) | (sp ? 0x1 : 0x0));
@@ -519,7 +519,7 @@
     int res, ret, sense_cat;
     uint8_t modes_cdb[MODE_SELECT10_CMDLEN] =
         {MODE_SELECT10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     modes_cdb[1] = (uint8_t)((pf ? 0x10 : 0x0) | (sp ? 0x1 : 0x0));
@@ -818,7 +818,7 @@
     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};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     if (mx_resp_len > 0xffff) {
@@ -902,7 +902,7 @@
     int res, ret, sense_cat;
     uint8_t logs_cdb[LOG_SELECT_CMDLEN] =
         {LOG_SELECT_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     if (param_len > 0xffff) {
@@ -973,7 +973,7 @@
     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];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
 
     if (immed)
         ssuBlk[1] = 0x1;
@@ -1073,7 +1073,7 @@
     int res, ret, sense_cat;
     uint8_t p_cdb[PREVENT_ALLOW_CMDLEN] =
                 {PREVENT_ALLOW_CMD, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     if ((prevent < 0) || (prevent > 3)) {
diff --git a/lib/sg_cmds_extra.c b/lib/sg_cmds_extra.c
index cbcc844..7d4f453 100644
--- a/lib/sg_cmds_extra.c
+++ b/lib/sg_cmds_extra.c
@@ -125,7 +125,7 @@
     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];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     memset(getLbaStatCmd, 0, sizeof(getLbaStatCmd));
@@ -202,7 +202,7 @@
     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];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     memset(gls32_cmd, 0, sizeof(gls32_cmd));
@@ -281,7 +281,7 @@
     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};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     if (extended)
@@ -346,7 +346,7 @@
     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};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     sg_put_unaligned_be32((uint32_t)param_len, stpg_cdb + 6);
@@ -402,7 +402,7 @@
     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};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     sg_put_unaligned_be64(start_llba, repRef_cdb + 2);
@@ -475,7 +475,7 @@
     int res, ret, s_cat, tmout;
     uint8_t senddiag_cdb[SEND_DIAGNOSTIC_CMDLEN] =
         {SEND_DIAGNOSTIC_CMD, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
 
     senddiag_cdb[1] = (uint8_t)(st_code << 5);
     if (pf_bit)
@@ -593,7 +593,7 @@
     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];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
 
     if (pcv)
         rcvdiag_cdb[1] = 0x1;
@@ -715,7 +715,7 @@
     int res, ret, s_cat;
     uint8_t rdef_cdb[READ_DEFECT10_CMDLEN] =
         {READ_DEFECT10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     rdef_cdb[2] = (dl_format & 0x7);
@@ -787,7 +787,7 @@
     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};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     sg_put_unaligned_be32((uint32_t)mx_resp_len, rmsn_cdb + 6);
@@ -851,7 +851,7 @@
     uint8_t rii_cdb[MAINTENANCE_IN_CMDLEN] = {MAINTENANCE_IN_CMD,
                         REPORT_IDENTIFYING_INFORMATION_SA,
                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     sg_put_unaligned_be32((uint32_t)max_resp_len, rii_cdb + 6);
@@ -917,7 +917,7 @@
     uint8_t sii_cdb[MAINTENANCE_OUT_CMDLEN] = {MAINTENANCE_OUT_CMD,
                          SET_IDENTIFYING_INFORMATION_SA,
                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     sg_put_unaligned_be32((uint32_t)param_len, sii_cdb + 6);
@@ -1000,7 +1000,7 @@
     int res, ret, s_cat, tmout;
     uint8_t fu_cdb[FORMAT_UNIT_CMDLEN] =
                 {FORMAT_UNIT_CMD, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     if (fmtpinfo)
@@ -1069,7 +1069,7 @@
     int res, ret, s_cat;
     uint8_t reass_cdb[REASSIGN_BLKS_CMDLEN] =
         {REASSIGN_BLKS_CMD, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     if (longlba)
@@ -1128,7 +1128,7 @@
     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};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     if (rq_servact > 0)
@@ -1195,7 +1195,7 @@
     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};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     if (rq_servact > 0)
@@ -1274,7 +1274,7 @@
     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];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     memset(readLong_cdb, 0, READ_LONG10_CMDLEN);
@@ -1366,7 +1366,7 @@
     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];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     memset(readLong_cdb, 0, sizeof(readLong_cdb));
@@ -1459,7 +1459,7 @@
     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];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     memset(writeLong_cdb, 0, WRITE_LONG10_CMDLEN);
@@ -1541,7 +1541,7 @@
     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];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     memset(writeLong_cdb, 0, sizeof(writeLong_cdb));
@@ -1628,7 +1628,7 @@
     int res, ret, s_cat, slen;
     uint8_t v_cdb[VERIFY10_CMDLEN] =
                 {VERIFY10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     /* N.B. BYTCHK field expanded to 2 bits sbc3r34 */
@@ -1709,7 +1709,7 @@
     int 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};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     /* N.B. BYTCHK field expanded to 2 bits sbc3r34 */
@@ -1807,7 +1807,7 @@
     int k, res, slen, duration;
     int ret = -1;
     uint8_t apt_cdb[ATA_PT_32_CMDLEN];
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     uint8_t * sp;
     const uint8_t * bp;
     struct sg_pt_base * ptvp;
@@ -1962,7 +1962,7 @@
     int res, ret, s_cat;
     uint8_t rbuf_cdb[READ_BUFFER_CMDLEN] =
         {READ_BUFFER_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     rbuf_cdb[1] = (uint8_t)(mode & 0x1f);
@@ -2027,7 +2027,7 @@
     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];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     wbuf_cdb[1] = (uint8_t)(mode & 0x1f);
@@ -2098,7 +2098,7 @@
     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];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     if (buffer_offset > 0xffffff) {
@@ -2182,7 +2182,7 @@
     int res, ret, s_cat, tmout;
     uint8_t u_cdb[UNMAP_CMDLEN] =
                          {UNMAP_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     if (anchor)
@@ -2240,7 +2240,7 @@
     int ret, res, s_cat;
     uint8_t rl_cdb[READ_BLOCK_LIMITS_CMDLEN] =
       {READ_BLOCK_LIMITS_CMD, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     if (vb) {
@@ -2301,7 +2301,7 @@
     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};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
     char b[64];
 
@@ -2364,7 +2364,7 @@
     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};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
     const char * cdb_s = "Extended copy (LID1)";
 
@@ -2424,7 +2424,7 @@
     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};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
     char cname[80];
 
@@ -2511,7 +2511,7 @@
     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];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     memset(preFetchCdb, 0, sizeof(preFetchCdb));
diff --git a/lib/sg_lib.c b/lib/sg_lib.c
index 8884379..35f0fbd 100644
--- a/lib/sg_lib.c
+++ b/lib/sg_lib.c
@@ -2818,13 +2818,14 @@
         b[k + 1] = '\0';
 }
 
-/* Note the ASCII-hex output goes to stdout. [Most other output from functions
- * in this file go to sg_warnings_strm (default stderr).]
- * 'no_ascii' allows for 3 output types:
+/* Read binary starting at 'str' for 'len' bytes and output as ASCII
+ * hexadecinal into file pointer (fp). 16 bytes per line are output with an
+ * additional space between 8th and 9th byte on each line (for readability).
+ * 'no_ascii' selects one of 3 output format types:
  *     > 0     each line has address then up to 16 ASCII-hex bytes
  *     = 0     in addition, the bytes are listed in ASCII to the right
  *     < 0     only the ASCII-hex bytes are listed (i.e. without address) */
-static void
+void
 dStrHexFp(const char* str, int len, int no_ascii, FILE * fp)
 {
     const char * p = str;
diff --git a/lib/sg_pt_dummy.c b/lib/sg_pt_dummy.c
index 9bed386..e080ff8 100644
--- a/lib/sg_pt_dummy.c
+++ b/lib/sg_pt_dummy.c
@@ -22,7 +22,7 @@
 #include "sg_lib.h"
 #include "sg_pr2serr.h"
 
-/* Version 1.03 20211115 */
+/* Version 1.02 20210618 */
 
 /* List of function names with external linkage that need to be defined
  *
diff --git a/sg3_utils.man8.html b/sg3_utils.man8.html
new file mode 100644
index 0000000..579985d
--- /dev/null
+++ b/sg3_utils.man8.html
@@ -0,0 +1,989 @@
+Content-type: text/html; charset=UTF-8
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML><HEAD><TITLE>Man page of SG3_UTILS</TITLE>
+</HEAD><BODY>
+<H1>SG3_UTILS</H1>
+Section: SG3_UTILS (8)<BR>Updated: November 2021<BR><A HREF="#index">Index</A>
+<A HREF="../index.html">Return to Main Contents</A><HR>
+
+<A NAME="lbAB">&nbsp;</A>
+<H2>NAME</H2>
+
+sg3_utils - a package of utilities for sending SCSI commands
+<A NAME="lbAC">&nbsp;</A>
+<H2>SYNOPSIS</H2>
+
+<B>sg_*</B>
+
+[<I>--dry-run</I>] [<I>--enumerate</I>] [<I>--help</I>] [<I>--hex</I>]
+[<I>--in=FN</I>] [<I>--inhex=FN</I>] [<I>--maxlen=LEN</I>] [<I>--raw</I>]
+[<I>--timeout=SECS</I>] [<I>--verbose</I>] [<I>--version</I>]
+[<I>OTHER_OPTIONS</I>] <I>DEVICE</I>
+<A NAME="lbAD">&nbsp;</A>
+<H2>DESCRIPTION</H2>
+
+
+<P>
+
+sg3_utils is a package of utilities that send SCSI commands to the given
+<I>DEVICE</I> via a SCSI pass through interface provided by the host
+operating system.
+<P>
+
+The names of all utilities start with &quot;sg&quot; and most start with &quot;sg_&quot; often
+followed by the name, or a shortening of the name, of the SCSI command that
+they send. For example the &quot;sg_verify&quot; utility sends the SCSI VERIFY
+command. A mapping between SCSI commands and the sg3_utils utilities that
+issue them is shown in the COVERAGE file. The sg_raw utility can be used to
+send an arbitrary SCSI command (supplied on the command line) to the
+given <I>DEVICE</I>.
+<P>
+
+sg_decode_sense can be used to decode SCSI sense data given on the command
+line or in a file. sg_raw -vvv will output the T10 name of a given SCSI
+CDB which is most often 16 bytes or less in length.
+<P>
+
+SCSI draft standards can be found at <A HREF="https://www.t10.org">https://www.t10.org</A> . The standards
+themselves can be purchased from ANSI and other standards organizations.
+A good overview of various SCSI standards can be seen in
+<A HREF="https://www.t10.org/scsi-3.htm">https://www.t10.org/scsi-3.htm</A> with the SCSI command sets in the upper part
+of the diagram. The highest level (i.e. most abstract) document is the SCSI
+Architecture Model (SAM) with SAM-5 being the most recent standard (ANSI
+INCITS 515-2016) with the most recent draft being SAM-6 revision 4 . SCSI
+commands in common with all device types can be found in SCSI Primary
+Commands (SPC) of which SPC-4 is the most recent standard (ANSI INCITS
+513-2015). The most recent SPC draft is SPC-5 revision 21. Block device
+specific commands (e.g. as used by disks) are in SBC, those for tape drives
+in SSC, those for SCSI enclosures in SES and those for CD/DVD/BD drives in
+MMC.
+<P>
+
+It is becoming more common to control ATA disks with the SCSI command set.
+This involves the translation of SCSI commands to their corresponding ATA
+equivalents (and that is an imperfect mapping in some cases). The relevant
+standard is called SCSI to ATA Translation (SAT, SAT-2 and SAT-3) are
+now standards at INCITS(ANSI) and ISO while SAT-4 is at the draft stage.
+The logic to perform the command translation is often called a SAT Layer or
+SATL and may be within an operating system, in host bus adapter firmware or
+in an external device (e.g. associated with a SAS expander). See
+<A HREF="https://www.t10.org">https://www.t10.org</A> for more information.
+<P>
+
+There is some support for SCSI tape devices but not for their basic
+operation. The reader is referred to the &quot;mt&quot; utility.
+<P>
+
+There are two generations of command line option usage. The newer
+utilities (written since July 2004) use the getopt_long() function to parse
+command line options. With that function, each option has two representations:
+a short form (e.g. '-v') and a longer form (e.g. '--verbose'). If an
+argument is required then it follows a space (optionally) in the short form
+and a &quot;=&quot; in the longer form (e.g. in the sg_verify utility '-l 2a6h'
+and '--lba=2a6h' are equivalent). Note that with getopt_long(), short form
+options can be elided, for example: '-all' is equivalent to '-a -l -l'.
+The <I>DEVICE</I> argument may appear after, between or prior to any options.
+<P>
+
+The older utilities, including as sg_inq, sg_logs, sg_modes, sg_opcode,
+sg_rbuff,  sg_readcap, sg_senddiag, sg_start and sg_turs had individual
+command line processing code typically based on a single &quot;-&quot; followed by one
+or more characters. If an argument is needed then it follows a &quot;=&quot; (
+e.g. '-p=1f' in sg_modes with its older interface). Various options can be
+elided as long as it is not ambiguous (e.g. '-vv' to increase the verbosity).
+<P>
+
+Over time the command line interface of these older utilities became messy
+and overloaded with options. So in sg3_utils version 1.23 the command line
+interface of these older utilities was altered to have both a cleaner
+getopt_long() interface and their older interface for backward compatibility.
+By default these older utilities use their getopt_long() based interface.
+The getopt_long() is a GNU extension (i.e. not yet POSIX certified) but
+more recent command line utilities tend to use it. That can be overridden
+by defining the SG3_UTILS_OLD_OPTS environment variable or using '-O'
+or '--old' as the first command line option. The man pages of the older
+utilities documents the details.
+<P>
+
+Several sg3_utils utilities are based on the Unix dd command (e.g. sg_dd)
+and permit copying data at the level of SCSI READ and WRITE commands. sg_dd
+is tightly bound to Linux and hence is not ported to other OSes. A more
+generic utility (than sg_dd) called ddpt in a package of the same name has
+been ported to other OSes.
+<A NAME="lbAE">&nbsp;</A>
+<H2>ENVIRONMENT VARIABLES</H2>
+
+The SG3_UTILS_OLD_OPTS environment variable is explained in the previous
+section. It is only for backward compatibility of the command line options
+for older utilities.
+<P>
+
+The SG3_UTILS_DSENSE environment variable may be set to a number. It is
+only used by the embedded SNTL within the library used by the utilities in
+this library. SNTL is a SCSI to NVMe Translation Layer. This environment
+variable defaults to 0 which will lead to any utility that issues a SCSI
+command that is translated to a NVMe command (by the embedded SNTL) that
+fails at the NVMe dvice, to return SCSI sense in 'fixed' format. If this
+variable is non-zero then then the returned SCSI sense will be in 'descriptor'
+format.
+<P>
+
+Several utilities have their own environment variable setting (e.g.
+sg_persist has SG_PERSIST_IN_RDONLY). See individual utility man pages
+for more information.
+<P>
+
+There is a Linux specific environment variable called SG3_UTILS_LINUX_NANO
+that if defined and the sg driver in the system is 4.0.30 or later, will
+show command durations in nanoseconds rather than the default milliseconds.
+Command durations are typically only shown if --verbose is used 3 or more
+times. Due to an interface problem (a 32 bit integer that should be 64 bits
+with the benefit of hindsight) the maximum duration that can be represented
+in nanoseconds is about 4.2 seconds. If longer durations may occur then
+don't define this environment variable (or undefine it).
+<A NAME="lbAF">&nbsp;</A>
+<H2>LINUX DEVICE NAMING</H2>
+
+Most disk block devices have names like /dev/sda, /dev/sdb, /dev/sdc, etc.
+SCSI disks in Linux have always had names like that but in recent Linux
+kernels it has become more common for many other disks (including SATA
+disks and USB storage devices) to be named like that. Partitions within a
+disk are specified by a number appended to the device name, starting at
+1 (e.g. /dev/sda1 ).
+<P>
+
+Tape drives are named /dev/st&lt;num&gt; or /dev/nst&lt;num&gt; where &lt;num&gt; starts
+at zero. Additionally one letter from this list: &quot;lma&quot; may be appended to
+the name. CD, DVD and BD readers (and writers) are named /dev/sr&lt;num&gt;
+where &lt;num&gt; start at zero. There are less used SCSI device type names,
+the dmesg and the lsscsi commands may help to find if any are attached to
+a running system.
+<P>
+
+There is also a SCSI device driver which offers alternate generic access
+to SCSI devices. It uses names of the form /dev/sg&lt;num&gt; where &lt;num&gt; starts
+at zero. The &quot;lsscsi -g&quot; command may be useful in finding these and which
+generic name corresponds to a device type name (e.g. /dev/sg2 may
+correspond to /dev/sda). In the lk 2.6 series a block SCSI generic
+driver was introduced and its names are of the form
+/dev/bsg/&lt;h:c:t:l&gt; where h, c, t and l are numbers. Again see the lsscsi
+command to find the correspondence between that SCSI tuple (i.e. &lt;h:c:t:l&gt;)
+and alternate device names.
+<P>
+
+Prior to the Linux kernel 2.6 series these utilities could only use
+generic device names (e.g. /dev/sg1 ). In almost all cases in the Linux
+kernel 2.6 series, any device name can be used by these utilities.
+<P>
+
+Very little has changed in Linux device naming in the Linux kernel 3
+and 4 series.
+<A NAME="lbAG">&nbsp;</A>
+<H2>WINDOWS DEVICE NAMING</H2>
+
+Storage and related devices can have several device names in Windows.
+Probably the most common in the volume name (e.g. &quot;D:&quot;). There are also
+a &quot;class&quot; device names such as &quot;PhysicalDrive&lt;n&gt;&quot;, &quot;CDROM&lt;n&gt;&quot;
+and &quot;TAPE&lt;n&gt;&quot;. &lt;n&gt; is an integer starting at 0 allocated in ascending
+order as devices are discovered (and sometimes rediscovered).
+<P>
+
+Some storage devices have a SCSI lower level device name which starts
+with a SCSI (pseudo) adapter name of the form &quot;SCSI&lt;n&gt;:&quot;. To this is added
+sub-addressing in the form of a &quot;bus&quot; number, a &quot;target&quot; identifier and
+a LUN (Logical Unit Number). The &quot;bus&quot; number is also known as a &quot;PathId&quot;.
+These are assembled to form a device name of the
+form: &quot;SCSI&lt;n&gt;:&lt;bus&gt;,&lt;target&gt;,&lt;lun&gt;&quot;. The trailing &quot;,&lt;lun&gt;&quot; may be omitted
+in which case a LUN of zero is assumed. This lower level device name cannot
+often be used directly since Windows blocks attempts to use it if a class
+driver has &quot;claimed&quot; the device. There are SCSI device types (e.g.
+Automation/Drive interface type) for which there is no class driver. At
+least two transports (&quot;bus types&quot; in Windows jargon): USB and IEEE 1394 do
+not have a &quot;scsi&quot; device names of this form.
+<P>
+
+In keeping with DOS file system conventions, the various device names
+can be given in upper, lower or mixed case. Since &quot;PhysicalDrive&lt;n&gt;&quot; is
+tedious to write, a shortened form of &quot;PD&lt;n&gt;&quot; is permitted by all
+utilities in this package.
+<P>
+
+A single device (e.g. a disk) can have many device names. For
+example: &quot;PD0&quot; can also be &quot;C:&quot;, &quot;D:&quot; and &quot;SCSI0:0,1,0&quot;. The two volume names
+reflect that the disk has two partitions on it. Disk partitions that are
+not recognized by Windows are not usually given a volume name. However
+Vista does show a volume name for a disk which has no partitions recognized
+by it and when selected invites the user to format it (which may be rather
+unfriendly to other OSes).
+<P>
+
+These utilities assume a given device name is in the Win32 device namespace.
+To make that explicit &quot;\\.\&quot; can be prepended to the device names mentioned
+in this section. Beware that backslash is an escape character in Unix like
+shells and the C programming language. In a shell like Msys (from MinGW)
+each backslash may need to be typed twice.
+<P>
+
+The sg_scan utility within this package lists out Windows device names in
+a form that is suitable for other utilities in this package to use.
+<A NAME="lbAH">&nbsp;</A>
+<H2>FREEBSD DEVICE NAMING</H2>
+
+SCSI disks have block names of the form /dev/da&lt;num&gt; where &lt;num&gt; is an
+integer starting at zero. The &quot;da&quot; is replaced by &quot;sa&quot; for SCSI tape
+drives and &quot;cd&quot; for SCSI CD/DVD/BD drives. Each SCSI device has a
+corresponding pass-through device name of the form /dev/pass&lt;num&gt;
+where &lt;num&gt; is an integer starting at zero. The &quot;camcontrol devlist&quot;
+command may be useful for finding out which SCSI device names are
+available and the correspondence between class and pass-through names.
+<P>
+
+FreeBSD allows device names to be given without the leading &quot;/dev/&quot; (e.g.
+da0 instead of /dev/da0). That worked in this package up until version
+1.43 when the unadorned device name (e.g. &quot;da0&quot;) gave an error. The
+original action (i.e. allowing unadorned device names) has been restored
+in version 1.46 . Also note that symlinks (to device names) are followed
+before prepending &quot;/dev/&quot; if the resultant name doesn't start with a &quot;/&quot;.
+<P>
+
+FreeBSD's NVMe naming has been evolving. The controller naming is the
+same as Linux: &quot;/dev/nvme&lt;n&gt;&quot; but the namespaces have an
+extra &quot;s&quot; (e.g. &quot;/dev/nvme0ns1&quot;). The latter is not a block (GEOM)
+device (strictly speaking FreeBSD does not have block devices). Initially
+FreeBSD had &quot;/dev/nvd&lt;m&gt;&quot; GEOM devices that were not based on the CAM
+subsystem. Then in FreeBSD release 12 a new nda driver was added that is
+CAM (and GEOM) based for NVMe namespaces; it has names like &quot;/dev/nda0&quot;.
+The preferred device nodes for this package are &quot;/dev/nvme0&quot; for NVMe
+controllers and &quot;/dev/nda0&quot; for NVMe namespaces.
+<A NAME="lbAI">&nbsp;</A>
+<H2>SOLARIS DEVICE NAMING</H2>
+
+SCSI device names below the /dev directory have a form like: c5t4d3s2
+where the number following &quot;c&quot; is the controller (HBA) number, the number
+following &quot;t&quot; is the target number (from the SCSI parallel interface days)
+and the number following &quot;d&quot; is the LUN. Following the &quot;s&quot; is the slice
+number which is related to a partition and by convention &quot;s2&quot; is the whole
+disk.
+<P>
+
+OpenSolaris also has a c5t4d3p2 form where the number following the &quot;p&quot; is
+the partition number apart from &quot;p0&quot; which is the whole disk. So a whole
+disk may be referred to as either c5t4d3, c5t4d3s2 or c5t4d3p0 .
+<P>
+
+And these device names are duplicated in the /dev/dsk and /dev/rdsk
+directories. The former is the block device name and the latter is
+for &quot;raw&quot; (or char device) access which is what sg3_utils needs. So in
+OpenSolaris something of the form 'sg_inq /dev/rdsk/c5t4d3p0' should work.
+If it doesn't work then add a '-vvv' option for more debug information.
+Trying this form 'sg_inq /dev/dsk/c5t4d3p0' (note &quot;rdsk&quot; changed to &quot;dsk&quot;)
+will result in an &quot;inappropriate ioctl for device&quot; error.
+<P>
+
+The device names within the /dev directory are typically symbolic links to
+much longer topological names in the /device directory. In Solaris cd/dvd/bd
+drives seem to be treated as disks and so are found in the /dev/rdsk
+directory. Tape drives appear in the /dev/rmt directory.
+<P>
+
+There is also a sgen (SCSI generic) driver which by default does not attach
+to any device. See the /kernel/drv/sgen.conf file to control what is
+attached. Any attached device will have a device name of the
+form /dev/scsi/c5t4d3 .
+<P>
+
+Listing available SCSI devices in Solaris seems to be a challenge. &quot;Use
+the 'format' command&quot; advice works but seems a very dangerous way to list
+devices. [It does prompt again before doing any damage.] 'devfsadm -Cv'
+cleans out the clutter in the /dev/rdsk directory, only leaving what
+is &quot;live&quot;. The &quot;cfgadm -v&quot; command looks promising.
+<A NAME="lbAJ">&nbsp;</A>
+<H2>NVME SUPPORT</H2>
+
+NVMe (or NVM Express) is a relatively new storage transport and command
+set. The level of abstraction of the NVMe command set is somewhat lower
+the SCSI command sets, closer to the level of abstraction of ATA (and SATA)
+command sets. NVMe claims to be designed with flash and modern &quot;solid
+state&quot; storage in mind, something unheard of when SCSI was originally
+developed in the 1980s.
+<P>
+
+The SCSI command sets' advantage is the length of time they have been in
+place and the existing tools (like these) to support it. Plus SCSI command
+sets level of abstraction is both and advantage and disadvantage. Recently
+the NVME-MI (Management Interface) designers decide to use the SCSI
+Enclosure Services (SES-3) standard &quot;as is&quot; with the addition of two
+tunnelling NVME-MI commands: SES Send and SES Receive. This means after the
+OS interface differences are taken into account, the sg_ses, sg_ses_microcode
+and sg_senddiag utilities can be used on a NVMe device that supports a newer
+version of NVME-MI.
+<P>
+
+The NVME-MI SES Send and SES Receive commands correspond to the SCSI
+SEND DIAGNOSTIC and RECEIVE DIAGNOSTIC RESULTS commands respectively.
+There are however a few other commands that need to be translated, the
+most important of which is the SCSI INQUIRY command to the NVMe Identify
+controller/namespace. Starting in version 1.43 these utilities contain a
+small SNTL (SCSI to NVMe Translation Layer) to take care of these details.
+<P>
+
+As a side effect of this &quot;juggling&quot; if the sg_inq utility is used (without
+the --page= option) on a NVMe <I>DEVICE</I> then the actual NVMe
+Identifier (controller and possibly namespace) responses are decoded and
+output. However if 'sg_inq --page=sinq &lt;device&gt;' is given for the
+same <I>DEVICE</I> then parts of the NVMe Identify controller and namespace
+response are translated to a SCSI standard INQUIRY response which is then
+decoded and output.
+<P>
+
+Apart from the special case with the sg_inq, all other utilities in the
+package assume they are talking to a SCSI device and decode any response
+accordingly. One easy way for users to see the underlying device is a
+NVMe device is the standard INQUIRY response Vendor Identification field
+of &quot;NVMe    &quot; (an 8 character long string with 4 spaces to the right).
+<P>
+
+The following SCSI commands are currently supported by the SNTL library:
+INQUIRY, MODE SELECT(10), MODE SENSE(10), READ(10,16), READ CAPACITY(10,16),
+RECEIVE DIAGNOSTIC RESULTS, REQUEST SENSE, REPORT LUNS, REPORT SUPPORTED
+OPERATION CODES, REPORT SUPPORTED TASK MANAGEMENT FUNCTIONS, SEND
+DIAGNOSTICS, START STOP UNIT, SYNCHRONIZE CACHE(10,16), TEST UNIT READY,
+VERIFY(10,16), WRITE(10,16) and WRITE SAME(10,16).
+<A NAME="lbAK">&nbsp;</A>
+<H2>EXIT STATUS</H2>
+
+To aid scripts that call these utilities, the exit status is set to indicate
+success (0) or failure (1 or more). Note that some of the lower values
+correspond to the SCSI sense key values.
+<P>
+
+The exit status values listed below can be given to the sg_decode_sense
+utility (which is found in this package) as follows:
+<BR>
+
+<BR>&nbsp;&nbsp;sg_decode_sense&nbsp;--err=&lt;exit_status&gt;
+<BR>
+
+and a short explanatory string will be output to stdout.
+<P>
+
+The exit status values are:
+<DL COMPACT>
+<DT><B>0</B>
+
+<DD>
+success. Also used for some utilities that wish to return a boolean value
+for the &quot;true&quot; case (and that no error has occurred). The false case is
+conveyed by exit status 36.
+<DT><B>1</B>
+
+<DD>
+syntax error. Either illegal command line options, options with bad
+arguments or a combination of options that is not permitted.
+<DT><B>2</B>
+
+<DD>
+the <I>DEVICE</I> reports that it is not ready for the operation requested.
+The <I>DEVICE</I> may be in the process of becoming ready (e.g.  spinning up
+but not at speed) so the utility may work after a wait. In Linux the
+<I>DEVICE</I> may be temporarily blocked while error recovery is taking place.
+<DT><B>3</B>
+
+<DD>
+the <I>DEVICE</I> reports a medium or hardware error (or a blank check). For
+example an attempt to read a corrupted block on a disk will yield this value.
+<DT><B>5</B>
+
+<DD>
+the <I>DEVICE</I> reports an &quot;illegal request&quot; with an additional sense code
+other than &quot;invalid command operation code&quot;. This is often a supported
+command with a field set requesting an unsupported capability. For commands
+that require a &quot;service action&quot; field this value can indicate that the
+command with that service action value is not supported.
+<DT><B>6</B>
+
+<DD>
+the <I>DEVICE</I> reports a &quot;unit attention&quot; condition. This usually indicates
+that something unrelated to the requested command has occurred (e.g. a device
+reset) potentially before the current SCSI command was sent. The requested
+command has not been executed by the device. Note that unit attention
+conditions are usually only reported once by a device.
+<DT><B>7</B>
+
+<DD>
+the <I>DEVICE</I> reports a &quot;data protect&quot; sense key. This implies some
+mechanism has blocked writes (or possibly all access to the media).
+<DT><B>9</B>
+
+<DD>
+the <I>DEVICE</I> reports an illegal request with an additional sense code
+of &quot;invalid command operation code&quot; which means that it doesn't support the
+requested command.
+<DT><B>10</B>
+
+<DD>
+the <I>DEVICE</I> reports a &quot;copy aborted&quot;. 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.
+<DT><B>11</B>
+
+<DD>
+the <I>DEVICE</I> reports an aborted command. In some cases aborted
+commands can be retried immediately (e.g. if the transport aborted
+the command due to congestion).
+<DT><B>14</B>
+
+<DD>
+the <I>DEVICE</I> reports a miscompare sense key. VERIFY and COMPARE AND
+WRITE commands may report this.
+<DT><B>15</B>
+
+<DD>
+the utility is unable to open, close or use the given <I>DEVICE</I> or some
+other file. The given file name could be incorrect or there may be
+permission problems. Adding the '-v' option may give more information.
+<DT><B>17</B>
+
+<DD>
+a SCSI &quot;Illegal request&quot; sense code received with a flag indicating the
+Info field is valid. This is often a LBA but its meaning is command specific.
+<DT><B>18</B>
+
+<DD>
+the <I>DEVICE</I> reports a medium or hardware error (or a blank check)
+with a flag indicating the Info field is valid. This is often a LBA (of
+the first encountered error) but its meaning is command specific.
+<DT><B>20</B>
+
+<DD>
+the <I>DEVICE</I> reports it has a check condition but &quot;no sense&quot;
+and non-zero information in its additional sense codes. Some polling
+commands (e.g. REQUEST SENSE) can receive this response. There may
+be useful information in the sense data such as a progress indication.
+<DT><B>21</B>
+
+<DD>
+the <I>DEVICE</I> reports a &quot;recovered error&quot;. The requested command
+was successful. Most likely a utility will report a recovered error
+to stderr and continue, probably leaving the utility with an exit
+status of 0 .
+<DT><B>22</B>
+
+<DD>
+the <I>DEVICE</I> reports that the current command or its parameters imply
+a logical block address (LBA) that is out of range. This happens surprisingly
+often when trying to access the last block on a storage device; either a
+classic &quot;off by one&quot; logic error or a misreading of the response from READ
+CAPACITY(10 or 16) in which the address of the last block rather than the
+number of blocks on the <I>DEVICE</I> is returned. Since LBAs are origin zero
+they range from 0 to n-1 where n is the number of blocks on the <I>DEVICE</I>,
+so the LBA of the last block is one less than the total number of blocks.
+<DT><B>24</B>
+
+<DD>
+the <I>DEVICE</I> reports a SCSI status of &quot;reservation conflict&quot;. This
+means access to the <I>DEVICE</I> with the current command has been blocked
+because another machine (HBA or SCSI &quot;initiator&quot;) holds a reservation on
+this <I>DEVICE</I>. On modern SCSI systems this is related to the use of
+the PERSISTENT RESERVATION family of commands.
+<DT><B>25</B>
+
+<DD>
+the <I>DEVICE</I> reports a SCSI status of &quot;condition met&quot;. Currently only
+the PRE-FETCH command (see SBC-4) yields this status.
+<DT><B>26</B>
+
+<DD>
+the <I>DEVICE</I> reports a SCSI status of &quot;busy&quot;. SAM-6 defines this status
+as the logical unit is temporarily unable to process a command. It is
+recommended to re-issue the command.
+<DT><B>27</B>
+
+<DD>
+the <I>DEVICE</I> reports a SCSI status of &quot;task set full&quot;.
+<DT><B>28</B>
+
+<DD>
+the <I>DEVICE</I> reports a SCSI status of &quot;ACA active&quot;. ACA is &quot;auto
+contingent allegiance&quot; and is seldom used.
+<DT><B>29</B>
+
+<DD>
+the <I>DEVICE</I> reports a SCSI status of &quot;task aborted&quot;. SAM-5 says:
+&quot;This status shall be returned if a command is aborted by a command or task
+management function on another I_T nexus and the Control mode page TAS bit
+is set to one&quot;.
+<DT><B>31</B>
+
+<DD>
+error involving two or more command line options. They may be contradicting,
+select an unsupported mode, or a required option (given the context) is
+missing.
+<DT><B>32</B>
+
+<DD>
+there is a logic error in the utility. It corresponds to code comments
+like &quot;shouldn't/can't get here&quot;. Perhaps the author should be informed.
+<DT><B>33</B>
+
+<DD>
+the command sent to <I>DEVICE</I> has timed out.
+<DT><B>34</B>
+
+<DD>
+this is a Windows only exit status and indicates that the Windows error
+number (32 bits) cannot meaningfully be mapped to an equivalent Unix error
+number returned as the exit status (7 bits).
+<DT><B>35</B>
+
+<DD>
+a transport error has occurred. This will either be in the driver (e.g. HBA
+driver) or in the interconnect between the host (initiator) and the
+device (target).  For example in SAS an expander can run out of paths and
+thus be unable to return the user data for a READ command.
+<DT><B>36</B>
+
+<DD>
+no error has occurred plus the utility wants to convey a boolean value
+of false. The corresponding true value is conveyed by a 0 exit status.
+<DT><B>40</B>
+
+<DD>
+the command sent to <I>DEVICE</I> has received an &quot;aborted command&quot; 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.
+<DT><B>41</B>
+
+<DD>
+the command sent to <I>DEVICE</I> has received an &quot;aborted command&quot; sense
+key with an additional sense code of 0x10 (as with error code) plus a flag
+indicating the Info field is valid.
+<DT><B>48</B>
+
+<DD>
+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.
+<DT><B>49</B>
+
+<DD>
+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
+nonsensical.
+<DT><B>50</B>
+
+<DD>
+OS system calls that fail often return a small integer number to help. In
+Unix these are called &quot;errno&quot; values where 0 implies no error. These error
+codes set aside 51 to 96 for mapping these errno values but that may not be
+sufficient. Higher errno values that cannot be mapped are all mapped to
+this value (i.e. 50).
+<BR>
+
+Note that an errno value of 0 is mapped to error code 0.
+<DT><B>50 + &lt;os_error_number&gt;</B>
+
+<DD>
+OS system calls that fail often return a small integer number to help
+indicate what the error is. For example in Unix the inability of a system
+call to allocate memory returns (in 'errno') ENOMEM which often is
+associated with the integer 12. So 62 (i.e. '50 + 12') may be returned
+by a utility in this case. It is also possible that a utility in this
+package reports 50+ENOMEM when it can't allocate memory, not necessarily
+from an OS system call. In recent versions of Linux the file showing the
+mapping between symbolic constants (e.g. ENOMEM) and the corresponding
+integer is in the kernel source code file:
+include/uapi/asm-generic/errno-base.h
+<BR>
+
+Note that errno values that are greater than or equal to 47 cannot fit in
+range provided. Instead they are all mapped to 50 as discussed in the
+previous entry.
+<DT><B>97</B>
+
+<DD>
+a SCSI command response failed sanity checks.
+<DT><B>98</B>
+
+<DD>
+the <I>DEVICE</I> reports it has a check condition but the error
+doesn't fit into any of the above categories.
+<DT><B>99</B>
+
+<DD>
+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.
+<DT><B>100-125</B>
+
+<DD>
+these error codes are used by the ddpt utility which uses the sg3_utils
+library. They are mainly specialized error codes associated with offloaded
+copies.
+<DT><B>126</B>
+
+<DD>
+the utility was found but could not be executed. That might occur if the
+executable does not have execute permissions.
+<DT><B>127</B>
+
+<DD>
+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.
+<DT><B>128 + &lt;signum&gt;</B>
+
+<DD>
+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 .
+<DT><B>255</B>
+
+<DD>
+the utility tried to yield an exit status of 255 or larger. That should
+not happen; given here for completeness.
+</DL>
+<P>
+
+Most of the error conditions reported above will be repeatable (an example
+of one that is not is &quot;unit attention&quot;) so the utility can be run again with
+the '-v' option (or several) to obtain more information.
+<A NAME="lbAL">&nbsp;</A>
+<H2>COMMON OPTIONS</H2>
+
+Arguments to long options are mandatory for short options as well. In the
+short form an argument to an option uses zero or more spaces as a
+separator (i.e. the short form does not use &quot;=&quot; as a separator).
+<P>
+
+If an option takes a numeric argument then that argument is assumed to
+be decimal unless otherwise indicated (e.g. with a leading &quot;0x&quot;, a
+trailing &quot;h&quot; or as noted in the usage message).
+<P>
+
+Some options are used uniformly in most of the utilities in this
+package. Those options are listed below. Note that there are some
+exceptions.
+<DL COMPACT>
+<DT><B>-d</B>, <B>--dry-run</B><DD>
+utilities that can cause lots of user data to be lost or overwritten
+sometimes have a <I>--dry-run</I> option. Device modifying actions are
+typically bypassed (or skipped) to implement a policy of &quot;do no harm&quot;.
+This allows complex command line invocations to be tested before the
+action required (e.g. format a disk) is performed. The <I>--dry-run</I>
+option has become a common feature of many command line utilities (e.g.
+the Unix 'patch' command), not just those from this package.
+<BR>
+
+Note that most hyphenated option names in this package also can be given
+with an underscore rather than a hyphen (e.g.  <I>--dry_run</I>).
+<DT><B>-e</B>, <B>--enumerate</B><DD>
+some utilities (e.g. sg_ses and sg_vpd) store a lot of information in
+internal tables. This option will output that information in some readable
+form (e.g. sorted by an acronym or by page number) then exit. Note that
+with this option <I>DEVICE</I> is ignored (as are most other options) and no
+SCSI IO takes place, so the invoker does not need any elevated permissions.
+<DT><B>-h</B>, <B>-?</B>, <B>--help</B><DD>
+output the usage message then exit. In a few older utilities the '-h'
+option requests hexadecimal output. In these cases the '-?' option will
+output the usage message then exit.
+<DT><B>-H</B>, <B>--hex</B><DD>
+for SCSI commands that yield a non-trivial response, print out that
+response in ASCII hexadecimal. To produce hexadecimal that can be parsed
+by other utilities (e.g. without a relative address to the left and without
+trailing ASCII) use this option three or four times.
+<DT><B>-i</B>, <B>--in</B>=<I>FN</I><DD>
+many SCSI commands fetch a significant amount of data (returned in the
+data-in buffer) which several of these utilities decode (e.g. sg_vpd and
+sg_logs). To separate the two steps of fetching the data from a SCSI device
+and then decoding it, this option has been added. The first step (fetching
+the data) can be done using the <I>--hex</I> or <I>--raw</I> option and
+redirecting the command line output to a file (often done with &quot;&gt;&quot; in Unix
+based operating systems). The difference between <I>--hex</I> and
+<I>--raw</I> is that the former produces output in ASCII hexadecimal
+while <I>--raw</I> produces its output in &quot;raw&quot; binary.
+<BR>
+
+The second step (i.e. decoding the SCSI response data now held in a file)
+can be done using this <I>--in=FN</I> option where the file name is
+<I>FN</I>. If &quot;-&quot; is used for <I>FN</I> then stdin is assumed, again this
+allows for command line redirection (or piping). That file (or stdin)
+is assumed to contain ASCII hexadecimal unless the <I>--raw</I> option is
+also given in which case it is assumed to be binary. Notice that the meaning
+of the <I>--raw</I> option is &quot;flipped&quot; when used with <I>--in=FN</I> to
+act on the input, typically it acts on the output data.
+<BR>
+
+Since the structure of the data returned by SCSI commands varies
+considerably then the usage information or the manpage of the utility being
+used should be checked. In some cases <I>--hex</I> may need to be used
+multiple times (and is more conveniently given as '-HH' or '-HHH).
+<DT><B>-i</B>, <B>--inhex</B>=<I>FN</I><DD>
+This option has the same or similar functionality as <I>--in=FN</I>. And
+perhaps 'inhex' is more descriptive since by default, ASCII hexadecimal is
+expected in the contents of file: <I>FN</I>. Alternatively the short form
+option may be <I>-I</I> or <I>-X</I>. See the &quot;FORMAT OF FILES CONTAINING
+ASCII HEX&quot; section below for more information.
+<DT><B>-m</B>, <B>--maxlen</B>=<I>LEN</I><DD>
+several important SCSI commands (e.g. INQUIRY and MODE SENSE) have response
+lengths that vary depending on many factors, only some of which these
+utilities take into account. The maximum response length is typically
+specified in the 'allocation length' field of the cdb. In the absence of
+this option, several utilities use a default allocation length (sometimes
+recommended in the SCSI draft standards) or a &quot;double fetch&quot; strategy.
+See <A HREF="../man8/sg_logs.8.html">sg_logs</A>(8) for its description of a &quot;double fetch&quot; strategy. These
+techniques are imperfect and in the presence of faulty SCSI targets can
+cause problems (e.g. some USB mass storage devices freeze if they receive
+an INQUIRY allocation length other than 36). Also use of this option
+disables any &quot;double fetch&quot; strategy that may have otherwise been used.
+<BR>
+
+To head off a class of degenerate bugs, if <I>LEN</I> is less than 16 then
+it is ignored (usually with a warning message) and the default value is
+used instead. Some utilities use 4 (bytes), rather than 16, as the cutoff
+value.
+<DT><B>-r</B>, <B>--raw</B><DD>
+for SCSI commands that yield a non-trivial response, output that response
+in binary to stdout. If any error messages or warning are produced they are
+usually sent to stderr so as to not interfere with the output from this
+option.
+<BR>
+
+Some utilities that consume data to send to the <I>DEVICE</I> along with the
+SCSI command, use this option. Alternatively the <I>--in=FN</I> option causes
+<I>DEVICE</I> to be ignored and the response data (to be decoded) fetched
+from a file named <I>FN</I>. In these cases this option may indicate that
+binary data can be read from stdin or from a nominated file (e.g. <I>FN</I>).
+<DT><B>-t</B>, <B>--timeout</B>=<I>SECS</I><DD>
+utilities that issue potentially long-running SCSI commands often have a
+<I>--timeout=SECS</I> option. This typically instructs the operating system
+to abort the SCSI command in question once the timeout expires. Aborting
+SCSI commands is typically a messy business and in the case of format like
+commands may leave the device in a &quot;format corrupt&quot; state requiring another
+long-running re-initialization command to be sent. The argument, <I>SECS</I>,
+is usually in seconds and the short form of the option may be something
+other than <I>-t</I> since the timeout option was typically added later as
+storage devices grew in size and initialization commands took longer. Since
+many utilities had relatively long internal command timeouts before this
+option was introduced, the actual command timeout given to the operating
+systems is the higher of the internal timeout and <I>SECS</I>.
+<BR>
+
+Many long running SCSI commands have an IMMED bit which causes the command
+to finish relatively quickly but the initialization process to continue. In
+such cases the REQUEST SENSE command can be used to monitor progress with
+its progress indication field (see the sg_requests and sg_turs utilities).
+Utilities that send such SCSI command either have an <I>--immed</I> option
+or a <I>--wait</I> option which is the logical inverse of the &quot;immediate&quot;
+action.
+<DT><B>-v</B>, <B>--verbose</B><DD>
+increase the level of verbosity, (i.e. debug output). Can be used multiple
+times to further increase verbosity. The additional output caused by this
+option is almost always sent to stderr.
+<DT><B>-V</B>, <B>--version</B><DD>
+print the version string and then exit. Each utility has its own version
+number and date of last code change.
+</DL>
+<A NAME="lbAM">&nbsp;</A>
+<H2>NUMERIC ARGUMENTS</H2>
+
+Many utilities have command line options that take numeric arguments. These
+numeric arguments can be large values (e.g. a logical block address (LBA) on
+a disk) and can be inconvenient to enter in the default decimal
+representation. So various other representations are permitted.
+<P>
+
+Multiplicative suffixes are accepted. They are one, two or three letter
+strings appended directly after the number to which they apply:
+<P>
+
+<BR>&nbsp;&nbsp;&nbsp;c&nbsp;C&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*1
+<BR>
+
+<BR>&nbsp;&nbsp;&nbsp;w&nbsp;W&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*2
+<BR>
+
+<BR>&nbsp;&nbsp;&nbsp;b&nbsp;B&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*512
+<BR>
+
+<BR>&nbsp;&nbsp;&nbsp;k&nbsp;K&nbsp;KiB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*1024
+<BR>
+
+<BR>&nbsp;&nbsp;&nbsp;KB&nbsp;kB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*1000
+<BR>
+
+<BR>&nbsp;&nbsp;&nbsp;m&nbsp;M&nbsp;MiB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*1048576
+<BR>
+
+<BR>&nbsp;&nbsp;&nbsp;MB&nbsp;mB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*1000000
+<BR>
+
+<BR>&nbsp;&nbsp;&nbsp;g&nbsp;G&nbsp;GiB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(2^30)
+<BR>
+
+<BR>&nbsp;&nbsp;&nbsp;GB&nbsp;gB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(10^9)
+<BR>
+
+<BR>&nbsp;&nbsp;&nbsp;t&nbsp;T&nbsp;TiB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(2^40)
+<BR>
+
+<BR>&nbsp;&nbsp;&nbsp;TB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(10^12)
+<BR>
+
+<BR>&nbsp;&nbsp;&nbsp;p&nbsp;P&nbsp;PiB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(2^50)
+<BR>
+
+<BR>&nbsp;&nbsp;&nbsp;PB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(10^15)
+<P>
+
+An example is &quot;2k&quot; for 2048. The large tera and peta suffixes are only
+available for numeric arguments that might require 64 bits to represent
+internally.
+<P>
+
+These multiplicative suffixes are compatible with GNU's dd command (since
+2002) which claims compliance with SI and with IEC 60027-2.
+<P>
+
+A suffix of the form &quot;x&lt;n&gt;&quot; multiplies the preceding number by &lt;n&gt;. An
+example is &quot;2x33&quot; for &quot;66&quot;. The left argument cannot be '0' as '0x' will
+be interpreted as hexadecimal number prefix (see below). The left
+argument to the multiplication must end in a hexadecimal digit (i.e.
+0 to f) and the whole expression cannot have any embedded whitespace (e.g.
+spaces). An ugly example: &quot;0xfx0x2&quot; for 30.
+<P>
+
+A suffix of the form &quot;+&lt;n&gt;&quot; adds the preceding number to &lt;n&gt;. An example
+is &quot;3+1k&quot; for &quot;1027&quot;. The left argument to the addition must end in a
+hexadecimal digit (i.e. 0 to f) and the whole expression cannot have any
+embedded whitespace (e.g. spaces). Another example: &quot;0xf+0x2&quot; for 17.
+<P>
+
+Alternatively numerical arguments can be given in hexadecimal. There are
+two syntaxes. The number can be preceded by either &quot;0x&quot; or &quot;0X&quot; as found
+in the C programming language. The second hexadecimal representation is a
+trailing &quot;h&quot; or &quot;H&quot; as found in (storage) standards. When hex numbers are
+given, multipliers cannot be used. For example the decimal value &quot;256&quot; can
+be given as &quot;0x100&quot; or &quot;100h&quot;.
+<A NAME="lbAN">&nbsp;</A>
+<H2>FORMAT OF FILES CONTAINING ASCII HEX</H2>
+
+Such a file is assumed to contain a sequence of one or two digit ASCII
+hexadecimal values separated by whitespace. &quot;Whitespace consists of either
+spaces, tabs, blank lines, or any combination thereof&quot;. Each one or two digit
+ASCII hex pair is decoded into a byte (i.e. 8 bits). The following will be
+decoded to valid (ascending valued)
+bytes: '0', '01', '3', 'c', 'F', '4a', 'cC', 'ff'.
+Lines containing only whitespace are ignored. The contents of any line
+containing a hash mark ('#') is ignored from that point until the end of that
+line. Users are encouraged to use hash marks to introduce comments in hex
+files. The author uses the extension'.hex' on such files. Examples can be
+found in the 'inhex' directory.
+<A NAME="lbAO">&nbsp;</A>
+<H2>MICROCODE AND FIRMWARE</H2>
+
+There are two standardized methods for downloading microcode (i.e. device
+firmware) to a SCSI device. The more general way is with the SCSI WRITE
+BUFFER command, see the sg_write_buffer utility. SCSI enclosures have
+their own method based on the Download microcode control/status diagnostic
+page, see the sg_ses_microcode utility.
+<A NAME="lbAP">&nbsp;</A>
+<H2>SCRIPTS, EXAMPLES and UTILS</H2>
+
+There are several bash shell scripts in the 'scripts' subdirectory that
+invoke compiled utilities (e.g. sg_readcap). Several of the scripts start
+with 'scsi_' rather than 'sg_'. One purpose of these scripts is to call the
+same utility (e.g. sg_readcap) on multiple devices. Most of the basic
+compiled utilities only allow one device as an argument. Some distributions
+install these scripts in a more visible directory (e.g. /usr/bin). Some of
+these scripts have man page entries. See the README file in the 'scripts'
+subdirectory.
+<P>
+
+There is some example C code plus examples of complex invocations in
+the 'examples' subdirectory. There is also a README file. The example C
+may be a simpler example of how to use a SCSI pass-through in Linux
+than the main utilities (found in the 'src' subdirectory). This is due
+to the fewer abstraction layers (e.g. they don't worry the MinGW in
+Windows may open a file in text rather than binary mode).
+<P>
+
+Some utilities that the author has found useful have been placed in
+the 'utils' subdirectory.
+<A NAME="lbAQ">&nbsp;</A>
+<H2>WEB SITE</H2>
+
+There is a web page discussing this package at
+<A HREF="https://sg.danny.cz/sg/sg3_utils.html">https://sg.danny.cz/sg/sg3_utils.html</A> . The device naming used by this
+package on various operating systems is discussed at:
+<A HREF="https://sg.danny.cz/sg/device_name.html">https://sg.danny.cz/sg/device_name.html</A> . There is a git code mirror at
+<A HREF="https://github.com/hreinecke/sg3_utils">https://github.com/hreinecke/sg3_utils</A> . The principle code repository
+uses subversion and is on the author's equipment. The author keeps track
+of this via the subversion revision number which is an ascending integer
+(currently at 774 for this package). The github mirror gets updated
+periodically from the author's repository. Depending on the time of
+update, the above Downloads section at sg.danny.cz may be more up to
+date than the github mirror.
+<A NAME="lbAR">&nbsp;</A>
+<H2>AUTHORS</H2>
+
+Written by Douglas Gilbert. Some utilities have been contributed, see the
+CREDITS file and individual source files (in the 'src' directory).
+<A NAME="lbAS">&nbsp;</A>
+<H2>REPORTING BUGS</H2>
+
+Report bugs to &lt;dgilbert at interlog dot com&gt;.
+<A NAME="lbAT">&nbsp;</A>
+<H2>COPYRIGHT</H2>
+
+Copyright &#169; 1999-2021 Douglas Gilbert
+<BR>
+
+Some utilities are distributed under a GPL version 2 license while
+others, usually more recent ones, are under a FreeBSD license. The files
+that are common to almost all utilities and thus contain the most reusable
+code, namely sg_lib.[hc], sg_cmds_basic.[hc] and sg_cmds_extra.[hc] are
+under a FreeBSD license. There is NO warranty; not even for MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE.
+<A NAME="lbAU">&nbsp;</A>
+<H2>SEE ALSO</H2>
+
+<B>sdparm(sdparm), ddpt(ddpt), lsscsi(lsscsi), <A HREF="../man1/dmesg.1.html">dmesg</A>(1), <A HREF="../man1/mt.1.html">mt</A>(1)</B>
+
+<P>
+
+<HR>
+<A NAME="index">&nbsp;</A><H2>Index</H2>
+<DL>
+<DT><A HREF="#lbAB">NAME</A><DD>
+<DT><A HREF="#lbAC">SYNOPSIS</A><DD>
+<DT><A HREF="#lbAD">DESCRIPTION</A><DD>
+<DT><A HREF="#lbAE">ENVIRONMENT VARIABLES</A><DD>
+<DT><A HREF="#lbAF">LINUX DEVICE NAMING</A><DD>
+<DT><A HREF="#lbAG">WINDOWS DEVICE NAMING</A><DD>
+<DT><A HREF="#lbAH">FREEBSD DEVICE NAMING</A><DD>
+<DT><A HREF="#lbAI">SOLARIS DEVICE NAMING</A><DD>
+<DT><A HREF="#lbAJ">NVME SUPPORT</A><DD>
+<DT><A HREF="#lbAK">EXIT STATUS</A><DD>
+<DT><A HREF="#lbAL">COMMON OPTIONS</A><DD>
+<DT><A HREF="#lbAM">NUMERIC ARGUMENTS</A><DD>
+<DT><A HREF="#lbAN">FORMAT OF FILES CONTAINING ASCII HEX</A><DD>
+<DT><A HREF="#lbAO">MICROCODE AND FIRMWARE</A><DD>
+<DT><A HREF="#lbAP">SCRIPTS, EXAMPLES and UTILS</A><DD>
+<DT><A HREF="#lbAQ">WEB SITE</A><DD>
+<DT><A HREF="#lbAR">AUTHORS</A><DD>
+<DT><A HREF="#lbAS">REPORTING BUGS</A><DD>
+<DT><A HREF="#lbAT">COPYRIGHT</A><DD>
+<DT><A HREF="#lbAU">SEE ALSO</A><DD>
+</DL>
+<HR>
+This document was created by
+<A HREF="/cgi-bin/man/man2html">man2html</A>,
+using the manual pages.<BR>
+Time: 03:12:28 GMT, November 11, 2021
+</BODY>
+</HTML>
diff --git a/sg3_utils.spec b/sg3_utils.spec
index e4d1abb..11c46bc 100644
--- a/sg3_utils.spec
+++ b/sg3_utils.spec
@@ -84,7 +84,7 @@
 %{_libdir}/*.la
 
 %changelog
-* Mon Nov 15 2021 - dgilbert at interlog dot com
+* Sat Nov 20 2021 - dgilbert at interlog dot com
 - track t10 changes
   * sg3_utils-1.48
 
diff --git a/src/Makefile.am b/src/Makefile.am
index 9007642..cd9d8b9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -10,7 +10,7 @@
 	sg_sat_set_features sg_seek sg_senddiag sg_ses sg_ses_microcode \
 	sg_start sg_stpg sg_stream_ctl sg_sync sg_timestamp sg_turs sg_unmap \
 	sg_verify sg_vpd sg_wr_mode sg_write_buffer sg_write_long \
-	sg_write_same sg_write_verify sg_write_x sg_zone
+	sg_write_same sg_write_verify sg_write_x sg_zone sg_z_act_query
 sg_scan_SOURCES =
 
 
@@ -203,3 +203,5 @@
 sg_xcopy_LDADD = ../lib/libsgutils2.la
 
 sg_zone_LDADD = ../lib/libsgutils2.la
+
+sg_z_act_query_LDADD = ../lib/libsgutils2.la
diff --git a/src/Makefile.in b/src/Makefile.in
index 5ae4625..39648d8 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -110,7 +110,8 @@
 	sg_vpd$(EXEEXT) sg_wr_mode$(EXEEXT) sg_write_buffer$(EXEEXT) \
 	sg_write_long$(EXEEXT) sg_write_same$(EXEEXT) \
 	sg_write_verify$(EXEEXT) sg_write_x$(EXEEXT) sg_zone$(EXEEXT) \
-	$(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3)
+	sg_z_act_query$(EXEEXT) $(am__EXEEXT_1) $(am__EXEEXT_2) \
+	$(am__EXEEXT_3)
 @OS_LINUX_TRUE@@PT_DUMMY_FALSE@am__append_1 = \
 @OS_LINUX_TRUE@@PT_DUMMY_FALSE@	sg_copy_results sg_dd sg_emc_trespass sg_map sg_map26 sg_rbuf \
 @OS_LINUX_TRUE@@PT_DUMMY_FALSE@	sg_read sg_reset sg_scan sg_test_rwbuf sg_xcopy sginfo sgm_dd sgp_dd
@@ -356,6 +357,9 @@
 sg_xcopy_SOURCES = sg_xcopy.c
 sg_xcopy_OBJECTS = sg_xcopy.$(OBJEXT)
 sg_xcopy_DEPENDENCIES = ../lib/libsgutils2.la
+sg_z_act_query_SOURCES = sg_z_act_query.c
+sg_z_act_query_OBJECTS = sg_z_act_query.$(OBJEXT)
+sg_z_act_query_DEPENDENCIES = ../lib/libsgutils2.la
 sg_zone_SOURCES = sg_zone.c
 sg_zone_OBJECTS = sg_zone.$(OBJEXT)
 sg_zone_DEPENDENCIES = ../lib/libsgutils2.la
@@ -421,8 +425,9 @@
 	./$(DEPDIR)/sg_write_buffer.Po ./$(DEPDIR)/sg_write_long.Po \
 	./$(DEPDIR)/sg_write_same.Po ./$(DEPDIR)/sg_write_verify.Po \
 	./$(DEPDIR)/sg_write_x.Po ./$(DEPDIR)/sg_xcopy.Po \
-	./$(DEPDIR)/sg_zone.Po ./$(DEPDIR)/sginfo.Po \
-	./$(DEPDIR)/sgm_dd.Po ./$(DEPDIR)/sgp_dd.Po
+	./$(DEPDIR)/sg_z_act_query.Po ./$(DEPDIR)/sg_zone.Po \
+	./$(DEPDIR)/sginfo.Po ./$(DEPDIR)/sgm_dd.Po \
+	./$(DEPDIR)/sgp_dd.Po
 am__mv = mv -f
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
 	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -458,7 +463,8 @@
 	sg_sync.c sg_test_rwbuf.c sg_timestamp.c sg_turs.c sg_unmap.c \
 	sg_verify.c $(sg_vpd_SOURCES) sg_wr_mode.c sg_write_buffer.c \
 	sg_write_long.c sg_write_same.c sg_write_verify.c sg_write_x.c \
-	sg_xcopy.c sg_zone.c sginfo.c sgm_dd.c sgp_dd.c
+	sg_xcopy.c sg_z_act_query.c sg_zone.c sginfo.c sgm_dd.c \
+	sgp_dd.c
 DIST_SOURCES = sg_bg_ctl.c sg_compare_and_write.c sg_copy_results.c \
 	sg_dd.c sg_decode_sense.c sg_emc_trespass.c sg_format.c \
 	sg_get_config.c sg_get_elem_status.c sg_get_lba_status.c \
@@ -475,7 +481,8 @@
 	sg_sync.c sg_test_rwbuf.c sg_timestamp.c sg_turs.c sg_unmap.c \
 	sg_verify.c $(sg_vpd_SOURCES) sg_wr_mode.c sg_write_buffer.c \
 	sg_write_long.c sg_write_same.c sg_write_verify.c sg_write_x.c \
-	sg_xcopy.c sg_zone.c sginfo.c sgm_dd.c sgp_dd.c
+	sg_xcopy.c sg_z_act_query.c sg_zone.c sginfo.c sgm_dd.c \
+	sgp_dd.c
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
@@ -719,6 +726,7 @@
 sg_write_x_LDADD = ../lib/libsgutils2.la
 sg_xcopy_LDADD = ../lib/libsgutils2.la
 sg_zone_LDADD = ../lib/libsgutils2.la
+sg_z_act_query_LDADD = ../lib/libsgutils2.la
 all: all-am
 
 .SUFFIXES:
@@ -1066,6 +1074,10 @@
 	@rm -f sg_xcopy$(EXEEXT)
 	$(AM_V_CCLD)$(LINK) $(sg_xcopy_OBJECTS) $(sg_xcopy_LDADD) $(LIBS)
 
+sg_z_act_query$(EXEEXT): $(sg_z_act_query_OBJECTS) $(sg_z_act_query_DEPENDENCIES) $(EXTRA_sg_z_act_query_DEPENDENCIES) 
+	@rm -f sg_z_act_query$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_z_act_query_OBJECTS) $(sg_z_act_query_LDADD) $(LIBS)
+
 sg_zone$(EXEEXT): $(sg_zone_OBJECTS) $(sg_zone_DEPENDENCIES) $(EXTRA_sg_zone_DEPENDENCIES) 
 	@rm -f sg_zone$(EXEEXT)
 	$(AM_V_CCLD)$(LINK) $(sg_zone_OBJECTS) $(sg_zone_LDADD) $(LIBS)
@@ -1157,6 +1169,7 @@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_write_verify.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_write_x.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_xcopy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_z_act_query.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_zone.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sginfo.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sgm_dd.Po@am__quote@ # am--include-marker
@@ -1390,6 +1403,7 @@
 	-rm -f ./$(DEPDIR)/sg_write_verify.Po
 	-rm -f ./$(DEPDIR)/sg_write_x.Po
 	-rm -f ./$(DEPDIR)/sg_xcopy.Po
+	-rm -f ./$(DEPDIR)/sg_z_act_query.Po
 	-rm -f ./$(DEPDIR)/sg_zone.Po
 	-rm -f ./$(DEPDIR)/sginfo.Po
 	-rm -f ./$(DEPDIR)/sgm_dd.Po
@@ -1508,6 +1522,7 @@
 	-rm -f ./$(DEPDIR)/sg_write_verify.Po
 	-rm -f ./$(DEPDIR)/sg_write_x.Po
 	-rm -f ./$(DEPDIR)/sg_xcopy.Po
+	-rm -f ./$(DEPDIR)/sg_z_act_query.Po
 	-rm -f ./$(DEPDIR)/sg_zone.Po
 	-rm -f ./$(DEPDIR)/sginfo.Po
 	-rm -f ./$(DEPDIR)/sgm_dd.Po
diff --git a/src/sg_bg_ctl.c b/src/sg_bg_ctl.c
index a1ca3f2..0e61d38 100644
--- a/src/sg_bg_ctl.c
+++ b/src/sg_bg_ctl.c
@@ -35,7 +35,7 @@
  * device. Based on sbc4r10.pdf .
  */
 
-static const char * version_str = "1.12 20210830";
+static const char * version_str = "1.13 20211114";
 
 #define BACKGROUND_CONTROL_SA 0x15
 
@@ -93,7 +93,7 @@
     uint8_t bcCDB[16] = {SG_SERVICE_ACTION_IN_16,
            BACKGROUND_CONTROL_SA, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
            0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     if (bo_ctl)
diff --git a/src/sg_compare_and_write.c b/src/sg_compare_and_write.c
index e376202..0a30700 100644
--- a/src/sg_compare_and_write.c
+++ b/src/sg_compare_and_write.c
@@ -56,7 +56,7 @@
 #include "sg_unaligned.h"
 #include "sg_pr2serr.h"
 
-static const char * version_str = "1.30 20210830";
+static const char * version_str = "1.31 20211114";
 
 #define DEF_BLOCK_SIZE 512
 #define DEF_NUM_BLOCKS (1)
@@ -355,7 +355,7 @@
         uint64_t ull = 0;
         struct sg_pt_base * ptvp;
         uint8_t cawCmd[COMPARE_AND_WRITE_CDB_SIZE];
-        uint8_t sense_b[SENSE_BUFF_LEN];
+        uint8_t sense_b[SENSE_BUFF_LEN] = {0};
 
         if (sg_build_scsi_cdb(cawCmd, blocks, lba, flags)) {
                 pr2serr(ME "bad cdb build, lba=0x%" PRIx64 ", blocks=%d\n",
diff --git a/src/sg_dd.c b/src/sg_dd.c
index 75430dd..2fa3750 100644
--- a/src/sg_dd.c
+++ b/src/sg_dd.c
@@ -24,7 +24,7 @@
  * command. The actual size of the SCSI READ or WRITE command block can be
  * selected with the "cdbsz" argument.
  *
- * This version is designed for the linux kernel 2, 3, 4 and 5 series.
+ * This version is designed for the Linux kernel 2, 3, 4 and 5 series.
  */
 
 #define _XOPEN_SOURCE 600
@@ -70,7 +70,7 @@
 #include "sg_unaligned.h"
 #include "sg_pr2serr.h"
 
-static const char * version_str = "6.31 20211115";
+static const char * version_str = "6.31 20211114";
 
 
 #define ME "sg_dd: "
@@ -715,7 +715,7 @@
     int res, slen;
     const uint8_t * sbp;
     uint8_t rdCmd[MAX_SCSI_CDBSZ];
-    uint8_t senseBuff[SENSE_BUFF_LEN];
+    uint8_t senseBuff[SENSE_BUFF_LEN] = {0};
     struct sg_io_hdr io_hdr;
 
     if (sg_build_scsi_cdb(rdCmd, ifp->cdbsz, blocks, from_block, do_verify,
@@ -1124,7 +1124,7 @@
     uint64_t io_addr = 0;
     const char * op_str = do_verify ? "verifying" : "writing";
     uint8_t wrCmd[MAX_SCSI_CDBSZ];
-    uint8_t senseBuff[SENSE_BUFF_LEN];
+    uint8_t senseBuff[SENSE_BUFF_LEN] = {0};
     struct sg_io_hdr io_hdr;
 
     if (sg_build_scsi_cdb(wrCmd, ofp->cdbsz, blocks, to_block, do_verify,
diff --git a/src/sg_decode_sense.c b/src/sg_decode_sense.c
index a21ceb4..76e5f6f 100644
--- a/src/sg_decode_sense.c
+++ b/src/sg_decode_sense.c
@@ -30,9 +30,9 @@
 #include "sg_unaligned.h"
 
 
-static const char * version_str = "1.24 20211104";
+static const char * version_str = "1.25 20211119";
 
-#define MAX_SENSE_LEN 1024 /* max descriptor format actually: 255+8 */
+#define MAX_SENSE_LEN 4096 /* max descriptor format actually: 255+8 */
 
 static struct option long_options[] = {
     {"binary", required_argument, 0, 'b'},
@@ -45,6 +45,7 @@
     {"hex", no_argument, 0, 'H'},
     {"in", required_argument, 0, 'i'},          /* don't advertise */
     {"inhex", required_argument, 0, 'i'},       /* same as --file */
+    {"nodecode", no_argument, 0, 'N'},
     {"nospace", no_argument, 0, 'n'},
     {"status", required_argument, 0, 's'},
     {"verbose", no_argument, 0, 'v'},
@@ -57,7 +58,7 @@
     bool do_binary;
     bool do_cdb;
     bool do_help;
-    bool do_hex;
+    bool no_decode;
     bool no_space;
     bool do_status;
     bool verbose_given;
@@ -66,6 +67,7 @@
     bool file_given;
     const char * fname;
     int es_val;
+    int hex_count;
     int sense_len;
     int sstatus;
     int verbose;
@@ -82,10 +84,11 @@
 {
   pr2serr("Usage: sg_decode_sense [--binary=BFN] [--cdb] [--err=ES] "
           "[--file=HFN]\n"
-          "                       [--help] [--hex] [--inhex=HFN] [--nospace] "
-          "[--status=SS]\n"
-          "                       [--verbose] [--version] [--write=WFN] H1 "
-          "H2 H3 ...\n"
+          "                       [--help] [--hex] [--inhex=HFN] "
+          "[--nodecode]\n"
+          "                       [--nospace] [--status=SS] [--verbose] "
+          "[--version]\n"
+          "                       [--write=WFN] H1 H2 H3 ...\n"
           "  where:\n"
           "    --binary=BFN|-b BFN    BFN is a file name to read sense "
           "data in\n"
@@ -105,6 +108,8 @@
           "                          C language style ASCII hex (instead "
           "of binary)\n"
           "    --inhex=HFN|-i HFN    same as action as --file=HFN\n"
+          "    --nodecode|-N         do not decode, may be neither sense "
+          "nor cdb\n"
           "    --nospace|-n          no spaces or other separators between "
           "pairs of\n"
           "                          hex digits (e.g. '3132330A')\n"
@@ -133,7 +138,8 @@
     char *endptr;
 
     while (1) {
-        c = getopt_long(argc, argv, "b:ce:f:hHi:ns:vVw:", long_options, NULL);
+        c = getopt_long(argc, argv, "b:ce:f:hHi:nNs:vVw:", long_options,
+                        NULL);
         if (c == -1)
             break;
 
@@ -182,11 +188,14 @@
             op->fname = optarg;
             break;
         case 'H':
-            op->do_hex = true;
+            op->hex_count++;
             break;
         case 'n':
             op->no_space = true;
             break;
+        case 'N':
+            op->no_decode = true;
+            break;
         case 's':
             if (1 != sscanf(optarg, "%x", &ui)) {
                 pr2serr("'--status=SS' expects a byte value\n");
@@ -257,6 +266,7 @@
     return 0;
 }
 
+/* Keep this format (e.g. 0xff,0x12,...) for backward compatibility */
 static void
 write2wfn(FILE * fp, struct opts_t * op)
 {
@@ -264,30 +274,23 @@
     size_t s;
     char b[128];
 
-    if (op->do_hex) {
-        for (k = 0, n = 0; k < op->sense_len; ++k) {
-            n += sprintf(b + n, "0x%02x,", op->sense[k]);
-            if (15 == (k % 16)) {
-                b[n] = '\n';
-                s = fwrite(b, 1, n + 1, fp);
-                if ((int)s != (n + 1))
-                    pr2serr("only able to write %d of %d bytes to %s\n",
-                            (int)s, n + 1, op->wfname);
-                n = 0;
-            }
-        }
-        if (n > 0) {
+    for (k = 0, n = 0; k < op->sense_len; ++k) {
+        n += sprintf(b + n, "0x%02x,", op->sense[k]);
+        if (15 == (k % 16)) {
             b[n] = '\n';
             s = fwrite(b, 1, n + 1, fp);
             if ((int)s != (n + 1))
-                pr2serr("only able to write %d of %d bytes to %s\n", (int)s,
-                        n + 1, op->wfname);
+                pr2serr("only able to write %d of %d bytes to %s\n",
+                        (int)s, n + 1, op->wfname);
+            n = 0;
         }
-    } else {
-        s = fwrite(op->sense, 1, op->sense_len, fp);
-        if ((int)s != op->sense_len)
+    }
+    if (n > 0) {
+        b[n] = '\n';
+        s = fwrite(b, 1, n + 1, fp);
+        if ((int)s != (n + 1))
             pr2serr("only able to write %d of %d bytes to %s\n", (int)s,
-                    op->sense_len, op->wfname);
+                    n + 1, op->wfname);
     }
 }
 
@@ -374,7 +377,8 @@
     if ((0 == op->sense_len) && (! op->do_binary) && (! op->file_given)) {
         if (op->do_status)
             return 0;
-        pr2serr(">> Need sense data on the command line or in a file\n\n");
+        pr2serr(">> Need sense/cdb/arbitrary data on the command line or "
+                "in a file\n\n");
         usage();
         return SG_LIB_SYNTAX_ERROR;
     }
@@ -413,19 +417,40 @@
         }
     }
 
-    if (op->sense_len) {
-        if (op->wfname) {
-            if ((fp = fopen(op->wfname, "w"))) {
+    if (op->sense_len > 0) {
+        if (op->wfname || op->hex_count) {
+            if (op->wfname) {
+                if (NULL == ((fp = fopen(op->wfname, "w")))) {
+                    err =errno;
+                    perror("open");
+                    pr2serr("trying to write to %s\n", op->wfname);
+                    ret = sg_convert_errno(err);
+                    goto fini;
+                }
+            } else
+                fp = stdout;
+
+            if (op->wfname && (1 == op->hex_count))
                 write2wfn(fp, op);
-                fclose(fp);
-            } else {
-                err =errno;
-                perror("open");
-                pr2serr("trying to write to %s\n", op->wfname);
-                ret = sg_convert_errno(err);
+            else if (op->hex_count && (2 != op->hex_count))
+                dStrHexFp((const char *)op->sense, op->sense_len,
+                           ((1 == op->hex_count) ? 1 : -1), fp);
+            else if (op->hex_count)
+                dStrHexFp((const char *)op->sense, op->sense_len, 0, fp);
+            else {
+                size_t s = fwrite(op->sense, 1, op->sense_len, fp);
+
+                if ((int)s != op->sense_len)
+                    pr2serr("only able to write %d of %d bytes to %s\n",
+                            (int)s, op->sense_len, op->wfname);
             }
-        }
-        if (op->do_cdb) {
+            if (op->wfname)
+                fclose(fp);
+        } else if (op->no_decode) {
+            if (op->verbose > 1)
+                pr2serr("Not decoding as %s because --nodecode given\n",
+                        (op->do_cdb ? "cdb" : "sense"));
+        } else if (op->do_cdb) {
             int sa, opcode;
 
             opcode = op->sense[0];
@@ -436,10 +461,12 @@
             else
                 sa = 0;
             sg_get_opcode_sa_name(opcode, sa, 0, blen, b);
-        } else
+            printf("%s\n", b);
+        } else {
             sg_get_sense_str(NULL, op->sense, op->sense_len,
                              op->verbose, blen, b);
-        printf("%s\n", b);
+            printf("%s\n", b);
+        }
     }
 fini:
     return ret;
diff --git a/src/sg_format.c b/src/sg_format.c
index 0d9b318..b6620ab 100644
--- a/src/sg_format.c
+++ b/src/sg_format.c
@@ -6,7 +6,7 @@
  *
  * Copyright (C) 2003  Grant Grundler    grundler at parisc-linux dot org
  * Copyright (C) 2003  James Bottomley       jejb at parisc-linux dot org
- * Copyright (C) 2005-2020  Douglas Gilbert   dgilbert at interlog dot com
+ * Copyright (C) 2005-2021  Douglas Gilbert   dgilbert at interlog dot com
  *
  *   This program is free software; you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -40,7 +40,7 @@
 #include "sg_pr2serr.h"
 #include "sg_pt.h"
 
-static const char * version_str = "1.63 20210830";
+static const char * version_str = "1.64 20211114";
 
 
 #define RW_ERROR_RECOVERY_PAGE 1  /* can give alternate with --mode=MP */
@@ -278,7 +278,7 @@
         int ret, res, sense_cat;
         uint8_t fm_cdb[SG_FORMAT_MEDIUM_CMDLEN] =
                                   {SG_FORMAT_MEDIUM_CMD, 0, 0, 0, 0, 0};
-        uint8_t sense_b[SENSE_BUFF_LEN];
+        uint8_t sense_b[SENSE_BUFF_LEN] = {0};
         struct sg_pt_base * ptvp;
 
         if (verify)
@@ -343,7 +343,7 @@
         int ret, res, sense_cat;
         uint8_t fwp_cdb[SG_FORMAT_WITH_PRESET_CMDLEN] =
                      {SG_FORMAT_WITH_PRESET_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-        uint8_t sense_b[SENSE_BUFF_LEN];
+        uint8_t sense_b[SENSE_BUFF_LEN] = {0};
         struct sg_pt_base * ptvp;
 
         if (immed)
diff --git a/src/sg_get_elem_status.c b/src/sg_get_elem_status.c
index c60d984..ea5b7d0 100644
--- a/src/sg_get_elem_status.c
+++ b/src/sg_get_elem_status.c
@@ -37,7 +37,7 @@
  * given SCSI device.
  */
 
-static const char * version_str = "1.07 20211104";      /* sbc5r01 */
+static const char * version_str = "1.08 20211114";      /* sbc5r01 */
 
 
 #ifndef UINT32_MAX
@@ -144,7 +144,7 @@
     uint8_t gpesCmd[16] = {SG_SERVICE_ACTION_IN_16,
                            GET_PHY_ELEM_STATUS_SA, 0, 0, 0, 0,
                            0, 0, 0, 0,  0, 0, 0, 0,  0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
     static const char * const cmd_name = "Get physical element status";
 
diff --git a/src/sg_map26.c b/src/sg_map26.c
index ddee5c4..3fca019 100644
--- a/src/sg_map26.c
+++ b/src/sg_map26.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005-2020 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.
@@ -11,7 +11,7 @@
  *
  *
  * This program maps a primary SCSI device node name to the corresponding
- * SCSI generic device node name (or vice versa). Targets linux
+ * SCSI generic device node name (or vice versa). Targets Linux
  * kernel 2.6, 3 and 4 series. Sysfs device names can also be mapped.
  */
 
@@ -47,7 +47,7 @@
 #endif
 #include "sg_lib.h"
 
-static const char * version_str = "1.17 20200501";
+static const char * version_str = "1.18 20211118";
 
 #define ME "sg_map26: "
 
diff --git a/src/sg_opcodes.c b/src/sg_opcodes.c
index ffa7823..9d90824 100644
--- a/src/sg_opcodes.c
+++ b/src/sg_opcodes.c
@@ -33,7 +33,7 @@
 
 #include "sg_pt.h"
 
-static const char * version_str = "0.71 20210830";    /* spc6r05 */
+static const char * version_str = "0.72 20211114";    /* spc6r05 */
 
 
 #define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
@@ -195,7 +195,7 @@
     int ret, res, sense_cat;
     uint8_t rsoc_cdb[RSOC_CMD_LEN] = {SG_MAINTENANCE_IN, RSOC_SA, 0,
                                               0, 0, 0, 0, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
 
     if (rctd)
         rsoc_cdb[2] |= 0x80;
@@ -259,7 +259,7 @@
     int ret, res, sense_cat;
     uint8_t rstmf_cdb[RSTMF_CMD_LEN] = {SG_MAINTENANCE_IN, RSTMF_SA,
                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
 
     if (repd)
         rstmf_cdb[2] = 0x80;
diff --git a/src/sg_read.c b/src/sg_read.c
index cd10c2e..628e0d8 100644
--- a/src/sg_read.c
+++ b/src/sg_read.c
@@ -1,6 +1,6 @@
 /*
  *  A utility program for the Linux OS SCSI generic ("sg") device driver.
- *    Copyright (C) 2001 - 2019 D. Gilbert
+ *    Copyright (C) 2001 - 2021 D. Gilbert
  * 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)
@@ -58,7 +58,7 @@
 #include "sg_pr2serr.h"
 
 
-static const char * version_str = "1.36 20191220";
+static const char * version_str = "1.37 20211114";
 
 #define DEF_BLOCK_SIZE 512
 #define DEF_BLOCKS_PER_TRANSFER 128
@@ -296,7 +296,7 @@
          bool no_dxfer)
 {
     uint8_t rdCmd[MAX_SCSI_CDBSZ];
-    uint8_t senseBuff[SENSE_BUFF_LEN];
+    uint8_t senseBuff[SENSE_BUFF_LEN] = {0};
     struct sg_io_hdr io_hdr;
 
     if (sg_build_scsi_cdb(rdCmd, cdbsz, blocks, from_block, false, fua,
diff --git a/src/sg_read_attr.c b/src/sg_read_attr.c
index 87124d3..b0bcedb 100644
--- a/src/sg_read_attr.c
+++ b/src/sg_read_attr.c
@@ -39,7 +39,7 @@
  * and decodes the response. Based on spc5r08.pdf
  */
 
-static const char * version_str = "1.15 20210830";
+static const char * version_str = "1.16 20211114";
 
 #define MAX_RATTR_BUFF_LEN (1024 * 1024)
 #define DEF_RATTR_BUFF_LEN (1024 * 8)
@@ -249,7 +249,7 @@
     uint8_t ra_cdb[SG_READ_ATTRIBUTE_CMDLEN] =
           {SG_READ_ATTRIBUTE_CMD, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
            0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     ra_cdb[1] = 0x1f & op->sa;
diff --git a/src/sg_read_buffer.c b/src/sg_read_buffer.c
index e3dae42..93c32a5 100644
--- a/src/sg_read_buffer.c
+++ b/src/sg_read_buffer.c
@@ -39,7 +39,7 @@
  * device.
  */
 
-static const char * version_str = "1.32 20210830";      /* spc6r05 */
+static const char * version_str = "1.33 20211114";      /* spc6r05 */
 
 
 #ifndef SG_READ_BUFFER_10_CMD
@@ -166,7 +166,7 @@
     int ret, res, sense_cat;
     uint8_t rb10_cb[SG_READ_BUFFER_10_CMDLEN] =
           {SG_READ_BUFFER_10_CMD, 0, 0, 0,  0, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     rb10_cb[1] = (uint8_t)(rb_mode & 0x1f);
@@ -234,7 +234,7 @@
     uint8_t rb16_cb[SG_READ_BUFFER_16_CMDLEN] =
           {SG_READ_BUFFER_16_CMD, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
            0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     rb16_cb[1] = (uint8_t)(rb_mode & 0x1f);
diff --git a/src/sg_rep_pip.c b/src/sg_rep_pip.c
index f800d81..2f6127e 100644
--- a/src/sg_rep_pip.c
+++ b/src/sg_rep_pip.c
@@ -39,7 +39,7 @@
  * sbc4r21.pdf
  */
 
-static const char * version_str = "1.02 20210830";
+static const char * version_str = "1.03 20211114";
 
 #define MAX_RPIP_BUFF_LEN (1024 * 1024)
 #define DEF_RPIP_BUFF_LEN 512
@@ -102,7 +102,7 @@
     uint8_t rz_cdb[SG_MAINT_IN_CMDLEN] =
           {SG_MAINTENANCE_IN, REPORT_PROVISIONING_INITIALIZATION_PATTERN_SA,
            0, 0,  0, 0, 0, 0,  0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     sg_put_unaligned_be32((uint32_t)mx_resp_len, rz_cdb + 6);
diff --git a/src/sg_rep_zones.c b/src/sg_rep_zones.c
index 7453ee2..2e719e7 100644
--- a/src/sg_rep_zones.c
+++ b/src/sg_rep_zones.c
@@ -39,7 +39,7 @@
  * Based on zbc2r10.pdf
  */
 
-static const char * version_str = "1.28 20210922";
+static const char * version_str = "1.29 20211117";
 
 #define MAX_RZONES_BUFF_LEN (1024 * 1024)
 #define DEF_RZONES_BUFF_LEN (1024 * 8)
@@ -105,7 +105,8 @@
 {
     if (h > 1) goto h_twoormore;
     pr2serr("Usage: "
-            "sg_rep_zones  [--domain] [--help] [--hex] [--inhex=FN]\n"
+            "sg_rep_zones  [--domain] [--force] [--help] [--hex] "
+            "[--inhex=FN]\n"
             "                     [--locator=LBA] [--maxlen=LEN] "
             "[--partial] [--raw]\n"
             "                     [--readonly] [--realm] [--report=OPT] "
@@ -113,6 +114,8 @@
             "                     [--verbose] [--version] DEVICE\n");
     pr2serr("  where:\n"
             "    --domain|-d        sends a REPORT ZONE DOMAINS command\n"
+            "    --force|-f         bypass some sanity checks when decoding "
+            "response\n"
             "    --help|-h          print out usage message, use twice for "
             "more help\n"
             "    --hex|-H           output response in hexadecimal; used "
@@ -140,7 +143,7 @@
             "    --version|-V       print version string and exit\n"
             "    --wp|-w            output write pointer only\n\n"
             "Sends a SCSI REPORT ZONES, REPORT ZONE DOMAINS or REPORT REALMS "
-            "command.\n By default sends a REPORT ZONES command. Give help "
+            "command.\nBy default sends a REPORT ZONES command. Give help "
             "option twice\n(e.g. '-hh') to see reporting options "
             "enumerated.\n");
     return;
@@ -191,7 +194,7 @@
     uint8_t rz_cdb[SG_ZONING_IN_CMDLEN] =
           {SG_ZONING_IN, REPORT_ZONES_SA, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     rz_cdb[1] = serv_act;
@@ -313,13 +316,13 @@
                  const struct opts_t * op)
 {
     uint8_t zt;
-    int k, same, zc, zones;
+    int k, same, zc, num_zd;
     uint64_t wp;
     const uint8_t * bp;
     char b[80];
 
     if ((uint32_t)act_len < decod_len) {
-        zones = (act_len - 64) / 64;
+        num_zd = (act_len >= 64) ? ((act_len - 64) / 64): 0;
         if (act_len == op->maxlen) {
             if (op->maxlen_given)
                 pr2serr("decode length [%u bytes] may be constrained by "
@@ -329,21 +332,23 @@
         } else if (op->in_fn)
             pr2serr("perhaps %s has been truncated\n", op->in_fn);
     } else
-        zones = (decod_len - 64) / 64;
+        num_zd = (decod_len - 64) / 64;
     same = rzBuff[4] & 0xf;
     if (! op->wp_only) {
         printf("  Same=%d: %s\n", same, same_desc_arr[same]);
         printf("  Maximum LBA: 0x%" PRIx64 "\n\n",
                sg_get_unaligned_be64(rzBuff + 8));
+        printf("  Reported zone starting LBA granularity: 0x%" PRIx64 "\n\n",
+               sg_get_unaligned_be64(rzBuff + 16));     /* zbc2r12 */
     }
     if (op->do_num > 0)
-            zones = (zones > op->do_num) ? op->do_num : zones;
-    if (((uint32_t)act_len < decod_len) && ((zones * 64) + 64 > act_len)) {
+            num_zd = (num_zd > op->do_num) ? op->do_num : num_zd;
+    if (((uint32_t)act_len < decod_len) && ((num_zd * 64) + 64 > act_len)) {
         pr2serr("Skip due to truncated response, try using --num= to a "
-                "value less than %d\n", zones);
+                "value less than %d\n", num_zd);
         return SG_LIB_CAT_MALFORMED;
     }
-    for (k = 0, bp = rzBuff + 64; k < zones; ++k, bp += 64) {
+    for (k = 0, bp = rzBuff + 64; k < num_zd; ++k, bp += 64) {
         if (! op->wp_only)
             printf(" Zone descriptor: %d\n", k);
         if (op->do_hex) {
@@ -374,7 +379,7 @@
             printf("   Write pointer LBA: 0x%" PRIx64 "\n", wp);
     }
     if ((op->do_num == 0) && (! op->wp_only)) {
-        if ((64 + (64 * (uint32_t)zones)) < decod_len)
+        if ((64 + (64 * (uint32_t)num_zd)) < decod_len)
             printf("\n>>> Beware: Zone list truncated, may need another "
                    "call\n");
     }
@@ -589,6 +594,10 @@
             break;
         case 's':
         case 'l':       /* --locator= and --start= are interchangeable */
+        if ((2 == strlen(optarg)) && (0 == memcmp("-1", optarg, 2))) {
+                op->st_lba = UINT64_MAX;
+                break;
+            }
             ll = sg_get_llnum(optarg);
             if (-1 == ll) {
                 pr2serr("bad argument to '--start=LBA' or '--locator=LBA\n");
diff --git a/src/sg_requests.c b/src/sg_requests.c
index a0afd36..ee0c038 100644
--- a/src/sg_requests.c
+++ b/src/sg_requests.c
@@ -34,7 +34,7 @@
  * This program issues the SCSI command REQUEST SENSE to the given SCSI device.
  */
 
-static const char * version_str = "1.38 20211001";
+static const char * version_str = "1.39 20211114";
 
 #define MAX_REQS_RESP_LEN 255
 #define DEF_REQS_RESP_LEN 252
@@ -152,7 +152,7 @@
     char b[256];
     uint8_t rs_cdb[REQUEST_SENSE_CMDLEN] =
         {REQUEST_SENSE_CMD, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
 #ifndef SG_LIB_MINGW
     bool do_time = false;
     struct timeval start_tm, end_tm;
diff --git a/src/sg_reset_wp.c b/src/sg_reset_wp.c
index 3a99a72..f3e76b3 100644
--- a/src/sg_reset_wp.c
+++ b/src/sg_reset_wp.c
@@ -37,7 +37,7 @@
  * device. Based on zbc-r04c.pdf .
  */
 
-static const char * version_str = "1.15 20210830";
+static const char * version_str = "1.16 20211114";
 
 #define SG_ZONING_OUT_CMDLEN 16
 #define RESET_WRITE_POINTER_SA 0x4
@@ -88,7 +88,7 @@
     int ret, res, sense_cat;
     uint8_t rwp_cdb[SG_ZONING_OUT_CMDLEN] = {SG_ZONING_OUT,
          RESET_WRITE_POINTER_SA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     sg_put_unaligned_be64(zid, rwp_cdb + 2);
diff --git a/src/sg_sanitize.c b/src/sg_sanitize.c
index 6865e46..ade9be7 100644
--- a/src/sg_sanitize.c
+++ b/src/sg_sanitize.c
@@ -33,7 +33,7 @@
 #include "sg_unaligned.h"
 #include "sg_pr2serr.h"
 
-static const char * version_str = "1.16 20210830";
+static const char * version_str = "1.17 20211114";
 
 /* Not all environments support the Unix sleep() */
 #if defined(MSC_VER) || defined(__MINGW32__)
@@ -185,7 +185,7 @@
     bool immed;
     int ret, res, sense_cat, timeout;
     uint8_t san_cdb[SANITIZE_OP_LEN];
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     if (op->early || op->wait)
diff --git a/src/sg_stream_ctl.c b/src/sg_stream_ctl.c
index 3f1ff84..eede6e8 100644
--- a/src/sg_stream_ctl.c
+++ b/src/sg_stream_ctl.c
@@ -35,7 +35,7 @@
  * to the given SCSI device. Based on sbc4r15.pdf .
  */
 
-static const char * version_str = "1.10 20210830";
+static const char * version_str = "1.11 20211114";
 
 #define STREAM_CONTROL_SA 0x14
 #define GET_STREAM_STATUS_SA 0x16
@@ -116,7 +116,7 @@
     int k, ret, res, sense_cat;
     uint8_t gssCdb[16] = {SG_SERVICE_ACTION_IN_16,
            GET_STREAM_STATUS_SA, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
     static const char * const cmd_name = "Get stream status";
 
@@ -184,7 +184,7 @@
     int k, ret, res, sense_cat;
     uint8_t scCdb[16] = {SG_SERVICE_ACTION_IN_16,
            STREAM_CONTROL_SA, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
     static const char * const cmd_name = "Stream control";
 
diff --git a/src/sg_sync.c b/src/sg_sync.c
index 751b25c..1a8eebf 100644
--- a/src/sg_sync.c
+++ b/src/sg_sync.c
@@ -37,7 +37,7 @@
  * (e.g. disks).
  */
 
-static const char * version_str = "1.26 20210830";
+static const char * version_str = "1.27 20211114";
 
 #define SYNCHRONIZE_CACHE16_CMD     0x91
 #define SYNCHRONIZE_CACHE16_CMDLEN  16
@@ -101,7 +101,7 @@
     uint8_t sc_cdb[SYNCHRONIZE_CACHE16_CMDLEN] =
                 {SYNCHRONIZE_CACHE16_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                  0, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     if (sync_nv)
diff --git a/src/sg_timestamp.c b/src/sg_timestamp.c
index 58f1297..b9da385 100644
--- a/src/sg_timestamp.c
+++ b/src/sg_timestamp.c
@@ -197,7 +197,7 @@
     int k, ret, res, sense_cat;
     uint8_t rt_cdb[REP_TIMESTAMP_CMDLEN] =
           {SG_MAINTENANCE_IN, REP_TIMESTAMP_SA, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     sg_put_unaligned_be32((uint32_t)mx_resp_len, rt_cdb + 6);
@@ -260,7 +260,7 @@
     uint8_t st_cdb[SET_TIMESTAMP_CMDLEN] =
           {SG_MAINTENANCE_OUT, SET_TIMESTAMP_SA, 0, 0,  0, 0, 0, 0,
            0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     sg_put_unaligned_be32(param_len, st_cdb + 6);
diff --git a/src/sg_vpd.c b/src/sg_vpd.c
index 82924da..cbd2876 100644
--- a/src/sg_vpd.c
+++ b/src/sg_vpd.c
@@ -40,7 +40,7 @@
 
 */
 
-static const char * version_str = "1.66 20210923";  /* spc6r05 + sbc5r01 */
+static const char * version_str = "1.67 20211112";  /* spc6r05 + sbc5r01 */
 
 /* standard VPD pages, in ascending page number order */
 #define VPD_SUPPORTED_VPDS 0x0
@@ -2599,7 +2599,7 @@
         printf("no limit\n");
     else
         printf("%" PRIu32 "\n", u);
-    printf("  Zone alignment mode: ");  /* zbc2r11 */
+    printf("  Zone alignment method: ");  /* zbc2r11,zbc2r12 */
     switch (b[23] & 0xf) {
     case 0:
         printf("not reported [0]\n");
diff --git a/src/sg_write_same.c b/src/sg_write_same.c
index bfbfdca..e2213fb 100644
--- a/src/sg_write_same.c
+++ b/src/sg_write_same.c
@@ -33,7 +33,7 @@
 #include "sg_unaligned.h"
 #include "sg_pr2serr.h"
 
-static const char * version_str = "1.33 20210830";
+static const char * version_str = "1.33 20211114";
 
 
 #define ME "sg_write_same: "
@@ -169,8 +169,8 @@
 {
     int ret, res, sense_cat, cdb_len;
     uint64_t llba;
-    uint8_t ws_cdb[WRITE_SAME32_LEN];
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t ws_cdb[WRITE_SAME32_LEN] = {0};
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     cdb_len = op->pref_cdb_size;
@@ -194,7 +194,6 @@
     }
     if (act_cdb_lenp)
         *act_cdb_lenp = cdb_len;
-    memset(ws_cdb, 0, sizeof(ws_cdb));
     switch (cdb_len) {
     case WRITE_SAME10_LEN:
         ws_cdb[0] = WRITE_SAME10_OP;
diff --git a/src/sg_write_verify.c b/src/sg_write_verify.c
index 1303020..384899b 100644
--- a/src/sg_write_verify.c
+++ b/src/sg_write_verify.c
@@ -40,7 +40,7 @@
 #include "sg_unaligned.h"
 #include "sg_pr2serr.h"
 
-static const char * version_str = "1.19 20210830";
+static const char * version_str = "1.20 20211114";
 
 
 #define ME "sg_write_verify: "
@@ -129,7 +129,7 @@
 {
     int res, sense_cat, ret;
     struct sg_pt_base * ptvp;
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     char b[32];
 
     snprintf(b, sizeof(b), "Write and verify(%d)", cdb_len);
diff --git a/src/sg_write_x.c b/src/sg_write_x.c
index 504fe26..18d1e7e 100644
--- a/src/sg_write_x.c
+++ b/src/sg_write_x.c
@@ -38,7 +38,7 @@
 #include "sg_unaligned.h"
 #include "sg_pr2serr.h"
 
-static const char * version_str = "1.28 20210923";
+static const char * version_str = "1.29 20211114";
 
 /* Protection Information refers to 8 bytes of extra information usually
  * associated with each logical block and is often abbreviated to PI while
@@ -1053,7 +1053,7 @@
 {
     int k, ret, res, sense_cat, cdb_len, vb, err;
     uint8_t x_cdb[WRITE_X_32_LEN];        /* use for both lengths */
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_pt_base * ptvp;
 
     memset(x_cdb, 0, sizeof(x_cdb));
diff --git a/src/sg_xcopy.c b/src/sg_xcopy.c
index 1fb09c4..4307668 100644
--- a/src/sg_xcopy.c
+++ b/src/sg_xcopy.c
@@ -27,7 +27,7 @@
  * in this case) is transferred to or from the sg device in a single SCSI
  * command.
  *
- * This version is designed for the linux kernel 2.4, 2.6, 3, 4 and 5 series.
+ * This version is designed for the Linux kernel 2.4, 2.6, 3, 4 and 5 series.
  */
 
 #define _XOPEN_SOURCE 600
diff --git a/src/sg_z_act_query.c b/src/sg_z_act_query.c
new file mode 100644
index 0000000..5958f94
--- /dev/null
+++ b/src/sg_z_act_query.c
@@ -0,0 +1,642 @@
+/*
+ * Copyright (c) 2014-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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "sg_lib.h"
+#include "sg_lib_data.h"
+#include "sg_pt.h"
+#include "sg_cmds_basic.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ *
+ * This program issues either a SCSI ZONE ACTIVATE command or a ZONE QUERY
+ * command to the given SCSI device. Based on zbc2r12.pdf .
+ */
+
+static const char * version_str = "1.01 20211117";
+
+#define SG_ZBC_IN_CMDLEN 16
+#define Z_ACTIVATE_SA 0x8
+#define Z_QUERY_SA 0x9
+
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+#define DEF_PT_TIMEOUT 60       /* 60 seconds */
+#define DEF_ALLOC_LEN 8192
+#define Z_ACT_DESC_LEN 32
+#define MAX_ACT_QUERY_BUFF_LEN (16 * 1024 * 1024)
+
+struct opts_t {
+    bool do_all;
+    bool do_activate;
+    bool do_force;
+    bool do_query;
+    bool do_raw;
+    bool maxlen_given;
+    uint8_t other_zdid;
+    uint16_t max_alloc;
+    uint16_t num_zones;
+    int hex_count;
+    int vb;
+    uint64_t st_lba;    /* Zone ID */
+    const char * device_name;
+    const char * inhex_fn;
+};
+
+static struct option long_options[] = {
+        {"activate", no_argument, 0, 'A'},
+        {"all", no_argument, 0, 'a'},
+        {"force", no_argument, 0, 'f'},
+        {"help", no_argument, 0, 'h'},
+        {"hex", no_argument, 0, 'H'},
+        {"in", required_argument, 0, 'i'},      /* silent, same as --inhex= */
+        {"inhex", required_argument, 0, 'i'},
+        {"maxlen", required_argument, 0, 'm'},
+        {"num", required_argument, 0, 'n'},
+        {"other", required_argument, 0, 'o'},
+        {"query", no_argument, 0, 'q'},
+        {"raw", no_argument, 0, 'r'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {"zone", required_argument, 0, 'z'},
+        {0, 0, 0, 0},
+};
+
+
+static void
+usage()
+{
+    pr2serr("Usage: "
+            "sg_z_act_query [--activate] [--all] [--force] [--help] "
+            "[--hex]\n"
+            "                      [--inhex=FN] [--maxlen=LEN] [--num=ZS] "
+            "[--other=ZDID]\n"
+            "                      [--query] [--raw] [--verbose] "
+            "[--version]\n"
+            "                      [--zone=ID] DEVICE\n");
+    pr2serr("  where:\n"
+            "    --activate|-A      do ZONE ACTIVATE command (def: ZONE "
+            "QUERY)\n"
+            "    --all|-a           sets the ALL flag in the cdb\n"
+            "    --force|-f         bypass some sanity checks\n"
+            "    --help|-h          print out usage message\n"
+            "    --hex|-H           print out response in hexadecimal\n"
+            "    --inhex=FN|-i FN    decode contents of FN, ignore DEVICE\n"
+            "    --maxlen=LEN|-m LEN    LEN place in cdb's allocation "
+            "length field\n"
+            "                           (def: 8192 (bytes))\n"
+            "    --num=ZS|-n ZS     ZS is the number of zones and is placed "
+            "in the cdb;\n"
+            "                       default value is 1, ignored if --all "
+            "given\n"
+            "    --other=ZDID|-o ZDID    ZDID is placed in Other zone domain "
+            "ID field\n"
+            "    --query|-q         do ZONE QUERY command (def: ZONE "
+            "QUERY)\n"
+            "    --raw|-r           output response in binary, or if "
+            "--inhex=FN is\n"
+            "                       given, then FN's contents are binary\n"
+            "    --verbose|-v       increase verbosity\n"
+            "    --version|-V       print version string and exit\n"
+            "    --zone=ID|-z ID    ID is the starting LBA of the zone "
+            "(def: 0)\n\n"
+            "Performs either a SCSI ZONE ACTIVATE command, or a ZONE QUERY "
+            "command.\nArguments to options are decimal by default, for hex "
+            "use a leading '0x'\nor a trailing 'h'. The default action is to "
+            "send a ZONE QUERY command.\n");
+}
+
+/* Invokes a ZBC IN command (with either a ZONE ACTIVATE or a ZONE QUERY
+ * service action).  Return of 0 -> success, various SG_LIB_CAT_* positive
+ * values or -1 -> other errors */
+static int
+sg_ll_zone_act_query(int sg_fd, const struct opts_t * op, void * resp,
+                     int * residp)
+{
+    uint8_t sa = op->do_activate ? Z_ACTIVATE_SA : Z_QUERY_SA;
+    int ret, res, sense_cat;
+    struct sg_pt_base * ptvp;
+    uint8_t zi_cdb[SG_ZBC_IN_CMDLEN] =
+          {SG_ZBC_IN, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0};
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
+    char b[64];
+
+    zi_cdb[1] = 0x1f & sa;
+    if (op->do_all)
+        zi_cdb[1] |= 0x80;
+
+    sg_put_unaligned_be64(op->st_lba, zi_cdb + 2);
+    sg_put_unaligned_be16(op->num_zones, zi_cdb + 10);
+    sg_put_unaligned_be16(op->max_alloc, zi_cdb + 12);
+    zi_cdb[14] = op->other_zdid;
+    sg_get_opcode_sa_name(zi_cdb[0], sa, -1, sizeof(b), b);
+    if (op->vb) {
+        char d[128];
+
+        pr2serr("    %s cdb: %s\n", b,
+                sg_get_command_str(zi_cdb, SG_ZBC_IN_CMDLEN, false,
+                                   sizeof(d), d));
+    }
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2serr("%s: out of memory\n", b);
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, zi_cdb, sizeof(zi_cdb));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (uint8_t *)resp, op->max_alloc);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, op->vb);
+    ret = sg_cmds_process_resp(ptvp, b, res, true /* noisy */,
+                               op->vb, &sense_cat);
+    if (-1 == ret) {
+        if (get_scsi_pt_transport_err(ptvp))
+            ret = SG_LIB_TRANSPORT_ERROR;
+        else
+            ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
+    } 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
+        ret = 0;
+    if (residp)
+        *residp = get_scsi_pt_resid(ptvp);
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+static const char *
+zone_condition_str(int zc, char * b, int blen, int vb)
+{
+    const char * cp;
+
+    if (NULL == b)
+        return "zone_condition_str: NULL ptr)";
+    switch (zc) {
+    case 0:
+        cp = "Not write pointer";
+        break;
+    case 1:
+        cp = "Empty";
+        break;
+    case 2:
+        cp = "Implicitly opened";
+        break;
+    case 3:
+        cp = "Explicitly opened";
+        break;
+    case 4:
+        cp = "Closed";
+        break;
+    case 5:
+        cp = "Inactive";
+        break;
+    case 0xd:
+        cp = "Read only";
+        break;
+    case 0xe:
+        cp = "Full";
+        break;
+    case 0xf:
+        cp = "Offline";
+        break;
+    default:
+        cp = NULL;
+        break;
+    }
+    if (cp) {
+        if (vb)
+            snprintf(b, blen, "%s [0x%x]", cp, zc);
+        else
+            snprintf(b, blen, "%s", cp);
+    } else
+        snprintf(b, blen, "Reserved [0x%x]", zc);
+    return b;
+}
+
+/* The allocation length field in each cdb cannot be less than 64 but the
+ * transport could still trim the response. */
+static int
+decode_z_act_query(const uint8_t * ziBuff, int act_len, uint32_t zar_len,
+                   const struct opts_t * op)
+{
+    uint8_t zt;
+    int k, zc, num_desc;
+    const uint8_t * bp;
+    char b[80];
+
+    if ((uint32_t)act_len < zar_len) {
+        num_desc = (act_len >= 64) ? ((act_len - 64) / Z_ACT_DESC_LEN) : 0;
+        if (act_len == op->max_alloc) {
+            if (op->maxlen_given)
+                pr2serr("response length [%u bytes] may be constrained by "
+                        "given --maxlen value, try increasing\n", zar_len);
+            else
+                pr2serr("perhaps --maxlen=%u needs to be used\n", zar_len);
+        } else if (op->inhex_fn)
+            pr2serr("perhaps %s has been truncated\n", op->inhex_fn);
+    } else
+        num_desc = (zar_len - 64) / Z_ACT_DESC_LEN;
+    if (act_len <= 8)
+        return 0;
+    if (0x80 & ziBuff[8]) {
+        printf("  Nz_valid=1\n");
+        if (act_len > 19)
+            printf("    Number of zones: %u\n",
+                   sg_get_unaligned_be32(ziBuff + 16));
+    } else
+        printf("  Nz_valid=0\n");
+    if (0x40 & ziBuff[8]) {
+        printf("  Ziwup_valid=1\n");
+        if (act_len > 31)
+            printf("    Zone ID with unmet prerequisite: 0x%" PRIx64 "\n",
+                   sg_get_unaligned_be64(ziBuff + 24));
+    } else
+        printf("  Ziwup_valid=0\n");
+    printf("  Activated=%d\n", (0x1 & ziBuff[8]));
+    if (act_len <= 9)
+        return 0;
+    printf("  Unmet prerequisites:\n");
+    if (0 == ziBuff[9])
+        printf("    none\n");
+    else {
+        if (0x40 & ziBuff[9])
+             printf("    security\n");
+        if (0x20 & ziBuff[9])
+             printf("    mult domn\n");
+        if (0x10 & ziBuff[9])
+             printf("    rlm rstct\n");
+        if (0x8 & ziBuff[9])
+             printf("    mult ztyp\n");
+        if (0x4 & ziBuff[9])
+             printf("    rlm align\n");
+        if (0x2 & ziBuff[9])
+             printf("    not empty\n");
+        if (0x1 & ziBuff[9])
+             printf("    not inact\n");
+    }
+    if (act_len <= 10)
+        return 0;
+    printf("  Other zone domain ID: %u\n", ziBuff[10]);
+    if (act_len <= 11)
+        return 0;
+    printf("  All: %d\n", (0x1 & ziBuff[11]));
+
+    if (((uint32_t)act_len < zar_len) &&
+        ((num_desc * Z_ACT_DESC_LEN) + 64 > act_len)) {
+        pr2serr("Skip due to truncated response, try using --num= to a "
+                "value less than %d\n", num_desc);
+        return SG_LIB_CAT_MALFORMED;
+    }
+    for (k = 0, bp = ziBuff + 64; k < num_desc; ++k, bp += Z_ACT_DESC_LEN) {
+        printf("  Zone activation descriptor: %d\n", k);
+        if (op->hex_count) {
+            hex2stdout(bp, Z_ACT_DESC_LEN, -1);
+            continue;
+        }
+        zt = bp[0] & 0xf;
+        zc = (bp[1] >> 4) & 0xf;
+        printf("    Zone type: %s\n", sg_get_zone_type_str(zt, sizeof(b),
+               b));
+        printf("    Zone condition: %s\n", zone_condition_str(zc, b,
+               sizeof(b), op->vb));
+        printf("    Zone domain ID: %u\n", bp[2]);
+        printf("    Zone range size: %" PRIu64 "\n",
+               sg_get_unaligned_be64(bp + 8));
+        printf("    Starting zone locator: 0x%" PRIx64 "\n",
+               sg_get_unaligned_be64(bp + 16));
+    }
+    return 0;
+}
+
+static void
+dStrRaw(const uint8_t * str, int len)
+{
+    int k;
+
+    for (k = 0; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    bool no_final_msg = false;
+    bool version_given = false;
+    int res, c, n, in_len, rlen, act_len;
+    int sg_fd = -1;
+    int resid = 0;
+    int verbose = 0;
+    int ret = 0;
+    uint32_t zar_len, zarr_len;
+    int64_t ll;
+    uint8_t * ziBuff = NULL;
+    uint8_t * free_zibp = NULL;
+    const char * sa_name;
+    char b[80];
+    struct opts_t opts;
+    struct opts_t * op = &opts;
+
+    memset(&opts, 0, sizeof(opts));
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "aAfhHi:m:n:o:qrvVz:", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'a':
+            op->do_all = true;
+            break;
+        case 'A':
+            op->do_activate = true;
+            break;
+        case 'f':
+            op->do_force = true;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'H':
+            ++op->hex_count;
+            break;
+        case 'i':
+            op->inhex_fn = optarg;
+            break;
+        case 'm':
+            n = sg_get_num(optarg);
+            if ((n < 0) || (n > 0xffff)) {
+                pr2serr("--maxlen= expects an argument between 0 and 0xffff "
+                        "inclusive\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->maxlen_given = true;
+            op->max_alloc = (uint16_t)n;
+            break;
+        case 'n':
+            n = sg_get_num(optarg);
+            if ((n < 0) || (n > 0xffff)) {
+                pr2serr("--num=ZS expects an argument between 0 and 0xffff "
+                        "inclusive\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->num_zones = (uint16_t)n;
+            break;
+        case 'o':
+            n = sg_get_num(optarg);
+            if ((n < 0) || (n > 0xff)) {
+                pr2serr("--other=ZDID expects an argument between 0 and 0xff "
+                        "inclusive\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->other_zdid = (uint8_t)n;
+            break;
+        case 'q':
+            op->do_query = true;
+            break;
+        case 'r':
+            op->do_raw = true;
+            break;
+        case 'v':
+            ++op->vb;
+            break;
+        case 'V':
+            version_given = true;
+            break;
+        case 'z':
+            if ((2 == strlen(optarg)) && (0 == memcmp("-1", optarg, 2))) {
+                op->st_lba = UINT64_MAX;
+                break;
+            }
+            ll = sg_get_llnum(optarg);
+            if (-1 == ll) {
+                pr2serr("bad argument to '--zone=ID'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->st_lba = (uint64_t)ll;  /* Zone ID is starting LBA */
+            break;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == op->device_name) {
+            op->device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n",
+                        argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (version_given) {
+        pr2serr("version: %s\n", version_str);
+        return 0;
+    }
+
+    if ((! op->do_all) && (0 == op->num_zones))
+        op->num_zones = 1;
+    if (op->do_activate && op->do_query){
+        pr2serr("only one of these options: --activate and --query may be "
+                "given\n\n");
+        usage();
+        return SG_LIB_CONTRADICT;
+    }
+    sa_name = op->do_activate ? "Zone activate" : "Zone query";
+    if (op->device_name && op->inhex_fn) {
+        pr2serr("ignoring DEVICE, best to give DEVICE or --inhex=FN, but "
+                "not both\n");
+        op->device_name = NULL;
+    }
+    if (op->max_alloc < 4) {
+        if (op->max_alloc > 0)
+            pr2serr("Won't accept --maxlen= of 1, 2 or 3, using %d "
+                    "instead\n", DEF_ALLOC_LEN);
+        op->max_alloc = DEF_ALLOC_LEN;
+    }
+    ziBuff = (uint8_t *)sg_memalign(op->max_alloc, 0, &free_zibp, op->vb > 3);
+    if (NULL == ziBuff) {
+        pr2serr("unable to sg_memalign %d bytes\n", op->max_alloc);
+        return sg_convert_errno(ENOMEM);
+    }
+
+    if (NULL == op->device_name) {
+        if (op->inhex_fn) {
+            if ((ret = sg_f2hex_arr(op->inhex_fn, op->do_raw, false, ziBuff,
+                                    &in_len, op->max_alloc))) {
+                if (SG_LIB_LBA_OUT_OF_RANGE == ret) {
+                    no_final_msg = true;
+                    pr2serr("... decode what we have, --maxlen=%d needs to "
+                            "be increased\n", op->max_alloc);
+                } else
+                    goto the_end;
+            }
+            if (verbose > 2)
+                pr2serr("Read %d [0x%x] bytes of user supplied data\n",
+                        in_len, in_len);
+            if (op->do_raw)
+                op->do_raw = false;    /* can interfere on decode */
+            if (in_len < 4) {
+                pr2serr("--inhex=%s only decoded %d bytes (needs 4 at "
+                        "least)\n", op->inhex_fn, in_len);
+                ret = SG_LIB_SYNTAX_ERROR;
+                goto the_end;
+            }
+            res = 0;
+            goto start_response;
+        } else {
+            pr2serr("missing device name!\n\n");
+            usage(1);
+            ret = SG_LIB_FILE_ERROR;
+            no_final_msg = true;
+            goto the_end;
+        }
+    } else
+        in_len = 0;
+
+    if (op->do_raw) {
+        if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+            perror("sg_set_binary_mode");
+            return SG_LIB_FILE_ERROR;
+        }
+    }
+
+    sg_fd = sg_cmds_open_device(op->device_name, false /* rw */, verbose);
+    if (sg_fd < 0) {
+        int err = -sg_fd;
+        if (verbose)
+            pr2serr("open error: %s: %s\n", op->device_name,
+                    safe_strerror(err));
+        ret = sg_convert_errno(err);
+        goto the_end;
+    }
+
+    res = sg_ll_zone_act_query(sg_fd, op, ziBuff, &resid);
+    ret = res;
+    if (res) {
+        if (SG_LIB_CAT_INVALID_OP == res)
+            pr2serr("%s command not supported\n", sa_name);
+        else {
+            char b[80];
+
+            sg_get_category_sense_str(res, sizeof(b), b, verbose);
+            pr2serr("%s command: %s\n", sa_name, b);
+        }
+    }
+
+start_response:
+    if (0 == res) {
+        if ((resid < 0) || (resid > op->max_alloc)) {
+            pr2serr("Unexpected resid=%d\n", resid);
+            ret = SG_LIB_CAT_MALFORMED;
+            goto the_end;
+        }
+        rlen = op->inhex_fn ? in_len : (op->max_alloc - resid);
+        if (rlen < 4) {
+            pr2serr("Decoded response length (%d) too short\n", rlen);
+            ret = SG_LIB_CAT_MALFORMED;
+            goto the_end;
+        }
+        zar_len = sg_get_unaligned_be32(ziBuff + 0) + 64;
+        zarr_len = sg_get_unaligned_be32(ziBuff + 4) + 64;
+        if ((zar_len > MAX_ACT_QUERY_BUFF_LEN) ||
+            (zarr_len > MAX_ACT_QUERY_BUFF_LEN) || (zarr_len > zar_len)) {
+            if (! op->do_force) {
+                pr2serr("zar or zarr length [%u/%u bytes] seems wild, use "
+                        "--force override\n", zar_len, zarr_len);
+                return SG_LIB_CAT_MALFORMED;
+            }
+        }
+        if (zarr_len > (uint32_t)rlen) {
+            pr2serr("zarr response length is %u bytes, but system "
+                    "reports %d bytes received??\n", zarr_len, rlen);
+            if (op->do_force)
+                act_len = rlen;
+            else {
+                pr2serr("Exiting, use --force to override\n");
+                ret = SG_LIB_CAT_MALFORMED;
+                goto the_end;
+            }
+        } else
+            act_len = zarr_len;
+        if (op->do_raw) {
+            dStrRaw(ziBuff, act_len);
+            goto the_end;
+        }
+        if (op->hex_count && (2 != op->hex_count)) {
+            hex2stdout(ziBuff, act_len, ((1 == op->hex_count) ? 1 : -1));
+            goto the_end;
+        }
+        printf("%s response:\n", sa_name);
+        if (act_len < 64) {
+            pr2serr("Zone length [%d] too short (perhaps after truncation\n)",
+                    act_len);
+            ret = SG_LIB_CAT_MALFORMED;
+            goto the_end;
+        }
+        ret = decode_z_act_query(ziBuff, act_len, zar_len, op);
+    } else if (SG_LIB_CAT_INVALID_OP == res)
+        pr2serr("%s command not supported\n", sa_name);
+    else {
+        sg_get_category_sense_str(res, sizeof(b), b, op->vb);
+        pr2serr("%s command: %s\n", sa_name, b);
+    }
+
+the_end:
+    if (sg_fd >= 0) {
+        res = sg_cmds_close_device(sg_fd);
+        if (res < 0) {
+            pr2serr("close error: %s\n", safe_strerror(-res));
+            if (0 == ret)
+                ret = sg_convert_errno(-res);
+        }
+    }
+    if (free_zibp)
+        free(free_zibp);
+    if ((0 == verbose) && (! no_final_msg)) {
+        if (! sg_if_can2stderr("sg_z_act_query failed: ", ret))
+            pr2serr("Some error occurred, try again with '-v' "
+                    "or '-vv' for more information\n");
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/src/sg_zone.c b/src/sg_zone.c
index f47e6f6..ff4dc36 100644
--- a/src/sg_zone.c
+++ b/src/sg_zone.c
@@ -37,7 +37,7 @@
  * to the given SCSI device. Based on zbc-r04c.pdf .
  */
 
-static const char * version_str = "1.16 20210830";
+static const char * version_str = "1.17 20211114";
 
 #define SG_ZONING_OUT_CMDLEN 16
 #define CLOSE_ZONE_SA 0x1
@@ -133,7 +133,7 @@
     struct sg_pt_base * ptvp;
     uint8_t zo_cdb[SG_ZONING_OUT_CMDLEN] =
           {SG_ZONING_OUT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0};
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     char b[64];
 
     zo_cdb[1] = 0x1f & sa;
diff --git a/src/sginfo.c b/src/sginfo.c
index ba094dc..51a312a 100644
--- a/src/sginfo.c
+++ b/src/sginfo.c
@@ -124,7 +124,7 @@
 #define _GNU_SOURCE 1
 #endif
 
-static const char * version_str = "2.43 [20190913]";
+static const char * version_str = "2.44 [20211114]";
 
 #include <stdio.h>
 #include <string.h>
@@ -393,7 +393,7 @@
 static int
 do_scsi_io(struct scsi_cmnd_io * sio)
 {
-    uint8_t sense_b[SENSE_BUFF_LEN];
+    uint8_t sense_b[SENSE_BUFF_LEN] = {0};
     struct sg_io_hdr io_hdr;
     struct sg_scsi_sense_hdr ssh;
     int res;
@@ -1758,7 +1758,7 @@
                     }
                     else if (!sorthead) printf("|");
                 }
-		break;
+                break;
             case 5:     /* physical sector */
                 while (len > 0) {
                     snprintf((char *)cbuffer1, 40, "%6d:%2u:%5d",
@@ -1777,7 +1777,7 @@
                     }
                     else if (!sorthead) printf("|");
                 }
-		break;
+                break;
             case 0:     /* lba (32 bit) */
                 while (len > 0) {
                     printf("%10d", getnbyte(df, 4));
@@ -1791,7 +1791,7 @@
                     else
                         printf("|");
                 }
-		break;
+                break;
             case 3:     /* lba (64 bit) */
                 while (len > 0) {
                     printf("%15" PRId64 , getnbyte_ll(df, 8));
diff --git a/src/sgm_dd.c b/src/sgm_dd.c
index 4909c82..e95fca9 100644
--- a/src/sgm_dd.c
+++ b/src/sgm_dd.c
@@ -27,7 +27,7 @@
    then only the read side will be mmap-ed, while the write side will
    use normal IO.
 
-   This version is designed for the linux kernel 2.4, 2.6, 3, 4 and 5 series.
+   This version is designed for the Linux kernel 2.4, 2.6, 3, 4 and 5 series.
 */
 
 #define _XOPEN_SOURCE 600
@@ -69,7 +69,7 @@
 #include "sg_pr2serr.h"
 
 
-static const char * version_str = "1.17 20211024";
+static const char * version_str = "1.18 20211114";
 
 #define DEF_BLOCK_SIZE 512
 #define DEF_BLOCKS_PER_TRANSFER 128
@@ -471,7 +471,7 @@
     bool print_cdb_after = false;
     int res;
     uint8_t rdCmd[MAX_SCSI_CDBSZ];
-    uint8_t senseBuff[SENSE_BUFF_LEN];
+    uint8_t senseBuff[SENSE_BUFF_LEN] = {0};
     struct sg_io_hdr io_hdr;
 
     if (sg_build_scsi_cdb(rdCmd, cdbsz, blocks, from_block, false, fua,
@@ -568,7 +568,7 @@
     bool print_cdb_after = false;
     int res;
     uint8_t wrCmd[MAX_SCSI_CDBSZ];
-    uint8_t senseBuff[SENSE_BUFF_LEN];
+    uint8_t senseBuff[SENSE_BUFF_LEN] = {0};
     struct sg_io_hdr io_hdr;
 
     if (sg_build_scsi_cdb(wrCmd, cdbsz, blocks, to_block, true, fua, dpo)) {
diff --git a/src/sgp_dd.c b/src/sgp_dd.c
index 2b2d4e5..b71bf7b 100644
--- a/src/sgp_dd.c
+++ b/src/sgp_dd.c
@@ -22,7 +22,7 @@
  * in this case) are transferred to or from the sg device in a single SCSI
  * command.
  *
- * This version is designed for the linux kernel 2.4, 2.6, 3, 4 and 5 series.
+ * This version is designed for the Linux kernel 2.4, 2.6, 3, 4 and 5 series.
  *
  * sgp_dd is a Posix threads specialization of the sg_dd utility. Both
  * sgp_dd and sg_dd only perform special tasks when one or both of the given
diff --git a/testing/bsg_queue_tst.c b/testing/bsg_queue_tst.c
index 011dcab..2761f8b 100644
--- a/testing/bsg_queue_tst.c
+++ b/testing/bsg_queue_tst.c
@@ -20,7 +20,7 @@
    The default behaviour is to "queue at head" which is useful for
    error processing but not for streaming READ and WRITE commands.
 
-*  Copyright (C) 2010-2019 D. Gilbert
+*  Copyright (C) 2010-2021 D. Gilbert
 *  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)
@@ -61,7 +61,7 @@
     struct sg_io_v4 rio_hdr;
     char * file_name = 0;
     char ebuff[EBUFF_SZ];
-    uint8_t sense_buffer[16][SENSE_BUFFER_LEN];
+    uint8_t sense_buffer[16][SENSE_BUFFER_LEN] = {0};
     int q_at_tail = 0;
 
     for (k = 1; k < argc; ++k) {
diff --git a/testing/sg_iovec_tst.cpp b/testing/sg_iovec_tst.cpp
index 6228b9e..0d28526 100644
--- a/testing/sg_iovec_tst.cpp
+++ b/testing/sg_iovec_tst.cpp
@@ -147,7 +147,7 @@
         int elem_size, int async)
 {
     uint8_t rdCmd[10] = {READ_10, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-    uint8_t senseBuff[SENSE_BUFF_LEN];
+    uint8_t senseBuff[SENSE_BUFF_LEN] = {0};
     struct sg_io_hdr io_hdr;
     struct pollfd a_poll;
     int dxfer_len = bs * num_blocks;
@@ -251,7 +251,7 @@
            int elem_size, int async)
 {
     uint8_t rdCmd[10] = {READ_10, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-    uint8_t senseBuff[SENSE_BUFF_LEN];
+    uint8_t senseBuff[SENSE_BUFF_LEN] = {0};
     struct sg_io_v4 io_hdr;
     struct pollfd a_poll;
     int dxfer_len = bs * num_blocks;
diff --git a/testing/sg_queue_tst.c b/testing/sg_queue_tst.c
index f7754fa..79f9c28 100644
--- a/testing/sg_queue_tst.c
+++ b/testing/sg_queue_tst.c
@@ -110,7 +110,7 @@
     sg_io_hdr_t rio_hdr;
     char * file_name = 0;
     char ebuff[EBUFF_SZ];
-    uint8_t sense_buffer[MAX_Q_LEN][SENSE_BUFFER_LEN];
+    uint8_t sense_buffer[MAX_Q_LEN][SENSE_BUFFER_LEN] = {0};
     int q_len = DEF_Q_LEN;
 
     for (k = 1; k < argc; ++k) {
diff --git a/testing/sg_tst_bidi.c b/testing/sg_tst_bidi.c
index 2fbb98c..6865e9a 100644
--- a/testing/sg_tst_bidi.c
+++ b/testing/sg_tst_bidi.c
@@ -182,7 +182,7 @@
     uint8_t * doutp;
     uint8_t * free_doutp = NULL;
     char ebuff[EBUFF_SZ];
-    uint8_t sense_buffer[MAX_Q_LEN][SENSE_BUFFER_LEN];
+    uint8_t sense_buffer[MAX_Q_LEN][SENSE_BUFFER_LEN] = {0};
 
     for (k = 1; k < argc; ++k) {
         if (0 == memcmp("-b=", argv[k], 3)) {
diff --git a/testing/sg_tst_ioctl.c b/testing/sg_tst_ioctl.c
index f7a421e..5f361d7 100644
--- a/testing/sg_tst_ioctl.c
+++ b/testing/sg_tst_ioctl.c
@@ -635,7 +635,7 @@
     struct sg_io_v4 * h4p;
     struct sg_io_v4 * mrq_h4p;
     struct sg_io_v4 mrq_h4;
-    uint8_t sense_buffer[SENSE_BUFFER_LEN];
+    uint8_t sense_buffer[SENSE_BUFFER_LEN] = {0};
     uint8_t inq_cdb[INQ_CMD_LEN] =      /* Device Id VPD page */
                                 {0x12, 0x1, 0x83, 0, INQ_REPLY_LEN, 0};
     uint8_t sdiag_cdb[SDIAG_CMD_LEN] =
@@ -786,7 +786,7 @@
     sg_io_hdr_t rio_hdr;
     char ebuff[EBUFF_SZ];
     char dname[256];
-    uint8_t sense_buffer[MAX_Q_LEN][SENSE_BUFFER_LEN];
+    uint8_t sense_buffer[MAX_Q_LEN][SENSE_BUFFER_LEN] = {0};
     const char * second_fname = NULL;
     const char * cp;
     char * chp;
diff --git a/testing/sg_tst_nvme.c b/testing/sg_tst_nvme.c
index 1adbfde..d92d3b5 100644
--- a/testing/sg_tst_nvme.c
+++ b/testing/sg_tst_nvme.c
@@ -381,7 +381,7 @@
     int res, k;
     uint16_t sct_sc = 0;
     uint32_t result, clen;
-    uint8_t sense_b[SENSE_BUFF_NVME_LEN];
+    uint8_t sense_b[SENSE_BUFF_NVME_LEN] = {0};
     uint8_t ucmd[128];
     char b[32];
 
@@ -514,7 +514,7 @@
 {
     int res, ret, k, 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 sense_b[SENSE_BUFF_LEN] = {0};
     uint8_t * up;
 
     if (evpd)