diff options
| author | Nishanth Aravamudan <nish.aravamudan@canonical.com> | 2016-06-16 17:26:30 (GMT) |
|---|---|---|
| committer | Nishanth Aravamudan <nish.aravamudan@canonical.com> | 2016-06-16 20:28:47 (GMT) |
| commit | 0785667f26f3901c65e62f378adba0d89ecb2dff (patch) | |
| tree | 61d54edff467f65cbaaa1bc17b0158102ade67ea | |
| parent | 3cb27f385430634b06bfd0c1152a2bf0fa361a58 (diff) | |
[ Mathieu Trudel-Lapierre ]
* Pick packaging for 1.4.0 from Debian git collab-maint.
94 files changed, 7750 insertions, 2310 deletions
@@ -7,7 +7,7 @@ of them on the right. The second table lists supported ATA commands. SCSI command sg3_utils utilities that use this SCSI command ------------ ------------------------------------------------- ATA COMMAND PASS-THROUGH(16) sg_sat_identify, sg_sat_set_features, - sg_sat_phy_event, ++ + sg_sat_phy_event, sg_sat_read_gplog ++ [sg_sat_chk_power, sg__sat_identify, sg__sat_set_features, sg_sat_smart_rd_data (previous four in the examples directory)] @@ -53,7 +53,7 @@ RECEIVE COPY DATA(LID1) sg_copy_results, ++ RECEIVE COPY FAILURE DETAILS(LID1) sg_copy_results, ++ RECEIVE COPY OPERATING PARAMETERS ddpt, sg_copy_results, sg_xcopy, ++ RECEIVE COPY STATUS(LID1) sg_copy_results, ++ -RECEIVE DIAGNOSTIC RESULTS sg_senddiag, sg_ses, ++ +RECEIVE DIAGNOSTIC RESULTS sg_senddiag, sg_ses, sg_ses_microcode ++ RECEIVE ROD TOKEN INFORMATION ddpt, ddptctl ++ REPORT ALL ROD TOKENS ddptctl ++ REPORT IDENTIFYING INFORMATION sg_ident, ++ (2) @@ -66,7 +66,7 @@ REPORT ZONES sg_rep_zones REQUEST SENSE sg_requests, ++ RESET WRITE POINTER sg_reset_wp SANITIZE sg_sanitize -SEND DIAGNOSTIC sg_senddiag, sg_ses, ++ +SEND DIAGNOSTIC sg_senddiag, sg_ses, sg_ses_microcode ++ SET IDENTIFYING INFORMATION sg_ident, ++ (3) SET TARGET PORT GROUPS sg_stpg, ++ START STOP sg_start, ++ @@ -80,6 +80,8 @@ WRITE(6) sg_dd, sgm_dd, sgp_dd WRITE(10) sg_dd, sgm_dd, sgp_dd WRITE(12) sg_dd, sgm_dd, sgp_dd WRITE(16) sg_dd, sgm_dd, sgp_dd +WRITE AND VERIFY(10) sg_write_verify +WRITE AND VERIFY(16) sg_write_verify WRITE ATOMIC(16) ddpt WRITE BUFFER sg_test_rwbuf, sg_write_buffer, ++ WRITE LONG(10) sg_write_long, ++ @@ -100,6 +102,8 @@ IDENTIFY DEVICE sg_inq, sg_scan, sg_sat_identify, IDENTIFY PACKET DEVICE sg_inq, sg_sat_identify, examples/sg__sat_identify READ LOG EXT sg_sat_phy_event, examples/sg__sat_phy_event + sg_sat_read_gplog +READ LOG DMA EXT sg_sat_read_gplog SET FEATURES sg_sat_set_features examples/sg__sat_set_features SMART READ DATA examples/sg_sat_smart_rd_data @@ -110,6 +114,9 @@ SMART READ DATA examples/sg_sat_smart_rd_data (2) this command was known as REPORT DEVICE IDENTIFIER prior to spc4r07 (3) this command was known as SET DEVICE IDENTIFIER prior to spc4r07 +Note that any SCSI command, including bi-directional and variable length +commands (whose cdb size is > 16 bytes) can be issued by the sg_raw utility. + The RECEIVE COPY * commands in SPC-4 were grouped as one command name with 4 service actions in SPC-3 and earlier. The single SPC-3 command name is RECEIVE COPY RESULTS. The two opcodes associated with all @@ -118,4 +125,4 @@ THIRD PARTY COPY IN (0x83). Douglas Gilbert -10th June 2014 +7th November 2014 @@ -51,6 +51,7 @@ Hannes Reinecke <hare at suse dot de> sg_xcopy+sg_copy_results [20120322] rescan-scsi-bus.sh patches to Kurt Garloff's v1.57 [20130715] 55-scsi-sg3_id.rules + 58-scsi-sg3_symlink.rules [20140527] + sg_sat_read_gplog [20141107] Hayashi Naoyuki <titan at culzean dot org> port to Tru64 [20060127] @@ -131,4 +132,4 @@ Trent Piepho <xyzzy at speakeasy dot org> print out some "sense key specific" Douglas Gilbert -27th May 2014 +10th November 2014 @@ -2,6 +2,54 @@ Each utility has its own version number, date of last change and some description at the top of its ".c" file. All utilities in the main directory have their own "man" pages. There is also a sg3_utils man page. +Changelog for sg3_utils-1.40 [20141110] [svn: r620] + - sg_write_verify: new utility for WRITE AND VERIFY + - sg_ses_microcode: new utility + - sg_sat_read_gplog: new utility + - sg_senddiag: add --maxlen= option + - sg_copy_results: correct response length calculations + - sg_format: make '-FFF' bypass mode sense/select + - add --mode=MP to supply alternate mode page, + default remains read-write error recovery mpage + - output unit serial number and LU name prior to + - sg_inq: expand Block limits VPD page output + - fix --cmddt output if not supported by device + - more sanity checks on vendor supplied fields + - sg_vpd: add --all option + - more TPC VPD page decoding + - add zoned block device characteristics page + - more sanity checks on vendor supplied fields + - sg_ses: fix problem with --index=sse (and ssc) + - mask status element before using as control + - defeat previous item with --mask (ignore) option + - SAS connector status element: add overcurrent bit + - handle element descriptor names that count a + trailing NULL + - add --warn option mainly for broken joins + - add optional descriptions to -ee output + - sync with ses3r07 + - sg_sanitize: add --desc and --zero options + - output unit serial number and LU name prior to + - sg_rep_zones: corrections, sync with zbc-r01c + - sg_persist: split help into two pages, '-hh' for 2nd + - sg_logs: refine tape drive output + - sg_raw: with -vvv decode T10 CDB name + - do not output/print data-in if error + - sg_opcodes: add --compact field + - sg_senddiag: add --page=PG option + - sg_reset: add words for EAGAIN from reset ioctl + - sg_sat_*: mention t_type and multiple_count fields + - win32: sg_scan: handle larger configurations + - sg_lib: trim trailing spaces in dStrHex() and friends + - sg_lib_data: sync asc/ascq codes with T10 20140924 + - clean up service action string functions + - sg_ll_unmap_v2(): fix group number + - sg_ll_inquiry(), sg_ll_mode_sense*(), + sg_ll_log_sense(): use resid to clear unfilled + data-in buffer + - sg_unaligned.h: add header for building parameters + - examples/sg_tst_async: new Linux sg test utility + Changelog for sg3_utils-1.39 [20140612] [svn: r588] - sg_rep_zones: new utility for ZBC REPORT ZONES - sg_reset_wp: new utility, ZBC RESET WRITE POINTER diff --git a/Makefile.in b/Makefile.in index 29b2ad1..15121c5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -296,6 +296,7 @@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ +os_cflags = @os_cflags@ os_libs = @os_libs@ pdfdir = @pdfdir@ prefix = @prefix@ @@ -236,9 +236,10 @@ subdirectory of the sg3_utils package: sg_read, sg_readcap, sg_read_block_limits, sg_read_buffer, sg_read_long, sg_reassign, sg_referrals, sg_request, sg_reset, sg_rmsn, sg_rtpg, sg_safte, sg_sanitize, sg_sat_identify, sg_sat_phy_event, - sg_sat_set_features, sg_scan, sg_senddiag, sg_ses, sg_start, sg_stpg, - sg_sync, sg_test_rwbuff, sg_turs, sg_unmap, sg_verify, sg_vpd, - sg_write_buffer, sg_write_long, sg_write_same, sg_wr_mode, sg_xcopy + sg_sat_read_gplog, sg_sat_set_features, sg_scan, sg_senddiag, sg_ses, + sg_ses_microcode, sg_start, sg_stpg, sg_sync, sg_test_rwbuff, sg_turs, + sg_unmap, sg_verify, sg_vpd, sg_write_buffer, sg_write_long, + sg_write_same, sg_write_verify sg_wr_mode, sg_xcopy Each of the above utilities depends on header files found in the 'include' subdirectory and library code found in the 'lib' subdirectory. Associated @@ -264,7 +265,7 @@ subdirectory: 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, sg_tst_excl, - sg_tst_excl2 and sg_tst_excl3 + sg_tst_excl2, sg_tst_excl3, sg_tst_context and sg_tst_async 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 @@ -347,6 +348,13 @@ 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. + Command line processing ======================= @@ -377,9 +385,10 @@ The more recent utilities that use "getopt_long" only are: sg_get_lba_status, sg_ident, sg_luns, sg_map26, sg_persist, sg_prevent, sg_raw, sg_read_block_limits, sg_read_buffer, sg_read_long, sg_reassign, sg_referrals, sg_requests, sg_rmsn, sg_rtpg, sg_safte, sg_sanitize, - sg_sat_identify, sg_sat_phy_event, sg_sat_set_features, sg_scan(w), - sg_ses, sg_stpg, sg_sync, sg_test_rwbuf, sg_unmap, sg_verify, sg_vpd, - sg_write_buffer, sg_write_long, sg_write_same, sg_wr_mode + sg_sat_identify, sg_sat_phy_event, sg_sat_read_gplog, sg_sat_set_features, + sg_scan(w), sg_ses, sg_ses_microcode, sg_stpg, sg_sync, sg_test_rwbuf, + sg_unmap, sg_verify, sg_vpd, sg_write_buffer, sg_write_long, + sg_write_same, sg_write_verify, sg_wr_mode Dangerous code @@ -403,4 +412,4 @@ See http://sg.danny.cz/sg/tools.html Douglas Gilbert -12th June 2014 +10th November 2014 diff --git a/README.win32 b/README.win32 index 76fdca9..cfce3ed 100644 --- a/README.win32 +++ b/README.win32 @@ -25,6 +25,11 @@ shell) builds freestanding console executables. The Unix library support is not as advanced with MinGW which has led to some timing functions being compiled out when sg3_utils is built for MinGW. +In later versions of Windows these utilities may need to be "run as +Administrator" for disks and other devices to be seen. If not those devices +will simply not be found as calls to query them fail with access permission +problems. + Supported Utilities =================== Here is a list of utilities that have been ported: @@ -203,6 +208,15 @@ MSYS or "cmd" shell. Various build options are available by giving command line options to "./configure", see the INSTALL file for generic information about the build infrastructure. +MinGW can be used to cross built on some Redhat (Linux) platforms. After +loading the cross build packages, the ./configure call in the normal +autotools sequence should be replaced by either mingw32-configure or +mingw64-configure. These scripts will set up the environment for +the cross build and then call ./configure (so this invocation should be +made in the top level of the untarred source). Options given to either +script (e.g. --enable-win32-spt-direct) will be passed through to +./configure . + Binary and Text files ===================== A problem has been reported with binary output being written in a MinGW @@ -216,4 +230,4 @@ mode" with the setmode() Windows command. Douglas Gilbert -7th March 2013 +24th September 2014 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for sg3_utils 1.39. +# Generated by GNU Autoconf 2.69 for sg3_utils 1.40. # # Report bugs to <dgilbert@interlog.com>. # @@ -590,8 +590,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='sg3_utils' PACKAGE_TARNAME='sg3_utils' -PACKAGE_VERSION='1.39' -PACKAGE_STRING='sg3_utils 1.39' +PACKAGE_VERSION='1.40' +PACKAGE_STRING='sg3_utils 1.40' PACKAGE_BUGREPORT='dgilbert@interlog.com' PACKAGE_URL='' @@ -648,6 +648,7 @@ OS_LINUX_TRUE OS_FREEBSD_FALSE OS_FREEBSD_TRUE os_libs +os_cflags GETOPT_O_FILES CPP OTOOL64 @@ -1330,7 +1331,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures sg3_utils 1.39 to adapt to many kinds of systems. +\`configure' configures sg3_utils 1.40 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1400,7 +1401,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sg3_utils 1.39:";; + short | recursive ) echo "Configuration of sg3_utils 1.40:";; esac cat <<\_ACEOF @@ -1512,7 +1513,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sg3_utils configure 1.39 +sg3_utils configure 1.40 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1790,7 +1791,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by sg3_utils $as_me 1.39, which was +It was created by sg3_utils $as_me 1.40, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2654,7 +2655,7 @@ fi # Define the identity of the package. PACKAGE='sg3_utils' - VERSION='1.39' + VERSION='1.40' cat >>confdefs.h <<_ACEOF @@ -11615,6 +11616,8 @@ cat >>confdefs.h <<_ACEOF #define SG_LIB_LINUX 1 _ACEOF + os_cflags='' + os_libs='' ;; *-*-linux*) @@ -11623,6 +11626,8 @@ cat >>confdefs.h <<_ACEOF #define SG_LIB_LINUX 1 _ACEOF + os_cflags='' + os_libs='' ;; *-*-freebsd*|*-*-kfreebsd*-gnu*) @@ -11631,6 +11636,8 @@ cat >>confdefs.h <<_ACEOF #define SG_LIB_FREEBSD 1 _ACEOF + os_cflags='' + os_libs='-lcam' ;; *-*-solaris*) @@ -11639,6 +11646,8 @@ cat >>confdefs.h <<_ACEOF #define SG_LIB_SOLARIS 1 _ACEOF + os_cflags='' + os_libs='' ;; *-*-osf*) @@ -11647,6 +11656,8 @@ cat >>confdefs.h <<_ACEOF #define SG_LIB_OSF1 1 _ACEOF + os_cflags='' + os_libs='' ;; *-*-cygwin*) @@ -11655,6 +11666,8 @@ cat >>confdefs.h <<_ACEOF #define SG_LIB_WIN32 1 _ACEOF + os_cflags='-Wno-char-subscripts' + os_libs='' ;; *-*-mingw*) @@ -11668,6 +11681,8 @@ cat >>confdefs.h <<_ACEOF #define SG_LIB_MINGW 1 _ACEOF + os_cflags='' + os_libs='' ;; *) @@ -11676,6 +11691,8 @@ cat >>confdefs.h <<_ACEOF #define SG_LIB_LINUX 1 _ACEOF + os_cflags='' + os_libs='' ;; esac @@ -12322,7 +12339,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by sg3_utils $as_me 1.39, which was +This file was extended by sg3_utils $as_me 1.40, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -12388,7 +12405,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -sg3_utils config.status 1.39 +sg3_utils config.status 1.40 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index a48a6b9..def549b 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT(sg3_utils, 1.39, dgilbert@interlog.com) +AC_INIT(sg3_utils, 1.40, dgilbert@interlog.com) AM_INIT_AUTOMAKE AM_MAINTAINER_MODE @@ -36,28 +36,36 @@ AC_DEFINE_UNQUOTED(SG_LIB_BUILD_HOST, "${host}", [sg3_utils Build Host]) case "${host}" in *-*-linux-gnu*) AC_DEFINE_UNQUOTED(SG_LIB_LINUX, 1, [sg3_utils on linux]) + AC_SUBST([os_cflags], ['']) AC_SUBST([os_libs], ['']) ;; *-*-linux*) AC_DEFINE_UNQUOTED(SG_LIB_LINUX, 1, [sg3_utils on linux]) + AC_SUBST([os_cflags], ['']) AC_SUBST([os_libs], ['']) ;; *-*-freebsd*|*-*-kfreebsd*-gnu*) AC_DEFINE_UNQUOTED(SG_LIB_FREEBSD, 1, [sg3_utils on FreeBSD]) + AC_SUBST([os_cflags], ['']) AC_SUBST([os_libs], ['-lcam']);; *-*-solaris*) AC_DEFINE_UNQUOTED(SG_LIB_SOLARIS, 1, [sg3_utils on Solaris]) + AC_SUBST([os_cflags], ['']) AC_SUBST([os_libs], ['']);; *-*-osf*) AC_DEFINE_UNQUOTED(SG_LIB_OSF1, 1, [sg3_utils on Tru64 UNIX]) + AC_SUBST([os_cflags], ['']) AC_SUBST([os_libs], ['']) ;; *-*-cygwin*) AC_DEFINE_UNQUOTED(SG_LIB_WIN32, 1, [sg3_utils on Win32]) + AC_SUBST([os_cflags], ['-Wno-char-subscripts']) AC_SUBST([os_libs], ['']) ;; *-*-mingw*) AC_DEFINE_UNQUOTED(SG_LIB_WIN32, 1, [sg3_utils on Win32]) AC_DEFINE_UNQUOTED(SG_LIB_MINGW, 1, [also MinGW environment]) + AC_SUBST([os_cflags], ['']) AC_SUBST([os_libs], ['']) ;; *) AC_DEFINE_UNQUOTED(SG_LIB_LINUX, 1, [assume sg3_utils on linux]) + AC_SUBST([os_cflags], ['']) AC_SUBST([os_libs], ['']) ;; esac diff --git a/doc/Makefile.am b/doc/Makefile.am index f9ea147..1b6fe46 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -20,10 +20,11 @@ man_MANS = \ sg_read_buffer.8 sg_read_long.8 sg_reassign.8 sg_referrals.8 \ sg_rep_zones.8 sg_requests.8 sg_reset.8 sg_reset_wp.8 sg_rmsn.8 \ sg_rtpg.8 sg_safte.8 sg_sanitize.8 sg_sat_identify.8 \ - sg_sat_phy_event.8 sg_sat_set_features.8 sg_scan.8 sg_senddiag.8 \ - sg_ses.8 sg_start.8 sg_stpg.8 sg_sync.8 sg_test_rwbuf.8 sg_turs.8 \ - sg_unmap.8 sg_verify.8 sg_vpd.8 sg_write_buffer.8 sg_write_long.8 \ - sg_write_same.8 sg_wr_mode.8 sg_xcopy.8 + sg_sat_phy_event.8 sg_sat_read_gplog.8 sg_sat_set_features.8 \ + sg_scan.8 sg_senddiag.8 sg_ses.8 sg_ses_microcode.8 sg_start.8 \ + sg_stpg.8 sg_sync.8 sg_test_rwbuf.8 sg_turs.8 sg_unmap.8 sg_verify.8 \ + sg_vpd.8 sg_write_buffer.8 sg_write_long.8 sg_write_same.8 \ + sg_write_verify.8 sg_wr_mode.8 sg_xcopy.8 distclean-local: rm -f sg_scan.8 @@ -45,12 +46,12 @@ man_MANS = \ sg_modes.8 sg_opcodes.8 sg_persist.8 sg_prevent.8 sg_raw.8 \ sg_rdac.8 sg_readcap.8 sg_read_block_limits.8 sg_read_buffer.8 \ sg_read_long.8 sg_reassign.8 sg_referrals.8 sg_rep_zones.8 \ - sg_requests.8 sg_reset_wp.8 sg_rmsn.8 \ - sg_rtpg.8 sg_safte.8 sg_sanitize.8 sg_sat_identify.8 \ - sg_sat_phy_event.8 sg_sat_set_features.8 sg_scan.8 sg_senddiag.8 \ - sg_ses.8 sg_start.8 sg_stpg.8 sg_sync.8 sg_turs.8 sg_unmap.8 \ - sg_verify.8 sg_vpd.8 sg_write_buffer.8 sg_write_long.8 \ - sg_write_same.8 sg_wr_mode.8 + sg_requests.8 sg_reset_wp.8 sg_rmsn.8 sg_rtpg.8 sg_safte.8 \ + sg_sanitize.8 sg_sat_identify.8 sg_sat_phy_event.8 \ + sg_sat_read_gplog.8 sg_sat_set_features.8 sg_scan.8 sg_senddiag.8 \ + sg_ses.8 sg_ses_microcode.8 sg_start.8 sg_stpg.8 sg_sync.8 \ + sg_turs.8 sg_unmap.8 sg_verify.8 sg_vpd.8 sg_write_buffer.8 \ + sg_write_long.8 sg_write_same.8 sg_write_verify.8 sg_wr_mode.8 distclean-local: rm -f sg_scan.8 @@ -72,12 +73,12 @@ man_MANS = \ sg_modes.8 sg_opcodes.8 sg_persist.8 sg_prevent.8 sg_raw.8 \ sg_rdac.8 sg_readcap.8 sg_read_block_limits.8 sg_read_buffer.8 \ sg_read_long.8 sg_reassign.8 sg_referrals.8 sg_rep_zones.8 \ - sg_requests.8 sg_reset_wp.8 sg_rmsn.8 \ - sg_rtpg.8 sg_safte.8 sg_sanitize.8 sg_sat_identify.8 \ - sg_sat_phy_event.8 sg_sat_set_features.8 sg_scan.8 sg_senddiag.8 \ - sg_ses.8 sg_start.8 sg_stpg.8 sg_sync.8 sg_turs.8 sg_unmap.8 \ - sg_verify.8 sg_vpd.8 sg_write_buffer.8 sg_write_long.8 \ - sg_write_same.8 sg_wr_mode.8 + sg_requests.8 sg_reset_wp.8 sg_rmsn.8 sg_rtpg.8 sg_safte.8 \ + sg_sanitize.8 sg_sat_identify.8 sg_sat_phy_event.8 \ + sg_sat_read_gplog.8 sg_sat_set_features.8 sg_scan.8 sg_senddiag.8 \ + sg_ses.8 sg_ses_microcode.8 sg_start.8 sg_stpg.8 sg_sync.8 \ + sg_turs.8 sg_unmap.8 sg_verify.8 sg_vpd.8 sg_write_buffer.8 \ + sg_write_long.8 sg_write_same.8 sg_write_verify.8 sg_wr_mode.8 distclean-local: rm -f sg_scan.8 @@ -99,12 +100,12 @@ man_MANS = \ sg_modes.8 sg_opcodes.8 sg_persist.8 sg_prevent.8 sg_raw.8 \ sg_rdac.8 sg_readcap.8 sg_read_block_limits.8 sg_read_buffer.8 \ sg_read_long.8 sg_reassign.8 sg_referrals.8 sg_rep_zones.8 \ - sg_requests.8 sg_reset_wp.8 sg_rmsn.8 \ - sg_rtpg.8 sg_safte.8 sg_sanitize.8 sg_sat_identify.8 \ - sg_sat_phy_event.8 sg_sat_set_features.8 sg_senddiag.8 sg_ses.8 \ - sg_start.8 sg_stpg.8 sg_sync.8 sg_turs.8 sg_unmap.8 sg_verify.8 \ - sg_vpd.8 sg_write_buffer.8 sg_write_long.8 sg_write_same.8 \ - sg_wr_mode.8 + sg_requests.8 sg_reset_wp.8 sg_rmsn.8 sg_rtpg.8 sg_safte.8 \ + sg_sanitize.8 sg_sat_identify.8 sg_sat_phy_event.8 \ + sg_sat_read_gplog.8 sg_sat_set_features.8 sg_senddiag.8 sg_ses.8 \ + sg_ses_microcode.8 sg_start.8 sg_stpg.8 sg_sync.8 sg_turs.8 \ + sg_unmap.8 sg_verify.8 sg_vpd.8 sg_write_buffer.8 sg_write_long.8 \ + sg_write_same.8 sg_write_verify.8 sg_wr_mode.8 endif @@ -120,12 +121,12 @@ man_MANS = \ sg_modes.8 sg_opcodes.8 sg_persist.8 sg_prevent.8 sg_raw.8 \ sg_rdac.8 sg_readcap.8 sg_read_block_limits.8 sg_read_buffer.8 \ sg_read_long.8 sg_reassign.8 sg_referrals.8 sg_rep_zones.8 \ - sg_requests.8 sg_reset_wp.8 sg_rmsn.8 \ - sg_rtpg.8 sg_safte.8 sg_sanitize.8 sg_sat_identify.8 \ - sg_sat_phy_event.8 sg_sat_set_features.8 sg_senddiag.8 sg_ses.8 \ - sg_start.8 sg_stpg.8 sg_sync.8 sg_turs.8 sg_unmap.8 sg_verify.8 \ - sg_vpd.8 sg_write_buffer.8 sg_write_long.8 sg_write_same.8 \ - sg_wr_mode.8 + sg_requests.8 sg_reset_wp.8 sg_rmsn.8 sg_rtpg.8 sg_safte.8 \ + sg_sanitize.8 sg_sat_identify.8 sg_sat_phy_event.8 \ + sg_sat_read_gplog.8 sg_sat_set_features.8 sg_senddiag.8 sg_ses.8 \ + sg_ses_microcode.8 sg_start.8 sg_stpg.8 sg_sync.8 sg_turs.8 \ + sg_unmap.8 sg_verify.8 sg_vpd.8 sg_write_buffer.8 sg_write_long.8 \ + sg_write_same.8 sg_write_verify.8 sg_wr_mode.8 endif @@ -141,12 +142,12 @@ man_MANS = \ sg_modes.8 sg_opcodes.8 sg_persist.8 sg_prevent.8 sg_raw.8 \ sg_rdac.8 sg_readcap.8 sg_read_block_limits.8 sg_read_buffer.8 \ sg_read_long.8 sg_reassign.8 sg_referrals.8 sg_rep_zones.8 \ - sg_requests.8 sg_reset_wp.8 sg_rmsn.8 \ - sg_rtpg.8 sg_safte.8 sg_sanitize.8 sg_sat_identify.8 \ - sg_sat_phy_event.8 sg_sat_set_features.8 sg_senddiag.8 sg_ses.8 \ - sg_start.8 sg_stpg.8 sg_sync.8 sg_turs.8 sg_unmap.8 sg_verify.8 \ - sg_vpd.8 sg_write_buffer.8 sg_write_long.8 sg_write_same.8 \ - sg_wr_mode.8 + sg_requests.8 sg_reset_wp.8 sg_rmsn.8 sg_rtpg.8 sg_safte.8 \ + sg_sanitize.8 sg_sat_identify.8 sg_sat_phy_event.8 \ + sg_sat_read_gplog.8 sg_sat_set_features.8 sg_senddiag.8 sg_ses.8 \ + sg_ses_microcode.8 sg_start.8 sg_stpg.8 sg_sync.8 sg_turs.8 \ + sg_unmap.8 sg_verify.8 sg_vpd.8 sg_write_buffer.8 sg_write_long.8 \ + sg_write_same.8 sg_write_verify.8 sg_wr_mode.8 endif diff --git a/doc/Makefile.in b/doc/Makefile.in index f05bd28..b5fcae7 100644 --- a/doc/Makefile.in +++ b/doc/Makefile.in @@ -243,6 +243,7 @@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ +os_cflags = @os_cflags@ os_libs = @os_libs@ pdfdir = @pdfdir@ prefix = @prefix@ @@ -265,12 +266,12 @@ top_srcdir = @top_srcdir@ @OS_FREEBSD_TRUE@ sg_modes.8 sg_opcodes.8 sg_persist.8 sg_prevent.8 sg_raw.8 \ @OS_FREEBSD_TRUE@ sg_rdac.8 sg_readcap.8 sg_read_block_limits.8 sg_read_buffer.8 \ @OS_FREEBSD_TRUE@ sg_read_long.8 sg_reassign.8 sg_referrals.8 sg_rep_zones.8 \ -@OS_FREEBSD_TRUE@ sg_requests.8 sg_reset_wp.8 sg_rmsn.8 \ -@OS_FREEBSD_TRUE@ sg_rtpg.8 sg_safte.8 sg_sanitize.8 sg_sat_identify.8 \ -@OS_FREEBSD_TRUE@ sg_sat_phy_event.8 sg_sat_set_features.8 sg_senddiag.8 sg_ses.8 \ -@OS_FREEBSD_TRUE@ sg_start.8 sg_stpg.8 sg_sync.8 sg_turs.8 sg_unmap.8 sg_verify.8 \ -@OS_FREEBSD_TRUE@ sg_vpd.8 sg_write_buffer.8 sg_write_long.8 sg_write_same.8 \ -@OS_FREEBSD_TRUE@ sg_wr_mode.8 +@OS_FREEBSD_TRUE@ sg_requests.8 sg_reset_wp.8 sg_rmsn.8 sg_rtpg.8 sg_safte.8 \ +@OS_FREEBSD_TRUE@ sg_sanitize.8 sg_sat_identify.8 sg_sat_phy_event.8 \ +@OS_FREEBSD_TRUE@ sg_sat_read_gplog.8 sg_sat_set_features.8 sg_senddiag.8 sg_ses.8 \ +@OS_FREEBSD_TRUE@ sg_ses_microcode.8 sg_start.8 sg_stpg.8 sg_sync.8 sg_turs.8 \ +@OS_FREEBSD_TRUE@ sg_unmap.8 sg_verify.8 sg_vpd.8 sg_write_buffer.8 sg_write_long.8 \ +@OS_FREEBSD_TRUE@ sg_write_same.8 sg_write_verify.8 sg_wr_mode.8 # sg_dd, sg_emc_trespass(?), sginfo, sg_map26, sg_map, sgm_dd, sgp_dd, @@ -291,10 +292,11 @@ top_srcdir = @top_srcdir@ @OS_LINUX_TRUE@ sg_read_buffer.8 sg_read_long.8 sg_reassign.8 sg_referrals.8 \ @OS_LINUX_TRUE@ sg_rep_zones.8 sg_requests.8 sg_reset.8 sg_reset_wp.8 sg_rmsn.8 \ @OS_LINUX_TRUE@ sg_rtpg.8 sg_safte.8 sg_sanitize.8 sg_sat_identify.8 \ -@OS_LINUX_TRUE@ sg_sat_phy_event.8 sg_sat_set_features.8 sg_scan.8 sg_senddiag.8 \ -@OS_LINUX_TRUE@ sg_ses.8 sg_start.8 sg_stpg.8 sg_sync.8 sg_test_rwbuf.8 sg_turs.8 \ -@OS_LINUX_TRUE@ sg_unmap.8 sg_verify.8 sg_vpd.8 sg_write_buffer.8 sg_write_long.8 \ -@OS_LINUX_TRUE@ sg_write_same.8 sg_wr_mode.8 sg_xcopy.8 +@OS_LINUX_TRUE@ sg_sat_phy_event.8 sg_sat_read_gplog.8 sg_sat_set_features.8 \ +@OS_LINUX_TRUE@ sg_scan.8 sg_senddiag.8 sg_ses.8 sg_ses_microcode.8 sg_start.8 \ +@OS_LINUX_TRUE@ sg_stpg.8 sg_sync.8 sg_test_rwbuf.8 sg_turs.8 sg_unmap.8 sg_verify.8 \ +@OS_LINUX_TRUE@ sg_vpd.8 sg_write_buffer.8 sg_write_long.8 sg_write_same.8 \ +@OS_LINUX_TRUE@ sg_write_verify.8 sg_wr_mode.8 sg_xcopy.8 @OS_OSF_TRUE@man_MANS = \ @OS_OSF_TRUE@ sg3_utils.8 sg_decode_sense.8 \ @@ -305,12 +307,12 @@ top_srcdir = @top_srcdir@ @OS_OSF_TRUE@ sg_modes.8 sg_opcodes.8 sg_persist.8 sg_prevent.8 sg_raw.8 \ @OS_OSF_TRUE@ sg_rdac.8 sg_readcap.8 sg_read_block_limits.8 sg_read_buffer.8 \ @OS_OSF_TRUE@ sg_read_long.8 sg_reassign.8 sg_referrals.8 sg_rep_zones.8 \ -@OS_OSF_TRUE@ sg_requests.8 sg_reset_wp.8 sg_rmsn.8 \ -@OS_OSF_TRUE@ sg_rtpg.8 sg_safte.8 sg_sanitize.8 sg_sat_identify.8 \ -@OS_OSF_TRUE@ sg_sat_phy_event.8 sg_sat_set_features.8 sg_senddiag.8 sg_ses.8 \ -@OS_OSF_TRUE@ sg_start.8 sg_stpg.8 sg_sync.8 sg_turs.8 sg_unmap.8 sg_verify.8 \ -@OS_OSF_TRUE@ sg_vpd.8 sg_write_buffer.8 sg_write_long.8 sg_write_same.8 \ -@OS_OSF_TRUE@ sg_wr_mode.8 +@OS_OSF_TRUE@ sg_requests.8 sg_reset_wp.8 sg_rmsn.8 sg_rtpg.8 sg_safte.8 \ +@OS_OSF_TRUE@ sg_sanitize.8 sg_sat_identify.8 sg_sat_phy_event.8 \ +@OS_OSF_TRUE@ sg_sat_read_gplog.8 sg_sat_set_features.8 sg_senddiag.8 sg_ses.8 \ +@OS_OSF_TRUE@ sg_ses_microcode.8 sg_start.8 sg_stpg.8 sg_sync.8 sg_turs.8 \ +@OS_OSF_TRUE@ sg_unmap.8 sg_verify.8 sg_vpd.8 sg_write_buffer.8 sg_write_long.8 \ +@OS_OSF_TRUE@ sg_write_same.8 sg_write_verify.8 sg_wr_mode.8 @OS_SOLARIS_TRUE@man_MANS = \ @OS_SOLARIS_TRUE@ sg3_utils.8 sg_decode_sense.8 \ @@ -321,12 +323,12 @@ top_srcdir = @top_srcdir@ @OS_SOLARIS_TRUE@ sg_modes.8 sg_opcodes.8 sg_persist.8 sg_prevent.8 sg_raw.8 \ @OS_SOLARIS_TRUE@ sg_rdac.8 sg_readcap.8 sg_read_block_limits.8 sg_read_buffer.8 \ @OS_SOLARIS_TRUE@ sg_read_long.8 sg_reassign.8 sg_referrals.8 sg_rep_zones.8 \ -@OS_SOLARIS_TRUE@ sg_requests.8 sg_reset_wp.8 sg_rmsn.8 \ -@OS_SOLARIS_TRUE@ sg_rtpg.8 sg_safte.8 sg_sanitize.8 sg_sat_identify.8 \ -@OS_SOLARIS_TRUE@ sg_sat_phy_event.8 sg_sat_set_features.8 sg_senddiag.8 sg_ses.8 \ -@OS_SOLARIS_TRUE@ sg_start.8 sg_stpg.8 sg_sync.8 sg_turs.8 sg_unmap.8 sg_verify.8 \ -@OS_SOLARIS_TRUE@ sg_vpd.8 sg_write_buffer.8 sg_write_long.8 sg_write_same.8 \ -@OS_SOLARIS_TRUE@ sg_wr_mode.8 +@OS_SOLARIS_TRUE@ sg_requests.8 sg_reset_wp.8 sg_rmsn.8 sg_rtpg.8 sg_safte.8 \ +@OS_SOLARIS_TRUE@ sg_sanitize.8 sg_sat_identify.8 sg_sat_phy_event.8 \ +@OS_SOLARIS_TRUE@ sg_sat_read_gplog.8 sg_sat_set_features.8 sg_senddiag.8 sg_ses.8 \ +@OS_SOLARIS_TRUE@ sg_ses_microcode.8 sg_start.8 sg_stpg.8 sg_sync.8 sg_turs.8 \ +@OS_SOLARIS_TRUE@ sg_unmap.8 sg_verify.8 sg_vpd.8 sg_write_buffer.8 sg_write_long.8 \ +@OS_SOLARIS_TRUE@ sg_write_same.8 sg_write_verify.8 sg_wr_mode.8 @OS_WIN32_CYGWIN_TRUE@man_MANS = \ @OS_WIN32_CYGWIN_TRUE@ sg3_utils.8 sg_decode_sense.8 \ @@ -337,12 +339,12 @@ top_srcdir = @top_srcdir@ @OS_WIN32_CYGWIN_TRUE@ sg_modes.8 sg_opcodes.8 sg_persist.8 sg_prevent.8 sg_raw.8 \ @OS_WIN32_CYGWIN_TRUE@ sg_rdac.8 sg_readcap.8 sg_read_block_limits.8 sg_read_buffer.8 \ @OS_WIN32_CYGWIN_TRUE@ sg_read_long.8 sg_reassign.8 sg_referrals.8 sg_rep_zones.8 \ -@OS_WIN32_CYGWIN_TRUE@ sg_requests.8 sg_reset_wp.8 sg_rmsn.8 \ -@OS_WIN32_CYGWIN_TRUE@ sg_rtpg.8 sg_safte.8 sg_sanitize.8 sg_sat_identify.8 \ -@OS_WIN32_CYGWIN_TRUE@ sg_sat_phy_event.8 sg_sat_set_features.8 sg_scan.8 sg_senddiag.8 \ -@OS_WIN32_CYGWIN_TRUE@ sg_ses.8 sg_start.8 sg_stpg.8 sg_sync.8 sg_turs.8 sg_unmap.8 \ -@OS_WIN32_CYGWIN_TRUE@ sg_verify.8 sg_vpd.8 sg_write_buffer.8 sg_write_long.8 \ -@OS_WIN32_CYGWIN_TRUE@ sg_write_same.8 sg_wr_mode.8 +@OS_WIN32_CYGWIN_TRUE@ sg_requests.8 sg_reset_wp.8 sg_rmsn.8 sg_rtpg.8 sg_safte.8 \ +@OS_WIN32_CYGWIN_TRUE@ sg_sanitize.8 sg_sat_identify.8 sg_sat_phy_event.8 \ +@OS_WIN32_CYGWIN_TRUE@ sg_sat_read_gplog.8 sg_sat_set_features.8 sg_scan.8 sg_senddiag.8 \ +@OS_WIN32_CYGWIN_TRUE@ sg_ses.8 sg_ses_microcode.8 sg_start.8 sg_stpg.8 sg_sync.8 \ +@OS_WIN32_CYGWIN_TRUE@ sg_turs.8 sg_unmap.8 sg_verify.8 sg_vpd.8 sg_write_buffer.8 \ +@OS_WIN32_CYGWIN_TRUE@ sg_write_long.8 sg_write_same.8 sg_write_verify.8 sg_wr_mode.8 @OS_WIN32_MINGW_TRUE@man_MANS = \ @OS_WIN32_MINGW_TRUE@ sg3_utils.8 sg_decode_sense.8 \ @@ -353,12 +355,12 @@ top_srcdir = @top_srcdir@ @OS_WIN32_MINGW_TRUE@ sg_modes.8 sg_opcodes.8 sg_persist.8 sg_prevent.8 sg_raw.8 \ @OS_WIN32_MINGW_TRUE@ sg_rdac.8 sg_readcap.8 sg_read_block_limits.8 sg_read_buffer.8 \ @OS_WIN32_MINGW_TRUE@ sg_read_long.8 sg_reassign.8 sg_referrals.8 sg_rep_zones.8 \ -@OS_WIN32_MINGW_TRUE@ sg_requests.8 sg_reset_wp.8 sg_rmsn.8 \ -@OS_WIN32_MINGW_TRUE@ sg_rtpg.8 sg_safte.8 sg_sanitize.8 sg_sat_identify.8 \ -@OS_WIN32_MINGW_TRUE@ sg_sat_phy_event.8 sg_sat_set_features.8 sg_scan.8 sg_senddiag.8 \ -@OS_WIN32_MINGW_TRUE@ sg_ses.8 sg_start.8 sg_stpg.8 sg_sync.8 sg_turs.8 sg_unmap.8 \ -@OS_WIN32_MINGW_TRUE@ sg_verify.8 sg_vpd.8 sg_write_buffer.8 sg_write_long.8 \ -@OS_WIN32_MINGW_TRUE@ sg_write_same.8 sg_wr_mode.8 +@OS_WIN32_MINGW_TRUE@ sg_requests.8 sg_reset_wp.8 sg_rmsn.8 sg_rtpg.8 sg_safte.8 \ +@OS_WIN32_MINGW_TRUE@ sg_sanitize.8 sg_sat_identify.8 sg_sat_phy_event.8 \ +@OS_WIN32_MINGW_TRUE@ sg_sat_read_gplog.8 sg_sat_set_features.8 sg_scan.8 sg_senddiag.8 \ +@OS_WIN32_MINGW_TRUE@ sg_ses.8 sg_ses_microcode.8 sg_start.8 sg_stpg.8 sg_sync.8 \ +@OS_WIN32_MINGW_TRUE@ sg_turs.8 sg_unmap.8 sg_verify.8 sg_vpd.8 sg_write_buffer.8 \ +@OS_WIN32_MINGW_TRUE@ sg_write_long.8 sg_write_same.8 sg_write_verify.8 sg_wr_mode.8 all: all-am diff --git a/doc/sg3_utils.8 b/doc/sg3_utils.8 index c4b6f25..7210c3f 100644 --- a/doc/sg3_utils.8 +++ b/doc/sg3_utils.8 @@ -1,4 +1,4 @@ -.TH SG3_UTILS "8" "June 2014" "sg3_utils\-1.39" SG3_UTILS +.TH SG3_UTILS "8" "October 2014" "sg3_utils\-1.40" SG3_UTILS .SH NAME sg3_utils \- a package of utilities for sending SCSI commands .SH SYNOPSIS @@ -20,6 +20,10 @@ 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 \fIDEVICE\fR. .PP +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. +.PP SCSI draft standards can be found at http://www.t10.org . The standards themselves can be purchased from ANSI and other standards organizations. A good overview of various SCSI standards can be seen in @@ -73,11 +77,11 @@ 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. .SH LINUX DEVICE NAMING -Normal 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 (e.g. lk 2.6 series) it is becoming more common -for almost all disks to be named like that. Partitions within a disk -are specified by a number appended to the device name, starting at +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 ). .PP Tape drives are named /dev/st<num> or /dev/nst<num> where <num> starts @@ -100,6 +104,8 @@ and alternate device names. 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. +.PP +Very little has changed in Linux device naming in the lk 3 series. .SH WINDOWS DEVICE NAMING Storage and related devices can have several device names in Windows. Probably the most common in the volume name (e.g. "D:"). There are also @@ -413,6 +419,12 @@ in the C programming language. The second hexadecimal representation is a trailing "h" or "H" as found in (storage) standards. When hex numbers are given, multipliers cannot be used. For example the decimal value "256" can be given as "0x100" or "100h". +.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 +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. .SH SCRIPTS, EXAMPLES and UTILS There are several bash shell scripts in the 'scripts' subdirectory that invoke compiled utilities (e.g. sg_readcap). Several of the scripts start diff --git a/doc/sg_compare_and_write.8 b/doc/sg_compare_and_write.8 index 68fa60e..dbdbd1a 100644 --- a/doc/sg_compare_and_write.8 +++ b/doc/sg_compare_and_write.8 @@ -31,7 +31,7 @@ starting at \fILBA\fR for \fINUM\fR blocks on the \fIDEVICE\fR. .PP The actual number of bytes transferred in the data\-out buffer of the COMPARE AND WRITE command may need to be given by the user with the -\fI\-\-xferlen=LEN\fR option. \fILEN\fR defaults to (2* \fINUM\fR * 512) +\fI\-\-xferlen=LEN\fR option. \fILEN\fR defaults to (2 * \fINUM\fR * 512) which is 1024 for the default \fINUM\fR of 1. If the block size is other than 512 then the user will need to use \fI\-\-xferlen=LEN\fR option. If protection information is given (indicated by a value of \fIWP\fR diff --git a/doc/sg_copy_results.8 b/doc/sg_copy_results.8 index ce363fe..9e67e52 100644 --- a/doc/sg_copy_results.8 +++ b/doc/sg_copy_results.8 @@ -1,4 +1,4 @@ -.TH SG_COPY_RESULTS "8" "May 2014" "sg3_utils\-1.39" SG3_UTILS +.TH SG_COPY_RESULTS "8" "September 2014" "sg3_utils\-1.40" SG3_UTILS .SH NAME sg_copy_results \- send SCSI RECEIVE COPY RESULTS command (XCOPY related) .SH SYNOPSIS @@ -83,6 +83,10 @@ length and is the maximum (byte) size of the response. \fIBTL\fR must be less than 10000 and defaults to 520. .SH NOTES Decoding of \fIRECEIVE DATA\fR service action is not implemented. +.PP +In a similar way the functionality of sg_xcopy has been ported to the +more general ddpt utility (and package), the functionality of this utility +has been ported to the ddptctl utility. .SH EXAMPLES Query the operating parameters for a device: .PP @@ -119,4 +123,4 @@ Copyright \(co 2012\-2014 Hannes Reinecke and Douglas Gilbert 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_xcopy(sg3_utils) +.B sg_xcopy(sg3_utils), ddpt,ddptctl(ddpt) diff --git a/doc/sg_decode_sense.8 b/doc/sg_decode_sense.8 index 63fe415..e68a795 100644 --- a/doc/sg_decode_sense.8 +++ b/doc/sg_decode_sense.8 @@ -1,4 +1,4 @@ -.TH SG_DECODE_SENSE "8" "May 2014" "sg3_utils\-1.39" SG3_UTILS +.TH SG_DECODE_SENSE "8" "August 2014" "sg3_utils\-1.40" SG3_UTILS .SH NAME sg_decode_sense \- decode SCSI sense data .SH SYNOPSIS @@ -87,6 +87,9 @@ Unlike most utilities in this package, this utility does not access a SCSI device (logical unit). This utility accesses a library associated with this package. Amongst other things the library decodes SCSI sense data. +.PP +T10 defined SCSI command names given a CDB can be decoded using the sg_raw +utility with the '\-vvv' 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 @@ -125,4 +128,4 @@ Copyright \(co 2010\-2014 Douglas Gilbert 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_requests(sg3_utils) +.B sg_requests,sg_raw(sg3_utils) diff --git a/doc/sg_format.8 b/doc/sg_format.8 index 13e0747..a62f5de 100644 --- a/doc/sg_format.8 +++ b/doc/sg_format.8 @@ -1,13 +1,13 @@ -.TH SG_FORMAT "8" "May 2014" "sg3_utils\-1.39" SG3_UTILS +.TH SG_FORMAT "8" "July 2014" "sg3_utils\-1.40" SG3_UTILS .SH NAME sg_format \- format, resize or modify protection information of a SCSI disk .SH SYNOPSIS .B sg_format [\fI\-\-cmplst=\fR{0|1}] [\fI\-\-count=COUNT\fR] [\fI\-\-dcrt\fR] [\fI\-\-early\fR] [\fI\-\-fmtpinfo=FPI\fR] [\fI\-\-format\fR] -[\fI\-\-help\fR] [\fI\-\-ip_def\fR] [\fI\-\-long\fR] [\fI\-\-pfu=PFU\fR] -[\fI\-\-pie=PIE\fR] [\fI\-\-pinfo\fR] [\fI\-\-poll=PT\fR] [\fI\-\-resize\fR] -[\fI\-\-rto_req\fR] [\fI\-\-security\fR] [\fI\-\-six\fR] +[\fI\-\-help\fR] [\fI\-\-ip_def\fR] [\fI\-\-long\fR] [\fI\-\-mode=MP\fR] +[\fI\-\-pfu=PFU\fR] [\fI\-\-pie=PIE\fR] [\fI\-\-pinfo\fR] [\fI\-\-poll=PT\fR] +[\fI\-\-resize\fR] [\fI\-\-rto_req\fR] [\fI\-\-security\fR] [\fI\-\-six\fR] [\fI\-\-size=SIZE\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] [\fI\-\-wait\fR] \fIDEVICE\fR .SH DESCRIPTION @@ -134,8 +134,15 @@ issue a SCSI FORMAT UNIT command. This option is required to change the block size of a disk. The user is given a 15 second count down to ponder the wisdom of doing this, during which time control\-C (amongst other Unix commands) can be used to kill this process -before it does any damage. See NOTES section for implementation details and -EXAMPLES section for typical use. +before it does any damage. +.br +When used three times (or more) the preliminary MODE SENSE and SELECT +commands are bypassed, leaving only the initial INQUIRY and FORMAT UNIT +commands. This is for emergency use (e.g. when the MODE SENSE/SELECT +commands are not working) and cannot change the logical block size. +.br +See NOTES section for implementation details and EXAMPLES section for typical +use. .TP \fB\-h\fR, \fB\-\-help\fR print out the usage information then exit. @@ -160,6 +167,11 @@ set internally. This option does not set the LONGLIST bit in the FORMAT UNIT command. The LONGLIST bit is set as required depending other parameters (e.g. when '\-\-pie=PIE' is greater than zero). .TP +\fB\-M\fR, \fB\-\-mode\fR=\fIMP\fR +\fIMP\fR is a mode page number (0 to 62 inclusive) that will be used for +reading and perhaps changing the device logical block size. The default +is 1 which is the Read\-Write Error Recovery mode page. +.TP \fB\-P\fR, \fB\-\-pfu\fR=\fIPFU\fR sets the "Protection Field Usage" field in the parameter block associated with a FORMAT UNIT command to \fIPFU\fR. The default value is 0, the only @@ -251,7 +263,7 @@ contains those detected during the format operation, the DLIST is a list of defects that can be given to the format operation. The GLIST is the grown list which starts in the format process as CLIST+DLIST and can "grow" later due to automatic reallocation (see the ARRE and AWRE bits in the -read\-write error recovery mode page (see sdparm(8))) and use of the +Read\-Write Error Recovery mode page (see sdparm(8))) and use of the SCSI REASSIGN BLOCKS command (see sg_reassign(8)). .PP The CMPLST bit (controlled by the \fI\-\-cmplst=\fR0|1 option) determines diff --git a/doc/sg_inq.8 b/doc/sg_inq.8 index 504298e..c9d3262 100644 --- a/doc/sg_inq.8 +++ b/doc/sg_inq.8 @@ -1,4 +1,4 @@ -.TH SG_INQ "8" "May 2014" "sg3_utils\-1.39" SG3_UTILS +.TH SG_INQ "8" "July 2014" "sg3_utils\-1.40" SG3_UTILS .SH NAME sg_inq \- issue SCSI INQUIRY command and/or decode its response .SH SYNOPSIS diff --git a/doc/sg_opcodes.8 b/doc/sg_opcodes.8 index 2d0920f..c950811 100644 --- a/doc/sg_opcodes.8 +++ b/doc/sg_opcodes.8 @@ -3,14 +3,15 @@ sg_opcodes \- report supported SCSI commands or task management functions .SH SYNOPSIS .B sg_opcodes -[\fI\-\-alpha\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-no-inquiry\fR] -[\fI\-\-opcode=OP\fR] [\fI\-\-raw\fR] [\fI\-\-rctd\fR] [\fI\-\-repd\fR] -[\fI\-\-sa=SA\fR] [\fI\-\-tmf\fR] [\fI\-\-unsorted\fR] [\fI\-\-verbose\fR] -[\fI\-\-version\fR] \fIDEVICE\fR +[\fI\-\-alpha\fR] [\fI\-\-compact\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR] +[\fI\-\-no-inquiry\fR] [\fI\-\-opcode=OP\fR] [\fI\-\-raw\fR] [\fI\-\-rctd\fR] +[\fI\-\-repd\fR] [\fI\-\-sa=SA\fR] [\fI\-\-tmf\fR] [\fI\-\-unsorted\fR] +[\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR .PP .B sg_opcodes -[\fI\-a\fR] [\fI\-n\fR] [\fI\-o=OP\fR] [\fI\-q\fR] [\fI\-R\fR] [\fI\-s=SA\fR] -[\fI\-t\fR] [\fI\-u\fR] [\fI\-v\fR] [\fI\-V\fR] [\fI\-?\fR] \fIDEVICE\fR +[\fI\-a\fR] [\fI\-c\fR] [\fI\-n\fR] [\fI\-o=OP\fR] [\fI\-q\fR] [\fI\-R\fR] +[\fI\-s=SA\fR] [\fI\-t\fR] [\fI\-u\fR] [\fI\-v\fR] [\fI\-V\fR] [\fI\-?\fR] +\fIDEVICE\fR .SH DESCRIPTION .\" Add any additional description here .PP @@ -47,6 +48,16 @@ name (alphabetically). When this option and the \fB\-\-unsorted\fR option are both _not_ given then the list of supported commands is sorted numerically (first by operation code and then by service action). .TP +\fB\-c\fR, \fB\-\-compact\fR +some command names, especially those associated with some service actions, +are getting longer. This may cause line wrap in the one line per command +mode on some terminals. When this option is given the opcode and service +action fields are combined into a single field with the service action, +prefixed by a comma shown directly after the opcode. If there is no service +action associated with the command, then the comma and the service action +are not shown after the opcode. The CDB size field is not shown when this +option is given. +.TP \fB\-h\fR, \fB\-\-help\fR outputs the usage message summarizing command line options then exits. Ignores \fIDEVICE\fR if given. diff --git a/doc/sg_persist.8 b/doc/sg_persist.8 index e62c49a..96b7e9d 100644 --- a/doc/sg_persist.8 +++ b/doc/sg_persist.8 @@ -1,4 +1,4 @@ -.TH SG_PERSIST "8" "May 2014" "sg3_utils\-1.39" SG3_UTILS +.TH SG_PERSIST "8" "October 2014" "sg3_utils\-1.40" SG3_UTILS .SH NAME sg_persist \- use SCSI PERSISTENT RESERVE command to access registrations and reservations @@ -82,7 +82,8 @@ these two: 'sg_persist \-\-device=/dev/sg2' and 'sg_persist /dev/sg2' are equivalent. .TP \fB\-h\fR, \fB\-\-help\fR -output a usage message. +output a usage message showing main options. Use twice (e.g. '\-hh') for +the other option and more help. .TP \fB\-H\fR, \fB\-\-hex\fR the response to a valid PRIN sub\-command will be output in hexadecimal. @@ -127,7 +128,7 @@ Default value is 0. This option is needed by some PROUT sub\-commands. Preempt is a sub\-command of the PROUT command. Preempts the existing persistent reservation (identified by \fI\-\-param\-sark=SARK\fR) with the registration key that is registered for this I_T_L nexus (identified -by \fI\-\-param\-rk=RK\fR). If a new reservation is establised as +by \fI\-\-param\-rk=RK\fR). If a new reservation is established as a result of the preemption then the supplied \fI\-\-prout\-type=TYPE\fR is used as the type for this new reservation. .TP @@ -135,7 +136,7 @@ is used as the type for this new reservation. Preempt and Abort is a sub\-command of the PROUT command. Preempts the existing persistent reservation (identified by \fI\-\-param\-sark=SARK\fR) with the registration key that is registered for this I_T_L nexus (identified -by \fI\-\-param\-rk=RK\fR). If a new reservation is establised as +by \fI\-\-param\-rk=RK\fR). If a new reservation is established as a result of the preemption then the supplied \fI\-\-prout\-type=TYPE\fR is used as the type for this new reservation. ACA and other pending tasks are aborted. @@ -278,7 +279,7 @@ form is an iSCSI qualified name. Apart from "iqn" the other transport specific leadin string may be given in upper case (e.g. "FCP,"). .PP The "fcp," form should be followed by 16 ASCII hex digits that represent an -initiator's N_PORT_NAME (e.g. "fcp,10000000C9F3A571"). The "spi," form shoul +initiator's N_PORT_NAME (e.g. "fcp,10000000C9F3A571"). The "spi," form should be followed by "<scsi_address>,<relative_target_port_identifier>" (both decimal numbers). The "sbp," form should be followed by 16 ASCII hex digits that represent an initiator's EUI\-64 name. The "srp," form should be @@ -306,7 +307,7 @@ then stdin (standard input) is read. If the \fITIDS\fR argument is of the form "file=<name>" than a file called <name> is read. A valid SPC\-4 TransportID is built from the transport specific string outlined in the previous paragraphs. The parsing of the data read is -realtively simple. Empty lines are ignored. Everything from and including +relatively simple. Empty lines are ignored. Everything from and including a "#" on a line is ignored. Leading spaces and tabs are ignored. There can be one transportID per line. The transportID can either be a comma, space or tab separated list of ASCII hex bytes that represent a diff --git a/doc/sg_raw.8 b/doc/sg_raw.8 index 2ef7dc2..fa3ba84 100644 --- a/doc/sg_raw.8 +++ b/doc/sg_raw.8 @@ -1,4 +1,4 @@ -.TH SG_RAW "8" "December 2012" "sg3_utils\-1.35" SG3_UTILS +.TH SG_RAW "8" "October 2014" "sg3_utils\-1.40" SG3_UTILS .SH NAME sg_raw \- send arbitrary SCSI command to a device .SH SYNOPSIS @@ -77,7 +77,15 @@ Product Pages of a devices which may contain useful information. .PP The ability to send more than a 16 byte CDB (in some cases 12 byte CDB) may be restricted by the pass\-through interface, the low level driver -or the transport. +or the transport. In the Linux series 3 kernels, the bsg driver can +handle longer CDBs, block devices (e.g. /dev/sdc) accessed via the +SG_IO ioctl cannot handle CDBs longer than 16 bytes, and the sg driver +can handle longer CDBs from lk 3.17 . +.PP +The CDB command name defined by T10 for the given CDB is shown if +the '\-vvv' option is given. The command line syntax still needs to be +correct, so /dev/null may be used for the \fIDEVICE\fR since the CDB +command name decoding is done before the \fIDEVICE\fR is checked. .SH EXAMPLES These examples, apart from the last one, use Linux device names. For suitable device names in other supported Operating Systems see the @@ -103,7 +111,7 @@ When used in conjunction with the previous example, if both commands work then 'cmp i512.bin o512.bin' should show a match. .TP sg_raw \-\-infile=urandom.bin \-\-send=512 \-\-request=512 \-\-outfile=out.bin "/dev/bsg/7:0:0:0" 53 00 00 00 00 00 00 00 01 00 -This is a bidirectional XDREADWRITE(10) command being sent via a Linux +This is a bidirectional XDWRITEREAD(10) command being sent via a Linux bsg device. Note that data is being read from "urandom.bin" and sent to the device (data\-out) while resulting data (data\-in) is placed in the "out.bin" file. Also note the length of both is 512 bytes @@ -125,10 +133,9 @@ Written by Ingo van Lil .SH "REPORTING BUGS" Report bugs to <inguin at gmx dot de>. .SH COPYRIGHT -Copyright \(co 2001\-2012 Ingo van Lil +Copyright \(co 2001\-2014 Ingo van Lil .br This software is distributed under the GPL version 2. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" .B sg_inq, sg_vpd, sg3_utils (sg3_utils), plscsi - diff --git a/doc/sg_rep_zones.8 b/doc/sg_rep_zones.8 index 6adce7b..fb9d3f2 100644 --- a/doc/sg_rep_zones.8 +++ b/doc/sg_rep_zones.8 @@ -1,4 +1,4 @@ -.TH SG_REP_ZONES "8" "June 2014" "sg3_utils\-1.39" SG3_UTILS +.TH SG_REP_ZONES "8" "October 2014" "sg3_utils\-1.40" SG3_UTILS .SH NAME sg_rep_zones \- send SCSI REPORT ZONES command .SH SYNOPSIS @@ -10,8 +10,8 @@ sg_rep_zones \- send SCSI REPORT ZONES command .\" Add any additional description here .PP Sends a SCSI REPORT ZONES command to \fIDEVICE\fR and outputs the data -returned. This command is found in the ZBC draft standard whose most -recent revision is 1a. +returned. This command is found in the ZBC draft standard, revision +1c (zbc\-r01c.pdf). .SH OPTIONS Arguments to long options are mandatory for short options as well. .TP @@ -42,14 +42,14 @@ where \fIOPT\fR will become the contents of the REPORTING OPTION field in the cdb. The default value is 0 which means report a list or all zones. Some other values are 1 for list full zones; 2 for list all open zones; 3 for list all empty zones; 4 for list all read only zones; 5 for list all -offline zones; and 6 for list all zones with the RESET bit set. +offline zones; 6 for list all zones with the RESET bit set; and 7 for list +all zones with the NON_SEQ bit set. .TP \fB\-s\fR, \fB\-\-start\fR=\fILBA\fR -where \fILBA\fR is a zone start logical block address (LBA) of the first -zone to be reported. The default value is 0. \fILBA\fR must be a zone -start LBA otherwise an ILLEGAL REQUEST sense key is returned. Assumed to be -in decimal unless prefixed with '0x' or has a trailing 'h' which indicate -hexadecimal. +where \fILBA\fR is at the start or within the first zone to be reported. The +default value is 0. If \fILBA\fR is not a zone start LBA then the preceding +zone start LBA is used for reporting. Assumed to be in decimal unless +prefixed with '0x' or has a trailing 'h' which indicate hexadecimal. .TP \fB\-v\fR, \fB\-\-verbose\fR increase the level of verbosity, (i.e. debug output). diff --git a/doc/sg_reset.8 b/doc/sg_reset.8 index 6695807..7ec309c 100644 --- a/doc/sg_reset.8 +++ b/doc/sg_reset.8 @@ -1,4 +1,4 @@ -.TH SG_RESET "8" "August 2013" "sg3_utils\-1.37" SG3_UTILS +.TH SG_RESET "8" "October 2014" "sg3_utils\-1.40" SG3_UTILS .SH NAME sg_reset \- sends SCSI device, target, bus or host reset; or checks reset state @@ -79,8 +79,7 @@ large storage configurations the escalation may be (very) undesirable. .PP This utility calls the SG_SCSI_RESET ioctl and as of lk 3.10.7 the \fI\-\-no-esc\fR option is not supported. Patches to implement this -functionality have not been accepted. If you want it, post your concern -to linux-scsi@vger.kernel.org which is the linux-scsi list. +functionality may be accepted in lk 3.18 or 3.19 . .PP SAM\-4 and 5 define a hard reset, a LOGICAL UNIT RESET and a I_T NEXUS RESET. A hard reset is defined to be a power on condition, a microcode @@ -123,7 +122,7 @@ sg_reset: completed host reset .SH AUTHORS Written by Douglas Gilbert. .SH COPYRIGHT -Copyright \(co 1999\-2013 Douglas Gilbert +Copyright \(co 1999\-2014 Douglas Gilbert .br This software is distributed under the GPL version 2. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/doc/sg_reset_wp.8 b/doc/sg_reset_wp.8 index ef20be6..d44c16a 100644 --- a/doc/sg_reset_wp.8 +++ b/doc/sg_reset_wp.8 @@ -1,4 +1,4 @@ -.TH SG_RESET_WP "8" "June 2014" "sg3_utils\-1.39" SG3_UTILS +.TH SG_RESET_WP "8" "October 2014" "sg3_utils\-1.40" SG3_UTILS .SH NAME sg_reset_wp \- send SCSI RESET WRITE POINTER command .SH SYNOPSIS @@ -9,7 +9,7 @@ sg_reset_wp \- send SCSI RESET WRITE POINTER command .\" Add any additional description here .PP Sends a SCSI RESET WRITE POINTER command to the \fIDEVICE\fR. This command -is found in the ZBC draft standard whose most recent revision is 1a. +is found in the ZBC draft standard revision 1c (zbc\-r01c.pdf). .SH OPTIONS Arguments to long options are mandatory for short options as well. .TP diff --git a/doc/sg_sanitize.8 b/doc/sg_sanitize.8 index 0e86bb9..2ba0b5d 100644 --- a/doc/sg_sanitize.8 +++ b/doc/sg_sanitize.8 @@ -1,13 +1,14 @@ -.TH SG_SANITIZE "8" "September 2013" "sg3_utils\-1.37" SG3_UTILS +.TH SG_SANITIZE "8" "September 2014" "sg3_utils\-1.40" SG3_UTILS .SH NAME sg_sanitize \- remove all user data from disk with SCSI SANITIZE command .SH SYNOPSIS .B sg_sanitize [\fI\-\-ause\fR] [\fI\-\-block\fR] [\fI\-\-count=OC\fR] [\fI\-\-crypto\fR] -[\fI\-\-early\fR] [\fI\-\-fail\fR] [\fI\-\-help\fR] [\fI\-\-invert\fR] -[\fI\-\-ipl=LEN\fR] [\fI\-\-overwrite\fR] [\fI\-\-pattern=PF\fR] -[\fI\-\-quick\fR] [\fI\-\-test=TE\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] -[\fI\-\-wait\fR] \fIDEVICE\fR +[\fI\-\-desc\fR] [\fI\-\-early\fR] [\fI\-\-fail\fR] [\fI\-\-help\fR] +[\fI\-\-invert\fR] [\fI\-\-ipl=LEN\fR] [\fI\-\-overwrite\fR] +[\fI\-\-pattern=PF\fR] [\fI\-\-quick\fR] [\fI\-\-test=TE\fR] +[\fI\-\-verbose\fR] [\fI\-\-version\fR] [\fI\-\-wait\fR] [\fI\-\-zero\fR] +\fIDEVICE\fR .SH DESCRIPTION .\" Add any additional description here .PP @@ -32,7 +33,7 @@ the \fI\-\-quick\fR option is given in which case the sanitize operation starts immediately. The disk's INQUIRY response strings are printed out just in case the wrong \fIDEVICE\fR has been given. .PP -If the \fI\-\-early\fR option is given this utility will exit soon +If the \fI\-\-early\fR option is given then this utility will exit soon after starting the SANITIZE command with the IMMED bit set. The user can monitor the progress of the sanitize operation with the "sg_request \-\-num=9999 \-\-progress" which sends a REQUEST SENSE @@ -51,7 +52,7 @@ option name. .TP \fB\-A\fR, \fB\-\-ause\fR sets the AUSE bit in the cdb. AUSE is an acronym for "allow unrestricted -sanitize exit". Default action is to leave the bit cleared. +sanitize exit". The default action is to leave the AUSE bit cleared. .TP \fB\-B\fR, \fB\-\-block\fR perform a "block erase" sanitize operation. @@ -64,11 +65,19 @@ the default. \fB\-C\fR, \fB\-\-crypto\fR perform a "cryptographic erase" sanitize operation. .TP +\fB\-d\fR, \fB\-\-desc\fR +sets the DESC field in the REQUEST SENSE command used for polling. By +default this field is set to zero. A REQUEST SENSE polling loop is +used after the SANITIZE command is issued (assuming that neither the +\fI\-\-early\fR nor the \fI\-\-wait\fR option have been given) to check +on the progress of this command as it can take some time. +.TP \fB\-e\fR, \fB\-\-early\fR the default action of this utility is to poll the disk every 60 seconds to fetch the progress indication until the sanitize is finished. When this -option is given this utility will exit "early" as soon as the sanitize -has commenced. This option and \fI\-\-wait\fR cannot both be given. +option is given this utility will exit "early" as soon as the SANITIZE +command with the IMMED bit set to 1 has been acknowledged. This option and +\fI\-\-wait\fR cannot both be given. .TP \fB\-F\fR, \fB\-\-fail\fR perform an "exit failure mode" sanitize operation. Typically requires the @@ -79,10 +88,11 @@ print out the usage information then exit. .TP \fB\-i\fR, \fB\-\-ipl\fR=\fILEN\fR set the initialization pattern length to \fILEN\fR bytes. By default it is -set to the length of the pattern file (\fIPF\fR). Only active when the -\fI\-\-overwrite\fR option is also given. It is the number of bytes from -the \fIPF\fR file that will be used as the initialization pattern. The -minimum size is 1 byte and the maximum is the logical block size of the +set to the length of the pattern file (\fIPF\fR) or 4 if the \fI\-\-zero\fR +option is given. Only active when the \fI\-\-overwrite\fR option is also +given. It is the number of bytes from the \fIPF\fR file that will be used +as the initialization pattern (if the \fI\-\-zero\fR option is not given). +The minimum size is 1 byte and the maximum is the logical block size of the \fIDEVICE\fR (and not to exceed 65535). If \fILEN\fR exceeds the \fIPF\fR file size then the initialization pattern is padded with zeros. .TP @@ -93,8 +103,8 @@ INVERT bit. When the INVERT bit is set then the initialization pattern is inverted between consecutive overwrite passes. .TP \fB\-O\fR, \fB\-\-overwrite\fR -perform an "overwrite" sanitize operation. When this option is given -then the \fI\-\-pattern=PF\fR option is required. +perform an "overwrite" sanitize operation. When this option is given then +the \fI\-\-pattern=PF\fR or the \fI\-\-zero\fR option is required. .TP \fB\-p\fR, \fB\-\-pattern\fR=\fIPF\fR where \fIPF\fR is the filename of a file containing the initialization @@ -128,12 +138,24 @@ operation is complete (or fails). When this option is given (and the \fI\-\-early\fR option is not given) then the SANITIZE command is started with the IMMED bit clear. For a large disk this might take hours. [A cryptographic erase operation could potentially be very quick.] +.TP +\fB\-z\fR, \fB\-\-zero\fR +with an "overwrite" sanitize operation this option causes the initialization +pattern to be zero (4 zeros are used as the initialization pattern). Cannot +be used with the \fI\-\-pattern=PF\fR option. If this option is given +twice (e.g. '\-zz') then 0xff is used as the initialization byte. .SH NOTES The SCSI SANITIZE command is closely related to the ATA SANITIZE command, both are relatively new with the ATA command being the first one defined. The SCSI to ATA Translation (SAT) definition for the SCSI SANITIZE command appeared in the SAT\-3 revision 4 draft. .PP +When a SAT layer is used to a (S)ATA disk then for OVERWRITE the +initialization pattern must be 4 bytes long. So this means either the +\fI\-\-zero\fR option may be given, or a pattern file (with the +\fI\-\-pattern=PF\fR option) that is 4 bytes long or set to that +length with the \fI\-\-ipl=LEN\fR option. +.PP The SCSI SANITIZE command is related to the SCSI FORMAT UNIT command. It is likely that a block erase sanitize operation would take a similar amount of time as a format on the same disk (e.g. 9 hours for a 2 Terabyte @@ -183,7 +205,7 @@ If the 15 second reconsideration time is not required add the .PP sg_sanitize \-\-block \-\-quick \-\-early /dev/sdm .PP -To do an "overwrite" sanitize a pattern file is required: +To do an "overwrite" sanitize a pattern file may be given: .PP sg_sanitize \-\-overwrite \-\-pattern=rand.img /dev/sdm .PP @@ -193,6 +215,8 @@ sanitize operation: .PP sg_sanitize \-\-overwrite \-\-pattern=rand.img \-\-ipl=17 /dev/sdm .PP +To overwrite with zeros use: + sg_sanitize \-\-overwrite \-\-zero /dev/sdm .SH EXIT STATUS The exit status of sg_sanitize is 0 when it is successful. Otherwise see the sg3_utils(8) man page. Unless the \fI\-\-wait\fR option is given, the @@ -202,7 +226,7 @@ Written by Douglas Gilbert. .SH "REPORTING BUGS" Report bugs to <dgilbert at interlog dot com>. .SH COPYRIGHT -Copyright \(co 2011\-2013 Douglas Gilbert +Copyright \(co 2011\-2014 Douglas Gilbert .br This software is distributed under a FreeBSD license. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/doc/sg_sat_identify.8 b/doc/sg_sat_identify.8 index c74200b..380e237 100644 --- a/doc/sg_sat_identify.8 +++ b/doc/sg_sat_identify.8 @@ -1,4 +1,4 @@ -.TH SG_SAT_IDENTIFY "8" "May 2014" "sg3_utils\-1.38" SG3_UTILS +.TH SG_SAT_IDENTIFY "8" "November 2014" "sg3_utils\-1.40" SG3_UTILS .SH NAME sg_sat_identify \- send ATA IDENTIFY DEVICE command via SCSI to ATA Translation (SAT) layer @@ -52,7 +52,8 @@ SCSI standard's preference). When given twice (i.e. '\-HH') the output is in hex, grouped in 16 bit words, the same as the default but without a header. When given thrice (i.e. '\-HHH') the output is in hex, grouped in 16 bit words, in a format that is acceptable for 'hdparm \-\-Istdin' to -process. +process. '\-HHHH' simply outputs hex data bytes, space separated, 16 per +line. .TP \fB\-i\fR, \fB\-\-indent\fR outputs the World Wide Name (WWN) of the device. This should be a NAA\-5 @@ -97,13 +98,8 @@ probably should treat opcode 0xa1 as a BLANK command and send it through to the cd/dvd drive. The ATA PASS\-THROUGH (16) command's opcode (0x85) does not clash with anything so it is a better choice. .PP -In the 2.4 series of Linux kernels the \fIDEVICE\fR must be -a SCSI generic (sg) device. In the 2.6 series block devices (e.g. disks -and ATAPI DVDs) can also be specified. For example "sg_inq /dev/sda" -will work in the 2.6 series kernels. From lk 2.6.6 other SCSI "char" -device names may be used as well (e.g. "/dev/st0m"). Prior to lk 2.6.29 -USB mass storage limited sense data to 18 bytes which made the -\fB\-\-ck_cond\fR option yield strange (truncated) results. +Prior to Linux kernel 2.6.29 USB mass storage limited sense data to 18 bytes +which made the \fB\-\-ck_cond\fR option yield strange (truncated) results. .SH EXIT STATUS The exit status of sg_sat_identify is 0 when it is successful. Otherwise see the sg3_utils(8) man page. @@ -112,7 +108,7 @@ Written by Douglas Gilbert .SH "REPORTING BUGS" Report bugs to <dgilbert at interlog dot com>. .SH COPYRIGHT -Copyright \(co 2006\-2013 Douglas Gilbert +Copyright \(co 2006\-2014 Douglas Gilbert .br This software is distributed under a FreeBSD license. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/doc/sg_sat_phy_event.8 b/doc/sg_sat_phy_event.8 index e120dd6..415f781 100644 --- a/doc/sg_sat_phy_event.8 +++ b/doc/sg_sat_phy_event.8 @@ -1,4 +1,4 @@ -.TH SG_SAT_PHY_EVENT "8" "June 2013" "sg3_utils\-1.37" SG3_UTILS +.TH SG_SAT_PHY_EVENT "8" "November 2014" "sg3_utils\-1.40" SG3_UTILS .SH NAME sg_sat_phy_event \- use ATA READ LOG EXT via a SAT pass\-through to fetch SATA phy event counters @@ -100,10 +100,10 @@ Written by Douglas Gilbert .SH "REPORTING BUGS" Report bugs to <dgilbert at interlog dot com>. .SH COPYRIGHT -Copyright \(co 2006\-2013 Douglas Gilbert +Copyright \(co 2006\-2014 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_sat_identify(sg3_utils), smp_rep_phy_err_log(smp_utils), -.B sdparm(sdparm), hdparm(hdparm) +.B sg_sat_identify,sg_sat_read_gplog(sg3_utils), +.B smp_rep_phy_err_log(smp_utils), sdparm(sdparm), hdparm(hdparm) diff --git a/doc/sg_sat_read_gplog.8 b/doc/sg_sat_read_gplog.8 new file mode 100644 index 0000000..c1ee1b8 --- /dev/null +++ b/doc/sg_sat_read_gplog.8 @@ -0,0 +1,114 @@ +.TH SG_SAT_READ_GPLOG "8" "November 2014" "sg3_utils\-1.40" SG3_UTILS +.SH NAME +sg_sat_read_gplog \- use ATA READ LOG EXT command via a SCSI to ATA +Translation (SAT) layer +.SH SYNOPSIS +.B sg_sat_read_gplog +[\fI\-\-ck_cond\fR] [\fI\-\-count=CO\fR] [\fI\-\-dma\fR] [\fI\-\-help\fR] +[\fI\-\-hex\fR] [\fI\-\-len=\fR{16|12}] [\fI\-\-log=\fRLA] +[\fI\-\-page=\fRPN] [\fI\-\-readonly\fR] [\fI\-\-verbose\fR] +[\fI\-\-version\fR] \fIDEVICE\fR +.SH DESCRIPTION +.\" Add any additional description here +.PP +This utility sends an ATA READ LOG EXT or an ATA READ LOG DMA EXT command to +the \fIDEVICE\fR. This command is used to read the general purpose log +of (S)ATA disks (not ATAPI devices such as DVD driver). Rather than send the +READ LOG (DMA) EXT command directly to the device it is sent via a SCSI +transport which is assumed to contain a SCSI to ATA Translation (SAT) +Layer (SATL). The SATL may be in an operating system driver, in host bus +adapter (HBA) firmware or in some external enclosure. +.PP +This utility does not currently attempt to decode the response from the +ATA disk, rather it outputs the response in ASCII hexadecimal grouped in +16 bit words. Following ATA conventions those words are decoded little +endian (note that SCSI commands use a big endian representation). In the +future this utility may attempt to decode some log pages, perhaps using +the \fI\-\-decode\fR option. +.PP +The SAT\-2 standard (SAT ANSI INCITS 465-2010, prior draft: sat2r09.pdf at +www.t10.org) defines two SCSI "ATA PASS\-THROUGH" commands: one using a 16 +byte "cdb" and the other with a 12 byte cdb. This utility defaults to using +the 16 byte cdb variant. +.SH OPTIONS +Arguments to long options are mandatory for short options as well. +.TP +\fB\-C\fR, \fB\-\-ck_cond\fR +sets the CK_COND bit in the ATA PASS\-THROUGH SCSI cdb. The +default setting is clear (i.e. 0). When set the SATL should yield a +sense buffer containing a ATA Result descriptor irrespective of whether +the ATA command succeeded or failed. When clear the SATL should only yield +a sense buffer containing a ATA Result descriptor if the ATA command failed. +.TP +\fB\-c\fR, \fB\-\-count\fR=\fICO\fR +the number \fICO\fR is placed in the "count" field in the ATA READ +LOG EXT command. This specified the number of 512-byte blocks of +data to be read from the specified log. +.TP +\fB\-d\fR, \fB\-\-dma\fR +use the ATA READ LOG DMA EXT command instead of ATA READ LOG EXT command. +Some devices require this to return valid log data. +.TP +\fB\-h\fR, \fB\-\-help\fR +outputs the usage message summarizing command line options then exits. +Ignores \fIDEVICE\fR if given. +.TP +\fB\-H\fR, \fB\-\-hex\fR +when given once, the response is output in ASCII hexadecimal bytes. When +given twice, then the response is grouped into 16 bit words using ATA +conventions (i.e. little endian); this is the default output (i.e. when +this option is not given). When given thrice (i.e. '\-HHH') the output +is in hex, grouped in 16 bit words (without a leading offset and trailing +ASCII on each line), in a format that is acceptable for 'hdparm \-\-Istdin' +to process. +.TP +\fB\-L\fR, \fB\-\-log\fR=\fILA\fR +the number \fILA\fR is known as the "log address" in the ATA standards and +is placed in bits 7:0 of the "lba" field of the ATA READ LOG (DMA) EXT +command. This specifies the log to be returned (See ATA-ACS for a detailed +list of available log addresses). The default value placed in the "lba +field is 0, returning the directory of available logs. The maximum value +allowed for \fILOG\fR is 0xff. +.TP +\fB\-p\fR, \fB\-\-page\fR=\fIPN\fR +the number \fIPN\fR is the page number (within the log address) and is +placed in bits 32:16 of the "lba" field of the ATA READ LOG (DMA) EXT +command. The default value placed in the "lba" field is 0. The maximum value +allowed for \fILOG\fR is 0xffff. +.TP +\fB\-l\fR, \fB\-\-len\fR={16|12} +this is the length of the SCSI cdb used for the ATA PASS\-THROUGH commands. +The argument can either be 16 or 12. The default is 16. Some SCSI +transports cannot convey SCSI commands longer than 12 bytes. +.TP +\fB\-r\fR, \fB\-\-readonly\fR +causes the \fIDEVICE\fR to be opened with the read\-only flag (O_RDONLY in +Unix). The default action is to open \fIDEVICE\fR with the read\-write +flag (O_RDWR in Unix). In some cases sending power management commands to +ATA disks are defeated by OS actions on the close() if the \fIDEVICE\fR was +opened with the read\-write flag (e.g. the OS might think it needs to +flush something to disk). +.TP +\fB\-v\fR, \fB\-\-verbose\fR +increases the level or verbosity. +.TP +\fB\-V\fR, \fB\-\-version\fR +print out version string +.SH NOTES +Prior to Linux kernel 2.6.29 USB mass storage limited sense data to 18 bytes +which made the \fB\-\-ck_cond\fR option yield strange (truncated) results. +.SH EXIT STATUS +The exit status of sg_sat_read_gplog is 0 when it is successful. Otherwise +see the sg3_utils(8) man page. +.SH AUTHOR +Written by Hannes Reinecke and Douglas Gilbert +.SH "REPORTING BUGS" +Report bugs to <dgilbert at interlog dot com>. +.SH COPYRIGHT +Copyright \(co 2014 Hannes Reinecke, SUSE Linux GmbH +.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_sat_identify(sg3_utils), sg_inq(sg3_utils), sdparm(sdparm), +.B hdparm(hdparm) diff --git a/doc/sg_sat_set_features.8 b/doc/sg_sat_set_features.8 index 02079fc..d732c3d 100644 --- a/doc/sg_sat_set_features.8 +++ b/doc/sg_sat_set_features.8 @@ -1,12 +1,13 @@ -.TH SG_SAT_SET_FEATURES "8" "April 2014" "sg3_utils\-1.39" SG3_UTILS +.TH SG_SAT_SET_FEATURES "8" "November 2014" "sg3_utils\-1.40" SG3_UTILS .SH NAME sg_sat_set_features \- use ATA SET FEATURES command via a SCSI to ATA Translation (SAT) layer .SH SYNOPSIS .B sg_sat_set_features -[\fI\-\-count=CO\fR] [\fI\-\-ck_cond\fR] [\fI\-\-feature=FEA\fR] -[\fI\-\-help\fR] [\fI\-\-lba=LBA\fR] [\fI\-\-len=\fR{16|12}] -[\fI\-\-readonly\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR +[\fI\-\-count=CO\fR] [\fI\-\-ck_cond\fR] [\fI--extended\fR] +[\fI\-\-feature=FEA\fR] [\fI\-\-help\fR] [\fI\-\-lba=LBA\fR] +[\fI\-\-len=\fR{16|12}] [\fI\-\-readonly\fR] [\fI\-\-verbose\fR] +[\fI\-\-version\fR] \fIDEVICE\fR .SH DESCRIPTION .\" Add any additional description here .PP @@ -44,6 +45,11 @@ sense buffer containing a ATA Result descriptor irrespective of whether the ATA command succeeded or failed. When clear the SATL should only yield a sense buffer containing a ATA Result descriptor if the ATA command failed. .TP +\fB\-e\fR, \fB\-\-extended\fR +allow for extended LBA numbers (i.e. larger than 32 bits). +This value is enabled automatically for large LBA numbers, but can be +enabled explicitly even for low LBA numbers with this option. +.TP \fB\-f\fR, \fB\-\-feature\fR=\fIFEA\fR the value \fIFEA\fR is placed in the "feature" field in the ATA SET FEATURES command. The term "subcommand" is sometimes used for this diff --git a/doc/sg_scan.8.win32 b/doc/sg_scan.8.win32 index d68e85a..b87888a 100644 --- a/doc/sg_scan.8.win32 +++ b/doc/sg_scan.8.win32 @@ -1,4 +1,4 @@ -.TH SG_SCAN "8" "December 2012" "sg3_utils\-1.35" SG3_UTILS +.TH SG_SCAN "8" "August 2014" "sg3_utils\-1.40" SG3_UTILS .SH NAME sg_scan \- scan storage devices and map to volume names .SH SYNOPSIS @@ -14,6 +14,11 @@ many, one or no corresponding volumes. There is one line output per device with identification strings to the right. Its purpose is to list the storage device names that can be used by other utilities in this package. .PP +In later versions of Windows this utility may need to be "run as +Administrator" for disks and other devices to be seen. If not those devices +will simply not appear as calls to query them fail with access permission +problems. +.PP There is an optional SCSI adapter scan which may find additional storage devices other than the ones listed above. An example is a SCSI Enclosure Services (SES) device typically found in disk arrays. diff --git a/doc/sg_senddiag.8 b/doc/sg_senddiag.8 index 90e8aaa..76de1dc 100644 --- a/doc/sg_senddiag.8 +++ b/doc/sg_senddiag.8 @@ -1,12 +1,13 @@ -.TH SG_SENDDIAG "8" "December 2011" "sg3_utils\-1.33" SG3_UTILS +.TH SG_SENDDIAG "8" "September 2014" "sg3_utils\-1.40" SG3_UTILS .SH NAME sg_senddiag \- performs a SCSI SEND DIAGNOSTIC command .SH SYNOPSIS .B sg_senddiag [\fI\-\-doff\fR] [\fI\-\-extdur\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR] -[\fI\-\-list\fR] [\fI\-\-pf\fR] [\fI\-\-raw=H,H...\fR] [\fI\-\-raw=\-\fR] -[\fI\-\-selftest=ST\fR] [\fI\-\-test\fR] [\fI\-\-uoff\fR] -[\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR +[\fI\-\-list\fR] [\fI\-\-maxlen=LEN\fR] [\fI\-\-page=PG\fR] [\fI\-\-pf\fR] +[\fI\-\-raw=H,H...\fR] [\fI\-\-raw=\-\fR] [\fI\-\-selftest=ST\fR] +[\fI\-\-test\fR] [\fI\-\-uoff\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] +\fIDEVICE\fR .PP .B sg_senddiag [\fI\-doff\fR] [\fI\-e\fR] [\fI\-h\fR] [\fI\-H\fR] [\fI\-l\fR] [\fI\-pf\fR] @@ -57,9 +58,24 @@ DIAGNOSTIC RESULTS command. When used in the absence of a \fI\-\-list\fR argument then a list of diagnostic page names and their numbers, known by this utility, are listed. .TP +\fB\-m\fR, \fB\-\-maxlen\fR=\fILEN\fR +where \fILEN\fR is the value placed in the parameter list length field of a +SEND DIAGNOSTIC command or in the allocation length field of a RECEIVE +DIAGNOSTIC RESULTS command. This only occurs when the other options imply +there will be data sent or received by the command. The default value +is 4096 bytes. \fILEN\fR cannot exceed 65535 or 0xffff in hexadecimal. +.TP \fB\-O\fR, \fB\-\-old\fR switch to older style options. .TP +\fB\-P\fR, \fB\-\-page\fR=\fIPG\fR +where \fIPG\fR is the RECEIVE DIAGNOSTIC RESULTS command page code field. +If this option is given the PCV bit in that command is set. When this option +is given then no SEND DIAGNOSTIC command is sent (unlike \fI\-\-list\fR). +If \fIPG\fR is 0 then the response is decoded as if it is the SPC Supported +Diagnostic pages diagnostic page. Other \fIPG\fR values (i.e. 1 to 255) +have their responses output in hex. +.TP \fB\-p\fR, \fB\-\-pf\fR set Page Format (PF) bit. By default it is clear (i.e. 0) unless the list \fI\-\-list\fR option is given in which case the Page Format @@ -251,7 +267,7 @@ Written by Douglas Gilbert .SH "REPORTING BUGS" Report bugs to <dgilbert at interlog dot com>. .SH COPYRIGHT -Copyright \(co 2003\-2011 Douglas Gilbert +Copyright \(co 2003\-2014 Douglas Gilbert .br This software is distributed under the GPL version 2. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/doc/sg_ses.8 b/doc/sg_ses.8 index 2d8df64..9d969bb 100644 --- a/doc/sg_ses.8 +++ b/doc/sg_ses.8 @@ -1,4 +1,4 @@ -.TH SG_SES "8" "May 2014" "sg3_utils\-1.39" SG3_UTILS +.TH SG_SES "8" "October 2014" "sg3_utils\-1.40" SG3_UTILS .SH NAME sg_ses \- access a SCSI Enclosure Services (SES) device .SH SYNOPSIS @@ -8,10 +8,11 @@ sg_ses \- access a SCSI Enclosure Services (SES) device [\fI\-\-dev\-slot\-num=SN\fR] [\fI\-\-eiioe=A_F\fR] [\fI\-\-enumerate\fR] [\fI\-\-filter\fR] [\fI\-\-get=STR\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-index=IIA\fR | \fI\-\-index=TIA,II\fR] [\fI\-\-inner\-hex\fR] -[\fI\-\-join\fR] [\fI\-\-list\fR] [\fI\-\-nickname=SEN\fR] -[\fI\-\-nickid=SEID\fR] [\fI\-\-page=PG\fR] [\fI\-\-raw\fR] -[\fI\-\-readonly\fR] [\fI\-\-sas\-addr=SA\fR] [\fI\-\-set=STR\fR] -[\fI\-\-status\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR +[\fI\-\-join\fR] [\fI\-\-list\fR] [\fI\-\-mask\fR] [\fI\-\-maxlen=LEN\fR] +[\fI\-\-nickname=SEN\fR] [\fI\-\-nickid=SEID\fR] [\fI\-\-page=PG\fR] +[\fI\-\-raw\fR] [\fI\-\-readonly\fR] [\fI\-\-sas\-addr=SA\fR] +[\fI\-\-set=STR\fR] [\fI\-\-status\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] +[\fI\-\-warn\fR] \fIDEVICE\fR .SH DESCRIPTION .\" Add any additional description here .PP @@ -27,7 +28,7 @@ If no options are given (i.e. only the \fIDEVICE\fR argument is given) then the names of all diagnostic pages supported are listed. Most, but not necessarily all, of the named diagnostic pages are defined in the SES standards and drafts. The most recent reference for this utility is the -draft SCSI Enclosure Services 3 document T10/2149\-D Revision 6 at +draft SCSI Enclosure Services 3 document T10/2149\-D Revision 7 at http://www.t10.org . Existing standards for SES and SES\-2 are ANSI INCITS 305\-1998 and ANSI INCITS 448\-2008 respectively. .PP @@ -36,7 +37,8 @@ to flash on a disk carrier in an array) is typically done using a read\-modify\-write cycle. See the section on CHANGING STATE below. .PP There is a web page discussing this utility at -http://sg.danny.cz/sg/sg_ses.html . +http://sg.danny.cz/sg/sg_ses.html . Support for downloading microcode to +a SES device has been placed in a separate utility called sg_ses_microcode. .PP In the following sections "page" refers to a diagnostic page, either fetched with a SCSI RECEIVE DIAGNOSTIC RESULTS command or sent to the @@ -87,7 +89,7 @@ detected. .TP \fB\-d\fR, \fB\-\-data\fR=@\fIFN\fR reads one or more data strings from the file called \fIFN\fR, limit 2048 -bytes. Othewise this option is the same as the previous item that reads +bytes. Otherwise this option is the same as the previous item that reads from stdin. .TP \fB\-D\fR, \fB\-\-descriptor\fR=\fIDN\fR @@ -96,15 +98,15 @@ Descriptor page. This is a medium level indexing alternative to the low level \fI\-\-index=\fR options. If the descriptor name contains a space then \fIDN\fR needs to be surrounded by quotes (single or double) or the space escaped (e.g. preceded by a backslash). See the DESCRIPTOR NAME, DEVICE SLOT -NAME AND SAS ADDRESS section below. +NUMBER AND SAS ADDRESS section below. .TP -\fB\-x\fR, \fB\-\-dev\-slot\-num\fR=\fISN\fR +\fB\-x\fR, \fB\-\-dev\-slot\-num\fR=\fISN\fR, \fB\-\-dsn\fR=\fISN\fR where \fISN\fR is a device slot number found in the Additional Element Status page. Only entries for FCP and SAS devices (with EIP=1) have device slot -numbers. \fISN\fR must be a number in the range 0 to 255 (inclusive). This is -a medium level indexing alternative to the low level \fI\-\-index=\fR -options. See the DESCRIPTOR NAME, DEVICE SLOT NAME AND SAS ADDRESS section -below. +numbers. \fISN\fR must be a number in the range 0 to 255 (inclusive). 255 is +used to indicate there is no corresponding device slot. This is a medium level +indexing alternative to the low level \fI\-\-index=\fR options. See the +DESCRIPTOR NAME, DEVICE SLOT NUMBER AND SAS ADDRESS section below. .TP \fB\-E\fR, \fB\-\-eiioe\fR=\fIA_F\fR \fIA_F\fR is either the string 'auto' or 'force'. There was some fuzziness @@ -185,14 +187,32 @@ group elements from the Element Descriptor, Enclosure Status and Additional Element Status pages. If this option is given twice then elements from the Threshold In page are also grouped. The order is dictated by the Configuration page. All elements are output unless one of the indexing options is given, -in which case only the matching element and its associateds are output. The -\fI\-\-filter\fR option can be added to reduce the amount of output generated -by this option. See the INDEXES and DESCRIPTOR NAME, DEVICE SLOT NAME AND SAS -ADDRESS sections below. +in which case only the matching element and its associated fields are output. +The \fI\-\-filter\fR option can be added to reduce the amount of output +generated by this option. See the INDEXES and DESCRIPTOR NAME, DEVICE SLOT +NUMBER AND SAS ADDRESS sections below. .TP \fB\-l\fR, \fB\-\-list\fR This option is equivalent to \fI\-\-enumerate\fR. See that option. .TP +\fB\-M\fR, \fB\-\-mask\fR +When modifying elements, the default action is a read (status element), +mask, modify (based on \fI\-\-clear=STR\fR or \fI\-\-set=STR\fR) then write +back as the control element. The mask step is new in sg_ses version 1.98 +and is based on what is allowable (and in the same location) in draft SES\-3 +revision 6. Those masks may evolve, as they have in the past. This option +re\-instates the previous logic which was to ignore the mask step. The +default action (i.e. without this option) is to perform the mask step in +the read\-mask\-modify\-write sequence. +.TP +\fB\-m\fR, \fB\-\-maxlen\fR=\fILEN\fR +\fILEN\fR is placed in the ALLOCATION LENGTH field of the SCSI RECEIVE +DIAGNOSTIC RESULTS commands sent by the utility. It represents the maximum +size of data the SES device can return (in bytes). It cannot exceed 65535 +and defaults to 65532 (bytes). Some systems may not permit such large sizes +hence the need for this option. If \fILEN\fR is set to 0 then the default +size is used. +.TP \fB\-n\fR, \fB\-\-nickname\fR=\fISEN\fR where \fISEN\fR is the new Subenclosure Nickname. Only the first 32 characters (bytes) of \fISEN\fR are used, if more are given they are @@ -237,7 +257,7 @@ manufactured target port identifier is different from a SATA disk's WWN. \fISA\fR is a hex number that is up to 8 digits long. It may have a leading '0x' or '0X' or a trailing 'h' or 'H'. This option is a medium level indexing alternative to the low level \fI\-\-index=\fR options. -See the DESCRIPTOR NAME, DEVICE SLOT NAME AND SAS ADDRESS section below. +See the DESCRIPTOR NAME, DEVICE SLOT NUMBER AND SAS ADDRESS section below. .TP \fB\-S\fR, \fB\-\-set\fR=\fISTR\fR Used to set an element field in the Enclosure Control or Threshold Out page. @@ -255,6 +275,13 @@ 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\-w\fR, \fB\-\-warn\fR +warn about certain irregularities with warnings sent to stderr. The join +is a complex operation that relies on information from several pages to be +synchronized. The quality of SES devices vary and to be fair, the +descriptions from T10 drafts and standards have been tweaked several +times (see the EIIOE bit) in order to clear up confusion. .SH INDEXES An enclosure can have information about its disk and tape drives plus other supporting components like power supplies spread across several pages. @@ -262,7 +289,7 @@ Addressing a specific element (overall or individual) within a page is complicated. This section describes low level indexing (i.e. choosing a single element (or a group of related elements) from a large number of elements). If available, the medium level indexing described in the -following section (DESCRIPTOR NAME, DEVICE SLOT NAME AND SAS ADDRESS) +following section (DESCRIPTOR NAME, DEVICE SLOT NUMBER AND SAS ADDRESS) might be simpler to use. .PP The Configuration page is key to low level indexing: it contains a list @@ -320,7 +347,7 @@ output (e.g. only showing the element associated with the second 12 volt power supply). They may also be used together with with the \fI\-\-clear=STR\fR, \fI\-\-get=STR\fR and \fI\-\-set=STR\fR options which are described in the STR section below. -.SH DESCRIPTOR NAME, DEVICE SLOT NAME AND SAS ADDRESS +.SH DESCRIPTOR NAME, DEVICE SLOT NUMBER AND SAS ADDRESS The three options: \fI\-\-descriptor=DN\fR, \fI\-\-dev\-slot\-num=SN\fR and \fI\-\-sas\-addr=SA\fR allow medium level indexing, as an alternative to the low level \fI\-\-index=\fR options. Only one of the three options @@ -359,11 +386,12 @@ is usually in the range 0 to 3, the <start_bit> must be in the range 0 to number of bits are read in the left to right sense of the element tables shown in the various SES draft documents. For example the 8 bits of byte 2 would be represented as 2:7:8 with the most significant bit being -2:7 and the least sugnificant bit being 2:0 . +2:7 and the least significant bit being 2:0 . .PP The <value> is optional but is ignored if provided to \fI\-\-get=STR\fR. For \fI\-\-set=STR\fR the default <value> is 1 while for \fI\-\-clear=STR\fR -the default value is 0 . +the default value is 0 . <value> is assumed to be decimal, hexadecimal +values can be given in the normal fashion. .PP The supported list of <acronym>s can be viewed by using the \fI\-\-enumerate\fR option twice (or "\-ee"). @@ -541,5 +569,5 @@ Copyright \(co 2004\-2014 Douglas Gilbert 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_inq, sg_safte, sg_senddiag, sg3_utils (in sg3_utils package); +.B sg_inq, sg_safte, sg_senddiag, sg_ses_microcode, sg3_utils (sg3_utils); .B safte\-monitor (Internet) diff --git a/doc/sg_ses_microcode.8 b/doc/sg_ses_microcode.8 new file mode 100644 index 0000000..3e4a9d2 --- /dev/null +++ b/doc/sg_ses_microcode.8 @@ -0,0 +1,246 @@ +.TH SG_SES_MICROCODE "8" "October 2014" "sg3_utils\-1.40" SG3_UTILS +.SH NAME +sg_ses_microcode \- send microcode to a SCSI enclosure +.SH SYNOPSIS +.B sg_ses_microcode +[\fI\-\-bpw=CS\fR] [\fI\-\-help\fR] [\fI\-\-id=ID\fR] [\fI\-\-in=FILE\fR] +[\fI\-\-length=LEN\fR] [\fI\-\-mode=MO\fR] [\fI\-\-non\fR] +[\fI\-\-offset=OFF\fR] [\fI\-\-skip=SKIP\fR] [\fI\-\-subenc=MS\fR] +[\fI\-\-tlength=TLEN\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR +.SH DESCRIPTION +.\" Add any additional description here +.PP +This utility attempts to download microcode to an enclosure (or one of its +sub\-enclosures) associated with the \fIDEVICE\fR. The process for doing +this is defined in the SCSI Enclosure Services (SES) standards and drafts +maintained by the T10 committee. +.PP +The process is to send one or more sequences containing a SCSI SEND +DIAGNOSTIC command followed optionally by a RECEIVE DIAGNOSTIC RESULTS +command. The former sends a Download microcode Control diagnostic +page (dpage) and the latter fetches a Download microcode status dpage which +can be viewed as a report on the former command. +.PP +The default action (i.e. when the \fI\-\-mode=MO\fR option is not given) +is to fetch the Download microcode status dpage and decode it. This does +not require the microcode (firmware) itself so the \fI\-\-in=FILE\fR option +is not required. +.PP +The most recent reference for this utility is the draft SCSI Enclosure +Services 3 (SES\-3) document T10/2149\-D Revision 7 at http://www.t10.org . +Existing standards for SES and SES\-2 are ANSI INCITS 305\-1998 and ANSI +INCITS 448\-2008 respectively. +.PP +Most other support for SES in this package (apart from downloading +microcode) can be found in the sg_ses utility. Another way of downloading +firmware to a SCSI device is with the WRITE BUFFER command defined in +SPC\-4, see the sg_write_buffer utility. +.SH OPTIONS +Arguments to long options are mandatory for short options as well. +.TP +\fB\-b\fR, \fB\-\-bpw\fR=\fICS\fR +where \fICS\fR is the chunk size in bytes and should be a multiple of 4. +This will be the maximum number of bytes sent per SEND DIAGNOSTIC command. +So if \fICS\fR is less than the effective length of the microcode then +multiple SEND DIAGNOSTIC commands are sent, each taking the next chunk +from the read data and increasing the buffer offset field in the Download +microcode control dpage by the appropriate amount. The default is +a chunk size of 0 which is interpreted as a very large number hence only +one SEND DIAGNOSTIC command will be sent. +.br +The number in \fICS\fR can optionally be followed by ",act" or ",activate". +In this case after the microcode has been successfully sent to the +\fIDEVICE\fR, an additional Download microcode control dpage with its mode +set to "Activate deferred microcode" [0xf] is sent. +.TP +\fB\-h\fR, \fB\-\-help\fR +output the usage message then exit. If used multiple times also prints +the mode names and their acronyms. +.TP +\fB\-i\fR, \fB\-\-id\fR=\fIID\fR +this option sets the BUFFER ID field in the Download microcode control +dpage. \fIID\fR is a value between 0 (default) and 255 inclusive. +.TP +\fB\-I\fR, \fB\-\-in\fR=\fIFILE\fR +read data from file \fIFILE\fR that will be sent with the SEND DIAGNOSTIC +command. If \fIFILE\fR is '\-' then stdin is read until an EOF is +detected (this is the same action as \fI\-\-raw\fR). Data is read from +the beginning of \fIFILE\fR except in the case when it is a regular file +and the \fI\-\-skip=SKIP\fR option is given. +.TP +\fB\-l\fR, \fB\-\-length\fR=\fILEN\fR +where \fILEN\fR is the length, in bytes, of data to be written to the device. +If not given (and the length cannot be deduced from \fI\-\-in=FILE\fR or +\fI\-\-raw\fR) then defaults to zero. If the option is given and the length +deduced from \fI\-\-in=FILE\fR or \fI\-\-raw\fR is less (or no data is +provided), then bytes of 0xff are used as fill bytes. +.TP +\fB\-m\fR, \fB\-\-mode\fR=\fIMO\fR +this option sets the MODE. \fIMO\fR is a value between +0 (which is dmc_status and the default) and 255 inclusive. Alternatively +an abbreviation can be given. See the MODES section below. To list the +available mode abbreviations at run time give an invalid +one (e.g. '\-\-mode=xxx') or use the '\-h' option. +.TP +\fB\-N\fR, \fB\-\-non\fR +allow for non\-standard implementations that reset their Download microcode +engine after a RECEIVE DIAGNOSTIC RESULTS command with the Download microcode +status dpage is sent. When this option is given sending that command and +dpage combination is avoided unless an error has already occurred. +.TP +\fB\-o\fR, \fB\-\-offset\fR=\fIOFF\fR +this option sets the BUFFER OFFSET field in the Download microcode control +dpage. \fIOFF\fR is a value between 0 (default) and 2**32\-1 . It is a +byte offset. +.TP +\fB\-s\fR, \fB\-\-skip\fR=\fISKIP\fR +this option is only active when \fI\-\-in=FILE\fR is given and \fIFILE\fR is +a regular file, rather than stdin. Data is read starting at byte offset +\fISKIP\fR to the end of file (or the amount given by \fI\-\-length=LEN\fR). +If not given the byte offset defaults to 0 (i.e. the start of the file). +.TP +\fB\-S\fR, \fB\-\-subenc\fR=\fISEID\fR +\fISEID\fR is the subenclosure identify. It defaults to 0 which is the +primary enclosure identifier. +.TP +\fB\-t\fR, \fB\-\-tlength\fR=\fITLEN\fR +\fITLEN\fR is the total length in bytes of the microcode to be (or being) +downloaded. It defaults to 0 which is okay in most cases. This option is +only needed when sections of microcode and being sent in separate invocations +of this utility. +.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. +.SH MODES +Following is a list accepted by the \fIMO\fR argument of this utility. +First shown is an acronym followed in square brackets by the corresponding +decimal and hex values that may also be given for \fIMO\fR. +.TP +dmc_status [0, 0x0] +Use RECEIVE DIAGNOSTIC RESULTS to fetch the Download microcode status dpage +and print it out. +.TP +dmc_offs [6, 0x6] +Download microcode with offsets and activate. +.TP +dmc_offs_save [7, 0x7] +Download microcode with offsets, save, and activate. +.TP +dmc_offs_defer [14, 0xe] +Download microcode with offsets, save, and defer activate. +.TP +activate_mc [15, 0xf] +Activate deferred microcode. There is no follow-up RECEIVE DIAGNOSTIC RESULTS +to fetch the Download microcode status dpage since the \fIDEVICE\fR might be +resetting. +.PP +Apart from dmc_status, these are placed in the Download microcode mode +field in the Download microcode control dpage. In the case of dmc_status +the Download microcode status dpage is fetch with the RECEIVE DIAGNOSTIC +RESULTS command and decoded. +.SH WHEN THE DOWNLOAD FAILS +Firstly, if it succeeds, this utility should stay silent and return. +Typically vendors will change the "revision" string (which is 4 characters +long) whenever they release new firmware. That can be seen in the response +to a SCSI INQUIRY command, for example by using the sg_inq utility. +It is possible that the device needs to be power cycled before the new +microcode becomes active. Also if mode dmc_offs_defer [0xe] is used to +download the microcode, then another invocation with activate_mc may +be needed. +.PP +If something goes wrong, there will typically be messages printed out +by this utility. The first thing to check is the microcode (firmware) +file itself. Is it designed for the device model; has it been corrupted, +and if downgrading (i.e. trying to re-instate older firmware), does +the vendor allow that? +.PP +Getting new firmware on a device is a delicate operation that is not +always well defined by T10's standards and drafts. One might speculate +that they are deliberately vague. In testing this utility one vendor's +interpretation of the standard was somewhat surprising. The \fI\-\-non\fR +option was added to cope with their interpretation. So if the above +suggestions don't help, try adding the \fI\-\-non\fR option. +.SH NOTES +This utility can handle a maximum size of 128 MB of microcode which +should be sufficient for most purposes. In a system that is memory +constrained, such large allocations of memory may fail. +.PP +The user should be aware that most operating systems have limits on the +amount of data that can be sent with one SCSI command. In Linux this +depends on the pass through mechanism used (e.g. block SG_IO or the sg +driver) and various setting in sysfs in the Linux lk 2.6/3 +series (e.g. /sys/block/sda/queue/max_sectors_kb). Devices (i.e. logical +units) also typically have limits on the maximum amount of data they can +handle in one command. These two limitations suggest that modes +containing the word "offset" together with the \fI\-\-bpw=CS\fR option +are required as firmware files get larger and larger. And \fICS\fR +can be quite small, for example 4096 bytes, resulting in many SEND +DIAGNOSTIC commands being sent. +.PP +The exact error from the non\-standard implementation was a sense key of +ILLEGAL REQUEST and an asc/ascq code of 0x26,0x0 which is "Invalid field in +parameter list". If that is seen try again with the \fI\-\-non\fR option. +.PP +Downloading incorrect microcode into a device has the ability to render +that device inoperable. One would hope that the device vendor verifies +the data before activating it. +.PP +A long (operating system) timeout of 7200 seconds is set on each SEND +DIAGNOSTIC command. +.PP +All numbers given with options are assumed to be decimal. +Alternatively numerical values can be given in hexadecimal preceded by +either "0x" or "0X" (or has a trailing "h" or "H"). +.SH EXAMPLES +If no microcode/firmware file is given then this utility fetches and decodes +the Download microcode status dpage which could possibly show another +initiator in the process of updating the microcode. Even if that is +happening, fetching the status page should not cause any problems: +.PP + sg_ses_microcode /dev/sg3 +.br +Download microcode status diagnostic page: +.br + number of secondary subenclosures: 0 +.br + generation code: 0x0 +.br + subenclosure identifier: 0 [primary] +.br + download microcode status: No download microcode operation in progress [0x0] +.br + download microcode additional status: 0x0 +.br + download microcode maximum size: 1048576 bytes +.br + download microcode expected buffer id: 0x0 +.br + download microcode expected buffer id offset: 0 +.PP +The following sends new microcode/firmware to an enclosure. Sending a 1.5 MB +file in one command caused the enclosure to lock up temporarily and did +not update the firmware. Breaking the firmware file into 4 KB chunks (an +educated guess) was more successful: +.PP + sg_ses_microcode \-b 4k \-m dmc_offs_save \-I firmware.bin /dev/sg4 +.PP +The firmware update occurred in the following enclosure power cycle. With +a modern enclosure the Extended Inquiry VPD page gives indications in which +situations a firmware upgrade will take place. +.SH EXIT STATUS +The exit status of sg_ses_microcode 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 2014 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_ses, sg_write_buffer(sg3_utils) diff --git a/doc/sg_vpd.8 b/doc/sg_vpd.8 index 04ae509..089dd31 100644 --- a/doc/sg_vpd.8 +++ b/doc/sg_vpd.8 @@ -1,12 +1,12 @@ -.TH SG_VPD "8" "May 2014" "sg3_utils\-1.39" SG3_UTILS +.TH SG_VPD "8" "August 2014" "sg3_utils\-1.40" SG3_UTILS .SH NAME sg_vpd \- fetch SCSI VPD page and/or decode its response .SH SYNOPSIS .B sg_vpd -[\fI\-\-enumerate\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-ident\fR] -[\fI\-\-inhex=FN\fR] [\fI\-\-long\fR] [\fI\-\-maxlen=LEN\fR] +[\fI\-\-all\fR] [\fI\-\-enumerate\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR] +[\fI\-\-ident\fR] [\fI\-\-inhex=FN\fR] [\fI\-\-long\fR] [\fI\-\-maxlen=LEN\fR] [\fI\-\-page=PG\fR] [\fI\-\-quiet\fR] [\fI\-\-raw\fR] [\fI\-\-vendor=VP\fR] -[\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR +[\fI\-\-verbose\fR] [\fI\-\-version\fR] [\fIDEVICE\fR] .SH DESCRIPTION .\" Add any additional description here .PP @@ -16,7 +16,8 @@ are fetched with a SCSI INQUIRY command. .PP Alternatively the \fI\-\-inhex=FN\fR option can be given. In this case \fIFN\fR is assumed to be a file name ('\-' for stdin) containing ASCII -hexadecimal representing a VPD page response. +hexadecimal representing a VPD page response. If the \fI\-\-raw\fR option +is also given then binary input is assumed (rather than ASCII hexadecimal). .PP Probably the most important page is the Device Identification VPD page (page number: 0x83). Since SPC\-3, support for this page @@ -34,6 +35,19 @@ Arguments to long options are mandatory for short options as well. The options are arranged in alphabetical order based on the long option name. .TP +\fB\-a\fR, \fB\-\-all\fR +decode all VPD pages. When used with \fIDEVICE\fR the pages to be decoded +are found in the "Supported VPD pages" VPD page. +.br +If this option is used with the \fI\-\-inhex=FN\fR option then the file +\fIFN\fR is assumed to contain 1 or more VPD pages (in ASCII hex or binary). +Decoding continues until the file is exhausted (or an error occurs). Sanity +checks are aplied on each VPD page's length and the ascending order of VPD +page numbers (required by SPC\-4) so bad data may be detected. +.br +If the \fI\-\-page=PG\fR option is also given then no VPD page whose page +number is greater than \fIPG\fR (or its numeric equivalent) is decoded. +.TP \fB\-e\fR, \fB\-\-enumerate\fR list the names of the known VPD pages, first the standard pages (i.e. those defined by T10), then the vendor specific pages. Each group is sorted @@ -54,7 +68,9 @@ times, see section on the ATA information vpd page. .br To generate output suitable for placing in a file that can be used by a later invocation with the \fI\-\-inhex=FN\fR option, use the '\-HHHH' -option (e.g. 'sg_vpd \-p di -HHHH /dev/sg3 > dev_id.hex'). +option (e.g. 'sg_vpd \-p di \-HHHH /dev/sg3 > dev_id.hex'). The +reason '\-HHHH' is used is to flag that unadorned hexadecimal (without other +text or address offsets) is sent to stdout. .TP \fB\-i\fR, \fB\-\-ident\fR decode the device identification (0x83) VPD page. When used once this option @@ -242,6 +258,29 @@ Search for the name (and acronym) of all pages that share VPD page number .br sad 0xb0 Sequential access device capabilities (SSC) .PP +Some examples follow using the "\-\-all" option. Send an ASCII hexadecimal +representation of all VPD pages to a file: +.PP + # sg_vpd \-\-all \-HHHH /dev/sg3 > all_vpds.hex +.PP +At some later time that file could be decoded with: +.PP + # sg_vpd \-\-all \-\-inhex=all_vpds.hex +.PP +To do the equivalent as the previous example but use a file containing +binary: +.PP + # sg_vpd \-\-all \-\-raw /dev/sg3 > all_vpds.bin +.br + # sg_vpd \-\-all \-\-raw \-\-inhex=all_vpds.bin +.PP +Notice that "\-\-raw" must be given with the second (\-\-inhex) invocation +to alert the utility that all_vpds.bin contains binary as it assumes ASCII +hexadecimal by default. Next we only decode T10 specified VPD pages +excluding vendor specific VPD pages that start at page number 0xc0: +.PP + # sg_vpd \-\-all \-\-page=0xbf \-\-raw \-\-inhex=all_vpds.bin +.PP Further examples can be found on the http://sg.danny.cz/sg/sg3_utils.html web page. .SH AUTHOR diff --git a/doc/sg_write_buffer.8 b/doc/sg_write_buffer.8 index 66b9e23..fb43c22 100644 --- a/doc/sg_write_buffer.8 +++ b/doc/sg_write_buffer.8 @@ -1,4 +1,4 @@ -.TH SG_WRITE_BUFFER "8" "April 2014" "sg3_utils\-1.39" SG3_UTILS +.TH SG_WRITE_BUFFER "8" "October 2014" "sg3_utils\-1.40" SG3_UTILS .SH NAME sg_write_buffer \- send SCSI WRITE BUFFER commands .SH SYNOPSIS @@ -17,7 +17,8 @@ stdin when either \fI\-\-raw\fR or \fI\-\-in=\-\fR is given. .PP Some WRITE BUFFER command variants do not have associated data to send to the device. For example "activate_mc" activates deferred microcode that was sent -via prior WRITE BUFFER commands. +via prior WRITE BUFFER commands. There is a different method used to download +microcode to SES devices, see the sg_ses_microcode utility. .SH OPTIONS Arguments to long options are mandatory for short options as well. .TP @@ -25,7 +26,7 @@ Arguments to long options are mandatory for short options as well. where \fICS\fR is the chunk size in bytes. This will be the maximum number of bytes sent per WRITE BUFFER command. So if \fICS\fR is less than the effective length then multiple WRITE BUFFER commands are sent, each taking -the next chunk from the read data and inceasing the buffer offset field +the next chunk from the read data and increasing the buffer offset field in the WRITE BUFFER command by the appropriate amount. The default is a chunk size of 0 which is interpreted as a very large number hence only one WRITE BUFFER command will be sent. This option should only be used with @@ -157,11 +158,18 @@ driver) and various setting in sysfs in the Linux lk 2.6/3 series (e.g. /sys/block/sda/queue/max_sectors_kb). Devices (i.e. logical units) also typically have limits on the maximum amount of data they can handle in one command. These two limitations suggest that modes -containing the word "offset" togther with the \fI\-\-bpw=CS\fR option +containing the word "offset" together with the \fI\-\-bpw=CS\fR option are required as firmware files get larger and larger. And \fICS\fR can be quite small, for example 4096 bytes, resulting in many WRITE BUFFER commands being sent. .PP +Attempting to download a microcode/firmware file that is too large may +cause an error to occur in the pass-through layer (i.e. before the +SCSI command is issued). In Linux such error reports can be obscure as +in "pass through os error invalid argument". FreeBSD reports such +errors well to the machine's console but returns a cryptic error message +to this utility. +.PP Downloading incorrect microcode into a device has the ability to render that device inoperable. One would hope that the device vendor verifies the data before activating it. If the SCSI WRITE BUFFER command is given @@ -201,4 +209,4 @@ Copyright \(co 2006\-2014 Luben Tuikov and Douglas Gilbert 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_read_buffer(sg3_utils) +.B sg_read_buffer, sg_ses_microcode(sg3_utils) diff --git a/doc/sg_write_same.8 b/doc/sg_write_same.8 index 2663bda..e0e2dca 100644 --- a/doc/sg_write_same.8 +++ b/doc/sg_write_same.8 @@ -1,4 +1,4 @@ -.TH SG_WRITE_SAME "8" "April 2014" "sg3_utils\-1.39" SG3_UTILS +.TH SG_WRITE_SAME "8" "August 2014" "sg3_utils\-1.40" SG3_UTILS .SH NAME sg_write_same \- send SCSI WRITE SAME command .SH SYNOPSIS @@ -211,10 +211,11 @@ suffixes or be given in hexadecimal. See the "NUMERIC ARGUMENTS" section in the sg3_utils(8) man page. .PP In Linux at this time the sg driver does not support cdb sizes greater than -16 bytes. Hence a device node like /dev/sg1 which is associated with the -sg driver will fail with this utility if the \fI\-\-32\fR option is -given (or implied by other options). The bsg driver with device nodes like -/dev/bsg/6:0:0:1 does support cdb sizes greater than 16 bytes. +16 bytes. [This restriction will be removed in lk 3.17 .] Hence a device +node like /dev/sg1 which is associated with the sg driver will fail with +this utility if the \fI\-\-32\fR option is given (or implied by other +options). The bsg driver with device nodes like /dev/bsg/6:0:0:1 does +support cdb sizes greater than 16 bytes. .SH EXIT STATUS The exit status of sg_write_same is 0 when it is successful. Otherwise see the sg3_utils(8) man page. diff --git a/doc/sg_write_verify.8 b/doc/sg_write_verify.8 new file mode 100644 index 0000000..195fa1a --- /dev/null +++ b/doc/sg_write_verify.8 @@ -0,0 +1,191 @@ +.TH "WRITE AND VERIFY" "8" "July 2014" "sg3_utils\-1.40" SG3_UTILS +.SH NAME +sg_write_and_verify \- send the SCSI WRITE AND VERIFY command +.SH SYNOPSIS +.B sg_write_verify +[\fI\-\-16\fR] [\fI\-\-bytchk=BC\fR] [\fI\-\-dpo\fR] [\fI\-\-group=GN\fR] +[\fI\-\-help\fR] [\fI\-\-ilen=ILEN\fR] [\fI\-\-in=IF\fR] \fI\-\-lba=LBA\fR +[\fI\-\-num=NUM\fR] [\fI\-\-repeat\fR] [\fI\-\-timeout=TO\fR] +[\fI\-\-verbose\fR] [\fI\-\-version\fR] [\fI\-\-wrprotect=WP\fR] \fIDEVICE\fR +.SH DESCRIPTION +.\" Add any additional description here +Send a SCSI WRITE AND VERIFY (10) or (16) command to \fIDEVICE\fR. The +data to be written is read from the \fIIF\fR file or, in its absence, a +buffer full of 0xff bytes is used. The length of the data\-out buffer sent +with the command is \fIILEN\fR bytes or, if that is not given, then it is +the length of the \fIIF\fR file. +.PP +The write operation is to the \fIDEVICE\fR's medium (optionally to its cache) +starting at logical block address \fILBA\fR for \fINUM\fR logical blocks. +After the write to medium is performed a verify operation takes place which +may viewed as a medium read (with appropriate checks) but without the data +being returned. Additionally, if \fIBS\fR is set to one, the data read back +from the medium in the verify operation is compared to the original data\-out +buffer. +.PP +The relationship between the number of logical blocks to be written (i.e. +\fINUM\fR) and the length (in bytes) of the data\-out buffer (i.e. +\fIILEN\fR) may be simply found by multiplying the former by the logical +block size. However if the \fIDEVICE\fR has protection information (PI) +then it becomes a bit more complicated. Hence the calculation is left to +the user with the default \fIILEN\fR, in the absence of the \fIIF\fR file, +being set to \fINUM\fR * 512. +.PP +For sending large amounts of data to contiguous logical blocks, a single +WRITE AND VERIFY command may not be appropriate (e.g. due to operating +system limitations). In such cases see the REPEAT section below. +.SH OPTIONS +Arguments to long options are mandatory for short options as well. +The options are arranged in alphabetical order based on the long option name. +.TP +\fB\-S\fR, \fB\-\-16\fR +Send a WRITE AND VERIFY(16) command. The default is to send a WRITE AND +VERIFY(10) command unless \fILBA\fR or \fINUM\fR are too large for the +10 byte variant. +.TP +\fB\-b\fR, \fB\-\-bytchk\fR=\fIBC\fR +where \fIBC\fR is the value to place in the command's BYTCHK field. Values +between 0 and 3 (inclusive) are accepted. The default is value is 0 which +implies only a write to the medium then a verify operation are performed. The +only other value T10 defines currently is 1 which does performs an additional +comparison between the data\-out buffer that was used by the write operation +and the contents of the logical blocks read back from the medium. +.TP +\fB\-d\fR, \fB\-\-dpo\fR +Set the DPO (disable page out) bit in the command. The default is to leave +it clear. +.TP +\fB\-g\fR, \fB\-\-group\fR=\fIGN\fR +where \fIGN\fR is the value to place in the command's GROUP NUMBER field. +Values between 0 and 31 (inclusive) are accepted. The default is value is 0. +.TP +\fB\-h\fR, \fB\-\-help\fR +output the usage message then exit. +.TP +\fB\-I\fR, \fB\-\-ilen\fR=\fIILEN\fR +where \fIILEN\fR is the number of bytes that will be placed in the data\-out +buffer. If the \fIIF\fR file is given then no more than \fIILEN\fR bytes +are read from that file. If the \fIIF\fR file does not contain \fIILEN\fR +bytes then an error is reported. If the \fIIF\fR file is not given then +a data\-out buffer with \fIILEN\fR bytes of 0xff is sent. +.TP +\fB\-i\fR, \fB\-\-in\fR=\fIIF\fR +read data (binary) from file named \fIIF\fR. If \fIIF\fR is "\-" then +stdin is used. This data will become the data\-out buffer and will be written +to the \fIDEVICE\fR's medium. If \fIBC\fR is 1 then that data\-out buffer +will be held until after the verify operation and compared to the data read +back from the medium. +.TP +\fB\-l\fR, \fB\-\-lba\fR=\fILBA\fR +where \fILBA\fR is the logical block address to start the write to medium. +Assumed to be in decimal unless prefixed with '0x' or has a trailing 'h'. +Must be provided. +.TP +\fB\-n\fR, \fB\-\-num\fR=\fINUM\fR +where \fINUM\fR is the number of blocks, starting at \fILBA\fR, to write +to the medium. The default value for \fINUM\fR is 1. +.TP +\fB\-R\fR, \fB\-\-repeat\fR +this option will continue to do WRITE AND VERIFY commands until the \fIIF\fR +file is exhausted. This option requires both the \fI\-\-ilen=ILEN\fR and +\fI\-\-in=IF\fR options to be given. Each command starts at the next logical +block address and is for no more than \fINUM\fR blocks. The last command may +be shorter with the number of blocks scaled as required. If there are +residue bytes a warning is sent to stderr. See the REPEAT section. +.TP +\fB\-t\fR, \fB\-\-timeout\fR=\fITO\fR +where \fITO\fR is the command timeout value in seconds. The default value is +60 seconds. If \fINUM\fR is large then command may require considerably more +time than 60 seconds to complete. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +increase the degree of verbosity (debug messages). +.TP +\fB\-V\fR, \fB\-\-version\fR +output version string then exit. +.TP +\fB\-w\fR, \fB\-\-wrprotect\fR=\fIWP\fR +set the WRPROTECT field in the cdb to \fIWP\fR. The default value is 0 which +implies no protection information is sent (along with the user data) in the +data\-out buffer. +.SH REPEAT +For data sizes around a megabyte and larger, it may be appropriate to send +multiple SCSI WRITE AND VERIFY commands due to operating system +limitations (e.g. pass\-through SCSI interfaces often limit the amount +of data that can be passed with a SCSI command). With this utility the +mechanism for doing that is the \fI\-\-repeat\fR option. +.PP +In this mode the \fI\-\-ilen=ILEN\fR and \fI\-\-in=IF\fR options must be +given. The \fIILEN\fR and \fINUM\fR values are treated as a per SCSI command +parameters. Up to \fIILEN\fR bytes will be read from the \fIIF\fR file +continually until it is exhausted. If the \fIIF\fR file is stdin, reading +continues until an EOF is detected. The data read from each iteration becomes +the data\-out buffer for a new WRITE AND VERIFY command. +.PP +The last read from the file (or stdin) may read less than \fIILEN\fR bytes +in which case the number of logical blocks sent to the last WRITE AND VERIFY +is scaled back accordingly. If there is a residual number of bytes left +after that scaling then that is reported to stderr. +.PP +If an error occurs then that is reported to stderr and via the exit status +and the utility stops at that point. +.SH NOTES +Other SCSI WRITE commands have a Force Unit Access (FUA) bit but that is +set (implicitly) by WRITE AND VERIFY commands hence there is no option to set +it. The data\-out buffer may still additionally be placed in the +\fIDEVICE\fR's cache and setting the DPO bit is a hint not to do that. +.PP +Normal SCSI WRITEs can be done with the ddpt and the sg_dd utilities. The +SCSI WRITE SAME command can be done with the sg_write_same utility while +the SCSI COMPARE AND WRITE command (sg_compare_and_write utility) offers +a "test and set" facility. +.PP +Various numeric arguments (e.g. \fILBA\fR) may include multiplicative +suffixes or be given in hexadecimal. See the "NUMERIC ARGUMENTS" section +in the sg3_utils(8) man page. +.SH EXIT STATUS +The exit status of sg_write_verify is 0 when it is successful. If the verify +operation fails that is typically indicated with a medium error which leads +to an exit status of 3. +.PP +If \fIBC\fR is set to 1 and the comparison it causes fails this utility will +indicate the miscompare with an exit status of 14. For other exit status +values see the EXIT STATUS section in the sg3_utils(8) man page. +.SH EXAMPLES +To start with, a simple example: write 1 block of data held in file t.bin +that is 512 bytes long then write that block to LBA 0x1234 on /dev/sg4 . +.PP + # sg_write_verify \-\-lba=0x1234 \-\-in=t.bin /dev/sg4 +.PP +Since '\-\-num=' is not given then it defaults to 1. Further the \fIILEN\fR +value is obtained from the file size of t.bin . To additionally do a +data\-out comparison to the read back data: +.PP + # sg_write_verify -l 0x1234 -i t.bin --bytchk=1 /dev/sg4 +.PP +The ddpt command can do copies between SCSI devices using READ and WRITE +commands. However, currently it has no facility to promote those WRITES +to WRITE AND VERIFY commands. Using a pipe, that could be done like this: +.PP + # ddpt if=/dev/sg2 bs=512 bpt=8 count=11 of=- | +.br +sg_write_verify \-\-in=\- \-l 0x567 \-n 8 \-\-ilen=4096 \-\-repeat /dev/sg4 +.PP +Both ddpt and sg_write_verify are configured for segments of 8 512 byte +logical blocks. Since 11 logical blocks are read then first 8 logical blocks +are copied followed by a copy of the remaining 3 blocks. Since it is assumed +that there is no protection information then the data\-in and data\-out +buffers will be 4096 bytes each. For sg_write_verify this needs to be stated +explicitly with the \-\-ilen=4096 option. +.SH AUTHORS +Bruno Goncalves and Douglas Gilbert. +.SH "REPORTING BUGS" +Report bugs to <dgilbert at interlog dot com>. +.SH COPYRIGHT +Copyright \(co 2014 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 ddpt(in a package of that name), sg_compare_and_write(8), sg_dd(8), +.B sg_write_same(8) diff --git a/doc/sg_xcopy.8 b/doc/sg_xcopy.8 index 1dfdbae..1f8935c 100644 --- a/doc/sg_xcopy.8 +++ b/doc/sg_xcopy.8 @@ -1,4 +1,4 @@ -.TH SG_XCOPY "8" "March 2014" "sg3_utils\-1.38" SG3_UTILS +.TH SG_XCOPY "8" "September 2014" "sg3_utils\-1.40" SG3_UTILS .SH NAME sg_xcopy \- copy data to and from files and devices using SCSI EXTENDED COPY (XCOPY) @@ -254,6 +254,14 @@ when set, equivalent to 'oflag=append'. When clear the action is to overwrite the existing file (if it exists); this is the default. See the 'append' flag. .SH NOTES +Copying data behind an Operating System's back can cause problems. In the +case of Linux, users should look at this link: +http://linux\-mm.org/Drop_Caches +.br +This command sequence may be useful: +.br + sync; echo 3 > /proc/sys/vm/drop_caches +.PP Various numeric arguments (e.g. \fISKIP\fR) may include multiplicative suffixes or be given in hexadecimal. See the "NUMERIC ARGUMENTS" section in the sg3_utils(8) man page. diff --git a/examples/Makefile b/examples/Makefile index cc9f1eb..7fb4340 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -22,9 +22,9 @@ MAN_PREF = man8 LARGE_FILE_FLAGS = -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -CFLAGS = -g -O2 -W -Wall -I../include -D_REENTRANT $(LARGE_FILE_FLAGS) -# CFLAGS = -g -O2 -Wall -I../include -D_REENTRANT -DSG_KERNEL_INCLUDES $(LARGE_FILE_FLAGS) -# CFLAGS = -g -O2 -Wall -pedantic -I../include -D_REENTRANT $(LARGE_FILE_FLAGS) +CFLAGS = -g -O2 -W -Wall -iquote ../include -D_REENTRANT $(LARGE_FILE_FLAGS) +# CFLAGS = -g -O2 -Wall -iquote ../include -D_REENTRANT -DSG_KERNEL_INCLUDES $(LARGE_FILE_FLAGS) +# CFLAGS = -g -O2 -Wall -pedantic -iquote ../include -D_REENTRANT $(LARGE_FILE_FLAGS) LDFLAGS = diff --git a/examples/Makefile.cplus b/examples/Makefile.cplus index 5abe900..b884b29 100644 --- a/examples/Makefile.cplus +++ b/examples/Makefile.cplus @@ -9,7 +9,7 @@ LD = g++ ## CC = clang++ ## LD = clang++ -EXECS = sg_tst_excl sg_tst_excl2 sg_tst_excl3 sg_tst_context +EXECS = sg_tst_excl sg_tst_excl2 sg_tst_excl3 sg_tst_context sg_tst_async EXTRAS = @@ -21,10 +21,10 @@ MAN_PREF = man8 LARGE_FILE_FLAGS = -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -CPPFLAGS = -std=c++11 -pthread -g -O2 -W -Wall -I../include -D_REENTRANT $(LARGE_FILE_FLAGS) -## CFLAGS = -g -O2 -W -Wall -I../include -D_REENTRANT $(LARGE_FILE_FLAGS) -# CFLAGS = -g -O2 -Wall -I../include -D_REENTRANT -DSG_KERNEL_INCLUDES $(LARGE_FILE_FLAGS) -# CFLAGS = -g -O2 -Wall -pedantic -I../include -D_REENTRANT $(LARGE_FILE_FLAGS) +CPPFLAGS = -std=c++11 -pthread -g -O2 -W -Wall -iquote ../include -D_REENTRANT $(LARGE_FILE_FLAGS) +## CFLAGS = -g -O2 -W -Wall -iquote ../include -D_REENTRANT $(LARGE_FILE_FLAGS) +# CFLAGS = -g -O2 -Wall -iquote ../include -D_REENTRANT -DSG_KERNEL_INCLUDES $(LARGE_FILE_FLAGS) +# CFLAGS = -g -O2 -Wall -pedantic -iquote ../include -D_REENTRANT $(LARGE_FILE_FLAGS) LDFLAGS = -std=c++11 -pthread @@ -55,6 +55,9 @@ sg_tst_excl3: sg_tst_excl3.o $(LIBFILESNEW) sg_tst_context: sg_tst_context.o $(LIBFILESNEW) $(LD) -o $@ $(LDFLAGS) $^ +sg_tst_async: sg_tst_async.o $(LIBFILESOLD) + $(LD) -o $@ $(LDFLAGS) $^ + install: $(EXECS) install -d $(INSTDIR) for name in $^; \ diff --git a/examples/README b/examples/README index b26d8da..0a17fb6 100644 --- a/examples/README +++ b/examples/README @@ -16,4 +16,4 @@ in C++11. They can be built by calling 'make -f Makefile.cplus'. A gcc/g++ compiler of 4.7.3 vintage or later will be required. Douglas Gilbert -21st October 2013 +10th July 2014 diff --git a/examples/sg_tst_async.cpp b/examples/sg_tst_async.cpp new file mode 100644 index 0000000..a070a81 --- /dev/null +++ b/examples/sg_tst_async.cpp @@ -0,0 +1,1137 @@ +/* + * Copyright (c) 2014 Douglas Gilbert. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <iostream> +#include <vector> +#include <map> +#include <list> +#include <system_error> +#include <thread> +#include <mutex> +#include <chrono> +#include <atomic> +#include <random> + +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <poll.h> +#include <errno.h> +#include <ctype.h> +#include <time.h> +#include <limits.h> +#define __STDC_FORMAT_MACROS 1 +#include <inttypes.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "sg_lib.h" +#include "sg_io_linux.h" +#include "sg_unaligned.h" + +static const char * version_str = "1.08 20141028"; +static const char * util_name = "sg_tst_async"; + +/* This is a test program for checking the async usage of the Linux sg + * driver. Each thread opens 1 file descriptor to the next sg device (1 + * or more can be given on the command line) and then starts up to 16 + * commands while checking with the poll command (or + * ioctl(SG_GET_NUM_WAITING) ) for the completion of those commands. Each + * command has a unique "pack_id" which is a sequence starting at 1. + * Either TEST UNIT UNIT, READ(16) or WRITE(16) commands are issued. + * + * This is C++ code with some things from C++11 (e.g. threads) and was + * only just able to compile (when some things were reverted) with gcc/g++ + * version 4.7.3 found in Ubuntu 13.04 . C++11 "feature complete" support + * was not available until g++ version 4.8.1 . It should build okay on + * recent distributions. + * + * The build uses various object files from the <sg3_utils>/lib directory + * which is assumed to be a sibling of this examples directory. Those + * object files in the lib directory can be built with: + * cd <sg3_utils_package_root> ; ./configure ; cd lib; make + * cd ../examples + * Then to build sg_tst_async concatenate the next 3 lines: + * g++ -Wall -std=c++11 -pthread -I ../include ../lib/sg_lib.o + * ../lib/sg_lib_data.o ../lib/sg_io_linux.o -o sg_tst_async + * sg_tst_async.cpp + * or use the C++ Makefile in that directory: + * make -f Makefile.cplus sg_tst_async + * + * Currently this utility is Linux only and uses the sg driver. The bsg + * driver is known to be broken (it doesn't match responses to the + * correct file descriptor that requested them) so this utility won't + * be extended to bsg until that is fixed. + * + * BEWARE: >>> This utility will modify a logical block (default LBA 1000) + * on the given device when the '-W' option is given. + * + */ + +using namespace std; +using namespace std::chrono; + +#define DEF_NUM_PER_THREAD 1000 +#define DEF_NUM_THREADS 4 +#define DEF_WAIT_MS 10 /* 0: yield or no wait */ +#define DEF_TIMEOUT_MS 20000 /* 20 seconds */ +#define DEF_LB_SZ 512 +#define DEF_BLOCKING 0 +#define DEF_DIRECT 0 +#define DEF_NO_XFER 0 +#define DEF_LBA 1000 + +#define MAX_Q_PER_FD 16 /* sg driver per file descriptor limit */ +#define MAX_CONSEC_NOMEMS 16 +#define URANDOM_DEV "/dev/urandom" + +#ifndef SG_FLAG_Q_AT_TAIL +#define SG_FLAG_Q_AT_TAIL 0x10 +#endif +#ifndef SG_FLAG_Q_AT_HEAD +#define SG_FLAG_Q_AT_HEAD 0x20 +#endif + + + +#define EBUFF_SZ 256 + +static mutex console_mutex; +static mutex rand_lba_mutex; +static atomic<int> async_starts(0); +static atomic<int> async_finishes(0); +static atomic<int> ebusy_count(0); +static atomic<int> eagain_count(0); +static atomic<int> uniq_pack_id(1); + +static int page_size = 4096; /* rough guess, will ask sysconf() */ + +enum command2execute {SCSI_TUR, SCSI_READ16, SCSI_WRITE16}; +/* Linux Block layer queue disciplines: */ +enum blkLQDiscipline {BLQ_DEFAULT, BLQ_AT_HEAD, BLQ_AT_TAIL}; +/* Queue disciplines of this utility. When both completions and + * queuing a new command are both possible: */ +enum myQDiscipline {MYQD_LOW, /* favour completions over new cmds */ + MYQD_MEDIUM, + MYQD_HIGH}; /* favour new cmds over completions */ + +struct opts_t { + vector<const char *> dev_names; + bool direct; + int maxq_per_thread; + int num_per_thread; + bool block; + uint64_t lba; + unsigned int hi_lba; /* last one, inclusive range */ + vector<unsigned int> hi_lbas; /* only used when hi_lba=-1 */ + int lb_sz; + bool no_xfer; + int verbose; + int wait_ms; + command2execute c2e; + blkLQDiscipline blqd; + myQDiscipline myqd; +}; + +#if 0 +class Rand_uint { +public: + Rand_uint(unsigned int lo, unsigned int hi) : p{lo, hi} {} + unsigned int operator()() const { return r(); } +private: + uniform_int_distribution<unsigned int>::param_type p; + auto r = bind(uniform_int_distribution<unsigned int>{p}, + default_random_engine()); + /* compiler thinks auto should be a static, bs again? */ +}; +#endif + +#if 0 +class Rand_uint { +public: + Rand_uint(unsigned int lo, unsigned int hi, unsigned int my_seed) + : r(bind(uniform_int_distribution<unsigned int>{lo, hi}, + default_random_engine())) { r.seed(myseed); } + unsigned int operator()() const { return r(); } +private: + function<unsigned int()> r; +}; +#endif + +/* Use this class to wrap C++11 <random> features to produce uniform random + * unsigned ints in the range [lo, hi] (inclusive) given a_seed */ +class Rand_uint { +public: + Rand_uint(unsigned int lo, unsigned int hi, unsigned int a_seed) + : uid(lo, hi), dre(a_seed) { } + /* uid ctor takes inclusive range when integral type */ + + unsigned int get() { return uid(dre); } + +private: + uniform_int_distribution<unsigned int> uid; + default_random_engine dre; +}; + + +static void +usage(void) +{ + printf("Usage: %s [-d] [-f] [-h] [-l <lba+>] [-M <maxq_per_thr>]\n" + " [-n <n_per_thr>] [-N] [-q 0|1] [-Q 0|1|2] " + "[-R]\n" + " [-s <lb_sz>] [-t <num_thrs>] [-T] [-v] " + "[-V]\n" + " [-w <wait_ms>] [-W] <sg_disk_device>*\n", + util_name); + printf(" where\n"); + printf(" -d do direct_io (def: indirect)\n"); + printf(" -f force: any sg device (def: only scsi_debug " + "owned)\n"); + printf(" WARNING: <lba> written to if '-W' given\n"); + printf(" -h print this usage message then exit\n"); + printf(" -l <lba> logical block to access (def: %u)\n", + DEF_LBA); + printf(" -l <lba,hi_lba> logical block range (inclusive), if " + "hi_lba=-1\n" + " assume last block on device\n"); + printf(" -M <maxq_per_thr> maximum commands queued per thread " + "(def:%d)\n", MAX_Q_PER_FD); + printf(" -n <n_per_thr> number of commands per thread " + "(def: %d)\n", DEF_NUM_PER_THREAD); + printf(" -N no data xfer (def: xfer on READ and " + "WRITE)\n"); + printf(" -q 0|1 0: blk q_at_head; 1: q_at_tail\n"); + printf(" -Q 0|1|2 0: favour completions (smaller q), 1: " + "medium,\n" + " 2: favour submissions (larger q, " + "default)\n"); + printf(" -s <lb_sz> logical block size (def: 512)\n"); + printf(" -R do READs (def: TUR)\n"); + printf(" -t <num_thrs> number of threads (def: %d)\n", + DEF_NUM_THREADS); + printf(" -T do TEST UNIT READYs (default is TURs)\n"); + printf(" -v increase verbosity\n"); + printf(" -V print version number then exit\n"); + printf(" -w <wait_ms> >0: poll(<wait_ms>); =0: poll(0); (def: " + "%d)\n", DEF_WAIT_MS); + printf(" -W do WRITEs (def: TUR)\n\n"); + printf("Multiple threads send READ(16), WRITE(16) or TEST UNIT READY " + "(TUR) SCSI\ncommands. There can be 1 or more <sg_disk_device>s " + "and each thread takes\nthe next in a round robin fashion. " + "Each thread queues up to 16 commands.\nOne block is transferred " + "by each READ and WRITE; zeros are written. If a\nlogical block " + "range is given, a uniform distribution generates a pseudo\n" + "random sequence of LBAs.\n"); +} + +#ifdef __GNUC__ +static int pr2serr_lk(const char * fmt, ...) + __attribute__ ((format (printf, 1, 2))); +static void pr_errno_lk(int e_no, const char * fmt, ...) + __attribute__ ((format (printf, 2, 3))); +#else +static int pr2serr_lk(const char * fmt, ...); +static void pr_errno_lk(int e_no, const char * fmt, ...); +#endif + + +static int +pr2serr_lk(const char * fmt, ...) +{ + int n; + va_list args; + lock_guard<mutex> lg(console_mutex); + + va_start(args, fmt); + n = vfprintf(stderr, fmt, args); + va_end(args); + return n; +} + +static void +pr_errno_lk(int e_no, const char * fmt, ...) +{ + char b[160]; + va_list args; + lock_guard<mutex> lg(console_mutex); + + va_start(args, fmt); + vsnprintf(b, sizeof(b), fmt, args); + fprintf(stderr, "%s: %s\n", b, strerror(e_no)); + va_end(args); +} + +static unsigned int +get_urandom_uint(void) +{ + unsigned int res = 0; + int n; + unsigned char b[sizeof(unsigned int)]; + lock_guard<mutex> lg(rand_lba_mutex); + + int fd = open(URANDOM_DEV, O_RDONLY); + if (fd >= 0) { + n = read(fd, b, sizeof(unsigned int)); + if (sizeof(unsigned int) == n) + memcpy(&res, b, sizeof(unsigned int)); + close(fd); + } + return res; +} + +#define TUR_CMD_LEN 6 +#define READ16_REPLY_LEN 512 +#define READ16_CMD_LEN 16 +#define WRITE16_REPLY_LEN 512 +#define WRITE16_CMD_LEN 16 + +/* Returns 0 if command injected okay, else -1 */ +static int +start_sg3_cmd(int sg_fd, command2execute cmd2exe, int pack_id, uint64_t lba, + unsigned char * lbp, int xfer_bytes, int flags) +{ + struct sg_io_hdr pt; + unsigned char turCmdBlk[TUR_CMD_LEN] = {0, 0, 0, 0, 0, 0}; + unsigned char r16CmdBlk[READ16_CMD_LEN] = + {0x88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}; + unsigned char w16CmdBlk[WRITE16_CMD_LEN] = + {0x8a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}; + unsigned char sense_buffer[64]; + const char * np = NULL; + + memset(&pt, 0, sizeof(pt)); + switch (cmd2exe) { + case SCSI_TUR: + np = "TEST UNIT READY"; + pt.cmdp = turCmdBlk; + pt.cmd_len = sizeof(turCmdBlk); + pt.dxfer_direction = SG_DXFER_NONE; + break; + case SCSI_READ16: + np = "READ(16)"; + if (lba > 0xffffffff) + sg_put_unaligned_be32(lba >> 32, &r16CmdBlk[2]); + sg_put_unaligned_be32(lba & 0xffffffff, &r16CmdBlk[6]); + pt.cmdp = r16CmdBlk; + pt.cmd_len = sizeof(r16CmdBlk); + pt.dxfer_direction = SG_DXFER_FROM_DEV; + pt.dxferp = lbp; + pt.dxfer_len = xfer_bytes; + break; + case SCSI_WRITE16: + np = "WRITE(16)"; + if (lba > 0xffffffff) + sg_put_unaligned_be32(lba >> 32, &w16CmdBlk[2]); + sg_put_unaligned_be32(lba & 0xffffffff, &w16CmdBlk[6]); + pt.cmdp = w16CmdBlk; + pt.cmd_len = sizeof(w16CmdBlk); + pt.dxfer_direction = SG_DXFER_TO_DEV; + pt.dxferp = lbp; + pt.dxfer_len = xfer_bytes; + break; + } + pt.interface_id = 'S'; + pt.mx_sb_len = sizeof(sense_buffer); + pt.sbp = sense_buffer; /* ignored .... */ + pt.timeout = DEF_TIMEOUT_MS; + pt.pack_id = pack_id; + pt.flags = flags; + + for (int k = 0; write(sg_fd, &pt, sizeof(pt)) < 0; ++k) { + if ((ENOMEM == errno) && (k < MAX_CONSEC_NOMEMS)) { + this_thread::yield(); + continue; + } + pr_errno_lk(errno, "%s: %s, pack_id=%d", __func__, np, pack_id); + return -1; + } + return 0; +} + +static int +finish_sg3_cmd(int sg_fd, command2execute cmd2exe, int & pack_id, int wait_ms, + unsigned int & eagains) +{ + int ok, res; + struct sg_io_hdr pt; + unsigned char sense_buffer[64]; + const char * np = NULL; + + memset(&pt, 0, sizeof(pt)); + switch (cmd2exe) { + case SCSI_TUR: + np = "TEST UNIT READY"; + break; + case SCSI_READ16: + np = "READ(16)"; + break; + case SCSI_WRITE16: + np = "WRITE(16)"; + break; + } + pt.interface_id = 'S'; + pt.mx_sb_len = sizeof(sense_buffer); + pt.sbp = sense_buffer; + pt.timeout = DEF_TIMEOUT_MS; + pt.pack_id = 0; + + while (((res = read(sg_fd, &pt, sizeof(pt))) < 0) && + (EAGAIN == errno)) { + ++eagains; + if (wait_ms > 0) + this_thread::sleep_for(milliseconds{wait_ms}); + else if (0 == wait_ms) + this_thread::yield(); + else if (-2 == wait_ms) + sleep(0); // process yield ?? + } + if (res < 0) { + pr_errno_lk(errno, "%s: %s", __func__, np); + return -1; + } + /* now for the error processing */ + pack_id = pt.pack_id; + ok = 0; + switch (sg_err_category3(&pt)) { + case SG_LIB_CAT_CLEAN: + ok = 1; + break; + case SG_LIB_CAT_RECOVERED: + pr2serr_lk("%s: Recovered error on %s, continuing\n", __func__, np); + ok = 1; + break; + default: /* won't bother decoding other categories */ + { + lock_guard<mutex> lg(console_mutex); + sg_chk_n_print3(np, &pt, 1); + } + break; + } + return ok ? 0 : -1; +} + +/* Should have page alignment if direct_io chosen */ +static unsigned char * +get_aligned_heap(int bytes_at_least) +{ + int n; + void * wp; + + if (bytes_at_least < page_size) + n = page_size; + else + n = bytes_at_least; +#if 1 + int err = posix_memalign(&wp, page_size, n); + if (err) { + pr2serr_lk("posix_memalign: error [%d] out of memory?\n", err); + return NULL; + } + memset(wp, 0, n); + return (unsigned char *)wp; +#else + /* hack if posix_memalign() is not available */ + if (n == page_size) { + wp = calloc(page_size, 1); + memset(wp, 0, n); + return (unsigned char *)wp; + } else { + pr2serr_lk("get_aligned_heap: too fiddly to align, choose smaller " + "lb_sz\n"); + return NULL; + } +#endif +} + +static void +work_thread(int id, struct opts_t * op) +{ + int thr_async_starts = 0; + int thr_async_finishes = 0; + unsigned int thr_eagain_count = 0; + unsigned int seed = 0; + unsigned int hi_lba; + int k, n, res, sg_fd, num_outstanding, do_inc, npt, pack_id, sg_flags; + int num_waiting_read, num_to_read; + int open_flags = O_RDWR; + bool is_rw = (SCSI_TUR != op->c2e); + char ebuff[EBUFF_SZ]; + uint64_t lba; + unsigned char * lbp; + const char * dev_name; + const char * err = NULL; + Rand_uint * ruip = NULL; + struct pollfd pfd[1]; + list<unsigned char *> free_lst; /* of aligned lb buffers */ + map<int, unsigned char *> pi_2_buff; /* pack_id -> lb buffer */ + map<int, uint64_t> pi_2_lba; /* pack_id -> LBA */ + + /* device name and hi_lba may depend on id */ + n = op->dev_names.size(); + dev_name = op->dev_names[id % n]; + if ((UINT_MAX == op->hi_lba) && (n == (int)op->hi_lbas.size())) + hi_lba = op->hi_lbas[id % n]; + else + hi_lba = op->hi_lba; + + if (op->verbose) { + if ((op->verbose > 1) && hi_lba) + pr2serr_lk("Enter work_thread id=%d using %s\n" + " LBA range: 0x%x to 0x%x (inclusive)\n", + id, dev_name, (unsigned int)op->lba, hi_lba); + else + pr2serr_lk("Enter work_thread id=%d using %s\n", id, dev_name); + } + if (! op->block) + open_flags |= O_NONBLOCK; + + sg_fd = open(dev_name, open_flags); + if (sg_fd < 0) { + pr_errno_lk(errno, "%s: id=%d, error opening file: %s", __func__, id, + dev_name); + return; + } + pfd[0].fd = sg_fd; + pfd[0].events = POLLIN; + if (is_rw && hi_lba) { + seed = get_urandom_uint(); + if (op->verbose > 1) + pr2serr_lk(" id=%d, /dev/urandom seed=0x%x\n", id, seed); + ruip = new Rand_uint((unsigned int)op->lba, hi_lba, seed); + } + + sg_flags = 0; + if (BLQ_AT_TAIL == op->blqd) + sg_flags |= SG_FLAG_Q_AT_TAIL; + else if (BLQ_AT_HEAD == op->blqd) + sg_flags |= SG_FLAG_Q_AT_HEAD; + if (op->direct) + sg_flags |= SG_FLAG_DIRECT_IO; + if (op->no_xfer) + sg_flags |= SG_FLAG_NO_DXFER; + if (op->verbose > 1) + pr2serr_lk(" id=%d, sg_flags=0x%x, %s cmds\n", id, sg_flags, + ((SCSI_TUR == op->c2e) ? "TUR": + ((SCSI_READ16 == op->c2e) ? "READ" : "WRITE"))); + + npt = op->num_per_thread; + /* main loop, continues until num_per_thread exhausted and there are + * no more outstanding responses */ + for (k = 0, num_outstanding = 0; (k < npt) || num_outstanding; + k = do_inc ? k + 1 : k) { + do_inc = 0; + if ((num_outstanding < op->maxq_per_thread) && (k < npt)) { + do_inc = 1; + pack_id = uniq_pack_id.fetch_add(1); + if (is_rw) { /* get new lb buffer or one from free list */ + if (free_lst.empty()) { + lbp = get_aligned_heap(op->lb_sz); + if (NULL == lbp) { + err = "out of memory"; + break; + } + } else { + lbp = free_lst.back(); + free_lst.pop_back(); + } + } else + lbp = NULL; + if (is_rw) { + if (ruip) { + lba = ruip->get(); /* fetch a random LBA */ + if (op->verbose > 3) + pr2serr_lk(" id=%d: start IO at lba=0x%" PRIx64 "\n", + id, lba); + } else + lba = op->lba; + } else + lba = 0; + if (start_sg3_cmd(sg_fd, op->c2e, pack_id, lba, lbp, op->lb_sz, + sg_flags)) { + err = "start_sg3_cmd()"; + break; + } + ++thr_async_starts; + ++num_outstanding; + pi_2_buff[pack_id] = lbp; + if (ruip) + pi_2_lba[pack_id] = lba; + } + num_to_read = 0; + if ((num_outstanding >= op->maxq_per_thread) || (k >= npt)) { + /* full queue or finished injecting */ + num_waiting_read = 0; + if (ioctl(sg_fd, SG_GET_NUM_WAITING, &num_waiting_read) < 0) { + err = "ioctl(SG_GET_NUM_WAITING) failed"; + break; + } + if (1 == num_waiting_read) + num_to_read = num_waiting_read; + else if (num_waiting_read > 0) { + if (k >= npt) + num_to_read = num_waiting_read; + else { + switch (op->myqd) { + case MYQD_LOW: + num_to_read = num_waiting_read; + break; + case MYQD_MEDIUM: + num_to_read = num_waiting_read / 2; + break; + case MYQD_HIGH: + default: + num_to_read = 1; + break; + } + } + } else { /* nothing waiting to be read */ + n = (op->wait_ms > 0) ? op->wait_ms : 0; + while (0 == (res = poll(pfd, 1, n))) { + if (res < 0) { + err = "poll(wait_ms) failed"; + break; + } + } + if (err) + break; + } + } else { /* not full, not finished injecting */ + if (MYQD_HIGH == op->myqd) + num_to_read = 0; + else { + num_waiting_read = 0; + if (ioctl(sg_fd, SG_GET_NUM_WAITING, &num_waiting_read) < 0) { + err = "ioctl(SG_GET_NUM_WAITING) failed"; + break; + } + if (num_waiting_read > 0) + num_to_read = num_waiting_read / + ((MYQD_LOW == op->myqd) ? 1 : 2); + else + num_to_read = 0; + } + } + + while (num_to_read-- > 0) { + if (finish_sg3_cmd(sg_fd, op->c2e, pack_id, op->wait_ms, + thr_eagain_count)) { + err = "finish_sg3_cmd()"; + if (ruip && (pack_id > 0)) { + auto q = pi_2_lba.find(pack_id); + + if (q != pi_2_lba.end()) { + snprintf(ebuff, sizeof(ebuff), "%s: lba=0x%" PRIx64 , + err, q->second); + err = ebuff; + } + } + break; + } + ++thr_async_finishes; + --num_outstanding; + auto p = pi_2_buff.find(pack_id); + + if (p == pi_2_buff.end()) { + snprintf(ebuff, sizeof(ebuff), "pack_id=%d from " + "finish_sg3_cmd() not found\n", pack_id); + if (! err) + err = ebuff; + } else { + lbp = p->second; + pi_2_buff.erase(p); + if (lbp) + free_lst.push_front(lbp); + } + if (ruip && (pack_id > 0)) { + auto q = pi_2_lba.find(pack_id); + + if (q != pi_2_lba.end()) { + if (op->verbose > 3) + pr2serr_lk(" id=%d: finish IO at lba=0x%" PRIx64 + "\n", id, q->second); + pi_2_lba.erase(q); + } + } + if (err) + break; + } + if (err) + break; + } + close(sg_fd); // sg driver will handle any commands "in flight" + if (ruip) + delete ruip; + + if (err || (k < npt)) { + if (k < npt) + pr2serr_lk("thread id=%d FAILed at iteration %d%s%s\n", id, k, + (err ? ", Reason: " : ""), (err ? err : "")); + else + pr2serr_lk("thread id=%d FAILed on last%s%s\n", id, + (err ? ", Reason: " : ""), (err ? err : "")); + } + n = pi_2_buff.size(); + if (n > 0) + pr2serr_lk("thread id=%d Still %d elements in pi_2_buff map on " + "exit\n", id, n); + for (k = 0; ! free_lst.empty(); ++k) { + lbp = free_lst.back(); + free_lst.pop_back(); + if (lbp) + free(lbp); + } + if ((op->verbose > 2) && (k > 0)) + pr2serr_lk("thread id=%d Maximum number of READ/WRITEs queued: %d\n", + id, k); + async_starts += thr_async_starts; + async_finishes += thr_async_finishes; + eagain_count += thr_eagain_count; +} + +#define INQ_REPLY_LEN 96 +#define INQ_CMD_LEN 6 + +/* Send INQUIRY and fetches response. If okay puts PRODUCT ID field + * in b (up to m_blen bytes). Does not use O_EXCL flag. Returns 0 on success, + * else -1 . */ +static int +do_inquiry_prod_id(const char * dev_name, int block, char * b, int b_mlen) +{ + int sg_fd, ok, ret; + struct sg_io_hdr pt; + unsigned char inqCmdBlk [INQ_CMD_LEN] = + {0x12, 0, 0, 0, INQ_REPLY_LEN, 0}; + unsigned char inqBuff[INQ_REPLY_LEN]; + unsigned char sense_buffer[64]; + int open_flags = O_RDWR; /* O_EXCL | O_RDONLY fails with EPERM */ + + if (! block) + open_flags |= O_NONBLOCK; + sg_fd = open(dev_name, open_flags); + if (sg_fd < 0) { + pr_errno_lk(errno, "%s: error opening file: %s", __func__, dev_name); + return -1; + } + /* Prepare INQUIRY command */ + memset(&pt, 0, sizeof(pt)); + pt.interface_id = 'S'; + pt.cmd_len = sizeof(inqCmdBlk); + /* pt.iovec_count = 0; */ /* memset takes care of this */ + pt.mx_sb_len = sizeof(sense_buffer); + pt.dxfer_direction = SG_DXFER_FROM_DEV; + pt.dxfer_len = INQ_REPLY_LEN; + pt.dxferp = inqBuff; + pt.cmdp = inqCmdBlk; + pt.sbp = sense_buffer; + pt.timeout = 20000; /* 20000 millisecs == 20 seconds */ + /* pt.flags = 0; */ /* take defaults: indirect IO, etc */ + /* pt.pack_id = 0; */ + /* pt.usr_ptr = NULL; */ + + if (ioctl(sg_fd, SG_IO, &pt) < 0) { + pr_errno_lk(errno, "%s: Inquiry SG_IO ioctl error", __func__); + close(sg_fd); + return -1; + } + + /* now for the error processing */ + ok = 0; + switch (sg_err_category3(&pt)) { + case SG_LIB_CAT_CLEAN: + ok = 1; + break; + case SG_LIB_CAT_RECOVERED: + pr2serr_lk("Recovered error on INQUIRY, continuing\n"); + ok = 1; + break; + default: /* won't bother decoding other categories */ + { + lock_guard<mutex> lg(console_mutex); + sg_chk_n_print3("INQUIRY command error", &pt, 1); + } + break; + } + if (ok) { + /* Good, so fetch Product ID from response, copy to 'b' */ + if (b_mlen > 0) { + if (b_mlen > 16) { + memcpy(b, inqBuff + 16, 16); + b[16] = '\0'; + } else { + memcpy(b, inqBuff + 16, b_mlen - 1); + b[b_mlen - 1] = '\0'; + } + } + ret = 0; + } else + ret = -1; + close(sg_fd); + return ret; +} + +/* Only allow ranges up to 2**32-1 upper limit, so READ CAPACITY(10) + * sufficient. Return of 0 -> success, -1 -> failure, 2 -> try again */ +static int +do_read_capacity(const char * dev_name, int block, unsigned int * last_lba, + unsigned int * blk_sz) +{ + int res, sg_fd; + unsigned char rcCmdBlk [10] = {0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + unsigned char rcBuff[64]; + unsigned char sense_b[64]; + sg_io_hdr_t io_hdr; + int open_flags = O_RDWR; /* O_EXCL | O_RDONLY fails with EPERM */ + + if (! block) + open_flags |= O_NONBLOCK; + sg_fd = open(dev_name, open_flags); + if (sg_fd < 0) { + pr_errno_lk(errno, "%s: error opening file: %s", __func__, dev_name); + return -1; + } + /* Prepare READ CAPACITY(10) command */ + memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof(rcCmdBlk); + io_hdr.mx_sb_len = sizeof(sense_b); + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.dxfer_len = sizeof(rcBuff); + io_hdr.dxferp = rcBuff; + io_hdr.cmdp = rcCmdBlk; + io_hdr.sbp = sense_b; + io_hdr.timeout = 20000; /* 20000 millisecs == 20 seconds */; + + if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { + pr_errno_lk(errno, "%s (SG_IO) error", __func__); + close(sg_fd); + return -1; + } + res = sg_err_category3(&io_hdr); + if (SG_LIB_CAT_UNIT_ATTENTION == res) { + lock_guard<mutex> lg(console_mutex); + sg_chk_n_print3("read capacity", &io_hdr, 1); + close(sg_fd); + return 2; /* probably have another go ... */ + } else if (SG_LIB_CAT_CLEAN != res) { + lock_guard<mutex> lg(console_mutex); + sg_chk_n_print3("read capacity", &io_hdr, 1); + close(sg_fd); + return -1; + } + *last_lba = sg_get_unaligned_be32(&rcBuff[0]); + *blk_sz = sg_get_unaligned_be32(&rcBuff[4]); + close(sg_fd); + return 0; +} + + +int +main(int argc, char * argv[]) +{ + int k, n, res; + int force = 0; + int64_t ll; + int num_threads = DEF_NUM_THREADS; + char b[128]; + struct timespec start_tm, end_tm; + struct opts_t opts; + struct opts_t * op; + const char * cp; + const char * dev_name; + + op = &opts; + op->direct = !! DEF_DIRECT; + op->lba = DEF_LBA; + op->hi_lba = 0; + op->lb_sz = DEF_LB_SZ; + op->maxq_per_thread = MAX_Q_PER_FD; + op->num_per_thread = DEF_NUM_PER_THREAD; + op->no_xfer = !! DEF_NO_XFER; + op->verbose = 0; + op->wait_ms = DEF_WAIT_MS; + op->c2e = SCSI_TUR; + op->blqd = BLQ_DEFAULT; + op->block = !! DEF_BLOCKING; + op->myqd = MYQD_HIGH; + page_size = sysconf(_SC_PAGESIZE); + + for (k = 1; k < argc; ++k) { + if (0 == memcmp("-d", argv[k], 2)) + op->direct = true; + else if (0 == memcmp("-f", argv[k], 2)) + ++force; + else if (0 == memcmp("-h", argv[k], 2)) { + usage(); + return 0; + } else if (0 == memcmp("-l", argv[k], 2)) { + ++k; + if ((k < argc) && isdigit(*argv[k])) { + ll = sg_get_llnum(argv[k]); + if (-1 == ll) { + pr2serr_lk("could not decode lba\n"); + return 1; + } else + op->lba = (uint64_t)ll; + cp = strchr(argv[k], ','); + if (cp) { + if (0 == strcmp("-1", cp + 1)) + op->hi_lba = UINT_MAX; + else { + ll = sg_get_llnum(cp + 1); + if ((-1 == ll) || (ll > UINT_MAX)) { + pr2serr_lk("could not decode hi_lba, or > " + "UINT_MAX\n"); + return 1; + } else + op->hi_lba = (unsigned int)ll; + } + } + } else + break; + } else if (0 == memcmp("-M", argv[k], 2)) { + ++k; + if ((k < argc) && isdigit(*argv[k])) { + n = atoi(argv[k]); + if ((n < 1) || (n > MAX_Q_PER_FD)) { + pr2serr_lk("-M expects a value from 1 to %d\n", + MAX_Q_PER_FD); + return 1; + } + op->maxq_per_thread = n; + } else + break; + } else if (0 == memcmp("-n", argv[k], 2)) { + ++k; + if ((k < argc) && isdigit(*argv[k])) + op->num_per_thread = sg_get_num(argv[k]); + else + break; + } else if (0 == memcmp("-N", argv[k], 2)) + op->no_xfer = true; + else if (0 == memcmp("-q", argv[k], 2)) { + ++k; + if ((k < argc) && isdigit(*argv[k])) { + n = atoi(argv[k]); + if (0 == n) + op->blqd = BLQ_AT_HEAD; + else if (1 == n) + op->blqd = BLQ_AT_TAIL; + } else + break; + } else if (0 == memcmp("-Q", argv[k], 2)) { + ++k; + if ((k < argc) && isdigit(*argv[k])) { + n = atoi(argv[k]); + if (0 == n) + op->myqd = MYQD_LOW; + else if (1 == n) + op->myqd = MYQD_MEDIUM; + else if (2 == n) + op->myqd = MYQD_HIGH; + } else + break; + } else if (0 == memcmp("-R", argv[k], 2)) + op->c2e = SCSI_READ16; + else if (0 == memcmp("-s", argv[k], 2)) { + ++k; + if ((k < argc) && isdigit(*argv[k])) { + op->lb_sz = atoi(argv[k]); + if (op->lb_sz < 256) { + cerr << "Strange lb_sz, using 256" << endl; + op->lb_sz = 256; + } + } else + break; + } else if (0 == memcmp("-t", argv[k], 2)) { + ++k; + if ((k < argc) && isdigit(*argv[k])) + num_threads = atoi(argv[k]); + else + break; + } else if (0 == memcmp("-T", argv[k], 2)) + op->c2e = SCSI_TUR; + else if (0 == memcmp("-vvvv", argv[k], 5)) + op->verbose += 4; + else if (0 == memcmp("-vvv", argv[k], 4)) + op->verbose += 3; + else if (0 == memcmp("-vv", argv[k], 3)) + op->verbose += 2; + else if (0 == memcmp("-v", argv[k], 2)) + ++op->verbose; + else if (0 == memcmp("-V", argv[k], 2)) { + printf("%s version: %s\n", util_name, version_str); + return 0; + } else if (0 == memcmp("-w", argv[k], 2)) { + ++k; + if ((k < argc) && (isdigit(*argv[k]) || ('-' == *argv[k]))) { + if ('-' == *argv[k]) + op->wait_ms = - atoi(argv[k] + 1); + else + op->wait_ms = atoi(argv[k]); + } else + break; + } else if (0 == memcmp("-W", argv[k], 2)) + op->c2e = SCSI_WRITE16; + else if (*argv[k] == '-') { + pr2serr_lk("Unrecognized switch: %s\n", argv[k]); + return 1; + } else + op->dev_names.push_back(argv[k]); + } + if (0 == op->dev_names.size()) { + usage(); + return 1; + } + if (op->hi_lba && (op->lba > op->hi_lba)) { + cerr << "lba,hi_lba range is illegal" << endl; + return 1; + } + + try { + struct stat a_stat; + + for (k = 0; k < (int)op->dev_names.size(); ++k) { + dev_name = op->dev_names[k]; + if (stat(dev_name, &a_stat) < 0) { + snprintf(b, sizeof(b), "could not stat() %s", dev_name); + perror(b); + return 1; + } + if (! S_ISCHR(a_stat.st_mode)) { + pr2serr_lk("%s should be a sg device which is a char " + "device. %s\n", dev_name, dev_name); + pr2serr_lk("is not a char device and damage could be done " + "if it is a BLOCK\ndevice, exiting ...\n"); + return 1; + } + if (! force) { + res = do_inquiry_prod_id(dev_name, op->block, b, sizeof(b)); + if (res) { + pr2serr_lk("INQUIRY failed on %s\n", dev_name); + return 1; + } + // For safety, since <lba> written to, only permit scsi_debug + // devices. Bypass this with '-f' option. + if (0 != memcmp("scsi_debug", b, 10)) { + pr2serr_lk("Since this utility may write to LBAs, " + "only devices with the\n" + "product ID 'scsi_debug' accepted. Use '-f' " + "to override.\n"); + return 2; + } + } + if (UINT_MAX == op->hi_lba) { + unsigned int last_lba; + unsigned int blk_sz; + + res = do_read_capacity(dev_name, op->block, &last_lba, + &blk_sz); + if (2 == res) + res = do_read_capacity(dev_name, op->block, &last_lba, + &blk_sz); + if (res) { + pr2serr_lk("READ CAPACITY(10) failed on %s\n", dev_name); + return 1; + } + op->hi_lbas.push_back(last_lba); + if (blk_sz != (unsigned int)op->lb_sz) + pr2serr_lk(">>> warning: Logical block size (%d) of %s\n" + " differs from command line option (or " + "default)\n", blk_sz, dev_name); + } + } + + start_tm.tv_sec = 0; + start_tm.tv_nsec = 0; + if (clock_gettime(CLOCK_MONOTONIC, &start_tm) < 0) + perror("clock_gettime failed"); + + vector<thread *> vt; + + /* start multi-threaded section */ + for (k = 0; k < num_threads; ++k) { + thread * tp = new thread {work_thread, k, op}; + vt.push_back(tp); + } + + // g++ 4.7.3 didn't like range-for loop here + for (k = 0; k < (int)vt.size(); ++k) + vt[k]->join(); + /* end multi-threaded section, just this main thread left */ + + for (k = 0; k < (int)vt.size(); ++k) + delete vt[k]; + + n = uniq_pack_id.load() - 1; + if ((n > 0) && (0 == clock_gettime(CLOCK_MONOTONIC, &end_tm))) { + struct timespec res_tm; + double a, b; + + res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec; + res_tm.tv_nsec = end_tm.tv_nsec - start_tm.tv_nsec; + if (res_tm.tv_nsec < 0) { + --res_tm.tv_sec; + res_tm.tv_nsec += 1000000000; + } + a = res_tm.tv_sec; + a += (0.000001 * (res_tm.tv_nsec / 1000)); + b = (double)n; + if (a > 0.000001) { + printf("Time to complete %d commands was %d.%06d seconds\n", + n, (int)res_tm.tv_sec, (int)(res_tm.tv_nsec / 1000)); + printf("Implies %.0f IOPS\n", (b / a)); + } + } + + if (op->verbose) { + cout << "Number of async_starts: " << async_starts.load() << endl; + cout << "Number of async_finishes: " << async_finishes.load() << + endl; + cout << "Last pack_id: " << n << endl; + cout << "Number of EBUSYs: " << ebusy_count.load() << endl; + cout << "Number of EAGAINs: " << eagain_count.load() << endl; + } + } + catch(system_error& e) { + cerr << "got a system_error exception: " << e.what() << '\n'; + auto ec = e.code(); + cerr << "category: " << ec.category().name() << '\n'; + cerr << "value: " << ec.value() << '\n'; + cerr << "message: " << ec.message() << '\n'; + cerr << "\nNote: if g++ may need '-pthread' or similar in " + "compile/link line" << '\n'; + } + catch(...) { + cerr << "got another exception: " << '\n'; + } + return 0; +} diff --git a/examples/sg_tst_context.cpp b/examples/sg_tst_context.cpp index 86612ba..6ac9935 100644 --- a/examples/sg_tst_context.cpp +++ b/examples/sg_tst_context.cpp @@ -46,7 +46,7 @@ #include "sg_lib.h" #include "sg_pt.h" -static const char * version_str = "1.01 20131119"; +static const char * version_str = "1.02 20140828"; static const char * util_name = "sg_tst_context"; /* This is a test program for checking that file handles keep their @@ -165,9 +165,9 @@ pt_cat_no_good(int cat, struct sg_pt_base * ptp, const unsigned char * sbp) break; } if (cp) { - console_mutex.lock(); + lock_guard<mutex> lg(console_mutex); + fprintf(stderr, cp, b); - console_mutex.unlock(); } return -EIO /* -5 */; } @@ -191,11 +191,13 @@ do_tur(int pt_fd, int id) set_scsi_pt_sense(ptp, sense_buffer, sizeof(sense_buffer)); res = do_scsi_pt(ptp, pt_fd, 20 /* secs timeout */, 1); if (res) { - console_mutex.lock(); - fprintf(stderr, "TEST UNIT READY do_scsi_pt() submission error, " - "id=%d\n", id); + { + lock_guard<mutex> lg(console_mutex); + + fprintf(stderr, "TEST UNIT READY do_scsi_pt() submission error, " + "id=%d\n", id); + } res = pt_err(res); - console_mutex.unlock(); goto err; } cat = get_scsi_pt_result_category(ptp); @@ -206,10 +208,12 @@ do_tur(int pt_fd, int id) res = 1024; goto err; } - console_mutex.lock(); - fprintf(stderr, "TEST UNIT READY do_scsi_pt() category problem, " - "id=%d\n", id); - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + fprintf(stderr, "TEST UNIT READY do_scsi_pt() category problem, " + "id=%d\n", id); + } res = pt_cat_no_good(cat, ptp, sense_buffer); goto err; } @@ -237,11 +241,13 @@ do_ssu(int pt_fd, int id, bool start) set_scsi_pt_sense(ptp, sense_buffer, sizeof(sense_buffer)); res = do_scsi_pt(ptp, pt_fd, 40 /* secs timeout */, 1); if (res) { - console_mutex.lock(); - fprintf(stderr, "START STOP UNIT do_scsi_pt() submission error, " - "id=%d\n", id); + { + lock_guard<mutex> lg(console_mutex); + + fprintf(stderr, "START STOP UNIT do_scsi_pt() submission error, " + "id=%d\n", id); + } res = pt_err(res); - console_mutex.unlock(); goto err; } cat = get_scsi_pt_result_category(ptp); @@ -252,10 +258,12 @@ do_ssu(int pt_fd, int id, bool start) res = 1024; goto err; } - console_mutex.lock(); - fprintf(stderr, "START STOP UNIT do_scsi_pt() category problem, " - "id=%d\n", id); - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + fprintf(stderr, "START STOP UNIT do_scsi_pt() category problem, " + "id=%d\n", id); + } res = pt_cat_no_good(cat, ptp, sense_buffer); goto err; } @@ -278,10 +286,12 @@ work_thread(const char * dev_name, int id, int num, bool share, bool started = true; char ebuff[EBUFF_SZ]; - console_mutex.lock(); - cerr << "Enter work_thread id=" << id << " num=" << num << " share=" - << share << endl; - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + cerr << "Enter work_thread id=" << id << " num=" << num << " share=" + << share << endl; + } if (! share) { int open_flags = O_RDWR; @@ -301,9 +311,9 @@ work_thread(const char * dev_name, int id, int num, bool share, return; } if (thr_ebusy_count) { - count_mutex.lock(); + lock_guard<mutex> lg(count_mutex); + ebusy_count += thr_ebusy_count; - count_mutex.unlock(); } } for (k = 0; k < num; ++k) { @@ -329,18 +339,22 @@ work_thread(const char * dev_name, int id, int num, bool share, if (! share) scsi_pt_close_device(pt_fd); - count_mutex.lock(); - even_notreadys += thr_even_notreadys; - odd_notreadys += thr_odd_notreadys; - count_mutex.unlock(); + { + lock_guard<mutex> lg(count_mutex); - console_mutex.lock(); - if (k < num) - cerr << "thread id=" << id << " FAILed at iteration: " << k - << " [negated errno: " << res << "]\n"; - else - cerr << "thread id=" << id << " normal exit" << '\n'; - console_mutex.unlock(); + even_notreadys += thr_even_notreadys; + odd_notreadys += thr_odd_notreadys; + } + + { + lock_guard<mutex> lg(console_mutex); + + if (k < num) + cerr << "thread id=" << id << " FAILed at iteration: " << k + << " [negated errno: " << res << "]\n"; + else + cerr << "thread id=" << id << " normal exit" << '\n'; + } } diff --git a/examples/sg_tst_excl.cpp b/examples/sg_tst_excl.cpp index b19d4a2..5b4e695 100644 --- a/examples/sg_tst_excl.cpp +++ b/examples/sg_tst_excl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Douglas Gilbert. + * Copyright (c) 2013-2014 Douglas Gilbert. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,7 +46,7 @@ #include "sg_lib.h" #include "sg_io_linux.h" -static const char * version_str = "1.07 20131110"; +static const char * version_str = "1.09 20140828"; static const char * util_name = "sg_tst_excl"; /* This is a test program for checking O_EXCL on open() works. It uses @@ -72,6 +72,8 @@ static const char * util_name = "sg_tst_excl"; * g++ -Wall -std=c++11 -pthread -I ../include ../lib/sg_lib.o * ../lib/sg_lib_data.o ../lib/sg_io_linux.o -o sg_tst_excl * sg_tst_excl.cpp + * or use the C++ Makefile in that directory: + * make -f Makefile.cplus sg_tst_excl * * Currently this utility is Linux only and assumes the SG_IO v3 interface * which is supported by sg and block devices (but not bsg devices which @@ -210,17 +212,21 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block, // queue up two READ_16s to same LBA if (write(sg_fd, &pt, sizeof(pt)) < 0) { - console_mutex.lock(); - perror("do_rd_inc_wr_twice: write(sg, READ_16)"); - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + perror("do_rd_inc_wr_twice: write(sg, READ_16)"); + } close(sg_fd); return -1; } pt2 = pt; if (write(sg_fd, &pt2, sizeof(pt2)) < 0) { - console_mutex.lock(); - perror("do_rd_inc_wr_twice: write(sg, READ_16) 2"); - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + perror("do_rd_inc_wr_twice: write(sg, READ_16) 2"); + } close(sg_fd); return -1; } @@ -236,9 +242,11 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block, sleep(0); // process yield ?? } if (res < 0) { - console_mutex.lock(); - perror("do_rd_inc_wr_twice: read(sg, READ_16)"); - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + perror("do_rd_inc_wr_twice: read(sg, READ_16)"); + } close(sg_fd); return -1; } @@ -249,15 +257,19 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block, ok = 1; break; case SG_LIB_CAT_RECOVERED: - console_mutex.lock(); - fprintf(stderr, "Recovered error on READ_16, continuing\n"); - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + fprintf(stderr, "Recovered error on READ_16, continuing\n"); + } ok = 1; break; default: /* won't bother decoding other categories */ - console_mutex.lock(); - sg_chk_n_print3("READ_16 command error", &pt, 1); - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + sg_chk_n_print3("READ_16 command error", &pt, 1); + } break; } if (ok) { @@ -272,9 +284,11 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block, sleep(0); // process yield ?? } if (res < 0) { - console_mutex.lock(); - perror("do_rd_inc_wr_twice: read(sg, READ_16) 2"); - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + perror("do_rd_inc_wr_twice: read(sg, READ_16) 2"); + } close(sg_fd); return -1; } @@ -286,15 +300,20 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block, ok = 1; break; case SG_LIB_CAT_RECOVERED: - console_mutex.lock(); - fprintf(stderr, "Recovered error on READ_16, continuing 2\n"); - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + fprintf(stderr, "Recovered error on READ_16, continuing " + "2\n"); + } ok = 1; break; default: /* won't bother decoding other categories */ - console_mutex.lock(); - sg_chk_n_print3("READ_16 command error 2", &pt, 1); - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + sg_chk_n_print3("READ_16 command error 2", &pt, 1); + } break; } } @@ -333,9 +352,11 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block, pt.pack_id = id; if (ioctl(sg_fd, SG_IO, &pt) < 0) { - console_mutex.lock(); - perror("do_rd_inc_wr_twice: WRITE_16 SG_IO ioctl error"); - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + perror("do_rd_inc_wr_twice: WRITE_16 SG_IO ioctl error"); + } close(sg_fd); return -1; } @@ -346,15 +367,19 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block, ok = 1; break; case SG_LIB_CAT_RECOVERED: - console_mutex.lock(); - fprintf(stderr, "Recovered error on WRITE_16, continuing\n"); - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + fprintf(stderr, "Recovered error on WRITE_16, continuing\n"); + } ok = 1; break; default: /* won't bother decoding other categories */ - console_mutex.lock(); - sg_chk_n_print3("WRITE_16 command error", &pt, 1); - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + sg_chk_n_print3("WRITE_16 command error", &pt, 1); + } break; } if (! ok) { @@ -468,10 +493,12 @@ work_thread(const char * dev_name, unsigned int lba, int id, int block, unsigned int thr_eagain_count = 0; int k, res; - console_mutex.lock(); - cerr << "Enter work_thread id=" << id << " excl=" << excl << " block=" - << block << endl; - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + cerr << "Enter work_thread id=" << id << " excl=" << excl << " block=" + << block << endl; + } for (k = 0; k < num; ++k) { res = do_rd_inc_wr_twice(dev_name, lba, block, excl, wait_ms, k, thr_ebusy_count, thr_eagain_count); @@ -480,18 +507,22 @@ work_thread(const char * dev_name, unsigned int lba, int id, int block, if (res) ++thr_odd_count; } - console_mutex.lock(); - if (k < num) - cerr << "thread id=" << id << " FAILed at iteration: " << k << '\n'; - else - cerr << "thread id=" << id << " normal exit" << '\n'; - console_mutex.unlock(); - - odd_count_mutex.lock(); - odd_count += thr_odd_count; - ebusy_count += thr_ebusy_count; - eagain_count += thr_eagain_count; - odd_count_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + if (k < num) + cerr << "thread id=" << id << " FAILed at iteration: " << k << + '\n'; + else + cerr << "thread id=" << id << " normal exit" << '\n'; + } + { + lock_guard<mutex> lg(odd_count_mutex); + + odd_count += thr_odd_count; + ebusy_count += thr_ebusy_count; + eagain_count += thr_eagain_count; + } } diff --git a/examples/sg_tst_excl2.cpp b/examples/sg_tst_excl2.cpp index cec1c9e..24d3677 100644 --- a/examples/sg_tst_excl2.cpp +++ b/examples/sg_tst_excl2.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Douglas Gilbert. + * Copyright (c) 2013-2014 Douglas Gilbert. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,7 +46,7 @@ #include "sg_lib.h" #include "sg_pt.h" -static const char * version_str = "1.06 20131110"; +static const char * version_str = "1.07 20140828"; static const char * util_name = "sg_tst_excl2"; /* This is a test program for checking O_EXCL on open() works. It uses @@ -134,6 +134,7 @@ usage(void) "on lba (using its first 4 bytes).\n"); } +/* Assumed a lock (mutex) held when pt_err() is called */ static int pt_err(int res) { @@ -148,6 +149,7 @@ pt_err(int res) return -1; } +/* Assumed a lock (mutex) held when pt_cat_no_good() is called */ static int pt_cat_no_good(int cat, struct sg_pt_base * ptp, const unsigned char * sbp) { @@ -229,9 +231,11 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block, if (sg_fd < 0) { snprintf(ebuff, EBUFF_SZ, "do_rd_inc_wr_twice: error opening file: %s", dev_name); - console_mutex.lock(); - perror(ebuff); - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + perror(ebuff); + } return -1; } @@ -244,18 +248,22 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block, set_scsi_pt_data_in(ptp, lb, READ16_REPLY_LEN); res = do_scsi_pt(ptp, sg_fd, 20 /* secs timeout */, 1); if (res) { - console_mutex.lock(); - fprintf(stderr, "READ_16 do_scsi_pt() submission error\n"); - res = pt_err(res); - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + fprintf(stderr, "READ_16 do_scsi_pt() submission error\n"); + res = pt_err(res); + } goto err; } cat = get_scsi_pt_result_category(ptp); if (SCSI_PT_RESULT_GOOD != cat) { - console_mutex.lock(); - fprintf(stderr, "READ_16 do_scsi_pt() category problem\n"); - console_mutex.unlock(); - res = pt_cat_no_good(cat, ptp, sense_buffer); + { + lock_guard<mutex> lg(console_mutex); + + fprintf(stderr, "READ_16 do_scsi_pt() category problem\n"); + res = pt_cat_no_good(cat, ptp, sense_buffer); + } goto err; } @@ -283,18 +291,22 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block, set_scsi_pt_data_out(ptp, lb, WRITE16_REPLY_LEN); res = do_scsi_pt(ptp, sg_fd, 20 /* secs timeout */, 1); if (res) { - console_mutex.lock(); - fprintf(stderr, "WRITE_16 do_scsi_pt() submission error\n"); - res = pt_err(res); - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + fprintf(stderr, "WRITE_16 do_scsi_pt() submission error\n"); + res = pt_err(res); + } goto err; } cat = get_scsi_pt_result_category(ptp); if (SCSI_PT_RESULT_GOOD != cat) { - console_mutex.lock(); - fprintf(stderr, "WRITE_16 do_scsi_pt() category problem\n"); - console_mutex.unlock(); - res = pt_cat_no_good(cat, ptp, sense_buffer); + { + lock_guard<mutex> lg(console_mutex); + + fprintf(stderr, "WRITE_16 do_scsi_pt() category problem\n"); + res = pt_cat_no_good(cat, ptp, sense_buffer); + } goto err; } } @@ -352,14 +364,22 @@ do_inquiry_prod_id(const char * dev_name, int block, int wait_ms, set_scsi_pt_data_in(ptp, inqBuff, INQ_REPLY_LEN); res = do_scsi_pt(ptp, sg_fd, 20 /* secs timeout */, 1); if (res) { - fprintf(stderr, "INQUIRY do_scsi_pt() submission error\n"); - res = pt_err(res); + { + lock_guard<mutex> lg(console_mutex); + + fprintf(stderr, "INQUIRY do_scsi_pt() submission error\n"); + res = pt_err(res); + } goto err; } cat = get_scsi_pt_result_category(ptp); if (SCSI_PT_RESULT_GOOD != cat) { - fprintf(stderr, "INQUIRY do_scsi_pt() category problem\n"); - res = pt_cat_no_good(cat, ptp, sense_buffer); + { + lock_guard<mutex> lg(console_mutex); + + fprintf(stderr, "INQUIRY do_scsi_pt() category problem\n"); + res = pt_cat_no_good(cat, ptp, sense_buffer); + } goto err; } @@ -388,10 +408,12 @@ work_thread(const char * dev_name, unsigned int lba, int id, int block, unsigned int thr_ebusy_count = 0; int k, res; - console_mutex.lock(); - cerr << "Enter work_thread id=" << id << " excl=" << excl << " block=" - << block << endl; - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + cerr << "Enter work_thread id=" << id << " excl=" << excl << " block=" + << block << endl; + } for (k = 0; k < num; ++k) { res = do_rd_inc_wr_twice(dev_name, lba, block, excl, wait_ms, thr_ebusy_count); @@ -400,17 +422,22 @@ work_thread(const char * dev_name, unsigned int lba, int id, int block, if (res) ++thr_odd_count; } - console_mutex.lock(); - if (k < num) - cerr << "thread id=" << id << " FAILed at iteration: " << k << '\n'; - else - cerr << "thread id=" << id << " normal exit" << '\n'; - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + if (k < num) + cerr << "thread id=" << id << " FAILed at iteration: " << k << + '\n'; + else + cerr << "thread id=" << id << " normal exit" << '\n'; + } + + { + lock_guard<mutex> lg(odd_count_mutex); - odd_count_mutex.lock(); - odd_count += thr_odd_count; - ebusy_count += thr_ebusy_count; - odd_count_mutex.unlock(); + odd_count += thr_odd_count; + ebusy_count += thr_ebusy_count; + } } diff --git a/examples/sg_tst_excl3.cpp b/examples/sg_tst_excl3.cpp index c4052a9..6b6b880 100644 --- a/examples/sg_tst_excl3.cpp +++ b/examples/sg_tst_excl3.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Douglas Gilbert. + * Copyright (c) 2013-2014 Douglas Gilbert. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,7 +46,7 @@ #include "sg_lib.h" #include "sg_pt.h" -static const char * version_str = "1.04 20131110"; +static const char * version_str = "1.05 20140828"; static const char * util_name = "sg_tst_excl3"; /* This is a test program for checking O_EXCL on open() works. It uses @@ -140,6 +140,7 @@ usage(void) "value is even.\n"); } +/* Assumed a lock (mutex) held when pt_err() is called */ static int pt_err(int res) { @@ -154,6 +155,7 @@ pt_err(int res) return -1; } +/* Assumed a lock (mutex) held when pt_cat_no_good() is called */ static int pt_cat_no_good(int cat, struct sg_pt_base * ptp, const unsigned char * sbp) { @@ -235,9 +237,11 @@ do_rd_inc_wr_twice(const char * dev_name, int read_only, unsigned int lba, if (sg_fd < 0) { snprintf(ebuff, EBUFF_SZ, "do_rd_inc_wr_twice: error opening file: %s", dev_name); - console_mutex.lock(); - perror(ebuff); - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + perror(ebuff); + } return -1; } @@ -250,18 +254,22 @@ do_rd_inc_wr_twice(const char * dev_name, int read_only, unsigned int lba, set_scsi_pt_data_in(ptp, lb, READ16_REPLY_LEN); res = do_scsi_pt(ptp, sg_fd, 20 /* secs timeout */, 1); if (res) { - console_mutex.lock(); - fprintf(stderr, "READ_16 do_scsi_pt() submission error\n"); - res = pt_err(res); - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + fprintf(stderr, "READ_16 do_scsi_pt() submission error\n"); + res = pt_err(res); + } goto err; } cat = get_scsi_pt_result_category(ptp); if (SCSI_PT_RESULT_GOOD != cat) { - console_mutex.lock(); - fprintf(stderr, "READ_16 do_scsi_pt() category problem\n"); - console_mutex.unlock(); - res = pt_cat_no_good(cat, ptp, sense_buffer); + { + lock_guard<mutex> lg(console_mutex); + + fprintf(stderr, "READ_16 do_scsi_pt() category problem\n"); + res = pt_cat_no_good(cat, ptp, sense_buffer); + } goto err; } @@ -292,18 +300,22 @@ do_rd_inc_wr_twice(const char * dev_name, int read_only, unsigned int lba, set_scsi_pt_data_out(ptp, lb, WRITE16_REPLY_LEN); res = do_scsi_pt(ptp, sg_fd, 20 /* secs timeout */, 1); if (res) { - console_mutex.lock(); - fprintf(stderr, "WRITE_16 do_scsi_pt() submission error\n"); - res = pt_err(res); - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + fprintf(stderr, "WRITE_16 do_scsi_pt() submission error\n"); + res = pt_err(res); + } goto err; } cat = get_scsi_pt_result_category(ptp); if (SCSI_PT_RESULT_GOOD != cat) { - console_mutex.lock(); - fprintf(stderr, "WRITE_16 do_scsi_pt() category problem\n"); - console_mutex.unlock(); - res = pt_cat_no_good(cat, ptp, sense_buffer); + { + lock_guard<mutex> lg(console_mutex); + + fprintf(stderr, "WRITE_16 do_scsi_pt() category problem\n"); + res = pt_cat_no_good(cat, ptp, sense_buffer); + } goto err; } } @@ -397,10 +409,12 @@ work_thread(const char * dev_name, unsigned int lba, int id, int block, int k, res; int reader = ((id > 0) || (all_readers)); - console_mutex.lock(); - cerr << "Enter work_thread id=" << id << " excl=" << excl << " block=" - << block << " reader=" << reader << endl; - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + cerr << "Enter work_thread id=" << id << " excl=" << excl << " block=" + << block << " reader=" << reader << endl; + } for (k = 0; k < num; ++k) { res = do_rd_inc_wr_twice(dev_name, reader, lba, block, excl, wait_ms, thr_ebusy_count); @@ -409,17 +423,22 @@ work_thread(const char * dev_name, unsigned int lba, int id, int block, if (res) ++thr_odd_count; } - console_mutex.lock(); - if (k < num) - cerr << "thread id=" << id << " FAILed at iteration: " << k << '\n'; - else - cerr << "thread id=" << id << " normal exit" << '\n'; - console_mutex.unlock(); + { + lock_guard<mutex> lg(console_mutex); + + if (k < num) + cerr << "thread id=" << id << " FAILed at iteration: " << k + << '\n'; + else + cerr << "thread id=" << id << " normal exit" << '\n'; + } - odd_count_mutex.lock(); - odd_count += thr_odd_count; - ebusy_count += thr_ebusy_count; - odd_count_mutex.unlock(); + { + lock_guard<mutex> lg(odd_count_mutex); + + odd_count += thr_odd_count; + ebusy_count += thr_ebusy_count; + } } diff --git a/examples/sgq_dd.c b/examples/sgq_dd.c index 7ed18a1..380493f 100644 --- a/examples/sgq_dd.c +++ b/examples/sgq_dd.c @@ -9,11 +9,11 @@ #include <errno.h> #include <limits.h> #include <signal.h> +#include <poll.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/sysmacros.h> -#include <sys/poll.h> #include <linux/major.h> #include <sys/time.h> typedef unsigned char u_char; /* horrible, for scsi.h */ @@ -47,7 +47,8 @@ typedef unsigned char u_char; /* horrible, for scsi.h */ */ -static char * version_str = "0.56 20140409"; /* was "0.55 20020509" */ +static char * version_str = "0.57 20140819"; +/* resurrected from "0.55 20020509" */ #define DEF_BLOCK_SIZE 512 #define DEF_BLOCKS_PER_TRANSFER 128 @@ -144,15 +145,11 @@ static int dd_count = -1; static const char * proc_allow_dio = "/proc/scsi/sg/allow_dio"; -int sg_fin_in_operation(Rq_coll * clp, Rq_elem * rep); -int sg_fin_out_operation(Rq_coll * clp, Rq_elem * rep); -int normal_in_operation(Rq_coll * clp, Rq_elem * rep, int blocks); -int normal_out_operation(Rq_coll * clp, Rq_elem * rep, int blocks); -int sg_start_io(Rq_elem * rep); -int sg_finish_io(int wr, Rq_elem * rep); +static int sg_finish_io(int wr, Rq_elem * rep); -static void install_handler (int sig_num, void (*sig_handler) (int sig)) +static void +install_handler (int sig_num, void (*sig_handler) (int sig)) { struct sigaction sigact; sigaction (sig_num, NULL, &sigact); @@ -165,7 +162,8 @@ static void install_handler (int sig_num, void (*sig_handler) (int sig)) } } -void print_stats() +static void +print_stats() { int infull, outfull; @@ -177,7 +175,8 @@ void print_stats() fprintf(stderr, "%d+%d records out\n", outfull, rcoll.out_partial); } -static void interrupt_handler(int sig) +static void +interrupt_handler(int sig) { struct sigaction sigact; @@ -190,14 +189,16 @@ static void interrupt_handler(int sig) kill (getpid (), sig); } -static void siginfo_handler(int sig) +static void +siginfo_handler(int sig) { fprintf(stderr, "Progress report, continuing ...\n"); print_stats (); if (sig) { } /* suppress unused warning */ } -int dd_filetype(const char * filename) +static int +dd_filetype(const char * filename) { struct stat st; @@ -212,7 +213,8 @@ int dd_filetype(const char * filename) return FT_OTHER; } -void usage() +static void +usage() { fprintf(stderr, "Usage: " "sgq_dd [if=<infile>] [skip=<n>] [of=<ofile>] [seek=<n>] " @@ -232,7 +234,8 @@ void usage() } /* Returns -1 for error, 0 for nothing found, QS_IN_POLL or QS_OUT_POLL */ -int do_poll(Rq_coll * clp, int timeout, int * req_indexp) +static int +do_poll(Rq_coll * clp, int timeout, int * req_indexp) { int k, res; @@ -277,7 +280,8 @@ int do_poll(Rq_coll * clp, int timeout, int * req_indexp) /* Return of 0 -> success, -1 -> failure, 2 -> try again */ -int read_capacity(int sg_fd, int * num_sect, int * sect_sz) +static int +read_capacity(int sg_fd, int * num_sect, int * sect_sz) { int res; unsigned char rcCmdBlk [10] = {0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -319,7 +323,8 @@ int read_capacity(int sg_fd, int * num_sect, int * sect_sz) } /* 0 -> ok, 1 -> short read, -1 -> error */ -int normal_in_operation(Rq_coll * clp, Rq_elem * rep, int blocks) +static int +normal_in_operation(Rq_coll * clp, Rq_elem * rep, int blocks) { int res; int stop_after_write = 0; @@ -357,7 +362,8 @@ int normal_in_operation(Rq_coll * clp, Rq_elem * rep, int blocks) } /* 0 -> ok, -1 -> error */ -int normal_out_operation(Rq_coll * clp, Rq_elem * rep, int blocks) +static int +normal_out_operation(Rq_coll * clp, Rq_elem * rep, int blocks) { int res; @@ -387,7 +393,8 @@ int normal_out_operation(Rq_coll * clp, Rq_elem * rep, int blocks) } /* Returns 1 for retryable, 0 for ok, -ve for error */ -int sg_fin_in_operation(Rq_coll * clp, Rq_elem * rep) +static int +sg_fin_in_operation(Rq_coll * clp, Rq_elem * rep) { int res; @@ -416,7 +423,8 @@ int sg_fin_in_operation(Rq_coll * clp, Rq_elem * rep) } /* Returns 1 for retryable, 0 for ok, -ve for error */ -int sg_fin_out_operation(Rq_coll * clp, Rq_elem * rep) +static int +sg_fin_out_operation(Rq_coll * clp, Rq_elem * rep) { int res; @@ -443,7 +451,8 @@ int sg_fin_out_operation(Rq_coll * clp, Rq_elem * rep) return res; } -int sg_start_io(Rq_elem * rep) +static int +sg_start_io(Rq_elem * rep) { sg_io_hdr_t * hp = &rep->io_hdr; int res; @@ -491,7 +500,8 @@ int sg_start_io(Rq_elem * rep) } /* -1 -> unrecoverable error, 0 -> successful, 1 -> try again */ -int sg_finish_io(int wr, Rq_elem * rep) +static int +sg_finish_io(int wr, Rq_elem * rep) { int res; sg_io_hdr_t io_hdr; @@ -555,7 +565,8 @@ int sg_finish_io(int wr, Rq_elem * rep) } /* Returns scsi_type or -1 for error */ -int sg_prepare(int fd, int sz) +static int +sg_prepare(int fd, int sz) { int res, t; struct sg_scsi_id info; @@ -584,7 +595,8 @@ int sg_prepare(int fd, int sz) } /* Return 0 for ok, anything else for errors */ -int prepare_rq_elems(Rq_coll * clp, const char * inf, const char * outf) +static int +prepare_rq_elems(Rq_coll * clp, const char * inf, const char * outf) { int k; Rq_elem * rep; @@ -658,7 +670,8 @@ int prepare_rq_elems(Rq_coll * clp, const char * inf, const char * outf) /* Returns a "QS" code and req index, or QS_IDLE and position of first idle (-1 if no idle position). Returns -1 on poll error. */ -int decider(Rq_coll * clp, int first_xfer, int * req_indexp) +static int +decider(Rq_coll * clp, int first_xfer, int * req_indexp) { int k, res; Rq_elem * rep; @@ -705,7 +718,8 @@ int decider(Rq_coll * clp, int first_xfer, int * req_indexp) } -int main(int argc, char * argv[]) +int +main(int argc, char * argv[]) { int skip = 0; int seek = 0; diff --git a/include/Makefile.in b/include/Makefile.in index bbe399c..54a4c3b 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -271,6 +271,7 @@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ +os_cflags = @os_cflags@ os_libs = @os_libs@ pdfdir = @pdfdir@ prefix = @prefix@ diff --git a/include/sg_unaligned.h b/include/sg_unaligned.h new file mode 100644 index 0000000..3060ba4 --- /dev/null +++ b/include/sg_unaligned.h @@ -0,0 +1,251 @@ +#ifndef SG_UNALIGNED_H +#define SG_UNALIGNED_H + +/* + * Copyright (c) 2014 Douglas Gilbert. + * All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the BSD_LICENSE file. + */ + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Borrowed from the Linux kernel, via mhvtl */ + +/* In the first section below, functions that copy unsigned integers in + * a computer's native format, to and from an unaligned big endian sequence + * of bytes. Big endian byte format "on the wire" is the default used by + * SCSI standards (www.t10.org). */ + +static inline uint16_t __get_unaligned_be16(const uint8_t *p) +{ + return p[0] << 8 | p[1]; +} + +static inline uint32_t __get_unaligned_be32(const uint8_t *p) +{ + return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; +} + +static inline uint64_t __get_unaligned_be64(const uint8_t *p) +{ + return (uint64_t)__get_unaligned_be32(p) << 32 | + __get_unaligned_be32(p + 4); +} + +static inline void __put_unaligned_be16(uint16_t val, uint8_t *p) +{ + *p++ = val >> 8; + *p++ = val; +} + +static inline void __put_unaligned_be32(uint32_t val, uint8_t *p) +{ + __put_unaligned_be16(val >> 16, p); + __put_unaligned_be16(val, p + 2); +} + +static inline void __put_unaligned_be64(uint64_t val, uint8_t *p) +{ + __put_unaligned_be32(val >> 32, p); + __put_unaligned_be32(val, p + 4); +} + +static inline uint16_t sg_get_unaligned_be16(const void *p) +{ + return __get_unaligned_be16((const uint8_t *)p); +} + +static inline uint32_t sg_get_unaligned_be24(const uint8_t *p) +{ + return p[0] << 16 | p[1] << 8 | p[2]; +} + +static inline uint32_t sg_get_unaligned_be32(const void *p) +{ + return __get_unaligned_be32((const uint8_t *)p); +} + +static inline uint64_t sg_get_unaligned_be64(const void *p) +{ + return __get_unaligned_be64((const uint8_t *)p); +} + +static inline void sg_put_unaligned_be16(uint16_t val, void *p) +{ + __put_unaligned_be16(val, (uint8_t *)p); +} + +static inline void sg_put_unaligned_be24(uint32_t val, void *p) +{ + ((uint8_t *)p)[0] = (val >> 16) & 0xff; + ((uint8_t *)p)[1] = (val >> 8) & 0xff; + ((uint8_t *)p)[2] = val & 0xff; +} + +static inline void sg_put_unaligned_be32(uint32_t val, void *p) +{ + __put_unaligned_be32(val, (uint8_t *)p); +} + +static inline void sg_put_unaligned_be64(uint64_t val, void *p) +{ + __put_unaligned_be64(val, (uint8_t *)p); +} + +/* Since cdb and parameter blocks are often memset to zero before these + * unaligned function partially fill them, then check for a val of zero + * and ignore if it is with these variants. */ +static inline void sg_nz_put_unaligned_be16(uint16_t val, void *p) +{ + if (val) + __put_unaligned_be16(val, (uint8_t *)p); +} + +static inline void sg_nz_put_unaligned_be24(uint32_t val, void *p) +{ + if (val) { + ((uint8_t *)p)[0] = (val >> 16) & 0xff; + ((uint8_t *)p)[1] = (val >> 8) & 0xff; + ((uint8_t *)p)[2] = val & 0xff; + } +} + +static inline void sg_nz_put_unaligned_be32(uint32_t val, void *p) +{ + if (val) + __put_unaligned_be32(val, (uint8_t *)p); +} + +static inline void sg_nz_put_unaligned_be64(uint64_t val, void *p) +{ + if (val) + __put_unaligned_be64(val, (uint8_t *)p); +} + + +/* Below are the little endian equivalents of the big endian functions + * above. Little endian is used by ATA, networking and PCI. + * This section could take advantage of the + * 'uint32_t htonl(uint32_t hostlong)' [and the complementary ntohl()] + * family of functions but that would introduce a dependency on the + * <arpa/inet.h> header. Also they don't address moving to and from + * an unaligned sequence of bytes. The latter would still need to be + * done. + */ + +static inline uint16_t __get_unaligned_le16(const uint8_t *p) +{ + return p[1] << 8 | p[0]; +} + +static inline uint32_t __get_unaligned_le32(const uint8_t *p) +{ + return p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0]; +} + +static inline uint64_t __get_unaligned_le64(const uint8_t *p) +{ + return (uint64_t)__get_unaligned_le32(p + 4) << 32 | + __get_unaligned_le32(p); +} + +static inline void __put_unaligned_le16(uint16_t val, uint8_t *p) +{ + *p++ = val; + *p++ = val >> 8; +} + +static inline void __put_unaligned_le32(uint32_t val, uint8_t *p) +{ + __put_unaligned_le16(val >> 16, p + 2); + __put_unaligned_le16(val, p); +} + +static inline void __put_unaligned_le64(uint64_t val, uint8_t *p) +{ + __put_unaligned_le32(val >> 32, p + 4); + __put_unaligned_le32(val, p); +} + +static inline uint16_t sg_get_unaligned_le16(const void *p) +{ + return __get_unaligned_le16((const uint8_t *)p); +} + +static inline uint32_t sg_get_unaligned_le24(const uint8_t *p) +{ + return p[2] << 16 | p[1] << 8 | p[0]; +} + +static inline uint32_t sg_get_unaligned_le32(const void *p) +{ + return __get_unaligned_le32((const uint8_t *)p); +} + +static inline uint64_t sg_get_unaligned_le64(const void *p) +{ + return __get_unaligned_le64((const uint8_t *)p); +} + +static inline void sg_put_unaligned_le16(uint16_t val, void *p) +{ + __put_unaligned_le16(val, (uint8_t *)p); +} + +static inline void sg_put_unaligned_le24(uint32_t val, void *p) +{ + ((uint8_t *)p)[2] = (val >> 16) & 0xff; + ((uint8_t *)p)[1] = (val >> 8) & 0xff; + ((uint8_t *)p)[0] = val & 0xff; +} + +static inline void sg_put_unaligned_le32(uint32_t val, void *p) +{ + __put_unaligned_le32(val, (uint8_t *)p); +} + +static inline void sg_put_unaligned_le64(uint64_t val, void *p) +{ + __put_unaligned_le64(val, (uint8_t *)p); +} + +/* Since cdb and parameter blocks are often memset to zero before these + * unaligned function partially fill them, then check for a val of zero + * and ignore if it is with these variants. */ +static inline void sg_nz_put_unaligned_le16(uint16_t val, void *p) +{ + if (val) + __put_unaligned_le16(val, (uint8_t *)p); +} + +static inline void sg_nz_put_unaligned_le24(uint32_t val, void *p) +{ + if (val) { + ((uint8_t *)p)[2] = (val >> 16) & 0xff; + ((uint8_t *)p)[1] = (val >> 8) & 0xff; + ((uint8_t *)p)[0] = val & 0xff; + } +} + +static inline void sg_nz_put_unaligned_le32(uint32_t val, void *p) +{ + if (val) + __put_unaligned_le32(val, (uint8_t *)p); +} + +static inline void sg_nz_put_unaligned_le64(uint64_t val, void *p) +{ + if (val) + __put_unaligned_le64(val, (uint8_t *)p); +} + +#ifdef __cplusplus +} +#endif + +#endif /* SG_UNALIGNED_H */ diff --git a/lib/Makefile.am b/lib/Makefile.am index dde5883..1e77664 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -40,9 +40,10 @@ endif ## CC = clang++ # -std=<s> can be c99, c11, gnu11, etc. Default is gnu89 (gnu90 is the same) -AM_CFLAGS = -I ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W -# AM_CFLAGS = -I ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W -pedantic -std=c11 -# AM_CFLAGS = -I ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W -pedantic -std=c++11 +# -Wall is no longer all warnings. Add -W (since renamed to -Wextra) for more +AM_CFLAGS = -iquote ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W +# AM_CFLAGS = -iquote ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W -pedantic -std=c11 +# AM_CFLAGS = -iquote ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W -pedantic -std=c++11 lib_LTLIBRARIES = libsgutils2.la diff --git a/lib/Makefile.in b/lib/Makefile.in index d9d9bea..1f32c60 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -316,6 +316,7 @@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ +os_cflags = @os_cflags@ os_libs = @os_libs@ pdfdir = @pdfdir@ prefix = @prefix@ @@ -337,9 +338,10 @@ libsgutils2_la_SOURCES = sg_lib.c sg_lib_data.c sg_cmds_basic.c \ # For C++/clang testing # -std=<s> can be c99, c11, gnu11, etc. Default is gnu89 (gnu90 is the same) -AM_CFLAGS = -I ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W -# AM_CFLAGS = -I ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W -pedantic -std=c11 -# AM_CFLAGS = -I ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W -pedantic -std=c++11 +# -Wall is no longer all warnings. Add -W (since renamed to -Wextra) for more +AM_CFLAGS = -iquote ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W +# AM_CFLAGS = -iquote ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W -pedantic -std=c11 +# AM_CFLAGS = -iquote ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W -pedantic -std=c++11 lib_LTLIBRARIES = libsgutils2.la libsgutils2_la_LDFLAGS = -version-info 2:0:0 libsgutils2_la_LIBADD = @GETOPT_O_FILES@ @os_libs@ diff --git a/lib/sg_cmds_basic.c b/lib/sg_cmds_basic.c index 2764927..b42d896 100644 --- a/lib/sg_cmds_basic.c +++ b/lib/sg_cmds_basic.c @@ -27,7 +27,7 @@ #endif -static const char * version_str = "1.68 20140604"; +static const char * version_str = "1.69 20141006"; #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */ @@ -46,7 +46,7 @@ static const char * version_str = "1.68 20140604"; #define TUR_CMD 0x0 #define TUR_CMDLEN 6 -#define INQUIRY_RESP_INITIAL_LEN 36 +#define SAFE_STD_INQ_RESP_LEN 36 /* other lengths lock up some devices */ const char * @@ -235,7 +235,7 @@ int sg_ll_inquiry(int sg_fd, int cmddt, int evpd, int pg_op, void * resp, int mx_resp_len, int noisy, int verbose) { - int res, ret, k, sense_cat; + int res, ret, k, sense_cat, resid; unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; unsigned char * up; @@ -274,6 +274,7 @@ sg_ll_inquiry(int sg_fd, int cmddt, int evpd, int pg_op, void * resp, res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, "inquiry", res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); + resid = get_scsi_pt_resid(ptvp); destruct_scsi_pt_obj(ptvp); if (-1 == ret) ; @@ -295,6 +296,15 @@ sg_ll_inquiry(int sg_fd, int cmddt, int evpd, int pg_op, void * resp, } else ret = 0; + if (resid > 0) { + if (resid > mx_resp_len) { + fprintf(sg_warnings_strm, "inquiry: resid (%d) should never " + "exceed requested len=%d\n", resid, mx_resp_len); + return ret ? ret : SG_LIB_CAT_MALFORMED; + } + /* zero unfilled section of response buffer */ + memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid); + } return ret; } @@ -308,7 +318,7 @@ sg_simple_inquiry(int sg_fd, struct sg_simple_inquiry_resp * inq_data, int res, ret, k, sense_cat; unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; - unsigned char inq_resp[INQUIRY_RESP_INITIAL_LEN]; + unsigned char inq_resp[SAFE_STD_INQ_RESP_LEN]; struct sg_pt_base * ptvp; if (inq_data) { diff --git a/lib/sg_cmds_basic2.c b/lib/sg_cmds_basic2.c index a9378de..022b7d7 100644 --- a/lib/sg_cmds_basic2.c +++ b/lib/sg_cmds_basic2.c @@ -262,7 +262,7 @@ int sg_ll_mode_sense6(int sg_fd, int dbd, int pc, int pg_code, int sub_pg_code, void * resp, int mx_resp_len, int noisy, int verbose) { - int res, ret, k, sense_cat; + int res, ret, k, sense_cat, resid; unsigned char modesCmdBlk[MODE_SENSE6_CMDLEN] = {MODE_SENSE6_CMD, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; @@ -295,6 +295,8 @@ sg_ll_mode_sense6(int sg_fd, int dbd, int pc, int pg_code, int sub_pg_code, res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, "mode sense (6)", res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); + resid = get_scsi_pt_resid(ptvp); + destruct_scsi_pt_obj(ptvp); if (-1 == ret) ; else if (-2 == ret) { @@ -315,7 +317,16 @@ sg_ll_mode_sense6(int sg_fd, int dbd, int pc, int pg_code, int sub_pg_code, } ret = 0; } - destruct_scsi_pt_obj(ptvp); + + if (resid > 0) { + if (resid > mx_resp_len) { + fprintf(sg_warnings_strm, "mode sense(6): resid (%d) should " + "never exceed requested len=%d\n", resid, mx_resp_len); + return ret ? ret : SG_LIB_CAT_MALFORMED; + } + /* zero unfilled section of response buffer */ + memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid); + } return ret; } @@ -326,7 +337,7 @@ sg_ll_mode_sense10(int sg_fd, int llbaa, int dbd, int pc, int pg_code, int sub_pg_code, void * resp, int mx_resp_len, int noisy, int verbose) { - int res, ret, k, sense_cat; + int res, ret, k, sense_cat, resid; unsigned char modesCmdBlk[MODE_SENSE10_CMDLEN] = {MODE_SENSE10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; @@ -360,6 +371,8 @@ sg_ll_mode_sense10(int sg_fd, int llbaa, int dbd, int pc, int pg_code, res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, "mode sense (10)", res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); + resid = get_scsi_pt_resid(ptvp); + destruct_scsi_pt_obj(ptvp); if (-1 == ret) ; else if (-2 == ret) { @@ -380,7 +393,16 @@ sg_ll_mode_sense10(int sg_fd, int llbaa, int dbd, int pc, int pg_code, } ret = 0; } - destruct_scsi_pt_obj(ptvp); + + if (resid > 0) { + if (resid > mx_resp_len) { + fprintf(sg_warnings_strm, "mode sense(10): resid (%d) should " + "never exceed requested len=%d\n", resid, mx_resp_len); + return ret ? ret : SG_LIB_CAT_MALFORMED; + } + /* zero unfilled section of response buffer */ + memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid); + } return ret; } @@ -678,7 +700,7 @@ sg_ll_log_sense(int sg_fd, int ppc, int sp, int pc, int pg_code, int subpg_code, int paramp, unsigned char * resp, int mx_resp_len, int noisy, int verbose) { - int res, ret, k, sense_cat; + int res, ret, k, sense_cat, resid; unsigned char logsCmdBlk[LOG_SENSE_CMDLEN] = {LOG_SENSE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; @@ -715,6 +737,8 @@ sg_ll_log_sense(int sg_fd, int ppc, int sp, int pc, int pg_code, res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, "log sense", res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); + resid = get_scsi_pt_resid(ptvp); + destruct_scsi_pt_obj(ptvp); if (-1 == ret) ; else if (-2 == ret) { @@ -735,7 +759,16 @@ sg_ll_log_sense(int sg_fd, int ppc, int sp, int pc, int pg_code, } ret = 0; } - destruct_scsi_pt_obj(ptvp); + + if (resid > 0) { + if (resid > mx_resp_len) { + fprintf(sg_warnings_strm, "log sense: resid (%d) should " + "never exceed requested len=%d\n", resid, mx_resp_len); + return ret ? ret : SG_LIB_CAT_MALFORMED; + } + /* zero unfilled section of response buffer */ + memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid); + } return ret; } diff --git a/lib/sg_cmds_extra.c b/lib/sg_cmds_extra.c index 5125783..6afea69 100644 --- a/lib/sg_cmds_extra.c +++ b/lib/sg_cmds_extra.c @@ -1883,7 +1883,7 @@ sg_ll_unmap_v2(int sg_fd, int anchor, int group_num, int timeout_secs, if (anchor) uCmdBlk[1] |= 0x1; tmout = (timeout_secs > 0) ? timeout_secs : DEF_PT_TIMEOUT; - uCmdBlk[7] = group_num & 0x1f; + uCmdBlk[6] = group_num & 0x1f; uCmdBlk[7] = (param_len >> 8) & 0xff; uCmdBlk[8] = param_len & 0xff; if (NULL == sg_warnings_strm) diff --git a/lib/sg_lib.c b/lib/sg_lib.c index a1020ec..3894de2 100644 --- a/lib/sg_lib.c +++ b/lib/sg_lib.c @@ -46,9 +46,20 @@ #define ASCQ_ATA_PT_INFO_AVAILABLE 0x1d /* corresponding ASC is 0 */ +/* The following two opcodes were renamed to 'Third party copy out/in' in + * spc4r34 */ +#define SG_THIRD_PARTY_COPY_OUT SG_EXTENDED_COPY +#define SG_THIRD_PARTY_COPY_IN SG_RECEIVE_COPY + FILE * sg_warnings_strm = NULL; /* would like to default to stderr */ +#ifdef __GNUC__ +static int my_snprintf(char * cp, int cp_max_len, const char * fmt, ...) + __attribute__ ((format (printf, 3, 4))); +#else +static int my_snprintf(char * cp, int cp_max_len, const char * fmt, ...); +#endif /* Want safe, 'n += snprintf(b + n, blen - n, ...)' style sequence of * functions. Returns number number of chars placed in cp excluding the @@ -129,7 +140,8 @@ sg_print_command(const unsigned char * command) void sg_get_scsi_status_str(int scsi_status, int buff_len, char * buff) { - const char * ccp; + const char * ccp = NULL; + int unknown = 0; if ((NULL == buff) || (buff_len < 1)) return; @@ -144,15 +156,20 @@ sg_get_scsi_status_str(int scsi_status, int buff_len, char * buff) case 0x4: ccp = "Condition Met"; break; case 0x8: ccp = "Busy"; break; case 0x10: ccp = "Intermediate (obsolete)"; break; - case 0x14: ccp = "Intermediate-Condition Met (obs)"; break; + case 0x14: ccp = "Intermediate-Condition Met (obsolete)"; break; case 0x18: ccp = "Reservation Conflict"; break; case 0x22: ccp = "Command Terminated (obsolete)"; break; case 0x28: ccp = "Task set Full"; break; case 0x30: ccp = "ACA Active"; break; case 0x40: ccp = "Task Aborted"; break; - default: ccp = "Unknown status"; break; + default: + unknown = 1; + break; } - my_snprintf(buff, buff_len, "%s", ccp); + if (unknown) + my_snprintf(buff, buff_len, "Unknown status [0x%x]", scsi_status); + else + my_snprintf(buff, buff_len, "%s", ccp); } void @@ -167,6 +184,8 @@ sg_print_scsi_status(int scsi_status) fprintf(sg_warnings_strm, "%s ", buff); } +/* Get sense key from sense buffer. If successful returns a sense key value + * between 0 and 15. If sense buffer cannot be decode, returns -1 . */ int sg_get_sense_key(const unsigned char * sensep, int sense_len) { @@ -184,6 +203,7 @@ sg_get_sense_key(const unsigned char * sensep, int sense_len) } } +/* Yield string associated with sense_key value. Returns 'buff'. */ char * sg_get_sense_key_str(int sense_key, int buff_len, char * buff) { @@ -198,6 +218,7 @@ sg_get_sense_key_str(int sense_key, int buff_len, char * buff) return buff; } +/* Yield string associated with ASC/ASCQ values. Returns 'buff'. */ char * sg_get_asc_ascq_str(int asc, int ascq, int buff_len, char * buff) { @@ -247,6 +268,9 @@ sg_get_asc_ascq_str(int asc, int ascq, int buff_len, char * buff) return buff; } +/* Attempt to find the first SCSI sense data descriptor that matches the + * given 'desc_type'. If found return pointer to start of sense data + * descriptor; otherwise (including fixed format sense data) returns NULL. */ const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep, int sense_len, int desc_type) @@ -273,6 +297,9 @@ sg_scsi_sense_desc_find(const unsigned char * sensep, int sense_len, return NULL; } +/* Returns 1 if valid bit set, 0 if valid bit clear. Irrespective the + * information field is written out via 'info_outp' (except when it is + * NULL). Handles both fixed and descriptor sense formats. */ int sg_get_sense_info_fld(const unsigned char * sensep, int sb_len, uint64_t * info_outp) @@ -312,6 +339,10 @@ sg_get_sense_info_fld(const unsigned char * sensep, int sb_len, } } +/* Returns 1 if any of the 3 bits (i.e. FILEMARK, EOM or ILI) are set. + * In descriptor format if the stream commands descriptor not found + * then returns 0. Writes 1 or 0 corresponding to these bits to the + * last three arguments if they are non-NULL. */ int sg_get_sense_filemark_eom_ili(const unsigned char * sensep, int sb_len, int * filemark_p, int * eom_p, int * ili_p) @@ -809,8 +840,7 @@ sg_get_sense_sat_pt_fixed_str(const unsigned char * sp, int slen, int blen, { int n = 0; - if (slen) { ; } /* unused, suppress warning */ - if (blen < 1) + if ((blen < 1) || (slen < 12)) return; if (SPC_SK_RECOVERED_ERROR != (0xf & sp[2])) n += my_snprintf(b + n, blen - n, " >> expected Sense key: " @@ -1175,12 +1205,39 @@ sg_get_command_name(const unsigned char * cmdp, int peri_type, int buff_len, sg_get_opcode_sa_name(cmdp[0], service_action, peri_type, buff_len, buff); } +struct op_code2sa_t { + int op_code; + struct sg_lib_value_name_t * arr; + const char * prefix; +}; + +static struct op_code2sa_t op_code2sa_arr[] = { + {SG_VARIABLE_LENGTH_CMD, sg_lib_variable_length_arr, NULL}, + {SG_MAINTENANCE_IN, sg_lib_maint_in_arr, NULL}, + {SG_MAINTENANCE_OUT, sg_lib_maint_out_arr, NULL}, + {SG_SERVICE_ACTION_IN_12, sg_lib_serv_in12_arr, NULL}, + {SG_SERVICE_ACTION_OUT_12, sg_lib_serv_out12_arr, NULL}, + {SG_SERVICE_ACTION_IN_16, sg_lib_serv_in16_arr, NULL}, + {SG_SERVICE_ACTION_OUT_16, sg_lib_serv_out16_arr, NULL}, + {SG_SERVICE_ACTION_BIDI, sg_lib_serv_bidi_arr, NULL}, + {SG_PERSISTENT_RESERVE_IN, sg_lib_pr_in_arr, "Persistent reserve in"}, + {SG_PERSISTENT_RESERVE_OUT, sg_lib_pr_out_arr, "Persistent reserve out"}, + {SG_THIRD_PARTY_COPY_OUT, sg_lib_xcopy_sa_arr, NULL}, + {SG_THIRD_PARTY_COPY_IN, sg_lib_rec_copy_sa_arr, NULL}, + {SG_READ_BUFFER, sg_lib_read_buff_arr, "Read buffer"}, + {SG_WRITE_BUFFER, sg_lib_write_buff_arr, "Write buffer"}, + {SG_SANITIZE, sg_lib_sanitize_sa_arr, "Sanitize"}, + {0xffff, NULL, NULL}, +}; + void sg_get_opcode_sa_name(unsigned char cmd_byte0, int service_action, int peri_type, int buff_len, char * buff) { const struct sg_lib_value_name_t * vnp; + const struct op_code2sa_t * osp; + char b[80]; if ((NULL == buff) || (buff_len < 1)) return; @@ -1188,133 +1245,26 @@ sg_get_opcode_sa_name(unsigned char cmd_byte0, int service_action, buff[0] = '\0'; return; } - switch ((int)cmd_byte0) { - case SG_VARIABLE_LENGTH_CMD: - vnp = get_value_name(sg_lib_variable_length_arr, service_action, - peri_type); - if (vnp) - my_snprintf(buff, buff_len, "%s", vnp->name); - else - my_snprintf(buff, buff_len, "Variable length service action=0x%x", - service_action); - break; - case SG_MAINTENANCE_IN: - vnp = get_value_name(sg_lib_maint_in_arr, service_action, peri_type); - if (vnp) - my_snprintf(buff, buff_len, "%s", vnp->name); - else - my_snprintf(buff, buff_len, "Maintenance in service action=0x%x", - service_action); - break; - case SG_MAINTENANCE_OUT: - vnp = get_value_name(sg_lib_maint_out_arr, service_action, peri_type); - if (vnp) - my_snprintf(buff, buff_len, "%s", vnp->name); - else - my_snprintf(buff, buff_len, "Maintenance out service action=0x%x", - service_action); - break; - case SG_SERVICE_ACTION_IN_12: - vnp = get_value_name(sg_lib_serv_in12_arr, service_action, peri_type); - if (vnp) - my_snprintf(buff, buff_len, "%s", vnp->name); - else - my_snprintf(buff, buff_len, "Service action in(12)=0x%x", - service_action); - break; - case SG_SERVICE_ACTION_OUT_12: - vnp = get_value_name(sg_lib_serv_out12_arr, service_action, peri_type); - if (vnp) - my_snprintf(buff, buff_len, "%s", vnp->name); - else - my_snprintf(buff, buff_len, "Service action out(12)=0x%x", - service_action); - break; - case SG_SERVICE_ACTION_IN_16: - vnp = get_value_name(sg_lib_serv_in16_arr, service_action, peri_type); - if (vnp) - my_snprintf(buff, buff_len, "%s", vnp->name); - else - my_snprintf(buff, buff_len, "Service action in(16)=0x%x", - service_action); - break; - case SG_SERVICE_ACTION_OUT_16: - vnp = get_value_name(sg_lib_serv_out16_arr, service_action, peri_type); - if (vnp) - my_snprintf(buff, buff_len, "%s", vnp->name); - else - my_snprintf(buff, buff_len, "Service action out(16)=0x%x", - service_action); - break; - case SG_PERSISTENT_RESERVE_IN: - vnp = get_value_name(sg_lib_pr_in_arr, service_action, peri_type); - if (vnp) - my_snprintf(buff, buff_len, "%s", vnp->name); - else - my_snprintf(buff, buff_len, "Persistent reserve in, service " - "action=0x%x", service_action); - break; - case SG_PERSISTENT_RESERVE_OUT: - vnp = get_value_name(sg_lib_pr_out_arr, service_action, peri_type); - if (vnp) - my_snprintf(buff, buff_len, "%s", vnp->name); - else - my_snprintf(buff, buff_len, "Persistent reserve out, service " - "action=0x%x", service_action); - break; - case SG_EXTENDED_COPY: - /* 'Extended copy' was renamed 'Third party copy out' in spc4r34 */ - vnp = get_value_name(sg_lib_xcopy_sa_arr, service_action, peri_type); - if (vnp) - my_snprintf(buff, buff_len, "%s", vnp->name); - else - my_snprintf(buff, buff_len, "Third party copy out, service " - "action=0x%x", service_action); - break; - case SG_RECEIVE_COPY: - /* 'Receive copy results' was renamed 'Third party copy in' in - * spc4r34 */ - vnp = get_value_name(sg_lib_rec_copy_sa_arr, service_action, - peri_type); - if (vnp) - my_snprintf(buff, buff_len, "%s", vnp->name); - else - my_snprintf(buff, buff_len, "Third party copy in, service " - "action=0x%x", service_action); - break; - case SG_READ_BUFFER: - /* spc4r34 requested treating mode as service action */ - vnp = get_value_name(sg_lib_read_buff_arr, service_action, - peri_type); - if (vnp) - my_snprintf(buff, buff_len, "Read buffer, %s", vnp->name); - else - my_snprintf(buff, buff_len, "Read buffer, mode=0x%x", - service_action); - break; - case SG_WRITE_BUFFER: - /* spc4r34 requested treating mode as service action */ - vnp = get_value_name(sg_lib_write_buff_arr, service_action, - peri_type); - if (vnp) - my_snprintf(buff, buff_len, "Write buffer, %s", vnp->name); - else - my_snprintf(buff, buff_len, "Write buffer, mode=0x%x", - service_action); - break; - case SG_SANITIZE: - vnp = get_value_name(sg_lib_sanitize_sa_arr, service_action, - peri_type); - if (vnp) - my_snprintf(buff, buff_len, "%s", vnp->name); - else - my_snprintf(buff, buff_len, "Sanitize, service action=0x%x", - service_action); - break; - default: - sg_get_opcode_name(cmd_byte0, peri_type, buff_len, buff); - break; + + for (osp = op_code2sa_arr; osp->arr; ++osp) { + if ((int)cmd_byte0 == osp->op_code) { + vnp = get_value_name(osp->arr, service_action, peri_type); + if (vnp) { + if (osp->prefix) + my_snprintf(buff, buff_len, "%s, %s", osp->prefix, + vnp->name); + else + my_snprintf(buff, buff_len, "%s", vnp->name); + } else { + sg_get_opcode_name(cmd_byte0, peri_type, sizeof(b), b); + my_snprintf(buff, buff_len, "%s service action=0x%x", + b, service_action); + } + return; + } } + + sg_get_opcode_name(cmd_byte0, peri_type, buff_len, buff); } void @@ -1555,6 +1505,18 @@ safe_strerror(int errnum) return errstr; } +static void +trimTrailingSpaces(char * b) +{ + int k; + + for (k = ((int)strlen(b) - 1); k >= 0; --k) { + if (' ' != b[k]) + break; + } + if ('\0' != b[k + 1]) + 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).] @@ -1582,9 +1544,9 @@ dStrHexFp(const char* str, int len, int no_ascii, FILE * fp) if (0 == no_ascii) /* address at left and ASCII at right */ formatstr = "%.76s\n"; else if (no_ascii > 0) - formatstr = "%.58s\n"; + formatstr = "%s\n"; /* was: "%.58s\n" */ else /* negative: no address at left and no ASCII at right */ - formatstr = "%.48s\n"; + formatstr = "%s\n"; /* was: "%.48s\n"; */ memset(buff, ' ', 80); buff[80] = '\0'; if (no_ascii < 0) { @@ -1598,6 +1560,7 @@ dStrHexFp(const char* str, int len, int no_ascii, FILE * fp) (int)(unsigned char)c); buff[bpos + 2] = ' '; if ((k > 0) && (0 == ((k + 1) % 16))) { + trimTrailingSpaces(buff); fprintf(fp, formatstr, buff); bpos = bpstart; memset(buff, ' ', 80); @@ -1606,6 +1569,7 @@ dStrHexFp(const char* str, int len, int no_ascii, FILE * fp) } if (bpos > bpstart) { buff[bpos + 2] = '\0'; + trimTrailingSpaces(buff); fprintf(fp, "%s\n", buff); } return; @@ -1629,6 +1593,8 @@ dStrHexFp(const char* str, int len, int no_ascii, FILE * fp) buff[cpos++] = c; } if (cpos > (cpstart + 15)) { + if (no_ascii) + trimTrailingSpaces(buff); fprintf(fp, formatstr, buff); bpos = bpstart; cpos = cpstart; @@ -1640,6 +1606,8 @@ dStrHexFp(const char* str, int len, int no_ascii, FILE * fp) } if (cpos > cpstart) { buff[cpos] = '\0'; + if (no_ascii) + trimTrailingSpaces(buff); fprintf(fp, "%s\n", buff); } } @@ -1671,8 +1639,11 @@ dStrHexStr(const char* str, int len, const char * leadin, int format, char buff[122]; int bpstart, bpos, k, n; - if (len <= 0) + if (len <= 0) { + if (b_len > 0) + b[0] = '\0'; return; + } if (0 != format) { ; /* do nothing different for now */ } @@ -1697,7 +1668,8 @@ dStrHexStr(const char* str, int len, const char * leadin, int format, (int)(unsigned char)c); buff[bpos + 2] = ' '; if ((k > 0) && (0 == ((k + 1) % 16))) { - n += my_snprintf(b + n, b_len - n, "%.*s\n", bpstart + 48, buff); + trimTrailingSpaces(buff); + n += my_snprintf(b + n, b_len - n, "%s\n", buff); if (n >= (b_len - 1)) return; bpos = bpstart; @@ -1707,8 +1679,10 @@ dStrHexStr(const char* str, int len, const char * leadin, int format, } else bpos += 3; } - if (bpos > bpstart) - n += my_snprintf(b + n, b_len - n, "%.*s\n", bpstart + 48, buff); + if (bpos > bpstart) { + trimTrailingSpaces(buff); + n += my_snprintf(b + n, b_len - n, "%s\n", buff); + } return; } diff --git a/lib/sg_lib_data.c b/lib/sg_lib_data.c index d4002b0..bce3ec8 100644 --- a/lib/sg_lib_data.c +++ b/lib/sg_lib_data.c @@ -17,7 +17,7 @@ #endif -const char * sg_lib_version_str = "2.01 20140521"; /* spc4r37, sbc4r02 */ +const char * sg_lib_version_str = "2.08 20141110"; /* spc4r37a, sbc4r02 */ #ifdef SG_SCSI_STRINGS struct sg_lib_value_name_t sg_lib_normal_opcodes[] = { @@ -117,7 +117,7 @@ struct sg_lib_value_name_t sg_lib_normal_opcodes[] = { {0x51, PDT_MMC, "Read disk information"}, {0x52, 0, "Xdread(10)"}, /* obsolete in SBC-3 r31 */ {0x52, PDT_MMC, "Read track information"}, - {0x53, 0, "Reserve track"}, + {0x53, 0, "Xdwriteread(10)"}, {0x54, 0, "Send OPC information"}, {0x55, 0, "Mode select(10)"}, {0x56, 0, "Reserve(10)"}, /* obsolete in SPC-4 r11 */ @@ -213,7 +213,8 @@ struct sg_lib_value_name_t sg_lib_normal_opcodes[] = { {0xffff, 0, NULL}, }; -struct sg_lib_value_name_t sg_lib_read_buff_arr[] = { /* opcode 0x3c */ +/* Read buffer [0x3c] service actions, need prefix */ +struct sg_lib_value_name_t sg_lib_read_buff_arr[] = { {0x0, 0, "combined header and data [or multiple modes]"}, {0x2, 0, "data"}, {0x3, 0, "descriptor"}, @@ -224,7 +225,8 @@ struct sg_lib_value_name_t sg_lib_read_buff_arr[] = { /* opcode 0x3c */ {0xffff, 0, NULL}, }; -struct sg_lib_value_name_t sg_lib_write_buff_arr[] = { /* opcode 0x3b */ +/* Write buffer [0x3b] service actions, need prefix */ +struct sg_lib_value_name_t sg_lib_write_buff_arr[] = { {0x0, 0, "combined header and data [or multiple modes]"}, {0x2, 0, "data"}, {0x4, 0, "download microcode and activate"}, @@ -242,7 +244,8 @@ struct sg_lib_value_name_t sg_lib_write_buff_arr[] = { /* opcode 0x3b */ {0xffff, 0, NULL}, }; -struct sg_lib_value_name_t sg_lib_maint_in_arr[] = { /* opcode 0xa3 */ +/* Maintenance in [0xa3] service actions */ +struct sg_lib_value_name_t sg_lib_maint_in_arr[] = { {0x5, 0, "Report identifying information"}, /* was "Report device identifier" prior to spc4r07 */ {0xa, 0, "Report target port groups"}, @@ -252,10 +255,12 @@ struct sg_lib_value_name_t sg_lib_maint_in_arr[] = { /* opcode 0xa3 */ {0xe, 0, "Report priority"}, {0xf, 0, "Report timestamp"}, {0x10, 0, "Management protocol in"}, + {0x1f, 0, "Maintenance in vendor specific"}, {0xffff, 0, NULL}, }; -struct sg_lib_value_name_t sg_lib_maint_out_arr[] = { /* opcode 0xa4 */ +/* Maintenance out [0xa4] service actions */ +struct sg_lib_value_name_t sg_lib_maint_out_arr[] = { {0x6, 0, "Set identifying information"}, /* was "Set device identifier" prior to spc4r07 */ {0xa, 0, "Set target port groups"}, @@ -264,28 +269,33 @@ struct sg_lib_value_name_t sg_lib_maint_out_arr[] = { /* opcode 0xa4 */ {0xe, 0, "Set priority"}, {0xf, 0, "Set timestamp"}, {0x10, 0, "Management protocol out"}, + {0x1f, 0, "Maintenance out vendor specific"}, {0xffff, 0, NULL}, }; -struct sg_lib_value_name_t sg_lib_sanitize_sa_arr[] = { /* opcode 0x48 */ - {0x1, 0, "Sanitize, overwrite"}, - {0x2, 0, "Sanitize, block erase"}, - {0x3, 0, "Sanitize, cryptographic erase"}, - {0x1f, 0, "Sanitize, exit failure mode"}, +/* Sanitize [0x48] service actions, need prefix */ +struct sg_lib_value_name_t sg_lib_sanitize_sa_arr[] = { + {0x1, 0, "overwrite"}, + {0x2, 0, "block erase"}, + {0x3, 0, "cryptographic erase"}, + {0x1f, 0, "exit failure mode"}, {0xffff, 0, NULL}, }; -struct sg_lib_value_name_t sg_lib_serv_in12_arr[] = { /* opcode 0xab */ +/* Service action in(12) [0xab] service actions */ +struct sg_lib_value_name_t sg_lib_serv_in12_arr[] = { {0x1, 0, "Read media serial number"}, {0xffff, 0, NULL}, }; -struct sg_lib_value_name_t sg_lib_serv_out12_arr[] = { /* opcode 0xa9 */ +/* Service action out(12) [0xa9] service actions */ +struct sg_lib_value_name_t sg_lib_serv_out12_arr[] = { {0xff, 0, "Impossible command name"}, {0xffff, 0, NULL}, }; -struct sg_lib_value_name_t sg_lib_serv_in16_arr[] = { /* opcode 0x9e */ +/* Service action in(16) [0x9e] service actions */ +struct sg_lib_value_name_t sg_lib_serv_in16_arr[] = { {0x10, 0, "Read capacity(16)"}, {0x11, 0, "Read long(16)"}, {0x12, 0, "Get LBA status"}, @@ -294,41 +304,46 @@ struct sg_lib_value_name_t sg_lib_serv_in16_arr[] = { /* opcode 0x9e */ {0xffff, 0, NULL}, }; -struct sg_lib_value_name_t sg_lib_serv_out16_arr[] = { /* opcode 0x9f */ +/* Service action out(16) [0x9f] service actions */ +struct sg_lib_value_name_t sg_lib_serv_out16_arr[] = { {0x11, 0, "Write long(16)"}, {0x14, PDT_ZBC, "Reset write pointer"}, {0x1f, PDT_ADC, "Notify data transfer device(16)"}, {0xffff, 0, NULL}, }; -struct sg_lib_value_name_t sg_lib_serv_bidi_arr[] = { /* opcode 0x9d */ +/* Service action bidirectional [0x9d] service actions */ +struct sg_lib_value_name_t sg_lib_serv_bidi_arr[] = { {0xffff, 0, NULL}, }; -struct sg_lib_value_name_t sg_lib_pr_in_arr[] = { /* opcode 0x5e */ - {0x0, 0, "Persistent reserve in, read keys"}, - {0x1, 0, "Persistent reserve in, read reservation"}, - {0x2, 0, "Persistent reserve in, report capabilities"}, - {0x3, 0, "Persistent reserve in, read full status"}, +/* Persistent reserve in [0x5e] service actions, need prefix */ +struct sg_lib_value_name_t sg_lib_pr_in_arr[] = { + {0x0, 0, "read keys"}, + {0x1, 0, "read reservation"}, + {0x2, 0, "report capabilities"}, + {0x3, 0, "read full status"}, {0xffff, 0, NULL}, }; -struct sg_lib_value_name_t sg_lib_pr_out_arr[] = { /* opcode 0x5f */ - {0x0, 0, "Persistent reserve out, register"}, - {0x1, 0, "Persistent reserve out, reserve"}, - {0x2, 0, "Persistent reserve out, release"}, - {0x3, 0, "Persistent reserve out, clear"}, - {0x4, 0, "Persistent reserve out, preempt"}, - {0x5, 0, "Persistent reserve out, preempt and abort"}, - {0x6, 0, "Persistent reserve out, register and ignore existing key"}, - {0x7, 0, "Persistent reserve out, register and move"}, - {0x8, 0, "Persistent reserve out, replace lost reservation"}, +/* Persistent reserve out [0x5f] service actions, need prefix */ +struct sg_lib_value_name_t sg_lib_pr_out_arr[] = { + {0x0, 0, "register"}, + {0x1, 0, "reserve"}, + {0x2, 0, "release"}, + {0x3, 0, "clear"}, + {0x4, 0, "preempt"}, + {0x5, 0, "preempt and abort"}, + {0x6, 0, "register and ignore existing key"}, + {0x7, 0, "register and move"}, + {0x8, 0, "replace lost reservation"}, {0xffff, 0, NULL}, }; -/* 'Extended copy' was renamed 'Third party copy in' in spc4r34 */ -/* LID1 is an abbreviation of List Identifier length of 1 byte */ -struct sg_lib_value_name_t sg_lib_xcopy_sa_arr[] = { /* opcode 0x83 */ +/* Third party copy in [0x83] service actions + * Opcode 'Receive copy results' was renamed 'Third party copy in' in spc4r34 + * LID1 is an abbreviation of List Identifier length of 1 byte */ +struct sg_lib_value_name_t sg_lib_xcopy_sa_arr[] = { {0x0, 0, "Extended copy(LID1)"}, {0x1, 0, "Extended copy(LID4)"}, {0x10, 0, "Populate token"}, @@ -337,9 +352,10 @@ struct sg_lib_value_name_t sg_lib_xcopy_sa_arr[] = { /* opcode 0x83 */ {0xffff, 0, NULL}, }; -/* 'Receive copy results' was renamed 'Third party copy out' in spc4r34 */ -/* LID4 is an abbreviation of List Identifier length of 4 bytes */ -struct sg_lib_value_name_t sg_lib_rec_copy_sa_arr[] = { /* opcode 0x84 */ +/* Third party copy out [0x84] service actions + * Opcode 'Extended copy' was renamed 'Third party copy out' in spc4r34 + * LID4 is an abbreviation of List Identifier length of 4 bytes */ +struct sg_lib_value_name_t sg_lib_rec_copy_sa_arr[] = { {0x0, 0, "Receive copy status(LID1)"}, {0x1, 0, "Receive copy data(LID1)"}, {0x3, 0, "Receive copy operating parameters"}, @@ -351,6 +367,7 @@ struct sg_lib_value_name_t sg_lib_rec_copy_sa_arr[] = { /* opcode 0x84 */ {0xffff, 0, NULL}, }; +/* Variable length cdb [0x7f] service actions (more than 16 bytes long) */ struct sg_lib_value_name_t sg_lib_variable_length_arr[] = { {0x1, 0, "Rebuild(32)"}, {0x2, 0, "Regenerate(32)"}, @@ -363,7 +380,7 @@ struct sg_lib_value_name_t sg_lib_variable_length_arr[] = { {0x9, 0, "Read(32)"}, {0xa, 0, "Verify(32)"}, {0xb, 0, "Write(32)"}, - {0xc, 0, "Write an verify(32)"}, + {0xc, 0, "Write and verify(32)"}, {0xd, 0, "Write same(32)"}, {0xe, 0, "Orwrite(32)"}, /* added sbc3r25 */ {0xf, 0, "Atomic write(32)"}, /* added sbc4r02 */ @@ -494,7 +511,7 @@ struct sg_lib_value_name_t sg_lib_variable_length_arr[] = { /* A conveniently formatted list of SCSI ASC/ASCQ codes and their * corresponding text can be found at: www.t10.org/lists/asc-num.txt - * The following should match asc-num.txt dated 20140516 */ + * The following should match asc-num.txt dated 20140924 */ #ifdef SG_SCSI_STRINGS struct sg_lib_asc_ascq_range_t sg_lib_asc_ascq_range[] = @@ -595,6 +612,7 @@ struct sg_lib_asc_ascq_t sg_lib_asc_ascq[] = {0x09,0x02,"Focus servo failure"}, {0x09,0x03,"Spindle servo failure"}, {0x09,0x04,"Head select fault"}, + {0x09,0x05,"Vibration induced tracking error"}, {0x0A,0x00,"Error log overflow"}, {0x0B,0x00,"Warning"}, {0x0B,0x01,"Warning - specified temperature exceeded"}, @@ -622,6 +640,7 @@ struct sg_lib_asc_ascq_t sg_lib_asc_ascq[] = {0x0C,0x0D,"Write error - not enough unsolicited data"}, {0x0C,0x0E,"Multiple write errors"}, {0x0C,0x0F,"Defects in error window"}, + {0x0C,0x10,"Incomplete multiple atomic write operations"}, {0x0D,0x00,"Error detected by third party temporary initiator"}, {0x0D,0x01,"Third party device failure"}, {0x0D,0x02,"Copy target device not reachable"}, diff --git a/lib/sg_pt_win32.c b/lib/sg_pt_win32.c index 6bb3912..a55e029 100644 --- a/lib/sg_pt_win32.c +++ b/lib/sg_pt_win32.c @@ -1,11 +1,11 @@ /* - * Copyright (c) 2006-2012 Douglas Gilbert. + * Copyright (c) 2006-2014 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ -/* sg_pt_win32 version 1.14 20121026 */ +/* sg_pt_win32 version 1.15 20140901 */ #include <stdio.h> #include <stdlib.h> @@ -24,9 +24,9 @@ #endif #ifndef O_EXCL -// #define O_EXCL 0x80 // cygwin ?? -// #define O_EXCL 0x80 // Linux -#define O_EXCL 0x400 // mingw +// #define O_EXCL 0x80 // cygwin ?? +// #define O_EXCL 0x80 // Linux +#define O_EXCL 0x400 // mingw #warning "O_EXCL not defined" #endif @@ -64,9 +64,10 @@ struct sg_pt_handle { int bus; int target; int lun; + int verbose; /* tunnel verbose through to scsi_pt_close_device */ }; -struct sg_pt_handle handle_arr[MAX_OPEN_SIMULT]; +static struct sg_pt_handle handle_arr[MAX_OPEN_SIMULT]; struct sg_pt_win32_scsi { unsigned char * dxferp; @@ -201,6 +202,7 @@ scsi_pt_open_flags(const char * device_name, int flags, int verbose) shp->bus = bus; shp->target = target; shp->lun = lun; + shp->verbose = verbose; memset(shp->adapter, 0, sizeof(shp->adapter)); strncpy(shp->adapter, "\\\\.\\", 4); if (got_pd_name) @@ -216,8 +218,8 @@ scsi_pt_open_flags(const char * device_name, int flags, int verbose) share_mode, NULL, OPEN_EXISTING, 0, NULL); if (shp->fh == INVALID_HANDLE_VALUE) { if (verbose) - fprintf(sg_warnings_strm, "Windows CreateFile error=%ld\n", - GetLastError()); + fprintf(sg_warnings_strm, "Windows CreateFile error=%u\n", + (unsigned int)GetLastError()); shp->in_use = 0; return -ENODEV; } @@ -225,7 +227,11 @@ scsi_pt_open_flags(const char * device_name, int flags, int verbose) } -/* Returns 0 if successful. If error in Unix returns negated errno. */ +/* Returns 0 if successful. If device_id seems wild returns -ENODEV, + * other errors return 0. If CloseHandle() fails and verbose > 0 then + * outputs warning with value from GetLastError(). The verbose value + * defaults to zero and is potentially set from the most recent call + * to scsi_pt_open_device() or do_scsi_pt(). */ int scsi_pt_close_device(int device_fd) { @@ -237,12 +243,15 @@ scsi_pt_close_device(int device_fd) if ((index < 0) || (index >= WIN32_FDOFFSET)) return -ENODEV; shp = handle_arr + index; - CloseHandle(shp->fh); + if ((! CloseHandle(shp->fh)) && shp->verbose) + fprintf(sg_warnings_strm, "Windows CloseHandle error=%u\n", + (unsigned int)GetLastError()); shp->bus = 0; shp->target = 0; shp->lun = 0; memset(shp->adapter, 0, sizeof(shp->adapter)); shp->in_use = 0; + shp->verbose = 0; return 0; } @@ -435,7 +444,7 @@ set_scsi_pt_flags(struct sg_pt_base * objp, int flags) /* Executes SCSI command (or at least forwards it to lower layers) * using direct interface. Clears os_err field prior to active call (whose * result may set it again). */ -int +static int do_scsi_pt_direct(struct sg_pt_base * vp, int device_fd, int time_secs, int verbose) { @@ -443,7 +452,7 @@ do_scsi_pt_direct(struct sg_pt_base * vp, int device_fd, int time_secs, struct sg_pt_win32_scsi * psp = vp->implp; struct sg_pt_handle * shp; BOOL status; - ULONG returned; + DWORD returned; if (NULL == sg_warnings_strm) sg_warnings_strm = stderr; @@ -473,6 +482,7 @@ do_scsi_pt_direct(struct sg_pt_base * vp, int device_fd, int time_secs, psp->os_err = ENODEV; return -psp->os_err; } + shp->verbose = verbose; psp->swb_d.spt.Length = sizeof (SCSI_PASS_THROUGH_DIRECT); psp->swb_d.spt.PathId = shp->bus; psp->swb_d.spt.TargetId = shp->target; @@ -486,13 +496,14 @@ do_scsi_pt_direct(struct sg_pt_base * vp, int device_fd, int time_secs, (int)psp->swb_d.spt.ScsiStatus, (int)psp->swb_d.spt.PathId, (int)psp->swb_d.spt.TargetId, (int)psp->swb_d.spt.Lun); fprintf(stderr, " CdbLength=%d SenseInfoLength=%d DataIn=%d " - "DataTransferLength=%lu\n", + "DataTransferLength=%u\n", (int)psp->swb_d.spt.CdbLength, (int)psp->swb_d.spt.SenseInfoLength, (int)psp->swb_d.spt.DataIn, - psp->swb_d.spt.DataTransferLength); - fprintf(stderr, " TimeOutValue=%lu SenseInfoOffset=%lu\n", - psp->swb_d.spt.TimeOutValue, psp->swb_d.spt.SenseInfoOffset); + (unsigned int)psp->swb_d.spt.DataTransferLength); + fprintf(stderr, " TimeOutValue=%u SenseInfoOffset=%u\n", + (unsigned int)psp->swb_d.spt.TimeOutValue, + (unsigned int)psp->swb_d.spt.SenseInfoOffset); } psp->swb_d.spt.DataBuffer = psp->dxferp; status = DeviceIoControl(shp->fh, IOCTL_SCSI_PASS_THROUGH_DIRECT, @@ -503,10 +514,13 @@ do_scsi_pt_direct(struct sg_pt_base * vp, int device_fd, int time_secs, &returned, NULL); if (! status) { - psp->transport_err = GetLastError(); + unsigned int u; + + u = (unsigned int)GetLastError(); if (verbose) - fprintf(sg_warnings_strm, "Windows DeviceIoControl error=%d\n", - psp->transport_err); + fprintf(sg_warnings_strm, "Windows DeviceIoControl error=%u\n", + u); + psp->transport_err = (int)u; psp->os_err = EIO; return 0; /* let app find transport error */ } @@ -537,7 +551,7 @@ do_scsi_pt_indirect(struct sg_pt_base * vp, int device_fd, int time_secs, struct sg_pt_win32_scsi * psp = vp->implp; struct sg_pt_handle * shp; BOOL status; - ULONG returned; + DWORD returned; if (NULL == sg_warnings_strm) sg_warnings_strm = stderr; @@ -568,6 +582,7 @@ do_scsi_pt_indirect(struct sg_pt_base * vp, int device_fd, int time_secs, psp->os_err = ENODEV; return -psp->os_err; } + shp->verbose = verbose; if (psp->dxfer_len > (int)sizeof(psp->swb_i.ucDataBuf)) { int extra = psp->dxfer_len - (int)sizeof(psp->swb_i.ucDataBuf); struct sg_pt_win32_scsi * epsp; @@ -576,7 +591,7 @@ do_scsi_pt_indirect(struct sg_pt_base * vp, int device_fd, int time_secs, fprintf(sg_warnings_strm, "spt_indirect: dxfer_len (%d) too " "large for initial data\n buffer (%d bytes), try " "enlarging\n", psp->dxfer_len, - sizeof(psp->swb_i.ucDataBuf)); + (int)sizeof(psp->swb_i.ucDataBuf)); epsp = (struct sg_pt_win32_scsi *) calloc(sizeof(struct sg_pt_win32_scsi) + extra, 1); if (NULL == epsp) { @@ -605,15 +620,16 @@ do_scsi_pt_indirect(struct sg_pt_base * vp, int device_fd, int time_secs, (int)psp->swb_i.spt.ScsiStatus, (int)psp->swb_i.spt.PathId, (int)psp->swb_i.spt.TargetId, (int)psp->swb_i.spt.Lun); fprintf(stderr, " CdbLength=%d SenseInfoLength=%d DataIn=%d " - "DataTransferLength=%lu\n", + "DataTransferLength=%u\n", (int)psp->swb_i.spt.CdbLength, (int)psp->swb_i.spt.SenseInfoLength, (int)psp->swb_i.spt.DataIn, - psp->swb_i.spt.DataTransferLength); - fprintf(stderr, " TimeOutValue=%lu DataBufferOffset=%lu " - "SenseInfoOffset=%lu\n", psp->swb_i.spt.TimeOutValue, - psp->swb_i.spt.DataBufferOffset, - psp->swb_i.spt.SenseInfoOffset); + (unsigned int)psp->swb_i.spt.DataTransferLength); + fprintf(stderr, " TimeOutValue=%u DataBufferOffset=%u " + "SenseInfoOffset=%u\n", + (unsigned int)psp->swb_i.spt.TimeOutValue, + (unsigned int)psp->swb_i.spt.DataBufferOffset, + (unsigned int)psp->swb_i.spt.SenseInfoOffset); } if ((psp->dxfer_len > 0) && (SCSI_IOCTL_DATA_OUT == psp->swb_i.spt.DataIn)) @@ -626,10 +642,13 @@ do_scsi_pt_indirect(struct sg_pt_base * vp, int device_fd, int time_secs, &returned, NULL); if (! status) { - psp->transport_err = GetLastError(); + unsigned int u; + + u = (unsigned int)GetLastError(); if (verbose) - fprintf(sg_warnings_strm, "Windows DeviceIoControl error=%d\n", - psp->transport_err); + fprintf(sg_warnings_strm, "Windows DeviceIoControl error=%u\n", + u); + psp->transport_err = (int)u; psp->os_err = EIO; return 0; /* let app find transport error */ } diff --git a/scripts/58-scsi-sg3_symlink.rules b/scripts/58-scsi-sg3_symlink.rules index a95cbcb..697f92b 100644 --- a/scripts/58-scsi-sg3_symlink.rules +++ b/scripts/58-scsi-sg3_symlink.rules @@ -10,6 +10,7 @@ ENV{DM_MULTIPATH_DEVICE_PATH}=="1", GOTO="sg3_utils_symlink_end" # Select which identifier to use per default # 0: vpd page 0x80 identifier ENV{SCSI_IDENT_SERIAL}=="?*", ENV{DEVTYPE}=="disk", SYMLINK+="disk/by-id/scsi-S$env{SCSI_VENDOR}_$env{SCSI_MODEL}_$env{SCSI_IDENT_SERIAL}" +ENV{SCSI_IDENT_SERIAL}=="?*", ENV{DEVTYPE}=="partition", SYMLINK+="disk/by-id/scsi-S$env{SCSI_VENDOR}_$env{SCSI_MODEL}_$env{SCSI_IDENT_SERIAL}-part%n" # 1: NAA identifier (prefix 3) ENV{SCSI_IDENT_LUN_NAA}=="?*", ENV{DEVTYPE}=="disk", SYMLINK+="disk/by-id/scsi-3$env{SCSI_IDENT_LUN_NAA}" ENV{SCSI_IDENT_LUN_NAA}=="?*", ENV{DEVTYPE}=="partition", SYMLINK+="disk/by-id/scsi-3$env{SCSI_IDENT_LUN_NAA}-part%n" diff --git a/scripts/Makefile.in b/scripts/Makefile.in index e16f947..7500c85 100644 --- a/scripts/Makefile.in +++ b/scripts/Makefile.in @@ -242,6 +242,7 @@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ +os_cflags = @os_cflags@ os_libs = @os_libs@ pdfdir = @pdfdir@ prefix = @prefix@ diff --git a/scripts/scsi_logging_level b/scripts/scsi_logging_level index d31f4c6..697ce26 100755 --- a/scripts/scsi_logging_level +++ b/scripts/scsi_logging_level @@ -231,7 +231,7 @@ set_logging_level() echo $LEVEL > /proc/sys/dev/scsi/logging_level if [ $? != 0 ] then - echo "$SCRIPTNAME: could not write scsi logging level" \ + echo "$SCRIPTNAME: could not write scsi logging level $LEVEL" \ "(kernel probably without SCSI_LOGGING support)" exit 1 fi diff --git a/sg3_utils.spec b/sg3_utils.spec index 0e83c2c..a6eb8b3 100644 --- a/sg3_utils.spec +++ b/sg3_utils.spec @@ -1,6 +1,6 @@ Summary: Utilities for devices that use SCSI command sets Name: sg3_utils -Version: 1.39 +Version: 1.40 # Release: 1%{?dist} Release: 1 License: GPL @@ -20,7 +20,7 @@ mode and log pages (sginfo, sg_modes and sg_logs); spin up and down disks (sg_start); do self tests (sg_senddiag); and various other functions. See the README, ChangeLog and COVERAGE files. Requires the linux kernel 2.4 series or later. In the 2.4 series SCSI generic device names (e.g. /dev/sg0) -must be used. In the 2.6 series other device names may be used as +must be used. In the 2.6 series and later other device names may be used as well (e.g. /dev/sda). Warning: Some of these tools access the internals of your system @@ -79,6 +79,10 @@ fi %{_libdir}/*.la %changelog +* Mon Nov 10 2014 - dgilbert at interlog dot com +- track t10 changes + * sg3_utils-1.40 + * Thu Jun 12 2014 - dgilbert at interlog dot com - track t10 changes * sg3_utils-1.39 diff --git a/src/Makefile.am b/src/Makefile.am index 483c772..ca67117 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -15,10 +15,11 @@ bin_PROGRAMS = \ sg_rdac sg_read sg_readcap sg_read_block_limits sg_read_buffer \ sg_read_long sg_reassign sg_referrals sg_rep_zones sg_requests \ sg_reset sg_reset_wp sg_rmsn sg_rtpg sg_safte sg_sanitize \ - sg_sat_identify sg_sat_phy_event sg_sat_set_features sg_scan \ - sg_senddiag sg_ses sg_start sg_stpg sg_sync sg_test_rwbuf sg_turs \ - sg_unmap sg_verify sg_vpd sg_write_buffer sg_write_long \ - sg_write_same sg_wr_mode sg_xcopy + sg_sat_identify sg_sat_phy_event sg_sat_read_gplog \ + sg_sat_set_features sg_scan sg_senddiag sg_ses sg_ses_microcode \ + sg_start sg_stpg sg_sync sg_test_rwbuf sg_turs sg_unmap sg_verify \ + sg_vpd sg_write_buffer sg_write_long sg_write_same sg_write_verify \ + sg_wr_mode sg_xcopy distclean-local: rm -f sg_scan.c @@ -38,9 +39,10 @@ bin_PROGRAMS = \ sg_read_block_limits sg_read_buffer sg_read_long sg_reassign \ sg_referrals sg_rep_zones sg_requests sg_reset_wp sg_rmsn \ sg_rtpg sg_safte sg_sanitize sg_sat_identify sg_sat_phy_event \ - sg_sat_set_features sg_scan sg_senddiag sg_ses sg_start sg_stpg \ - sg_sync sg_turs sg_unmap sg_verify sg_vpd sg_write_buffer \ - sg_write_long sg_write_same sg_wr_mode + sg_sat_read_gplog sg_sat_set_features sg_scan sg_senddiag sg_ses \ + sg_ses_microcode sg_start sg_stpg sg_sync sg_turs sg_unmap sg_verify \ + sg_vpd sg_write_buffer sg_write_long sg_write_same sg_write_verify \ + sg_wr_mode distclean-local: rm -f sg_scan.c @@ -60,9 +62,10 @@ bin_PROGRAMS = \ sg_read_block_limits sg_read_buffer sg_read_long sg_reassign \ sg_referrals sg_rep_zones sg_requests sg_reset_wp sg_rmsn \ sg_rtpg sg_safte sg_sanitize sg_sat_identify sg_sat_phy_event \ - sg_sat_set_features sg_scan sg_senddiag sg_ses sg_start sg_stpg \ - sg_sync sg_turs sg_unmap sg_verify sg_vpd sg_write_buffer \ - sg_write_long sg_write_same sg_wr_mode + sg_sat_read_gplog sg_sat_set_features sg_scan sg_senddiag sg_ses \ + sg_ses_microcode sg_start sg_stpg sg_sync sg_turs sg_unmap sg_verify \ + sg_vpd sg_write_buffer sg_write_long sg_write_same sg_write_verify \ + sg_wr_mode distclean-local: rm -f sg_scan.c @@ -82,9 +85,10 @@ bin_PROGRAMS = \ sg_read_block_limits sg_read_buffer sg_read_long sg_reassign \ sg_referrals sg_rep_zones sg_requests sg_reset_wp sg_rmsn \ sg_rtpg sg_safte sg_sanitize sg_sat_identify sg_sat_phy_event \ - sg_sat_set_features sg_senddiag sg_ses sg_start sg_stpg sg_sync \ - sg_turs sg_unmap sg_verify sg_vpd sg_write_buffer sg_write_long \ - sg_write_same sg_wr_mode + sg_sat_read_gplog sg_sat_set_features sg_senddiag sg_ses \ + sg_ses_microcode sg_start sg_stpg sg_sync sg_turs sg_unmap sg_verify \ + sg_vpd sg_write_buffer sg_write_long sg_write_same sg_write_verify \ + sg_wr_mode endif @@ -98,9 +102,10 @@ bin_PROGRAMS = \ sg_read_block_limits sg_read_buffer sg_read_long sg_reassign \ sg_referrals sg_rep_zones sg_requests sg_reset_wp sg_rmsn \ sg_rtpg sg_safte sg_sanitize sg_sat_identify sg_sat_phy_event \ - sg_sat_set_features sg_senddiag sg_ses sg_start sg_stpg sg_sync \ - sg_turs sg_unmap sg_verify sg_vpd sg_write_buffer sg_write_long \ - sg_write_same sg_wr_mode + sg_sat_read_gplog sg_sat_set_features sg_senddiag sg_ses \ + sg_ses_microcode sg_start sg_stpg sg_sync sg_turs sg_unmap sg_verify \ + sg_vpd sg_write_buffer sg_write_long sg_write_same sg_write_verify \ + sg_wr_mode endif @@ -112,24 +117,26 @@ bin_PROGRAMS = \ sg_get_lba_status sg_ident sg_inq sg_logs sg_luns sg_modes \ sg_opcodes sg_persist sg_prevent sg_raw sg_rdac sg_readcap \ sg_read_block_limits sg_read_buffer sg_read_long sg_reassign \ - sg_referrals sg_rep_zones sg_requests sg_reset_wp sg_rmsn \ - sg_rtpg sg_safte sg_sanitize sg_sat_identify sg_sat_phy_event \ - sg_sat_set_features sg_senddiag sg_ses sg_start sg_stpg sg_sync \ - sg_turs sg_unmap sg_verify sg_vpd sg_write_buffer sg_write_long \ - sg_write_same sg_wr_mode + sg_referrals sg_rep_zones sg_requests sg_reset_wp sg_rmsn sg_rtpg \ + sg_safte sg_sanitize sg_sat_identify sg_sat_phy_event \ + sg_sat_read_gplog sg_sat_set_features sg_senddiag sg_ses \ + sg_ses_microcode sg_start sg_stpg sg_sync sg_turs sg_unmap sg_verify \ + sg_vpd sg_write_buffer sg_write_long sg_write_same sg_write_verify \ + sg_wr_mode endif # For C++/clang testing -## CC = g++ +## CC = gcc ## CC = g++ ## CC = clang ## CC = clang++ # -std=<s> can be c99, c11, gnu11, etc. Default is gnu89 (gnu90 is the same) -AM_CFLAGS = -I ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W -# AM_CFLAGS = -I ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W -pedantic -std=c11 -# AM_CFLAGS = -I ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W -pedantic -std=c++11 +# -Wall is no longer all warnings. Add -W (since renamed to -Wextra) for more +AM_CFLAGS = -iquote ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W @os_cflags@ +# AM_CFLAGS = -iquote ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W @os_cflags@ -pedantic -std=c11 +# AM_CFLAGS = -iquote ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W @os_cflags@ -pedantic -std=c++11 sg_dd_SOURCES = sg_dd.c sg_dd_LDADD = ../lib/libsgutils2.la @os_libs@ @@ -254,6 +261,9 @@ sg_senddiag_LDADD = ../lib/libsgutils2.la @os_libs@ sg_ses_SOURCES = sg_ses.c sg_ses_LDADD = ../lib/libsgutils2.la @os_libs@ +sg_ses_microcode_SOURCES = sg_ses_microcode.c +sg_ses_microcode_LDADD = ../lib/libsgutils2.la @os_libs@ + sg_start_SOURCES = sg_start.c sg_start_LDADD = ../lib/libsgutils2.la @os_libs@ @@ -304,3 +314,9 @@ sg_rep_zones_LDADD = ../lib/libsgutils2.la @os_libs@ sg_reset_wp_SOURCES = sg_reset_wp.c sg_reset_wp_LDADD = ../lib/libsgutils2.la @os_libs@ + +sg_write_verify_SOURCES = sg_write_verify.c +sg_write_verify_LDADD = ../lib/libsgutils2.la @os_libs@ + +sg_sat_read_gplog_SOURCES = sg_sat_read_gplog.c +sg_sat_read_gplog_LDADD = ../lib/libsgutils2.la @os_libs@ diff --git a/src/Makefile.in b/src/Makefile.in index cad4f2e..8baf8f4 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -108,10 +108,12 @@ host_triplet = @host@ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@ sg_sanitize$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@ sg_sat_identify$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@ sg_sat_phy_event$(EXEEXT) \ +@OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@ sg_sat_read_gplog$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@ sg_sat_set_features$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@ sg_scan$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@ sg_senddiag$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@ sg_ses$(EXEEXT) \ +@OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@ sg_ses_microcode$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@ sg_start$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@ sg_stpg$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@ sg_sync$(EXEEXT) \ @@ -122,6 +124,7 @@ host_triplet = @host@ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@ sg_write_buffer$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@ sg_write_long$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@ sg_write_same$(EXEEXT) \ +@OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@ sg_write_verify$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_FALSE@@OS_WIN32_MINGW_TRUE@ sg_wr_mode$(EXEEXT) @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@bin_PROGRAMS = sg_compare_and_write$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@ sg_decode_sense$(EXEEXT) \ @@ -153,10 +156,12 @@ host_triplet = @host@ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@ sg_sanitize$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@ sg_sat_identify$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@ sg_sat_phy_event$(EXEEXT) \ +@OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@ sg_sat_read_gplog$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@ sg_sat_set_features$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@ sg_scan$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@ sg_senddiag$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@ sg_ses$(EXEEXT) \ +@OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@ sg_ses_microcode$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@ sg_start$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@ sg_stpg$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@ sg_sync$(EXEEXT) \ @@ -167,6 +172,7 @@ host_triplet = @host@ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@ sg_write_buffer$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@ sg_write_long$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@ sg_write_same$(EXEEXT) \ +@OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@ sg_write_verify$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_FALSE@@OS_WIN32_CYGWIN_TRUE@ sg_wr_mode$(EXEEXT) @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@bin_PROGRAMS = sg_compare_and_write$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@ sg_decode_sense$(EXEEXT) \ @@ -198,9 +204,11 @@ host_triplet = @host@ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@ sg_sanitize$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@ sg_sat_identify$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@ sg_sat_phy_event$(EXEEXT) \ +@OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@ sg_sat_read_gplog$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@ sg_sat_set_features$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@ sg_senddiag$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@ sg_ses$(EXEEXT) \ +@OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@ sg_ses_microcode$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@ sg_start$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@ sg_stpg$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@ sg_sync$(EXEEXT) \ @@ -211,6 +219,7 @@ host_triplet = @host@ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@ sg_write_buffer$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@ sg_write_long$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@ sg_write_same$(EXEEXT) \ +@OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@ sg_write_verify$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_FALSE@@OS_SOLARIS_TRUE@ sg_wr_mode$(EXEEXT) @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@bin_PROGRAMS = sg_compare_and_write$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@ sg_decode_sense$(EXEEXT) \ @@ -242,9 +251,11 @@ host_triplet = @host@ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@ sg_sanitize$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@ sg_sat_identify$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@ sg_sat_phy_event$(EXEEXT) \ +@OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@ sg_sat_read_gplog$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@ sg_sat_set_features$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@ sg_senddiag$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@ sg_ses$(EXEEXT) \ +@OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@ sg_ses_microcode$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@ sg_start$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@ sg_stpg$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@ sg_sync$(EXEEXT) \ @@ -255,6 +266,7 @@ host_triplet = @host@ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@ sg_write_buffer$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@ sg_write_long$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@ sg_write_same$(EXEEXT) \ +@OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@ sg_write_verify$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_FALSE@@OS_OSF_TRUE@ sg_wr_mode$(EXEEXT) @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@bin_PROGRAMS = sg_compare_and_write$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@ sg_copy_results$(EXEEXT) \ @@ -297,10 +309,12 @@ host_triplet = @host@ @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@ sg_sanitize$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@ sg_sat_identify$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@ sg_sat_phy_event$(EXEEXT) \ +@OS_FREEBSD_FALSE@@OS_LINUX_TRUE@ sg_sat_read_gplog$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@ sg_sat_set_features$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@ sg_scan$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@ sg_senddiag$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@ sg_ses$(EXEEXT) \ +@OS_FREEBSD_FALSE@@OS_LINUX_TRUE@ sg_ses_microcode$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@ sg_start$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@ sg_stpg$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@ sg_sync$(EXEEXT) \ @@ -312,6 +326,7 @@ host_triplet = @host@ @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@ sg_write_buffer$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@ sg_write_long$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@ sg_write_same$(EXEEXT) \ +@OS_FREEBSD_FALSE@@OS_LINUX_TRUE@ sg_write_verify$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@ sg_wr_mode$(EXEEXT) \ @OS_FREEBSD_FALSE@@OS_LINUX_TRUE@ sg_xcopy$(EXEEXT) @OS_FREEBSD_TRUE@bin_PROGRAMS = sg_compare_and_write$(EXEEXT) \ @@ -331,14 +346,16 @@ host_triplet = @host@ @OS_FREEBSD_TRUE@ sg_rtpg$(EXEEXT) sg_safte$(EXEEXT) \ @OS_FREEBSD_TRUE@ sg_sanitize$(EXEEXT) sg_sat_identify$(EXEEXT) \ @OS_FREEBSD_TRUE@ sg_sat_phy_event$(EXEEXT) \ +@OS_FREEBSD_TRUE@ sg_sat_read_gplog$(EXEEXT) \ @OS_FREEBSD_TRUE@ sg_sat_set_features$(EXEEXT) \ @OS_FREEBSD_TRUE@ sg_senddiag$(EXEEXT) sg_ses$(EXEEXT) \ -@OS_FREEBSD_TRUE@ sg_start$(EXEEXT) sg_stpg$(EXEEXT) \ -@OS_FREEBSD_TRUE@ sg_sync$(EXEEXT) sg_turs$(EXEEXT) \ -@OS_FREEBSD_TRUE@ sg_unmap$(EXEEXT) sg_verify$(EXEEXT) \ -@OS_FREEBSD_TRUE@ sg_vpd$(EXEEXT) sg_write_buffer$(EXEEXT) \ +@OS_FREEBSD_TRUE@ sg_ses_microcode$(EXEEXT) sg_start$(EXEEXT) \ +@OS_FREEBSD_TRUE@ sg_stpg$(EXEEXT) sg_sync$(EXEEXT) \ +@OS_FREEBSD_TRUE@ sg_turs$(EXEEXT) sg_unmap$(EXEEXT) \ +@OS_FREEBSD_TRUE@ sg_verify$(EXEEXT) sg_vpd$(EXEEXT) \ +@OS_FREEBSD_TRUE@ sg_write_buffer$(EXEEXT) \ @OS_FREEBSD_TRUE@ sg_write_long$(EXEEXT) sg_write_same$(EXEEXT) \ -@OS_FREEBSD_TRUE@ sg_wr_mode$(EXEEXT) +@OS_FREEBSD_TRUE@ sg_write_verify$(EXEEXT) sg_wr_mode$(EXEEXT) subdir = src DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/depcomp @@ -470,6 +487,9 @@ sg_sat_identify_DEPENDENCIES = ../lib/libsgutils2.la am_sg_sat_phy_event_OBJECTS = sg_sat_phy_event.$(OBJEXT) sg_sat_phy_event_OBJECTS = $(am_sg_sat_phy_event_OBJECTS) sg_sat_phy_event_DEPENDENCIES = ../lib/libsgutils2.la +am_sg_sat_read_gplog_OBJECTS = sg_sat_read_gplog.$(OBJEXT) +sg_sat_read_gplog_OBJECTS = $(am_sg_sat_read_gplog_OBJECTS) +sg_sat_read_gplog_DEPENDENCIES = ../lib/libsgutils2.la am_sg_sat_set_features_OBJECTS = sg_sat_set_features.$(OBJEXT) sg_sat_set_features_OBJECTS = $(am_sg_sat_set_features_OBJECTS) sg_sat_set_features_DEPENDENCIES = ../lib/libsgutils2.la @@ -482,6 +502,9 @@ sg_senddiag_DEPENDENCIES = ../lib/libsgutils2.la am_sg_ses_OBJECTS = sg_ses.$(OBJEXT) sg_ses_OBJECTS = $(am_sg_ses_OBJECTS) sg_ses_DEPENDENCIES = ../lib/libsgutils2.la +am_sg_ses_microcode_OBJECTS = sg_ses_microcode.$(OBJEXT) +sg_ses_microcode_OBJECTS = $(am_sg_ses_microcode_OBJECTS) +sg_ses_microcode_DEPENDENCIES = ../lib/libsgutils2.la am_sg_start_OBJECTS = sg_start.$(OBJEXT) sg_start_OBJECTS = $(am_sg_start_OBJECTS) sg_start_DEPENDENCIES = ../lib/libsgutils2.la @@ -518,6 +541,9 @@ sg_write_long_DEPENDENCIES = ../lib/libsgutils2.la am_sg_write_same_OBJECTS = sg_write_same.$(OBJEXT) sg_write_same_OBJECTS = $(am_sg_write_same_OBJECTS) sg_write_same_DEPENDENCIES = ../lib/libsgutils2.la +am_sg_write_verify_OBJECTS = sg_write_verify.$(OBJEXT) +sg_write_verify_OBJECTS = $(am_sg_write_verify_OBJECTS) +sg_write_verify_DEPENDENCIES = ../lib/libsgutils2.la am_sg_xcopy_OBJECTS = sg_xcopy.$(OBJEXT) sg_xcopy_OBJECTS = $(am_sg_xcopy_OBJECTS) sg_xcopy_DEPENDENCIES = ../lib/libsgutils2.la @@ -580,14 +606,16 @@ SOURCES = $(sg_compare_and_write_SOURCES) $(sg_copy_results_SOURCES) \ $(sg_reset_SOURCES) $(sg_reset_wp_SOURCES) $(sg_rmsn_SOURCES) \ $(sg_rtpg_SOURCES) $(sg_safte_SOURCES) $(sg_sanitize_SOURCES) \ $(sg_sat_identify_SOURCES) $(sg_sat_phy_event_SOURCES) \ - $(sg_sat_set_features_SOURCES) $(sg_scan_SOURCES) \ - $(sg_senddiag_SOURCES) $(sg_ses_SOURCES) $(sg_start_SOURCES) \ + $(sg_sat_read_gplog_SOURCES) $(sg_sat_set_features_SOURCES) \ + $(sg_scan_SOURCES) $(sg_senddiag_SOURCES) $(sg_ses_SOURCES) \ + $(sg_ses_microcode_SOURCES) $(sg_start_SOURCES) \ $(sg_stpg_SOURCES) $(sg_sync_SOURCES) $(sg_test_rwbuf_SOURCES) \ $(sg_turs_SOURCES) $(sg_unmap_SOURCES) $(sg_verify_SOURCES) \ $(sg_vpd_SOURCES) $(sg_wr_mode_SOURCES) \ $(sg_write_buffer_SOURCES) $(sg_write_long_SOURCES) \ - $(sg_write_same_SOURCES) $(sg_xcopy_SOURCES) $(sginfo_SOURCES) \ - $(sgm_dd_SOURCES) $(sgp_dd_SOURCES) + $(sg_write_same_SOURCES) $(sg_write_verify_SOURCES) \ + $(sg_xcopy_SOURCES) $(sginfo_SOURCES) $(sgm_dd_SOURCES) \ + $(sgp_dd_SOURCES) DIST_SOURCES = $(sg_compare_and_write_SOURCES) \ $(sg_copy_results_SOURCES) $(sg_dd_SOURCES) \ $(sg_decode_sense_SOURCES) $(sg_emc_trespass_SOURCES) \ @@ -605,14 +633,16 @@ DIST_SOURCES = $(sg_compare_and_write_SOURCES) \ $(sg_reset_SOURCES) $(sg_reset_wp_SOURCES) $(sg_rmsn_SOURCES) \ $(sg_rtpg_SOURCES) $(sg_safte_SOURCES) $(sg_sanitize_SOURCES) \ $(sg_sat_identify_SOURCES) $(sg_sat_phy_event_SOURCES) \ - $(sg_sat_set_features_SOURCES) $(sg_scan_SOURCES) \ - $(sg_senddiag_SOURCES) $(sg_ses_SOURCES) $(sg_start_SOURCES) \ + $(sg_sat_read_gplog_SOURCES) $(sg_sat_set_features_SOURCES) \ + $(sg_scan_SOURCES) $(sg_senddiag_SOURCES) $(sg_ses_SOURCES) \ + $(sg_ses_microcode_SOURCES) $(sg_start_SOURCES) \ $(sg_stpg_SOURCES) $(sg_sync_SOURCES) $(sg_test_rwbuf_SOURCES) \ $(sg_turs_SOURCES) $(sg_unmap_SOURCES) $(sg_verify_SOURCES) \ $(sg_vpd_SOURCES) $(sg_wr_mode_SOURCES) \ $(sg_write_buffer_SOURCES) $(sg_write_long_SOURCES) \ - $(sg_write_same_SOURCES) $(sg_xcopy_SOURCES) $(sginfo_SOURCES) \ - $(sgm_dd_SOURCES) $(sgp_dd_SOURCES) + $(sg_write_same_SOURCES) $(sg_write_verify_SOURCES) \ + $(sg_xcopy_SOURCES) $(sginfo_SOURCES) $(sgm_dd_SOURCES) \ + $(sgp_dd_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ @@ -742,6 +772,7 @@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ +os_cflags = @os_cflags@ os_libs = @os_libs@ pdfdir = @pdfdir@ prefix = @prefix@ @@ -759,9 +790,10 @@ top_srcdir = @top_srcdir@ # For C++/clang testing # -std=<s> can be c99, c11, gnu11, etc. Default is gnu89 (gnu90 is the same) -AM_CFLAGS = -I ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W -# AM_CFLAGS = -I ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W -pedantic -std=c11 -# AM_CFLAGS = -I ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W -pedantic -std=c++11 +# -Wall is no longer all warnings. Add -W (since renamed to -Wextra) for more +AM_CFLAGS = -iquote ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W @os_cflags@ +# AM_CFLAGS = -iquote ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W @os_cflags@ -pedantic -std=c11 +# AM_CFLAGS = -iquote ../include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -W @os_cflags@ -pedantic -std=c++11 sg_dd_SOURCES = sg_dd.c sg_dd_LDADD = ../lib/libsgutils2.la @os_libs@ sg_decode_sense_SOURCES = sg_decode_sense.c @@ -844,6 +876,8 @@ sg_senddiag_SOURCES = sg_senddiag.c sg_senddiag_LDADD = ../lib/libsgutils2.la @os_libs@ sg_ses_SOURCES = sg_ses.c sg_ses_LDADD = ../lib/libsgutils2.la @os_libs@ +sg_ses_microcode_SOURCES = sg_ses_microcode.c +sg_ses_microcode_LDADD = ../lib/libsgutils2.la @os_libs@ sg_start_SOURCES = sg_start.c sg_start_LDADD = ../lib/libsgutils2.la @os_libs@ sg_stpg_SOURCES = sg_stpg.c @@ -878,6 +912,10 @@ sg_rep_zones_SOURCES = sg_rep_zones.c sg_rep_zones_LDADD = ../lib/libsgutils2.la @os_libs@ sg_reset_wp_SOURCES = sg_reset_wp.c sg_reset_wp_LDADD = ../lib/libsgutils2.la @os_libs@ +sg_write_verify_SOURCES = sg_write_verify.c +sg_write_verify_LDADD = ../lib/libsgutils2.la @os_libs@ +sg_sat_read_gplog_SOURCES = sg_sat_read_gplog.c +sg_sat_read_gplog_LDADD = ../lib/libsgutils2.la @os_libs@ all: all-am .SUFFIXES: @@ -1114,6 +1152,10 @@ sg_sat_phy_event$(EXEEXT): $(sg_sat_phy_event_OBJECTS) $(sg_sat_phy_event_DEPEND @rm -f sg_sat_phy_event$(EXEEXT) $(AM_V_CCLD)$(LINK) $(sg_sat_phy_event_OBJECTS) $(sg_sat_phy_event_LDADD) $(LIBS) +sg_sat_read_gplog$(EXEEXT): $(sg_sat_read_gplog_OBJECTS) $(sg_sat_read_gplog_DEPENDENCIES) $(EXTRA_sg_sat_read_gplog_DEPENDENCIES) + @rm -f sg_sat_read_gplog$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(sg_sat_read_gplog_OBJECTS) $(sg_sat_read_gplog_LDADD) $(LIBS) + sg_sat_set_features$(EXEEXT): $(sg_sat_set_features_OBJECTS) $(sg_sat_set_features_DEPENDENCIES) $(EXTRA_sg_sat_set_features_DEPENDENCIES) @rm -f sg_sat_set_features$(EXEEXT) $(AM_V_CCLD)$(LINK) $(sg_sat_set_features_OBJECTS) $(sg_sat_set_features_LDADD) $(LIBS) @@ -1130,6 +1172,10 @@ sg_ses$(EXEEXT): $(sg_ses_OBJECTS) $(sg_ses_DEPENDENCIES) $(EXTRA_sg_ses_DEPENDE @rm -f sg_ses$(EXEEXT) $(AM_V_CCLD)$(LINK) $(sg_ses_OBJECTS) $(sg_ses_LDADD) $(LIBS) +sg_ses_microcode$(EXEEXT): $(sg_ses_microcode_OBJECTS) $(sg_ses_microcode_DEPENDENCIES) $(EXTRA_sg_ses_microcode_DEPENDENCIES) + @rm -f sg_ses_microcode$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(sg_ses_microcode_OBJECTS) $(sg_ses_microcode_LDADD) $(LIBS) + sg_start$(EXEEXT): $(sg_start_OBJECTS) $(sg_start_DEPENDENCIES) $(EXTRA_sg_start_DEPENDENCIES) @rm -f sg_start$(EXEEXT) $(AM_V_CCLD)$(LINK) $(sg_start_OBJECTS) $(sg_start_LDADD) $(LIBS) @@ -1178,6 +1224,10 @@ sg_write_same$(EXEEXT): $(sg_write_same_OBJECTS) $(sg_write_same_DEPENDENCIES) $ @rm -f sg_write_same$(EXEEXT) $(AM_V_CCLD)$(LINK) $(sg_write_same_OBJECTS) $(sg_write_same_LDADD) $(LIBS) +sg_write_verify$(EXEEXT): $(sg_write_verify_OBJECTS) $(sg_write_verify_DEPENDENCIES) $(EXTRA_sg_write_verify_DEPENDENCIES) + @rm -f sg_write_verify$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(sg_write_verify_OBJECTS) $(sg_write_verify_LDADD) $(LIBS) + sg_xcopy$(EXEEXT): $(sg_xcopy_OBJECTS) $(sg_xcopy_DEPENDENCIES) $(EXTRA_sg_xcopy_DEPENDENCIES) @rm -f sg_xcopy$(EXEEXT) $(AM_V_CCLD)$(LINK) $(sg_xcopy_OBJECTS) $(sg_xcopy_LDADD) $(LIBS) @@ -1239,10 +1289,12 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_sanitize.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_sat_identify.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_sat_phy_event.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_sat_read_gplog.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_sat_set_features.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_scan.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_senddiag.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_ses.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_ses_microcode.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_start.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_stpg.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_sync.Po@am__quote@ @@ -1256,6 +1308,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_write_buffer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_write_long.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_write_same.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_write_verify.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_xcopy.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sginfo.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sgm_dd.Po@am__quote@ diff --git a/src/sg_compare_and_write.c b/src/sg_compare_and_write.c index 7a59b74..6fc18ce 100644 --- a/src/sg_compare_and_write.c +++ b/src/sg_compare_and_write.c @@ -51,7 +51,7 @@ #include "sg_cmds_basic.h" #include "sg_pt.h" -static const char * version_str = "1.09 20140516"; +static const char * version_str = "1.09 20140715"; #define DEF_BLOCK_SIZE 512 #define DEF_NUM_BLOCKS (1) @@ -137,8 +137,8 @@ usage() " not given)\n" " --inw=WF|-D WF WF is a file containing a write " "buffer\n" - " --lba=LBA|-l LBA LBA of the first block of the " - "compare and write\n" + " --lba=LBA|-l LBA LBA of the first block to compare " + "and write\n" " --num=NUM|-n NUM number of blocks to " "compare/write (def: 1)\n" " --quiet|-q suppress MISCOMPARE report to " diff --git a/src/sg_copy_results.c b/src/sg_copy_results.c index 127d10f..e27d42e 100644 --- a/src/sg_copy_results.c +++ b/src/sg_copy_results.c @@ -35,7 +35,7 @@ and the optional list identifier passed as the list_id argument. */ -static const char * version_str = "1.9 20140515"; +static const char * version_str = "1.10 20140625"; #define MAX_XFER_LEN 10000 @@ -133,9 +133,9 @@ scsi_failed_segment_details(unsigned char *rcBuff, unsigned int rcBuffLen) } len = (rcBuff[0] << 24) | (rcBuff[1] << 16) | (rcBuff[2] << 8) | rcBuff[3]; - if (len + 3 > rcBuffLen) { - pr2serr(" <<report too long for internal buffer, output " - "truncated\n"); + if (len + 4 > rcBuffLen) { + pr2serr(" <<report len %d > %d too long for internal buffer, output " + "truncated\n", len, rcBuffLen); } if (len < 52) { pr2serr(" <<no segment details, response data length %d\n", len); @@ -154,14 +154,14 @@ scsi_copy_status(unsigned char *rcBuff, unsigned int rcBuffLen) unsigned int len; if (rcBuffLen < 4) { - pr2serr(" <<not enough data to procedd report>>\n"); + pr2serr(" <<not enough data to proceed report>>\n"); return; } len = (rcBuff[0] << 24) | (rcBuff[1] << 16) | (rcBuff[2] << 8) | rcBuff[3]; - if (len > rcBuffLen) { - pr2serr(" <<report too long for internal buffer, output " - "truncated\n"); + if (len + 4 > rcBuffLen) { + pr2serr(" <<report len %d > %d too long for internal buffer, output " + "truncated\n", len, rcBuffLen); } printf("Receive copy results (copy status):\n"); printf(" Held data discarded: %s\n", rcBuff[4] & 0x80 ? "Yes":"No"); @@ -193,9 +193,9 @@ scsi_operating_parameters(unsigned char *rcBuff, unsigned int rcBuffLen) len = (rcBuff[0] << 24) | (rcBuff[1] << 16) | (rcBuff[2] << 8) | rcBuff[3]; - if (len > rcBuffLen) { - pr2serr(" <<report too long for internal buffer, output " - "truncated\n"); + if (len + 4 > rcBuffLen) { + pr2serr(" <<report len %d > %d too long for internal buffer, output " + "truncated\n", len, rcBuffLen); } printf("Receive copy results (report operating parameters):\n"); printf(" Supports no list identifier (SNLID): %s\n", diff --git a/src/sg_dd.c b/src/sg_dd.c index 13e1847..8497a20 100644 --- a/src/sg_dd.c +++ b/src/sg_dd.c @@ -57,8 +57,9 @@ #include "sg_cmds_basic.h" #include "sg_cmds_extra.h" #include "sg_io_linux.h" +#include "sg_unaligned.h" -static const char * version_str = "5.80 20140516"; +static const char * version_str = "5.81 20140917"; #define ME "sg_dd: " @@ -416,7 +417,7 @@ usage() static int scsi_read_capacity(int sg_fd, int64_t * num_sect, int * sect_sz) { - int k, res; + int res; unsigned int ui; unsigned char rcBuff[RCAP16_REPLY_LEN]; int verb; @@ -434,20 +435,14 @@ scsi_read_capacity(int sg_fd, int64_t * num_sect, int * sect_sz) verb); if (0 != res) return res; - for (k = 0, ls = 0; k < 8; ++k) { - ls <<= 8; - ls |= rcBuff[k]; - } + ls = (int64_t)sg_get_unaligned_be64(rcBuff); *num_sect = ls + 1; - *sect_sz = (rcBuff[8] << 24) | (rcBuff[9] << 16) | - (rcBuff[10] << 8) | rcBuff[11]; + *sect_sz = (int)sg_get_unaligned_be32(rcBuff + 8); } else { - ui = ((rcBuff[0] << 24) | (rcBuff[1] << 16) | (rcBuff[2] << 8) | - rcBuff[3]); + ui = sg_get_unaligned_be32(rcBuff); /* take care not to sign extend values > 0x7fffffff */ *num_sect = (int64_t)ui + 1; - *sect_sz = (rcBuff[4] << 24) | (rcBuff[5] << 16) | - (rcBuff[6] << 8) | rcBuff[7]; + *sect_sz = (int)sg_get_unaligned_be32(rcBuff + 4); } if (verbose) fprintf(stderr, " number of blocks=%" PRId64 " [0x%" PRIx64 @@ -522,9 +517,7 @@ sg_build_scsi_cdb(unsigned char * cdbp, int cdb_sz, unsigned int blocks, sz_ind = 0; cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] : rd_opcode[sz_ind]); - cdbp[1] = (unsigned char)((start_block >> 16) & 0x1f); - cdbp[2] = (unsigned char)((start_block >> 8) & 0xff); - cdbp[3] = (unsigned char)(start_block & 0xff); + sg_put_unaligned_be24(start_block, cdbp + 1); cdbp[4] = (256 == blocks) ? 0 : (unsigned char)blocks; if (blocks > 256) { fprintf(stderr, ME "for 6 byte commands, maximum number of " @@ -546,12 +539,8 @@ sg_build_scsi_cdb(unsigned char * cdbp, int cdb_sz, unsigned int blocks, sz_ind = 1; cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] : rd_opcode[sz_ind]); - cdbp[2] = (unsigned char)((start_block >> 24) & 0xff); - cdbp[3] = (unsigned char)((start_block >> 16) & 0xff); - cdbp[4] = (unsigned char)((start_block >> 8) & 0xff); - cdbp[5] = (unsigned char)(start_block & 0xff); - cdbp[7] = (unsigned char)((blocks >> 8) & 0xff); - cdbp[8] = (unsigned char)(blocks & 0xff); + sg_put_unaligned_be32(start_block, cdbp + 2); + sg_put_unaligned_be16(blocks, cdbp + 7); if (blocks & (~0xffff)) { fprintf(stderr, ME "for 10 byte commands, maximum number of " "blocks is %d\n", 0xffff); @@ -562,31 +551,15 @@ sg_build_scsi_cdb(unsigned char * cdbp, int cdb_sz, unsigned int blocks, sz_ind = 2; cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] : rd_opcode[sz_ind]); - cdbp[2] = (unsigned char)((start_block >> 24) & 0xff); - cdbp[3] = (unsigned char)((start_block >> 16) & 0xff); - cdbp[4] = (unsigned char)((start_block >> 8) & 0xff); - cdbp[5] = (unsigned char)(start_block & 0xff); - cdbp[6] = (unsigned char)((blocks >> 24) & 0xff); - cdbp[7] = (unsigned char)((blocks >> 16) & 0xff); - cdbp[8] = (unsigned char)((blocks >> 8) & 0xff); - cdbp[9] = (unsigned char)(blocks & 0xff); + sg_put_unaligned_be32(start_block, cdbp + 2); + sg_put_unaligned_be32(blocks, cdbp + 6); break; case 16: sz_ind = 3; cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] : rd_opcode[sz_ind]); - cdbp[2] = (unsigned char)((start_block >> 56) & 0xff); - cdbp[3] = (unsigned char)((start_block >> 48) & 0xff); - cdbp[4] = (unsigned char)((start_block >> 40) & 0xff); - cdbp[5] = (unsigned char)((start_block >> 32) & 0xff); - cdbp[6] = (unsigned char)((start_block >> 24) & 0xff); - cdbp[7] = (unsigned char)((start_block >> 16) & 0xff); - cdbp[8] = (unsigned char)((start_block >> 8) & 0xff); - cdbp[9] = (unsigned char)(start_block & 0xff); - cdbp[10] = (unsigned char)((blocks >> 24) & 0xff); - cdbp[11] = (unsigned char)((blocks >> 16) & 0xff); - cdbp[12] = (unsigned char)((blocks >> 8) & 0xff); - cdbp[13] = (unsigned char)(blocks & 0xff); + sg_put_unaligned_be64(start_block, cdbp + 2); + sg_put_unaligned_be32(blocks, cdbp + 10); break; default: fprintf(stderr, ME "expected cdb size of 6, 10, 12, or 16 but got" diff --git a/src/sg_format.c b/src/sg_format.c index e84846e..bbb28bf 100644 --- a/src/sg_format.c +++ b/src/sg_format.c @@ -13,23 +13,8 @@ * the Free Software Foundation; either version 2, or (at your option) * any later version. * - * http://www.t10.org/scsi-3.htm - * http://www.tldp.org/HOWTO/SCSI-Generic-HOWTO - * - * - * List of some (older) disk manufacturers' block counts. - * These are not needed in newer disks which will automatically use - * the manufacturers' recommended block count if a count of -1 is given. - * Inquiry Block Count (@512 byte blocks) - * ST150150N 8388315 - * IBM_DCHS04F 8888543 - * IBM_DGHS09Y 17916240 - * ST336704FC 71132960 - * ST318304FC 35145034 (Factory spec is 35885167 sectors) - * ST336605FC ??? - * ST336753FC 71132960 (Factory spec is 71687372 sectors) - * and a newer one: - * ST33000650SS 5860533168 (3 TB SAS disk) + * See http://www.t10.org for relevant standards and drafts. The most recent + * draft is SBC-4 revision 2. */ #include <stdio.h> @@ -47,13 +32,10 @@ #include "sg_cmds_basic.h" #include "sg_cmds_extra.h" -static const char * version_str = "1.26 20140516"; +static const char * version_str = "1.30 20141006"; -#define RW_ERROR_RECOVERY_PAGE 1 /* every disk should have one */ -#define FORMAT_DEV_PAGE 3 /* Format Device Mode Page [now obsolete] */ -#define CONTROL_MODE_PAGE 0xa /* alternative page all devices have?? */ -#define THIS_MPAGE_EXISTS RW_ERROR_RECOVERY_PAGE +#define RW_ERROR_RECOVERY_PAGE 1 /* can give alternate with --mode=MP */ #define SHORT_TIMEOUT 20 /* 20 seconds unless --wait given */ #define FORMAT_TIMEOUT (20 * 3600) /* 20 hours ! */ @@ -88,6 +70,7 @@ static struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"ip_def", no_argument, 0, 'I'}, {"long", no_argument, 0, 'l'}, + {"mode", required_argument, 0, 'M'}, {"pinfo", no_argument, 0, 'p'}, {"pfu", required_argument, 0, 'P'}, {"pie", required_argument, 0, 'q'}, @@ -111,19 +94,19 @@ usage() "[--early]\n" " [--fmtpinfo=FPI] [--format] [--help] " "[--ip_def] [--long]\n" - " [--pfu=PFU] [--pie=PIE] [--pinfo] " - "[--poll=PT] [--resize]\n" - " [--rto_req] [--security] [--six] " - "[--size=SIZE] [--verbose]\n" - " [--version] [--wait] DEVICE\n" + " [--mode=MP] [--pfu=PFU] [--pie=PIE] " + "[--pinfo] [--poll=PT]\n" + " [--resize] [--rto_req] [--security] " + "[--six] [--size=SIZE]\n" + " [--verbose] [--version] [--wait] DEVICE\n" " where:\n" " --cmplst=0|1\n" " -C 0|1 sets CMPLST bit in format cdb " "(default: 1)\n" - " --count=COUNT|-c COUNT number of blocks to " - "report after format or\n" - " resize. With format " - "defaults to same as current\n" + " --count=COUNT|-c COUNT number of blocks to report " + "after format or\n" + " resize. Format default is " + "same as current\n" " --dcrt|-D disable certification (doesn't " "verify media)\n" " --early|-e exit once format started (user can " @@ -132,12 +115,14 @@ usage() "(default: 0)\n" " --format|-F format unit (default: report current " "count and size)\n" + " use thrice for FORMAT UNIT command " + "only\n" " --help|-h prints out this usage message\n" " --ip_def|-I initialization pattern: default\n" " --long|-l allow for 64 bit lbas (default: assume " "32 bit lbas)\n" - " --pfu=PFU|-P PFU Protection Field Usage value " - "(default: 0)\n" + " --mode=MP|-M MP mode page (def: 1 -> RW error " + "recovery mpage)\n" " --pie=PIE|-q PIE Protection Information Exponent " "(default: 0)\n" " --pinfo|-p set upper bit of FMTPINFO field\n" @@ -156,11 +141,12 @@ usage() " --six|-6 use 6 byte MODE SENSE/SELECT to probe " "disk\n" " (def: use 10 byte MODE SENSE/SELECT)\n" - " --size=SIZE|-s SIZE bytes per block, defaults to " - "DEVICE's current\n" - " block size. Only needed to " - "change current block\n" - " size\n" + " --size=SIZE|-s SIZE bytes per logical block, " + "defaults to DEVICE's\n" + " current logical block size. Only " + "needed to\n" + " change current logical block " + "size\n" " --verbose|-v increase verbosity\n" " --version|-V print version details and exit\n" " --wait|-w format command waits until format " @@ -317,6 +303,180 @@ scsi_format(int fd, int fmtpinfo, int cmplst, int pf_usage, int immed, return 0; } +#define VPD_DEVICE_ID 0x83 +#define VPD_ASSOC_LU 0 +#define VPD_ASSOC_TPORT 1 +#define TPROTO_ISCSI 5 + +static char * +get_lu_name(const unsigned char * ucp, int u_len, char * b, int b_len) +{ + int len, off, sns_dlen, dlen, k; + unsigned char u_sns[512]; + char * cp; + + len = u_len - 4; + ucp += 4; + off = -1; + if (0 == sg_vpd_dev_id_iter(ucp, len, &off, VPD_ASSOC_LU, + 8 /* SCSI name string (sns) */, + 3 /* UTF-8 */)) { + sns_dlen = ucp[off + 3]; + memcpy(u_sns, ucp + off + 4, sns_dlen); + /* now want to check if this is iSCSI */ + off = -1; + if (0 == sg_vpd_dev_id_iter(ucp, len, &off, VPD_ASSOC_TPORT, + 8 /* SCSI name string (sns) */, + 3 /* UTF-8 */)) { + if ((0x80 & ucp[1]) && + (TPROTO_ISCSI == (ucp[0] >> 4))) { + snprintf(b, b_len, "%.*s", sns_dlen, u_sns); + return b; + } + } + } else + sns_dlen = 0; + if (0 == sg_vpd_dev_id_iter(ucp, len, &off, VPD_ASSOC_LU, + 3 /* NAA */, 1 /* binary */)) { + dlen = ucp[off + 3]; + if (! ((8 == dlen) || (16 ==dlen))) + return b; + cp = b; + for (k = 0; ((k < dlen) && (b_len > 1)); ++k) { + snprintf(cp, b_len, "%02x", ucp[off + 4 + k]); + cp += 2; + b_len -= 2; + } + } else if (0 == sg_vpd_dev_id_iter(ucp, len, &off, VPD_ASSOC_LU, + 2 /* EUI */, 1 /* binary */)) { + dlen = ucp[off + 3]; + if (! ((8 == dlen) || (12 == dlen) || (16 ==dlen))) + return b; + cp = b; + for (k = 0; ((k < dlen) && (b_len > 1)); ++k) { + snprintf(cp, b_len, "%02x", ucp[off + 4 + k]); + cp += 2; + b_len -= 2; + } + } else if (sns_dlen > 0) + snprintf(b, b_len, "%.*s", sns_dlen, u_sns); + return b; +} + +#define SAFE_STD_INQ_RESP_LEN 36 +#define VPD_SUPPORTED_VPDS 0x0 +#define VPD_UNIT_SERIAL_NUM 0x80 +#define VPD_DEVICE_ID 0x83 + +static int +print_dev_id(int fd, unsigned char * sinq_resp, int max_rlen, int verbose) +{ + int res, k, n, verb, pdt, has_sn, has_di; + unsigned char b[256]; + char a[256]; + char pdt_name[64]; + + verb = (verbose > 1) ? verbose - 1 : 0; + memset(sinq_resp, 0, max_rlen); + res = sg_ll_inquiry(fd, 0, 0 /* evpd */, 0 /* pg_op */, b, + SAFE_STD_INQ_RESP_LEN, 1, verb); + if (res) + return res; + n = b[4] + 5; + if (n > SAFE_STD_INQ_RESP_LEN) + n = SAFE_STD_INQ_RESP_LEN; + memcpy(sinq_resp, b, (n < max_rlen) ? n : max_rlen); + if (n == SAFE_STD_INQ_RESP_LEN) { + pdt = b[0] & 0x1f; + printf(" %.8s %.16s %.4s peripheral_type: %s [0x%x]\n", + (const char *)(b + 8), (const char *)(b + 16), + (const char *)(b + 32), + sg_get_pdt_str(pdt, sizeof(pdt_name), pdt_name), pdt); + if (verbose) + printf(" PROTECT=%d\n", !!(b[5] & 1)); + if (b[5] & 1) + printf(" << supports protection information>>" + "\n"); + } else { + fprintf(stderr, "Short INQUIRY response: %d bytes, expect at " + "least " "36\n", n); + return SG_LIB_CAT_OTHER; + } + res = sg_ll_inquiry(fd, 0, 1 /* evpd */, VPD_SUPPORTED_VPDS, b, + SAFE_STD_INQ_RESP_LEN, 1, verb); + if (res) { + if (verbose) + fprintf(stderr, "VPD_SUPPORTED_VPDS gave res=%d\n", + res); + return 0; + } + if (VPD_SUPPORTED_VPDS != b[1]) { + if (verbose) + fprintf(stderr, "VPD_SUPPORTED_VPDS corrupted\n"); + return 0; + } + n = (b[2] << 8) + b[3]; + if (n > (SAFE_STD_INQ_RESP_LEN - 4)) + n = (SAFE_STD_INQ_RESP_LEN - 4); + for (k = 0, has_sn = 0, has_di = 0; k < n; ++k) { + if (VPD_UNIT_SERIAL_NUM == b[4 + k]) { + if (has_di) { + if (verbose) + fprintf(stderr, "VPD_SUPPORTED_VPDS " + "dis-ordered\n"); + return 0; + } + ++has_sn; + } else if (VPD_DEVICE_ID == b[4 + k]) { + ++has_di; + break; + } + } + if (has_sn) { + res = sg_ll_inquiry(fd, 0, 1 /* evpd */, VPD_UNIT_SERIAL_NUM, + b, sizeof(b), 1, verb); + if (res) { + if (verbose) + fprintf(stderr, "VPD_UNIT_SERIAL_NUM gave " + "res=%d\n", res); + return 0; + } + if (VPD_UNIT_SERIAL_NUM != b[1]) { + if (verbose) + fprintf(stderr, "VPD_UNIT_SERIAL_NUM " + "corrupted\n"); + return 0; + } + n = (b[2] << 8) + b[3]; + if (n > (int)(sizeof(b) - 4)) + n = (sizeof(b) - 4); + printf(" Unit serial number: %.*s\n", n, + (const char *)(b + 4)); + } + if (has_di) { + res = sg_ll_inquiry(fd, 0, 1 /* evpd */, VPD_DEVICE_ID, b, + sizeof(b), 1, verb); + if (res) { + if (verbose) + fprintf(stderr, "VPD_DEVICE_ID gave res=%d\n", + res); + return 0; + } + if (VPD_DEVICE_ID != b[1]) { + if (verbose) + fprintf(stderr, "VPD_DEVICE_ID corrupted\n"); + return 0; + } + n = (b[2] << 8) + b[3]; + if (n > (int)(sizeof(b) - 4)) + n = (sizeof(b) - 4); + n = strlen(get_lu_name(b, n + 4, a, sizeof(a))); + if (n > 0) + printf(" LU name: %.*s\n", n, a); + } + return 0; +} + #define RCAP_REPLY_LEN 32 /* Returns block size or -2 if do_16==0 and the number of blocks is too @@ -397,9 +557,9 @@ print_read_cap(int fd, int do_16, int verbose) int main(int argc, char **argv) { - const int mode_page = THIS_MPAGE_EXISTS; /* hopefully */ + int mode_page = RW_ERROR_RECOVERY_PAGE; int fd, res, calc_len, bd_len, dev_specific_param; - int offset, j, bd_blk_len, prob, len; + int offset, j, bd_blk_len, prob, len, pdt; uint64_t ull; int64_t blk_count = 0; /* -c value */ int blk_size = 0; /* -s value */ @@ -422,16 +582,15 @@ main(int argc, char **argv) int do_si = 0; int early = 0; const char * device_name = NULL; - char pdt_name[64]; char b[80]; - struct sg_simple_inquiry_resp inq_out; + unsigned char inq_resp[SAFE_STD_INQ_RESP_LEN]; int ret = 0; while (1) { int option_index = 0; int c; - c = getopt_long(argc, argv, "c:C:Def:FhIlpP:q:rRs:SvVwx:6", + c = getopt_long(argc, argv, "c:C:Def:FhIlM:pP:q:rRs:SvVwx:6", long_options, &option_index); if (c == -1) break; @@ -473,7 +632,7 @@ main(int argc, char **argv) } break; case 'F': - format = 1; + ++format; break; case 'h': usage(); @@ -485,6 +644,14 @@ main(int argc, char **argv) long_lba = 1; do_rcap16 = 1; break; + case 'M': + mode_page = sg_get_num(optarg); + if ((mode_page < 0) || ( mode_page > 62)) { + fprintf(stderr, "bad argument to '--mode', " + "accepts 0 to 62 inclusive\n"); + return SG_LIB_SYNTAX_ERROR; + } + break; case 'p': pinfo = 1; break; @@ -603,25 +770,14 @@ main(int argc, char **argv) return SG_LIB_FILE_ERROR; } - if (sg_simple_inquiry(fd, &inq_out, 1, verbose)) { - fprintf(stderr, "%s doesn't respond to a SCSI INQUIRY\n", - device_name); - ret = SG_LIB_CAT_OTHER; - goto out; - } - printf(" %.8s %.16s %.4s peripheral_type: %s [0x%x]\n", - inq_out.vendor, inq_out.product, inq_out.revision, - sg_get_pdt_str(inq_out.peripheral_type, sizeof(pdt_name), - pdt_name), - inq_out.peripheral_type); - if (verbose) - printf(" PROTECT=%d\n", !!(inq_out.byte_5 & 1)); - if (inq_out.byte_5 & 1) - printf(" << supports protection information>>\n"); - - if ((0 != inq_out.peripheral_type) && - (7 != inq_out.peripheral_type) && - (0xe != inq_out.peripheral_type)) { + if (format > 2) + goto format_only; + + ret = print_dev_id(fd, inq_resp, sizeof(inq_resp), verbose); + if (ret) + goto out; + pdt = 0x1f & inq_resp[0]; + if ((0 != pdt) && (7 != pdt) && (0xe != pdt)) { fprintf(stderr, "This format is only defined for disks " "(using SBC-2 or RBC) and MO media\n"); ret = SG_LIB_CAT_MALFORMED; @@ -835,7 +991,8 @@ again_with_long_lba: goto out; } - if (format) + if (format) { +format_only: #if 1 printf("\nA FORMAT will commence in 15 seconds\n"); printf(" ALL data on %s will be DESTROYED\n", device_name); @@ -861,6 +1018,7 @@ again_with_long_lba: #else fprintf(stderr, "FORMAT ignored, testing\n"); #endif + } out: res = sg_cmds_close_device(fd); diff --git a/src/sg_ident.c b/src/sg_ident.c index cfd83cc..af21784 100644 --- a/src/sg_ident.c +++ b/src/sg_ident.c @@ -27,7 +27,7 @@ * DEVICE IDENTIFIER and SET DEVICE IDENTIFIER prior to spc4r07. */ -static const char * version_str = "1.12 20140516"; +static const char * version_str = "1.13 20140904"; #define ME "sg_ident: " @@ -95,7 +95,8 @@ static void usage() " --clear|-C clear (set to zero length) identifying " "information\n" " --help|-h print out usage message\n" - " --itype=IT|-i IT specify information type\n" + " --itype=IT|-i IT specify identifying information type " + "(def: 0)\n" " --raw|-r output identifying information to " "stdout\n" " --set|-S invoke set identifying information with " diff --git a/src/sg_inq.c b/src/sg_inq.c index 3d83ba4..ca3d539 100644 --- a/src/sg_inq.c +++ b/src/sg_inq.c @@ -41,7 +41,7 @@ #include "sg_cmds_basic.h" #include "sg_pt.h" -static const char * version_str = "1.39 20140527"; /* SPC-4 rev 37 */ +static const char * version_str = "1.43 20141107"; /* SPC-4 rev 37 */ /* INQUIRY notes: * It is recommended that the initial allocation length given to a @@ -935,7 +935,7 @@ static int pt_inquiry(int sg_fd, int evpd, int pg_op, void * resp, int mx_resp_len, int * residp, int noisy, int verbose) { - int res, ret, k, sense_cat; + int res, ret, k, sense_cat, resid; unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; unsigned char * up; @@ -970,8 +970,9 @@ pt_inquiry(int sg_fd, int evpd, int pg_op, void * resp, int mx_resp_len, res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, "inquiry", res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); + resid = get_scsi_pt_resid(ptvp); if (residp) - *residp = get_scsi_pt_resid(ptvp); + *residp = resid; destruct_scsi_pt_obj(ptvp); if (-1 == ret) ; @@ -992,6 +993,15 @@ pt_inquiry(int sg_fd, int evpd, int pg_op, void * resp, int mx_resp_len, } else ret = 0; + if (resid > 0) { + if (resid > mx_resp_len) { + pr2serr("INQUIRY resid (%d) should never exceed requested " + "len=%d\n", resid, mx_resp_len); + return ret ? ret : SG_LIB_CAT_MALFORMED; + } + /* zero unfilled section of response buffer */ + memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid); + } return ret; } @@ -1510,7 +1520,7 @@ decode_dev_ids(const char * leadin, unsigned char * buff, int len, int do_hex) switch (desig_type) { case 0: /* vendor specific */ k = 0; - if ((1 == c_set) || (2 == c_set)) { /* ASCII or UTF-8 */ + if ((2 == c_set) || (3 == c_set)) { /* ASCII or UTF-8 */ for (k = 0; (k < i_len) && isprint(ip[k]); ++k) ; if (k >= i_len) @@ -1824,6 +1834,8 @@ export_dev_ids(unsigned char * buff, int len, int verbose) } switch (desig_type) { case 0: /* vendor specific */ + if (i_len > 128) + break; printf("SCSI_IDENT_%s_VENDOR=", assoc_str); if ((2 == c_set) || (3 == c_set)) { /* ASCII or UTF-8 */ k = encode_whitespaces(ip, i_len); @@ -1962,6 +1974,16 @@ export_dev_ids(unsigned char * buff, int len, int verbose) } break; } + if (strncmp((const char *)ip, "eui.", 4) || + strncmp((const char *)ip, "naa.", 4) || + strncmp((const char *)ip, "iqn.", 4)) { + if (verbose) { + pr2serr(" << expected name string prefix>>\n"); + dStrHexErr((const char *)ip, i_len, -1); + } + break; + } + printf("SCSI_IDENT_%s_NAME=%.*s\n", assoc_str, i_len, (const char *)ip); break; @@ -2274,11 +2296,15 @@ decode_power_condition(unsigned char * buff, int len, int do_hex) (buff[16] << 8) + buff[17]); } +/* VPD_BLOCK_LIMITS sbc */ +/* Sequential access device characteristics, ssc+smc */ +/* OSD information, osd */ static void decode_b0_vpd(unsigned char * buff, int len, int do_hex) { - int pdt; + int pdt, m; unsigned int u; + uint64_t mwsl; if (do_hex) { dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1); @@ -2325,6 +2351,27 @@ decode_b0_vpd(unsigned char * buff, int len, int do_hex) (buff[33] << 16) | (buff[34] << 8) | buff[35]; printf(" Unmap granularity alignment: %u\n", u); } + if (len > 43) { /* added in sbc3r26 */ + mwsl = 0; + for (m = 0; m < 8; ++m) { + if (m > 0) + mwsl <<= 8; + mwsl |= buff[36 + m]; + } + printf(" Maximum write same length: 0x%" PRIx64 " blocks\n", + mwsl); + } + if (len > 44) { /* added in sbc4r02 */ + u = ((unsigned int)buff[44] << 24) | (buff[45] << 16) | + (buff[46] << 8) | buff[47]; + printf(" Maximum atomic transfer length: %u\n", u); + u = ((unsigned int)buff[48] << 24) | (buff[49] << 16) | + (buff[50] << 8) | buff[51]; + printf(" Atomic alignment: %u\n", u); + u = ((unsigned int)buff[52] << 24) | (buff[53] << 16) | + (buff[54] << 8) | buff[55]; + printf(" Atomic transfer length granularity: %u\n", u); + } break; case PDT_TAPE: case PDT_MCHANGER: printf(" WORM=%d\n", !!(buff[4] & 0x1)); @@ -3056,7 +3103,8 @@ cmddt_process(int sg_fd, const struct opts_t * op) "given standard INQUIRY response, stop\n", k); break; } - } + } else if (SG_LIB_CAT_ILLEGAL_REQ == res) + break; else { pr2serr("CmdDt INQUIRY on opcode=0x%.2x: failed\n", k); break; @@ -3117,8 +3165,7 @@ cmddt_process(int sg_fd, const struct opts_t * op) } else printf(" Support field: %s\n", desc_p); } - } - else { + } else if (SG_LIB_CAT_ILLEGAL_REQ != res) { if (! op->do_raw) { printf("CmdDt INQUIRY, opcode=0x%.2x: [", op->page_num); sg_get_opcode_name((unsigned char)op->page_num, 0, diff --git a/src/sg_logs.c b/src/sg_logs.c index 81fd8e9..3bcf1fb 100644 --- a/src/sg_logs.c +++ b/src/sg_logs.c @@ -28,7 +28,7 @@ #include "sg_cmds_basic.h" #include "sg_pt.h" /* needed for scsi_pt_win32_direct() */ -static const char * version_str = "1.24 20140523"; /* spc4r37 + sbc4r01 */ +static const char * version_str = "1.26 20141007"; /* spc4r37 + sbc4r02 */ #define MX_ALLOC_LEN (0xfffc) #define SHORT_RESP_LEN 128 @@ -2555,8 +2555,8 @@ show_sas_port_param(unsigned char * ucp, int param_len, printf(" sas_addr=0x%" PRIx64 "\n", ull); } else { t = ((0x70 & vcp[4]) >> 4); - /* attached device type. In SAS-1.1 case 2 was an edge expander; - * in SAS-2 case 3 is marked as obsolete. */ + /* attached SAS device type. In SAS-1.1 case 2 was an edge + * expander; in SAS-2 case 3 is marked as obsolete. */ switch (t) { case 0: snprintf(s, sz, "no device attached"); break; case 1: snprintf(s, sz, "SAS or SATA device"); break; @@ -2564,7 +2564,8 @@ show_sas_port_param(unsigned char * ucp, int param_len, case 3: snprintf(s, sz, "expander device (fanout)"); break; default: snprintf(s, sz, "reserved [%d]", t); break; } - printf(" attached device type: %s\n", s); + /* the word 'SAS' in following added in spl4r01 */ + printf(" attached SAS device type: %s\n", s); t = 0xf & vcp[4]; switch (t) { case 0: snprintf(s, sz, "unknown"); break; @@ -4342,7 +4343,7 @@ static void show_tape_diag_data_page(unsigned char * resp, int len, const struct opts_t * op) { - int num, pl, pc, pcb; + int k, num, pl, pc, pcb; unsigned int v; unsigned char * ucp; char str[PCB_STR_LEN]; @@ -4389,12 +4390,30 @@ show_tape_diag_data_page(unsigned char * resp, int len, printf(" Hours since last clean: %u\n", v); printf(" Operation code: 0x%x\n", ucp[28]); printf(" Service action: 0x%x\n", ucp[29] & 0xf); - printf(" Medium id number (in hex):\n"); + // Check Medium id number for all zeros // ssc4r03.pdf does not define this field, why? xxxxxx - dStrHex((const char *)(ucp + 32), 32, 0); + for (k = 32; k < 64; ++k) { + if(ucp[k]) + break; + } + if (64 == k) + printf(" Medium id number is 32 bytes of zero\n"); + else { + printf(" Medium id number (in hex):\n"); + dStrHex((const char *)(ucp + 32), 32, 0); + } printf(" Timestamp origin: 0x%x\n", ucp[64] & 0xf); - printf(" Timestamp:\n"); - dStrHex((const char *)(ucp + 66), 6, 1); + // Check Timestamp for all zeros + for (k = 66; k < 72; ++k) { + if(ucp[k]) + break; + } + if (72 == k) + printf(" Timestamp is all zeros:\n"); + else { + printf(" Timestamp:\n"); + dStrHex((const char *)(ucp + 66), 6, 1); + } if (pl > 72) { printf(" Vendor specific:\n"); dStrHex((const char *)(ucp + 72), pl - 72, 0); diff --git a/src/sg_modes.c b/src/sg_modes.c index 70335fb..289af99 100644 --- a/src/sg_modes.c +++ b/src/sg_modes.c @@ -15,6 +15,7 @@ #include <fcntl.h> #include <stdio.h> #include <stdlib.h> +#include <stdarg.h> #include <string.h> #include <ctype.h> #include <getopt.h> @@ -25,7 +26,7 @@ #include "sg_lib.h" #include "sg_cmds_basic.h" -static const char * version_str = "1.43 20140514"; +static const char * version_str = "1.44 20140708"; #define DEF_ALLOC_LEN (1024 * 4) #define DEF_6_ALLOC_LEN 252 @@ -84,6 +85,28 @@ struct opts_t { int opt_new; }; + +#ifdef __GNUC__ +static int pr2serr(const char * fmt, ...) + __attribute__ ((format (printf, 1, 2))); +#else +static int pr2serr(const char * fmt, ...); +#endif + + +static int +pr2serr(const char * fmt, ...) +{ + va_list args; + int n; + + va_start(args, fmt); + n = vfprintf(stderr, fmt, args); + va_end(args); + return n; +} + + static void usage() { @@ -212,7 +235,7 @@ process_cl_new(struct opts_t * op, int argc, char * argv[]) case 'c': n = sg_get_num(optarg); if ((n < 0) || (n > 3)) { - fprintf(stderr, "bad argument to '--control='\n"); + pr2serr("bad argument to '--control='\n"); usage(); return SG_LIB_SYNTAX_ERROR; } @@ -246,7 +269,7 @@ process_cl_new(struct opts_t * op, int argc, char * argv[]) case 'm': n = sg_get_num(optarg); if ((n < 0) || (n > 65535)) { - fprintf(stderr, "bad argument to '--maxlen='\n"); + pr2serr("bad argument to '--maxlen='\n"); usage(); return SG_LIB_SYNTAX_ERROR; } @@ -261,15 +284,14 @@ process_cl_new(struct opts_t * op, int argc, char * argv[]) cp = strchr(optarg, ','); n = sg_get_num_nomult(optarg); if ((n < 0) || (n > 63)) { - fprintf(stderr, "Bad argument to '--page='\n"); + pr2serr("Bad argument to '--page='\n"); usage(); return SG_LIB_SYNTAX_ERROR; } if (cp) { nn = sg_get_num_nomult(cp + 1); if ((nn < 0) || (nn > 255)) { - fprintf(stderr, "Bad second value in argument to " - "'--page='\n"); + pr2serr("Bad second value in argument to '--page='\n"); usage(); return SG_LIB_SYNTAX_ERROR; } @@ -295,7 +317,7 @@ process_cl_new(struct opts_t * op, int argc, char * argv[]) ++op->do_version; break; default: - fprintf(stderr, "unrecognised option code %c [0x%x]\n", c, c); + pr2serr("unrecognised option code %c [0x%x]\n", c, c); if (op->do_help) break; usage(); @@ -309,7 +331,7 @@ process_cl_new(struct opts_t * op, int argc, char * argv[]) } if (optind < argc) { for (; optind < argc; ++optind) - fprintf(stderr, "Unexpected extra argument: %s\n", + pr2serr("Unexpected extra argument: %s\n", argv[optind]); usage(); return SG_LIB_SYNTAX_ERROR; @@ -395,7 +417,7 @@ process_cl_old(struct opts_t * op, int argc, char * argv[]) if (0 == strncmp("c=", cp, 2)) { num = sscanf(cp + 2, "%x", &u); if ((1 != num) || (u > 3)) { - fprintf(stderr, "Bad page control after 'c=' option\n"); + pr2serr("Bad page control after 'c=' option\n"); usage_old(); return SG_LIB_SYNTAX_ERROR; } @@ -403,7 +425,7 @@ process_cl_old(struct opts_t * op, int argc, char * argv[]) } else if (0 == strncmp("m=", cp, 2)) { num = sscanf(cp + 2, "%d", &n); if ((1 != num) || (n < 0) || (n > 65535)) { - fprintf(stderr, "Bad argument after 'm=' option\n"); + pr2serr("Bad argument after 'm=' option\n"); usage_old(); return SG_LIB_SYNTAX_ERROR; } @@ -412,16 +434,14 @@ process_cl_old(struct opts_t * op, int argc, char * argv[]) if (NULL == strchr(cp + 2, ',')) { num = sscanf(cp + 2, "%x", &u); if ((1 != num) || (u > 63)) { - fprintf(stderr, "Bad page code value after 'p=' " - "option\n"); + pr2serr("Bad page code value after 'p=' option\n"); usage_old(); return SG_LIB_SYNTAX_ERROR; } op->pg_code = u; } else if (2 == sscanf(cp + 2, "%x,%x", &u, &uu)) { if (uu > 255) { - fprintf(stderr, "Bad sub page code value after 'p=' " - "option\n"); + pr2serr("Bad subpage code value after 'p=' option\n"); usage_old(); return SG_LIB_SYNTAX_ERROR; } @@ -429,16 +449,15 @@ process_cl_old(struct opts_t * op, int argc, char * argv[]) op->subpg_code = uu; op->subpg_code_set = 1; } else { - fprintf(stderr, "Bad page code, subpage code sequence " - "after 'p=' option\n"); + pr2serr("Bad page code, subpage code sequence after 'p=' " + "option\n"); usage_old(); return SG_LIB_SYNTAX_ERROR; } } else if (0 == strncmp("subp=", cp, 5)) { num = sscanf(cp + 5, "%x", &u); if ((1 != num) || (u > 255)) { - fprintf(stderr, "Bad sub page code after 'subp=' " - "option\n"); + pr2serr("Bad sub page code after 'subp=' option\n"); usage_old(); return SG_LIB_SYNTAX_ERROR; } @@ -449,15 +468,15 @@ process_cl_old(struct opts_t * op, int argc, char * argv[]) } else if (0 == strncmp("-old", cp, 4)) ; else if (jmp_out) { - fprintf(stderr, "Unrecognized option: %s\n", cp); + pr2serr("Unrecognized option: %s\n", cp); usage_old(); return SG_LIB_SYNTAX_ERROR; } } else if (0 == op->device_name) op->device_name = cp; else { - fprintf(stderr, "too many arguments, got: %s, not expecting: " - "%s\n", op->device_name, cp); + pr2serr("too many arguments, got: %s, not expecting: %s\n", + op->device_name, cp); usage_old(); return SG_LIB_SYNTAX_ERROR; } @@ -868,22 +887,22 @@ examine_pages(int sg_fd, int inq_pdt, int inq_byte6, res = sg_ll_mode_sense6(sg_fd, 0, 0, k, 0, rbuf, mresp_len, 1, op->do_verbose); if (SG_LIB_CAT_INVALID_OP == res) { - fprintf(stderr, ">>>>>> try again without the '-6' " - "switch for a 10 byte MODE SENSE command\n"); + pr2serr(">>>>>> try again without the '-6' switch for a 10 " + "byte MODE SENSE command\n"); return res; } else if (SG_LIB_CAT_NOT_READY == res) { - fprintf(stderr, "MODE SENSE (6) failed, device not ready\n"); + pr2serr("MODE SENSE (6) failed, device not ready\n"); return res; } } else { res = sg_ll_mode_sense10(sg_fd, 0, 0, 0, k, 0, rbuf, mresp_len, 1, op->do_verbose); if (SG_LIB_CAT_INVALID_OP == res) { - fprintf(stderr, ">>>>>> try again with a '-6' " - "switch for a 6 byte MODE SENSE command\n"); + pr2serr(">>>>>> try again with a '-6' switch for a 6 byte " + "MODE SENSE command\n"); return res; } else if (SG_LIB_CAT_NOT_READY == res) { - fprintf(stderr, "MODE SENSE (10) failed, device not ready\n"); + pr2serr("MODE SENSE (10) failed, device not ready\n"); return res; } } @@ -911,8 +930,8 @@ examine_pages(int sg_fd, int inq_pdt, int inq_byte6, char b[80]; sg_get_category_sense_str(res, sizeof(b), b, op->do_verbose - 1); - fprintf(stderr, "MODE SENSE (%s) failed: %s\n", - (op->do_six ? "6" : "10"), b); + pr2serr("MODE SENSE (%s) failed: %s\n", (op->do_six ? "6" : "10"), + b); } } return res; @@ -958,7 +977,7 @@ main(int argc, char * argv[]) return 0; } if (op->do_version) { - fprintf(stderr, "Version string: %s\n", version_str); + pr2serr("Version string: %s\n", version_str); return 0; } @@ -978,34 +997,31 @@ main(int argc, char * argv[]) } return 0; } - fprintf(stderr, "No DEVICE argument given\n"); + pr2serr("No DEVICE argument given\n"); usage_for(op); return SG_LIB_SYNTAX_ERROR; } if (op->do_examine && (op->pg_code >= 0)) { - fprintf(stderr, "can't give '-e' and a page number\n"); + pr2serr("can't give '-e' and a page number\n"); return SG_LIB_SYNTAX_ERROR; } if ((op->do_six) && (op->do_llbaa)) { - fprintf(stderr, "LLBAA not defined for MODE SENSE 6, try " - "without '-L'\n"); + pr2serr("LLBAA not defined for MODE SENSE 6, try without '-L'\n"); return SG_LIB_SYNTAX_ERROR; } if (op->maxlen > 0) { if (op->do_six && (op->maxlen > 255)) { - fprintf(stderr, "For Mode Sense (6) maxlen cannot exceed " - "255\n"); + pr2serr("For Mode Sense (6) maxlen cannot exceed 255\n"); return SG_LIB_SYNTAX_ERROR; } if (op->maxlen > DEF_ALLOC_LEN) { malloc_rsp_buff = (unsigned char *)malloc(op->maxlen); if (NULL == malloc_rsp_buff) { - fprintf(stderr, "Unable to malloc maxlen=%d bytes\n", - op->maxlen); + pr2serr("Unable to malloc maxlen=%d bytes\n", op->maxlen); return SG_LIB_SYNTAX_ERROR; - } + } rsp_buff = malloc_rsp_buff; } else rsp_buff = def_rsp_buff; @@ -1027,16 +1043,15 @@ main(int argc, char * argv[]) if ((sg_fd = sg_cmds_open_device(op->device_name, 1 /* ro */, op->do_verbose)) < 0) { - fprintf(stderr, "error opening file: %s: %s\n", - op->device_name, safe_strerror(-sg_fd)); + pr2serr("error opening file: %s: %s\n", op->device_name, + safe_strerror(-sg_fd)); if (malloc_rsp_buff) free(malloc_rsp_buff); return SG_LIB_FILE_ERROR; } if (sg_simple_inquiry(sg_fd, &inq_out, 1, op->do_verbose)) { - fprintf(stderr, "%s doesn't respond to a SCSI INQUIRY\n", - op->device_name); + pr2serr("%s doesn't respond to a SCSI INQUIRY\n", op->device_name); ret = SG_LIB_CAT_OTHER; goto finish; } @@ -1068,11 +1083,9 @@ main(int argc, char * argv[]) if (op->do_raw > 1) { if (op->do_all) { if (op->opt_new) - fprintf(stderr, "'-R' requires a specific (sub)page, not " - "all\n"); + pr2serr("'-R' requires a specific (sub)page, not all\n"); else - fprintf(stderr, "'-r' requires a specific (sub)page, not " - "all\n"); + pr2serr("'-r' requires a specific (sub)page, not all\n"); usage_for(op); ret = SG_LIB_SYNTAX_ERROR; goto finish; @@ -1085,30 +1098,30 @@ main(int argc, char * argv[]) op->pg_code, op->subpg_code, rsp_buff, rsp_buff_size, 1, op->do_verbose); if (SG_LIB_CAT_INVALID_OP == res) - fprintf(stderr, ">>>>>> try again without the '-6' " - "switch for a 10 byte MODE SENSE command\n"); + pr2serr(">>>>>> try again without the '-6' switch for a 10 byte " + "MODE SENSE command\n"); } else { res = sg_ll_mode_sense10(sg_fd, op->do_llbaa, op->do_dbd, op->page_control, op->pg_code, op->subpg_code, rsp_buff, rsp_buff_size, 1, op->do_verbose); if (SG_LIB_CAT_INVALID_OP == res) - fprintf(stderr, ">>>>>> try again with a '-6' " - "switch for a 6 byte MODE SENSE command\n"); + pr2serr(">>>>>> try again with a '-6' switch for a 6 byte MODE " + "SENSE command\n"); } if (SG_LIB_CAT_ILLEGAL_REQ == res) { if (op->subpg_code > 0) - fprintf(stderr, "invalid field in cdb (perhaps subpages " - "not supported)\n"); + pr2serr("invalid field in cdb (perhaps subpages not " + "supported)\n"); else if (op->page_control > 0) - fprintf(stderr, "invalid field in cdb (perhaps " - "page control (PC) not supported)\n"); + pr2serr("invalid field in cdb (perhaps page control (PC) not " + "supported)\n"); else - fprintf(stderr, "invalid field in cdb (perhaps " - "page 0x%x not supported)\n", op->pg_code); + pr2serr("invalid field in cdb (perhaps page 0x%x not " + "supported)\n", op->pg_code); } else if (res) { sg_get_category_sense_str(res, sizeof(b), b, op->do_verbose); - fprintf(stderr, "%s\n", b); + pr2serr("%s\n", b); } ret = res; if (0 == res) { @@ -1125,8 +1138,8 @@ main(int argc, char * argv[]) (0 == rsp_buff[5]) && (0 == rsp_buff[6])) { rsp_buff[1] = num; rsp_buff[0] = 0; - fprintf(stderr, ">>> msense(10) but resp[0]=%d and " - "not msense(6) response so fix length\n", num); + pr2serr(">>> msense(10) but resp[0]=%d and not msense(6) " + "response so fix length\n", num); } else resp_mode6 = 1; } @@ -1156,8 +1169,7 @@ main(int argc, char * argv[]) longlba = rsp_buff[4] & 1; } if ((bd_len + headerlen) > md_len) { - fprintf(stderr, "Invalid block descriptor length=%d, ignore\n", - bd_len); + pr2serr("Invalid block descriptor length=%d, ignore\n", bd_len); bd_len = 0; } if (op->do_raw) { @@ -1230,8 +1242,8 @@ main(int argc, char * argv[]) for (k = 0; md_len > 0; ++k) { /* got mode page(s) */ if ((k > 0) && (! op->do_all) && (SPG_CODE_ALL != op->subpg_code)) { - fprintf(stderr, "Unexpectedly received extra mode page " - "responses, ignore\n"); + pr2serr("Unexpectedly received extra mode page responses, " + "ignore\n"); break; } uc = *ucp; @@ -1241,10 +1253,9 @@ main(int argc, char * argv[]) if (0x0 == page_num) { ++num_ua_pages; if((num_ua_pages > 3) && (md_len > 0xa00)) { - fprintf(stderr, ">>> Seen 3 unit attention pages " - "(only one should be at end)\n and mpage " - "length=%d, looks malformed, try '-f' option\n", - md_len); + pr2serr(">>> Seen 3 unit attention pages (only one " + "should be at end)\n and mpage length=%d, " + "looks malformed, try '-f' option\n", md_len); break; } } @@ -1281,8 +1292,8 @@ main(int argc, char * argv[]) num = (len > md_len) ? md_len : len; if ((k > 0) && (num > 256)) { num = 256; - fprintf(stderr, ">>> page length (%d) > 256 bytes, unlikely " - "trim\n Try '-f' option\n", len); + pr2serr(">>> page length (%d) > 256 bytes, unlikely trim\n" + " Try '-f' option\n", len); } dStrHex((const char *)ucp, num , 1); ucp += len; diff --git a/src/sg_opcodes.c b/src/sg_opcodes.c index 862b518..9aa3da3 100644 --- a/src/sg_opcodes.c +++ b/src/sg_opcodes.c @@ -26,7 +26,7 @@ #include "sg_pt.h" -static const char * version_str = "0.41 20140521"; /* spc4r37 */ +static const char * version_str = "0.42 20140904"; /* spc4r37 */ #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */ @@ -53,6 +53,7 @@ static int do_rstmf(int sg_fd, int repd, void * resp, int mx_resp_len, static struct option long_options[] = { {"alpha", 0, 0, 'a'}, + {"compact", 0, 0, 'c'}, {"help", 0, 0, 'h'}, {"hex", 0, 0, 'H'}, {"mask", 0, 0, 'm'}, @@ -73,6 +74,7 @@ static struct option long_options[] = { struct opts_t { int do_alpha; + int do_compact; int do_help; int do_hex; int no_inquiry; @@ -95,15 +97,17 @@ static void usage() { fprintf(stderr, - "Usage: sg_opcodes [--alpha] [--help] [--hex] [--mask] " - "[--no-inquiry]\n" - " [--opcode=OP[,SA]] [--raw] [--rctd] " - "[--repd] [--sa=SA]\n" - " [--tmf] [--unsorted] [--verbose] " - "[--version] DEVICE\n" + "Usage: sg_opcodes [--alpha] [--compact] [--help] [--hex] " + "[--mask]\n" + " [--no-inquiry] [--opcode=OP[,SA]] [--raw] " + "[--rctd]\n" + " [--repd] [--sa=SA] [--tmf] [--unsorted] " + "[--verbose]\n" + " [--version] DEVICE\n" " where:\n" " --alpha|-a output list of operation codes sorted " "alphabetically\n" + " --compact|-c more compact output\n" " --help|-h print usage message then exit\n" " --hex|-H output response in hex\n" " --mask|-m and show cdb usage data (a mask) when " @@ -137,12 +141,13 @@ static void usage_old() { fprintf(stderr, - "Usage: sg_opcodes [-a] [-H] [-m] [-n] [-o=OP] [-q] [-r] [-R] " - "[-s=SA]\n" + "Usage: sg_opcodes [-a] [-c] [-H] [-m] [-n] [-o=OP] [-q] [-r] " + "[-R] [-s=SA]\n" " [-t] [-u] [-v] [-V] DEVICE\n" " where:\n" " -a output list of operation codes sorted " "alphabetically\n" + " -c more compact output\n" " -H print response in hex\n" " -m and show cdb usage data (a mask) when all listed\n" " -n don't output INQUIRY information\n" @@ -171,14 +176,17 @@ process_cl_new(struct opts_t * optsp, int argc, char * argv[]) while (1) { int option_index = 0; - c = getopt_long(argc, argv, "ahHmnNo:OqrRs:tuvV", long_options, + c = getopt_long(argc, argv, "achHmnNo:OqrRs:tuvV", long_options, &option_index); if (c == -1) break; switch (c) { case 'a': - optsp->do_alpha = 1; + ++optsp->do_alpha; + break; + case 'c': + ++optsp->do_compact; break; case 'h': case '?': @@ -301,6 +309,9 @@ process_cl_old(struct opts_t * optsp, int argc, char * argv[]) case 'a': ++optsp->do_alpha; break; + case 'c': + ++optsp->do_compact; + break; case 'H': ++optsp->do_hex; break; @@ -477,7 +488,7 @@ static void list_all_codes(unsigned char * rsoc_buff, int rsoc_len, struct opts_t * op, int sg_fd) { - int k, j, m, cd_len, serv_act, len, servactv, opcode, res; + int k, j, m, cd_len, serv_act, len, sa_v, opcode, res; unsigned int to; unsigned char * ucp; char name_buff[NAME_BUFF_SZ]; @@ -496,14 +507,27 @@ list_all_codes(unsigned char * rsoc_buff, int rsoc_len, struct opts_t * op, return; } if (op->do_rctd) { - printf("\nOpcode Service CDB Nominal Recommended Name\n"); - printf( "(hex) action(h) size timeout timeout(sec) \n"); - printf("-----------------------------------------------------------" - "-----\n"); + if (op->do_compact) { + printf("\nOpcode,sa Nominal Recommended Name\n"); + printf( " (hex) timeout timeout(sec) \n"); + printf("-----------------------------------------------" + "---------\n"); + } else { + printf("\nOpcode Service CDB Nominal Recommended Name\n"); + printf( "(hex) action(h) size timeout timeout(sec) \n"); + printf("-------------------------------------------------------" + "---------\n"); + } } else { - printf("\nOpcode Service CDB Name\n"); - printf( "(hex) action(h) size \n"); - printf("-----------------------------------------------\n"); + if (op->do_compact) { + printf("\nOpcode,sa Name\n"); + printf( " (hex) \n"); + printf("---------------------------------------\n"); + } else { + printf("\nOpcode Service CDB Name\n"); + printf( "(hex) action(h) size \n"); + printf("-----------------------------------------------\n"); + } } /* SPC-4 does _not_ require any ordering of opcodes in the response */ if (! op->do_unsorted) { @@ -526,21 +550,28 @@ list_all_codes(unsigned char * rsoc_buff, int rsoc_len, struct opts_t * op, ucp = op->do_unsorted ? (rsoc_buff + 4 + k) : sort_arr[j]; len = (ucp[5] & 0x2) ? 20 : 8; opcode = ucp[0]; - servactv = ucp[5] & 1; + sa_v = ucp[5] & 1; serv_act = 0; - if (servactv) { + if (sa_v) { serv_act = ((ucp[2] << 8) | ucp[3]); sg_get_opcode_sa_name(opcode, serv_act, peri_type, NAME_BUFF_SZ, name_buff); - snprintf(sa_buff, sizeof(sa_buff), "%4x", serv_act); + if (op->do_compact) + snprintf(sa_buff, sizeof(sa_buff), "%-4x", serv_act); + else + snprintf(sa_buff, sizeof(sa_buff), "%4x", serv_act); } else { sg_get_opcode_name(opcode, peri_type, NAME_BUFF_SZ, name_buff); memset(sa_buff, ' ', sizeof(sa_buff)); } if (op->do_rctd) { if (ucp[5] & 0x2) { - printf(" %.2x %.4s %3d", opcode, sa_buff, - ((ucp[6] << 8) | ucp[7])); + if (op->do_compact) + printf(" %.2x%c%.4s", opcode, (sa_v ? ',' : ' '), + sa_buff); + else + printf(" %.2x %.4s %3d", opcode, sa_buff, + ((ucp[6] << 8) | ucp[7])); to = ((unsigned int)ucp[12] << 24) + (ucp[13] << 16) + (ucp[14] << 8) + ucp[15]; if (0 == to) @@ -555,23 +586,34 @@ list_all_codes(unsigned char * rsoc_buff, int rsoc_len, struct opts_t * op, printf(" %8u", to); printf(" %s\n", name_buff); } else - printf(" %.2x %.4s %3d " - "%s\n", opcode, sa_buff, ((ucp[6] << 8) | ucp[7]), - name_buff); + if (op->do_compact) + printf(" %.2x%c%.4s %s\n", opcode, + (sa_v ? ',' : ' '), sa_buff, name_buff); + else + printf(" %.2x %.4s %3d " + "%s\n", opcode, sa_buff, ((ucp[6] << 8) | ucp[7]), + name_buff); } else - printf(" %.2x %.4s %3d %s\n", - ucp[0], sa_buff, ((ucp[6] << 8) | ucp[7]), name_buff); + if (op->do_compact) + printf(" %.2x%c%.4s %s\n", ucp[0], (sa_v ? ',' : ' '), + sa_buff, name_buff); + else + printf(" %.2x %.4s %3d %s\n", + ucp[0], sa_buff, ((ucp[6] << 8) | ucp[7]), name_buff); if (op->do_mask) { int cdb_sz; unsigned char b[64]; memset(b, 0, sizeof(b)); - res = do_rsoc(sg_fd, 0, (servactv ? 2 : 1), opcode, serv_act, + res = do_rsoc(sg_fd, 0, (sa_v ? 2 : 1), opcode, serv_act, b, sizeof(b), 1, op->do_verbose); if (0 == res) { cdb_sz = (b[2] << 8) + b[3]; if ((cdb_sz > 0) && (cdb_sz <= 80)) { - printf(" cdb usage: "); + if (op->do_compact) + printf(" usage: "); + else + printf(" cdb usage: "); for (m = 0; m < cdb_sz; ++m) printf("%.2x ", b[4 + m]); printf("\n"); @@ -780,7 +822,7 @@ main(int argc, char * argv[]) op->do_verbose); if (res) { sg_get_category_sense_str(res, sizeof(b), b, op->do_verbose); - fprintf(stderr, "%s: %s command\n", op_name, b); + fprintf(stderr, "%s: %s\n", op_name, b); goto err_out; } if (op->do_taskman) { diff --git a/src/sg_persist.c b/src/sg_persist.c index 23d528d..e108f38 100644 --- a/src/sg_persist.c +++ b/src/sg_persist.c @@ -27,7 +27,7 @@ #include "sg_cmds_basic.h" #include "sg_cmds_extra.h" -static const char * version_str = "0.48 20140515"; +static const char * version_str = "0.49 20141007"; #define PRIN_RKEY_SA 0x0 @@ -71,39 +71,39 @@ struct opts_t { static struct option long_options[] = { - {"alloc-length", 1, 0, 'l'}, - {"clear", 0, 0, 'C'}, - {"device", 1, 0, 'd'}, - {"help", 0, 0, 'h'}, - {"hex", 0, 0, 'H'}, - {"in", 0, 0, 'i'}, - {"no-inquiry", 0, 0, 'n'}, - {"out", 0, 0, 'o'}, - {"param-alltgpt", 0, 0, 'Y'}, - {"param-aptpl", 0, 0, 'Z'}, - {"param-rk", 1, 0, 'K'}, - {"param-sark", 1, 0, 'S'}, - {"param-unreg", 0, 0, 'U'}, - {"preempt", 0, 0, 'P'}, - {"preempt-abort", 0, 0, 'A'}, - {"prout-type", 1, 0, 'T'}, - {"read-full-status", 0, 0, 's'}, - {"read-keys", 0, 0, 'k'}, - {"readonly", 0, 0, 'y'}, - {"read-reservation", 0, 0, 'r'}, - {"read-status", 0, 0, 's'}, - {"register", 0, 0, 'G'}, - {"register-ignore", 0, 0, 'I'}, - {"register-move", 0, 0, 'M'}, - {"release", 0, 0, 'L'}, - {"relative-target-port", 1, 0, 'Q'}, - {"replace-lost", 0, 0, 'z'}, - {"report-capabilities", 0, 0, 'c'}, - {"reserve", 0, 0, 'R'}, - {"transport-id", 1, 0, 'X'}, - {"unreg", 0, 0, 'U'}, - {"verbose", 0, 0, 'v'}, - {"version", 0, 0, 'V'}, + {"alloc-length", required_argument, 0, 'l'}, + {"clear", no_argument, 0, 'C'}, + {"device", required_argument, 0, 'd'}, + {"help", no_argument, 0, 'h'}, + {"hex", no_argument, 0, 'H'}, + {"in", no_argument, 0, 'i'}, + {"no-inquiry", no_argument, 0, 'n'}, + {"out", no_argument, 0, 'o'}, + {"param-alltgpt", no_argument, 0, 'Y'}, + {"param-aptpl", no_argument, 0, 'Z'}, + {"param-rk", required_argument, 0, 'K'}, + {"param-sark", required_argument, 0, 'S'}, + {"param-unreg", no_argument, 0, 'U'}, + {"preempt", no_argument, 0, 'P'}, + {"preempt-abort", no_argument, 0, 'A'}, + {"prout-type", required_argument, 0, 'T'}, + {"read-full-status", no_argument, 0, 's'}, + {"read-keys", no_argument, 0, 'k'}, + {"readonly", no_argument, 0, 'y'}, + {"read-reservation", no_argument, 0, 'r'}, + {"read-status", no_argument, 0, 's'}, + {"register", no_argument, 0, 'G'}, + {"register-ignore", no_argument, 0, 'I'}, + {"register-move", no_argument, 0, 'M'}, + {"release", no_argument, 0, 'L'}, + {"relative-target-port", required_argument, 0, 'Q'}, + {"replace-lost", no_argument, 0, 'z'}, + {"report-capabilities", no_argument, 0, 'c'}, + {"reserve", no_argument, 0, 'R'}, + {"transport-id", required_argument, 0, 'X'}, + {"unreg", no_argument, 0, 'U'}, + {"verbose", no_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, {0, 0, 0, 0} }; @@ -170,59 +170,88 @@ pr2serr(const char * fmt, ...) } static void -usage() +usage(int help) { - pr2serr("Usage: sg_persist [OPTIONS] [DEVICE]\n" - " where OPTIONS include:\n" - " --alloc-length=LEN|-l LEN allocation length hex value " - "(used with\n" - " PR In only) (default: 8192 " - "(2000 in hex))\n" - " --clear|-C PR Out: Clear\n" - " --device=DEVICE|-d DEVICE query or change DEVICE\n" - " --help|-h output this usage message\n" - " --hex|-H output response in hex\n" - " --in|-i request PR In command (default)\n" - " --no-inquiry|-n skip INQUIRY (default: do " - "INQUIRY)\n" - " --out|-o request PR Out command\n" - " --param-alltgpt|-Y PR Out parameter 'ALL_TG_PT'\n" - " --param-aptpl|-Z PR Out parameter 'APTPL'\n" - " --param-rk=RK|-K RK PR Out parameter reservation key\n" - " (RK is in hex)\n" - " --param-sark=SARK|-S SARK PR Out parameter service " - "action\n" - " reservation key (SARK is in " - "hex)\n" - " --preempt|-P PR Out: Preempt\n" - " --preempt-abort|-A PR Out: Preempt and Abort\n" - " --prout-type=TYPE|-T TYPE PR Out command type\n" - " --read-full-status|-s PR In: Read Full Status\n" - " --read-keys|-k PR In: Read Keys\n"); - pr2serr(" --readonly|-y open DEVICE read-only (def: " - "read-write)\n" - " --read-reservation|-r PR In: Read Reservation\n" - " --read-status|-s PR In: Read Full Status\n" - " --register|-G PR Out: Register\n" - " --register-ignore|-I PR Out: Register and Ignore\n" - " --register-move|-M PR Out: Register and Move\n" - " --relative-target-port=RTPI|-Q RTPI relative target port " - "identifier\n" - " for '--register-move'\n" - " --release|-L PR Out: Release\n" - " --replace-lost|-x PR Out: Replace Lost Reservation\n" - " --report-capabilities|-c PR In: Report Capabilities\n" - " --reserve|-R PR Out: Reserve\n" - " --transport-id=TIDS|-X TIDS one or more " - "TransportIDs can\n" - " be given in several forms\n" - " --unreg|-U optional with PR Out Register " - "and Move\n" - " --verbose|-v output additional debug " - "information\n" - " --version|-V output version string\n" - " -? output this usage message\n\n" - "Performs a SCSI PERSISTENT RESERVE (IN or OUT) command\n"); + if (help < 2) { + pr2serr("Usage: sg_persist [OPTIONS] [DEVICE]\n" + " where the main OPTIONS are:\n" + " --clear|-C PR Out: Clear\n" + " --help|-h print usage message, " + "twice for more\n" + " --in|-i request PR In command " + "(default)\n" + " --out|-o request PR Out command\n" + " --param-rk=RK|-K RK PR Out parameter reservation " + "key\n" + " (RK is in hex)\n" + " --param-sark=SARK|-S SARK PR Out parameter service " + "action\n" + " reservation key (SARK is " + "in hex)\n" + " --preempt|-P PR Out: Preempt\n" + " --preempt-abort|-A PR Out: Preempt and Abort\n" + " --prout-type=TYPE|-T TYPE PR Out type field (see " + "'-hh')\n" + " --read-full-status|-s PR In: Read Full Status\n" + " --read-keys|-k PR In: Read Keys " + "(default)\n"); + pr2serr(" --read-reservation|-r PR In: Read Reservation\n" + " --read-status|-s PR In: Read Full Status\n" + " --register|-G PR Out: Register\n" + " --register-ignore|-I PR Out: Register and Ignore\n" + " --register-move|-M PR Out: Register and Move\n" + " for '--register-move'\n" + " --release|-L PR Out: Release\n" + " --replace-lost|-x PR Out: Replace Lost " + "Reservation\n" + " --report-capabilities|-c PR In: Report Capabilities\n" + " --reserve|-R PR Out: Reserve\n" + " --unreg|-U optional with PR Out " + "Register and Move\n\n" + "Performs a SCSI PERSISTENT RESERVE (IN or OUT) command. " + "Invoking\n'sg_persist DEVICE' will do a PR In Read Keys " + "command. Use '-hh'\nfor more options and TYPE meanings.\n"); + } else { + pr2serr("Usage: sg_persist [OPTIONS] [DEVICE]\n" + " where the other OPTIONS are:\n" + " --alloc-length=LEN|-l LEN allocation length hex " + "value (used with\n" + " PR In only) (default: 8192 " + "(2000 in hex))\n" + " --device=DEVICE|-d DEVICE supply DEVICE as an option " + "rather than\n" + " an argument\n" + " --hex|-H output response in hex (for " + "PR In commands)\n" + " --no-inquiry|-n skip INQUIRY (default: do " + "INQUIRY)\n" + " --param-alltgpt|-Y PR Out parameter " + "'ALL_TG_PT'\n" + " --param-aptpl|-Z PR Out parameter 'APTPL'\n" + " --readonly|-y open DEVICE read-only (def: " + "read-write)\n" + " --relative-target-port=RTPI|-Q RTPI relative target " + "port " + "identifier\n" + " --transport-id=TIDS|-X TIDS one or more " + "TransportIDs can\n" + " be given in several " + "forms\n" + " --verbose|-v output additional debug " + "information\n" + " --version|-V output version string\n\n" + "For the main options use '--help' or '-h' once.\n\n\n"); + pr2serr("PR Out TYPE field value meanings:\n" + " 0: obsolete (was 'read shared' in SPC)\n" + " 1: write exclusive\n" + " 2: obsolete (was 'read exclusive')\n" + " 3: exclusive access\n" + " 4: obsolete (was 'shared access')\n" + " 5: write exclusive, registrants only\n" + " 6: exclusive access, registrants only\n" + " 7: write exclusive, all registrants\n" + " 8: exclusive access, all registrants\n"); + } } /* If num_tids==0 then only one TransportID is assumed with len bytes in @@ -1029,6 +1058,7 @@ main(int argc, char * argv[]) int sg_fd, c, res; const char * device_name = NULL; char buff[48]; + int help =0; int num_prin_sa = 0; int num_prout_sa = 0; int num_prout_param = 0; @@ -1078,8 +1108,8 @@ main(int argc, char * argv[]) ++num_prout_sa; break; case 'h': - usage(); - return 0; + ++help; + break; case 'H': ++op->hex; break; @@ -1199,11 +1229,11 @@ main(int argc, char * argv[]) ++num_prout_param; break; case '?': - usage(); + usage(1); return 0; default: pr2serr("unrecognised switch code 0x%x ??\n", c); - usage(); + usage(1); return SG_LIB_SYNTAX_ERROR; } } @@ -1215,19 +1245,23 @@ main(int argc, char * argv[]) if (optind < argc) { for (; optind < argc; ++optind) pr2serr("Unexpected extra argument: %s\n", argv[optind]); - usage(); + usage(1); return SG_LIB_SYNTAX_ERROR; } } + if (help > 0) { + usage(help); + return 0; + } if (NULL == device_name) { pr2serr("No device name given\n"); - usage(); + usage(1); return SG_LIB_SYNTAX_ERROR; } if ((want_prout + want_prin) > 1) { pr2serr("choose '--in' _or_ '--out' (not both)\n"); - usage(); + usage(1); return SG_LIB_SYNTAX_ERROR; } else if (want_prout) { /* syntax check on PROUT arguments */ op->prin = 0; @@ -1251,7 +1285,7 @@ main(int argc, char * argv[]) ++num_prin_sa; } else if (num_prin_sa > 1) { pr2serr("Too many service actions given; choose one only\n"); - usage(); + usage(1); return SG_LIB_SYNTAX_ERROR; } } @@ -1259,14 +1293,14 @@ main(int argc, char * argv[]) (PROUT_REG_MOVE_SA != op->prout_sa)) { pr2serr("--unreg or --relative-target-port only useful with " "--register-move\n"); - usage(); + usage(1); return SG_LIB_SYNTAX_ERROR; } if ((PROUT_REG_MOVE_SA == op->prout_sa) && (1 != op->num_transportids)) { pr2serr("with --register-move one (and only one) --transport-id " "should be given\n"); - usage(); + usage(1); return SG_LIB_SYNTAX_ERROR; } if (((PROUT_RES_SA == op->prout_sa) || diff --git a/src/sg_raw.c b/src/sg_raw.c index 9380d32..2580b08 100644 --- a/src/sg_raw.c +++ b/src/sg_raw.c @@ -27,7 +27,7 @@ #include "sg_lib.h" #include "sg_pt.h" -#define SG_RAW_VERSION "0.4.9 (2014-05-14)" +#define SG_RAW_VERSION "0.4.11 (2014-10-18)" #ifdef SG_LIB_WIN32 #ifndef HAVE_SYSCONF @@ -242,7 +242,20 @@ process_cl(struct opts_t * op, int argc, char *argv[]) fprintf(stderr, "CDB too short (min. %d bytes)\n", MIN_SCSI_CDBSZ); return SG_LIB_SYNTAX_ERROR; } - + if (op->do_verbose > 2) { + int sa; + char b[80]; + + if (op->cdb_length > 16) { + sa = (op->cdb[8] << 8) + op->cdb[9]; + if (0x7f != op->cdb[0]) + printf(">>> Unlikely to be SCSI CDB since all over 16 " + "bytes long should\n>>> start with 0x7f\n"); + } else + sa = op->cdb[1] & 0x1f; + sg_get_opcode_sa_name(op->cdb[0], sa, 0, sizeof(b), b); + printf("Attempt to decode cdb name: %s\n", b); + } return 0; } @@ -556,7 +569,11 @@ main(int argc, char *argv[]) if (op->do_datain) { int data_len = op->datain_len - get_scsi_pt_resid(ptvp); - if (data_len == 0) { + + if (ret && !(SG_LIB_CAT_RECOVERED == ret || + SG_LIB_CAT_NO_SENSE == ret)) + fprintf(stderr, "Error %d occurred, no data received\n", ret); + else if (data_len == 0) { fprintf(stderr, "No data received\n"); } else { if (op->datain_file == NULL && !op->datain_binary) { diff --git a/src/sg_read_buffer.c b/src/sg_read_buffer.c index 1caf71e..a774a6a 100644 --- a/src/sg_read_buffer.c +++ b/src/sg_read_buffer.c @@ -25,7 +25,7 @@ * This utility issues the SCSI READ BUFFER command to the given device. */ -static const char * version_str = "1.11 20140515"; +static const char * version_str = "1.12 20140919"; static struct option long_options[] = { @@ -64,12 +64,13 @@ usage() " --readonly|-R open DEVICE read-only (def: read-write)\n" " --verbose|-v increase verbosity\n" " --version|-V print version string and exit\n\n" - "Performs a SCSI READ BUFFER command. Numbers given in options are " - "decimal\nunless they have a hex indicator (e.g. a leading '0x').\n" + "Performs a SCSI READ BUFFER command. Use '-m xxx' to list " + "available\nmodes. Numbers given in options are decimal unless " + "they have a hex\nindicator (e.g. a leading '0x').\n" ); - } + #define MODE_HEADER_DATA 0 #define MODE_VENDOR 1 #define MODE_DATA 2 diff --git a/src/sg_rep_zones.c b/src/sg_rep_zones.c index 0494e75..a182afd 100644 --- a/src/sg_rep_zones.c +++ b/src/sg_rep_zones.c @@ -22,15 +22,16 @@ #include "sg_lib.h" #include "sg_pt.h" #include "sg_cmds_basic.h" +#include "sg_unaligned.h" /* A utility program originally written for the Linux OS SCSI subsystem. * * * This program issues the SCSI REPORT ZONES command to the given SCSI device - * and decodes the response. + * and decodes the response. Based on zbc-r01c.pdf */ -static const char * version_str = "1.02 20140604"; +static const char * version_str = "1.03 20141028"; #define MAX_RZONES_BUFF_LEN (1024 * 1024) #define DEF_RZONES_BUFF_LEN (1024 * 8) @@ -97,7 +98,7 @@ usage() " --readonly|-R open DEVICE read-only (def: read-write)\n" " --report=OPT|-o OP reporting option (def: 0)\n" " --start=LBA|-s LBA report zones from the LBA (def: 0)\n" - " must be a zone starting LBA\n" + " need not be a zone starting LBA\n" " --verbose|-v increase verbosity\n" " --version|-V print version string and exit\n\n" "Performs a SCSI REPORT ZONES command.\n"); @@ -116,19 +117,8 @@ sg_ll_report_zones(int sg_fd, uint64_t zs_lba, int report_opts, void * resp, unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; - /* assume zbc-r01a is wrong and that ZS LBA is an 8 byte field */ - rzCmdBlk[2] = (zs_lba >> 56) & 0xff; - rzCmdBlk[3] = (zs_lba >> 48) & 0xff; - rzCmdBlk[4] = (zs_lba >> 40) & 0xff; - rzCmdBlk[5] = (zs_lba >> 32) & 0xff; - rzCmdBlk[6] = (zs_lba >> 24) & 0xff; - rzCmdBlk[7] = (zs_lba >> 16) & 0xff; - rzCmdBlk[8] = (zs_lba >> 8) & 0xff; - rzCmdBlk[9] = zs_lba & 0xff; - rzCmdBlk[10] = (mx_resp_len >> 24) & 0xff; - rzCmdBlk[11] = (mx_resp_len >> 16) & 0xff; - rzCmdBlk[12] = (mx_resp_len >> 8) & 0xff; - rzCmdBlk[13] = mx_resp_len & 0xff; + sg_put_unaligned_be64(zs_lba, rzCmdBlk + 2); + sg_put_unaligned_be32((uint32_t)mx_resp_len, rzCmdBlk + 10); rzCmdBlk[14] = report_opts & 0xf; if (verbose) { pr2serr(" Report zones cdb: "); @@ -216,6 +206,9 @@ zone_condition_str(int zc, char * b, int blen, int vb) if (NULL == b) return "zone_condition_str: NULL ptr)"; switch (zc) { + case 0: + cp = "No write pointer"; + break; case 1: cp = "Empty"; break; @@ -245,11 +238,18 @@ zone_condition_str(int zc, char * b, int blen, int vb) return b; } +static const char * same_desc_arr[4] = { + "zone type and length may differ in each descriptor", + "zone type and length same in each descriptor", + "zone type and length same apart from length in last descriptor", + "Reserved", +}; + int main(int argc, char * argv[]) { - int sg_fd, k, m, res, c, zl_len, len, zones, resid, rlen, zt, zc; + int sg_fd, k, res, c, zl_len, len, zones, resid, rlen, zt, zc, same; int do_hex = 0; int maxlen = 0; int do_raw = 0; @@ -374,8 +374,7 @@ main(int argc, char * argv[]) ret = SG_LIB_CAT_MALFORMED; goto the_end; } - zl_len = (reportZonesBuff[0] << 24) + (reportZonesBuff[1] << 16) + - (reportZonesBuff[2] << 8) + reportZonesBuff[3] + 64; + zl_len = sg_get_unaligned_be32(reportZonesBuff + 0) + 64; if (zl_len > rlen) { if (verbose) pr2serr("zl_len available is %d, response length is %d\n", @@ -399,7 +398,8 @@ main(int argc, char * argv[]) ret = SG_LIB_CAT_MALFORMED; goto the_end; } - printf(" Same=%d\n\n", reportZonesBuff[4] & 1); + same = reportZonesBuff[4] & 3; + printf(" Same=%d: %s\n\n", same, same_desc_arr[same]); zones = (len - 64) / 64; for (k = 0, ucp = reportZonesBuff + 64; k < zones; ++k, ucp += 64) { printf(" Zone descriptor: %d\n", k); @@ -413,17 +413,14 @@ main(int argc, char * argv[]) verbose)); printf(" Zone condition: %s\n", zone_condition_str(zc, b, sizeof(b), verbose)); + printf(" Non_seq: %d\n", !!(ucp[1] & 0x2)); printf(" Reset: %d\n", ucp[1] & 0x1); - printf(" Zone Length: 0x"); - for (m = 0; m < 8; ++m) - printf("%02x", ucp[8 + m]); - printf("\n Zone start LBA: 0x"); - for (m = 0; m < 8; ++m) - printf("%02x", ucp[64 + m]); - printf("\n Write pointer LBA: 0x"); - for (m = 0; m < 8; ++m) - printf("%02x", ucp[64 + m]); - printf("\n"); + printf(" Zone Length: 0x%" PRIx64 "\n", + sg_get_unaligned_be64(ucp + 8)); + printf(" Zone start LBA: 0x%" PRIx64 "\n", + sg_get_unaligned_be64(ucp + 16)); + printf(" Write pointer LBA: 0x%" PRIx64 "\n", + sg_get_unaligned_be64(ucp + 24)); } if ((64 + (64 * zones)) < zl_len) printf("\n>>> Beware: Zone list truncated, may need another " diff --git a/src/sg_reset.c b/src/sg_reset.c index 2e4bd24..708ac01 100644 --- a/src/sg_reset.c +++ b/src/sg_reset.c @@ -1,5 +1,5 @@ /* A utility program originally written for the Linux OS SCSI subsystem. - * Copyright (C) 1999-2013 D. Gilbert + * Copyright (C) 1999-2014 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) @@ -28,7 +28,7 @@ #define ME "sg_reset: " -static const char * version_str = "0.58 20130507"; +static const char * version_str = "0.59 20141024"; #ifndef SG_SCSI_RESET #define SG_SCSI_RESET 0x2284 @@ -105,7 +105,7 @@ usage(int compat_mode) int main(int argc, char * argv[]) { - int c, sg_fd, res, k; + int c, sg_fd, res, k, hold_errno; int do_device_reset = 0; int do_bus_reset = 0; int do_host_reset = 0; @@ -226,22 +226,43 @@ int main(int argc, char * argv[]) } if (no_escalate) k += SG_SCSI_RESET_NO_ESCALATE; + if (verbose > 2) + fprintf(stderr, " third argument to ioctl(SG_SCSI_RESET) is " + "0x%x\n", k); res = ioctl(sg_fd, SG_SCSI_RESET, &k); if (res < 0) { - if (EBUSY == errno) + hold_errno = errno; + switch (errno) { + case EBUSY: fprintf(stderr, ME "BUSY, may be resetting now\n"); - else if (EIO == errno) + break; + case ENODEV: + fprintf(stderr, ME "'no device' error, may be temporary while " + "device is resetting\n"); + break; + case EAGAIN: + fprintf(stderr, ME "try again later, may be resetting now\n"); + break; + case EIO: fprintf(stderr, ME "reset (for value=0x%x) may not be " "available\n", k); - else if (EACCES == errno) + break; + case EPERM: + case EACCES: fprintf(stderr, ME "reset requires CAP_SYS_ADMIN (root) " "permission\n"); - else if (EINVAL == errno) + break; + case EINVAL: fprintf(stderr, ME "SG_SCSI_RESET not supported (for " "value=0x%x)\n", k); - else + default: perror(ME "SG_SCSI_RESET failed"); + break; + } + if (verbose > 1) + fprintf(stderr, ME "ioctl(SG_SCSI_RESET) returned %d, errno=%d\n", + res, hold_errno); close(sg_fd); return 1; } diff --git a/src/sg_reset_wp.c b/src/sg_reset_wp.c index 6ca532c..ed3fd7b 100644 --- a/src/sg_reset_wp.c +++ b/src/sg_reset_wp.c @@ -22,15 +22,16 @@ #include "sg_lib.h" #include "sg_pt.h" #include "sg_cmds_basic.h" +#include "sg_unaligned.h" /* A utility program originally written for the Linux OS SCSI subsystem. * * * This program issues the SCSI RESET WRITE POINTER command to the given SCSI - * device. + * device. Based on zbc-r01c.pdf . */ -static const char * version_str = "1.01 20140604"; +static const char * version_str = "1.02 20141028"; #define SERVICE_ACTION_OUT_16_CMD 0x9f #define SERVICE_ACTION_OUT_16_CMDLEN 16 @@ -103,15 +104,7 @@ sg_ll_reset_write_pointer(int sg_fd, uint64_t zid, int reset_all, int noisy, unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; - /* assume zbc-r01a is wrong and that ZS LBA is an 8 byte field */ - rwpCmdBlk[2] = (zid >> 56) & 0xff; - rwpCmdBlk[3] = (zid >> 48) & 0xff; - rwpCmdBlk[4] = (zid >> 40) & 0xff; - rwpCmdBlk[5] = (zid >> 32) & 0xff; - rwpCmdBlk[6] = (zid >> 24) & 0xff; - rwpCmdBlk[7] = (zid >> 16) & 0xff; - rwpCmdBlk[8] = (zid >> 8) & 0xff; - rwpCmdBlk[9] = zid & 0xff; + sg_put_unaligned_be64(zid, rwpCmdBlk + 2); if (reset_all) rwpCmdBlk[14] = 0x1; if (verbose) { diff --git a/src/sg_sanitize.c b/src/sg_sanitize.c index a21cb7b..860fc7c 100644 --- a/src/sg_sanitize.c +++ b/src/sg_sanitize.c @@ -26,7 +26,7 @@ #include "sg_cmds_basic.h" #include "sg_cmds_extra.h" -static const char * version_str = "0.94 20140516"; +static const char * version_str = "0.96 20141006"; /* Not all environments support the Unix sleep() */ #if defined(MSC_VER) || defined(__MINGW32__) @@ -64,6 +64,7 @@ static struct option long_options[] = { {"block", no_argument, 0, 'B'}, {"count", required_argument, 0, 'c'}, {"crypto", no_argument, 0, 'C'}, + {"desc", no_argument, 0, 'd'}, {"early", no_argument, 0, 'e'}, {"fail", no_argument, 0, 'F'}, {"help", no_argument, 0, 'h'}, @@ -76,6 +77,7 @@ static struct option long_options[] = { {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, {"wait", no_argument, 0, 'w'}, + {"zero", no_argument, 0, 'z'}, {0, 0, 0, 0}, }; @@ -84,6 +86,7 @@ struct opts_t { int block; int count; int crypto; + int desc; int early; int fail; int invert; @@ -93,6 +96,7 @@ struct opts_t { int quick; int verbose; int wait; + int zero; const char * pattern_fn; }; @@ -114,6 +118,9 @@ usage() " --count=OC|-c OC OC is overwrite count field (from 1 " "(def) to 31)\n" " --crypto|-C do CRYPTOGRAPHIC ERASE sanitize\n" + " --desc|-d polling request sense sets 'desc' " + "field\n" + " (def: clear 'desc' field)\n" " --early|-e exit once sanitize started (IMMED set " "in cdb)\n" " user can monitor progress with REQUEST " @@ -137,9 +144,11 @@ usage() " --verbose|-v increase verbosity\n" " --version|-V print version string then exit\n" " --wait|-w wait for command to finish (could " - "take hours)\n\n" + "take hours)\n" + " --zero|-z use pattern of zeros for " + "OVERWRITE\n\n" "Performs a SCSI SANITIZE command.\n <<<WARNING>>>: all data " - "on DEVICE will lost.\nDefault action is to give user time to " + "on DEVICE will be lost.\nDefault action is to give user time to " "reconsider; then execute SANITIZE\ncommand with IMMED bit set; " "then use REQUEST SENSE command every 60\nseconds to poll for a " "progress indication; then exit when there is no\nmore progress " @@ -234,16 +243,181 @@ do_sanitize(int sg_fd, const struct opts_t * op, const void * param_lstp, return ret; } +#define VPD_DEVICE_ID 0x83 +#define VPD_ASSOC_LU 0 +#define VPD_ASSOC_TPORT 1 +#define TPROTO_ISCSI 5 + +static char * +get_lu_name(const unsigned char * ucp, int u_len, char * b, int b_len) +{ + int len, off, sns_dlen, dlen, k; + unsigned char u_sns[512]; + char * cp; + + len = u_len - 4; + ucp += 4; + off = -1; + if (0 == sg_vpd_dev_id_iter(ucp, len, &off, VPD_ASSOC_LU, + 8 /* SCSI name string (sns) */, + 3 /* UTF-8 */)) { + sns_dlen = ucp[off + 3]; + memcpy(u_sns, ucp + off + 4, sns_dlen); + /* now want to check if this is iSCSI */ + off = -1; + if (0 == sg_vpd_dev_id_iter(ucp, len, &off, VPD_ASSOC_TPORT, + 8 /* SCSI name string (sns) */, + 3 /* UTF-8 */)) { + if ((0x80 & ucp[1]) && (TPROTO_ISCSI == (ucp[0] >> 4))) { + snprintf(b, b_len, "%.*s", sns_dlen, u_sns); + return b; + } + } + } else + sns_dlen = 0; + if (0 == sg_vpd_dev_id_iter(ucp, len, &off, VPD_ASSOC_LU, + 3 /* NAA */, 1 /* binary */)) { + dlen = ucp[off + 3]; + if (! ((8 == dlen) || (16 ==dlen))) + return b; + cp = b; + for (k = 0; ((k < dlen) && (b_len > 1)); ++k) { + snprintf(cp, b_len, "%02x", ucp[off + 4 + k]); + cp += 2; + b_len -= 2; + } + } else if (0 == sg_vpd_dev_id_iter(ucp, len, &off, VPD_ASSOC_LU, + 2 /* EUI */, 1 /* binary */)) { + dlen = ucp[off + 3]; + if (! ((8 == dlen) || (12 == dlen) || (16 ==dlen))) + return b; + cp = b; + for (k = 0; ((k < dlen) && (b_len > 1)); ++k) { + snprintf(cp, b_len, "%02x", ucp[off + 4 + k]); + cp += 2; + b_len -= 2; + } + } else if (sns_dlen > 0) + snprintf(b, b_len, "%.*s", sns_dlen, u_sns); + return b; +} + +#define SAFE_STD_INQ_RESP_LEN 36 +#define VPD_SUPPORTED_VPDS 0x0 +#define VPD_UNIT_SERIAL_NUM 0x80 +#define VPD_DEVICE_ID 0x83 + +static int +print_dev_id(int fd, unsigned char * sinq_resp, int max_rlen, int verbose) +{ + int res, k, n, verb, pdt, has_sn, has_di; + unsigned char b[256]; + char a[256]; + char pdt_name[64]; + + verb = (verbose > 1) ? verbose - 1 : 0; + memset(sinq_resp, 0, max_rlen); + res = sg_ll_inquiry(fd, 0, 0 /* evpd */, 0 /* pg_op */, b, + SAFE_STD_INQ_RESP_LEN, 1, verb); + if (res) + return res; + n = b[4] + 5; + if (n > SAFE_STD_INQ_RESP_LEN) + n = SAFE_STD_INQ_RESP_LEN; + memcpy(sinq_resp, b, (n < max_rlen) ? n : max_rlen); + if (n == SAFE_STD_INQ_RESP_LEN) { + pdt = b[0] & 0x1f; + printf(" %.8s %.16s %.4s peripheral_type: %s [0x%x]\n", + (const char *)(b + 8), (const char *)(b + 16), + (const char *)(b + 32), + sg_get_pdt_str(pdt, sizeof(pdt_name), pdt_name), pdt); + if (verbose) + printf(" PROTECT=%d\n", !!(b[5] & 1)); + if (b[5] & 1) + printf(" << supports protection information>>\n"); + } else { + fprintf(stderr, "Short INQUIRY response: %d bytes, expect at least " + "36\n", n); + return SG_LIB_CAT_OTHER; + } + res = sg_ll_inquiry(fd, 0, 1 /* evpd */, VPD_SUPPORTED_VPDS, b, + SAFE_STD_INQ_RESP_LEN, 1, verb); + if (res) { + if (verbose) + fprintf(stderr, "VPD_SUPPORTED_VPDS gave res=%d\n", res); + return 0; + } + if (VPD_SUPPORTED_VPDS != b[1]) { + if (verbose) + fprintf(stderr, "VPD_SUPPORTED_VPDS corrupted\n"); + return 0; + } + n = (b[2] << 8) + b[3]; + if (n > (SAFE_STD_INQ_RESP_LEN - 4)) + n = (SAFE_STD_INQ_RESP_LEN - 4); + for (k = 0, has_sn = 0, has_di = 0; k < n; ++k) { + if (VPD_UNIT_SERIAL_NUM == b[4 + k]) { + if (has_di) { + if (verbose) + fprintf(stderr, "VPD_SUPPORTED_VPDS dis-ordered\n"); + return 0; + } + ++has_sn; + } else if (VPD_DEVICE_ID == b[4 + k]) { + ++has_di; + break; + } + } + if (has_sn) { + res = sg_ll_inquiry(fd, 0, 1 /* evpd */, VPD_UNIT_SERIAL_NUM, b, + sizeof(b), 1, verb); + if (res) { + if (verbose) + fprintf(stderr, "VPD_UNIT_SERIAL_NUM gave res=%d\n", res); + return 0; + } + if (VPD_UNIT_SERIAL_NUM != b[1]) { + if (verbose) + fprintf(stderr, "VPD_UNIT_SERIAL_NUM corrupted\n"); + return 0; + } + n = (b[2] << 8) + b[3]; + if (n > (int)(sizeof(b) - 4)) + n = (sizeof(b) - 4); + printf(" Unit serial number: %.*s\n", n, (const char *)(b + 4)); + } + if (has_di) { + res = sg_ll_inquiry(fd, 0, 1 /* evpd */, VPD_DEVICE_ID, b, + sizeof(b), 1, verb); + if (res) { + if (verbose) + fprintf(stderr, "VPD_DEVICE_ID gave res=%d\n", res); + return 0; + } + if (VPD_DEVICE_ID != b[1]) { + if (verbose) + fprintf(stderr, "VPD_DEVICE_ID corrupted\n"); + return 0; + } + n = (b[2] << 8) + b[3]; + if (n > (int)(sizeof(b) - 4)) + n = (sizeof(b) - 4); + n = strlen(get_lu_name(b, n + 4, a, sizeof(a))); + if (n > 0) + printf(" LU name: %.*s\n", n, a); + } + return 0; +} + int main(int argc, char * argv[]) { - int sg_fd, k, res, c, infd, progress, vb, n, desc, resp_len; + int sg_fd, k, res, c, infd, progress, vb, n, resp_len; int got_stdin = 0; int param_lst_len = 0; const char * device_name = NULL; char ebuff[EBUFF_SZ]; - char pdt_name[32]; char b[80]; unsigned char requestSenseBuff[DEF_REQS_RESP_LEN]; unsigned char * wBuff = NULL; @@ -251,7 +425,7 @@ main(int argc, char * argv[]) struct opts_t opts; struct opts_t * op; struct stat a_stat; - struct sg_simple_inquiry_resp inq_out; + unsigned char inq_resp[SAFE_STD_INQ_RESP_LEN]; op = &opts; memset(op, 0, sizeof(opts)); @@ -259,7 +433,7 @@ main(int argc, char * argv[]) while (1) { int option_index = 0; - c = getopt_long(argc, argv, "ABc:CeFhi:IOp:QT:vVw", long_options, + c = getopt_long(argc, argv, "ABc:CdeFhi:IOp:QT:vVwz", long_options, &option_index); if (c == -1) break; @@ -282,6 +456,9 @@ main(int argc, char * argv[]) case 'C': ++op->crypto; break; + case 'd': + ++op->desc; + break; case 'e': ++op->early; break; @@ -328,6 +505,9 @@ main(int argc, char * argv[]) case 'w': ++op->wait; break; + case 'z': + ++op->zero; + break; default: fprintf(stderr, "unrecognised option code 0x%x ??\n", c); usage(); @@ -360,34 +540,42 @@ main(int argc, char * argv[]) return SG_LIB_SYNTAX_ERROR; } if (op->overwrite) { - if (NULL == op->pattern_fn) { - fprintf(stderr, "'--overwrite' requires '--pattern=PF' " - "option\n"); - return SG_LIB_SYNTAX_ERROR; - } - got_stdin = (0 == strcmp(op->pattern_fn, "-")) ? 1 : 0; - if (! got_stdin) { - memset(&a_stat, 0, sizeof(a_stat)); - if (stat(op->pattern_fn, &a_stat) < 0) { - fprintf(stderr, "pattern file: unable to stat(%s): %s\n", - op->pattern_fn, safe_strerror(errno)); - return SG_LIB_FILE_ERROR; + if (op->zero) { + if (op->pattern_fn) { + fprintf(stderr, "confused: both '--pattern=PF' and '--zero' " + "options\n"); + return SG_LIB_SYNTAX_ERROR; + } + op->ipl = 4; + } else { + if (NULL == op->pattern_fn) { + fprintf(stderr, "'--overwrite' requires '--pattern=PF' " + "or '--zero' option\n"); + return SG_LIB_SYNTAX_ERROR; } - if (op->ipl <= 0) { - op->ipl = (int)a_stat.st_size; - if (op->ipl > MAX_XFER_LEN) { - fprintf(stderr, "pattern file length exceeds 65535 " - "bytes, need '--ipl=LEN' option\n"); - return SG_LIB_FILE_ERROR; + got_stdin = (0 == strcmp(op->pattern_fn, "-")) ? 1 : 0; + if (! got_stdin) { + memset(&a_stat, 0, sizeof(a_stat)); + if (stat(op->pattern_fn, &a_stat) < 0) { + fprintf(stderr, "pattern file: unable to stat(%s): %s\n", + op->pattern_fn, safe_strerror(errno)); + return SG_LIB_FILE_ERROR; + } + if (op->ipl <= 0) { + op->ipl = (int)a_stat.st_size; + if (op->ipl > MAX_XFER_LEN) { + fprintf(stderr, "pattern file length exceeds 65535 " + "bytes, need '--ipl=LEN' option\n"); + return SG_LIB_FILE_ERROR; + } } } + if (op->ipl < 1) { + fprintf(stderr, "'--overwrite' requires '--ipl=LEN' " + "option if can't get PF length\n"); + return SG_LIB_SYNTAX_ERROR; + } } - if (op->ipl < 1) { - fprintf(stderr, "'--overwrite' requires '--ipl=LEN' " - "option if can't get PF length\n"); - return SG_LIB_SYNTAX_ERROR; - } - } sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, vb); @@ -397,18 +585,9 @@ main(int argc, char * argv[]) return SG_LIB_FILE_ERROR; } - if (sg_simple_inquiry(sg_fd, &inq_out, 1, vb)) { - fprintf(stderr, "%s doesn't respond to a SCSI INQUIRY\n", - device_name); - ret = SG_LIB_CAT_OTHER; - goto err_out; - } - printf(" %.8s %.16s %.4s peripheral_type: %s [0x%x]\n", - inq_out.vendor, inq_out.product, inq_out.revision, - sg_get_pdt_str(inq_out.peripheral_type, sizeof(pdt_name), - pdt_name), - inq_out.peripheral_type); - + ret = print_dev_id(sg_fd, inq_resp, sizeof(inq_resp), op->verbose); + if (ret) + goto err_out; if (op->overwrite) { param_lst_len = op->ipl + 4; @@ -419,38 +598,44 @@ main(int argc, char * argv[]) ret = SG_LIB_SYNTAX_ERROR; goto err_out; } - if (got_stdin) { - infd = STDIN_FILENO; - if (sg_set_binary_mode(STDIN_FILENO) < 0) - perror("sg_set_binary_mode"); + if (op->zero) { + if (2 == op->zero) /* treat -zz as fill with 0xff bytes */ + memset(wBuff + 4, 0xff, op->ipl); + else + memset(wBuff + 4, 0, op->ipl); } else { - if ((infd = open(op->pattern_fn, O_RDONLY)) < 0) { - snprintf(ebuff, EBUFF_SZ, - ME "could not open %s for reading", op->pattern_fn); + if (got_stdin) { + infd = STDIN_FILENO; + if (sg_set_binary_mode(STDIN_FILENO) < 0) + perror("sg_set_binary_mode"); + } else { + if ((infd = open(op->pattern_fn, O_RDONLY)) < 0) { + snprintf(ebuff, EBUFF_SZ, ME "could not open %s for " + "reading", op->pattern_fn); + perror(ebuff); + ret = SG_LIB_FILE_ERROR; + goto err_out; + } else if (sg_set_binary_mode(infd) < 0) + perror("sg_set_binary_mode"); + } + res = read(infd, wBuff + 4, op->ipl); + if (res < 0) { + snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %s", + op->pattern_fn); perror(ebuff); + if (! got_stdin) + close(infd); ret = SG_LIB_FILE_ERROR; goto err_out; - } else if (sg_set_binary_mode(infd) < 0) - perror("sg_set_binary_mode"); - } - res = read(infd, wBuff + 4, op->ipl); - if (res < 0) { - snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %s", - op->pattern_fn); - perror(ebuff); + } + if (res < op->ipl) { + fprintf(stderr, "tried to read %d bytes from %s, got %d " + "bytes\n", op->ipl, op->pattern_fn, res); + fprintf(stderr, " so pad with 0x0 bytes and continue\n"); + } if (! got_stdin) close(infd); - ret = SG_LIB_FILE_ERROR; - goto err_out; - } - if (res < op->ipl) { - fprintf(stderr, "tried to read %d bytes from %s, got %d " - "bytes\n", op->ipl, op->pattern_fn, res); - fprintf(stderr, " so pad with 0x0 bytes and continue\n"); } - if (! got_stdin) - close(infd); - wBuff[0] = op->count & 0x1f;; if (op->test) wBuff[0] |= ((op->test & 0x3) << 5); @@ -482,10 +667,10 @@ main(int argc, char * argv[]) } if ((0 == ret) && (0 == op->early) && (0 == op->wait)) { - for (k = 0, desc = 1 ;; ++k) { + for (k = 0 ;; ++k) { sleep_for(POLL_DURATION_SECS); memset(requestSenseBuff, 0x0, sizeof(requestSenseBuff)); - res = sg_ll_request_sense(sg_fd, desc, requestSenseBuff, + res = sg_ll_request_sense(sg_fd, op->desc, requestSenseBuff, sizeof(requestSenseBuff), 1, vb); if (res) { ret = res; @@ -493,10 +678,10 @@ main(int argc, char * argv[]) fprintf(stderr, "Request Sense command not supported\n"); else if (SG_LIB_CAT_ILLEGAL_REQ == res) { fprintf(stderr, "bad field in Request Sense cdb\n"); - if (1 == desc) { + if (1 == op->desc) { fprintf(stderr, "Descriptor type sense may not be " "supported, try again with fixed type\n"); - desc = 0; + op->desc = 0; continue; } } else { diff --git a/src/sg_sat_identify.c b/src/sg_sat_identify.c index e0daa53..9fa2a7f 100644 --- a/src/sg_sat_identify.c +++ b/src/sg_sat_identify.c @@ -45,7 +45,7 @@ #define EBUFF_SZ 256 -static const char * version_str = "1.10 20140515"; +static const char * version_str = "1.11 20141110"; static struct option long_options[] = { {"ck_cond", no_argument, 0, 'c'}, @@ -103,9 +103,12 @@ static int do_identify_dev(int sg_fd, int do_packet, int cdb_len, int do_hex, int do_raw, int verbose) { int ok, j, res, ret; + /* Following for ATA READ/WRITE MULTIPLE (EXT) cmds, normally 0 */ + int multiple_count = 0; int protocol = 4; /* PIO data-in */ + int t_type = 0; /* 0 -> 512 byte blocks, 1 -> device's LB size */ int t_dir = 1; /* 0 -> to device, 1 -> from device */ - int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */ + int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks (if t_type=0) */ int t_length = 2; /* 0 -> no data transferred, 2 -> sector count */ int resid = 0; int got_ard = 0; /* got ATA result descriptor */ @@ -133,8 +136,8 @@ static int do_identify_dev(int sg_fd, int do_packet, int cdb_len, aptCmdBlk[6] = 1; /* sector count */ aptCmdBlk[14] = (do_packet ? ATA_IDENTIFY_PACKET_DEVICE : ATA_IDENTIFY_DEVICE); - aptCmdBlk[1] = (protocol << 1) | extend; - aptCmdBlk[2] = (ck_cond << 5) | (t_dir << 3) | + aptCmdBlk[1] = (multiple_count << 5) | (protocol << 1) | extend; + aptCmdBlk[2] = (ck_cond << 5) | (t_type << 4) | (t_dir << 3) | (byte_block << 2) | t_length; res = sg_ll_ata_pt(sg_fd, aptCmdBlk, cdb_len, DEF_TIMEOUT, inBuff, NULL /* doutp */, ID_RESPONSE_LEN, sense_buffer, @@ -145,8 +148,8 @@ static int do_identify_dev(int sg_fd, int do_packet, int cdb_len, apt12CmdBlk[4] = 1; /* sector count */ apt12CmdBlk[9] = (do_packet ? ATA_IDENTIFY_PACKET_DEVICE : ATA_IDENTIFY_DEVICE); - apt12CmdBlk[1] = (protocol << 1); - apt12CmdBlk[2] = (ck_cond << 5) | (t_dir << 3) | + apt12CmdBlk[1] = (multiple_count << 5) | (protocol << 1); + apt12CmdBlk[2] = (ck_cond << 5) | (t_type << 4) | (t_dir << 3) | (byte_block << 2) | t_length; res = sg_ll_ata_pt(sg_fd, apt12CmdBlk, cdb_len, DEF_TIMEOUT, inBuff, NULL /* doutp */, ID_RESPONSE_LEN, sense_buffer, @@ -314,9 +317,11 @@ static int do_identify_dev(int sg_fd, int do_packet, int cdb_len, else if (2 == do_hex) dWordHex((const unsigned short *)inBuff, 256, 0, sg_is_big_endian()); - else /* '-HHH' output suitable for "hdparm --Istdin" */ + else if (3 == do_hex) /* '-HHH' suitable for "hdparm --Istdin" */ dWordHex((const unsigned short *)inBuff, 256, -2, sg_is_big_endian()); + else /* '-HHHH' hex bytes only */ + dStrHex((const char *)inBuff, 512, -1); } return 0; } @@ -362,8 +367,8 @@ int main(int argc, char * argv[]) ++do_indent; break; case 'l': - cdb_len = sg_get_num(optarg); - if (! ((cdb_len == 12) || (cdb_len == 16))) { + cdb_len = sg_get_num(optarg); + if (! ((cdb_len == 12) || (cdb_len == 16))) { fprintf(stderr, "argument to '--len' should be 12 or 16\n"); return SG_LIB_SYNTAX_ERROR; } diff --git a/src/sg_sat_phy_event.c b/src/sg_sat_phy_event.c index 7db267a..e465c96 100644 --- a/src/sg_sat_phy_event.c +++ b/src/sg_sat_phy_event.c @@ -21,7 +21,7 @@ #include "sg_cmds_basic.h" #include "sg_cmds_extra.h" -static const char * version_str = "1.04 20140515"; +static const char * version_str = "1.05 20141030"; /* This program uses a ATA PASS-THROUGH SCSI command. This usage is * defined in the SCSI to ATA Translation (SAT) drafts and standards. @@ -153,9 +153,12 @@ do_read_log_ext(int sg_fd, int log_addr, int page_in_log, int feature, int ck_cond, int extend, int do_hex, int do_raw, int verbose) { int ok, res, ret; + /* Following for ATA READ/WRITE MULTIPLE (EXT) cmds, normally 0 */ + int multiple_count = 0; int protocol = 4; /* PIO data-in */ + int t_type = 0; /* 0 -> 512 byte blocks, 1 -> device's LB size */ int t_dir = 1; /* 0 -> to device, 1 -> from device */ - int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */ + int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks (if t_type=0) */ int t_length = 2; /* 0 -> no data transferred, 2 -> sector count */ int resid = 0; int got_ard = 0; /* got ATA result descriptor */ @@ -185,8 +188,8 @@ do_read_log_ext(int sg_fd, int log_addr, int page_in_log, int feature, /* lba_mid(15:8) == LBA(39:32) */ aptCmdBlk[10] = page_in_log & 0xff; /* lba_mid(7:0) == LBA(15:8) */ aptCmdBlk[14] = ATA_READ_LOG_EXT; - aptCmdBlk[1] = (protocol << 1) | extend; - aptCmdBlk[2] = (ck_cond << 5) | (t_dir << 3) | + aptCmdBlk[1] = (multiple_count << 5) | (protocol << 1) | extend; + aptCmdBlk[2] = (ck_cond << 5) | (t_type << 4) | (t_dir << 3) | (byte_block << 2) | t_length; res = sg_ll_ata_pt(sg_fd, aptCmdBlk, cdb_len, DEF_TIMEOUT, resp, NULL /* doutp */, mx_resp_len, sense_buffer, @@ -199,8 +202,8 @@ do_read_log_ext(int sg_fd, int log_addr, int page_in_log, int feature, apt12CmdBlk[5] = log_addr & 0xff; /* lba_low(7:0) == LBA(7:0) */ apt12CmdBlk[6] = page_in_log & 0xff; /* lba_mid(7:0) == LBA(15:8) */ apt12CmdBlk[9] = ATA_READ_LOG_EXT; - apt12CmdBlk[1] = (protocol << 1); - apt12CmdBlk[2] = (ck_cond << 5) | (t_dir << 3) | + apt12CmdBlk[1] = (multiple_count << 5) | (protocol << 1); + apt12CmdBlk[2] = (ck_cond << 5) | (t_type << 4) | (t_dir << 3) | (byte_block << 2) | t_length; res = sg_ll_ata_pt(sg_fd, apt12CmdBlk, cdb_len, DEF_TIMEOUT, resp, NULL /* doutp */, mx_resp_len, sense_buffer, @@ -378,8 +381,8 @@ int main(int argc, char * argv[]) ++ignore; break; case 'l': - cdb_len = sg_get_num(optarg); - if (! ((cdb_len == 12) || (cdb_len == 16))) { + cdb_len = sg_get_num(optarg); + if (! ((cdb_len == 12) || (cdb_len == 16))) { fprintf(stderr, "argument to '--len' should be 12 or 16\n"); return SG_LIB_SYNTAX_ERROR; } diff --git a/src/sg_sat_read_gplog.c b/src/sg_sat_read_gplog.c new file mode 100644 index 0000000..0757635 --- /dev/null +++ b/src/sg_sat_read_gplog.c @@ -0,0 +1,454 @@ +/* + * Copyright (c) 2014 Hannes Reinecke, SUSE Linux GmbH. + * All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the BSD_LICENSE file. + */ + +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include <getopt.h> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "sg_lib.h" +#include "sg_cmds_basic.h" +#include "sg_cmds_extra.h" + +/* This program uses a ATA PASS-THROUGH SCSI command. This usage is + * defined in the SCSI to ATA Translation (SAT) drafts and standards. + * See http://www.t10.org for drafts. SAT is a standard: SAT ANSI INCITS + * 431-2007 (draft prior to that is sat-r09.pdf). SAT-2 is also a + * standard: SAT-2 ANSI INCITS 465-2010 and the draft prior to that is + * sat2r09.pdf . The SAT-3 project has started and the most recent draft + * is sat3r01.pdf . + */ + +/* This program performs a ATA PASS-THROUGH (16) SCSI command in order + * to perform an ATA READ LOG EXT or ATA READ LOG DMA EXT command. + * + * See man page (sg_sat_read_gplog.8) for details. + */ + +#define SAT_ATA_PASS_THROUGH16 0x85 +#define SAT_ATA_PASS_THROUGH16_LEN 16 +#define SAT_ATA_PASS_THROUGH12 0xa1 /* clashes with MMC BLANK comand */ +#define SAT_ATA_PASS_THROUGH12_LEN 12 +#define SAT_ATA_RETURN_DESC 9 /* ATA Return (sense) Descriptor */ +#define ASCQ_ATA_PT_INFO_AVAILABLE 0x1d + +#define ATA_READ_LOG_EXT 0x2f +#define ATA_READ_LOG_DMA_EXT 0x47 + +#define DEF_TIMEOUT 20 + +static const char * version_str = "1.09 20141110"; + +struct opts_t { + int cdb_len; + int ck_cond; + int count; + int hex; + int la; /* log address */ + int pn; /* page number within log address */ + int rdonly; + int verbose; + const char * device_name; +}; + +static struct option long_options[] = { + {"count", required_argument, 0, 'c'}, + {"ck_cond", no_argument, 0, 'C'}, + {"dma", no_argument, 0, 'd'}, + {"help", no_argument, 0, 'h'}, + {"hex", no_argument, 0, 'H'}, + {"len", required_argument, 0, 'l'}, + {"log", required_argument, 0, 'L'}, + {"page", required_argument, 0, 'p'}, + {"readonly", no_argument, 0, 'r'}, + {"verbose", no_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, + {0, 0, 0, 0}, +}; + + +#ifdef __GNUC__ +static int pr2serr(const char * fmt, ...) + __attribute__ ((format (printf, 1, 2))); +#else +static int pr2serr(const char * fmt, ...); +#endif + + +static int +pr2serr(const char * fmt, ...) +{ + va_list args; + int n; + + va_start(args, fmt); + n = vfprintf(stderr, fmt, args); + va_end(args); + return n; +} + +static void +usage() +{ + pr2serr("Usage: " + "sg_sat_read_gplog [--ck_cond] [--count=CO] [--dma] [--help]\n" + " [--hex] [--len=16|12] [--log=LA] " + "[--page=PN]\n" + " [--readonly] [--verbose] [--version] " + "DEVICE\n" + " where:\n" + " --ck_cond | -C set ck_cond field in pass-through " + "(def: 0)\n" + " --count=CO | -c CO block count (def: 1)\n" + " --dma | -d Use READ LOG DMA EXT (def: READ LOG " + "EXT)\n" + " --help | -h output this usage message\n" + " --hex | -H output response in hex bytes, -HH " + "yields hex\n" + " words + ASCII (def), -HHH hex words " + "only\n" + " --len=16|12 | -l 16|12 cdb length: 16 or 12 bytes " + "(def: 16)\n" + " --log=LA | -L LA Log address to be read (def: 0)\n" + " --page=PN|-p PN Log page number within address (def: " + "0)\n" + " --readonly | -r open DEVICE read-only (def: " + "read-write)\n" + " --verbose | -v increase verbosity\n" + " recommended if DEVICE is ATA disk\n" + " --version | -V print version string and exit\n\n" + "Sends an ATA READ LOG EXT (or READ LOG DMA EXT) command via a " + "SAT pass\nthrough to fetch a General Purpose (GP) log page. Each " + "page is accessed\nvia a log address and then a page number " + "within that address: LA,PN .\n" + "By default the output is the response in hex (16 bit) words.\n" + ); +} + +static int +do_read_gplog(int sg_fd, int ata_cmd, unsigned char *inbuff, + const struct opts_t * op) +{ + int res, ret; + int extend = 1; + int protocol; + int t_dir = 1; /* 0 -> to device, 1 -> from device */ + int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */ + int t_length = 2; /* 0 -> no data transferred, 2 -> sector count */ + int t_type = 0; /* 0 -> 512 byte blocks, 1 -> logical sectors */ + int resid = 0; + int got_ard = 0; /* got ATA result descriptor */ + int sb_sz; + struct sg_scsi_sense_hdr ssh; + unsigned char sense_buffer[64]; + unsigned char ata_return_desc[16]; + unsigned char aptCmdBlk[SAT_ATA_PASS_THROUGH16_LEN] = + {SAT_ATA_PASS_THROUGH16, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}; + unsigned char apt12CmdBlk[SAT_ATA_PASS_THROUGH12_LEN] = + {SAT_ATA_PASS_THROUGH12, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0}; + char cmd_name[32]; + + snprintf(cmd_name, sizeof(cmd_name), "ATA PASS-THROUGH (%d)", + op->cdb_len); + if (ata_cmd == ATA_READ_LOG_DMA_EXT) { + protocol = 6; /* DMA */ + } else { + protocol = 4; /* PIO Data-In */ + } + sb_sz = sizeof(sense_buffer); + memset(sense_buffer, 0, sb_sz); + memset(ata_return_desc, 0, sizeof(ata_return_desc)); + memset(inbuff, 0, op->count * 512); + if (op->verbose > 1) + pr2serr("Building ATA READ LOG%s EXT command; la=0x%x, pn=0x%x\n", + ((ata_cmd == ATA_READ_LOG_DMA_EXT) ? " DMA" : ""), op->la, + op->pn); + if (op->cdb_len == 16) { + /* Prepare ATA PASS-THROUGH COMMAND (16) command */ + aptCmdBlk[14] = ata_cmd; + aptCmdBlk[5] = (op->count >> 8) & 0xff; + aptCmdBlk[6] = op->count & 0xff; + aptCmdBlk[8] = op->la; + aptCmdBlk[9] = (op->pn >> 8) & 0xff; + aptCmdBlk[10] = op->pn & 0xff; + aptCmdBlk[1] = (protocol << 1) | extend; + aptCmdBlk[2] = (op->ck_cond << 5) | (t_type << 4) | (t_dir << 3) | + (byte_block << 2) | t_length; + res = sg_ll_ata_pt(sg_fd, aptCmdBlk, op->cdb_len, DEF_TIMEOUT, inbuff, + NULL, op->count * 512, sense_buffer, + sb_sz, ata_return_desc, + sizeof(ata_return_desc), &resid, op->verbose); + } else { + /* Prepare ATA PASS-THROUGH COMMAND (12) command */ + apt12CmdBlk[9] = ata_cmd; + apt12CmdBlk[4] = op->count; + apt12CmdBlk[5] = op->la; + apt12CmdBlk[6] = op->pn & 0xff; + apt12CmdBlk[7] = (op->pn >> 8) & 0xff; + apt12CmdBlk[1] = (protocol << 1); + apt12CmdBlk[2] = (op->ck_cond << 5) | (t_type << 4) | (t_dir << 3) | + (byte_block << 2) | t_length; + res = sg_ll_ata_pt(sg_fd, apt12CmdBlk, op->cdb_len, DEF_TIMEOUT, + inbuff, NULL, op->count * 512, sense_buffer, + sb_sz, ata_return_desc, + sizeof(ata_return_desc), &resid, op->verbose); + } + if (0 == res) { + if (op->verbose > 2) + pr2serr("command completed with SCSI GOOD status\n"); + if ((0 == op->hex) || (2 == op->hex)) + dWordHex((const unsigned short *)inbuff, op->count * 256, 0, + sg_is_big_endian()); + else if (1 == op->hex) + dStrHex((const char *)inbuff, 512, 0); + else if (3 == op->hex) /* '-HHH' suitable for "hdparm --Istdin" */ + dWordHex((const unsigned short *)inbuff, 256, -2, + sg_is_big_endian()); + else /* '-HHHH' hex bytes only */ + dStrHex((const char *)inbuff, 512, -1); + } else if ((res > 0) && (res & SAM_STAT_CHECK_CONDITION)) { + if (op->verbose > 1) + sg_print_sense("ATA pass through", sense_buffer, sb_sz, + ((op->verbose > 2) ? 1 : 0)); + if (sg_scsi_normalize_sense(sense_buffer, sb_sz, &ssh)) { + switch (ssh.sense_key) { + case SPC_SK_ILLEGAL_REQUEST: + if ((0x20 == ssh.asc) && (0x0 == ssh.ascq)) { + ret = SG_LIB_CAT_INVALID_OP; + if (op->verbose < 2) + pr2serr("%s not supported\n", cmd_name); + } else { + ret = SG_LIB_CAT_ILLEGAL_REQ; + if (op->verbose < 2) + pr2serr("%s, bad field in cdb\n", cmd_name); + } + return ret; + case SPC_SK_NO_SENSE: + case SPC_SK_RECOVERED_ERROR: + if ((0x0 == ssh.asc) && + (ASCQ_ATA_PT_INFO_AVAILABLE == ssh.ascq)) { + if (SAT_ATA_RETURN_DESC != ata_return_desc[0]) { + if (op->verbose) + pr2serr("did not find ATA Return (sense) " + "Descriptor\n"); + return SG_LIB_CAT_RECOVERED; + } + got_ard = 1; + break; + } else if (SPC_SK_RECOVERED_ERROR == ssh.sense_key) + return SG_LIB_CAT_RECOVERED; + else { + if ((0x0 == ssh.asc) && (0x0 == ssh.ascq)) + break; + return SG_LIB_CAT_SENSE; + } + case SPC_SK_UNIT_ATTENTION: + if (op->verbose < 2) + pr2serr("%s, Unit Attention detected\n", cmd_name); + return SG_LIB_CAT_UNIT_ATTENTION; + case SPC_SK_NOT_READY: + if (op->verbose < 2) + pr2serr("%s, device not ready\n", cmd_name); + return SG_LIB_CAT_NOT_READY; + case SPC_SK_MEDIUM_ERROR: + case SPC_SK_HARDWARE_ERROR: + if (op->verbose < 2) + pr2serr("%s, medium or hardware error\n", cmd_name); + return SG_LIB_CAT_MEDIUM_HARD; + case SPC_SK_ABORTED_COMMAND: + if (0x10 == ssh.asc) { + pr2serr("Aborted command: protection information\n"); + return SG_LIB_CAT_PROTECTION; + } else { + pr2serr("Aborted command\n"); + return SG_LIB_CAT_ABORTED_COMMAND; + } + case SPC_SK_DATA_PROTECT: + pr2serr("%s: data protect, read only media?\n", cmd_name); + return SG_LIB_CAT_DATA_PROTECT; + default: + if (op->verbose < 2) + pr2serr("%s, some sense data, use '-v' for more " + "information\n", cmd_name); + return SG_LIB_CAT_SENSE; + } + } else { + pr2serr("CHECK CONDITION without response code ??\n"); + return SG_LIB_CAT_SENSE; + } + if (0x72 != (sense_buffer[0] & 0x7f)) { + pr2serr("expected descriptor sense format, response " + "code=0x%x\n", sense_buffer[0]); + return SG_LIB_CAT_MALFORMED; + } + } else if (res > 0) { + if (SAM_STAT_RESERVATION_CONFLICT == res) { + pr2serr("SCSI status: RESERVATION CONFLICT\n"); + return SG_LIB_CAT_RES_CONFLICT; + } else { + pr2serr("Unexpected SCSI status=0x%x\n", res); + return SG_LIB_CAT_MALFORMED; + } + } else { + pr2serr("%s failed\n", cmd_name); + if (op->verbose < 2) + pr2serr(" try adding '-v' for more information\n"); + return -1; + } + + if ((SAT_ATA_RETURN_DESC == ata_return_desc[0]) && (0 == got_ard)) + pr2serr("Seem to have got ATA Result Descriptor but it was not " + "indicated\n"); + if (got_ard) { + if (ata_return_desc[3] & 0x4) { + pr2serr("error indication in returned FIS: aborted " + "command\n"); + return SG_LIB_CAT_ABORTED_COMMAND; + } + } + return 0; +} + + +int +main(int argc, char * argv[]) +{ + int sg_fd, c, ret, res, n; + int ata_cmd = ATA_READ_LOG_EXT; + unsigned char *inbuff; + struct opts_t opts; + struct opts_t * op; + + op = &opts; + memset(op, 0, sizeof(opts)); + op->cdb_len = SAT_ATA_PASS_THROUGH16_LEN; + op->count = 1; + while (1) { + int option_index = 0; + + c = getopt_long(argc, argv, "c:CdhHl:L:p:rvV", long_options, + &option_index); + if (c == -1) + break; + + switch (c) { + case 'c': + op->count = sg_get_num(optarg); + if ((op->count < 1) || (op->count > 0xffff)) { + pr2serr("bad argument for '--count'\n"); + return SG_LIB_SYNTAX_ERROR; + } + break; + case 'C': + op->ck_cond = 1; + break; + case 'd': + ata_cmd = ATA_READ_LOG_DMA_EXT; + break; + case 'h': + case '?': + usage(); + return 0; + case 'H': + ++op->hex; + break; + case 'l': + op->cdb_len = sg_get_num(optarg); + if (! ((op->cdb_len == 12) || (op->cdb_len == 16))) { + pr2serr("argument to '--len' should be 12 or 16\n"); + return SG_LIB_SYNTAX_ERROR; + } + break; + case 'L': + op->la = sg_get_num(optarg); + if (op->la < 0 || op->la > 0xff) { + pr2serr("bad argument for '--log'\n"); + return SG_LIB_SYNTAX_ERROR; + } + break; + case 'p': + op->pn = sg_get_num(optarg); + if ((op->pn < 0) || (op->pn > 0xffff)) { + pr2serr("bad argument for '--page'\n"); + return SG_LIB_SYNTAX_ERROR; + } + break; + case 'r': + ++op->rdonly; + break; + case 'v': + ++op->verbose; + break; + case 'V': + pr2serr("version: %s\n", version_str); + return 0; + default: + pr2serr("unrecognised option code 0x%x ??\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 (NULL == op->device_name) { + pr2serr("missing device name!\n"); + usage(); + return 1; + } + + if ((op->count > 0xff) && (12 == op->cdb_len)) { + op->cdb_len = 16; + if (op->verbose) + pr2serr("Since count > 0xff, forcing cdb length to " + "16\n"); + } + + n = op->count * 512; + inbuff = malloc(n); + if (!inbuff) { + pr2serr("Cannot allocate output buffer of size %d\n", n); + return SG_LIB_CAT_OTHER; + } + + if ((sg_fd = sg_cmds_open_device(op->device_name, op->rdonly, + op->verbose)) < 0) { + pr2serr("error opening file: %s: %s\n", op->device_name, + safe_strerror(-sg_fd)); + return SG_LIB_FILE_ERROR; + } + + ret = do_read_gplog(sg_fd, ata_cmd, inbuff, op); + + res = sg_cmds_close_device(sg_fd); + if (res < 0) { + pr2serr("close error: %s\n", safe_strerror(-res)); + if (0 == ret) + return SG_LIB_FILE_ERROR; + } + return (ret >= 0) ? ret : SG_LIB_CAT_OTHER; +} diff --git a/src/sg_sat_set_features.c b/src/sg_sat_set_features.c index 633df61..fd4011d 100644 --- a/src/sg_sat_set_features.c +++ b/src/sg_sat_set_features.c @@ -46,19 +46,20 @@ #define DEF_TIMEOUT 20 -static const char * version_str = "1.08 20140515"; +static const char * version_str = "1.10 20141106"; static struct option long_options[] = { - {"count", required_argument, 0, 'c'}, - {"ck_cond", no_argument, 0, 'C'}, - {"feature", required_argument, 0, 'f'}, - {"help", no_argument, 0, 'h'}, - {"len", required_argument, 0, 'l'}, - {"lba", required_argument, 0, 'L'}, - {"readonly", no_argument, 0, 'r'}, - {"verbose", no_argument, 0, 'v'}, - {"version", no_argument, 0, 'V'}, - {0, 0, 0, 0}, + {"count", required_argument, 0, 'c'}, + {"ck_cond", no_argument, 0, 'C'}, + {"extended", no_argument, 0, 'e'}, + {"feature", required_argument, 0, 'f'}, + {"help", no_argument, 0, 'h'}, + {"len", required_argument, 0, 'l'}, + {"lba", required_argument, 0, 'L'}, + {"readonly", no_argument, 0, 'r'}, + {"verbose", no_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, + {0, 0, 0, 0}, }; @@ -66,17 +67,18 @@ static void usage() { fprintf(stderr, "Usage: " - "sg_sat_set_features [--count=CO] [--ck_cond] [--feature=FEA] " - "[--help]\n" - " [--lba=LBA] [--len=16|12] [--readonly] " - "[--verbose]\n" - " [--version] DEVICE\n" + "sg_sat_set_features [--count=CO] [--ck_cond] [--extended] " + "[--feature=FEA]\n" + " [--help] [--lba=LBA] [--len=16|12] " + "[--readonly]\n" + " [--verbose] [--version] DEVICE\n" " where:\n" " --count=CO | -c CO count field contents (def: 0)\n" " --ck_cond | -C set ck_cond field in pass-through " "(def: 0)\n" - " --feature=FEA|-f FEA feature field contents\n" - " (def: 0 (which is reserved))\n" + " --extended | -e enable extended lba values\n" + " --feature=FEA|-f FEA feature field contents\n" + " (def: 0 (which is reserved))\n" " --help | -h output this usage message\n" " --lba=LBA | -L LBA LBA field contents (def: 0)\n" " meaning depends on sub-command " @@ -100,14 +102,16 @@ usage() } static int -do_set_features(int sg_fd, int feature, int count, unsigned int lba, - int cdb_len, int ck_cond, int verbose) +do_set_features(int sg_fd, int feature, int count, uint64_t lba, + int cdb_len, int ck_cond, int extend, int verbose) { int res, ret; - int extend = 0; + /* Following for ATA READ/WRITE MULTIPLE (EXT) cmds, normally 0 */ + int multiple_count = 0; int protocol = 3; /* non-data */ + int t_type = 0; /* 0 -> 512 byte blocks, 1 -> device's LB size */ int t_dir = 1; /* 0 -> to device, 1 -> from device */ - int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */ + int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks (if t_type=0) */ int t_length = 0; /* 0 -> no data transferred, 2 -> sector count */ int resid = 0; int got_ard = 0; /* got ATA result descriptor */ @@ -134,8 +138,10 @@ do_set_features(int sg_fd, int feature, int count, unsigned int lba, aptCmdBlk[10] = (lba >> 8) & 0xff; aptCmdBlk[12] = (lba >> 16) & 0xff; aptCmdBlk[7] = (lba >> 24) & 0xff; - aptCmdBlk[1] = (protocol << 1) | extend; - aptCmdBlk[2] = (ck_cond << 5) | (t_dir << 3) | + aptCmdBlk[9] = (lba >> 32) & 0xff; + aptCmdBlk[11] = (lba >> 40) & 0xff; + aptCmdBlk[1] = (multiple_count << 5) | (protocol << 1) | extend; + aptCmdBlk[2] = (ck_cond << 5) | (t_type << 4)| (t_dir << 3) | (byte_block << 2) | t_length; res = sg_ll_ata_pt(sg_fd, aptCmdBlk, cdb_len, DEF_TIMEOUT, NULL, NULL /* doutp */, 0, sense_buffer, @@ -149,8 +155,8 @@ do_set_features(int sg_fd, int feature, int count, unsigned int lba, apt12CmdBlk[5] = lba & 0xff; apt12CmdBlk[6] = (lba >> 8) & 0xff; apt12CmdBlk[7] = (lba >> 16) & 0xff; - apt12CmdBlk[1] = (protocol << 1); - apt12CmdBlk[2] = (ck_cond << 5) | (t_dir << 3) | + apt12CmdBlk[1] = (multiple_count << 5) | (protocol << 1); + apt12CmdBlk[2] = (ck_cond << 5) | (t_type << 4) | (t_dir << 3) | (byte_block << 2) | t_length; res = sg_ll_ata_pt(sg_fd, apt12CmdBlk, cdb_len, DEF_TIMEOUT, NULL, NULL /* doutp */, 0, sense_buffer, @@ -274,12 +280,13 @@ do_set_features(int sg_fd, int feature, int count, unsigned int lba, int main(int argc, char * argv[]) { - int sg_fd, c, k, ret, res; + int sg_fd, c, ret, res; const char * device_name = NULL; int count = 0; + int extend = 0; int rdonly = 0; int feature = 0; - unsigned int lba = 0; + uint64_t lba = 0; int verbose = 0; int ck_cond = 0; int cdb_len = SAT_ATA_PASS_THROUGH16_LEN; @@ -287,7 +294,7 @@ main(int argc, char * argv[]) while (1) { int option_index = 0; - c = getopt_long(argc, argv, "c:Cf:hl:L:rvV", long_options, + c = getopt_long(argc, argv, "c:Cef:hl:L:rvV", long_options, &option_index); if (c == -1) break; @@ -303,6 +310,9 @@ main(int argc, char * argv[]) case 'C': ck_cond = 1; break; + case 'e': + extend = 1; + break; case 'f': feature = sg_get_num(optarg); if ((feature < 0) || (feature > 255)) { @@ -315,19 +325,20 @@ main(int argc, char * argv[]) usage(); return 0; case 'l': - cdb_len = sg_get_num(optarg); - if (! ((cdb_len == 12) || (cdb_len == 16))) { + cdb_len = sg_get_num(optarg); + if (! ((cdb_len == 12) || (cdb_len == 16))) { fprintf(stderr, "argument to '--len' should be 12 or 16\n"); return SG_LIB_SYNTAX_ERROR; } break; - case 'L': /* up to 26 bits, allow for 32 bits (less -1) */ - k = sg_get_num(optarg); - if (-1 == k) { + case 'L': /* up to 32 bits, allow for 48 bits (less -1) */ + lba = sg_get_llnum(optarg); + if ((uint64_t)-1 == lba) { fprintf(stderr, "bad argument for '--lba'\n"); return SG_LIB_SYNTAX_ERROR; } - lba = (unsigned int)k; + if (lba > 0xffffffff) + extend = 1; break; case 'r': ++rdonly; @@ -378,7 +389,7 @@ main(int argc, char * argv[]) } ret = do_set_features(sg_fd, feature, count, lba, cdb_len, ck_cond, - verbose); + extend, verbose); res = sg_cmds_close_device(sg_fd); if (res < 0) { diff --git a/src/sg_scan.c.win32 b/src/sg_scan.c.win32 index f9463f9..faaa7ce 100644 --- a/src/sg_scan.c.win32 +++ b/src/sg_scan.c.win32 @@ -37,7 +37,7 @@ #include "sg_pt_win32.h" -static const char * version_str = "1.12 (win32) 20130513"; +static const char * version_str = "1.15 (win32) 20140827"; #define MAX_SCSI_ELEMS 1024 #define MAX_ADAPTER_NUM 64 @@ -143,7 +143,7 @@ union STORAGE_DEVICE_DESCRIPTOR_DATA { union STORAGE_DEVICE_UID_DATA { STORAGE_DEVICE_UNIQUE_IDENTIFIER desc; - char raw[512]; + char raw[1060]; }; struct storage_elem { @@ -280,20 +280,20 @@ query_dev_property(HANDLE hdevice, if (verbose > 2) { err = GetLastError(); fprintf(stderr, " IOCTL_STORAGE_QUERY_PROPERTY(Devprop) failed, " - "Error=%ld %s\n", err, get_err_str(err, sizeof(b), b)); + "Error=%u %s\n", (unsigned int)err, + get_err_str(err, sizeof(b), b)); } return -ENOSYS; } if (verbose > 3) fprintf(stderr, " IOCTL_STORAGE_QUERY_PROPERTY(DevProp) " - "num_out=%ld\n", num_out); + "num_out=%u\n", (unsigned int)num_out); return 0; } static int -query_dev_uid(HANDLE hdevice, - union STORAGE_DEVICE_UID_DATA * data) +query_dev_uid(HANDLE hdevice, union STORAGE_DEVICE_UID_DATA * data) { DWORD num_out, err; char b[256]; @@ -301,19 +301,37 @@ query_dev_uid(HANDLE hdevice, PropertyStandardQuery, {0} }; memset(data, 0, sizeof(*data)); + num_out = 0; + query.QueryType = PropertyExistsQuery; + if (! DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY, + &query, sizeof(query), NULL, 0, &num_out, NULL)) { + if (verbose > 2) { + err = GetLastError(); + fprintf(stderr, " IOCTL_STORAGE_QUERY_PROPERTY(DevUid(exists)) " + "failed, Error=%u %s\n", (unsigned int)err, + get_err_str(err, sizeof(b), b)); + } + if (verbose > 3) + fprintf(stderr, " num_out=%u\n", (unsigned int)num_out); + /* interpret any error to mean this property doesn't exist */ + return 0; + } + + query.QueryType = PropertyStandardQuery; if (! DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query), data, sizeof(*data), &num_out, NULL)) { if (verbose > 2) { err = GetLastError(); fprintf(stderr, " IOCTL_STORAGE_QUERY_PROPERTY(DevUid) failed, " - "Error=%ld %s\n", err, get_err_str(err, sizeof(b), b)); + "Error=%u %s\n", (unsigned int)err, + get_err_str(err, sizeof(b), b)); } return -ENOSYS; } if (verbose > 3) - fprintf(stderr, " IOCTL_STORAGE_QUERY_PROPERTY(DevUid) num_out=%ld\n", - num_out); + fprintf(stderr, " IOCTL_STORAGE_QUERY_PROPERTY(DevUid) num_out=%u\n", + (unsigned int)num_out); return 0; } @@ -366,7 +384,7 @@ enum_scsi_adapters(void) BYTE bus; BOOL success; char adapter_name[64]; - char inqDataBuff[2048]; + char inqDataBuff[8192]; PSCSI_ADAPTER_BUS_INFO ai; char b[256]; @@ -379,7 +397,7 @@ enum_scsi_adapters(void) hole_count = 0; success = DeviceIoControl(fh, IOCTL_SCSI_GET_INQUIRY_DATA, NULL, 0, inqDataBuff, - sizeof(inqDataBuff), &dummy, FALSE); + sizeof(inqDataBuff), &dummy, NULL); if (success) { PSCSI_BUS_DATA pbd; PSCSI_INQUIRY_DATA pid; @@ -413,16 +431,19 @@ enum_scsi_adapters(void) } else { err = GetLastError(); fprintf(stderr, "%s: IOCTL_SCSI_GET_INQUIRY_DATA failed " - "err=%lu\n\t%s", - adapter_name, err, get_err_str(err, sizeof(b), b)); + "err=%u\n\t%s", adapter_name, (unsigned int)err, + get_err_str(err, sizeof(b), b)); } CloseHandle(fh); } else { - if (verbose > 3) { - err = GetLastError(); - fprintf(stderr, "%s: CreateFile failed err=%lu\n\t%s", - adapter_name, err, get_err_str(err, sizeof(b), b)); - } + err = GetLastError(); + if (ERROR_SHARING_VIOLATION == err) + fprintf(stderr, "%s: in use by other process (sharing " + "violation [34])\n", adapter_name); + else if (verbose > 3) + fprintf(stderr, "%s: CreateFile failed err=%u\n\t%s", + adapter_name, (unsigned int)err, + get_err_str(err, sizeof(b), b)); if (++hole_count >= MAX_HOLE_COUNT) break; } @@ -504,11 +525,14 @@ enum_pds(void) memcpy(&storage_arr[next_unused_elem++], &tmp_se, sizeof(tmp_se)); CloseHandle(fh); } else { - if (verbose > 3) { - err = GetLastError(); - fprintf(stderr, "%s: CreateFile failed err=%lu\n\t%s", - adapter_name, err, get_err_str(err, sizeof(b), b)); - } + err = GetLastError(); + if (ERROR_SHARING_VIOLATION == err) + fprintf(stderr, "%s: in use by other process (sharing " + "violation [34])\n", adapter_name); + else if (verbose > 3) + fprintf(stderr, "%s: CreateFile failed err=%u\n\t%s", + adapter_name, (unsigned int)err, + get_err_str(err, sizeof(b), b)); if (++hole_count >= MAX_HOLE_COUNT) break; } @@ -552,11 +576,14 @@ enum_cdroms(void) memcpy(&storage_arr[next_unused_elem++], &tmp_se, sizeof(tmp_se)); CloseHandle(fh); } else { - if (verbose > 3) { - err = GetLastError(); - fprintf(stderr, "%s: CreateFile failed err=%lu\n\t%s", - adapter_name, err, get_err_str(err, sizeof(b), b)); - } + err = GetLastError(); + if (ERROR_SHARING_VIOLATION == err) + fprintf(stderr, "%s: in use by other process (sharing " + "violation [34])\n", adapter_name); + else if (verbose > 3) + fprintf(stderr, "%s: CreateFile failed err=%u\n\t%s", + adapter_name, (unsigned int)err, + get_err_str(err, sizeof(b), b)); if (++hole_count >= MAX_HOLE_COUNT) break; } @@ -600,11 +627,14 @@ enum_tapes(void) memcpy(&storage_arr[next_unused_elem++], &tmp_se, sizeof(tmp_se)); CloseHandle(fh); } else { - if (verbose > 3) { - err = GetLastError(); - fprintf(stderr, "%s: CreateFile failed err=%lu\n\t%s", - adapter_name, err, get_err_str(err, sizeof(b), b)); - } + err = GetLastError(); + if (ERROR_SHARING_VIOLATION == err) + fprintf(stderr, "%s: in use by other process (sharing " + "violation [34])\n", adapter_name); + else if (verbose > 3) + fprintf(stderr, "%s: CreateFile failed err=%u\n\t%s", + adapter_name, (unsigned int)err, + get_err_str(err, sizeof(b), b)); if (++hole_count >= MAX_HOLE_COUNT) break; } @@ -668,12 +698,12 @@ sg_do_wscan(char letter, int show_bt, int scsi_scan) printf("%s", sp->qp_descriptor.raw + j); printf("\n"); if (verbose > 2) - dStrHex(sp->qp_descriptor.raw, 144, 0); + dStrHexErr(sp->qp_descriptor.raw, 144, 0); } else printf("\n"); if ((verbose > 3) && sp->qp_uid_valid) { printf(" UID valid, in hex:\n"); - dStrHex(sp->qp_uid.raw, sizeof(sp->qp_uid.raw), 1); + dStrHexErr(sp->qp_uid.raw, sizeof(sp->qp_uid.raw), 1); } } } diff --git a/src/sg_senddiag.c b/src/sg_senddiag.c index 3b7333c..9dc148f 100644 --- a/src/sg_senddiag.c +++ b/src/sg_senddiag.c @@ -23,28 +23,33 @@ #include "sg_lib.h" #include "sg_cmds_basic.h" #include "sg_cmds_extra.h" +#include "sg_pt.h" /* needed for scsi_pt_win32_direct() */ +#include "sg_unaligned.h" -static const char * version_str = "0.41 20140517"; + +static const char * version_str = "0.44 20140919"; #define ME "sg_senddiag: " -#define MX_ALLOC_LEN (1024 * 4) +#define DEF_ALLOC_LEN (1024 * 4) static struct option long_options[] = { - {"doff", 0, 0, 'd'}, - {"extdur", 0, 0, 'e'}, - {"help", 0, 0, 'h'}, - {"hex", 0, 0, 'H'}, - {"list", 0, 0, 'l'}, - {"new", 0, 0, 'N'}, - {"old", 0, 0, 'O'}, - {"pf", 0, 0, 'p'}, - {"raw", 1, 0, 'r'}, - {"selftest", 1, 0, 's'}, - {"test", 0, 0, 't'}, - {"uoff", 0, 0, 'u'}, - {"verbose", 0, 0, 'v'}, - {"version", 0, 0, 'V'}, + {"doff", no_argument, 0, 'd'}, + {"extdur", no_argument, 0, 'e'}, + {"help", no_argument, 0, 'h'}, + {"hex", no_argument, 0, 'H'}, + {"list", no_argument, 0, 'l'}, + {"maxlen", required_argument, 0, 'm'}, + {"new", no_argument, 0, 'N'}, + {"old", no_argument, 0, 'O'}, + {"page", required_argument, 0, 'P'}, + {"pf", no_argument, 0, 'p'}, + {"raw", required_argument, 0, 'r'}, + {"selftest", required_argument, 0, 's'}, + {"test", no_argument, 0, 't'}, + {"uoff", no_argument, 0, 'u'}, + {"verbose", no_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, {0, 0, 0, 0}, }; @@ -54,6 +59,8 @@ struct opts_t { int do_help; int do_hex; int do_list; + int maxlen; + int page_code; int do_pf; int do_raw; int do_selftest; @@ -66,22 +73,30 @@ struct opts_t { int opt_new; }; -static void usage() + +static void +usage() { printf("Usage: sg_senddiag [--doff] [--extdur] [--help] [--hex] " - "[--list] [--pf]\n" - " [--raw=H,H...] [--selftest=ST] " - "[--test] [--uoff]\n" - " [--verbose] [--version] " - "[DEVICE]\n" + "[--list]\n" + " [--maxlen=LEN] [--page=PG] [--pf] " + "[--raw=H,H...]\n" + " [--selftest=ST] [--test] [--uoff] " + "[--verbose] [--version]\n" + " [DEVICE]\n" " where:\n" " --doff|-d device online (def: 0, only with '--test')\n" " --extdur|-e duration of an extended self-test (from mode " "page 0xa)\n" " --help|-h print usage message then exit\n" - " --hex|H output in hex\n" + " --hex|-H output in hex\n" " --list|-l list supported page codes (with or without " "DEVICE)\n" + " --maxlen=LEN|-m LEN parameter list length or maximum " + "allocation\n" + " length (default: 4096 bytes)\n" + " --page=PG|-p PG do RECEIVE DIAGNOSTIC RESULTS only, set " + "PCV\n" " --pf|-p set PF bit (def: 0)\n" " --raw=H,H...|-r H,H... sequence of hex bytes to form " "diag page to send\n" @@ -102,7 +117,8 @@ static void usage() ); } -static void usage_old() +static void +usage_old() { printf("Usage: sg_senddiag [-doff] [-e] [-h] [-H] [-l] [-pf]" " [-raw=H,H...]\n" @@ -133,46 +149,65 @@ static void usage_old() ); } -static int process_cl_new(struct opts_t * optsp, int argc, char * argv[]) +static int +process_cl_new(struct opts_t * op, int argc, char * argv[]) { int c, n; while (1) { int option_index = 0; - c = getopt_long(argc, argv, "dehHlNOpr:s:tuvV", long_options, + c = getopt_long(argc, argv, "dehHlm:NOpP:r:s:tuvV", long_options, &option_index); if (c == -1) break; switch (c) { case 'd': - optsp->do_doff = 1; + op->do_doff = 1; break; case 'e': - optsp->do_extdur = 1; + op->do_extdur = 1; break; case 'h': case '?': - ++optsp->do_help; + ++op->do_help; break; case 'H': - ++optsp->do_hex; + ++op->do_hex; break; case 'l': - ++optsp->do_list; + ++op->do_list; + break; + case 'm': + n = sg_get_num(optarg); + if ((n < 0) || (n > 0xffff)) { + fprintf(stderr, "bad argument to '--maxlen=' or greater " + "than 65535 [0xffff]\n"); + return SG_LIB_SYNTAX_ERROR; + } + op->maxlen = n; break; case 'N': break; /* ignore */ case 'O': - optsp->opt_new = 0; + op->opt_new = 0; return 0; case 'p': - optsp->do_pf = 1; + op->do_pf = 1; + break; + case 'P': + n = sg_get_num(optarg); + if ((n < 0) || (n > 0xff)) { + fprintf(stderr, "bad argument to '--page=' or greater " + "than 255 [0xff]\n"); + return SG_LIB_SYNTAX_ERROR; + } + op->page_code = n; break; case 'r': - optsp->raw_arg = optarg; - optsp->do_raw = 1; + op->raw_arg = optarg; + op->do_raw = 1; break; case 's': n = sg_get_num(optarg); @@ -181,31 +216,31 @@ static int process_cl_new(struct opts_t * optsp, int argc, char * argv[]) usage(); return SG_LIB_SYNTAX_ERROR; } - optsp->do_selftest = n; + op->do_selftest = n; break; case 't': - optsp->do_deftest = 1; + op->do_deftest = 1; break; case 'u': - optsp->do_uoff = 1; + op->do_uoff = 1; break; case 'v': - ++optsp->do_verbose; + ++op->do_verbose; break; case 'V': - ++optsp->do_version; + ++op->do_version; break; default: fprintf(stderr, "unrecognised option code %c [0x%x]\n", c, c); - if (optsp->do_help) + if (op->do_help) break; usage(); return SG_LIB_SYNTAX_ERROR; } } if (optind < argc) { - if (NULL == optsp->device_name) { - optsp->device_name = argv[optind]; + if (NULL == op->device_name) { + op->device_name = argv[optind]; ++optind; } if (optind < argc) { @@ -219,7 +254,8 @@ static int process_cl_new(struct opts_t * optsp, int argc, char * argv[]) return 0; } -static int process_cl_old(struct opts_t * optsp, int argc, char * argv[]) +static int +process_cl_old(struct opts_t * op, int argc, char * argv[]) { int k, jmp_out, plen, num; unsigned int u; @@ -235,54 +271,54 @@ static int process_cl_old(struct opts_t * optsp, int argc, char * argv[]) switch (*cp) { case 'd': if (0 == strncmp("doff", cp, 4)) { - optsp->do_doff = 1; + op->do_doff = 1; cp += 3; plen -= 3; } else jmp_out = 1; break; case 'e': - optsp->do_extdur = 1; + op->do_extdur = 1; break; case 'h': case 'H': - ++optsp->do_hex; + ++op->do_hex; break; case 'l': - ++optsp->do_list; + ++op->do_list; break; case 'N': - optsp->opt_new = 1; + op->opt_new = 1; return 0; case 'O': break; case 'p': if (0 == strncmp("pf", cp, 2)) { - optsp->do_pf = 1; + op->do_pf = 1; ++cp; --plen; } else jmp_out = 1; break; case 't': - optsp->do_deftest = 1; + op->do_deftest = 1; break; case 'u': if (0 == strncmp("uoff", cp, 4)) { - optsp->do_uoff = 1; + op->do_uoff = 1; cp += 3; plen -= 3; } else jmp_out = 1; break; case 'v': - ++optsp->do_verbose; + ++op->do_verbose; break; case 'V': - ++optsp->do_version; + ++op->do_version; break; case '?': - ++optsp->do_help; + ++op->do_help; break; default: jmp_out = 1; @@ -294,8 +330,8 @@ static int process_cl_old(struct opts_t * optsp, int argc, char * argv[]) if (plen <= 0) continue; if (0 == strncmp("raw=", cp, 4)) { - optsp->raw_arg = cp + 4; - optsp->do_raw = 1; + op->raw_arg = cp + 4; + op->do_raw = 1; } else if (0 == strncmp("s=", cp, 2)) { num = sscanf(cp + 2, "%x", &u); if ((1 != num) || (u > 7)) { @@ -303,7 +339,7 @@ static int process_cl_old(struct opts_t * optsp, int argc, char * argv[]) usage_old(); return SG_LIB_SYNTAX_ERROR; } - optsp->do_selftest = u; + op->do_selftest = u; } else if (0 == strncmp("-old", cp, 5)) ; else if (jmp_out) { @@ -311,11 +347,11 @@ static int process_cl_old(struct opts_t * optsp, int argc, char * argv[]) usage_old(); return SG_LIB_SYNTAX_ERROR; } - } else if (0 == optsp->device_name) - optsp->device_name = cp; + } else if (0 == op->device_name) + op->device_name = cp; else { fprintf(stderr, "too many arguments, got: %s, not expecting: " - "%s\n", optsp->device_name, cp); + "%s\n", op->device_name, cp); usage_old(); return SG_LIB_SYNTAX_ERROR; } @@ -323,30 +359,32 @@ static int process_cl_old(struct opts_t * optsp, int argc, char * argv[]) return 0; } -static int process_cl(struct opts_t * optsp, int argc, char * argv[]) +static int +process_cl(struct opts_t * op, int argc, char * argv[]) { int res; char * cp; cp = getenv("SG3_UTILS_OLD_OPTS"); if (cp) { - optsp->opt_new = 0; - res = process_cl_old(optsp, argc, argv); - if ((0 == res) && optsp->opt_new) - res = process_cl_new(optsp, argc, argv); + op->opt_new = 0; + res = process_cl_old(op, argc, argv); + if ((0 == res) && op->opt_new) + res = process_cl_new(op, argc, argv); } else { - optsp->opt_new = 1; - res = process_cl_new(optsp, argc, argv); - if ((0 == res) && (0 == optsp->opt_new)) - res = process_cl_old(optsp, argc, argv); + op->opt_new = 1; + res = process_cl_new(op, argc, argv); + if ((0 == res) && (0 == op->opt_new)) + res = process_cl_old(op, argc, argv); } return res; } /* Return of 0 -> success, otherwise see sg_ll_send_diag() */ -static int do_senddiag(int sg_fd, int sf_code, int pf_bit, int sf_bit, - int devofl_bit, int unitofl_bit, void * outgoing_pg, - int outgoing_len, int noisy, int verbose) +static int +do_senddiag(int sg_fd, int sf_code, int pf_bit, int sf_bit, int devofl_bit, + int unitofl_bit, void * outgoing_pg, int outgoing_len, + int noisy, int verbose) { int long_duration = 0; @@ -357,9 +395,10 @@ static int do_senddiag(int sg_fd, int sf_code, int pf_bit, int sf_bit, outgoing_len, noisy, verbose); } -/* Get expected extended self-test time from mode page 0xa (for '-e' option) */ -static int do_modes_0a(int sg_fd, void * resp, int mx_resp_len, int noisy, - int mode6, int verbose) +/* Get expected extended self-test time from mode page 0xa (for '-e') */ +static int +do_modes_0a(int sg_fd, void * resp, int mx_resp_len, int noisy, int mode6, + int verbose) { int res; @@ -381,8 +420,9 @@ static int do_modes_0a(int sg_fd, void * resp, int mx_resp_len, int noisy, /* Read hex numbers from command line (comma separated list) or from */ /* stdin (one per line, comma separated list or space separated list). */ /* Returns 0 if ok, or 1 if error. */ -static int build_diag_page(const char * inp, unsigned char * mp_arr, - int * mp_arr_len, int max_arr_len) +static int +build_diag_page(const char * inp, unsigned char * mp_arr, int * mp_arr_len, + int max_arr_len) { int in_len, k, j, m; unsigned int h; @@ -555,7 +595,8 @@ static struct page_code_desc pc_desc_arr[] = { {0x42, "Rebuild assist (direct access)"}, /* sbc3r31 */ }; -static const char * find_page_code_desc(int page_num) +static const char * +find_page_code_desc(int page_num) { int k; int num = sizeof(pc_desc_arr) / sizeof(pc_desc_arr[0]); @@ -570,7 +611,8 @@ static const char * find_page_code_desc(int page_num) return NULL; } -static void list_page_codes() +static void +list_page_codes() { int k; int num = sizeof(pc_desc_arr) / sizeof(pc_desc_arr[0]); @@ -583,49 +625,59 @@ static void list_page_codes() } -int main(int argc, char * argv[]) +int +main(int argc, char * argv[]) { - int sg_fd, k, num, rsp_len, res; - unsigned char rsp_buff[MX_ALLOC_LEN]; - int rsp_buff_size = MX_ALLOC_LEN; + int sg_fd, k, num, rsp_len, res, rsp_buff_size, pg; int read_in_len = 0; - const char * cp; - unsigned char read_in[MX_ALLOC_LEN]; int ret = 0; struct opts_t opts; + struct opts_t * op; + unsigned char * rsp_buff = NULL; + const char * cp; + unsigned char * read_in = NULL; - memset(&opts, 0, sizeof(opts)); - res = process_cl(&opts, argc, argv); + op = &opts; + memset(op, 0, sizeof(opts)); + op->maxlen = DEF_ALLOC_LEN; + op->page_code = -1; + res = process_cl(op, argc, argv); if (res) return SG_LIB_SYNTAX_ERROR; - if (opts.do_help) { - if (opts.opt_new) + if (op->do_help) { + if (op->opt_new) usage(); else usage_old(); return 0; } - if (opts.do_version) { + if (op->do_version) { fprintf(stderr, "Version string: %s\n", version_str); return 0; } + rsp_buff_size = op->maxlen; - if (NULL == opts.device_name) { - if (opts.do_list) { + if (NULL == op->device_name) { + if (op->do_list) { list_page_codes(); return 0; } fprintf(stderr, "No DEVICE argument given\n"); - if (opts.opt_new) + if (op->opt_new) usage(); else usage_old(); return SG_LIB_SYNTAX_ERROR; } - if (opts.do_raw) { - if (build_diag_page(opts.raw_arg, read_in, &read_in_len, + if (op->do_raw) { + read_in = (unsigned char *)calloc(op->maxlen, 1); + if (NULL == read_in) { + fprintf(stderr, "unable to allocate %d bytes\n", op->maxlen); + return SG_LIB_CAT_OTHER; + } + if (build_diag_page(op->raw_arg, read_in, &read_in_len, sizeof(read_in))) { - if (opts.opt_new) { + if (op->opt_new) { printf("Bad sequence after '--raw=' option\n"); usage(); } else { @@ -636,8 +688,8 @@ int main(int argc, char * argv[]) } } - if ((opts.do_doff || opts.do_uoff) && (! opts.do_deftest)) { - if (opts.opt_new) { + if ((op->do_doff || op->do_uoff) && (! op->do_deftest)) { + if (op->opt_new) { printf("setting --doff or --uoff only useful when -t is set\n"); usage(); } else { @@ -646,8 +698,8 @@ int main(int argc, char * argv[]) } return SG_LIB_SYNTAX_ERROR; } - if ((opts.do_selftest > 0) && opts.do_deftest) { - if (opts.opt_new) { + if ((op->do_selftest > 0) && op->do_deftest) { + if (op->opt_new) { printf("either set --selftest=SF or --test (not both)\n"); usage(); } else { @@ -656,10 +708,10 @@ int main(int argc, char * argv[]) } return SG_LIB_SYNTAX_ERROR; } - if (opts.do_raw) { - if ((opts.do_selftest > 0) || opts.do_deftest || opts.do_extdur || - opts.do_list) { - if (opts.opt_new) { + if (op->do_raw) { + if ((op->do_selftest > 0) || op->do_deftest || op->do_extdur || + op->do_list) { + if (op->opt_new) { printf("'--raw=' cannot be used with self-tests, '-e' or " "'-l'\n"); usage(); @@ -670,8 +722,8 @@ int main(int argc, char * argv[]) } return SG_LIB_SYNTAX_ERROR; } - if (! opts.do_pf) { - if (opts.opt_new) + if (! op->do_pf) { + if (op->opt_new) printf(">>> warning, '--pf' probably should be used with " "'--raw='\n"); else @@ -679,22 +731,36 @@ int main(int argc, char * argv[]) "'-raw='\n"); } } +#ifdef SG_LIB_WIN32 +#ifdef SG_LIB_WIN32_DIRECT + if (op->do_verbose > 4) + fprintf(stderr, "Initial win32 SPT interface state: %s\n", + scsi_pt_win32_spt_state() ? "direct" : "indirect"); + if (op->maxlen >= 16384) + scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT pt interface */); +#endif +#endif - if ((sg_fd = sg_cmds_open_device(opts.device_name, 0 /* rw */, - opts.do_verbose)) < 0) { - fprintf(stderr, ME "error opening file: %s: %s\n", opts.device_name, + if ((sg_fd = sg_cmds_open_device(op->device_name, 0 /* rw */, + op->do_verbose)) < 0) { + fprintf(stderr, ME "error opening file: %s: %s\n", op->device_name, safe_strerror(-sg_fd)); return SG_LIB_FILE_ERROR; } - if (opts.do_extdur) { - res = do_modes_0a(sg_fd, rsp_buff, 32, 1, 0, opts.do_verbose); + rsp_buff = (unsigned char *)calloc(op->maxlen, 1); + if (NULL == rsp_buff) { + fprintf(stderr, "unable to allocate %d bytes (2)\n", op->maxlen); + return SG_LIB_CAT_OTHER; + } + if (op->do_extdur) { + res = do_modes_0a(sg_fd, rsp_buff, 32, 1, 0, op->do_verbose); if (0 == res) { /* Assume mode sense(10) response without block descriptors */ - num = (rsp_buff[0] << 8) + rsp_buff[1] - 6; + num = sg_get_unaligned_be16(rsp_buff) - 6; if (num >= 0xc) { int secs; - secs = (rsp_buff[18] << 8) + rsp_buff[19]; + secs = sg_get_unaligned_be16(rsp_buff + 18); #ifdef SG_LIB_MINGW printf("Expected extended self-test duration=%d seconds " "(%g minutes)\n", secs, secs / 60.0); @@ -709,23 +775,37 @@ int main(int argc, char * argv[]) printf("Extended self-test duration (mode page 0xa) failed\n"); goto err_out9; } - } else if (opts.do_list) { - memset(rsp_buff, 0, sizeof(rsp_buff)); - res = do_senddiag(sg_fd, 0, 1 /* pf */, 0, 0, 0, rsp_buff, 4, 1, - opts.do_verbose); + } else if ((op->do_list) || (op->page_code >= 0x0)) { + pg = op->page_code; + if (pg < 0) + res = do_senddiag(sg_fd, 0, 1 /* pf */, 0, 0, 0, rsp_buff, 4, 1, + op->do_verbose); + else + res = 0; if (0 == res) { - if (0 == sg_ll_receive_diag(sg_fd, 0, 0, rsp_buff, - rsp_buff_size, 1, opts.do_verbose)) { - printf("Supported diagnostic pages response:\n"); - rsp_len = (rsp_buff[2] << 8) + rsp_buff[3] + 4; - if (opts.do_hex) - dStrHex((const char *)rsp_buff, rsp_len, 1); - else { - for (k = 0; k < (rsp_len - 4); ++k) { - cp = find_page_code_desc(rsp_buff[k + 4]); - printf(" 0x%02x %s\n", rsp_buff[k + 4], - (cp ? cp : "<unknown>")); + if (0 == sg_ll_receive_diag(sg_fd, (pg >= 0x0), + ((pg >= 0x0) ? pg : 0), rsp_buff, + rsp_buff_size, 1, op->do_verbose)) { + rsp_len = sg_get_unaligned_be16(rsp_buff + 2) + 4; + if (pg < 0x1) { + printf("Supported diagnostic pages response:\n"); + if (op->do_hex) + dStrHex((const char *)rsp_buff, rsp_len, 1); + else { + for (k = 0; k < (rsp_len - 4); ++k) { + cp = find_page_code_desc(rsp_buff[k + 4]); + printf(" 0x%02x %s\n", rsp_buff[k + 4], + (cp ? cp : "<unknown>")); + } } + } else { + cp = find_page_code_desc(pg); + if (cp) + printf("%s diagnostic page [0x%x] response in " + "hex:\n", cp, pg); + else + printf("diagnostic page 0x%x response in hex:\n", pg); + dStrHex((const char *)rsp_buff, rsp_len, 1); } } else { ret = res; @@ -737,21 +817,21 @@ int main(int argc, char * argv[]) ret = res; goto err_out; } - } else if (opts.do_raw) { - res = do_senddiag(sg_fd, 0, opts.do_pf, 0, 0, 0, read_in, - read_in_len, 1, opts.do_verbose); + } else if (op->do_raw) { + res = do_senddiag(sg_fd, 0, op->do_pf, 0, 0, 0, read_in, + read_in_len, 1, op->do_verbose); if (res) { ret = res; goto err_out; } } else { - res = do_senddiag(sg_fd, opts.do_selftest, opts.do_pf, - opts.do_deftest, opts.do_doff, opts.do_uoff, NULL, - 0, 1, opts.do_verbose); + res = do_senddiag(sg_fd, op->do_selftest, op->do_pf, op->do_deftest, + op->do_doff, op->do_uoff, NULL, 0, 1, + op->do_verbose); if (0 == res) { - if ((5 == opts.do_selftest) || (6 == opts.do_selftest)) + if ((5 == op->do_selftest) || (6 == op->do_selftest)) printf("Foreground self-test returned GOOD status\n"); - else if (opts.do_deftest && (! opts.do_doff) && (! opts.do_uoff)) + else if (op->do_deftest && (! op->do_doff) && (! op->do_uoff)) printf("Default self-test returned GOOD status\n"); } else { ret = res; @@ -774,10 +854,14 @@ err_out: else fprintf(stderr, "SEND DIAGNOSTIC command, failed\n"); err_out9: - if (opts.do_verbose < 2) + if (op->do_verbose < 2) fprintf(stderr, " try again with '-vv' for more information\n"); res = sg_cmds_close_device(sg_fd); if ((res < 0) && (0 == ret)) return SG_LIB_FILE_ERROR; + if (read_in) + free(read_in); + if (rsp_buff) + free(rsp_buff); return (ret >= 0) ? ret : SG_LIB_CAT_OTHER; } diff --git a/src/sg_ses.c b/src/sg_ses.c index 1627c01..c911f4c 100644 --- a/src/sg_ses.c +++ b/src/sg_ses.c @@ -29,7 +29,7 @@ * commands tailored for SES (enclosure) devices. */ -static const char * version_str = "1.91 20140515"; /* ses3r06 */ +static const char * version_str = "1.99 20141028"; /* ses3r07 */ #define MX_ALLOC_LEN ((64 * 1024) - 4) /* max allowable for big enclosures */ #define MX_ELEM_HDR 1024 @@ -68,7 +68,7 @@ static const char * version_str = "1.91 20140515"; /* ses3r06 */ #define TEMPERATURE_ETC 0x4 #define DOOR_ETC 0x5 /* prior to ses3r05 was DOOR_LOCK_ETC */ #define AUD_ALARM_ETC 0x6 -#define ESC_ELECTRONICS_ETC 0x7 +#define ENC_ELECTRONICS_ETC 0x7 #define SCC_CELECTR_ETC 0x8 #define NV_CACHE_ETC 0x9 #define INV_OP_REASON_ETC 0xa @@ -87,6 +87,9 @@ static const char * version_str = "1.91 20140515"; /* ses3r06 */ #define ARRAY_DEV_ETC 0x17 #define SAS_EXPANDER_ETC 0x18 #define SAS_CONNECTOR_ETC 0x19 +#define LAST_ETC SAS_CONNECTOR_ETC /* adjust as necessary */ + +#define NUM_ETC (LAST_ETC + 1) struct element_type_t { @@ -101,7 +104,7 @@ struct opts_t { int do_control; int do_data; int dev_slot_num; - int do_enumerate; + int enumerate; int eiioe_auto; int eiioe_force; int do_filter; @@ -114,6 +117,7 @@ struct opts_t { int inner_hex; int do_join; int do_list; + int mask_ign; /* element read-mask-modify-write actions */ int maxlen; int seid; int seid_given; @@ -124,6 +128,7 @@ struct opts_t { int do_status; int verbose; int do_version; + int warn; int num_cgs; int arr_len; unsigned char sas_addr[8]; @@ -203,6 +208,7 @@ struct acronym2tuple { int start_byte; /* origin 0, normally 0 to 3 */ int start_bit; /* 7 (MSB or rightmost in SES drafts) to 0 (LSB) */ int num_bits; /* usually 1 */ + const char * info; /* optional, set to NULL if not used */ }; /* Structure for holding (sub-)enclosure information found in the @@ -359,7 +365,7 @@ static struct element_type_t element_type_arr[] = { {DOOR_ETC, "do", "Door"}, /* prior to ses3r05 was 'dl' (for Door Lock) but the "Lock" has been dropped */ {AUD_ALARM_ETC, "aa", "Audible alarm"}, - {ESC_ELECTRONICS_ETC, "esc", "Enclosure services controller electronics"}, + {ENC_ELECTRONICS_ETC, "esc", "Enclosure services controller electronics"}, {SCC_CELECTR_ETC, "sce", "SCC controller electronics"}, {NV_CACHE_ETC, "nc", "Nonvolatile cache"}, {INV_OP_REASON_ETC, "ior", "Invalid operation reason"}, @@ -387,73 +393,89 @@ static struct element_type_t element_type_by_code = /* Many control element names below have "RQST" in front in drafts. These are for the Enclosure Control/Status diagnostic page */ static struct acronym2tuple ecs_a2t_arr[] = { - {"active", DEVICE_ETC, 2, 7, 1}, /* in control but not in status */ - {"active", ARRAY_DEV_ETC, 2, 7, 1}, /* in control but not in status */ - {"conscheck", ARRAY_DEV_ETC, 1, 4, 1}, - {"disable", -1, 0, 5, 1}, /* the -1 is for all element types */ - {"devoff", DEVICE_ETC, 3, 4, 1}, /* device off */ - {"devoff", ARRAY_DEV_ETC, 3, 4, 1}, - {"dnr", DEVICE_ETC, 2, 6, 1}, /* do not remove */ - {"dnr", ARRAY_DEV_ETC, 2, 6, 1}, - {"fault", DEVICE_ETC, 3, 5, 1}, - {"fault", ARRAY_DEV_ETC, 3, 5, 1}, - {"hotspare", ARRAY_DEV_ETC, 1, 5, 1}, - {"ident", DEVICE_ETC, 2, 1, 1}, - {"ident", ARRAY_DEV_ETC, 2, 1, 1}, - {"ident", POWER_SUPPLY_ETC, 1, 7, 1}, - {"ident", COOLING_ETC, 1, 7, 1}, - {"ident", ENCLOSURE_ETC, 1, 7, 1}, - {"incritarray", ARRAY_DEV_ETC, 1, 3, 1}, - {"infailedarray", ARRAY_DEV_ETC, 1, 2, 1}, - {"insert", DEVICE_ETC, 2, 3, 1}, - {"insert", ARRAY_DEV_ETC, 2, 3, 1}, - {"locate", DEVICE_ETC, 2, 1, 1}, - {"locate", ARRAY_DEV_ETC, 2, 1, 1}, - {"locate", POWER_SUPPLY_ETC, 1, 7, 1}, - {"locate", COOLING_ETC, 1, 7, 1}, - {"locate", ENCLOSURE_ETC, 1, 7, 1}, - {"missing", DEVICE_ETC, 2, 4, 1}, - {"missing", ARRAY_DEV_ETC, 2, 4, 1}, - {"ok", ARRAY_DEV_ETC, 1, 7, 1}, - {"locate", DEVICE_ETC, 2, 1, 1}, - {"locate", ARRAY_DEV_ETC, 2, 1, 1}, - {"prdfail", -1, 0, 6, 1}, - {"rebuildremap", ARRAY_DEV_ETC, 1, 1, 1}, - {"remove", DEVICE_ETC, 2, 2, 1}, - {"remove", ARRAY_DEV_ETC, 2, 2, 1}, - {"rrabort", ARRAY_DEV_ETC, 1, 0, 1}, - {"rsvddevice", ARRAY_DEV_ETC, 1, 6, 1}, - {"speed_act", COOLING_ETC, 2, 7, 8}, /* actual speed (rpm / 10) */ - {"speed_code", COOLING_ETC, 3, 2, 3}, - {"swap", -1, 0, 4, 1}, /* Reset swap */ - {NULL, 0, 0, 0, 0}, + {"active", DEVICE_ETC, 2, 7, 1, NULL}, /* for control only */ + {"active", ARRAY_DEV_ETC, 2, 7, 1, NULL}, /* for control only */ + {"conscheck", ARRAY_DEV_ETC, 1, 4, 1, "consistency check"}, + {"disable", -1, 0, 5, 1, NULL}, /* -1 is for all element types */ + {"devoff", DEVICE_ETC, 3, 4, 1, NULL}, /* device off */ + {"devoff", ARRAY_DEV_ETC, 3, 4, 1, NULL}, + {"dnr", DEVICE_ETC, 2, 6, 1, "do not remove"}, + {"dnr", ARRAY_DEV_ETC, 2, 6, 1, "do not remove"}, + {"fail", SAS_CONNECTOR_ETC, 3, 6, 1, NULL}, + {"fault", DEVICE_ETC, 3, 5, 1, NULL}, + {"fault", ARRAY_DEV_ETC, 3, 5, 1, NULL}, + {"hotspare", ARRAY_DEV_ETC, 1, 5, 1, NULL}, + {"ident", DEVICE_ETC, 2, 1, 1, "flash LED"}, + {"ident", ARRAY_DEV_ETC, 2, 1, 1, "flash LED"}, + {"ident", POWER_SUPPLY_ETC, 1, 7, 1, "flash LED"}, + {"ident", COOLING_ETC, 1, 7, 1, "flash LED"}, + {"ident", ENCLOSURE_ETC, 1, 7, 1, "flash LED"}, + {"ident", AUD_ALARM_ETC, 1, 7, 1, NULL}, + {"ident", SAS_CONNECTOR_ETC, 1, 7, 1, "flash LED"}, + {"incritarray", ARRAY_DEV_ETC, 1, 3, 1, NULL}, + {"infailedarray", ARRAY_DEV_ETC, 1, 2, 1, NULL}, + {"info", AUD_ALARM_ETC, 3, 3, 1, "emits warning tone when set"}, + {"insert", DEVICE_ETC, 2, 3, 1, NULL}, + {"insert", ARRAY_DEV_ETC, 2, 3, 1, NULL}, + {"locate", DEVICE_ETC, 2, 1, 1, "flash LED"}, + {"locate", ARRAY_DEV_ETC, 2, 1, 1, "flash LED"}, + {"locate", POWER_SUPPLY_ETC, 1, 7, 1, "flash LED"}, + {"locate", COOLING_ETC, 1, 7, 1, "flash LED"}, + {"locate", ENCLOSURE_ETC, 1, 7, 1, "flash LED"}, + {"locate", SAS_CONNECTOR_ETC, 1, 7, 1, "flash LED"}, + {"missing", DEVICE_ETC, 2, 4, 1, NULL}, + {"missing", ARRAY_DEV_ETC, 2, 4, 1, NULL}, + {"ok", ARRAY_DEV_ETC, 1, 7, 1, NULL}, + {"on", POWER_SUPPLY_ETC, 3, 5, 1, "0: turn (remain) off; 1: turn on"}, + {"overcurrent", POWER_SUPPLY_ETC, 2, 1, 1, "DC overcurrent"}, + {"overcurrent", SAS_CONNECTOR_ETC, 3, 5, 1, NULL}, /* added ses3r07 */ + {"locate", DEVICE_ETC, 2, 1, 1, NULL}, + {"locate", ARRAY_DEV_ETC, 2, 1, 1, NULL}, + {"pow_cycle", ENCLOSURE_ETC, 2, 7, 2, + "0: no; 1: start in pow_c_delay minutes; 2: cancel"}, + {"pow_c_delay", ENCLOSURE_ETC, 2, 5, 6, + "delay in minutes before starting power cycle"}, + {"prdfail", -1, 0, 6, 1, "predict failure"}, + {"rebuildremap", ARRAY_DEV_ETC, 1, 1, 1, NULL}, + {"remove", DEVICE_ETC, 2, 2, 1, NULL}, + {"remove", ARRAY_DEV_ETC, 2, 2, 1, NULL}, + {"rrabort", ARRAY_DEV_ETC, 1, 0, 1, "rebuild/remap abort"}, + {"rsvddevice", ARRAY_DEV_ETC, 1, 6, 1, "reserved device"}, + {"speed_act", COOLING_ETC, 1, 2, 11, "actual speed (rpm / 10)"}, + {"speed_code", COOLING_ETC, 3, 2, 3, + "0: leave; 1: lowest... 7: highest"}, + {"swap", -1, 0, 4, 1, NULL}, /* Reset swap */ + {NULL, 0, 0, 0, 0, NULL}, }; /* These are for the Threshold in/out diagnostic page */ static struct acronym2tuple th_a2t_arr[] = { - {"high_crit", -1, 0, 7, 8}, - {"high_warn", -1, 1, 7, 8}, - {"low_crit", -1, 2, 7, 8}, - {"low_warn", -1, 3, 7, 8}, - {NULL, 0, 0, 0, 0}, + {"high_crit", -1, 0, 7, 8, NULL}, + {"high_warn", -1, 1, 7, 8, NULL}, + {"low_crit", -1, 2, 7, 8, NULL}, + {"low_warn", -1, 3, 7, 8, NULL}, + {NULL, 0, 0, 0, 0, NULL}, }; -/* These are for the Additional element status diagnostic page for SAS - * with the EIP bit set. First phy only. */ +/* These are for the Additional element status diagnostic page for SAS with + * the EIP bit set. First phy only. Index from start of AES descriptor */ static struct acronym2tuple ae_sas_a2t_arr[] = { - {"at_sas_addr", -1, 12, 7, 64}, /* best viewed with --hex --get= */ - {"dev_type", -1, 8, 6, 3}, - {"phy_id", -1, 28, 7, 8}, - {"sas_addr", -1, 20, 7, 64}, /* from end device's POV, often a disk */ - {"sata_dev", -1, 11, 0, 1}, - {"sata_port_sel", -1, 11, 7, 1}, - {"smp_init", -1, 10, 1, 1}, - {"smp_targ", -1, 11, 1, 1}, - {"ssp_init", -1, 10, 3, 1}, - {"ssp_targ", -1, 11, 3, 1}, - {"stp_init", -1, 10, 2, 1}, - {"stp_targ", -1, 11, 2, 1}, - {NULL, 0, 0, 0, 0}, + {"at_sas_addr", -1, 12, 7, 64, NULL}, /* best viewed with --hex --get= */ + /* typically this is the expander's SAS address */ + {"dev_type", -1, 8, 6, 3, "1: SAS/SATA dev, 2: expander"}, + {"dsn", -1, 7, 7, 8, "device slot number (255: none)"}, + {"num_phys", -1, 4, 7, 8, "number of phys"}, + {"phy_id", -1, 28, 7, 8, NULL}, + {"sas_addr", -1, 20, 7, 64, NULL}, /* should be disk or tape ... */ + {"sata_dev", -1, 11, 0, 1, NULL}, + {"sata_port_sel", -1, 11, 7, 1, NULL}, + {"smp_init", -1, 10, 1, 1, NULL}, + {"smp_targ", -1, 11, 1, 1, NULL}, + {"ssp_init", -1, 10, 3, 1, NULL}, + {"ssp_targ", -1, 11, 3, 1, NULL}, + {"stp_init", -1, 10, 2, 1, NULL}, + {"stp_targ", -1, 11, 2, 1, NULL}, + {NULL, 0, 0, 0, 0, NULL}, }; /* Boolean array of element types of interest to the Additional Element @@ -473,6 +495,7 @@ static struct option long_options[] = { {"data", required_argument, 0, 'd'}, {"descriptor", required_argument, 0, 'D'}, {"dev-slot-num", required_argument, 0, 'x'}, + {"dsn", required_argument, 0, 'x'}, {"eiioe", required_argument, 0, 'E'}, {"enumerate", no_argument, 0, 'e'}, {"filter", no_argument, 0, 'f'}, @@ -485,6 +508,7 @@ static struct option long_options[] = { {"list", no_argument, 0, 'l'}, {"nickid", required_argument, 0, 'N'}, {"nickname", required_argument, 0, 'n'}, + {"mask", required_argument, 0, 'M'}, {"maxlen", required_argument, 0, 'm'}, {"page", required_argument, 0, 'p'}, {"raw", no_argument, 0, 'r'}, @@ -497,6 +521,39 @@ static struct option long_options[] = { {0, 0, 0, 0}, }; +/* For overzealous SES device servers that don't like some status elements + * sent back as control elements. This table is as per ses3r06. */ +static uint8_t ses3_element_cmask_arr[NUM_ETC][4] = { + /* Element type code (ETC) names; comment */ + {0x40, 0xff, 0xff, 0xff}, /* [0] unspecified */ + {0x40, 0, 0x4e, 0x3c}, /* DEVICE */ + {0x40, 0x80, 0, 0x60}, /* POWER_SUPPLY */ + {0x40, 0x80, 0, 0x60}, /* COOLING; requested speed as is unless */ + {0x40, 0xc0, 0, 0}, /* TEMPERATURE */ + {0x40, 0xc0, 0, 0x1}, /* DOOR */ + {0x40, 0xc0, 0, 0x5f}, /* AUD_ALARM */ + {0x40, 0xc0, 0x1, 0}, /* ENC_ELECTRONICS */ + {0x40, 0xc0, 0, 0}, /* SCC_CELECTR */ + {0x40, 0xc0, 0, 0}, /* NV_CACHE */ + {0x40, 0, 0, 0}, /* [10] INV_OP_REASON */ + {0x40, 0, 0, 0xc0}, /* UI_POWER_SUPPLY */ + {0x40, 0xc0, 0xff, 0xff}, /* DISPLAY */ + {0x40, 0xc3, 0, 0}, /* KEY_PAD */ + {0x40, 0x80, 0, 0xff}, /* ENCLOSURE */ + {0x40, 0xc0, 0, 0x10}, /* SCSI_PORT_TRAN */ + {0x40, 0x80, 0xff, 0xff}, /* LANGUAGE */ + {0x40, 0xc0, 0, 0x1}, /* COMM_PORT */ + {0x40, 0xc0, 0, 0}, /* VOLT_SENSOR */ + {0x40, 0xc0, 0, 0}, /* CURR_SENSOR */ + {0x40, 0xc0, 0, 0x1}, /* [20] SCSI_TPORT */ + {0x40, 0xc0, 0, 0x1}, /* SCSI_IPORT */ + {0x40, 0xc0, 0, 0}, /* SIMPLE_SUBENC */ + {0x40, 0xff, 0x4e, 0x3c}, /* ARRAY */ + {0x40, 0xc0, 0, 0}, /* SAS_EXPANDER */ + {0x40, 0x80, 0, 0x40}, /* SAS_CONNECTOR */ +}; + + static int read_hex(const char * inp, unsigned char * arr, int * arr_len, int verb); static int strcase_eq(const char * s1p, const char * s2p); @@ -536,16 +593,16 @@ usage(int help_num) "[--hex]\n" " [--index=IIA | =TIA,II] [--inner-hex] " "[--join] [--list]\n" - " [--maxlen=LEN] [--nickname=SEN] [--nickid=SEID] " - "[--page=PG]\n" - " [--raw] [--sas-addr=SA] [--set=STR] [--status] " - "[--verbose]\n" - " [--version] DEVICE\n" + " [--mask] [--maxlen=LEN] [--nickname=SEN] " + "[--nickid=SEID]\n" + " [--page=PG] [--raw] [--sas-addr=SA] [--set=STR] " + "[--status]\n" + " [--verbose] [--version] [--warn] DEVICE\n" " where the main options are:\n" " --clear=STR|-C STR clear field by acronym or position\n" - " --descriptor=DN|-D DN descriptor name, indexing method\n" - " --dev-slot-num=SN|-x SN device slot number, indexing " - "method\n" + " --descriptor=DN|-D DN descriptor name (for indexing)\n" + " --dev-slot-num=SN|--dsn=SN|-x SN device slot number " + "(for indexing)\n" " --eiioe=A_F|-E A_F where A_F is either 'auto' or 'force'." "'force'\n" " acts as if EIIOE is set, 'auto' tries " @@ -582,8 +639,7 @@ usage(int help_num) "or number)\n" " (def: 'ssp' [0x0] (supported diagnostic " "pages))\n" - " --sas-addr=SA|-A SA SAS address in hex, indexing " - "method\n" + " --sas-addr=SA|-A SA SAS address in hex (for indexing)\n" " --set=STR|-S STR set value of field by acronym or " "position\n\n" "Fetches status or sends control data to a SCSI enclosure. Use " @@ -605,6 +661,10 @@ usage(int help_num) " --inner-hex|-i print innermost level of a" " status page in hex\n" " --list|-l same as '--enumerate' option\n" + " --mask|-M ignore status element mask in modify " + "actions\n" + " (e.g.--set= and --clear=) (def: apply " + "mask)\n" " --maxlen=LEN|-m LEN max response length (allocation " "length in cdb)\n" " --nickname=SEN|-n SEN SEN is new subenclosure nickname\n" @@ -621,12 +681,14 @@ usage(int help_num) " --status|-s fetch status information (default " "action)\n" " --verbose|-v increase verbosity\n" - " --version|-V print version string and exit\n\n" + " --version|-V print version string and exit\n" + " --warn|-w warn about join (and other) issues\n\n" "If no options are given then DEVICE's supported diagnostic " - "pages are\noutput. STR can be '<acronym>[=val]' or\n" - "'<start_byte>:<start_bit>[:<num_bits>][=<val>]'. Element " - "type\nabbreviations may be followed by a number (e.g. 'ps1' " - "is the second\npower supply element type).\n\n" + "pages are\nlisted. STR can be '<start_byte>:<start_bit>" + "[:<num_bits>][=<val>]'\nor '<acronym>[=val]'. Element type " + "abbreviations may be followed by a\nnumber (e.g. 'ps1' is " + "the second power supply element type). Use\n'sg_ses -e' and " + "'sg_ses -ee' for more information.\n\n" ); pr2serr( "Low level indexing can be done with one of the two '--index=' " @@ -726,9 +788,11 @@ parse_index(struct opts_t *op) if (NULL == cp) op->ind_indiv = -1; } else { /* element type abbreviation perhaps followed by <num> */ + int blen = strlen(b); + for (etp = element_type_arr; etp->desc; ++etp) { n = strlen(etp->abbrev); - if (0 == strncmp(b, etp->abbrev, n)) + if ((n == blen) && (0 == strncmp(b, etp->abbrev, n))) break; } if (NULL == etp->desc) { @@ -775,8 +839,8 @@ cl_process(struct opts_t *op, int argc, char *argv[]) while (1) { int option_index = 0; - c = getopt_long(argc, argv, "A:b:cC:d:D:eE:fG:hHiI:jln:N:m:p:rRsS:v" - "Vx:", long_options, &option_index); + c = getopt_long(argc, argv, "A:b:cC:d:D:eE:fG:hHiI:jln:N:m:Mp:rRsS:v" + "Vwx:", long_options, &option_index); if (c == -1) break; @@ -823,7 +887,7 @@ cl_process(struct opts_t *op, int argc, char *argv[]) op->desc_name = optarg; break; case 'e': - ++op->do_enumerate; + ++op->enumerate; break; case 'E': if (0 == strcmp("auto", optarg)) @@ -881,6 +945,9 @@ cl_process(struct opts_t *op, int argc, char *argv[]) return SG_LIB_SYNTAX_ERROR; } break; + case 'M': + ++op->mask_ign; + break; case 'p': if (isdigit(optarg[0])) { op->page_code = sg_get_num(optarg); @@ -926,6 +993,9 @@ cl_process(struct opts_t *op, int argc, char *argv[]) case 'V': ++op->do_version; return 0; + case 'w': + ++op->warn; + break; case 'x': op->dev_slot_num = sg_get_num(optarg); if ((op->dev_slot_num < 0) || (op->dev_slot_num > 255)) { @@ -1005,7 +1075,7 @@ cl_process(struct opts_t *op, int argc, char *argv[]) pr2serr("assume --page=2 (es) option is set\n"); } } - if (op->do_list || op->do_enumerate) + if (op->do_list || op->enumerate) return 0; if (op->do_control && op->do_status) { pr2serr("cannot have both '--control' and '--status'\n"); @@ -1367,7 +1437,7 @@ ses_configuration_sdg(const unsigned char * resp, int resp_len) el = ucp[3] + 4; sum_elem_types += ucp[2]; printf(" Subenclosure identifier: %d%s\n", ucp[1], - (ucp[1] ? "" : " (primary)")); + (ucp[1] ? "" : " [primary]")); printf(" relative ES process id: %d, number of ES processes" ": %d\n", ((ucp[0] & 0x70) >> 4), (ucp[0] & 0x7)); printf(" number of type descriptor headers: %d\n", ucp[2]); @@ -1757,7 +1827,7 @@ enc_status_helper(const char * pad, const unsigned char * statp, int etype, "Unrecov=%d\n", pad, !!(statp[3] & 0x8), !!(statp[3] & 0x4), !!(statp[3] & 0x2), !!(statp[3] & 0x1)); break; - case ESC_ELECTRONICS_ETC: /* enclosure services controller electronics */ + case ENC_ELECTRONICS_ETC: /* enclosure services controller electronics */ if (nofilter || (0xc0 & statp[1]) || (0x1 & statp[2]) || (0x80 & statp[3])) printf("%sIdent=%d, Fail=%d, Report=%d, Hot swap=%d\n", pad, @@ -1945,11 +2015,11 @@ enc_status_helper(const char * pad, const unsigned char * statp, int etype, printf("%sIdent=%d, Fail=%d\n", pad, !!(statp[1] & 0x80), !!(statp[1] & 0x40)); break; - case SAS_CONNECTOR_ETC: + case SAS_CONNECTOR_ETC: /* OC (overcurrent) added in ses3r07 */ printf("%sIdent=%d, %s\n", pad, !!(statp[1] & 0x80), find_sas_connector_type((statp[1] & 0x7f), bb, sizeof(bb))); - printf("%sConnector physical link=0x%x, Fail=%d\n", pad, statp[2], - !!(statp[3] & 0x40)); + printf("%sConnector physical link=0x%x, Fail=%d, OC=%d\n", pad, + statp[2], !!(statp[3] & 0x40), !!(statp[3] & 0x20)); break; default: if (etype < 0x80) @@ -2190,7 +2260,7 @@ ses_element_desc_sdg(const struct type_desc_hdr_t * tdhp, int num_telems, pr2serr(" <<state of enclosure changed, please try again>>\n"); return; } - printf(" element descriptor by type list\n"); + printf(" element descriptor list (grouped by type):\n"); ucp = resp + 8; for (k = 0, got1 = 0, tp = tdhp; k < num_telems; ++k, ++tp) { if ((ucp + 3) > last_ucp) @@ -2256,7 +2326,7 @@ static void additional_elem_helper(const char * pad, const unsigned char * ucp, int len, int elem_type, const struct opts_t * op) { - int ports, phys, j, m, desc_type, eip_offset, print_sas_addr; + int ports, phys, j, m, desc_type, eip_offset, print_sas_addr, saddr_nz; const unsigned char * per_ucp; int nofilter = ! op->do_filter; char b[64]; @@ -2302,6 +2372,8 @@ additional_elem_helper(const char * pad, const unsigned char * ucp, int len, if (len < (4 + eip_offset)) break; desc_type = (ucp[3 + eip_offset] >> 6) & 0x3; + if (op->verbose > 1) + printf("%sdescriptor_type: %d\n", pad, desc_type); if (0 == desc_type) { phys = ucp[2 + eip_offset]; printf("%snumber of phys: %d, not all phys: %d", pad, phys, @@ -2312,7 +2384,7 @@ additional_elem_helper(const char * pad, const unsigned char * ucp, int len, per_ucp = ucp + 4 + eip_offset + eip_offset; for (j = 0; j < phys; ++j, per_ucp += 28) { printf("%sphy index: %d\n", pad, j); - printf("%s device type: %s\n", pad, + printf("%s SAS device type: %s\n", pad, sas_device_type[(0x70 & per_ucp[0]) >> 4]); if (nofilter || (0xe & per_ucp[2])) printf("%s initiator port for:%s%s%s\n", pad, @@ -2327,17 +2399,25 @@ additional_elem_helper(const char * pad, const unsigned char * ucp, int len, ((per_ucp[3] & 2) ? " SMP" : ""), ((per_ucp[3] & 1) ? " SATA_device" : "")); print_sas_addr = 0; - if (nofilter || saddr_non_zero(per_ucp + 4)) { + saddr_nz = saddr_non_zero(per_ucp + 4); + if (nofilter || saddr_nz) { ++print_sas_addr; printf("%s attached SAS address: 0x", pad); - for (m = 0; m < 8; ++m) - printf("%02x", per_ucp[4 + m]); + if (saddr_nz) { + for (m = 0; m < 8; ++m) + printf("%02x", per_ucp[4 + m]); + } else + printf("0"); } - if (nofilter || saddr_non_zero(per_ucp + 12)) { + saddr_nz = saddr_non_zero(per_ucp + 12); + if (nofilter || saddr_nz) { ++print_sas_addr; printf("\n%s SAS address: 0x", pad); - for (m = 0; m < 8; ++m) - printf("%02x", per_ucp[12 + m]); + if (saddr_nz) { + for (m = 0; m < 8; ++m) + printf("%02x", per_ucp[12 + m]); + } else + printf("0"); } if (print_sas_addr) printf("\n%s phy identifier: 0x%x\n", pad, per_ucp[20]); @@ -2363,7 +2443,7 @@ additional_elem_helper(const char * pad, const unsigned char * ucp, int len, } } else if ((SCSI_TPORT_ETC == elem_type) || (SCSI_IPORT_ETC == elem_type) || - (ESC_ELECTRONICS_ETC == elem_type)) { + (ENC_ELECTRONICS_ETC == elem_type)) { printf("%snumber of phys: %d\n", pad, phys); per_ucp = ucp + 6 + eip_offset; for (j = 0; j < phys; ++j, per_ucp += 12) { @@ -2650,6 +2730,38 @@ ses_supported_pages_sdg(const char * leadin, const unsigned char * resp, } } +/* An array of Download microcode status field values and descriptions */ +static struct diag_page_code mc_status_arr[] = { + {0x0, "No download microcode operation in progress"}, + {0x1, "Download in progress, awaiting more"}, + {0x2, "Download complete, updating storage"}, + {0x3, "Updating storage with deferred microcode"}, + {0x10, "Complete, no error, starting now"}, + {0x11, "Complete, no error, start after hard reset or power cycle"}, + {0x12, "Complete, no error, start after power cycle"}, + {0x13, "Complete, no error, start after activate_mc, hard reset or " + "power cycle"}, + {0x80, "Error, discarded, see additional status"}, + {0x81, "Error, discarded, image error"}, + {0x82, "Timeout, discarded"}, + {0x83, "Internal error, need new microcode before reset"}, + {0x84, "Internal error, need new microcode, reset safe"}, + {0x85, "Unexpected activate_mc received"}, + {0x1000, NULL}, +}; + +static const char * +get_mc_status(unsigned char status_val) +{ + const struct diag_page_code * mcsp; + + for (mcsp = mc_status_arr; mcsp->desc; ++mcsp) { + if (status_val == mcsp->page_code) + return mcsp->desc; + } + return ""; +} + /* DPC_DOWNLOAD_MICROCODE [0xe] */ static void ses_download_code_sdg(const unsigned char * resp, int resp_len) @@ -2658,6 +2770,7 @@ ses_download_code_sdg(const unsigned char * resp, int resp_len) unsigned int gen_code; const unsigned char * ucp; const unsigned char * last_ucp; + const char * cp; printf("Download microcode status diagnostic page:\n"); if (resp_len < 4) @@ -2673,9 +2786,16 @@ ses_download_code_sdg(const unsigned char * resp, int resp_len) for (k = 0; k < num_subs; ++k, ucp += 16) { if ((ucp + 3) > last_ucp) goto truncated; - printf(" subenclosure identifier: %d\n", ucp[1]); - printf(" download microcode status: 0x%x [additional status: " - "0x%x]\n", ucp[2], ucp[3]); + cp = (0 == ucp[1]) ? " [primary]" : ""; + printf(" subenclosure identifier: %d%s\n", ucp[1], cp); + cp = get_mc_status(ucp[2]); + if (strlen(cp) > 0) { + printf(" download microcode status: %s [0x%x]\n", cp, ucp[2]); + printf(" download microcode additional status: 0x%x\n", + ucp[3]); + } else + printf(" download microcode status: 0x%x [additional " + "status: 0x%x]\n", ucp[2], ucp[3]); printf(" download microcode maximum size: %d bytes\n", (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7]); printf(" download microcode expected buffer id: 0x%x\n", ucp[11]); @@ -2915,21 +3035,9 @@ ses_process_status_page(int sg_fd, struct opts_t * op) case DPC_STRING: printf("String In diagnostic page (for primary " "subenclosure):\n"); - if (resp_len > 4) { -#if 1 + if (resp_len > 4) dStrHex((const char *)(resp + 4), resp_len - 4, 0); -#else - int j; - - printf(" "); - for (j = 0; j < (resp_len - 4); ++j) { - if ((j > 0) && (0 == (j % 16))) - printf("\n "); - printf("%02x ", *(resp + 4 + j)); - } - printf("\n"); -#endif - } else + else printf(" <empty>\n"); break; case DPC_THRESHOLD: @@ -3056,8 +3164,8 @@ devslotnum_and_sasaddr(struct join_row_t * jrp, unsigned char * ae_ucp) static int join_work(int sg_fd, struct opts_t * op, int display) { - int k, j, res, num_t_hdrs, elem_ind, ei, get_out, desc_len, dn_len; - int et4aes, broken_ei, ei2, got1, jr_max_ind, eip, eiioe, mlen; + int k, j, res, num_t_hdrs, elem_ind, ei, desc_len, dn_len; + int et4aes, broken_ei, ei2, got1, jr_max_ind, mlen; unsigned int ref_gen_code, gen_code; struct join_row_t * jrp; struct join_row_t * jr2p; @@ -3263,19 +3371,25 @@ join_work(int sg_fd, struct opts_t * op, int display) broken_ei = 0; if (ae_ucp) { - get_out = 0; + int eip, eiioe; + int aes_i = 0; + int get_out = 0; + jrp = join_arr; tdhp = type_desc_hdr_arr; for (k = 0; k < num_t_hdrs; ++k, ++tdhp) { if (active_et_aesp(tdhp->etype)) { + /* only consider element types that AES element are permiited + * to refer to, then loop over those number of elements */ for (j = 0; j < tdhp->num_elements; ++j) { if ((ae_ucp + 1) > ae_last_ucp) { get_out = 1; - if (op->verbose) - pr2serr("join_work: off end of ae page\n"); + if (op->verbose || op->warn) + pr2serr("warning: %s: off end of ae page\n", + __func__); break; } - eip = !!(ae_ucp[0] & 0x10); + eip = !!(ae_ucp[0] & 0x10); /* element index present */ if (eip) eiioe = op->eiioe_force ? 1 : (ae_ucp[2] & 1); else @@ -3285,13 +3399,18 @@ join_work(int sg_fd, struct opts_t * op, int display) jr2p = join_arr + ei; if ((ei >= jr_max_ind) || (NULL == jr2p->enc_statp)) { get_out = 1; - pr2serr("join_work: oi=%d, ei=%d [max_ind=%d], " - "eiioe=1 not in join_arr\n", k, ei, + pr2serr("%s: oi=%d, ei=%d [max_ind=%d], eiioe=1 " + "not in join_arr\n", __func__, k, ei, jr_max_ind); break; } devslotnum_and_sasaddr(jr2p, ae_ucp); - jr2p->add_elem_statp = ae_ucp; + if (jr2p->add_elem_statp) { + if (op->warn || op->verbose) + pr2serr("warning: aes slot busy [oi=%d, " + "ei=%d, aes_i=%d]\n", k, ei, aes_i); + } else + jr2p->add_elem_statp = ae_ucp; } else if (eip) { /* and EIIOE=0 */ ei = ae_ucp[3]; try_again: @@ -3306,8 +3425,9 @@ try_again: } if (NULL == jr2p->enc_statp) { get_out = 1; - pr2serr("join_work: oi=%d, ei=%d (broken_ei=%d) " - "not in join_arr\n", k, ei, broken_ei); + pr2serr("warning: %s: oi=%d, ei=%d (broken_ei=%d) " + "not in join_arr\n", __func__, k, ei, + broken_ei); break; } if (! active_et_aesp(jr2p->etype)) { @@ -3316,28 +3436,35 @@ try_again: goto try_again; } devslotnum_and_sasaddr(jr2p, ae_ucp); - jr2p->add_elem_statp = ae_ucp; + if (jr2p->add_elem_statp) { + if (op->warn || op->verbose) + pr2serr("warning: aes slot busy [oi=%d, " + "ei=%d, aes_i=%d]\n", k, ei, aes_i); + } else + jr2p->add_elem_statp = ae_ucp; } else { /* EIP=0 */ while (jrp->enc_statp && ((-1 == jrp->el_ind_indiv) || jrp->add_elem_statp)) ++jrp; if (NULL == jrp->enc_statp) { get_out = 1; - pr2serr("join_work: join_arr has no space for " - "ae\n"); + pr2serr("warning: %s: join_arr has no space for " + "ae\n", __func__); break; } jrp->add_elem_statp = ae_ucp; ++jrp; } ae_ucp += ae_ucp[1] + 2; + ++aes_i; } } else { /* element type not relevant to ae status */ /* step over overall and individual elements */ for (j = 0; j <= tdhp->num_elements; ++j, ++jrp) { if (NULL == jrp->enc_statp) { get_out = 1; - pr2serr("join_work: join_arr has no space\n"); + pr2serr("warning: %s: join_arr has no space\n", + __func__); break; } } @@ -3385,6 +3512,10 @@ try_again: if (NULL == ed_ucp) continue; desc_len = (ed_ucp[2] << 8) + ed_ucp[3]; + /* some element descriptor strings have a trailing NULL and + * count it in their length; adjust */ + if ('\0' == ed_ucp[4 + desc_len - 1]) + --desc_len; if (desc_len != dn_len) continue; if (0 != strncmp(op->desc_name, (const char *)(ed_ucp + 4), @@ -3520,37 +3651,37 @@ strcase_eq(const char * s1p, const char * s2p) static int is_acronym_in_status_ctl(const struct tuple_acronym_val * tavp) { - const struct acronym2tuple * a2tp; + const struct acronym2tuple * ap; - for (a2tp = ecs_a2t_arr; a2tp->acron; ++ a2tp) { - if (strcase_eq(tavp->acron, a2tp->acron)) + for (ap = ecs_a2t_arr; ap->acron; ++ ap) { + if (strcase_eq(tavp->acron, ap->acron)) break; } - return (a2tp->acron ? 1 : 0); + return (ap->acron ? 1 : 0); } static int is_acronym_in_threshold(const struct tuple_acronym_val * tavp) { - const struct acronym2tuple * a2tp; + const struct acronym2tuple * ap; - for (a2tp = th_a2t_arr; a2tp->acron; ++ a2tp) { - if (strcase_eq(tavp->acron, a2tp->acron)) + for (ap = th_a2t_arr; ap->acron; ++ ap) { + if (strcase_eq(tavp->acron, ap->acron)) break; } - return (a2tp->acron ? 1 : 0); + return (ap->acron ? 1 : 0); } static int is_acronym_in_additional(const struct tuple_acronym_val * tavp) { - const struct acronym2tuple * a2tp; + const struct acronym2tuple * ap; - for (a2tp = ae_sas_a2t_arr; a2tp->acron; ++ a2tp) { - if (strcase_eq(tavp->acron, a2tp->acron)) + for (ap = ae_sas_a2t_arr; ap->acron; ++ ap) { + if (strcase_eq(tavp->acron, ap->acron)) break; } - return (a2tp->acron ? 1 : 0); + return (ap->acron ? 1 : 0); } /* DPC_ENC_STATUS DPC_ENC_CONTROL @@ -3561,9 +3692,9 @@ cgs_enc_ctl_stat(int sg_fd, const struct join_row_t * jrp, const struct tuple_acronym_val * tavp, const struct opts_t * op) { - int ret, len, s_byte, s_bit, n_bits; + int ret, len, s_byte, s_bit, n_bits, k; uint64_t ui; - const struct acronym2tuple * a2tp; + const struct acronym2tuple * ap; if (NULL == tavp->acron) { s_byte = tavp->start_byte; @@ -3571,17 +3702,27 @@ cgs_enc_ctl_stat(int sg_fd, const struct join_row_t * jrp, n_bits = tavp->num_bits; } if (tavp->acron) { - for (a2tp = ecs_a2t_arr; a2tp->acron; ++ a2tp) { - if (((jrp->etype == a2tp->etype) || (-1 == a2tp->etype)) && - strcase_eq(tavp->acron, a2tp->acron)) + for (ap = ecs_a2t_arr; ap->acron; ++ ap) { + if (((jrp->etype == ap->etype) || (-1 == ap->etype)) && + strcase_eq(tavp->acron, ap->acron)) break; } - if (a2tp->acron) { - s_byte = a2tp->start_byte; - s_bit = a2tp->start_bit; - n_bits = a2tp->num_bits; - } else + if (ap->acron) { + s_byte = ap->start_byte; + s_bit = ap->start_bit; + n_bits = ap->num_bits; + } else { + if (-1 != ap->etype) { + for (ap = ecs_a2t_arr; ap->acron; ++ap) { + if (0 == strcase_eq(tavp->acron, ap->acron)) { + pr2serr(">>> Found %s acronym but not for element " + "type %d\n", tavp->acron, jrp->etype); + break; + } + } + } return -2; + } } if (op->verbose > 1) pr2serr(" s_byte=%d, s_bit=%d, n_bits=%d\n", s_byte, s_bit, n_bits); @@ -3591,8 +3732,16 @@ cgs_enc_ctl_stat(int sg_fd, const struct join_row_t * jrp, printf("0x%" PRIx64 "\n", ui); else printf("%" PRId64 "\n", (int64_t)ui); - } else { - jrp->enc_statp[0] &= 0x40; /* keep PRDFAIL bit in byte 0 */ + } else { /* --set or --clear */ + if ((0 == op->mask_ign) && (jrp->etype < NUM_ETC)) { + if (op->verbose > 2) + pr2serr("Applying mask to element status [etc=%d] prior to " + "modify then write\n", jrp->etype); + for (k = 0; k < 4; ++k) + jrp->enc_statp[k] &= ses3_element_cmask_arr[jrp->etype][k]; + } else + jrp->enc_statp[0] &= 0x40; /* keep PRDFAIL is set in byte 0 */ + /* next we modify requested bit(s) */ set_big_endian((uint64_t)tavp->val, jrp->enc_statp + s_byte, s_bit, n_bits); jrp->enc_statp[0] |= 0x80; /* set SELECT bit */ @@ -3618,7 +3767,7 @@ cgs_threshold(int sg_fd, const struct join_row_t * jrp, { int ret, len, s_byte, s_bit, n_bits; uint64_t ui; - const struct acronym2tuple * a2tp; + const struct acronym2tuple * ap; if (NULL == jrp->thresh_inp) { pr2serr("No Threshold In/Out element available\n"); @@ -3630,15 +3779,15 @@ cgs_threshold(int sg_fd, const struct join_row_t * jrp, n_bits = tavp->num_bits; } if (tavp->acron) { - for (a2tp = th_a2t_arr; a2tp->acron; ++ a2tp) { - if (((jrp->etype == a2tp->etype) || (-1 == a2tp->etype)) && - strcase_eq(tavp->acron, a2tp->acron)) + for (ap = th_a2t_arr; ap->acron; ++ap) { + if (((jrp->etype == ap->etype) || (-1 == ap->etype)) && + strcase_eq(tavp->acron, ap->acron)) break; } - if (a2tp->acron) { - s_byte = a2tp->start_byte; - s_bit = a2tp->start_bit; - n_bits = a2tp->num_bits; + if (ap->acron) { + s_byte = ap->start_byte; + s_bit = ap->start_bit; + n_bits = ap->num_bits; } else return -2; } @@ -3673,7 +3822,7 @@ cgs_additional_el(const struct join_row_t * jrp, { int s_byte, s_bit, n_bits; uint64_t ui; - const struct acronym2tuple * a2tp; + const struct acronym2tuple * ap; if (NULL == jrp->add_elem_statp) { pr2serr("No additional element status element available\n"); @@ -3685,15 +3834,15 @@ cgs_additional_el(const struct join_row_t * jrp, n_bits = tavp->num_bits; } if (tavp->acron) { - for (a2tp = ae_sas_a2t_arr; a2tp->acron; ++ a2tp) { - if (((jrp->etype == a2tp->etype) || (-1 == a2tp->etype)) && - strcase_eq(tavp->acron, a2tp->acron)) + for (ap = ae_sas_a2t_arr; ap->acron; ++ap) { + if (((jrp->etype == ap->etype) || (-1 == ap->etype)) && + strcase_eq(tavp->acron, ap->acron)) break; } - if (a2tp->acron) { - s_byte = a2tp->start_byte; - s_bit = a2tp->start_bit; - n_bits = a2tp->num_bits; + if (ap->acron) { + s_byte = ap->start_byte; + s_bit = ap->start_bit; + n_bits = ap->num_bits; } else return -2; } @@ -3757,6 +3906,10 @@ ses_cgs(int sg_fd, const struct tuple_acronym_val * tavp, if (NULL == ed_ucp) continue; desc_len = (ed_ucp[2] << 8) + ed_ucp[3]; + /* some element descriptor strings have a trailing NULL and + * count it; adjust */ + if ('\0' == ed_ucp[4 + desc_len - 1]) + --desc_len; if (desc_len != dn_len) continue; if (0 != strncmp(op->desc_name, (const char *)(ed_ucp + 4), @@ -3871,14 +4024,15 @@ enumerate_work(const struct opts_t * op) { int num; const struct element_type_t * etp; - const struct acronym2tuple * a2tp; + const struct acronym2tuple * ap; char b[64]; + char a[160]; const char * cp; if (op->dev_name) printf(">>> DEVICE %s ignored when --%s option given.\n", op->dev_name, (op->do_list ? "list" : "enumerate")); - num = op->do_enumerate + op->do_list; + num = op->enumerate + op->do_list; if (num < 2) { enumerate_diag_pages(); printf("\nSES element type names, followed by abbreviation and " @@ -3890,27 +4044,42 @@ enumerate_work(const struct opts_t * op) /* command line has multiple --enumerate and/or --list options */ printf("--clear, --get, --set acronyms for Enclosure Status/Control " "['es' or 'ec'] page:\n"); - for (a2tp = ecs_a2t_arr; a2tp->acron; ++a2tp) { - cp = (a2tp->etype < 0) ? "*" : - find_element_tname(a2tp->etype, b, sizeof(b)); - printf(" %s [%s] [%d:%d:%d]\n", a2tp->acron, (cp ? cp : "??"), - a2tp->start_byte, a2tp->start_bit, a2tp->num_bits); + for (ap = ecs_a2t_arr; ap->acron; ++ap) { + cp = (ap->etype < 0) ? + "*" : find_element_tname(ap->etype, b, sizeof(b)); + snprintf(a, sizeof(a), " %s [%s] [%d:%d:%d]", ap->acron, + (cp ? cp : "??"), ap->start_byte, ap->start_bit, + ap->num_bits); + if (ap->info) + printf("%-44s %s\n", a, ap->info); + else + printf("%s\n", a); } printf("\n--clear, --get, --set acronyms for Threshold In/Out " "['th'] page:\n"); - for (a2tp = th_a2t_arr; a2tp->acron; ++a2tp) { - cp = (a2tp->etype < 0) ? "*" : - find_element_tname(a2tp->etype, b, sizeof(b)); - printf(" %s [%s] [%d:%d:%d]\n", a2tp->acron, (cp ? cp : "??"), - a2tp->start_byte, a2tp->start_bit, a2tp->num_bits); + for (ap = th_a2t_arr; ap->acron; ++ap) { + cp = (ap->etype < 0) ? "*" : + find_element_tname(ap->etype, b, sizeof(b)); + snprintf(a, sizeof(a), " %s [%s] [%d:%d:%d]", ap->acron, + (cp ? cp : "??"), ap->start_byte, ap->start_bit, + ap->num_bits); + if (ap->info) + printf("%-34s %s\n", a, ap->info); + else + printf("%s\n", a); } printf("\n--get acronyms for Additional Element Status ['aes'] page " "(SAS EIP=1):\n"); - for (a2tp = ae_sas_a2t_arr; a2tp->acron; ++a2tp) { - cp = (a2tp->etype < 0) ? "*" : - find_element_tname(a2tp->etype, b, sizeof(b)); - printf(" %s [%s] [%d:%d:%d]\n", a2tp->acron, (cp ? cp : "??"), - a2tp->start_byte, a2tp->start_bit, a2tp->num_bits); + for (ap = ae_sas_a2t_arr; ap->acron; ++ap) { + cp = (ap->etype < 0) ? "*" : + find_element_tname(ap->etype, b, sizeof(b)); + snprintf(a, sizeof(a), " %s [%s] [%d:%d:%d]", ap->acron, + (cp ? cp : "??"), ap->start_byte, ap->start_bit, + ap->num_bits); + if (ap->info) + printf("%-34s %s\n", a, ap->info); + else + printf("%s\n", a); } } } @@ -3944,7 +4113,7 @@ main(int argc, char * argv[]) usage(op->do_help); return 0; } - if (op->do_enumerate || op->do_list) { + if (op->enumerate || op->do_list) { enumerate_work(op); return 0; } @@ -4086,6 +4255,8 @@ main(int argc, char * argv[]) case DPC_DOWNLOAD_MICROCODE: /* Download Microcode Control [0xe] */ printf("Sending Download Microcode Control [0x%x] page, with " "page length=%d bytes\n", op->page_code, op->arr_len); + printf(" Perhaps it would be better to use the sg_ses_microcode " + "utility\n"); ret = do_senddiag(sg_fd, 1, op->data_arr, op->arr_len + 4, 1, op->verbose); if (ret) { diff --git a/src/sg_ses_microcode.c b/src/sg_ses_microcode.c new file mode 100644 index 0000000..f49a346 --- /dev/null +++ b/src/sg_ses_microcode.c @@ -0,0 +1,801 @@ +/* + * Copyright (c) 2014 Douglas Gilbert. + * All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the BSD_LICENSE file. + */ + +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <ctype.h> +#include <string.h> +#include <getopt.h> +#include <sys/stat.h> +#include <sys/types.h> +#define __STDC_FORMAT_MACROS 1 +#include <inttypes.h> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "sg_lib.h" +#include "sg_cmds_basic.h" +#include "sg_cmds_extra.h" +#ifdef SG_LIB_WIN32 +#ifdef SG_LIB_WIN32_DIRECT +#include "sg_pt.h" /* needed for scsi_pt_win32_direct() */ +#endif +#endif +#include "sg_unaligned.h" + +/* + * This utility issues the SCSI SEND DIAGNOSTIC and RECEIVE DIAGNOSTIC + * RESULTS commands in order to send microcode to the given SES device. + */ + +static const char * version_str = "1.02 20141029"; /* ses3r07 */ + +#define ME "sg_ses_microcode: " +#define MAX_XFER_LEN (128 * 1024 * 1024) +#define DEF_XFER_LEN (8 * 1024 * 1024) +#define DEF_DI_LEN (8 * 1024) +#define EBUFF_SZ 256 + +#define DPC_DOWNLOAD_MICROCODE 0xe + +struct opts_t { + int bpw; + int bpw_then_activate; + int mc_id; + int mc_len; + int mc_len_given; + int mc_mode; + int mc_non; + int mc_offset; + int mc_skip; + int mc_subenc; + int mc_tlen; + int verbose; +}; + +static struct option long_options[] = { + {"bpw", required_argument, 0, 'b'}, + {"help", no_argument, 0, 'h'}, + {"id", required_argument, 0, 'i'}, + {"in", required_argument, 0, 'I'}, + {"length", required_argument, 0, 'l'}, + {"mode", required_argument, 0, 'm'}, + {"non", no_argument, 0, 'N'}, + {"offset", required_argument, 0, 'o'}, + {"skip", required_argument, 0, 's'}, + {"subenc", required_argument, 0, 'S'}, + {"tlength", required_argument, 0, 't'}, + {"verbose", no_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, + {0, 0, 0, 0}, +}; + + +#ifdef __GNUC__ +static int pr2serr(const char * fmt, ...) + __attribute__ ((format (printf, 1, 2))); +#else +static int pr2serr(const char * fmt, ...); +#endif + + +static int +pr2serr(const char * fmt, ...) +{ + va_list args; + int n; + + va_start(args, fmt); + n = vfprintf(stderr, fmt, args); + va_end(args); + return n; +} + +static void +usage() +{ + pr2serr("Usage: " + "sg_ses_microcode [--bpw=CS] [--help] [--id=ID] [--in=FILE]\n" + " [--length=LEN] [--mode=MO] " + "[--non]\n" + " [--offset=OFF] [--skip=SKIP] " + "[--subenc=SEID]\n" + " [--tlength=TLEN] [--verbose] " + "[--version]\n" + " DEVICE\n" + " where:\n" + " --bpw=CS|-b CS CS is chunk size: bytes per send " + "diagnostic\n" + " command (def: 0 -> as many as " + "possible)\n" + " --help|-h print out usage message then exit\n" + " --id=ID|-i ID buffer identifier (0 (default) to " + "255)\n" + " --in=FILE|-I FILE read from FILE ('-I -' read " + "from stdin)\n" + " --length=LEN|-l LEN length in bytes to send; may be " + "deduced from\n" + " FILE\n" + " --mode=MO|-m MO download microcode mode, MO is " + "number or\n" + " acronym (def: 0 -> 'dmc_status')\n" + " --non|-N non-standard: bypass all receive " + "diagnostic\n" + " results commands except after check " + "condition\n" + " --offset=OFF|-o OFF buffer offset (unit: bytes, def: " + "0);\n" + " ignored if --bpw=CS given\n" + " --skip=SKIP|-s SKIP bytes in file FILE to skip before " + "reading\n" + " --subenc=SEID|-S SEID subenclosure identifier (def: 0 " + "(primary))\n" + " --tlength=TLEN|-t TLEN total length of firmware in " + "bytes\n" + " (def: 0). Only needed if " + "TLEN>LEN\n" + " --verbose|-v increase verbosity\n" + " --version|-V print version string and exit\n\n" + "Does one or more SCSI SEND DIAGNOSTIC followed by RECEIVE " + "DIAGNOSTIC\nRESULTS command sequences in order to download " + "microcode. Use '-m xxx'\nto list available modes. With only " + "DEVICE given, the Download Microcode\nStatus dpage is output.\n" + ); +} + +#define MODE_DNLD_STATUS 0 +#define MODE_DNLD_MC_OFFS 6 +#define MODE_DNLD_MC_OFFS_SAVE 7 +#define MODE_DNLD_MC_OFFS_DEFER 0x0E +#define MODE_ACTIVATE_MC 0x0F + +struct mode_s { + const char *mode_string; + int mode; + const char *comment; +}; + +static struct mode_s mode_arr[] = { + {"dmc_status", MODE_DNLD_STATUS, "report status of microcode " + "download"}, + {"dmc_offs", MODE_DNLD_MC_OFFS, "download microcode with offsets " + "and activate"}, + {"dmc_offs_save", MODE_DNLD_MC_OFFS_SAVE, "download microcode with " + "offsets, save and\n\t\t\t\tactivate"}, + {"dmc_offs_defer", MODE_DNLD_MC_OFFS_DEFER, "download microcode " + "with offsets, save and\n\t\t\t\tdefer activation"}, + {"activate_mc", MODE_ACTIVATE_MC, "activate deferred microcode"}, + {NULL, 0, NULL}, +}; + + +static void +print_modes(void) +{ + const struct mode_s * mp; + + pr2serr("The modes parameter argument can be numeric (hex or decimal)\n" + "or symbolic:\n"); + for (mp = mode_arr; mp->mode_string; ++mp) { + pr2serr(" %2d (0x%02x) %-18s%s\n", mp->mode, mp->mode, + mp->mode_string, mp->comment); + } + pr2serr("\nAdditionally '--bpw=<val>,act' does a activate deferred " + "microcode after a\nsuccessful multipart dmc_offs_defer mode " + "download.\n"); +} + +struct diag_page_code { + int page_code; + const char * desc; +}; + +/* An array of Download microcode status field values and descriptions */ +static struct diag_page_code mc_status_arr[] = { + {0x0, "No download microcode operation in progress"}, + {0x1, "Download in progress, awaiting more"}, + {0x2, "Download complete, updating storage"}, + {0x3, "Updating storage with deferred microcode"}, + {0x10, "Complete, no error, starting now"}, + {0x11, "Complete, no error, start after hard reset or power cycle"}, + {0x12, "Complete, no error, start after power cycle"}, + {0x13, "Complete, no error, start after activate_mc, hard reset or " + "power cycle"}, + {0x80, "Error, discarded, see additional status"}, + {0x81, "Error, discarded, image error"}, + {0x82, "Timeout, discarded"}, + {0x83, "Internal error, need new microcode before reset"}, + {0x84, "Internal error, need new microcode, reset safe"}, + {0x85, "Unexpected activate_mc received"}, + {0x1000, NULL}, +}; + +static const char * +get_mc_status_str(unsigned char status_val) +{ + const struct diag_page_code * mcsp; + + for (mcsp = mc_status_arr; mcsp->desc; ++mcsp) { + if (status_val == mcsp->page_code) + return mcsp->desc; + } + return ""; +} + +/* DPC_DOWNLOAD_MICROCODE [0xe] */ +static void +ses_download_code_sdg(const unsigned char * resp, int resp_len, + uint32_t gen_code) +{ + int k, num_subs, num; + const unsigned char * ucp; + const char * cp; + + printf("Download microcode status diagnostic page:\n"); + if (resp_len < 8) + goto truncated; + num_subs = resp[1]; /* primary is additional one) */ + num = (resp_len - 8) / 16; + if ((resp_len - 8) % 16) + pr2serr("Found %d Download microcode status descriptors, but there " + "is residual\n", num); + printf(" number of secondary subenclosures: %d\n", num_subs); + printf(" generation code: 0x%" PRIx32 "\n", gen_code); + ucp = resp + 8; + for (k = 0; k < num; ++k, ucp += 16) { + cp = (0 == ucp[1]) ? " [primary]" : ""; + printf(" subenclosure identifier: %d%s\n", ucp[1], cp); + cp = get_mc_status_str(ucp[2]); + if (strlen(cp) > 0) { + printf(" download microcode status: %s [0x%x]\n", cp, ucp[2]); + printf(" download microcode additional status: 0x%x\n", + ucp[3]); + } else + printf(" download microcode status: 0x%x [additional " + "status: 0x%x]\n", ucp[2], ucp[3]); + printf(" download microcode maximum size: %" PRIu32 " bytes\n", + sg_get_unaligned_be32(ucp + 4)); + printf(" download microcode expected buffer id: 0x%x\n", ucp[11]); + printf(" download microcode expected buffer id offset: %" PRIu32 + "\n", sg_get_unaligned_be32(ucp + 12)); + } + return; +truncated: + pr2serr(" <<<download status: response too short>>>\n"); + return; +} + +struct dout_buff_t { + unsigned char * doutp; + int dout_len; +}; + +static int +send_then_receive(int sg_fd, uint32_t gen_code, int off_off, + const unsigned char * dmp, int dmp_len, + struct dout_buff_t * wp, unsigned char * dip, + int last, const struct opts_t * op) +{ + int do_len, rem, res, rsp_len, k, num, mc_status, verb; + int send_data = 0; + int ret = 0; + uint32_t rec_gen_code; + const unsigned char * ucp; + const char * cp; + + verb = (op->verbose > 1) ? op->verbose - 1 : 0; + switch (op->mc_mode) { + case MODE_DNLD_MC_OFFS: + case MODE_DNLD_MC_OFFS_SAVE: + case MODE_DNLD_MC_OFFS_DEFER: + send_data = 1; + do_len = 24 + dmp_len; + rem = do_len % 4; + if (rem) + do_len += (4 - rem); + break; + case MODE_ACTIVATE_MC: + do_len = 24; + break; + default: + pr2serr("send_then_receive: unexpected mc_mode=0x%x\n", op->mc_mode); + return SG_LIB_SYNTAX_ERROR; + } + if (do_len > wp->dout_len) { + if (wp->doutp) + free(wp->doutp); + wp->doutp = (unsigned char *)malloc(do_len); + if (! wp->doutp) { + pr2serr("send_then_receive: unable to malloc %d bytes\n", do_len); + return SG_LIB_CAT_OTHER; + } + wp->dout_len = do_len; + } + memset(wp->doutp, 0, do_len); + wp->doutp[0] = DPC_DOWNLOAD_MICROCODE; + wp->doutp[1] = op->mc_subenc; + sg_put_unaligned_be16(do_len - 4, wp->doutp + 2); + sg_put_unaligned_be32(gen_code, wp->doutp + 4); + wp->doutp[8] = op->mc_mode; + wp->doutp[11] = op->mc_id; + if (send_data) + sg_put_unaligned_be32(op->mc_offset + off_off, wp->doutp + 12); + sg_put_unaligned_be32(op->mc_tlen, wp->doutp + 16); + sg_put_unaligned_be32(dmp_len, wp->doutp + 20); + if (send_data && (dmp_len > 0)) + memcpy(wp->doutp + 24, dmp, dmp_len); + /* select long duration timeout (7200 seconds) */ + res = sg_ll_send_diag(sg_fd, 0 /* sf_code */, 1 /* pf */, 0 /* sf */, + 0 /* devofl */, 0 /* unitofl */, + 1 /* long_duration */, wp->doutp, do_len, + 1 /* noisy */, verb); + if (op->mc_non) { + /* If non-standard, only call RDR after failed SD */ + if (0 == res) + return 0; + /* If RDR error after SD error, prefer reporting SD error */ + ret = res; + } else { + switch (op->mc_mode) { + case MODE_DNLD_MC_OFFS: + case MODE_DNLD_MC_OFFS_SAVE: + if (res) + return res; + else if (last) + return 0; /* RDR after last may hit a device reset */ + break; + case MODE_DNLD_MC_OFFS_DEFER: + if (res) + return res; + break; + case MODE_ACTIVATE_MC: + if (0 == res) + return 0; /* RDR after ACTIVATE_MC may hit a device reset */ + /* SD has failed, so do a RDR but return SD's error */ + ret = res; + break; + default: + pr2serr("send_then_receive: mc_mode=0x%x\n", op->mc_mode); + return SG_LIB_SYNTAX_ERROR; + } + } + + res = sg_ll_receive_diag(sg_fd, 1 /* pcv */, DPC_DOWNLOAD_MICROCODE, dip, + DEF_DI_LEN, 1, verb); + if (res) + return ret ? ret : res; + rsp_len = sg_get_unaligned_be16(dip + 2) + 4; + if (rsp_len > DEF_DI_LEN) { + pr2serr("<<< warning response buffer too small [%d but need " + "%d]>>>\n", DEF_DI_LEN, rsp_len); + rsp_len = DEF_DI_LEN; + } + if (rsp_len < 8) { + pr2serr("Download microcode status dpage too short\n"); + return ret ? ret : SG_LIB_CAT_OTHER; + } + rec_gen_code = sg_get_unaligned_be32(dip + 4); + if (rec_gen_code != gen_code) + pr2serr("gen_code changed from %" PRIu32 " to %" PRIu32 + ", continuing but may fail\n", gen_code, rec_gen_code); + num = (rsp_len - 8) / 16; + if ((rsp_len - 8) % 16) + pr2serr("Found %d Download microcode status descriptors, but there " + "is residual\n", num); + ucp = dip + 8; + for (k = 0; k < num; ++k, ucp += 16) { + if ((unsigned int)op->mc_subenc == (unsigned int)ucp[1]) { + mc_status = ucp[2]; + cp = get_mc_status_str(mc_status); + if ((mc_status >= 0x80) || op->verbose) + pr2serr("mc offset=%d: status: %s [0x%x, additional=0x%x]\n", + off_off, cp, mc_status, ucp[3]); + if (op->verbose > 1) + pr2serr(" subenc_id=%d, expected_buffer_id=%d, " + "expected_offset=0x%" PRIx32 "\n", ucp[1], ucp[11], + sg_get_unaligned_be32(ucp + 12)); + if (mc_status >= 0x80) + ret = ret ? ret : SG_LIB_CAT_OTHER; + } + } + return ret; +} + + +int +main(int argc, char * argv[]) +{ + int sg_fd, res, c, len, k, n, got_stdin, is_reg, rsp_len, verb, last; + int infd = -1; + int do_help = 0; + const char * device_name = NULL; + const char * file_name = NULL; + unsigned char * dmp = NULL; + unsigned char * dip = NULL; + char * cp; + char ebuff[EBUFF_SZ]; + struct stat a_stat; + struct dout_buff_t dout; + struct opts_t opts; + struct opts_t * op; + const struct mode_s * mp; + uint32_t gen_code = 0; + int ret = 0; + + op = &opts; + memset(op, 0, sizeof(opts)); + memset(&dout, 0, sizeof(dout)); + while (1) { + int option_index = 0; + + c = getopt_long(argc, argv, "b:hi:I:l:m:No:s:S:t:vV", long_options, + &option_index); + if (c == -1) + break; + + switch (c) { + case 'b': + op->bpw = sg_get_num(optarg); + if (op->bpw < 0) { + pr2serr("argument to '--bpw' should be in a positive " + "number\n"); + return SG_LIB_SYNTAX_ERROR; + } + if ((cp = strchr(optarg, ','))) { + if (0 == strncmp("act", cp + 1, 3)) + ++op->bpw_then_activate; + } + break; + case 'h': + case '?': + ++do_help; + break; + case 'i': + op->mc_id = sg_get_num(optarg); + if ((op->mc_id < 0) || (op->mc_id > 255)) { + pr2serr("argument to '--id' should be in the range 0 to " + "255\n"); + return SG_LIB_SYNTAX_ERROR; + } + break; + case 'I': + file_name = optarg; + break; + case 'l': + op->mc_len = sg_get_num(optarg); + if (op->mc_len < 0) { + pr2serr("bad argument to '--length'\n"); + return SG_LIB_SYNTAX_ERROR; + } + op->mc_len_given = 1; + break; + case 'm': + if (isdigit(*optarg)) { + op->mc_mode = sg_get_num(optarg); + if ((op->mc_mode < 0) || (op->mc_mode > 255)) { + pr2serr("argument to '--mode' should be in the range 0 " + "to 255\n"); + return SG_LIB_SYNTAX_ERROR; + } + } else { + len = strlen(optarg); + for (mp = mode_arr; mp->mode_string; ++mp) { + if (0 == strncmp(mp->mode_string, optarg, len)) { + op->mc_mode = mp->mode; + break; + } + } + if (! mp->mode_string) { + print_modes(); + return SG_LIB_SYNTAX_ERROR; + } + } + break; + case 'N': + ++op->mc_non; + break; + case 'o': + op->mc_offset = sg_get_num(optarg); + if (op->mc_offset < 0) { + pr2serr("bad argument to '--offset'\n"); + return SG_LIB_SYNTAX_ERROR; + } + if (0 != (op->mc_offset % 4)) { + pr2serr("'--offset' value needs to be a multiple of 4\n"); + return SG_LIB_SYNTAX_ERROR; + } + break; + case 's': + op->mc_skip = sg_get_num(optarg); + if (op->mc_skip < 0) { + pr2serr("bad argument to '--skip'\n"); + return SG_LIB_SYNTAX_ERROR; + } + break; + case 'S': + op->mc_subenc = sg_get_num(optarg); + if ((op->mc_subenc < 0) || (op->mc_subenc > 255)) { + pr2serr("expected argument to '--subenc' to be 0 to 255\n"); + return SG_LIB_SYNTAX_ERROR; + } + break; + case 't': + op->mc_tlen = sg_get_num(optarg); + if (op->mc_tlen < 0) { + pr2serr("bad argument to '--tlength'\n"); + return SG_LIB_SYNTAX_ERROR; + } + break; + case 'v': + ++op->verbose; + break; + case 'V': + pr2serr(ME "version: %s\n", version_str); + return 0; + default: + pr2serr("unrecognised option code 0x%x ??\n", c); + usage(); + return SG_LIB_SYNTAX_ERROR; + } + } + if (do_help) { + if (do_help > 1) { + usage(); + pr2serr("\n"); + print_modes(); + } else + usage(); + return 0; + } + if (optind < argc) { + if (NULL == device_name) { + 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 (NULL == device_name) { + pr2serr("missing device name!\n"); + usage(); + return SG_LIB_SYNTAX_ERROR; + } + + if ((op->mc_len > 0) && (op->bpw > op->mc_len)) { + pr2serr("trim chunk size (CS) to be the same as LEN\n"); + op->bpw = op->mc_len; + } + +#ifdef SG_LIB_WIN32 +#ifdef SG_LIB_WIN32_DIRECT + if (op->verbose > 4) + pr2serr("Initial win32 SPT interface state: %s\n", + scsi_pt_win32_spt_state() ? "direct" : "indirect"); + scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT pt interface */); +#endif +#endif + + sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, op->verbose); + if (sg_fd < 0) { + pr2serr(ME "open error: %s: %s\n", device_name, + safe_strerror(-sg_fd)); + return SG_LIB_FILE_ERROR; + } + + if (file_name && ((MODE_DNLD_STATUS == op->mc_mode) || + (MODE_ACTIVATE_MC == op->mc_mode))) + pr2serr("ignoring --in=FILE option\n"); + else if (file_name) { + got_stdin = (0 == strcmp(file_name, "-")) ? 1 : 0; + if (got_stdin) + infd = STDIN_FILENO; + else { + if ((infd = open(file_name, O_RDONLY)) < 0) { + snprintf(ebuff, EBUFF_SZ, + ME "could not open %s for reading", file_name); + perror(ebuff); + ret = SG_LIB_FILE_ERROR; + goto fini; + } else if (sg_set_binary_mode(infd) < 0) + perror("sg_set_binary_mode"); + } + if ((0 == fstat(infd, &a_stat)) && S_ISREG(a_stat.st_mode)) { + is_reg = 1; + if (0 == op->mc_len) { + if (op->mc_skip >= a_stat.st_size) { + pr2serr("skip exceeds file size of %d bytes\n", + (int)a_stat.st_size); + ret = SG_LIB_FILE_ERROR; + goto fini; + } + op->mc_len = (int)(a_stat.st_size) - op->mc_skip; + } + } else { + is_reg = 0; + if (0 == op->mc_len) + op->mc_len = DEF_XFER_LEN; + } + if (op->mc_len > MAX_XFER_LEN) { + pr2serr("file size or requested length (%d) exceeds " + "MAX_XFER_LEN of %d bytes\n", op->mc_len, + MAX_XFER_LEN); + ret = SG_LIB_FILE_ERROR; + goto fini; + } + if (NULL == (dmp = (unsigned char *)malloc(op->mc_len))) { + pr2serr(ME "out of memory (to hold microcode)\n"); + ret = SG_LIB_CAT_OTHER; + goto fini; + } + /* Don't remember why this is preset to 0xff, from write_buffer */ + memset(dmp, 0xff, op->mc_len); + if (op->mc_skip > 0) { + if (! is_reg) { + if (got_stdin) + pr2serr("Can't skip on stdin\n"); + else + pr2serr(ME "not a 'regular' file so can't apply skip\n"); + ret = SG_LIB_FILE_ERROR; + goto fini; + } + if (lseek(infd, op->mc_skip, SEEK_SET) < 0) { + snprintf(ebuff, EBUFF_SZ, ME "couldn't skip to " + "required position on %s", file_name); + perror(ebuff); + ret = SG_LIB_FILE_ERROR; + goto fini; + } + } + res = read(infd, dmp, op->mc_len); + if (res < 0) { + snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %s", + file_name); + perror(ebuff); + ret = SG_LIB_FILE_ERROR; + goto fini; + } + if (res < op->mc_len) { + if (op->mc_len_given) { + pr2serr("tried to read %d bytes from %s, got %d bytes\n", + op->mc_len, file_name, res); + pr2serr("pad with 0xff bytes and continue\n"); + } else { + if (op->verbose) { + pr2serr("tried to read %d bytes from %s, got %d " + "bytes\n", op->mc_len, file_name, res); + pr2serr("will send %d bytes", res); + if ((op->bpw > 0) && (op->bpw < op->mc_len)) + pr2serr(", %d bytes per WRITE BUFFER command\n", + op->bpw); + else + pr2serr("\n"); + } + op->mc_len = res; + } + } + if (! got_stdin) + close(infd); + infd = -1; + } else if (! ((MODE_DNLD_STATUS == op->mc_mode) || + (MODE_ACTIVATE_MC == op->mc_mode))) { + pr2serr("need --in=FILE option with given mode\n"); + ret = SG_LIB_SYNTAX_ERROR; + goto fini; + } + if (op->mc_tlen < op->mc_len) + op->mc_tlen = op->mc_len; + if (op->mc_non && (MODE_DNLD_STATUS == op->mc_mode)) { + pr2serr("Do nothing because '--non' given so fetching the Download " + "microcode status\ndpage might be dangerous\n"); + goto fini; + } + + if (NULL == (dip = (unsigned char *)malloc(DEF_DI_LEN))) { + pr2serr(ME "out of memory (data-in buffer)\n"); + ret = SG_LIB_CAT_OTHER; + goto fini; + } + memset(dip, 0, DEF_DI_LEN); + verb = (op->verbose > 1) ? op->verbose - 1 : 0; + /* Fetch Download microcode status dpage for generation code ++ */ + res = sg_ll_receive_diag(sg_fd, 1 /* pcv */, DPC_DOWNLOAD_MICROCODE, dip, + DEF_DI_LEN, 1, verb); + if (0 == res) { + rsp_len = sg_get_unaligned_be16(dip + 2) + 4; + if (rsp_len > DEF_DI_LEN) { + pr2serr("<<< warning response buffer too small [%d but need " + "%d]>>>\n", DEF_DI_LEN, rsp_len); + rsp_len = DEF_DI_LEN; + } + if (rsp_len < 8) { + pr2serr("Download microcode status dpage too short\n"); + ret = SG_LIB_CAT_OTHER; + goto fini; + } + } else { + ret = res; + goto fini; + } + gen_code = sg_get_unaligned_be32(dip + 4); + + if (MODE_DNLD_STATUS == op->mc_mode) { + ses_download_code_sdg(dip, rsp_len, gen_code); + goto fini; + } else if (MODE_ACTIVATE_MC == op->mc_mode) { + res = send_then_receive(sg_fd, gen_code, 0, NULL, 0, &dout, dip, 1, + op); + ret = res; + goto fini; + } + + res = 0; + if (op->bpw > 0) { + for (k = 0, last = 0; k < op->mc_len; k += n) { + n = op->mc_len - k; + if (n > op->bpw) + n = op->bpw; + else + last = 1; + if (op->verbose) + pr2serr("bpw loop: mode=0x%x, id=%d, off_off=%d, len=%d, " + "last=%d\n", op->mc_mode, op->mc_id, k, n, last); + res = send_then_receive(sg_fd, gen_code, k, dmp + k, n, &dout, + dip, last, op); + if (res) + break; + } + if (op->bpw_then_activate && (0 == res)) { + op->mc_mode = MODE_ACTIVATE_MC; + if (op->verbose) + pr2serr("sending Activate deferred microcode [0xf]\n"); + res = send_then_receive(sg_fd, gen_code, 0, NULL, 0, &dout, + dip, 1, op); + } + } else { + if (op->verbose) + pr2serr("single: mode=0x%x, id=%d, offset=%d, len=%d\n", + op->mc_mode, op->mc_id, op->mc_offset, op->mc_len); + res = send_then_receive(sg_fd, gen_code, 0, dmp, op->mc_len, &dout, + dip, 1, op); + } + if (res) + ret = res; + +fini: + if ((infd >= 0) && (! got_stdin)) + close(infd); + if (dmp) + free(dmp); + if (dout.doutp) + free(dout.doutp); + res = sg_cmds_close_device(sg_fd); + if (res < 0) { + pr2serr("close error: %s\n", safe_strerror(-res)); + if (0 == ret) + return SG_LIB_FILE_ERROR; + } + if (ret && (0 == op->verbose)) { + if (SG_LIB_CAT_INVALID_OP == ret) + pr2serr("%sRECEIVE DIAGNOSTIC RESULTS command not supported\n", + ((MODE_DNLD_STATUS == op->mc_mode) ? + "" : "SEND DIAGNOSTIC or ")); + else if (ret > 0) + fprintf(stderr, "Failed, exit status %d\n", ret); + else if (ret < 0) + fprintf(stderr, "Some error occurred\n"); + } + return (ret >= 0) ? ret : SG_LIB_CAT_OTHER; +} diff --git a/src/sg_vpd.c b/src/sg_vpd.c index c363dc5..5089efc 100644 --- a/src/sg_vpd.c +++ b/src/sg_vpd.c @@ -16,6 +16,8 @@ #define __STDC_FORMAT_MACROS 1 #include <inttypes.h> #include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> #ifdef HAVE_CONFIG_H #include "config.h" @@ -23,6 +25,7 @@ #include "sg_lib.h" #include "sg_cmds_basic.h" #include "sg_pt.h" +#include "sg_unaligned.h" /* This utility program was originally written for the Linux OS SCSI subsystem. @@ -33,18 +36,48 @@ */ -static const char * version_str = "0.87 20140529"; /* spc4r37 + sbc4r01 */ - /* And with sbc3r35, vale Mark Evans */ +static const char * version_str = "0.96 20141110"; /* spc4r37 + sbc4r03 */ + + +/* These structures are duplicates of those of the same name in + * sg_vpd_vendor.c . Take care that both are the same. */ +struct opts_t { + int do_all; + int do_enum; + int do_hex; + int num_vpd; + int do_ident; + int do_long; + int maxlen; + int do_quiet; + int do_raw; + int vend_prod_num; + int verbose; + const char * device_name; + const char * page_str; + const char * inhex_fn; + const char * vend_prod; +}; + +struct svpd_values_name_t { + int value; /* VPD page number */ + int subvalue; /* to differentiate if value+pdt are not unique */ + int pdt; /* peripheral device type id, -1 is the default */ + /* (all or not applicable) value */ + const char * acron; + const char * name; +}; + -void svpd_enumerate_vendor(int vp_num); -int svpd_count_vendor_vpds(int num_vpd, int vp_num); -int svpd_decode_vendor(int sg_fd, int num_vpd, int subvalue, int maxlen, - int do_hex, int do_raw, int do_long, int do_quiet, - int verbose); +void svpd_enumerate_vendor(int vend_prod_num); +int svpd_count_vendor_vpds(int num_vpd, int vend_prod_num); +int svpd_decode_vendor(int sg_fd, struct opts_t * op, int off); const struct svpd_values_name_t * svpd_find_vendor_by_acron(const char * ap); int svpd_find_vp_num_by_acron(const char * vp_ap); const struct svpd_values_name_t * svpd_find_vendor_by_num(int page_num, - int vp_num); + int vend_prod_num); +int vpd_fetch_page_from_dev(int sg_fd, unsigned char * rp, int page, + int mxlen, int vb, int * rlenp); /* standard VPD pages, in ascending page number order */ @@ -79,6 +112,7 @@ const struct svpd_values_name_t * svpd_find_vendor_by_num(int page_num, #define VPD_SUP_BLOCK_LENS 0xb4 /* SBC-4 */ #define VPD_DTDE_ADDRESS 0xb4 /* SSC-4 */ #define VPD_BLOCK_DEV_C_EXTENS 0xb5 /* SBC-4 */ +#define VPD_ZBC_DEV_CHARS 0xb6 /* ZBC */ #define VPD_NO_RATHER_STD_INQ -2 /* request for standard inquiry */ /* Device identification VPD page associations */ @@ -103,19 +137,7 @@ const struct svpd_values_name_t * svpd_find_vendor_by_num(int page_num, #define DEF_PT_TIMEOUT 60 /* 60 seconds */ -/* This structure is a duplicate of one of the same name in sg_vpd_vendor.c . - Take care that both have the same fields (and types). */ -struct svpd_values_name_t { - int value; /* VPD page number */ - int subvalue; /* to differentiate if value+pdt are not unique */ - int pdt; /* peripheral device type id, -1 is the default */ - /* (all or not applicable) value */ - const char * acron; - const char * name; -}; - - -static unsigned char rsp_buff[MX_ALLOC_LEN + 2]; +unsigned char rsp_buff[MX_ALLOC_LEN + 2]; static int decode_dev_ids(const char * print_if_found, unsigned char * buff, int len, int m_assoc, int m_desig_type, @@ -124,6 +146,7 @@ static void decode_transport_id(const char * leadin, unsigned char * ucp, int len); static struct option long_options[] = { + {"all", no_argument, 0, 'a'}, {"enumerate", no_argument, 0, 'e'}, {"help", no_argument, 0, 'h'}, {"hex", no_argument, 0, 'H'}, @@ -196,6 +219,8 @@ static struct svpd_values_name_t standard_vpd_pg[] = { {VPD_SUPPORTED_VPDS, 0, -1, "sv", "Supported VPD pages"}, {VPD_TA_SUPPORTED, 0, 1, "tas", "TapeAlert supported flags (SSC)"}, {VPD_3PARTY_COPY, 0, -1, "tpc", "Third party copy"}, + {VPD_ZBC_DEV_CHARS, 0, -1, "zbdc", "Zoned block device characteristics"}, + /* Use pdt of -1 since this page both for pdt=0 and pdt=0x14 */ {0, 0, 0, NULL, NULL}, }; @@ -223,12 +248,16 @@ pr2serr(const char * fmt, ...) static void usage() { - pr2serr("Usage: sg_vpd [--enumerate] [--help] [--hex] [--ident] " - "[--inhex=fn]\n" - " [--long] [--maxlen=LEN] [--page=PG] [--quiet] " - "[--raw]\n" - " [--vendor=VP] [--verbose] [--version] DEVICE\n"); + pr2serr("Usage: sg_vpd [--all] [--enumerate] [--help] [--hex] " + "[--ident]\n" + " [--inhex=FN] [--long] [--maxlen=LEN] " + "[--page=PG] [--quiet]\n" + " [--raw] [--vendor=VP] [--verbose] [--version] " + "DEVICE\n"); pr2serr(" where:\n" + " --all|-a output all pages listed in the supported " + "pages VPD\n" + " page\n" " --enumerate|-e enumerate known VPD pages names (ignore " "DEVICE),\n" " can be used with --page=num to search\n" @@ -279,6 +308,7 @@ f2hex_arr(const char * fname, int as_binary, int no_space, char line[512]; char carry_over[4]; int off = 0; + struct stat a_stat; if ((NULL == fname) || (NULL == mp_arr) || (NULL == mp_arr_len)) return 1; @@ -308,6 +338,22 @@ f2hex_arr(const char * fname, int as_binary, int no_space, close(fd); return 1; } + if ((0 == fstat(fd, &a_stat)) && S_ISFIFO(a_stat.st_mode)) { + /* pipe; keep reading till error or 0 read */ + while (k < max_arr_len) { + m = read(fd, mp_arr + k, max_arr_len - k); + if (0 == m) + break; + if (m < 0) { + pr2serr("read from binary pipe %s: %s\n", fname, + safe_strerror(errno)); + if (! has_stdin) + close(fd); + return 1; + } + k += m; + } + } *mp_arr_len = k; if (! has_stdin) close(fd); @@ -443,7 +489,7 @@ static int pt_inquiry(int sg_fd, int evpd, int pg_op, void * resp, int mx_resp_len, int * residp, int noisy, int verbose) { - int res, ret, k, sense_cat; + int res, ret, k, sense_cat, resid; unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; unsigned char * up; @@ -478,8 +524,9 @@ pt_inquiry(int sg_fd, int evpd, int pg_op, void * resp, int mx_resp_len, res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, "inquiry", res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); + resid = get_scsi_pt_resid(ptvp); if (residp) - *residp = get_scsi_pt_resid(ptvp); + *residp = resid; destruct_scsi_pt_obj(ptvp); if (-1 == ret) ; @@ -500,6 +547,15 @@ pt_inquiry(int sg_fd, int evpd, int pg_op, void * resp, int mx_resp_len, } else ret = 0; + if (resid > 0) { + if (resid > mx_resp_len) { + pr2serr("INQUIRY resid (%d) should never exceed requested " + "len=%d\n", resid, mx_resp_len); + return ret ? ret : SG_LIB_CAT_MALFORMED; + } + /* zero unfilled section of response buffer */ + memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid); + } return ret; } @@ -512,7 +568,7 @@ vpd_fetch_page_from_dev(int sg_fd, unsigned char * rp, int page, int res, resid, rlen, len, n; if (sg_fd < 0) { - len = ((rp[2] << 8) + rp[3]) + 4; + len = sg_get_unaligned_be16(rp + 2) + 4; if (vb && (len > mxlen)) pr2serr("warning: VPD page's length (%d) > bytes in --inhex=FN " "file (%d)\n", len , mxlen); @@ -546,7 +602,7 @@ vpd_fetch_page_from_dev(int sg_fd, unsigned char * rp, int page, if (mxlen < 0) len = rp[3] + 4; else - len = ((rp[2] << 8) + rp[3]) + 4; + len = sg_get_unaligned_be16(rp + 2) + 4; if (len <= rlen) { if (rlenp) *rlenp = len; @@ -814,7 +870,7 @@ decode_net_man_vpd(unsigned char * buff, int len, int do_hex) printf(" %s, Service type: %s\n", assoc_arr[(ucp[0] >> 5) & 0x3], network_service_type_arr[ucp[0] & 0x1f]); - na_len = (ucp[2] << 8) + ucp[3]; + na_len = sg_get_unaligned_be16(ucp + 2); bump = 4 + na_len; if ((k + bump) > len) { pr2serr("Management network addresses VPD page, short " @@ -896,9 +952,9 @@ decode_scsi_ports_vpd(unsigned char * buff, int len, int do_hex, int do_long, len -= 4; ucp = buff + 4; for (k = 0; k < len; k += bump, ucp += bump) { - rel_port = (ucp[2] << 8) + ucp[3]; - printf("Relative port=%d\n", rel_port); - ip_tid_len = (ucp[6] << 8) + ucp[7]; + rel_port = sg_get_unaligned_be16(ucp + 2); + printf(" Relative port=%d\n", rel_port); + ip_tid_len = sg_get_unaligned_be16(ucp + 6); bump = 8 + ip_tid_len; if ((k + bump) > len) { pr2serr("SCSI Ports VPD page, short descriptor " @@ -907,12 +963,12 @@ decode_scsi_ports_vpd(unsigned char * buff, int len, int do_hex, int do_long, } if (ip_tid_len > 0) { if (do_hex > 1) { - printf(" Initiator port transport id:\n"); + printf(" Initiator port transport id:\n"); dStrHex((const char *)(ucp + 8), ip_tid_len, 1); } else - decode_transport_id(" ", ucp + 8, ip_tid_len); + decode_transport_id(" ", ucp + 8, ip_tid_len); } - tpd_len = (ucp[bump + 2] << 8) + ucp[bump + 3]; + tpd_len = sg_get_unaligned_be16(ucp + bump + 2); if ((k + bump + tpd_len + 4) > len) { pr2serr("SCSI Ports VPD page, short descriptor(tgt) " "length=%d, left=%d\n", bump, (len - k)); @@ -920,11 +976,11 @@ decode_scsi_ports_vpd(unsigned char * buff, int len, int do_hex, int do_long, } if (tpd_len > 0) { if (do_hex > 1) { - printf(" Target port descriptor(s):\n"); + printf(" Target port descriptor(s):\n"); dStrHex((const char *)(ucp + bump + 4), tpd_len, 1); } else { if ((0 == do_quiet) || (ip_tid_len > 0)) - printf(" Target port descriptor(s):\n"); + printf(" Target port descriptor(s):\n"); decode_dev_ids("SCSI Ports", ucp + bump + 4, tpd_len, VPD_ASSOC_TPORT, -1, -1, do_long, do_quiet); } @@ -1016,7 +1072,7 @@ decode_dev_ids_quiet(unsigned char * buff, int len, int m_assoc, if ((8 != i_len) && (12 != i_len) && (16 != i_len)) pr2serr(" << expect 8, 12 and 16 byte " "EUI, got %d>>\n", i_len); - printf("0x"); + printf(" 0x"); for (m = 0; m < i_len; ++m) printf("%02x", (unsigned int)ip[m]); printf("\n"); @@ -1037,7 +1093,7 @@ decode_dev_ids_quiet(unsigned char * buff, int len, int m_assoc, dStrHexErr((const char *)ip, i_len, 0); break; } - printf("0x"); + printf(" 0x"); for (m = 0; m < 8; ++m) printf("%02x", (unsigned int)ip[m]); printf("\n"); @@ -1051,19 +1107,19 @@ decode_dev_ids_quiet(unsigned char * buff, int len, int m_assoc, break; } if ((0 == is_sas) || (1 != assoc)) { - printf("0x"); + printf(" 0x"); for (m = 0; m < 8; ++m) printf("%02x", (unsigned int)ip[m]); printf("\n"); } else if (rtp) { - printf("0x"); + printf(" 0x"); for (m = 0; m < 8; ++m) printf("%02x", (unsigned int)ip[m]); printf(",0x%x\n", rtp); rtp = 0; } else { if (sas_tport_addr[0]) { - printf("0x"); + printf(" 0x"); for (m = 0; m < 8; ++m) printf("%02x", (unsigned int)sas_tport_addr[m]); printf("\n"); @@ -1078,7 +1134,7 @@ decode_dev_ids_quiet(unsigned char * buff, int len, int m_assoc, dStrHexErr((const char *)ip, i_len, 0); break; } - printf("0x"); + printf(" 0x"); for (m = 0; m < 16; ++m) printf("%02x", (unsigned int)ip[m]); printf("\n"); @@ -1093,9 +1149,9 @@ decode_dev_ids_quiet(unsigned char * buff, int len, int m_assoc, case 4: /* Relative target port */ if ((0 == is_sas) || (1 != c_set) || (1 != assoc) || (4 != i_len)) break; - rtp = ((ip[2] << 8) | ip[3]); + rtp = sg_get_unaligned_be16(ip + 2); if (sas_tport_addr[0]) { - printf("0x"); + printf(" 0x"); for (m = 0; m < 8; ++m) printf("%02x", (unsigned int)sas_tport_addr[m]); printf(",0x%x\n", rtp); @@ -1115,11 +1171,18 @@ decode_dev_ids_quiet(unsigned char * buff, int len, int m_assoc, dStrHexErr((const char *)ip, i_len, 0); break; } + if (strncmp((const char *)ip, "eui.", 4) || + strncmp((const char *)ip, "naa.", 4) || + strncmp((const char *)ip, "iqn.", 4)) { + pr2serr(" << expected name string prefix>>\n"); + dStrHexErr((const char *)ip, i_len, -1); + break; + } /* does %s print out UTF-8 ok?? * Seems to depend on the locale. Looks ok here with my * locale setting: en_AU.UTF-8 */ - printf("%s\n", (const char *)ip); + printf(" %s\n", (const char *)ip); break; case 9: /* PCIe routing ID */ break; @@ -1128,7 +1191,7 @@ decode_dev_ids_quiet(unsigned char * buff, int len, int m_assoc, } } if (sas_tport_addr[0]) { - printf("0x"); + printf(" 0x"); for (m = 0; m < 8; ++m) printf("%02x", (unsigned int)sas_tport_addr[m]); printf("\n"); @@ -1162,7 +1225,7 @@ decode_designation_descriptor(const unsigned char * ip, int i_len, switch (desig_type) { case 0: /* vendor specific */ k = 0; - if ((1 == c_set) || (2 == c_set)) { /* ASCII or UTF-8 */ + if ((2 == c_set) || (3 == c_set)) { /* ASCII or UTF-8 */ for (k = 0; (k < i_len) && isprint(ip[k]); ++k) ; if (k >= i_len) @@ -1211,32 +1274,21 @@ decode_designation_descriptor(const unsigned char * ip, int i_len, ci_off = 0; if (16 == i_len) { ci_off = 8; - id_ext = 0; - for (m = 0; m < 8; ++m) { - if (m > 0) - id_ext <<= 8; - id_ext |= ip[m]; - } + id_ext = sg_get_unaligned_be64(ip); printf(" Identifier extension: 0x%" PRIx64 "\n", id_ext); } else if ((8 != i_len) && (12 != i_len)) { pr2serr(" << can only decode 8, 12 and 16 byte ids>>\n"); dStrHexErr((const char *)ip, i_len, 0); break; } - c_id = ((ip[ci_off] << 16) | (ip[ci_off + 1] << 8) | - ip[ci_off + 2]); + c_id = sg_get_unaligned_be24(ip + ci_off); printf(" IEEE Company_id: 0x%x\n", c_id); - vsei = 0; - for (m = 0; m < 5; ++m) { - if (m > 0) - vsei <<= 8; - vsei |= ip[ci_off + 3 + m]; - } + vsei = ((uint64_t)sg_get_unaligned_be32(ip + ci_off + 3) << 8) + + ip[ci_off + 3 + 4]; /* 5 byte integer */ printf(" Vendor Specific Extension Identifier: 0x%" PRIx64 "\n", vsei); if (12 == i_len) { - d_id = (((unsigned int)ip[8] << 24) | (ip[9] << 16) | - (ip[10] << 8) | ip[11]); + d_id = sg_get_unaligned_be32(ip + 8); printf(" Directory ID: 0x%x\n", d_id); } break; @@ -1255,9 +1307,9 @@ decode_designation_descriptor(const unsigned char * ip, int i_len, dStrHexErr((const char *)ip, i_len, 0); break; } - d_id = (((ip[0] & 0xf) << 8) | ip[1]); - c_id = ((ip[2] << 16) | (ip[3] << 8) | ip[4]); - vsi = ((ip[5] << 16) | (ip[6] << 8) | ip[7]); + d_id = sg_get_unaligned_be16(ip) & 0xfff; + c_id = sg_get_unaligned_be24(ip + 2); + vsi = sg_get_unaligned_be24(ip + 5); if (long_out) { printf(" NAA 2, vendor specific identifier A: " "0x%x\n", d_id); @@ -1334,12 +1386,7 @@ decode_designation_descriptor(const unsigned char * ip, int i_len, printf(" NAA 6, IEEE Company_id: 0x%x\n", c_id); printf(" Vendor Specific Identifier: 0x%" PRIx64 "\n", vsei); - vsei = 0; - for (m = 0; m < 8; ++m) { - if (m > 0) - vsei <<= 8; - vsei |= ip[8 + m]; - } + vsei = sg_get_unaligned_be64(ip + 8); printf(" Vendor Specific Identifier Extension: " "0x%" PRIx64 "\n", vsei); printf(" [0x"); @@ -1366,7 +1413,7 @@ decode_designation_descriptor(const unsigned char * ip, int i_len, dStrHexErr((const char *)ip, i_len, 0); break; } - d_id = ((ip[2] << 8) | ip[3]); + d_id = sg_get_unaligned_be16(ip + 2); printf(" Relative target port: 0x%x\n", d_id); break; case 5: /* (primary) Target port group */ @@ -1376,7 +1423,7 @@ decode_designation_descriptor(const unsigned char * ip, int i_len, dStrHexErr((const char *)ip, i_len, 0); break; } - d_id = ((ip[2] << 8) | ip[3]); + d_id = sg_get_unaligned_be16(ip + 2); printf(" Target port group: 0x%x\n", d_id); break; case 6: /* Logical unit group */ @@ -1386,7 +1433,7 @@ decode_designation_descriptor(const unsigned char * ip, int i_len, dStrHexErr((const char *)ip, i_len, 0); break; } - d_id = ((ip[2] << 8) | ip[3]); + d_id = sg_get_unaligned_be16(ip + 2); printf(" Logical unit group: 0x%x\n", d_id); break; case 7: /* MD5 logical unit identifier */ @@ -1405,6 +1452,13 @@ decode_designation_descriptor(const unsigned char * ip, int i_len, dStrHexErr((const char *)ip, i_len, 0); break; } + if (strncmp((const char *)ip, "eui.", 4) || + strncmp((const char *)ip, "naa.", 4) || + strncmp((const char *)ip, "iqn.", 4)) { + pr2serr(" << expected name string prefix>>\n"); + dStrHexErr((const char *)ip, i_len, -1); + break; + } printf(" SCSI name string:\n"); /* does %s print out UTF-8 ok?? * Seems to depend on the locale. Looks ok here with my @@ -1497,7 +1551,7 @@ decode_dev_ids(const char * print_if_found, unsigned char * buff, int len, static void decode_transport_id(const char * leadin, unsigned char * ucp, int len) { - int format_code, proto_id, num, j, k; + int format_code, proto_id, num, k; uint64_t ull; int bump; @@ -1520,12 +1574,12 @@ decode_transport_id(const char * leadin, unsigned char * ucp, int len) break; case TPROTO_SPI: /* Scsi Parallel Interface */ printf("%s Parallel SCSI initiator SCSI address: 0x%x\n", - leadin, ((ucp[2] << 8) | ucp[3])); + leadin, sg_get_unaligned_be16(ucp + 2)); if (0 != format_code) printf("%s [Unexpected format code: %d]\n", leadin, format_code); printf("%s relative port number (of corresponding target): " - "0x%x\n", leadin, ((ucp[6] << 8) | ucp[7])); + "0x%x\n", leadin, sg_get_unaligned_be16(ucp + 6)); bump = 24; break; case TPROTO_SSA: @@ -1552,7 +1606,7 @@ decode_transport_id(const char * leadin, unsigned char * ucp, int len) break; case TPROTO_ISCSI: printf("%s iSCSI ", leadin); - num = ((ucp[2] << 8) | ucp[3]); + num = sg_get_unaligned_be16(ucp + 2); if (0 == format_code) printf("name: %.*s\n", num, &ucp[4]); else if (1 == format_code) @@ -1564,12 +1618,7 @@ decode_transport_id(const char * leadin, unsigned char * ucp, int len) bump = (((num + 4) < 24) ? 24 : num + 4); break; case TPROTO_SAS: - ull = 0; - for (j = 0; j < 8; ++j) { - if (j > 0) - ull <<= 8; - ull |= ucp[4 + j]; - } + ull = sg_get_unaligned_be64(ucp + 4); printf("%s SAS address: 0x%" PRIx64 "\n", leadin, ull); if (0 != format_code) printf("%s [Unexpected format code: %d]\n", leadin, @@ -1596,7 +1645,7 @@ decode_transport_id(const char * leadin, unsigned char * ucp, int len) break; case TPROTO_SOP: printf("%s SOP ", leadin); - num = ((ucp[2] << 8) | ucp[3]); + num = sg_get_unaligned_be16(ucp + 2); if (0 == format_code) printf("Routing ID: 0x%x\n", num); else { @@ -1695,7 +1744,7 @@ decode_x_inq_vpd(unsigned char * b, int len, int do_hex, int do_long, printf(" CBCS=%d\n", !!(b[8] & 0x1)); printf(" Multi I_T nexus microcode download=%d\n", b[9] & 0xf); printf(" Extended self-test completion minutes=%d\n", - (b[10] << 8) + b[11]); + sg_get_unaligned_be16(b + 10)); printf(" POA_SUP=%d\n", !!(b[12] & 0x80)); /* spc4r32 */ printf(" HRA_SUP=%d\n", !!(b[12] & 0x40)); /* spc4r32 */ printf(" VSA_SUP=%d\n", !!(b[12] & 0x20)); /* spc4r32 */ @@ -1715,7 +1764,7 @@ decode_x_inq_vpd(unsigned char * b, int len, int do_hex, int do_long, !!(b[7] & 0x10), !!(b[7] & 0x1), !!(b[8] & 0x10), !!(b[8] & 0x1)); printf(" Multi I_T nexus microcode download=%d\n", b[9] & 0xf); printf(" Extended self-test completion minutes=%d\n", - (b[10] << 8) + b[11]); /* spc4r27 */ + sg_get_unaligned_be16(b + 10)); /* spc4r27 */ printf(" POA_SUP=%d HRA_SUP=%d VSA_SUP=%d\n", /* spc4r32 */ !!(b[12] & 0x80), !!(b[12] & 0x40), !!(b[12] & 0x20)); printf(" Maximum supported sense data length=%d\n", b[13]); /* spc4r34 */ @@ -1733,8 +1782,8 @@ decode_softw_inf_id(unsigned char * buff, int len, int do_hex) buff += 4; for ( ; len > 5; len -= 6, buff += 6) { printf(" IEEE Company_id: 0x%06x, vendor specific extension " - "id: 0x%06x\n", (buff[0] << 16) | (buff[1] << 8) | buff[2], - (buff[3] << 16) | (buff[4] << 8) | buff[5]); + "id: 0x%06x\n", sg_get_unaligned_be24(buff), + sg_get_unaligned_be24(buff + 3)); } } @@ -1745,6 +1794,7 @@ decode_ata_info_vpd(unsigned char * buff, int len, int do_long, int do_hex) char b[80]; int num, is_be; const char * cp; + const char * ata_transp; if (len < 36) { pr2serr("ATA information VPD page length too short=%d\n", len); @@ -1765,10 +1815,12 @@ decode_ata_info_vpd(unsigned char * buff, int len, int do_long, int do_hex) printf(" SAT Product revision level: %s\n", b); if (len < 56) return; + ata_transp = (0x34 == buff[36]) ? "SATA" : "PATA"; if (do_long) { - printf(" Signature (Device to host FIS):\n"); + printf(" Device signature [%s] (in hex):\n", ata_transp); dStrHex((const char *)buff + 36, 20, 0); - } + } else + printf(" Device signature indicates %s transport\n", ata_transp); if (len < 60) return; is_be = sg_is_big_endian(); @@ -1816,17 +1868,17 @@ decode_power_condition(unsigned char * buff, int len, int do_hex) !!(buff[4] & 0x2), !!(buff[4] & 0x1), !!(buff[5] & 0x4), !!(buff[5] & 0x2), !!(buff[5] & 0x1)); printf(" Stopped condition recovery time (ms) %d\n", - (buff[6] << 8) + buff[7]); + sg_get_unaligned_be16(buff + 6)); printf(" Standby_z condition recovery time (ms) %d\n", - (buff[8] << 8) + buff[9]); + sg_get_unaligned_be16(buff + 8)); printf(" Standby_y condition recovery time (ms) %d\n", - (buff[10] << 8) + buff[11]); + sg_get_unaligned_be16(buff + 10)); printf(" Idle_a condition recovery time (ms) %d\n", - (buff[12] << 8) + buff[13]); + sg_get_unaligned_be16(buff + 12)); printf(" Idle_b condition recovery time (ms) %d\n", - (buff[14] << 8) + buff[15]); + sg_get_unaligned_be16(buff + 14)); printf(" Idle_c condition recovery time (ms) %d\n", - (buff[16] << 8) + buff[17]); + sg_get_unaligned_be16(buff + 16)); } static const char * power_unit_arr[] = @@ -1869,7 +1921,7 @@ decode_power_consumption_vpd(unsigned char * buff, int len, int do_hex) if (do_hex > 1) dStrHex((const char *)ucp, 4, 1); else { - value = (ucp[2] << 8) + ucp[3]; + value = sg_get_unaligned_be16(ucp + 2); printf(" Power consumption identifier: 0x%x", ucp[0]); if (value >= 1000 && (ucp[1] & 0x7) > 0) printf(" Maximum power consumption: %d.%03d %s\n", @@ -1888,84 +1940,37 @@ static void decode_rod_descriptor(const unsigned char * buff, int len) { const unsigned char * ucp = buff; - int k, bump, j; - uint64_t ull; + int k, bump; for (k = 0; k < len; k += bump, ucp += bump) { - bump = (ucp[2] << 8) + ucp[3]; + bump = sg_get_unaligned_be16(ucp + 2) + 4; switch (ucp[0]) { case 0: /* Block ROD device type specific descriptor */ - printf(" Optimal block ROD length granularity: %d\n", - (ucp[6] << 8) + ucp[7]); - ull = 0; - for (j = 0; j < 8; j++) { - if (j > 0) - ull <<= 8; - ull |= ucp[8 + j]; - } - printf(" Maximum Bytes in block ROD: %" PRIu64 "\n", ull); - ull = 0; - for (j = 0; j < 8; j++) { - if (j > 0) - ull <<= 8; - ull |= ucp[16 + j]; - } + printf(" Optimal block ROD length granularity: %d\n", + sg_get_unaligned_be16(ucp + 6)); + printf(" Maximum Bytes in block ROD: %" PRIu64 "\n", + sg_get_unaligned_be64(ucp + 8)); printf(" Optimal Bytes in block ROD transfer: %" PRIu64 "\n", - ull); - ull = 0; - for (j = 0; j < 8; j++) { - if (j > 0) - ull <<= 8; - ull |= ucp[24 + j]; - } + sg_get_unaligned_be64(ucp + 16)); printf(" Optimal Bytes to token per segment: %" PRIu64 "\n", - ull); - ull = 0; - for (j = 0; j < 8; j++) { - if (j > 0) - ull <<= 8; - ull |= ucp[32 + j]; - } + sg_get_unaligned_be64(ucp + 24)); printf(" Optimal Bytes from token per segment:" - " %" PRIu64 "\n", ull); + " %" PRIu64 "\n", sg_get_unaligned_be64(ucp + 32)); break; case 1: /* Stream ROD device type specific descriptor */ - ull = 0; - for (j = 0; j < 8; j++) { - if (j > 0) - ull <<= 8; - ull |= ucp[8 + j]; - } - printf(" Maximum Bytes in stream ROD: %" PRIu64 "\n", ull); - ull = 0; - for (j = 0; j < 8; j++) { - if (j > 0) - ull <<= 8; - ull |= ucp[16 + j]; - } + printf(" Maximum Bytes in stream ROD: %" PRIu64 "\n", + sg_get_unaligned_be64(ucp + 8)); printf(" Optimal Bytes in stream ROD transfer:" - " %" PRIu64 "\n", ull); + " %" PRIu64 "\n", sg_get_unaligned_be64(ucp + 16)); break; case 3: /* Copy manager ROD device type specific descriptor */ - ull = 0; - for (j = 0; j < 8; j++) { - if (j > 0) - ull <<= 8; - ull |= ucp[8 + j]; - } - printf(" Maximum Bytes in processor ROD:" - " %" PRIu64 "\n", ull); - ull = 0; - for (j = 0; j < 8; j++) { - if (j > 0) - ull <<= 8; - ull |= ucp[16 + j]; - } + printf(" Maximum Bytes in processor ROD: %" PRIu64 "\n", + sg_get_unaligned_be64(ucp + 8)); printf(" Optimal Bytes in processor ROD transfer:" - " %" PRIu64 "\n", ull); + " %" PRIu64 "\n", sg_get_unaligned_be64(ucp + 16)); break; default: printf(" Unhandled descriptor (format %d, device type %d)\n", @@ -1975,13 +1980,102 @@ decode_rod_descriptor(const unsigned char * buff, int len) } } +struct tpc_desc_type { + unsigned char code; + const char * name; +}; + +static struct tpc_desc_type tpc_desc_arr[] = { + {0x0, "block -> stream"}, + {0x1, "stream -> block"}, + {0x2, "block -> block"}, + {0x3, "stream -> stream"}, + {0x4, "inline -> stream"}, + {0x5, "embedded -> stream"}, + {0x6, "stream -> discard"}, + {0x7, "verify CSCD"}, + {0x8, "block<o> -> stream"}, + {0x9, "stream -> block<o>"}, + {0xa, "block<o> -> block<o>"}, + {0xb, "block -> stream & application_client"}, + {0xc, "stream -> block & application_client"}, + {0xd, "block -> block & application_client"}, + {0xe, "stream -> stream&application_client"}, + {0xf, "stream -> discard&application_client"}, + {0x10, "filemark -> tape"}, + {0x11, "space -> tape"}, + {0x12, "locate -> tape"}, + {0x13, "<i>tape -> <i>tape"}, + {0x14, "register persistent reservation key"}, + {0x15, "third party persistent reservation source I_T nexus"}, + {0x16, "<i>block -> <i>block"}, + {0xbe, "ROD <- block range(n)"}, + {0xbf, "ROD <- block range(1)"}, + {0xe0, "CSCD: FC N_Port_Name"}, + {0xe1, "CSCD: FC N_Port_ID"}, + {0xe2, "CSCD: FC N_Port_ID with N_Port_Name, checking"}, + {0xe3, "CSCD: Parallel interface: I_T"}, + {0xe4, "CSCD: Identification Descriptor"}, + {0xe5, "CSCD: IPv4"}, + {0xe6, "CSCD: Alias"}, + {0xe7, "CSCD: RDMA"}, + {0xe8, "CSCD: IEEE 1394 EUI-64"}, + {0xe9, "CSCD: SAS SSP"}, + {0xea, "CSCD: IPv6"}, + {0xeb, "CSCD: IP copy service"}, + {0xfe, "CSCD: ROD"}, + {0xff, "CSCD: extension"}, + {0x0, NULL}, +}; + +static const char * +get_tpc_desc_name(unsigned char code) +{ + const struct tpc_desc_type * dtp; + + for (dtp = tpc_desc_arr; dtp->name; ++dtp) { + if (code == dtp->code) + return dtp->name; + } + return ""; +} + +struct tpc_rod_type { + uint32_t type; + const char * name; +}; + +static struct tpc_rod_type tpc_rod_arr[] = { + {0x0, "copy manager internal"}, + {0x10000, "access upon reference"}, + {0x800000, "point in time copy - default"}, + {0x800001, "point in time copy - change vulnerable"}, + {0x800002, "point in time copy - persistent"}, + {0x80ffff, "point in time copy - any"}, + {0xffff0001, "block device zero"}, + {0x0, NULL}, +}; + +static const char * +get_tpc_rod_name(uint32_t rod_type) +{ + const struct tpc_rod_type * rtp; + + for (rtp = tpc_rod_arr; rtp->name; ++rtp) { + if (rod_type == rtp->type) + return rtp->name; + } + return ""; +} + /* VPD_3PARTY_COPY [3PC, third party copy] */ static void decode_3party_copy_vpd(unsigned char * buff, int len, int do_hex, int verbose) { - int j, k, bump, desc_type, desc_len, sa_len; + int j, k, m, bump, desc_type, desc_len, sa_len; unsigned int u; const unsigned char * ucp; + const char * cp; uint64_t ull; char b[80]; @@ -1992,8 +2086,8 @@ decode_3party_copy_vpd(unsigned char * buff, int len, int do_hex, int verbose) len -= 4; ucp = buff + 4; for (k = 0; k < len; k += bump, ucp += bump) { - desc_type = (ucp[0] << 8) + ucp[1]; - desc_len = (ucp[2] << 8) + ucp[3]; + desc_type = sg_get_unaligned_be16(ucp); + desc_len = sg_get_unaligned_be16(ucp + 2); if (verbose) printf("Descriptor type=%d, len %d\n", desc_type, desc_len); bump = 4 + desc_len; @@ -2013,26 +2107,14 @@ decode_3party_copy_vpd(unsigned char * buff, int len, int do_hex, int verbose) case 0x0000: /* Required if POPULATE TOKEN (or friend) used */ printf(" Block Device ROD Token Limits:\n"); printf(" Maximum Range Descriptors: %d\n", - (ucp[10] << 8) + ucp[11]); - u = (ucp[12] << 24) | (ucp[13] << 16) | (ucp[14] << 8) | - ucp[15]; + sg_get_unaligned_be16(ucp + 10)); + u = sg_get_unaligned_be32(ucp + 12); printf(" Maximum Inactivity Timeout: %u seconds\n", u); - u = (ucp[16] << 24) | (ucp[17] << 16) | (ucp[18] << 8) | - ucp[19]; + u = sg_get_unaligned_be32(ucp + 16); printf(" Default Inactivity Timeout: %u seconds\n", u); - ull = 0; - for (j = 0; j < 8; j++) { - if (j > 0) - ull <<= 8; - ull |= ucp[20 + j]; - } + ull = sg_get_unaligned_be64(ucp + 20); printf(" Maximum Token Transfer Size: %" PRIu64 "\n", ull); - ull = 0; - for (j = 0; j < 8; j++) { - if (j > 0) - ull <<= 8; - ull |= ucp[28 + j]; - } + ull = sg_get_unaligned_be64(ucp + 28); printf(" Optimal Transfer Count: %" PRIu64 "\n", ull); break; case 0x0001: /* Mandatory (SPC-4) */ @@ -2040,60 +2122,63 @@ decode_3party_copy_vpd(unsigned char * buff, int len, int do_hex, int verbose) j = 0; while (j < ucp[4]) { sa_len = ucp[6 + j]; - for (k = 0; k < sa_len; k++) { - sg_get_opcode_sa_name(ucp[5 + j], ucp[7 + j + k], + for (m = 0; m < sa_len; ++m) { + sg_get_opcode_sa_name(ucp[5 + j], ucp[7 + j + m], 0, sizeof(b), b); - printf(" %s\n", b); + printf(" %s\n", b); } - j += sa_len; + j += sa_len + 2; } break; case 0x0004: printf(" Parameter Data:\n"); printf(" Maximum CSCD Descriptor Count: %d\n", - (ucp[8] << 8) + ucp[9]); + sg_get_unaligned_be16(ucp + 8)); printf(" Maximum Segment Descriptor Count: %d\n", - (ucp[10] << 8) + ucp[11]); - u = (ucp[12] << 24) | (ucp[13] << 16) | (ucp[14] << 8) | - ucp[15]; + sg_get_unaligned_be16(ucp + 10)); + u = sg_get_unaligned_be32(ucp + 12); printf(" Maximum Descriptor List Length: %u\n", u); - u = (ucp[16] << 24) | (ucp[17] << 16) | (ucp[18] << 8) | - ucp[19]; + u = sg_get_unaligned_be32(ucp + 16); printf(" Maximum Inline Data Length: %u\n", u); break; case 0x0008: printf(" Supported Descriptors:\n"); for (j = 0; j < ucp[4]; j++) { - printf(" 0x%x\n", ucp[5 + j]); + cp = get_tpc_desc_name(ucp[5 + j]); + if (strlen(cp) > 0) + printf(" %s [0x%x]\n", cp, ucp[5 + j]); + else + printf(" 0x%x\n", ucp[5 + j]); } break; case 0x000C: printf(" Supported CSCD IDs:\n"); - for (j = 0; j < (ucp[4] << 8) + ucp[5]; j += 2) { - u = (ucp[6 + j] << 8) | ucp[7 + j]; + for (j = 0; j < sg_get_unaligned_be16(ucp + 4); j += 2) { + u = sg_get_unaligned_be16(ucp + 6 + j); printf(" 0x%04x\n", u); } break; case 0x0106: printf(" ROD Token Features:\n"); printf(" Remote Tokens: %d\n", ucp[4] & 0x0f); - u = (ucp[16] << 24) | (ucp[17] << 16) | (ucp[18] << 8) | - ucp[19]; + u = sg_get_unaligned_be32(ucp + 16); printf(" Minimum Token Lifetime: %u seconds\n", u); - u = (ucp[20] << 24) | (ucp[21] << 16) | (ucp[22] << 8) | - ucp[23]; + u = sg_get_unaligned_be32(ucp + 20); printf(" Maximum Token Lifetime: %u seconds\n", u); - u = (ucp[24] << 24) | (ucp[25] << 16) | (ucp[26] << 8) | - ucp[27]; + u = sg_get_unaligned_be32(ucp + 24); printf(" Maximum Token inactivity timeout: %d\n", u); - decode_rod_descriptor(&ucp[48], (ucp[46] << 8) + ucp[47]); + decode_rod_descriptor(ucp + 48, + sg_get_unaligned_be16(ucp + 46)); break; case 0x0108: printf(" Supported ROD Token and ROD Types:\n"); - for (j = 0; j < (ucp[6] << 8) + ucp[7]; j+= 64) { - u = (ucp[8 + j] << 24) | (ucp[8 + j + 1] << 16) | - (ucp[8 + j + 2] << 8) | ucp[8 + j + 3]; - printf(" ROD Type %u:\n", u); + for (j = 0; j < sg_get_unaligned_be16(ucp + 6); j+= 64) { + u = sg_get_unaligned_be32(ucp + 8 + j); + cp = get_tpc_rod_name(u); + if (strlen(cp) > 0) + printf(" ROD Type: %s [0x%x]\n", cp, u); + else + printf(" ROD Type: 0x%x\n", u); printf(" Internal: %s\n", (ucp[8 + j + 4] & 0x80) ? "yes" : "no"); printf(" Token In: %s\n", @@ -2101,35 +2186,30 @@ decode_3party_copy_vpd(unsigned char * buff, int len, int do_hex, int verbose) printf(" Token Out: %s\n", (ucp[8 + j + 4] & 0x01) ? "yes" : "no"); printf(" Preference: %d\n", - (ucp[8 + j + 6] << 8) + ucp[8 + j + 7]); + sg_get_unaligned_be16(ucp + 8 + j + 6)); } break; case 0x8001: /* Mandatory (SPC-4) */ printf(" General Copy Operations:\n"); - u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | - ucp[7]; + u = sg_get_unaligned_be32(ucp + 4); printf(" Total Concurrent Copies: %u\n", u); - u = (ucp[8] << 24) | (ucp[9] << 16) | (ucp[10] << 8) | - ucp[11]; + u = sg_get_unaligned_be32(ucp + 8); printf(" Maximum Identified Concurrent Copies: %u\n", u); - u = (ucp[12] << 24) | (ucp[13] << 16) | (ucp[14] << 8) | - ucp[15]; + u = sg_get_unaligned_be32(ucp + 12); printf(" Maximum Segment Length: %u\n", u); - ull = (1 << ucp[16]); + ull = (1 << ucp[16]); /* field is power of 2 */ printf(" Data Segment Granularity: %" PRIu64 "\n", ull); ull = (1 << ucp[17]); printf(" Inline Data Granularity: %" PRIu64 "\n", ull); break; case 0x9101: printf(" Stream Copy Operations:\n"); - u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | - ucp[7]; + u = sg_get_unaligned_be32(ucp + 4); printf(" Maximum Stream Device Transfer Size: %u\n", u); break; case 0xC001: printf(" Held Data:\n"); - u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | - ucp[7]; + u = sg_get_unaligned_be32(ucp + 4); printf(" Held Data Limit: %u\n", u); ull = (1 << ucp[8]); printf(" Held Data Granularity: %" PRIu64 "\n", ull); @@ -2162,10 +2242,10 @@ decode_proto_lu_vpd(unsigned char * buff, int len, int do_hex) len -= 4; ucp = buff + 4; for (k = 0; k < len; k += bump, ucp += bump) { - rel_port = (ucp[0] << 8) + ucp[1]; - printf("Relative port=%d\n", rel_port); + rel_port = sg_get_unaligned_be16(ucp); + printf(" Relative port=%d\n", rel_port); proto = ucp[2] & 0xf; - desc_len = (ucp[6] << 8) + ucp[7]; + desc_len = sg_get_unaligned_be16(ucp + 6); bump = 8 + desc_len; if ((k + bump) > len) { pr2serr("Protocol-specific logical unit information VPD page, " @@ -2181,8 +2261,8 @@ decode_proto_lu_vpd(unsigned char * buff, int len, int do_hex) else { switch (proto) { case TPROTO_SAS: - printf(" Protocol identifier: SAS\n"); - printf(" TLR control supported: %d\n", !!(ucp[8] & 0x1)); + printf(" Protocol identifier: SAS\n"); + printf(" TLR control supported: %d\n", !!(ucp[8] & 0x1)); break; default: pr2serr("Unexpected proto=%d\n", proto); @@ -2213,10 +2293,10 @@ decode_proto_port_vpd(unsigned char * buff, int len, int do_hex) len -= 4; ucp = buff + 4; for (k = 0; k < len; k += bump, ucp += bump) { - rel_port = (ucp[0] << 8) + ucp[1]; - printf("Relative port=%d\n", rel_port); + rel_port = sg_get_unaligned_be16(ucp); + printf(" Relative port=%d\n", rel_port); proto = ucp[2] & 0xf; - desc_len = (ucp[6] << 8) + ucp[7]; + desc_len = sg_get_unaligned_be16(ucp + 6); bump = 8 + desc_len; if ((k + bump) > len) { pr2serr("Protocol-specific port VPD page, short descriptor " @@ -2231,11 +2311,12 @@ decode_proto_port_vpd(unsigned char * buff, int len, int do_hex) dStrHex((const char *)ucp, bump, 1); else { switch (proto) { - case TPROTO_SAS: /* for SSP, added spl3r2 */ - printf(" pwr_d_s=%d\n", !!(ucp[3] & 0x1)); + case TPROTO_SAS: /* page added in spl3r02 */ + printf(" power disable supported (pwr_d_s)=%d\n", + !!(ucp[3] & 0x1)); /* added spl3r03 */ pidp = ucp + 8; for (j = 0; j < desc_len; j += 4, pidp += 4) - printf(" phy id=%d, ssp persistent capable=%d\n", + printf(" phy id=%d, ssp persistent capable=%d\n", pidp[1], (0x1 & pidp[2])); break; default: @@ -2265,62 +2346,43 @@ decode_b0_vpd(unsigned char * buff, int len, int do_hex, int pdt) pr2serr("Block limits VPD page length too short=%d\n", len); return; } - printf(" Write same no zero (WSNZ): %d\n", !!(buff[4] & 0x1)); + printf(" Write same non-zero (WSNZ): %d\n", !!(buff[4] & 0x1)); printf(" Maximum compare and write length: %u blocks\n", buff[5]); - u = (buff[6] << 8) | buff[7]; + u = sg_get_unaligned_be16(buff + 6); printf(" Optimal transfer length granularity: %u blocks\n", u); - u = (buff[8] << 24) | (buff[9] << 16) | (buff[10] << 8) | - buff[11]; + u = sg_get_unaligned_be32(buff + 8); printf(" Maximum transfer length: %u blocks\n", u); - u = (buff[12] << 24) | (buff[13] << 16) | (buff[14] << 8) | - buff[15]; + u = sg_get_unaligned_be32(buff + 12); printf(" Optimal transfer length: %u blocks\n", u); if (len > 19) { /* added in sbc3r09 */ - u = (buff[16] << 24) | (buff[17] << 16) | (buff[18] << 8) | - buff[19]; + u = sg_get_unaligned_be32(buff + 16); printf(" Maximum prefetch length: %u blocks\n", u); /* was 'Maximum prefetch transfer length' prior to sbc3r33 */ } if (len > 27) { /* added in sbc3r18 */ - u = ((unsigned int)buff[20] << 24) | (buff[21] << 16) | - (buff[22] << 8) | buff[23]; + u = sg_get_unaligned_be32(buff + 20); printf(" Maximum unmap LBA count: %u\n", u); - u = ((unsigned int)buff[24] << 24) | (buff[25] << 16) | - (buff[26] << 8) | buff[27]; + u = sg_get_unaligned_be32(buff + 24); printf(" Maximum unmap block descriptor count: %u\n", u); } if (len > 35) { /* added in sbc3r19 */ - int m; - uint64_t mwsl; - - u = ((unsigned int)buff[28] << 24) | (buff[29] << 16) | - (buff[30] << 8) | buff[31]; + u = sg_get_unaligned_be32(buff + 28); printf(" Optimal unmap granularity: %u\n", u); printf(" Unmap granularity alignment valid: %u\n", !!(buff[32] & 0x80)); - u = ((unsigned int)(buff[32] & 0x7f) << 24) | (buff[33] << 16) | - (buff[34] << 8) | buff[35]; + u = sg_get_unaligned_be32(buff + 32); printf(" Unmap granularity alignment: %u\n", u); /* added in sbc3r26 */ - mwsl = 0; - for (m = 0; m < 8; ++m) { - if (m > 0) - mwsl <<= 8; - mwsl |= buff[36 + m]; - } printf(" Maximum write same length: 0x%" PRIx64 " blocks\n", - mwsl); + sg_get_unaligned_be64(buff + 36)); } if (len > 44) { /* added in sbc4r02 */ - u = ((unsigned int)buff[44] << 24) | (buff[45] << 16) | - (buff[46] << 8) | buff[47]; + u = sg_get_unaligned_be32(buff + 44); printf(" Maximum atomic transfer length: %u\n", u); - u = ((unsigned int)buff[48] << 24) | (buff[49] << 16) | - (buff[50] << 8) | buff[51]; + u = sg_get_unaligned_be32(buff + 48); printf(" Atomic alignment: %u\n", u); - u = ((unsigned int)buff[52] << 24) | (buff[53] << 16) | - (buff[54] << 8) | buff[55]; + u = sg_get_unaligned_be32(buff + 52); printf(" Atomic transfer length granularity: %u\n", u); } break; @@ -2366,7 +2428,7 @@ decode_b1_vpd(unsigned char * buff, int len, int do_hex, int pdt) "short=%d\n", len); return; } - u = (buff[4] << 8) | buff[5]; + u = sg_get_unaligned_be16(buff + 4); if (0 == u) printf(" Medium rotation rate is not reported\n"); else if (1 == u) @@ -2486,8 +2548,7 @@ decode_sup_block_lens_vpd(unsigned char * buff, int len) len -= 4; ucp = buff + 4; for (k = 0; k < len; k += 8, ucp += 8) { - u = ((unsigned int)ucp[0] << 24) | (ucp[1] << 16) | (ucp[2] << 8) | - ucp[3]; + u = sg_get_unaligned_be32(ucp); printf(" Logical block length: %u\n", u); printf(" P_I_I_SUP: %d\n", !!(ucp[4] & 0x40)); printf(" GRD_CHK: %d\n", !!(ucp[4] & 0x4)); @@ -2504,8 +2565,6 @@ decode_sup_block_lens_vpd(unsigned char * buff, int len) static void decode_block_dev_char_ext_vpd(unsigned char * b, int len) { - unsigned int u; - if (len < 16) { pr2serr("Block device characteristics extension VPD page " "length too short=%d\n", len); @@ -2562,10 +2621,8 @@ decode_block_dev_char_ext_vpd(unsigned char * b, int len) break; } printf(" [0x%x]\n", b[7]); - u = ((unsigned int)b[8] << 24) | (b[9] << 16) | (b[10] << 8) | b[11]; - printf(" Utilization B: %u\n", u); - u = ((unsigned int)b[12] << 24) | (b[13] << 16) | (b[14] << 8) | b[15]; - printf(" Utilization A: %u\n", u); + printf(" Utilization B: %u\n", sg_get_unaligned_be32(b + 8)); + printf(" Utilization A: %u\n", sg_get_unaligned_be32(b + 12)); } /* VPD_TA_SUPPORTED */ @@ -2652,10 +2709,9 @@ decode_b3_vpd(unsigned char * b, int len, int do_hex, int pdt) pr2serr("Referrals VPD page length too short=%d\n", len); break; } - u = ((unsigned int)b[8] << 24) | (b[9] << 16) | (b[10] << 8) | b[11]; + u = sg_get_unaligned_be32(b + 8); printf(" User data segment size: %u\n", u); - u = ((unsigned int)b[12] << 24) | (b[13] << 16) | - (b[14] << 8) | b[15]; + u = sg_get_unaligned_be32(b + 12); printf(" User data segment multiplier: %u\n", u); break; case PDT_TAPE: case PDT_MCHANGER: @@ -2720,108 +2776,152 @@ decode_b5_vpd(unsigned char * b, int len, int do_hex, int pdt) } } +/* VPD_ZBC_DEV_CHARS sbc or zbc */ +static void +decode_zbdc_vpd(unsigned char * b, int len, int do_hex) +{ + uint32_t u; + + if (do_hex) { + dStrHex((const char *)b, len, (1 == do_hex) ? 0 : -1); + return; + } + if (len < 64) { + pr2serr("Zoned block device characteristics VPD page length too " + "short=%d\n", len); + return; + } + printf(" URSWRZ type: %d\n", !!(b[4] & 0x1)); + u = sg_get_unaligned_be32(b + 8); + printf(" Optimal number of open sequential write preferred zones: "); + if (0xffffffff == u) + printf("0xffffffff\n"); + else + printf("%" PRIu32 "\n", u); + u = sg_get_unaligned_be32(b + 12); + printf(" Optimal number of non-sequentially written sequential write " + "preferred zones: "); + if (0xffffffff == u) + printf("0xffffffff\n"); + else + printf("%" PRIu32 "\n", u); + u = sg_get_unaligned_be32(b + 16); + printf(" Maximum number of open sequential write required: "); + if (0xffffffff == u) + printf("0xffffffff\n"); + else + printf("%" PRIu32 "\n", u); +} + /* Returns 0 if successful */ static int -svpd_unable_to_decode(int sg_fd, int num_vpd, int subvalue, int maxlen, - int do_hex, int do_raw, int do_long, int do_quiet, - int verbose) +svpd_unable_to_decode(int sg_fd, struct opts_t * op, int subvalue, int off) { int len, res; - int alloc_len = maxlen; + int alloc_len = op->maxlen; + unsigned char * rp; - if (do_quiet) { ; } /* unused, dummy to suppress warning */ - if ((! do_hex) && (! do_raw)) + rp = rsp_buff + off; + if ((! op->do_hex) && (! op->do_raw)) printf("Only hex output supported\n"); - if ((!do_raw) && (do_hex < 2)) { + if ((!op->do_raw) && (op->do_hex < 2)) { if (subvalue) - printf("VPD page code=0x%.2x, subvalue=0x%.2x:\n", num_vpd, + printf("VPD page code=0x%.2x, subvalue=0x%.2x:\n", op->num_vpd, subvalue); - else if (num_vpd >= 0) - printf("VPD page code=0x%.2x:\n", num_vpd); + else if (op->num_vpd >= 0) + printf("VPD page code=0x%.2x:\n", op->num_vpd); else - printf("VPD page code=%d:\n", num_vpd); + printf("VPD page code=%d:\n", op->num_vpd); } if (sg_fd >= 0) { if (0 == alloc_len) alloc_len = DEF_ALLOC_LEN; } - res = vpd_fetch_page_from_dev(sg_fd, rsp_buff, num_vpd, alloc_len, - verbose, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, op->num_vpd, alloc_len, + op->verbose, &len); if (0 == res) { - if (do_raw) - dStrRaw((const char *)rsp_buff, len); + if (op->do_raw) + dStrRaw((const char *)rp, len); else { - if (do_hex > 1) - dStrHex((const char *)rsp_buff, len, -1); - else if (VPD_ASCII_OP_DEF == num_vpd) - dStrHex((const char *)rsp_buff, len, 0); + if (op->do_hex > 1) + dStrHex((const char *)rp, len, -1); + else if (VPD_ASCII_OP_DEF == op->num_vpd) + dStrHex((const char *)rp, len, 0); else - dStrHex((const char *)rsp_buff, len, (do_long ? 0 : 1)); + dStrHex((const char *)rp, len, (op->do_long ? 0 : 1)); } return 0; } else { - if (num_vpd >= 0) - pr2serr("fetching VPD page code=0x%.2x: failed\n", num_vpd); + if (op->num_vpd >= 0) + pr2serr("fetching VPD page code=0x%.2x: failed\n", op->num_vpd); else - pr2serr("fetching VPD page code=%d: failed\n", num_vpd); + pr2serr("fetching VPD page code=%d: failed\n", op->num_vpd); return res; } } /* Returns 0 if successful, else see sg_ll_inquiry() */ static int -svpd_decode_t10(int sg_fd, int pn, int subvalue, int vp_num, int maxlen, - int do_hex, int do_raw, int do_long, int do_quiet, int vb) +svpd_decode_t10(int sg_fd, struct opts_t * op, int subvalue, int off) { - int len, pdt, num, k, resid, alloc_len; + int len, pdt, num, k, resid, alloc_len, pn, vb, allow_name, long_notquiet; int res = 0; char b[48]; const struct svpd_values_name_t * vnp; char obuff[DEF_ALLOC_LEN]; unsigned char * rp; - rp = rsp_buff; + pn = op->num_vpd; + vb = op->verbose; + long_notquiet = op->do_long && (! op->do_quiet); + if (op->do_raw || (op->do_quiet && (! op->do_long) && (! op->do_all)) || + (op->do_hex >= 3)) + allow_name = 0; + else + allow_name = 1; + rp = rsp_buff + off; switch(pn) { case VPD_NO_RATHER_STD_INQ: /* -2 (want standard inquiry response) */ if (sg_fd >= 0) { - if (maxlen > 0) - alloc_len = maxlen; - else if (do_long) + if (op->maxlen > 0) + alloc_len = op->maxlen; + else if (op->do_long) alloc_len = DEF_ALLOC_LEN; else alloc_len = 36; res = pt_inquiry(sg_fd, 0, 0, rp, alloc_len, &resid, 1, vb); } else { - alloc_len = maxlen; + alloc_len = op->maxlen; resid = 0; res = 0; } if (0 == res) { alloc_len -= resid; - if (do_raw) + if (op->do_raw) dStrRaw((const char *)rp, alloc_len); - else if (do_hex) { - if (! do_quiet && (do_hex < 3)) + else if (op->do_hex) { + if (! op->do_quiet && (op->do_hex < 3)) printf("Standard Inquiry reponse:\n"); - dStrHex((const char *)rp, alloc_len, (1 == do_hex) ? 0 : -1); + dStrHex((const char *)rp, alloc_len, + (1 == op->do_hex) ? 0 : -1); } else decode_std_inq(rp, alloc_len, vb); return 0; } break; case VPD_SUPPORTED_VPDS: /* 0x0 */ - if ((! do_raw) && (! do_quiet) && (do_hex < 3)) + if (allow_name) printf("Supported VPD pages VPD page:\n"); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { - if (do_raw) + if (op->do_raw) dStrRaw((const char *)rp, len); - else if (do_hex) - dStrHex((const char *)rp, len, (1 == do_hex) ? 0 : -1); + else if (op->do_hex) + dStrHex((const char *)rp, len, (1 == op->do_hex) ? 0 : -1); else { pdt = rp[0] & 0x1f; - if (vb || do_long) + if (vb || long_notquiet) printf(" [PQual=%d Peripheral device type: %s]\n", (rp[0] & 0xe0) >> 5, sg_get_pdt_str(pdt, sizeof(b), b)); @@ -2832,15 +2932,15 @@ svpd_decode_t10(int sg_fd, int pn, int subvalue, int vp_num, int maxlen, pn = rp[4 + k]; vnp = sdp_get_vpd_detail(pn, -1, pdt); if (vnp) { - if (do_long) + if (op->do_long) printf(" 0x%02x %s [%s]\n", pn, vnp->name, vnp->acron); else printf(" %s [%s]\n", vnp->name, vnp->acron); - } else if (vp_num >= 0) { - vnp = svpd_find_vendor_by_num(pn, vp_num); + } else if (op->vend_prod_num >= 0) { + vnp = svpd_find_vendor_by_num(pn, op->vend_prod_num); if (vnp) { - if (do_long) + if (op->do_long) printf(" 0x%02x %s [%s]\n", pn, vnp->name, vnp->acron); else @@ -2855,17 +2955,17 @@ svpd_decode_t10(int sg_fd, int pn, int subvalue, int vp_num, int maxlen, } break; case VPD_UNIT_SERIAL_NUM: /* 0x80 */ - if ((! do_raw) && (! do_quiet) && (do_hex < 3)) + if (allow_name) printf("Unit serial number VPD page:\n"); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { - if (do_raw) + if (op->do_raw) dStrRaw((const char *)rp, len); - else if (do_hex) - dStrHex((const char *)rp, len, (1 == do_hex) ? 0 : -1); + else if (op->do_hex) + dStrHex((const char *)rp, len, (1 == op->do_hex) ? 0 : -1); else { pdt = rp[0] & 0x1f; - if (vb || do_long) + if (vb || long_notquiet) printf(" [PQual=%d Peripheral device type: %s]\n", (rp[0] & 0xe0) >> 5, sg_get_pdt_str(pdt, sizeof(b), b)); @@ -2880,238 +2980,246 @@ svpd_decode_t10(int sg_fd, int pn, int subvalue, int vp_num, int maxlen, } break; case VPD_DEVICE_ID: /* 0x83 */ - if ((! do_raw) && (! do_quiet) && (do_hex < 3)) + if (allow_name) printf("Device Identification VPD page:\n"); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { - if (do_raw) + if (op->do_raw) dStrRaw((const char *)rp, len); - else if (do_hex) - dStrHex((const char *)rp, len, (1 == do_hex) ? 0 : -1); + else if (op->do_hex) + dStrHex((const char *)rp, len, (1 == op->do_hex) ? 0 : -1); else { pdt = rp[0] & 0x1f; - if (vb || do_long) + if (vb || long_notquiet) printf(" [PQual=%d Peripheral device type: %s]\n", (rp[0] & 0xe0) >> 5, sg_get_pdt_str(pdt, sizeof(b), b)); - decode_id_vpd(rp, len, subvalue, do_long, do_quiet); + decode_id_vpd(rp, len, subvalue, op->do_long, op->do_quiet); } return 0; } break; case VPD_SOFTW_INF_ID: /* 0x84 */ - if ((! do_raw) && (! do_quiet) && (do_hex < 3)) + if (allow_name) printf("Software interface identification VPD page:\n"); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { - if (do_raw) + if (op->do_raw) dStrRaw((const char *)rp, len); else { pdt = rp[0] & 0x1f; - if (vb || do_long) + if (vb || long_notquiet) printf(" [PQual=%d Peripheral device type: %s]\n", (rp[0] & 0xe0) >> 5, sg_get_pdt_str(pdt, sizeof(b), b)); - decode_softw_inf_id(rp, len, do_hex); + decode_softw_inf_id(rp, len, op->do_hex); } return 0; } break; case VPD_MAN_NET_ADDR: /* 0x85 */ - if ((! do_raw) && (! do_quiet) && (do_hex < 3)) + if (allow_name) printf("Management network addresses VPD page:\n"); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { - if (do_raw) + if (op->do_raw) dStrRaw((const char *)rp, len); else - decode_net_man_vpd(rp, len, do_hex); + decode_net_man_vpd(rp, len, op->do_hex); return 0; } break; case VPD_EXT_INQ: /* 0x86 */ - if ((! do_raw) && (! do_quiet) && (do_hex < 3)) + if (allow_name) printf("extended INQUIRY data VPD page:\n"); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { - if (do_raw) + if (op->do_raw) dStrRaw((const char *)rp, len); else { int protect = 0; struct sg_simple_inquiry_resp sir; - if (do_long) { + if ((sg_fd >= 0) && long_notquiet) { res = sg_simple_inquiry(sg_fd, &sir, 0, vb); - if (res) - break; - protect = sir.byte_5 & 0x1; /* SPC-3 and later */ + if (res) { + if (op->verbose) + pr2serr("%s: sg_simple_inquiry() failed, " + "res=%d\n", __func__, res); + } else + protect = sir.byte_5 & 0x1; /* SPC-3 and later */ } pdt = rp[0] & 0x1f; - if (vb) + if (vb || long_notquiet) printf(" [PQual=%d Peripheral device type: %s]\n", (rp[0] & 0xe0) >> 5, sg_get_pdt_str(pdt, sizeof(b), b)); - decode_x_inq_vpd(rp, len, do_hex, do_long, protect); + decode_x_inq_vpd(rp, len, op->do_hex, long_notquiet, protect); } return 0; } break; case VPD_MODE_PG_POLICY: /* 0x87 */ - if ((! do_raw) && (! do_quiet) && (do_hex < 3)) + if (allow_name) printf("Mode page policy VPD page:\n"); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { - if (do_raw) + if (op->do_raw) dStrRaw((const char *)rp, len); else { pdt = rp[0] & 0x1f; - if (vb || do_long) + if (vb || long_notquiet) printf(" [PQual=%d Peripheral device type: %s]\n", (rp[0] & 0xe0) >> 5, sg_get_pdt_str(pdt, sizeof(b), b)); - decode_mode_policy_vpd(rp, len, do_hex); + decode_mode_policy_vpd(rp, len, op->do_hex); } return 0; } break; case VPD_SCSI_PORTS: /* 0x88 */ - if ((! do_raw) && (! do_quiet) && (do_hex < 3)) + if (allow_name) printf("SCSI Ports VPD page:\n"); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { - if (do_raw) + if (op->do_raw) dStrRaw((const char *)rp, len); else { pdt = rp[0] & 0x1f; - if (vb || do_long) + if (vb || long_notquiet) printf(" [PQual=%d Peripheral device type: %s]\n", (rp[0] & 0xe0) >> 5, sg_get_pdt_str(pdt, sizeof(b), b)); - decode_scsi_ports_vpd(rp, len, do_hex, do_long, do_quiet); + decode_scsi_ports_vpd(rp, len, op->do_hex, op->do_long, + op->do_quiet); } return 0; } break; case VPD_ATA_INFO: /* 0x89 */ - if ((! do_raw) && (do_hex < 3) && (! do_quiet)) + if (allow_name) printf("ATA information VPD page:\n"); - alloc_len = maxlen ? maxlen : VPD_ATA_INFO_LEN; + alloc_len = op->maxlen ? op->maxlen : VPD_ATA_INFO_LEN; res = vpd_fetch_page_from_dev(sg_fd, rp, pn, alloc_len, vb, &len); if (0 == res) { - if ((2 == do_raw) || (3 == do_hex)) /* special for hdparm */ -// xxxxxxxxxx check len is long enough - dWordHex((const unsigned short *)(rp + 60), - 256, -2, sg_is_big_endian()); - else if (do_raw) + if ((2 == op->do_raw) || (3 == op->do_hex)) { /* for hdparm */ + if (len < (60 + 512)) + pr2serr("ATA_INFO VPD page len (%d) less than expected " + "572\n", len); + else + dWordHex((const unsigned short *)(rp + 60), 256, -2, + sg_is_big_endian()); + } + else if (op->do_raw) dStrRaw((const char *)rp, len); else { pdt = rp[0] & 0x1f; - if (vb || do_long) + if (vb || long_notquiet) printf(" [PQual=%d Peripheral device type: %s]\n", (rp[0] & 0xe0) >> 5, sg_get_pdt_str(pdt, sizeof(b), b)); - decode_ata_info_vpd(rp, len, do_long, do_hex); + decode_ata_info_vpd(rp, len, long_notquiet, op->do_hex); } return 0; } break; case VPD_POWER_CONDITION: /* 0x8a */ - if ((! do_raw) && (! do_quiet) && (do_hex < 3)) + if (allow_name) printf("Power condition VPD page:\n"); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { - if (do_raw) + if (op->do_raw) dStrRaw((const char *)rp, len); else { pdt = rp[0] & 0x1f; - if (vb || do_long) + if (vb || long_notquiet) printf(" [PQual=%d Peripheral device type: %s]\n", (rp[0] & 0xe0) >> 5, sg_get_pdt_str(pdt, sizeof(b), b)); - decode_power_condition(rp, len, do_hex); + decode_power_condition(rp, len, op->do_hex); } return 0; } break; case VPD_POWER_CONSUMPTION: /* 0x8d */ - if ((! do_raw) && (! do_quiet) && (do_hex < 3)) + if (allow_name) printf("Power consumption VPD page:\n"); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { - if (do_raw) + if (op->do_raw) dStrRaw((const char *)rp, len); else { pdt = rp[0] & 0x1f; - if (vb || do_long) + if (vb || long_notquiet) printf(" [PQual=%d Peripheral device type: %s]\n", (rp[0] & 0xe0) >> 5, sg_get_pdt_str(pdt, sizeof(b), b)); - decode_power_consumption_vpd(rp, len, do_hex); + decode_power_consumption_vpd(rp, len, op->do_hex); } return 0; } break; case VPD_3PARTY_COPY: /* 0x8f */ - if ((! do_raw) && (! do_quiet) && (do_hex < 3)) + if (allow_name) printf("Third party copy VPD page:\n"); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { - if (do_raw) + if (op->do_raw) dStrRaw((const char *)rp, len); - else if (1 == do_hex) + else if (1 == op->do_hex) dStrHex((const char *)rp, len, 0); else { pdt = rp[0] & 0x1f; - if (vb || do_long) + if (vb || long_notquiet) printf(" [PQual=%d Peripheral device type: %s]\n", (rp[0] & 0xe0) >> 5, sg_get_pdt_str(pdt, sizeof(b), b)); - decode_3party_copy_vpd(rp, len, do_hex, vb); + decode_3party_copy_vpd(rp, len, op->do_hex, vb); } return 0; } break; case VPD_PROTO_LU: /* 0x90 */ - if ((! do_raw) && (! do_quiet) && (do_hex < 3)) + if (allow_name) printf("Protocol-specific logical unit information:\n"); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { - if (do_raw) + if (op->do_raw) dStrRaw((const char *)rp, len); else { pdt = rsp_buff[0] & 0x1f; - if (vb || do_long) + if (vb || long_notquiet) printf(" [PQual=%d Peripheral device type: %s]\n", (rp[0] & 0xe0) >> 5, sg_get_pdt_str(pdt, sizeof(b), b)); - decode_proto_lu_vpd(rp, len, do_hex); + decode_proto_lu_vpd(rp, len, op->do_hex); } return 0; } break; case VPD_PROTO_PORT: /* 0x91 */ - if ((! do_raw) && (! do_quiet) && (do_hex < 3)) + if (allow_name) printf("Protocol-specific port information:\n"); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { - if (do_raw) + if (op->do_raw) dStrRaw((const char *)rp, len); else { pdt = rp[0] & 0x1f; - if (vb || do_long) + if (vb || long_notquiet) printf(" [PQual=%d Peripheral device type: %s]\n", (rp[0] & 0xe0) >> 5, sg_get_pdt_str(pdt, sizeof(b), b)); - decode_proto_port_vpd(rp, len, do_hex); + decode_proto_port_vpd(rp, len, op->do_hex); } return 0; } break; case 0xb0: /* depends on pdt */ - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { pdt = rp[0] & 0x1f; - if ((! do_raw) && (! do_quiet) && (do_hex < 3)) { + if (allow_name) { switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: printf("Block limits VPD page (SBC):\n"); @@ -3128,25 +3236,25 @@ svpd_decode_t10(int sg_fd, int pn, int subvalue, int vp_num, int maxlen, break; } } - if (do_raw) + if (op->do_raw) dStrRaw((const char *)rp, len); else { pdt = rp[0] & 0x1f; - if (vb || do_long) + if (vb || long_notquiet) printf(" [PQual=%d Peripheral device type: %s]\n", (rp[0] & 0xe0) >> 5, sg_get_pdt_str(pdt, sizeof(b), b)); - decode_b0_vpd(rp, len, do_hex, pdt); + decode_b0_vpd(rp, len, op->do_hex, pdt); } return 0; - } else if ((! do_raw) && (! do_quiet) && (do_hex < 3)) + } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3)) printf("VPD page=0xb0\n"); break; case 0xb1: /* depends on pdt */ - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { pdt = rp[0] & 0x1f; - if ((! do_raw) && (! do_quiet) && (do_hex < 3)) { + if (allow_name) { switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: printf("Block device characteristics VPD page (SBC):\n"); @@ -3167,24 +3275,24 @@ svpd_decode_t10(int sg_fd, int pn, int subvalue, int vp_num, int maxlen, break; } } - if (do_raw) + if (op->do_raw) dStrRaw((const char *)rp, len); else { - if (vb || do_long) + if (vb || long_notquiet) printf(" [PQual=%d Peripheral device type: %s]\n", (rp[0] & 0xe0) >> 5, sg_get_pdt_str(pdt, sizeof(b), b)); - decode_b1_vpd(rp, len, do_hex, pdt); + decode_b1_vpd(rp, len, op->do_hex, pdt); } return 0; - } else if ((! do_raw) && (! do_quiet) && (do_hex < 3)) + } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3)) printf("VPD page=0xb1\n"); break; case 0xb2: /* VPD page depends on pdt */ - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { pdt = rp[0] & 0x1f; - if ((! do_raw) && (! do_quiet) && (do_hex < 3)) { + if (allow_name) { switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: printf("Logical block provisioning VPD page (SBC):\n"); @@ -3197,24 +3305,24 @@ svpd_decode_t10(int sg_fd, int pn, int subvalue, int vp_num, int maxlen, break; } } - if (do_raw) + if (op->do_raw) dStrRaw((const char *)rp, len); else { - if (vb || do_long) + if (vb || long_notquiet) printf(" [PQual=%d Peripheral device type: %s]\n", (rp[0] & 0xe0) >> 5, sg_get_pdt_str(pdt, sizeof(b), b)); - decode_b2_vpd(rp, len, do_hex, pdt); + decode_b2_vpd(rp, len, op->do_hex, pdt); } return 0; - } else if ((! do_raw) && (! do_quiet) && (do_hex < 3)) + } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3)) printf("VPD page=0xb2\n"); break; case 0xb3: /* VPD page depends on pdt */ - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { pdt = rp[0] & 0x1f; - if ((! do_raw) && (! do_quiet) && (do_hex < 3)) { + if (allow_name) { switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: printf("Referrals VPD page (SBC):\n"); @@ -3228,24 +3336,24 @@ svpd_decode_t10(int sg_fd, int pn, int subvalue, int vp_num, int maxlen, break; } } - if (do_raw) + if (op->do_raw) dStrRaw((const char *)rp, len); else { - if (vb || do_long) + if (vb || long_notquiet) printf(" [PQual=%d Peripheral device type: %s]\n", (rp[0] & 0xe0) >> 5, sg_get_pdt_str(pdt, sizeof(b), b)); - decode_b3_vpd(rp, len, do_hex, pdt); + decode_b3_vpd(rp, len, op->do_hex, pdt); } return 0; - } else if ((! do_raw) && (! do_quiet) && (do_hex < 3)) + } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3)) printf("VPD page=0xb3\n"); break; case 0xb4: /* VPD page depends on pdt */ - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { pdt = rp[0] & 0x1f; - if ((! do_raw) && (! do_quiet) && (do_hex < 3)) { + if (allow_name) { switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: printf("Supported block lengths and protection types " @@ -3259,24 +3367,24 @@ svpd_decode_t10(int sg_fd, int pn, int subvalue, int vp_num, int maxlen, break; } } - if (do_raw) + if (op->do_raw) dStrRaw((const char *)rp, len); else { - if (vb || do_long) + if (vb || long_notquiet) printf(" [PQual=%d Peripheral device type: %s]\n", (rp[0] & 0xe0) >> 5, sg_get_pdt_str(pdt, sizeof(b), b)); - decode_b4_vpd(rp, len, do_hex, pdt); + decode_b4_vpd(rp, len, op->do_hex, pdt); } return 0; - } else if ((! do_raw) && (! do_quiet) && (do_hex < 3)) + } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3)) printf("VPD page=0xb4\n"); break; case 0xb5: /* VPD page depends on pdt */ - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { pdt = rp[0] & 0x1f; - if ((! do_raw) && (! do_quiet) && (do_hex < 3)) { + if (allow_name) { switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: printf("Block device characteristics extension VPD page " @@ -3287,17 +3395,45 @@ svpd_decode_t10(int sg_fd, int pn, int subvalue, int vp_num, int maxlen, break; } } - if (do_raw) + if (op->do_raw) + dStrRaw((const char *)rp, len); + else { + if (vb || long_notquiet) + printf(" [PQual=%d Peripheral device type: %s]\n", + (rp[0] & 0xe0) >> 5, + sg_get_pdt_str(pdt, sizeof(b), b)); + decode_b5_vpd(rp, len, op->do_hex, pdt); + } + return 0; + } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3)) + printf("VPD page=0xb5\n"); + break; + case VPD_ZBC_DEV_CHARS: /* 0xb6 for both pdt=0 and pdt=0x14 */ + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); + if (0 == res) { + pdt = rp[0] & 0x1f; + if (allow_name) { + switch (pdt) { + case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: + printf("Zoned block device characteristics VPD page " + "(SBC, ZBC):\n"); + break; + default: + printf("VPD page=0x%x, pdt=0x%x:\n", pn, pdt); + break; + } + } + if (op->do_raw) dStrRaw((const char *)rp, len); else { - if (vb || do_long) + if (vb || long_notquiet) printf(" [PQual=%d Peripheral device type: %s]\n", (rp[0] & 0xe0) >> 5, sg_get_pdt_str(pdt, sizeof(b), b)); - decode_b5_vpd(rp, len, do_hex, pdt); + decode_zbdc_vpd(rp, len, op->do_hex); } return 0; - } else if ((! do_raw) && (! do_quiet) && (do_hex < 3)) + } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3)) printf("VPD page=0xb5\n"); break; default: @@ -3306,97 +3442,191 @@ svpd_decode_t10(int sg_fd, int pn, int subvalue, int vp_num, int maxlen, return res; } +static int +svpd_decode_all(int sg_fd, struct opts_t * op) +{ + int k, res, rlen, n, pn; + int max_pn = 255; + int any_err = 0; + unsigned char vpd0_buff[512]; + unsigned char * rp = vpd0_buff; + + if (op->num_vpd > 0) + max_pn = op->num_vpd; + if (sg_fd >= 0) { + res = vpd_fetch_page_from_dev(sg_fd, rp, VPD_SUPPORTED_VPDS, + op->maxlen, op->verbose, &rlen); + if (res) { + if (SG_LIB_CAT_ABORTED_COMMAND == res) + pr2serr("%s: VPD page 0, aborted command\n", __func__); + else if (res) { + char b[80]; + + sg_get_category_sense_str(res, sizeof(b), b, op->verbose); + pr2serr("%s: fetching VPD page 0 failed: %s\n", __func__, b); + } + return res; + } + n = (rp[2] >> 8) + rp[3]; + if (n > (rlen - 4)) { + if (op->verbose) + pr2serr("%s: rlen=%d > page0 size=%d\n", __func__, rlen, + n + 4); + n = (rlen - 4); + } + for (k = 0; k < n; ++k) { + pn = rp[4 + k]; + if (pn > max_pn) + continue; + op->num_vpd = pn; + if (op->do_long) + printf("[0x%x] ", pn); + + res = svpd_decode_t10(sg_fd, op, 0, 0); + if (SG_LIB_SYNTAX_ERROR == res) { + res = svpd_decode_vendor(sg_fd, op, 0); + if (SG_LIB_SYNTAX_ERROR == res) + res = svpd_unable_to_decode(sg_fd, op, 0, 0); + } + if (SG_LIB_CAT_ABORTED_COMMAND == res) + pr2serr("fetching VPD page failed, aborted command\n"); + else if (res) { + char b[80]; + + sg_get_category_sense_str(res, sizeof(b), b, op->verbose); + pr2serr("fetching VPD page failed: %s\n", b); + } + if (res) + any_err = res; + } + res = any_err; + } else { /* input is coming from --inhex=FN */ + int bump, off; + int in_len = op->maxlen; + int prev_pn = -1; + + rp = rsp_buff; + for (k = 0, off = 0; off < in_len; ++k, off += bump) { + rp = rsp_buff + off; + pn = rp[1]; + bump = sg_get_unaligned_be16(rp + 2) + 4; + if ((off + bump) > in_len) { + pr2serr("%s: page 0x%x size (%d) exceeds buffer\n", __func__, + pn, bump); + bump = in_len - off; + } + if (pn <= prev_pn) { + pr2serr("%s: prev_pn=0x%x, this pn=0x%x, not ascending so " + "exit\n", __func__, prev_pn, pn); + break; + } + prev_pn = pn; + op->num_vpd = pn; + if (pn > max_pn) { + if (op->verbose > 2) + pr2serr("%s: skipping as this pn=0x%x exceeds " + "max_pn=0x%x\n", __func__, pn, max_pn); + continue; + } + if (op->do_long) + printf("[0x%x] ", pn); + + res = svpd_decode_t10(-1, op, 0, off); + if (SG_LIB_SYNTAX_ERROR == res) { + res = svpd_decode_vendor(-1, op, off); + if (SG_LIB_SYNTAX_ERROR == res) + res = svpd_unable_to_decode(-1, op, 0, off); + } + } + } + return 0; +} + int main(int argc, char * argv[]) { int sg_fd, c, res, matches; - const char * device_name = NULL; const struct svpd_values_name_t * vnp; - const char * page_str = NULL; - const char * inhex_fn = NULL; - const char * vp_str = NULL; const char * cp; - int num_vpd = 0; - int do_enum = 0; - int do_hex = 0; int inhex_len = 0; - int do_ident = 0; - int do_long = 0; - int maxlen = 0; - int do_quiet = 0; - int do_raw = 0; - int do_verbose = 0; int ret = 0; int subvalue = 0; int page_pdt = -1; - int vp_num = -1; + struct opts_t opts; + struct opts_t * op; + op = &opts; + memset(&opts, 0, sizeof(opts)); + op->vend_prod_num = -1; while (1) { int option_index = 0; - c = getopt_long(argc, argv, "ehHiI:lm:M:p:qrvV", long_options, + c = getopt_long(argc, argv, "aehHiI:lm:M:p:qrvV", long_options, &option_index); if (c == -1) break; switch (c) { + case 'a': + ++op->do_all; + break; case 'e': - ++do_enum; + ++op->do_enum; break; case 'h': case '?': usage(); return 0; case 'H': - ++do_hex; + ++op->do_hex; break; case 'i': - ++do_ident; + ++op->do_ident; break; case 'I': - if (inhex_fn) { + if (op->inhex_fn) { pr2serr("only one '--inhex=' option permitted\n"); usage(); return SG_LIB_SYNTAX_ERROR; } else - inhex_fn = optarg; + op->inhex_fn = optarg; break; case 'l': - ++do_long; + ++op->do_long; break; case 'm': - maxlen = sg_get_num(optarg); - if ((maxlen < 0) || (maxlen > MX_ALLOC_LEN)) { + op->maxlen = sg_get_num(optarg); + if ((op->maxlen < 0) || (op->maxlen > MX_ALLOC_LEN)) { pr2serr("argument to '--maxlen' should be %d or less\n", MX_ALLOC_LEN); return SG_LIB_SYNTAX_ERROR; } break; case 'M': - if (vp_str) { + if (op->vend_prod) { pr2serr("only one '--vendor=' option permitted\n"); usage(); return SG_LIB_SYNTAX_ERROR; } else - vp_str = optarg; + op->vend_prod = optarg; break; case 'p': - if (page_str) { + if (op->page_str) { pr2serr("only one '--page=' option permitted\n"); usage(); return SG_LIB_SYNTAX_ERROR; } else - page_str = optarg; + op->page_str = optarg; break; case 'q': - ++do_quiet; + ++op->do_quiet; break; case 'r': - ++do_raw; + ++op->do_raw; break; case 'v': - ++do_verbose; + ++op->verbose; break; case 'V': pr2serr("version: %s\n", version_str); @@ -3408,8 +3638,8 @@ main(int argc, char * argv[]) } } if (optind < argc) { - if (NULL == device_name) { - device_name = argv[optind]; + if (NULL == op->device_name) { + op->device_name = argv[optind]; ++optind; } if (optind < argc) { @@ -3419,36 +3649,36 @@ main(int argc, char * argv[]) return SG_LIB_SYNTAX_ERROR; } } - if (do_enum) { - if (device_name) + if (op->do_enum) { + if (op->device_name) pr2serr("Device name %s ignored when --enumerate given\n", - device_name); - if (vp_str) { - if (isdigit(vp_str[0])) { - vp_num = sg_get_num_nomult(vp_str); - if ((vp_num < 0) || (vp_num > 10)) { + op->device_name); + if (op->vend_prod) { + if (isdigit(op->vend_prod[0])) { + op->vend_prod_num = sg_get_num_nomult(op->vend_prod); + if ((op->vend_prod_num < 0) || (op->vend_prod_num > 10)) { pr2serr("Bad vendor/product number after '--vendor=' " "option\n"); return SG_LIB_SYNTAX_ERROR; } } else { - vp_num = svpd_find_vp_num_by_acron(vp_str); - if (vp_num < 0) { + op->vend_prod_num = svpd_find_vp_num_by_acron(op->vend_prod); + if (op->vend_prod_num < 0) { pr2serr("Bad vendor/product acronym after '--vendor=' " "option\n"); return SG_LIB_SYNTAX_ERROR; } } - svpd_enumerate_vendor(vp_num); + svpd_enumerate_vendor(op->vend_prod_num); return 0; } - if (page_str) { - if ((0 == strcmp("-1", page_str)) || - (0 == strcmp("-2", page_str))) - num_vpd = VPD_NO_RATHER_STD_INQ; - else if (isdigit(page_str[0])) { - num_vpd = sg_get_num_nomult(page_str); - if ((num_vpd < 0) || (num_vpd > 255)) { + if (op->page_str) { + if ((0 == strcmp("-1", op->page_str)) || + (0 == strcmp("-2", op->page_str))) + op->num_vpd = VPD_NO_RATHER_STD_INQ; + else if (isdigit(op->page_str[0])) { + op->num_vpd = sg_get_num_nomult(op->page_str); + if ((op->num_vpd < 0) || (op->num_vpd > 255)) { pr2serr("Bad page code value after '-p' option\n"); return SG_LIB_SYNTAX_ERROR; } @@ -3457,25 +3687,27 @@ main(int argc, char * argv[]) "numbers\n"); return SG_LIB_SYNTAX_ERROR; } - matches = count_standard_vpds(num_vpd); + matches = count_standard_vpds(op->num_vpd); if (0 == matches) - matches = svpd_count_vendor_vpds(num_vpd, vp_num); + matches = svpd_count_vendor_vpds(op->num_vpd, + op->vend_prod_num); if (0 == matches) printf("No matches found for VPD page number 0x%x\n", - num_vpd); + op->num_vpd); } else { /* enumerate standard then vendor VPD pages */ printf("Standard VPD pages:\n"); enumerate_vpds(1, 1); } return 0; } - if (page_str) { - if ((0 == strcmp("-1", page_str)) || (0 == strcmp("-2", page_str))) - num_vpd = VPD_NO_RATHER_STD_INQ; - else if (isalpha(page_str[0])) { - vnp = sdp_find_vpd_by_acron(page_str); + if (op->page_str) { + if ((0 == strcmp("-1", op->page_str)) || + (0 == strcmp("-2", op->page_str))) + op->num_vpd = VPD_NO_RATHER_STD_INQ; + else if (isalpha(op->page_str[0])) { + vnp = sdp_find_vpd_by_acron(op->page_str); if (NULL == vnp) { - vnp = svpd_find_vendor_by_acron(page_str); + vnp = svpd_find_vendor_by_acron(op->page_str); if (NULL == vnp) { pr2serr("abbreviation doesn't match a VPD page\n"); printf("Available standard VPD pages:\n"); @@ -3483,19 +3715,19 @@ main(int argc, char * argv[]) return SG_LIB_SYNTAX_ERROR; } } - num_vpd = vnp->value; + op->num_vpd = vnp->value; subvalue = vnp->subvalue; - vp_num = subvalue; + op->vend_prod_num = subvalue; page_pdt = vnp->pdt; } else { - cp = strchr(page_str, ','); - if (cp && vp_str) { + cp = strchr(op->page_str, ','); + if (cp && op->vend_prod) { pr2serr("the --page=pg,vp and the --vendor=vp forms overlap, " "choose one or the other\n"); return SG_LIB_SYNTAX_ERROR; } - num_vpd = sg_get_num_nomult(page_str); - if ((num_vpd < 0) || (num_vpd > 255)) { + op->num_vpd = sg_get_num_nomult(op->page_str); + if ((op->num_vpd < 0) || (op->num_vpd > 255)) { pr2serr("Bad page code value after '-p' option\n"); printf("Available standard VPD pages:\n"); enumerate_vpds(1, 1); @@ -3503,150 +3735,155 @@ main(int argc, char * argv[]) } if (cp) { if (isdigit(*(cp + 1))) - vp_num = sg_get_num_nomult(cp + 1); + op->vend_prod_num = sg_get_num_nomult(cp + 1); else - vp_num = svpd_find_vp_num_by_acron(cp + 1); - if ((vp_num < 0) || (vp_num > 255)) { + op->vend_prod_num = svpd_find_vp_num_by_acron(cp + 1); + if ((op->vend_prod_num < 0) || (op->vend_prod_num > 255)) { pr2serr("Bad vendor/product acronym after comma in '-p' " "option\n"); - if (vp_num < 0) + if (op->vend_prod_num < 0) svpd_enumerate_vendor(-1); return SG_LIB_SYNTAX_ERROR; } - subvalue = vp_num; - } else if (vp_str) { - if (isdigit(vp_str[0])) - vp_num = sg_get_num_nomult(vp_str); + subvalue = op->vend_prod_num; + } else if (op->vend_prod) { + if (isdigit(op->vend_prod[0])) + op->vend_prod_num = sg_get_num_nomult(op->vend_prod); else - vp_num = svpd_find_vp_num_by_acron(vp_str); - if ((vp_num < 0) || (vp_num > 255)) { + op->vend_prod_num = + svpd_find_vp_num_by_acron(op->vend_prod); + if ((op->vend_prod_num < 0) || (op->vend_prod_num > 255)) { pr2serr("Bad vendor/product acronym after '--vendor=' " "option\n"); svpd_enumerate_vendor(-1); return SG_LIB_SYNTAX_ERROR; } - subvalue = vp_num; + subvalue = op->vend_prod_num; } } - } else if (vp_str) { - if (isdigit(vp_str[0])) - vp_num = sg_get_num_nomult(vp_str); + } else if (op->vend_prod) { + if (isdigit(op->vend_prod[0])) + op->vend_prod_num = sg_get_num_nomult(op->vend_prod); else - vp_num = svpd_find_vp_num_by_acron(vp_str); - if ((vp_num < 0) || (vp_num > 255)) { + op->vend_prod_num = svpd_find_vp_num_by_acron(op->vend_prod); + if ((op->vend_prod_num < 0) || (op->vend_prod_num > 255)) { pr2serr("Bad vendor/product acronym after '--vendor=' " "option\n"); svpd_enumerate_vendor(-1); return SG_LIB_SYNTAX_ERROR; } - subvalue = vp_num; + subvalue = op->vend_prod_num; } - if (inhex_fn) { - if (device_name) { + if (op->inhex_fn) { + if (op->device_name) { pr2serr("Cannot have both a DEVICE and --inhex= option\n"); return SG_LIB_SYNTAX_ERROR; } - if (f2hex_arr(inhex_fn, do_raw, 0, rsp_buff, &inhex_len, + if (f2hex_arr(op->inhex_fn, op->do_raw, 0, rsp_buff, &inhex_len, sizeof(rsp_buff))) return SG_LIB_FILE_ERROR; - do_raw = 0; /* don't want raw on output with --inhex= */ - if (NULL == page_str) { /* may be able to deduce VPD page */ + op->do_raw = 0; /* don't want raw on output with --inhex= */ + if ((NULL == op->page_str) && (0 == op->do_all)) { + /* may be able to deduce VPD page */ if ((0x2 == (0xf & rsp_buff[3])) && (rsp_buff[2] > 2)) { - if (do_verbose) + if (op->verbose) pr2serr("Guessing from --inhex= this is a standard " "INQUIRY\n"); if (page_pdt < 0) page_pdt = 0x1f & rsp_buff[0]; } else if (rsp_buff[2] <= 2) { - if (do_verbose) + if (op->verbose) pr2serr("Guessing from --inhex this is VPD page 0x%x\n", rsp_buff[1]); - num_vpd = rsp_buff[1]; + op->num_vpd = rsp_buff[1]; if (page_pdt < 0) page_pdt = 0x1f & rsp_buff[0]; } else { - if (num_vpd > 0x80) { - num_vpd = rsp_buff[1]; + if (op->num_vpd > 0x80) { + op->num_vpd = rsp_buff[1]; if (page_pdt < 0) page_pdt = 0x1f & rsp_buff[0]; - if (do_verbose) + if (op->verbose) pr2serr("Guessing from --inhex this is VPD page " "0x%x\n", rsp_buff[1]); } else { - num_vpd = VPD_NO_RATHER_STD_INQ; - if (do_verbose) + op->num_vpd = VPD_NO_RATHER_STD_INQ; + if (op->verbose) pr2serr("page number unclear from --inhex, hope " "it's a standard INQUIRY response\n"); } } } - } else if (NULL == device_name) { + } else if (NULL == op->device_name) { pr2serr("No DEVICE argument given\n"); usage(); return SG_LIB_SYNTAX_ERROR; } - if (do_raw && do_hex) { + if (op->do_raw && op->do_hex) { pr2serr("Can't do hex and raw at the same time\n"); usage(); return SG_LIB_SYNTAX_ERROR; } - if (do_ident) { - num_vpd = VPD_DEVICE_ID; - if (do_ident > 1) { - if (0 == do_long) - ++do_quiet; + if (op->do_ident) { + op->num_vpd = VPD_DEVICE_ID; + if (op->do_ident > 1) { + if (0 == op->do_long) + ++op->do_quiet; subvalue = VPD_DI_SEL_LU; } } - if (do_raw) { + if (op->do_raw) { if (sg_set_binary_mode(STDOUT_FILENO) < 0) { perror("sg_set_binary_mode"); return SG_LIB_FILE_ERROR; } } - if (inhex_fn) { - res = svpd_decode_t10(-1, num_vpd, subvalue, vp_num, inhex_len, - do_hex, do_raw, do_long, do_quiet, do_verbose); - if (SG_LIB_SYNTAX_ERROR == res) { - res = svpd_decode_vendor(-1, num_vpd, vp_num, inhex_len, do_hex, - do_raw, do_long, do_quiet, do_verbose); - if (SG_LIB_SYNTAX_ERROR == res) - res = svpd_unable_to_decode(-1, num_vpd, subvalue, inhex_len, - do_hex, do_raw, do_long, do_quiet, - do_verbose); + if (op->inhex_fn) { + if ((0 == op->maxlen) || (inhex_len < op->maxlen)) + op->maxlen = inhex_len; + if (op->do_all) + res = svpd_decode_all(-1, op); + else { + res = svpd_decode_t10(-1, op, subvalue, 0); + if (SG_LIB_SYNTAX_ERROR == res) { + res = svpd_decode_vendor(-1, op, 0); + if (SG_LIB_SYNTAX_ERROR == res) + res = svpd_unable_to_decode(-1, op, subvalue, 0); + } } return res; } - if ((sg_fd = sg_cmds_open_device(device_name, 1 /* ro */, - do_verbose)) < 0) { - pr2serr("error opening file: %s: %s\n", device_name, + if ((sg_fd = sg_cmds_open_device(op->device_name, 1 /* ro */, + op->verbose)) < 0) { + pr2serr("error opening file: %s: %s\n", op->device_name, safe_strerror(-sg_fd)); return SG_LIB_FILE_ERROR; } - memset(rsp_buff, 0, sizeof(rsp_buff)); - res = svpd_decode_t10(sg_fd, num_vpd, subvalue, vp_num, maxlen, do_hex, - do_raw, do_long, do_quiet, do_verbose); - if (SG_LIB_SYNTAX_ERROR == res) { - res = svpd_decode_vendor(sg_fd, num_vpd, vp_num, maxlen, do_hex, - do_raw, do_long, do_quiet, do_verbose); - if (SG_LIB_SYNTAX_ERROR == res) - res = svpd_unable_to_decode(sg_fd, num_vpd, subvalue, maxlen, - do_hex, do_raw, do_long, do_quiet, - do_verbose); - } - if (SG_LIB_CAT_ABORTED_COMMAND == res) - pr2serr("fetching VPD page failed, aborted command\n"); - else if (res) { - char b[80]; + if (op->do_all) + ret = svpd_decode_all(sg_fd, op); + else { + memset(rsp_buff, 0, sizeof(rsp_buff)); - sg_get_category_sense_str(res, sizeof(b), b, do_verbose); - pr2serr("fetching VPD page failed: %s\n", b); + res = svpd_decode_t10(sg_fd, op, subvalue, 0); + if (SG_LIB_SYNTAX_ERROR == res) { + res = svpd_decode_vendor(sg_fd, op, 0); + if (SG_LIB_SYNTAX_ERROR == res) + res = svpd_unable_to_decode(sg_fd, op, subvalue, 0); + } + if (SG_LIB_CAT_ABORTED_COMMAND == res) + pr2serr("fetching VPD page failed, aborted command\n"); + else if (res) { + char b[80]; + + sg_get_category_sense_str(res, sizeof(b), b, op->verbose); + pr2serr("fetching VPD page failed: %s\n", b); + } + ret = res; } - ret = res; res = sg_cmds_close_device(sg_fd); if (res < 0) { pr2serr("close error: %s\n", safe_strerror(-res)); diff --git a/src/sg_vpd_vendor.c b/src/sg_vpd_vendor.c index 281b328..c0344d5 100644 --- a/src/sg_vpd_vendor.c +++ b/src/sg_vpd_vendor.c @@ -84,30 +84,48 @@ #define DEF_ALLOC_LEN 252 #define MX_ALLOC_LEN (0xc000 + 0x80) -struct svpd_vp_name_t { - int vp_num; /* vendor/product identifier */ - const char * acron; - const char * name; +/* These structures are duplicates of those of the same name in + * sg_vpd.c . Take care that both are the same. */ +struct opts_t { + int do_all; + int do_enum; + int do_hex; + int num_vpd; + int do_ident; + int do_long; + int maxlen; + int do_quiet; + int do_raw; + int vend_prod_num; + int verbose; + const char * device_name; + const char * page_str; + const char * inhex_fn; + const char * vend_prod; }; -/* This structure is a duplicate of one of the same name in sg_vpd.c . - Take care that both have the same fields (and types). */ struct svpd_values_name_t { - int value; /* VPD number */ - int subvalue; /* vendor/product identifier used to disambiguate */ - /* shared VPD numbers */ + int value; /* VPD page number */ + int subvalue; /* to differentiate if value+pdt are not unique */ int pdt; /* peripheral device type id, -1 is the default */ /* (all or not applicable) value */ const char * acron; const char * name; }; - int vpd_fetch_page_from_dev(int sg_fd, unsigned char * rp, int page, int mxlen, int vb, int * rlenp); -/* Size of this array must match the array of the same name in sg_vpd.c */ -static unsigned char rsp_buff[MX_ALLOC_LEN + 2]; +/* sharing large global buffer, defined in sg_vpd.c */ +extern unsigned char rsp_buff[]; + +/* end of section copied from sg_vpd.c . Maybe sg_vpd.h is needed */ + +struct svpd_vp_name_t { + int vend_prod_num; /* vendor/product identifier */ + const char * acron; + const char * name; +}; /* Supported vendor specific VPD pages */ @@ -127,45 +145,45 @@ static struct svpd_vp_name_t vp_arr[] = { /* 'subvalue' holds vendor/product number to disambiguate */ /* Arrange in alphabetical order by acronym */ static struct svpd_values_name_t vendor_vpd_pg[] = { - {VPD_V_ACI_LTO6, VPD_VP_LTO6, -1, "aci", "ACI revision level (LTO-6)"}, - {VPD_V_DATC_SEA, VPD_VP_SEAGATE, -1, "datc", "Date code (Seagate)"}, - {VPD_V_DCRL_LTO5, VPD_VP_LTO5, -1, "dcrl" , "Drive component revision " + {VPD_V_ACI_LTO6, VPD_VP_LTO6, 1, "aci", "ACI revision level (LTO-6)"}, + {VPD_V_DATC_SEA, VPD_VP_SEAGATE, 0, "datc", "Date code (Seagate)"}, + {VPD_V_DCRL_LTO5, VPD_VP_LTO5, 1, "dcrl" , "Drive component revision " "levels (LTO-5)"}, - {VPD_V_FVER_DDS, VPD_VP_DDS, -1, "ddsver", "Firmware revision (DDS)"}, - {VPD_V_DEV_BEH_SEA, VPD_VP_SEAGATE, -1, "devb", "Device behavior " + {VPD_V_FVER_DDS, VPD_VP_DDS, 1, "ddsver", "Firmware revision (DDS)"}, + {VPD_V_DEV_BEH_SEA, VPD_VP_SEAGATE, 0, "devb", "Device behavior " "(Seagate)"}, - {VPD_V_DSN_LTO5, VPD_VP_LTO5, -1, "dsn" , "Drive serial numbers (LTO-5)"}, - {VPD_V_DUCD_LTO5, VPD_VP_LTO5, -1, "ducd" , "Device unique " + {VPD_V_DSN_LTO5, VPD_VP_LTO5, 1, "dsn" , "Drive serial numbers (LTO-5)"}, + {VPD_V_DUCD_LTO5, VPD_VP_LTO5, 1, "ducd" , "Device unique " "configuration data (LTO-5)"}, - {VPD_V_EDID_RDAC, VPD_VP_RDAC, -1, "edid", "Extended device " + {VPD_V_EDID_RDAC, VPD_VP_RDAC, 0, "edid", "Extended device " "identification (RDAC)"}, - {VPD_V_FEAT_RDAC, VPD_VP_RDAC, -1, "feat", "Feature Parameters (RDAC)"}, - {VPD_V_FIRM_SEA, VPD_VP_SEAGATE, -1, "firm", "Firmware numbers " + {VPD_V_FEAT_RDAC, VPD_VP_RDAC, 0, "feat", "Feature Parameters (RDAC)"}, + {VPD_V_FIRM_SEA, VPD_VP_SEAGATE, 0, "firm", "Firmware numbers " "(Seagate)"}, - {VPD_V_FVER_LTO6, VPD_VP_LTO6, -1, "frl" , "Firmware revision level " + {VPD_V_FVER_LTO6, VPD_VP_LTO6, 0, "frl" , "Firmware revision level " "(LTO-6)"}, - {VPD_V_FVER_RDAC, VPD_VP_RDAC, -1, "fver", "Firmware version (RDAC)"}, - {VPD_V_HEAD_LTO6, VPD_VP_LTO6, -1, "head", "Head Assy revision level " + {VPD_V_FVER_RDAC, VPD_VP_RDAC, 0, "fver", "Firmware version (RDAC)"}, + {VPD_V_HEAD_LTO6, VPD_VP_LTO6, 1, "head", "Head Assy revision level " "(LTO-6)"}, - {VPD_V_HP3PAR, VPD_VP_HP3PAR, -1, "hp3par", "Volume information " + {VPD_V_HP3PAR, VPD_VP_HP3PAR, 0, "hp3par", "Volume information " "(HP/3PAR)"}, - {VPD_V_HVER_LTO6, VPD_VP_LTO6, -1, "hrl", "Hardware revision level " + {VPD_V_HVER_LTO6, VPD_VP_LTO6, 1, "hrl", "Hardware revision level " "(LTO-6)"}, - {VPD_V_HVER_RDAC, VPD_VP_RDAC, -1, "hver", "Hardware version (RDAC)"}, - {VPD_V_JUMP_SEA, VPD_VP_SEAGATE, -1, "jump", "Jump setting (Seagate)"}, - {VPD_V_MECH_LTO6, VPD_VP_LTO6, -1, "mech", "Mechanism revision level " + {VPD_V_HVER_RDAC, VPD_VP_RDAC, 0, "hver", "Hardware version (RDAC)"}, + {VPD_V_JUMP_SEA, VPD_VP_SEAGATE, 0, "jump", "Jump setting (Seagate)"}, + {VPD_V_MECH_LTO6, VPD_VP_LTO6, 1, "mech", "Mechanism revision level " "(LTO-6)"}, - {VPD_V_MPDS_LTO5, VPD_VP_LTO5, -1, "mpds" , "Mode parameter default " + {VPD_V_MPDS_LTO5, VPD_VP_LTO5, 1, "mpds" , "Mode parameter default " "settings (LTO-5)"}, - {VPD_V_PCA_LTO6, VPD_VP_LTO6, -1, "pca", "PCA revision level (LTO-6)"}, - {VPD_V_RVSI_RDAC, VPD_VP_RDAC, -1, "rvsi", "Replicated volume source " + {VPD_V_PCA_LTO6, VPD_VP_LTO6, 1, "pca", "PCA revision level (LTO-6)"}, + {VPD_V_RVSI_RDAC, VPD_VP_RDAC, 0, "rvsi", "Replicated volume source " "identifier (RDAC)"}, - {VPD_V_SAID_RDAC, VPD_VP_RDAC, -1, "said", "Storage array world wide " + {VPD_V_SAID_RDAC, VPD_VP_RDAC, 0, "said", "Storage array world wide " "name (RDAC)"}, - {VPD_V_SUBS_RDAC, VPD_VP_RDAC, -1, "sub", "Subsystem identifier (RDAC)"}, - {VPD_V_SVER_RDAC, VPD_VP_RDAC, -1, "sver", "Software version (RDAC)"}, - {VPD_V_UPR_EMC, VPD_VP_EMC, -1, "upr", "Unit path report (EMC)"}, - {VPD_V_VAC_RDAC, VPD_VP_RDAC, -1, "vac", "Volume access control (RDAC)"}, + {VPD_V_SUBS_RDAC, VPD_VP_RDAC, 0, "sub", "Subsystem identifier (RDAC)"}, + {VPD_V_SVER_RDAC, VPD_VP_RDAC, 0, "sver", "Software version (RDAC)"}, + {VPD_V_UPR_EMC, VPD_VP_EMC, 0, "upr", "Unit path report (EMC)"}, + {VPD_V_VAC_RDAC, VPD_VP_RDAC, 0, "vac", "Volume access control (RDAC)"}, {0, 0, 0, NULL, NULL}, }; @@ -190,35 +208,66 @@ pr2serr(const char * fmt, ...) return n; } +static int +is_like_pdt(int actual_pdt, const struct svpd_values_name_t * vnp) +{ + if (actual_pdt == vnp->pdt) + return 1; + if (PDT_DISK == vnp->pdt) { + switch (actual_pdt) { + case PDT_DISK: + case PDT_RBC: + case PDT_PROCESSOR: + case PDT_SAC: + case PDT_ZBC: + return 1; + default: + return 0; + } + } else if (PDT_TAPE == vnp->pdt) { + switch (actual_pdt) { + case PDT_TAPE: + case PDT_MCHANGER: + case PDT_ADC: + return 1; + default: + return 0; + } + } else + return 0; +} + static const struct svpd_values_name_t * -svpd_get_v_detail(int page_num, int vp_num, int pdt) +svpd_get_v_detail(int page_num, int vend_prod_num, int pdt) { const struct svpd_values_name_t * vnp; int vp, ty; - vp = (vp_num < 0) ? 1 : 0; + vp = (vend_prod_num < 0) ? 1 : 0; ty = (pdt < 0) ? 1 : 0; for (vnp = vendor_vpd_pg; vnp->acron; ++vnp) { if ((page_num == vnp->value) && - (vp || (vp_num == vnp->subvalue)) && - (ty || (pdt == vnp->pdt))) + (vp || (vend_prod_num == vnp->subvalue)) && + (ty || is_like_pdt(pdt, vnp))) return vnp; } +#if 0 if (! ty) - return svpd_get_v_detail(page_num, vp_num, -1); + return svpd_get_v_detail(page_num, vend_prod_num, -1); if (! vp) - return svpd_get_v_detail(page_num, -1, -1); + return svpd_get_v_detail(page_num, -1, pdt); +#endif return NULL; } const struct svpd_values_name_t * -svpd_find_vendor_by_num(int page_num, int vp_num) +svpd_find_vendor_by_num(int page_num, int vend_prod_num) { const struct svpd_values_name_t * vnp; for (vnp = vendor_vpd_pg; vnp->acron; ++vnp) { if ((page_num == vnp->value) && - ((vp_num < 0) || (vp_num == vnp->subvalue))) + ((vend_prod_num < 0) || (vend_prod_num == vnp->subvalue))) return vnp; } return NULL; @@ -234,7 +283,7 @@ svpd_find_vp_num_by_acron(const char * vp_ap) for (vpp = vp_arr; vpp->acron; ++vpp) { len = strlen(vpp->acron); if (0 == strncmp(vpp->acron, vp_ap, len)) - return vpp->vp_num; + return vpp->vend_prod_num; } return -1; } @@ -252,15 +301,16 @@ svpd_find_vendor_by_acron(const char * ap) return NULL; } -/* vp_num=-2 everthing, =-1 only vendor_product, else just that vp_num */ +/* if vend_prod_num < -1 then list vendor_product ids + vendor pages, =-1 + * list only vendor_product ids, else list pages for that vend_prod_num */ void -svpd_enumerate_vendor(int vp_num) +svpd_enumerate_vendor(int vend_prod_num) { const struct svpd_vp_name_t * vpp; const struct svpd_values_name_t * vnp; int seen; - if (vp_num < 0) { + if (vend_prod_num < 0) { for (seen = 0, vpp = vp_arr; vpp->acron; ++vpp) { if (vpp->name) { if (! seen) { @@ -268,14 +318,14 @@ svpd_enumerate_vendor(int vp_num) seen = 1; } printf(" %-10s %d %s\n", vpp->acron, - vpp->vp_num, vpp->name); + vpp->vend_prod_num, vpp->name); } } } - if (-1 == vp_num) + if (-1 == vend_prod_num) return; for (seen = 0, vnp = vendor_vpd_pg; vnp->acron; ++vnp) { - if ((vp_num >= 0) && (vp_num != vnp->subvalue)) + if ((vend_prod_num >= 0) && (vend_prod_num != vnp->subvalue)) continue; if (vnp->name) { if (! seen) { @@ -289,14 +339,14 @@ svpd_enumerate_vendor(int vp_num) } int -svpd_count_vendor_vpds(int num_vpd, int vp_num) +svpd_count_vendor_vpds(int num_vpd, int vend_prod_num) { const struct svpd_values_name_t * vnp; int matches; for (vnp = vendor_vpd_pg, matches = 0; vnp->acron; ++vnp) { if ((num_vpd == vnp->value) && vnp->name) { - if ((vp_num < 0) || (vp_num == vnp->subvalue)) { + if ((vend_prod_num < 0) || (vend_prod_num == vnp->subvalue)) { if (0 == matches) printf("Matching vendor specific VPD pages:\n"); ++matches; @@ -1070,127 +1120,129 @@ decode_lto5_dsn(unsigned char * buff, int len) /* Returns 0 if successful, see sg_ll_inquiry() plus SG_LIB_SYNTAX_ERROR for unsupported page */ int -svpd_decode_vendor(int sg_fd, int num_vpd, int vp_num, int maxlen, - int do_hex, int do_raw, int do_long, int do_quiet, - int verbose) +svpd_decode_vendor(int sg_fd, struct opts_t * op, int off) { int len, res; char name[64]; const struct svpd_values_name_t * vnp; - int alloc_len = maxlen; + int alloc_len = op->maxlen; + unsigned char * rp; - if (do_long) { ; } /* unused, dummy to suppress warning */ - vnp = svpd_get_v_detail(num_vpd, vp_num, -1); - if (vnp && vnp->name) - strcpy(name, vnp->name); - else - snprintf(name, sizeof(name) - 1, "Vendor VPD page=0x%x", num_vpd); + rp = rsp_buff + off; if (sg_fd >= 0) { if (0 == alloc_len) alloc_len = DEF_ALLOC_LEN; } - if ((! do_raw) && (! do_quiet) && (do_hex < 2)) - printf("%s VPD Page:\n", name); - res = vpd_fetch_page_from_dev(sg_fd, rsp_buff, num_vpd, alloc_len, - verbose, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, op->num_vpd, alloc_len, + op->verbose, &len); if (0 == res) { - if (do_raw) - dStrRaw((const char *)rsp_buff, len); - else if (do_hex) - dStrHex((const char *)rsp_buff, len, ((1 == do_hex) ? 0 : -1)); + vnp = svpd_get_v_detail(op->num_vpd, op->vend_prod_num, 0xf & rp[0]); + if (vnp && vnp->name) + strcpy(name, vnp->name); + else + snprintf(name, sizeof(name) - 1, "Vendor VPD page=0x%x", + op->num_vpd); + if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 2)) + printf("%s VPD Page:\n", name); + if (op->do_raw) + dStrRaw((const char *)rp, len); + else if (op->do_hex) + dStrHex((const char *)rp, len, + ((1 == op->do_hex) ? 0 : -1)); else { - switch(num_vpd) { + switch(op->num_vpd) { case 0xc0: - if (VPD_VP_SEAGATE == vp_num) - decode_firm_vpd_c0_sea(rsp_buff, len); - else if (VPD_VP_EMC == vp_num) - decode_upr_vpd_c0_emc(rsp_buff, len); - else if (VPD_VP_HP3PAR == vp_num) - decode_vpd_c0_hp3par(rsp_buff, len); - else if (VPD_VP_RDAC == vp_num) - decode_rdac_vpd_c0(rsp_buff, len); - else if (VPD_VP_DDS == vp_num) - decode_dds_vpd_c0(rsp_buff, len); - else if (VPD_VP_LTO5 == vp_num) - decode_lto5_dcrl(rsp_buff, len); - else if (VPD_VP_LTO6 == vp_num) - decode_lto6_vpd_cx(rsp_buff, len, num_vpd); + if (VPD_VP_SEAGATE == op->vend_prod_num) + decode_firm_vpd_c0_sea(rp, len); + else if (VPD_VP_EMC == op->vend_prod_num) + decode_upr_vpd_c0_emc(rp, len); + else if (VPD_VP_HP3PAR == op->vend_prod_num) + decode_vpd_c0_hp3par(rp, len); + else if (VPD_VP_RDAC == op->vend_prod_num) + decode_rdac_vpd_c0(rp, len); + else if (VPD_VP_DDS == op->vend_prod_num) + decode_dds_vpd_c0(rp, len); + else if (VPD_VP_LTO5 == op->vend_prod_num) + decode_lto5_dcrl(rp, len); + else if (VPD_VP_LTO6 == op->vend_prod_num) + decode_lto6_vpd_cx(rp, len, op->num_vpd); else - dStrHex((const char *)rsp_buff, len, 0); + dStrHex((const char *)rp, len, 0); break; case 0xc1: - if (VPD_VP_SEAGATE == vp_num) - decode_date_code_vpd_c1_sea(rsp_buff, len); - else if (VPD_VP_RDAC == vp_num) - decode_rdac_vpd_c1(rsp_buff, len); - else if (VPD_VP_LTO5 == vp_num) - decode_lto5_dsn(rsp_buff, len); - else if (VPD_VP_LTO6 == vp_num) - decode_lto6_vpd_cx(rsp_buff, len, num_vpd); + if (VPD_VP_SEAGATE == op->vend_prod_num) + decode_date_code_vpd_c1_sea(rp, len); + else if (VPD_VP_RDAC == op->vend_prod_num) + decode_rdac_vpd_c1(rp, len); + else if (VPD_VP_LTO5 == op->vend_prod_num) + decode_lto5_dsn(rp, len); + else if (VPD_VP_LTO6 == op->vend_prod_num) + decode_lto6_vpd_cx(rp, len, op->num_vpd); else - dStrHex((const char *)rsp_buff, len, 0); + dStrHex((const char *)rp, len, 0); break; case 0xc2: - if (VPD_VP_RDAC == vp_num) - decode_rdac_vpd_c2(rsp_buff, len); - else if (VPD_VP_LTO6 == vp_num) - decode_lto6_vpd_cx(rsp_buff, len, num_vpd); + if (VPD_VP_RDAC == op->vend_prod_num) + decode_rdac_vpd_c2(rp, len); + else if (VPD_VP_LTO6 == op->vend_prod_num) + decode_lto6_vpd_cx(rp, len, op->num_vpd); else - dStrHex((const char *)rsp_buff, len, 0); + dStrHex((const char *)rp, len, 0); break; case 0xc3: - if (VPD_VP_SEAGATE == vp_num) - decode_dev_beh_vpd_c3_sea(rsp_buff, len); - else if (VPD_VP_RDAC == vp_num) - decode_rdac_vpd_c3(rsp_buff, len); - else if (VPD_VP_LTO6 == vp_num) - decode_lto6_vpd_cx(rsp_buff, len, num_vpd); + if (VPD_VP_SEAGATE == op->vend_prod_num) + decode_dev_beh_vpd_c3_sea(rp, len); + else if (VPD_VP_RDAC == op->vend_prod_num) + decode_rdac_vpd_c3(rp, len); + else if (VPD_VP_LTO6 == op->vend_prod_num) + decode_lto6_vpd_cx(rp, len, op->num_vpd); else - dStrHex((const char *)rsp_buff, len, 0); + dStrHex((const char *)rp, len, 0); break; case 0xc4: - if (VPD_VP_RDAC == vp_num) - decode_rdac_vpd_c4(rsp_buff, len); - else if (VPD_VP_LTO6 == vp_num) - decode_lto6_vpd_cx(rsp_buff, len, num_vpd); + if (VPD_VP_RDAC == op->vend_prod_num) + decode_rdac_vpd_c4(rp, len); + else if (VPD_VP_LTO6 == op->vend_prod_num) + decode_lto6_vpd_cx(rp, len, op->num_vpd); else - dStrHex((const char *)rsp_buff, len, 0); + dStrHex((const char *)rp, len, 0); break; case 0xc5: - if (VPD_VP_LTO6 == vp_num) - decode_lto6_vpd_cx(rsp_buff, len, num_vpd); + if (VPD_VP_LTO6 == op->vend_prod_num) + decode_lto6_vpd_cx(rp, len, op->num_vpd); else - dStrHex((const char *)rsp_buff, len, 0); + dStrHex((const char *)rp, len, 0); break; case 0xc8: - if (VPD_VP_RDAC == vp_num) - decode_rdac_vpd_c8(rsp_buff, len); + if (VPD_VP_RDAC == op->vend_prod_num) + decode_rdac_vpd_c8(rp, len); else - dStrHex((const char *)rsp_buff, len, 0); + dStrHex((const char *)rp, len, 0); break; case 0xc9: - if (VPD_VP_RDAC == vp_num) - decode_rdac_vpd_c9(rsp_buff, len); + if (VPD_VP_RDAC == op->vend_prod_num) + decode_rdac_vpd_c9(rp, len); else - dStrHex((const char *)rsp_buff, len, 0); + dStrHex((const char *)rp, len, 0); break; case 0xca: - if (VPD_VP_RDAC == vp_num) - decode_rdac_vpd_ca(rsp_buff, len); + if (VPD_VP_RDAC == op->vend_prod_num) + decode_rdac_vpd_ca(rp, len); else - dStrHex((const char *)rsp_buff, len, 0); + dStrHex((const char *)rp, len, 0); break; case 0xd0: - if (VPD_VP_RDAC == vp_num) - decode_rdac_vpd_d0(rsp_buff, len); + if (VPD_VP_RDAC == op->vend_prod_num) + decode_rdac_vpd_d0(rp, len); else - dStrHex((const char *)rsp_buff, len, 0); + dStrHex((const char *)rp, len, 0); break; default: return SG_LIB_SYNTAX_ERROR; } return 0; } - } + } else + pr2serr("Vendor VPD page=0x%x failed to fetch", op->num_vpd); return res; } diff --git a/src/sg_write_buffer.c b/src/sg_write_buffer.c index fc14b1c..2cfffb1 100644 --- a/src/sg_write_buffer.c +++ b/src/sg_write_buffer.c @@ -26,7 +26,7 @@ * This utility issues the SCSI WRITE BUFFER command to the given device. */ -static const char * version_str = "1.15 20140518"; /* spc4r37 */ +static const char * version_str = "1.18 20141107"; /* spc4r37 */ #define ME "sg_write_buffer: " #define DEF_XFER_LEN (8 * 1024 * 1024) @@ -101,7 +101,7 @@ usage() "acronym\n" " (def: 0 -> 'combined header and " "data' (obs))\n" - " --off=OFF|-o OFF buffer offset (unit: bytes, def: 0)\n" + " --offset=OFF|-o OFF buffer offset (unit: bytes, def: 0)\n" " --raw|-r read from stdin (same as '-I -')\n" " --skip=SKIP|-s SKIP bytes in file FILE to skip before " "reading\n" @@ -110,8 +110,9 @@ usage() " --verbose|-v increase verbosity\n" " --version|-V print version string and exit\n\n" "Performs one or more SCSI WRITE BUFFER commands. Use '-m xxx' " - "to list\navailable modes. Numbers given in options are decimal " - "unless they have\na hex indicator.\n" + "to list\navailable modes. A chunk size of 4 KB ('--bpw=4k') " + "seems to work well.\nExample: sg_write_buffer -b 4k -I xxx.lod " + "-m 7 /dev/sg3\n" ); } @@ -132,51 +133,54 @@ usage() #define MODE_DNLD_ERR_HISTORY 0x1C -static struct mode_s { +struct mode_s { const char *mode_string; int mode; const char *comment; -} modes[] = { - { "hd", MODE_HEADER_DATA, "combined header and data " +}; + +static struct mode_s mode_arr[] = { + {"hd", MODE_HEADER_DATA, "combined header and data " "(obsolete)"}, - { "vendor", MODE_VENDOR, "vendor specific"}, - { "data", MODE_DATA, "data"}, - { "dmc", MODE_DNLD_MC, "download microcode and activate"}, - { "dmc_save", MODE_DNLD_MC_SAVE, "download microcode, save and " + {"vendor", MODE_VENDOR, "vendor specific"}, + {"data", MODE_DATA, "data"}, + {"dmc", MODE_DNLD_MC, "download microcode and activate"}, + {"dmc_save", MODE_DNLD_MC_SAVE, "download microcode, save and " "activate"}, - { "dmc_offs", MODE_DNLD_MC_OFFS, "download microcode with offsets " + {"dmc_offs", MODE_DNLD_MC_OFFS, "download microcode with offsets " "and activate"}, - { "dmc_offs_save", MODE_DNLD_MC_OFFS_SAVE, "download microcode with " + {"dmc_offs_save", MODE_DNLD_MC_OFFS_SAVE, "download microcode with " "offsets, save and\n\t\t\t\tactivate"}, - { "echo", MODE_ECHO_BUFFER, "write data to echo buffer"}, - { "dmc_offs_ev_defer", MODE_DNLD_MC_EV_OFFS_DEFER, "download " + {"echo", MODE_ECHO_BUFFER, "write data to echo buffer"}, + {"dmc_offs_ev_defer", MODE_DNLD_MC_EV_OFFS_DEFER, "download " "microcode with offsets, select\n\t\t\t\tactivation event, " "save and defer activation"}, - { "dmc_offs_defer", MODE_DNLD_MC_OFFS_DEFER, "download microcode " - "with offsets, save and defer\n\t\t\t\tactivation"}, - { "activate_mc", MODE_ACTIVATE_MC, - "activate deferred microcode"}, - { "en_ex", MODE_EN_EX_ECHO, "enable expander communications " - "protocol and echo\n\t\t\t\tbuffer (obsolete)"}, - { "dis_ex", MODE_DIS_EX, "disable expander communications " + {"dmc_offs_defer", MODE_DNLD_MC_OFFS_DEFER, "download microcode " + "with offsets, save and\n\t\t\t\tdefer activation"}, + {"activate_mc", MODE_ACTIVATE_MC, "activate deferred microcode"}, + {"en_ex", MODE_EN_EX_ECHO, "enable expander communications " + "protocol and\n\t\t\t\techo buffer (obsolete)"}, + {"dis_ex", MODE_DIS_EX, "disable expander communications " "protocol\n\t\t\t\t(obsolete)"}, - { "deh", MODE_DNLD_ERR_HISTORY, "download application client " + {"deh", MODE_DNLD_ERR_HISTORY, "download application client " "error history "}, + {NULL, 0, NULL}, }; -#define NUM_MODES ((int)(sizeof(modes)/sizeof(modes[0]))) - static void print_modes(void) { - int k; + const struct mode_s * mp; pr2serr("The modes parameter argument can be numeric (hex or decimal)\n" "or symbolic:\n"); - for (k = 0; k < NUM_MODES; k++) { - pr2serr(" %2d (0x%02x) %-18s%s\n", modes[k].mode, modes[k].mode, - modes[k].mode_string, modes[k].comment); + for (mp = mode_arr; mp->mode_string; ++mp) { + pr2serr(" %2d (0x%02x) %-18s%s\n", mp->mode, mp->mode, + mp->mode_string, mp->comment); } + pr2serr("\nAdditionally '--bpw=<val>,act' does a activate deferred " + "microcode after\nsuccessful dmc_offs_defer and " + "dmc_offs_ev_defer mode downloads.\n"); } /* <<<< This function will be moved to the library in the future >>> */ @@ -268,6 +272,7 @@ main(int argc, char * argv[]) const char * file_name = NULL; unsigned char * dop = NULL; char * cp; + const struct mode_s * mp; char ebuff[EBUFF_SZ]; int ret = 0; @@ -325,13 +330,13 @@ main(int argc, char * argv[]) } } else { len = strlen(optarg); - for (k = 0; k < NUM_MODES; ++k) { - if (0 == strncmp(modes[k].mode_string, optarg, len)) { - wb_mode = modes[k].mode; + for (mp = mode_arr; mp->mode_string; ++mp) { + if (0 == strncmp(mp->mode_string, optarg, len)) { + wb_mode = mp->mode; break; } } - if (NUM_MODES == k) { + if (! mp->mode_string) { print_modes(); return SG_LIB_SYNTAX_ERROR; } @@ -478,7 +483,12 @@ main(int argc, char * argv[]) if (verbose) { pr2serr("tried to read %d bytes from %s, got %d " "bytes\n", wb_len, file_name, res); - pr2serr("will write %d bytes\n", res); + pr2serr("will write %d bytes", res); + if ((bpw > 0) && (bpw < wb_len)) + pr2serr(", %d bytes per WRITE BUFFER command\n", + bpw); + else + pr2serr("\n"); } wb_len = res; } @@ -507,8 +517,8 @@ main(int argc, char * argv[]) if (bpw_then_activate) { if (verbose) pr2serr("sending Activate deferred microcode [0xf]\n"); - res = sg_ll_write_buffer_v2(sg_fd, 0xf, 0, 0, 0, NULL, 0, 1, - verbose); + res = sg_ll_write_buffer_v2(sg_fd, MODE_ACTIVATE_MC, 0, 0, 0, + NULL, 0, 1, verbose); } } else { if (verbose) diff --git a/src/sg_write_same.c b/src/sg_write_same.c index b77f9b6..5cbb0b2 100644 --- a/src/sg_write_same.c +++ b/src/sg_write_same.c @@ -26,7 +26,7 @@ #include "sg_cmds_basic.h" #include "sg_cmds_extra.h" -static const char * version_str = "1.06 20140516"; +static const char * version_str = "1.07 20141021"; #define ME "sg_write_same: " @@ -461,8 +461,8 @@ main(int argc, char * argv[]) vb = op->verbose; if ((! if_given) && (! lba_given) && (! num_given)) { - fprintf(stderr, "As a precaution require one of '--in=', '--lba=' " - "or '--num=' to be given\n"); + fprintf(stderr, "As a precaution, one of '--in=', '--lba=' or " + "'--num=' is required\n"); return SG_LIB_SYNTAX_ERROR; } diff --git a/src/sg_write_verify.c b/src/sg_write_verify.c new file mode 100644 index 0000000..e3ae094 --- /dev/null +++ b/src/sg_write_verify.c @@ -0,0 +1,611 @@ +/* + * Copyright (c) 2014 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. + * + * This program issues the SCSI command WRITE AND VERIFY to a given SCSI + * device. It sends the command with the logical block address passed as the + * LBA argument, for the given number of blocks. The number of bytes sent is + * supplied separately, either by the size of the given file (IF) or + * explicitly with ILEN. + * + * This code was contributed by Bruno Goncalves + */ + +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <getopt.h> +#include <sys/types.h> +#include <sys/stat.h> +#define __STDC_FORMAT_MACROS 1 +#include <inttypes.h> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "sg_lib.h" +#include "sg_pt.h" +#include "sg_cmds_basic.h" + +static const char * version_str = "1.05 20141107"; + + +#define ME "sg_write_verify: " + +#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */ + +#define WRITE_VERIFY10_CMD 0x2e +#define WRITE_VERIFY10_CMDLEN 10 +#define WRITE_VERIFY16_CMD 0x8e +#define WRITE_VERIFY16_CMDLEN 16 + +#define WRPROTECT_MASK (0x7) +#define WRPROTECT_SHIFT (5) + +#define DEF_TIMEOUT_SECS 60 + + +static struct option long_options[] = { + {"16", no_argument, 0, 'S'}, + {"bytchk", required_argument, 0, 'b'}, + {"dpo", no_argument, 0, 'd'}, + {"group", required_argument, 0, 'g'}, + {"help", no_argument, 0, 'h'}, + {"ilen", required_argument, 0, 'I'}, + {"in", required_argument, 0, 'i'}, + {"lba", required_argument, 0, 'l'}, + {"num", required_argument, 0, 'n'}, + {"repeat", no_argument, 0, 'R'}, + {"timeout", required_argument, 0, 't'}, + {"verbose", no_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, + {"wrprotect", required_argument, 0, 'w'}, + {0, 0, 0, 0}, +}; + + +static void +usage() +{ + fprintf(stderr, "Usage: " + "sg_write_verify [--16] [--bytchk=BC] [--dpo] [--group=GN] " + "[--help]\n" + " [--ilen=IL] [--in=IF] --lba=LBA " + "[--num=NUM]\n" + " [--repeat] [--timeout=TO] [--verbose] " + "[--version]\n" + " [--wrprotect=WPR] DEVICE\n" + " where:\n" + " --16|-S do WRITE AND VERIFY(16) (default: 10)\n" + " --bytchk=BC|-b BC set BYTCHK field (default: 0)\n" + " --dpo|-d set DPO bit (default: 0)\n" + " --group=GN|-g GN GN is group number (default: 0)\n" + " --help|-h print out usage message\n" + " --ilen=IL| -I IL input (file) length in bytes, becomes " + "data-out\n" + " buffer length (def: deduced from IF " + "size)\n" + " --in=IF|-i IF IF is a file containing the data to " + "be written\n" + " --lba=LBA|-l LBA LBA of the first block to write " + "and verify;\n" + " no default, must be given\n" + " --num=NUM|-n NUM number of logical blocks to write and " + "verify\n" + " --repeat|-R while IF still has data to read, send " + "another\n" + " command, bumping LBA with up to NUM " + "blocks again\n" + " --timeout=TO|-t TO command timeout in seconds (def: 60)\n" + " --verbose|-v increase verbosity\n" + " --version|-V print version string then exit\n" + " --wrprotect|-w WPR WPR is the WRPROTECT field value " + "(def: 0)\n\n" + "Performs a SCSI WRITE AND VERIFY (10 or 16) command on DEVICE, " + "startings\nat LBA for NUM logical blocks. More commands " + "performed only if '--repeat'\noption given. Data to be written " + "is fetched from the IF file.\n" + ); +} + +/* Invokes a SCSI WRITE AND VERIFY according with CDB. Returns 0 -> success, + * various SG_LIB_CAT_* positive values or -1 -> other errors */ +static int +run_scsi_transaction(int sg_fd, const unsigned char *cdbp, int cdb_len, + unsigned char *dop, int do_len, int timeout, int verbose) +{ + int res, k, sense_cat, ret; + unsigned char sense_b[SENSE_BUFF_LEN]; + int noisy = 1; + struct sg_pt_base * ptvp; + char b[32]; + + snprintf(b, sizeof(b), "Write and verify(%d)", cdb_len); + if (verbose) { + fprintf(stderr, " %s cmd: ", b); + for (k = 0; k < cdb_len; ++k) + fprintf(stderr, "%02x ", cdbp[k]); + fprintf(stderr, "\n"); + if ((verbose > 2) && dop && do_len) { + fprintf(stderr, " Data out buffer [%d bytes]:\n", do_len); + dStrHexErr((const char *)dop, do_len, -1); + } + } + ptvp = construct_scsi_pt_obj(); + if (NULL == ptvp) { + fprintf(stderr, "%s: out of memory\n", b); + return -1; + } + set_scsi_pt_cdb(ptvp, cdbp, cdb_len); + set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); + set_scsi_pt_data_out(ptvp, dop, do_len); + res = do_scsi_pt(ptvp, sg_fd, timeout, verbose); + ret = sg_cmds_process_resp(ptvp, b, res, 0, sense_b, noisy, verbose, + &sense_cat); + if (-1 == ret) + ; + else if (-2 == ret) { + switch (sense_cat) { + case SG_LIB_CAT_RECOVERED: + case SG_LIB_CAT_NO_SENSE: + ret = 0; + break; + case SG_LIB_CAT_MEDIUM_HARD: /* write or verify failed */ + { + int valid, slen; + uint64_t ull = 0; + + slen = get_scsi_pt_sense_len(ptvp); + valid = sg_get_sense_info_fld(sense_b, slen, &ull); + if (valid) + fprintf(stderr, "Medium or hardware error starting at " + "lba=%" PRIu64 " [0x%" PRIx64 "]\n", ull, ull); + } + ret = sense_cat; + break; + case SG_LIB_CAT_PROTECTION: /* PI failure */ + case SG_LIB_CAT_MISCOMPARE: /* only in bytchk=1 case */ + default: + ret = sense_cat; + break; + } + } else + ret = 0; + + destruct_scsi_pt_obj(ptvp); + return ret; +} + +/* Invokes a SCSI WRITE AND VERIFY (10) command (SBC). Returns 0 -> success, +* various SG_LIB_CAT_* positive values or -1 -> other errors */ +static int +sg_ll_write_verify10(int sg_fd, int wrprotect, int dpo, int bytchk, + unsigned int lba, int num_lb, int group, + unsigned char *dop, int do_len, int timeout, int verbose) +{ + int ret; + unsigned char wv_cdb[WRITE_VERIFY10_CMDLEN]; + + memset(wv_cdb, 0, WRITE_VERIFY10_CMDLEN); + wv_cdb[0] = WRITE_VERIFY10_CMD; + wv_cdb[1] = ((wrprotect & WRPROTECT_MASK) << WRPROTECT_SHIFT); + if (dpo) + wv_cdb[1] |= 0x10; + if (bytchk) + wv_cdb[1] |= ((bytchk & 0x3) << 1); + + wv_cdb[2] = (lba >> 24) & 0xff; + wv_cdb[3] = (lba >> 16) & 0xff; + wv_cdb[4] = (lba >> 8) & 0xff; + wv_cdb[5] = lba & 0xff; + wv_cdb[6] = group & 0x1f; + wv_cdb[7] = (num_lb >> 8) & 0xff; + wv_cdb[8] = num_lb & 0xff; + ret = run_scsi_transaction(sg_fd, wv_cdb, sizeof(wv_cdb), dop, do_len, + timeout, verbose); + return ret; +} + +/* Invokes a SCSI WRITE AND VERIFY (16) command (SBC). Returns 0 -> success, +* various SG_LIB_CAT_* positive values or -1 -> other errors */ +static int +sg_ll_write_verify16(int sg_fd, int wrprotect, int dpo, int bytchk, + uint64_t llba, int num_lb, int group, unsigned char *dop, + int do_len, int timeout, int verbose) +{ + int ret; + unsigned char wv_cdb[WRITE_VERIFY16_CMDLEN]; + + + memset(wv_cdb, 0, sizeof(wv_cdb)); + wv_cdb[0] = WRITE_VERIFY16_CMD; + wv_cdb[1] = ((wrprotect & WRPROTECT_MASK) << WRPROTECT_SHIFT); + if (dpo) + wv_cdb[1] |= 0x10; + if (bytchk) + wv_cdb[1] |= ((bytchk & 0x3) << 1); + + wv_cdb[2] = (llba >> 56) & 0xff; + wv_cdb[3] = (llba >> 48) & 0xff; + wv_cdb[4] = (llba >> 40) & 0xff; + wv_cdb[5] = (llba >> 32) & 0xff; + wv_cdb[6] = (llba >> 24) & 0xff; + wv_cdb[7] = (llba >> 16) & 0xff; + wv_cdb[8] = (llba >> 8) & 0xff; + wv_cdb[9] = llba & 0xff; + wv_cdb[10] = (num_lb >> 24) & 0xff; + wv_cdb[11] = (num_lb >> 16) & 0xff; + wv_cdb[12] = (num_lb >> 8) & 0xff; + wv_cdb[13] = num_lb & 0xff; + wv_cdb[14] = group & 0x1f; + ret = run_scsi_transaction(sg_fd, wv_cdb, sizeof(wv_cdb), dop, do_len, + timeout, verbose); + return ret; +} + +static int +open_if(const char * fn, int got_stdin) +{ + int fd; + + if (got_stdin) + fd = STDIN_FILENO; + else { + fd = open(fn, O_RDONLY); + if (fd < 0) { + fprintf(stderr, ME "open error: %s: %s\n", fn, + safe_strerror(errno)); + return -SG_LIB_FILE_ERROR; + } + } + if (sg_set_binary_mode(fd) < 0) { + perror("sg_set_binary_mode"); + return -SG_LIB_FILE_ERROR; + } + return fd; +} + +int +main(int argc, char * argv[]) +{ + int sg_fd, res, c, n, first_time; + unsigned char * wvb = NULL; + void * wrkBuff = NULL; + int dpo = 0; + int bytchk = 0; + int group = 0; + int do_16 = 0; + int given_do_16 = 0; + uint64_t llba = 0; + int lba_given = 0; + uint32_t num_lb = 1; + uint32_t snum_lb = 1; + int repeat = 0; + int timeout = DEF_TIMEOUT_SECS; + int verbose = 0; + int64_t ll; + int wrprotect = 0; + const char * device_name = NULL; + const char * ifnp; + int has_filename = 0; + int ilen = -1; + int ifd = -1; + int ret = 1; + int b_p_lb = 512; + int tnum_lb_wr = 0; + char cmd_name[32]; + + ifnp = ""; /* keep MinGW quiet */ + while (1) { + int option_index = 0; + + c = getopt_long(argc, argv, "b:dg:hi:I:l:n:RSt:w:vV", long_options, + &option_index); + if (c == -1) + break; + + switch (c) { + case 'b': + /* Only bytchk=0 and =1 are meaningful for this command in + * sbc4r02 (not =2 nor =3) but that may change in the future. */ + bytchk = sg_get_num(optarg); + if ((bytchk < 0) || (bytchk > 3)) { + fprintf(stderr, "argument to '--bytchk' expected to be 0 " + "to 3\n"); + return SG_LIB_SYNTAX_ERROR; + } + break; + case 'd': + dpo = 1; + break; + case 'g': + group = sg_get_num(optarg); + if ((group < 0) || (group > 31)) { + fprintf(stderr, "argument to '--group' expected to be 0 " + "to 31\n"); + return SG_LIB_SYNTAX_ERROR; + } + break; + case 'h': + case '?': + usage(); + return 0; + case 'i': + ifnp = optarg; + has_filename = 1; + break; + case 'I': + ilen = sg_get_num(optarg); + if (-1 == ilen) { + fprintf(stderr, "bad argument to '--ilen'\n"); + return SG_LIB_SYNTAX_ERROR; + } + break; + case 'l': + if (lba_given) { + fprintf(stderr, "must have one and only one '--lba'\n"); + return SG_LIB_SYNTAX_ERROR; + } + ll = sg_get_llnum(optarg); + if (ll < 0) { + fprintf(stderr, "bad argument to '--lba'\n"); + return SG_LIB_SYNTAX_ERROR; + } + llba = (uint64_t)ll; + ++lba_given; + break; + case 'n': + n = sg_get_num(optarg); + if (-1 == n) { + fprintf(stderr, "bad argument to '--num'\n"); + return SG_LIB_SYNTAX_ERROR; + } + num_lb = (uint32_t)n; + break; + case 'R': + ++repeat; + break; + case 'S': + do_16 = 1; + given_do_16 = 1; + break; + case 't': + timeout = sg_get_num(optarg); + if (timeout < 1) { + fprintf(stderr, "bad argument to '--timeout'\n"); + return SG_LIB_SYNTAX_ERROR; + } + break; + case 'v': + ++verbose; + break; + case 'V': + fprintf(stderr, ME "version: %s\n", version_str); + return 0; + case 'w': + wrprotect = sg_get_num(optarg); + if ((wrprotect < 0) || (wrprotect > 7)) { + fprintf(stderr, "wrprotect (%d) is out of range ( < %d)\n", + wrprotect, 7); + return SG_LIB_SYNTAX_ERROR; + } + + break; + default: + fprintf(stderr, "unrecognised option code 0x%x ??\n", c); + usage(); + return SG_LIB_SYNTAX_ERROR; + } + } + if (optind < argc) { + if (NULL == device_name) { + device_name = argv[optind]; + ++optind; + } + if (optind < argc) { + for (; optind < argc; ++optind) + fprintf(stderr, "Unexpected extra argument: %s\n", + argv[optind]); + usage(); + return SG_LIB_SYNTAX_ERROR; + } + } + + if (NULL == device_name) { + fprintf(stderr, "missing device name!\n"); + usage(); + return SG_LIB_SYNTAX_ERROR; + } + if (! lba_given) { + fprintf(stderr, "need a --lba=LBA option\n"); + usage(); + return SG_LIB_SYNTAX_ERROR; + } + if (repeat) { + if (! has_filename) { + fprintf(stderr, "with '--repeat' need '--in=IF' option\n"); + usage(); + return SG_LIB_SYNTAX_ERROR; + } + if (ilen < 1) { + fprintf(stderr, "with '--repeat' need '--ilen=ILEN' option\n"); + usage(); + return SG_LIB_SYNTAX_ERROR; + } else { + b_p_lb = ilen / num_lb; + if (b_p_lb < 64) { + fprintf(stderr, "calculated %d bytes per logical block, " + "too small\n", b_p_lb); + usage(); + return SG_LIB_SYNTAX_ERROR; + } + } + } + + sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose); + if (sg_fd < 0) { + fprintf(stderr, ME "open error: %s: %s\n", device_name, + safe_strerror(-sg_fd)); + return SG_LIB_FILE_ERROR; + } + + if ((0 == do_16) && (llba > UINT_MAX)) + do_16 = 1; + if ((0 == do_16) && (num_lb > 0xffff)) + do_16 = 1; + snprintf(cmd_name, sizeof(cmd_name), "Write and verify(%d)", + (do_16 ? 16 : 10)); + if (verbose && (0 == given_do_16) && do_16) + fprintf(stderr, "Switching to %s because LBA or NUM too large\n", + cmd_name); + if (verbose) { + fprintf(stderr, "Issue %s to device %s\n\tilen=%d", cmd_name, + device_name, ilen); + if (ilen > 0) + fprintf(stderr, " [0x%x]", ilen); + fprintf(stderr, ", lba=%" PRIu64 " [0x%" PRIx64 "]\n\twrprotect=%d, " + "dpo=%d, bytchk=%d, group=%d, repeat=%d\n", llba, llba, + wrprotect, dpo, bytchk, group, repeat); + } + + first_time = 1; + do { + if (first_time) { + //If a file with data to write has been provided + if (has_filename) { + struct stat a_stat; + + if ((1 == strlen(ifnp)) && ('-' == ifnp[0])) { + ifd = STDIN_FILENO; + ifnp = "<stdin>"; + if (verbose > 1) + fprintf(stderr, "Reading input data from stdin\n"); + } else { + ifd = open_if(ifnp, 0); + if (ifd < 0) { + ret = -ifd; + goto err_out; + } + } + if (ilen < 1) { + if (fstat(ifd, &a_stat) < 0) { + fprintf(stderr, "Could not fstat(%s)\n", ifnp); + goto err_out; + } + if (! S_ISREG(a_stat.st_mode)) { + fprintf(stderr, "Cannot determine IF size, please " + "give '--ilen='\n"); + goto err_out; + } + ilen = (int)a_stat.st_size; + if (ilen < 1) { + fprintf(stderr, "%s file size too small\n", ifnp); + goto err_out; + } else if (verbose) + fprintf(stderr, "Using file size of %d bytes\n", ilen); + } + if (NULL == (wrkBuff = malloc(ilen))) { + fprintf(stderr, ME "out of memory\n"); + ret = SG_LIB_CAT_OTHER; + goto err_out; + } + wvb = (unsigned char *)wrkBuff; + res = read(ifd, wvb, ilen); + if (res < 0) { + fprintf(stderr, "Could not read from %s", ifnp); + goto err_out; + } + if (res < ilen) { + fprintf(stderr, "Read only %d bytes (expected %d) from " + "%s\n", res, ilen, ifnp); + if (repeat) + fprintf(stderr, "Will scale subsequent pieces when " + "repeat=1, but this is first\n"); + goto err_out; + } + } else { + if (ilen < 1) { + if (verbose) + fprintf(stderr, "Default write length to %d*%d=%d " + "bytes\n", num_lb, 512, 512 * num_lb); + ilen = 512 * num_lb; + } + if (NULL == (wrkBuff = malloc(ilen))) { + fprintf(stderr, ME "out of memory\n"); + ret = SG_LIB_CAT_OTHER; + goto err_out; + } + wvb = (unsigned char *)wrkBuff; + /* Not sure about this: default contents to 0xff bytes */ + memset(wrkBuff, 0xff, ilen); + } + first_time = 0; + snum_lb = num_lb; + } else { /* repeat=1, first_time=0, must be reading file */ + llba += snum_lb; + res = read(ifd, wvb, ilen); + if (res < 0) { + fprintf(stderr, "Could not read from %s", ifnp); + goto err_out; + } else { + if (verbose > 1) + fprintf(stderr, "Subsequent read from %s got %d bytes\n", + ifnp, res); + if (0 == res) + break; + if (res < ilen) { + snum_lb = (uint32_t)(res / b_p_lb); + n = res % b_p_lb; + if (0 != n) + fprintf(stderr, ">>> warning: ignoring last %d " + "bytes of %s\n", n, ifnp); + if (snum_lb < 1) + break; + } + } + } + if (do_16) + res = sg_ll_write_verify16(sg_fd, wrprotect, dpo, bytchk, llba, + snum_lb, group, wvb, ilen, timeout, + verbose); + else + res = sg_ll_write_verify10(sg_fd, wrprotect, dpo, bytchk, + (unsigned int)llba, snum_lb, group, + wvb, ilen, timeout, verbose); + ret = res; + if (repeat && (0 == ret)) + tnum_lb_wr += snum_lb; + if (ret || (snum_lb != num_lb)) + break; + } while (repeat); + +err_out: + if (repeat) + fprintf(stderr, "%d [0x%x] logical blocks written, in total\n", + tnum_lb_wr, tnum_lb_wr); + if (wrkBuff) + free(wrkBuff); + if ((ifd >= 0) && (STDIN_FILENO != ifd)) + close(ifd); + res = sg_cmds_close_device(sg_fd); + if (res < 0) { + fprintf(stderr, "close error: %s\n", safe_strerror(-res)); + if (0 == ret) + return SG_LIB_FILE_ERROR; + } + if (ret && (0 == verbose)) { + if (SG_LIB_CAT_INVALID_OP == ret) + fprintf(stderr, "%s command not supported\n", cmd_name); + else if (ret > 0) + fprintf(stderr, "%s, exit status %d\n", cmd_name, ret); + else if (ret < 0) + fprintf(stderr, "Some error occurred\n"); + } + return (ret >= 0) ? ret : SG_LIB_CAT_OTHER; +} diff --git a/src/sg_xcopy.c b/src/sg_xcopy.c index 5b1f4cd..ef23959 100644 --- a/src/sg_xcopy.c +++ b/src/sg_xcopy.c @@ -62,7 +62,7 @@ #include "sg_cmds_extra.h" #include "sg_io_linux.h" -static const char * version_str = "0.45 20140516"; +static const char * version_str = "0.46 20140625"; #define ME "sg_xcopy: " @@ -749,8 +749,8 @@ scsi_operating_parameter(struct xcopy_fp_t *xfp, int is_target) len = ((rcBuff[0] << 24) | (rcBuff[1] << 16) | (rcBuff[2] << 8) | rcBuff[3]) + 4; if (len > rcBuffLen) { - pr2serr(" <<report too long for internal buffer, output " - "truncated\n"); + pr2serr(" <<report len %d > %d too long for internal buffer, output " + "truncated\n", len, rcBuffLen); } if (verbose > 2) { pr2serr("\nOutput response in hex:\n"); diff --git a/suse/sg3_utils.spec b/suse/sg3_utils.spec index b460c13..c743fa7 100644 --- a/suse/sg3_utils.spec +++ b/suse/sg3_utils.spec @@ -24,7 +24,7 @@ Name: sg3_utils %define lname libsgutils2-2 -Version: 1.38 +Version: 1.40 Release: 0 Summary: A collection of tools that send SCSI commands to devices License: GPL-2.0+ and BSD-3-Clause diff --git a/utils/Makefile b/utils/Makefile index fb9b6ea..aa11a7d 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -13,8 +13,8 @@ EXTRA_EXECS = hxascdmp sg_chk_asc tst_sg_lib MAN_PGS = hxascdmp.1 MAN_PREF = man1 -CFLAGS = -g -O2 -W -Wall -I ../include -# CFLAGS = -g -O2 -W -I ../include -pedantic -std=c99 +CFLAGS = -g -O2 -W -Wall -iquote ../include +# CFLAGS = -g -O2 -W -iquote ../include -pedantic -std=c99 LDFLAGS = diff --git a/utils/tst_sg_lib.c b/utils/tst_sg_lib.c index 1c4fa7b..188cf03 100644 --- a/utils/tst_sg_lib.c +++ b/utils/tst_sg_lib.c @@ -119,6 +119,7 @@ main(int argc, char * argv[]) int verbose = 0; int ret = 0; char b[2048]; + char bb[128]; while (1) { int option_index = 0; @@ -275,6 +276,8 @@ main(int argc, char * argv[]) dStrHex(b, k, 0); dStrHex(b, k, 1); dStrHex(b, k, -1); + dStrHexStr(b, k, "dStrHexStr:^", 0, sizeof(bb), bb); + printf("%s", bb); printf("\n"); } } |
