aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BSD_LICENSE2
-rw-r--r--ChangeLog8
-rw-r--r--config.h.in3
-rwxr-xr-xconfigure24
-rw-r--r--configure.ac13
-rw-r--r--debian/changelog2
-rw-r--r--doc/sg3_utils.84
-rw-r--r--doc/sg_raw.810
-rw-r--r--doc/sg_read_buffer.87
-rw-r--r--include/freebsd_nvme_ioctl.h11
-rw-r--r--include/sg_lib.h14
-rw-r--r--include/sg_linux_inc.h1
-rw-r--r--lib/BSD_LICENSE2
-rw-r--r--lib/sg_lib.c79
-rw-r--r--lib/sg_lib_data.c10
-rw-r--r--lib/sg_pt_freebsd.c7
-rw-r--r--sg3_utils.spec2
-rw-r--r--src/BSD_LICENSE4
-rw-r--r--src/sg_format.c4
-rw-r--r--src/sg_inq.c8
-rw-r--r--src/sg_logs.c7
-rw-r--r--src/sg_opcodes.c8
-rw-r--r--src/sg_read_buffer.c7
-rw-r--r--src/sg_vpd.c40
-rw-r--r--testing/Makefile5
-rw-r--r--testing/sg_queue_tst.c73
-rw-r--r--testing/sg_tst_async.cpp10
-rw-r--r--testing/sg_tst_bidi.c566
-rw-r--r--testing/sg_tst_ioctl.c14
-rw-r--r--testing/sgh_dd.c18
-rw-r--r--testing/sgs_dd.c415
-rw-r--r--testing/tst_sg_lib.c6
32 files changed, 1197 insertions, 187 deletions
diff --git a/BSD_LICENSE b/BSD_LICENSE
index 609967d6..eef2b043 100644
--- a/BSD_LICENSE
+++ b/BSD_LICENSE
@@ -1,5 +1,5 @@
-Copyright (c) 1999-2018, Douglas Gilbert
+Copyright (c) 1999-2019, Douglas Gilbert
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/ChangeLog b/ChangeLog
index f85461a9..a3e639fd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,18 +2,22 @@ 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.45 [20181228] [svn: r803]
+Changelog for sg3_utils-1.45 [20190110] [svn: r804]
- sg_ses: bug: --page= being overridden when --control
and --data= also given; fix
- - sg_opcodes: expand MLU (18-102r0)
+ - sg_opcodes: expand MLU (spc5r20)
- sg_write_buffer: allow comma and period separated
lists when input from stdin
- sg_format: add --dcrt used twice (FOV=1 DCRT=0)
- sg_raw: fix --send bug when using stdin
+ - sg_vpd: 3pc VPD page add copy group descriptor
- sg_scan (win32): expand limits for big arrays
+ - sg_lib: add sg_t10_uuid_desig2str()
+ - sg_pt_freebsd: fixes for FreeBSD 12.0 release
- rescan-scsi-bus: widen LUN 0 only scanning
- testing/sg_tst_async: fix free_list issue
- testing/sg_tst_ioctl: for sg 4.0 driver
+ - testing/sg_tst_bidi: for sg 4.0 driver
- testing/sgs_dd: for share in sg 4.0 driver
- testing/sgh_dd: rename of sgs_dd to avoid ...
- testing/sgs_dd: back from archive, for testing
diff --git a/config.h.in b/config.h.in
index 1aa1e952..4173c074 100644
--- a/config.h.in
+++ b/config.h.in
@@ -27,6 +27,9 @@
/* Define to 1 if you have the <linux/nvme_ioctl.h> header file. */
#undef HAVE_LINUX_NVME_IOCTL_H
+/* Have Linux sg v4 header */
+#undef HAVE_LINUX_SG_V4_HDR
+
/* Define to 1 if you have the <linux/types.h> header file. */
#undef HAVE_LINUX_TYPES_H
diff --git a/configure b/configure
index b73d8102..83e174e0 100755
--- a/configure
+++ b/configure
@@ -12588,6 +12588,28 @@ done
}
+check_for_linux_sg_v4_hdr() {
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+ # include <scsi/sg.h>
+ #ifdef SG_IOSUBMIT
+ found
+ #endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "found" >/dev/null 2>&1; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LINUX_SG_V4_HDR 1
+_ACEOF
+
+fi
+rm -f conftest*
+
+
+}
+
case "${host}" in
*-*-android*)
@@ -12600,6 +12622,7 @@ cat >>confdefs.h <<_ACEOF
#define SG_LIB_LINUX 1
_ACEOF
+ check_for_linux_sg_v4_hdr
check_for_linux_nvme_headers;;
*-*-freebsd*|*-*-kfreebsd*-gnu*)
@@ -12662,6 +12685,7 @@ cat >>confdefs.h <<_ACEOF
#define SG_LIB_LINUX 1
_ACEOF
+ check_for_linux_sg_v4_hdr
check_for_linux_nvme_headers;;
esac
diff --git a/configure.ac b/configure.ac
index c9dbd4f4..8d641eb6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -59,10 +59,22 @@ check_for_linux_nvme_headers() {
]])
}
+check_for_linux_sg_v4_hdr() {
+ AC_EGREP_CPP(found,
+ [ # include <scsi/sg.h>
+ #ifdef SG_IOSUBMIT
+ found
+ #endif
+ ],
+ [AC_DEFINE_UNQUOTED(HAVE_LINUX_SG_V4_HDR, 1, [Have Linux sg v4 header]) ])
+
+}
+
case "${host}" in
*-*-android*)
AC_DEFINE_UNQUOTED(SG_LIB_ANDROID, 1, [sg3_utils on android])
AC_DEFINE_UNQUOTED(SG_LIB_LINUX, 1, [sg3_utils on linux])
+ check_for_linux_sg_v4_hdr
check_for_linux_nvme_headers;;
*-*-freebsd*|*-*-kfreebsd*-gnu*)
AC_DEFINE_UNQUOTED(SG_LIB_FREEBSD, 1, [sg3_utils on FreeBSD])
@@ -85,6 +97,7 @@ case "${host}" in
CFLAGS="$CFLAGS -D__USE_MINGW_ANSI_STDIO";;
*-*-linux-gnu* | *-*-linux* | *)
AC_DEFINE_UNQUOTED(SG_LIB_LINUX, 1, [sg3_utils on linux])
+ check_for_linux_sg_v4_hdr
check_for_linux_nvme_headers;;
esac
diff --git a/debian/changelog b/debian/changelog
index b70a85da..8d8bb482 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,7 +2,7 @@ sg3-utils (1.45-0.1) unstable; urgency=low
* New upstream version
- -- Douglas Gilbert <dgilbert@interlog.com> Sun, 23 Dec 2018 10:00:00 -0500
+ -- Douglas Gilbert <dgilbert@interlog.com> Fri, 04 Jan 2018 11:00:00 -0500
sg3-utils (1.44-0.1) unstable; urgency=low
diff --git a/doc/sg3_utils.8 b/doc/sg3_utils.8
index 9bf46d4a..be4a53e8 100644
--- a/doc/sg3_utils.8
+++ b/doc/sg3_utils.8
@@ -1,4 +1,4 @@
-.TH SG3_UTILS "8" "December 2018" "sg3_utils\-1.45" SG3_UTILS
+.TH SG3_UTILS "8" "January 2019" "sg3_utils\-1.45" SG3_UTILS
.SH NAME
sg3_utils \- a package of utilities for sending SCSI commands
.SH SYNOPSIS
@@ -706,7 +706,7 @@ CREDITS file and individual source files (in the 'src' directory).
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 1999\-2018 Douglas Gilbert
+Copyright \(co 1999\-2019 Douglas Gilbert
.br
Some utilities are distributed under a GPL version 2 license while
others, usually more recent ones, are under a FreeBSD license. The files
diff --git a/doc/sg_raw.8 b/doc/sg_raw.8
index 62fa3cc2..3e01bd02 100644
--- a/doc/sg_raw.8
+++ b/doc/sg_raw.8
@@ -1,4 +1,4 @@
-.TH SG_RAW "8" "December 2018" "sg3_utils\-1.45" SG3_UTILS
+.TH SG_RAW "8" "January 2019" "sg3_utils\-1.45" SG3_UTILS
.SH NAME
sg_raw \- send arbitrary SCSI command to a device
.SH SYNOPSIS
@@ -193,7 +193,9 @@ 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
which corresponds to the transfer length of 1 (block) in the cdb (i.e.
-the second last byte).
+the second last byte). urandom.bin can be produced like this:
+.br
+dd if=/dev/urandom bs=512 count=1 of=urandom.bin
.TP
sg_raw.exe PhysicalDrive1 a1 0c 0e 00 00 00 00 00 00 e0 00 00
This example is from Windows and shows a ATA STANDBY IMMEDIATE command
@@ -208,9 +210,9 @@ the sg3_utils(8) man page.
.SH AUTHOR
Written by Ingo van Lil
.SH "REPORTING BUGS"
-Report bugs to <inguin at gmx dot de>.
+Report bugs to <inguin at gmx dot de> or to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2001\-2018 Ingo van Lil
+Copyright \(co 2001\-2019 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.
diff --git a/doc/sg_read_buffer.8 b/doc/sg_read_buffer.8
index fd9296cf..d133cce0 100644
--- a/doc/sg_read_buffer.8
+++ b/doc/sg_read_buffer.8
@@ -1,4 +1,4 @@
-.TH SG_READ_BUFFER "8" "May 2014" "sg3_utils\-1.39" SG3_UTILS
+.TH SG_READ_BUFFER "8" "January 2019" "sg3_utils\-1.45" SG3_UTILS
.SH NAME
sg_read_buffer \- send SCSI READ BUFFER command
.SH SYNOPSIS
@@ -87,6 +87,9 @@ Echo buffer descriptor: yields 4 bytes of which the last (lowest) 13 bits
represent the echo buffer capacity. The maximum echo buffer size is 4096
bytes.
.TP
+rd_microc_st [15, 0xf]
+Read microcode status. Added in spc5r20 .
+.TP
en_ex [26, 0x1a]
Enable expander communications protocol and Echo buffer. Made obsolete in
SPC\-4.
@@ -105,7 +108,7 @@ Written by Luben Tuikov and Douglas Gilbert.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2006\-2014 Luben Tuikov and Douglas Gilbert
+Copyright \(co 2006\-2019 Luben Tuikov and 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/include/freebsd_nvme_ioctl.h b/include/freebsd_nvme_ioctl.h
index a7c970b9..0b79d851 100644
--- a/include/freebsd_nvme_ioctl.h
+++ b/include/freebsd_nvme_ioctl.h
@@ -39,6 +39,11 @@ extern "C" {
#define NVME_PASSTHROUGH_CMD _IOWR('n', 0, struct nvme_pt_command)
#if __FreeBSD_version < 1100110
+
+#define NVME_STATUS_GET_SC(st) (st.sc)
+#define NVME_STATUS_GET_SCT(st) (st.sct)
+
+
struct nvme_command
{
/* dword 0 */
@@ -151,12 +156,14 @@ struct nvme_pt_command {
*/
struct mtx * driver_lock;
};
-#else
+#else /* not __FreeBSD_version < 1100110 */
#include <dev/nvme/nvme.h>
-#endif
+#endif /* __FreeBSD_version < 1100110 */
+#ifndef nvme_completion_is_error
#define nvme_completion_is_error(cpl) \
((cpl)->status.sc != 0 || (cpl)->status.sct != 0)
+#endif
#define NVME_CTRLR_PREFIX "/dev/nvme"
#define NVME_NS_PREFIX "ns"
diff --git a/include/sg_lib.h b/include/sg_lib.h
index a5f8a3d5..9688991d 100644
--- a/include/sg_lib.h
+++ b/include/sg_lib.h
@@ -2,7 +2,7 @@
#define SG_LIB_H
/*
- * Copyright (c) 2004-2018 Douglas Gilbert.
+ * Copyright (c) 2004-2019 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
@@ -271,6 +271,18 @@ int sg_get_designation_descriptor_str(const char * leadin,
bool print_assoc, bool do_long,
int blen, char * b);
+/* Expects a T10 UUID designator (as found in the Device Identification VPD
+ * page) pointed to by 'dp'. To not produce an error string in 'b', c_set
+ * should be 1 (binary) and dlen should be 18. Currently T10 only supports
+ * locally assigned UUIDs. Writes output to string 'b' of no more than blen
+ * bytes and returns the number of bytes actually written to 'b' but doesn't
+ * count the trailing null character it always appends (if blen > 0). 'lip'
+ * is lead-in string (on each line) than may be NULL. skip_prefix avoids
+ * outputing ' Locally assigned UUID: ' before the UUID. */
+int sg_t10_uuid_desig2str(const uint8_t * dp, int dlen, int c_set,
+ bool do_long, bool skip_prefix,
+ const char * lip, int blen, char * b);
+
/* Yield string associated with peripheral device type (pdt). Returns
* 'buff'. If 'pdt' out of range yields "bad pdt" string. */
char * sg_get_pdt_str(int pdt, int buff_len, char * buff);
diff --git a/include/sg_linux_inc.h b/include/sg_linux_inc.h
index 1f760185..e6f6b523 100644
--- a/include/sg_linux_inc.h
+++ b/include/sg_linux_inc.h
@@ -12,6 +12,7 @@
#include <linux/../scsi/sg.h>
#include <linux/../scsi/scsi.h>
#else
+ #define __user
#include <scsi/sg.h>
#include <scsi/scsi.h>
#endif
diff --git a/lib/BSD_LICENSE b/lib/BSD_LICENSE
index 02f13c10..15e3da3f 100644
--- a/lib/BSD_LICENSE
+++ b/lib/BSD_LICENSE
@@ -1,5 +1,5 @@
-Copyright (c) 1999-2018, Douglas Gilbert
+Copyright (c) 1999-2019, Douglas Gilbert
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/lib/sg_lib.c b/lib/sg_lib.c
index 0a98f0e8..465aa3e0 100644
--- a/lib/sg_lib.c
+++ b/lib/sg_lib.c
@@ -756,6 +756,57 @@ sg_get_desig_type_str(int val)
return NULL;
}
+/* Expects a T10 UUID designator (as found in the Device Identification VPD
+ * page) pointed to by 'dp'. To not produce an error string in 'b', c_set
+ * should be 1 (binary) and dlen should be 18. Currently T10 only supports
+ * locally assigned UUIDs. Writes output to string 'b' of no more than blen
+ * bytes and returns the number of bytes actually written to 'b' but doesn't
+ * count the trailing null character it always appends (if blen > 0). 'lip'
+ * is lead-in string (on each line) than may be NULL. skip_prefix avoids
+ * outputing ' Locally assigned UUID: ' before the UUID. */
+int
+sg_t10_uuid_desig2str(const uint8_t *dp, int dlen, int c_set, bool do_long,
+ bool skip_prefix, const char * lip /* lead-in */,
+ int blen, char * b)
+{
+ int m;
+ int n = 0;
+
+ if (NULL == lip)
+ lip = "";
+ if (1 != c_set) {
+ n += sg_scnpr(b + n, blen - n, "%s << expected binary "
+ "code_set >>\n", lip);
+ n += hex2str(dp, dlen, lip, 0, blen - n, b + n);
+ return n;
+ }
+ if ((1 != ((dp[0] >> 4) & 0xf)) || (18 != dlen)) {
+ n += sg_scnpr(b + n, blen - n, "%s << expected locally "
+ "assigned UUID, 16 bytes long >>\n", lip);
+ n += hex2str(dp, dlen, lip, 0, blen - n, b + n);
+ return n;
+ }
+ if (skip_prefix) {
+ if (strlen(lip) > 0)
+ n += sg_scnpr(b + n, blen - n, "%s", lip);
+ } else
+ n += sg_scnpr(b + n, blen - n, "%s Locally assigned UUID: ",
+ lip);
+ for (m = 0; m < 16; ++m) {
+ if ((4 == m) || (6 == m) || (8 == m) || (10 == m))
+ n += sg_scnpr(b + n, blen - n, "-");
+ n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)dp[2 + m]);
+ }
+ n += sg_scnpr(b + n, blen - n, "\n");
+ if (do_long) {
+ n += sg_scnpr(b + n, blen - n, "%s [0x", lip);
+ for (m = 0; m < 16; ++m)
+ n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)dp[2 + m]);
+ n += sg_scnpr(b + n, blen - n, "]\n");
+ }
+ return n;
+}
+
int
sg_get_designation_descriptor_str(const char * lip, const uint8_t * ddp,
int dd_len, bool print_assoc, bool do_long,
@@ -1104,32 +1155,8 @@ sg_get_designation_descriptor_str(const char * lip, const uint8_t * ddp,
sg_get_trans_proto_str(p_id, sizeof(e), e), lip);
break;
case 0xa: /* UUID identifier */
- if (1 != c_set) {
- n += sg_scnpr(b + n, blen - n, "%s << expected binary "
- "code_set >>\n", lip);
- n += hex2str(ip, dlen, lip, 0, blen - n, b + n);
- break;
- }
- if ((1 != ((ip[0] >> 4) & 0xf)) || (18 != dlen)) {
- n += sg_scnpr(b + n, blen - n, "%s << expected locally "
- "assigned UUID, 16 bytes long >>\n", lip);
- n += hex2str(ip, dlen, lip, 0, blen - n, b + n);
- break;
- }
- n += sg_scnpr(b + n, blen - n, "%s Locally assigned UUID: ",
- lip);
- for (m = 0; m < 16; ++m) {
- if ((4 == m) || (6 == m) || (8 == m) || (10 == m))
- n += sg_scnpr(b + n, blen - n, "-");
- n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)ip[2 + m]);
- }
- n += sg_scnpr(b + n, blen - n, "\n");
- if (do_long) {
- n += sg_scnpr(b + n, blen - n, "%s [0x", lip);
- for (m = 0; m < 16; ++m)
- n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)ip[2 + m]);
- n += sg_scnpr(b + n, blen - n, "]\n");
- }
+ n += sg_t10_uuid_desig2str(ip, dlen, c_set, do_long, false, lip,
+ blen - n, b + n);
break;
default: /* reserved */
n += sg_scnpr(b + n, blen - n, "%s reserved designator=0x%x\n",
diff --git a/lib/sg_lib_data.c b/lib/sg_lib_data.c
index d66b1c7a..5b40fd49 100644
--- a/lib/sg_lib_data.c
+++ b/lib/sg_lib_data.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007-2018 Douglas Gilbert.
+ * Copyright (c) 2007-2019 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
@@ -19,7 +19,7 @@
#include "sg_lib_data.h"
-const char * sg_lib_version_str = "2.58 20180911";/* spc5r19, sbc4r15 */
+const char * sg_lib_version_str = "2.60 20190108";/* spc5r20, sbc4r15 */
/* indexed by pdt; those that map to own index do not decay */
@@ -275,6 +275,7 @@ struct sg_lib_value_name_t sg_lib_read_pos_arr[] = {
/* Maintenance in [0xa3] service actions */
struct sg_lib_value_name_t sg_lib_maint_in_arr[] = {
{0x0, PDT_SAC, "Report assigned/unassigned p_extent"},
+ {0x0, PDT_ADC, "Report automation device attributes"},
{0x1, PDT_SAC, "Report component device"},
{0x2, PDT_SAC, "Report component device attachments"},
{0x3, PDT_SAC, "Report peripheral device"},
@@ -304,6 +305,7 @@ struct sg_lib_value_name_t sg_lib_maint_in_arr[] = {
/* Maintenance out [0xa4] service actions */
struct sg_lib_value_name_t sg_lib_maint_out_arr[] = {
{0x0, PDT_SAC, "Add peripheral device / component device"},
+ {0x0, PDT_ADC, "Set automation device attribute"},
{0x1, PDT_SAC, "Attach to component device"},
{0x2, PDT_SAC, "Exchange p_extent"},
{0x3, PDT_SAC, "Exchange peripheral device / component device"},
@@ -342,6 +344,7 @@ struct sg_lib_value_name_t sg_lib_serv_in12_arr[] = {
/* Service action out(12) [0xa9] service actions */
struct sg_lib_value_name_t sg_lib_serv_out12_arr[] = {
+ {0x1f, PDT_ADC, "Set medium attribute"},
{0xff, 0, "Impossible command name"},
{0xffff, 0, NULL},
};
@@ -882,6 +885,7 @@ struct sg_lib_asc_ascq_t sg_lib_asc_ascq[] =
{0x21,0x06,"Attempt to read invalid data"},
{0x21,0x07,"Read boundary violation"},
{0x21,0x08,"Misaligned write command"},
+ {0x21,0x09,"Attempt to access gap zone"},
{0x22,0x00,"Illegal function (use 20 00, 24 00, or 26 00)"},
{0x23,0x00,"Invalid token operation, cause not reportable"},
{0x23,0x01,"Invalid token operation, unsupported token type"},
@@ -928,6 +932,7 @@ struct sg_lib_asc_ascq_t sg_lib_asc_ascq[] =
{0x26,0x13,"Application tag mode page is invalid"},
{0x26,0x14,"Tape stream mirroring prevented"},
{0x26,0x15,"Copy source or copy destination not authorized"},
+ {0x26,0x16,"Fast copy not possible"},
{0x27,0x00,"Write protected"},
{0x27,0x01,"Hardware write protected"},
{0x27,0x02,"Logical unit software write protected"},
@@ -989,6 +994,7 @@ struct sg_lib_asc_ascq_t sg_lib_asc_ascq[] =
{0x2C,0x0F,"Stream not open"},
{0x2C,0x10,"Unwritten data in zone"},
{0x2C,0x11,"Descriptor format sense data required"},
+ {0x2C,0x12,"Zone is inactive"},
{0x2D,0x00,"Overwrite error on update in place"},
{0x2E,0x00,"Insufficient time for operation"},
{0x2E,0x01,"Command timeout before processing"},
diff --git a/lib/sg_pt_freebsd.c b/lib/sg_pt_freebsd.c
index b4c5e303..73c376bd 100644
--- a/lib/sg_pt_freebsd.c
+++ b/lib/sg_pt_freebsd.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005-2018 Douglas Gilbert.
+ * Copyright (c) 2005-2019 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
@@ -7,7 +7,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
-/* sg_pt_freebsd version 1.32 20180627 */
+/* sg_pt_freebsd version 1.33 20190102 */
#include <stdio.h>
#include <stdlib.h>
@@ -1117,7 +1117,8 @@ nvme_pt_low(struct freebsd_dev_channel *fdc_p, void * dxferp, uint32_t len,
err = ioctl(fdc_p->dev_fd, NVME_PASSTHROUGH_CMD, npcp);
if (err < 0)
return -errno; /* Assume Unix error in normal place */
- sct_sc = ((npcp->cpl.status.sct << 8) | npcp->cpl.status.sc);
+ sct_sc = (NVME_STATUS_GET_SCT(npcp->cpl.status) << 8) |
+ NVME_STATUS_GET_SC(npcp->cpl.status);
fdc_p->nvme_result = npcp->cpl.cdw0;
sg_put_unaligned_le32(npcp->cpl.cdw0,
fdc_p->cq_dw0_3 + SG_NVME_PT_CQ_RESULT);
diff --git a/sg3_utils.spec b/sg3_utils.spec
index fa539670..31e6c3ae 100644
--- a/sg3_utils.spec
+++ b/sg3_utils.spec
@@ -84,7 +84,7 @@ fi
%{_libdir}/*.la
%changelog
-* Sun Dec 23 2018 - dgilbert at interlog dot com
+* Fri Jan 04 2019 - dgilbert at interlog dot com
- track t10 changes
* sg3_utils-1.45
diff --git a/src/BSD_LICENSE b/src/BSD_LICENSE
index 7f1906bb..15e3da3f 100644
--- a/src/BSD_LICENSE
+++ b/src/BSD_LICENSE
@@ -1,5 +1,5 @@
-Copyright (c) 1999-2018, Douglas Gilbert
+Copyright (c) 1999-2019, Douglas Gilbert
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -22,3 +22,5 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+Above is the:
+SPDX-License-Identifier: BSD-2-Clause
diff --git a/src/sg_format.c b/src/sg_format.c
index 092b55d7..a75208d4 100644
--- a/src/sg_format.c
+++ b/src/sg_format.c
@@ -40,7 +40,7 @@
#include "sg_pr2serr.h"
#include "sg_pt.h"
-static const char * version_str = "1.56 20181212";
+static const char * version_str = "1.57 20190107";
#define RW_ERROR_RECOVERY_PAGE 1 /* can give alternate with --mode=MP */
@@ -238,7 +238,7 @@ usage()
"\tExample: sg_format --format /dev/sdc\n\n"
"This utility formats a SCSI disk [FORMAT UNIT] or resizes "
"it. Alternatively\nif '--tape=FM' is given formats a tape "
- "[FORMAT MEDIUM].\n");
+ "[FORMAT MEDIUM].\n\n");
printf("WARNING: This utility will destroy all the data on "
"DEVICE when '--format'\n\t or '--tape' is given. Check that "
"you have specified the correct\n\t DEVICE.\n");
diff --git a/src/sg_inq.c b/src/sg_inq.c
index 61f4fdb3..186e5bf6 100644
--- a/src/sg_inq.c
+++ b/src/sg_inq.c
@@ -1,5 +1,5 @@
/* A utility program originally written for the Linux OS SCSI subsystem.
- * Copyright (C) 2000-2018 D. Gilbert
+ * Copyright (C) 2000-2019 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)
@@ -51,7 +51,7 @@
#include "sg_pt_nvme.h"
#endif
-static const char * version_str = "1.98 20180828"; /* SPC-5 rev 19 */
+static const char * version_str = "1.99 20190109"; /* SPC-5 rev 20 */
/* INQUIRY notes:
* It is recommended that the initial allocation length given to a
@@ -2176,7 +2176,7 @@ decode_x_inq_vpd(uint8_t * buff, int len, int do_hex)
sg_get_unaligned_be16(buff + 10)); /* spc4r27 */
printf(" POA_SUP=%d HRA_SUP=%d VSA_SUP=%d DMS_VALID=%d\n",
!!(buff[12] & 0x80), !!(buff[12] & 0x40), !!(buff[12] & 0x20),
- !!(buff[12] & 0x10)); /* spc4r32 + 17-142r5 */
+ !!(buff[12] & 0x10)); /* spc5r20 */
printf(" Maximum supported sense data length=%d\n",
buff[13]); /* spc4r34 */
/* All byte 14 bits added in spc5r09 */
@@ -2189,7 +2189,7 @@ decode_x_inq_vpd(uint8_t * buff, int len, int do_hex)
sg_get_unaligned_be16(buff + 17)); /* spc5r17 */
printf(" DM_MD_4=%d DM_MD_5=%d DM_MD_6=%d DM_MD_7=%d\n",
!!(buff[19] & 0x80), !!(buff[19] & 0x40), !!(buff[19] & 0x20),
- !!(buff[19] & 0x10)); /* 17-142r5 */
+ !!(buff[19] & 0x10)); /* spc5r20 */
printf(" DM_MD_D=%d DM_MD_E=%d DM_MD_F=%d\n",
!!(buff[19] & 0x8), !!(buff[19] & 0x4), !!(buff[19] & 0x2));
}
diff --git a/src/sg_logs.c b/src/sg_logs.c
index 65eb3947..33a77b38 100644
--- a/src/sg_logs.c
+++ b/src/sg_logs.c
@@ -1,5 +1,5 @@
/* A utility program originally written for the Linux OS SCSI subsystem.
- * Copyright (C) 2000-2018 D. Gilbert
+ * Copyright (C) 2000-2019 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)
@@ -36,7 +36,7 @@
#include "sg_unaligned.h"
#include "sg_pr2serr.h"
-static const char * version_str = "1.69 20180911"; /* spc5r19 + sbc4r11 */
+static const char * version_str = "1.70 20190104"; /* spc5r19 + sbc4r11 */
#define MX_ALLOC_LEN (0xfffc)
#define SHORT_RESP_LEN 128
@@ -57,6 +57,7 @@ static const char * version_str = "1.69 20180911"; /* spc5r19 + sbc4r11 */
#define APP_CLIENT_LPAGE 0xf
#define SELF_TEST_LPAGE 0x10
#define SOLID_STATE_MEDIA_LPAGE 0x11
+#define REQ_RECOVERY_LPAGE 0x13
#define BACKGROUND_SCAN_LPAGE 0x15
#define SAT_ATA_RESULTS_LPAGE 0x16
#define PROTO_SPECIFIC_LPAGE 0x18
@@ -363,7 +364,7 @@ static struct log_elem log_arr[] = {
show_dt_device_status_page}, /* 0x11, 0x0 SSC,ADC */
{0x12, 0, 0, PDT_TAPE, MVP_STD, "Tape alert response", "tar",
show_tapealert_response_page}, /* 0x12, 0x0 SSC,ADC */
- {0x13, 0, 0, PDT_TAPE, MVP_STD, "Requested recovery", "rr",
+ {REQ_RECOVERY_LPAGE, 0, 0, PDT_TAPE, MVP_STD, "Requested recovery", "rr",
show_requested_recovery_page}, /* 0x13, 0x0 SSC,ADC */
{0x14, 0, 0, PDT_TAPE, MVP_STD, "Device statistics", "ds",
show_device_stats_page}, /* 0x14, 0x0 SSC,ADC */
diff --git a/src/sg_opcodes.c b/src/sg_opcodes.c
index 16d7c6c9..49e2ac1c 100644
--- a/src/sg_opcodes.c
+++ b/src/sg_opcodes.c
@@ -1,5 +1,5 @@
/* A utility program originally written for the Linux OS SCSI subsystem.
- * Copyright (C) 2004-2018 D. Gilbert
+ * Copyright (C) 2004-2019 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)
@@ -33,7 +33,7 @@
#include "sg_pt.h"
-static const char * version_str = "0.64 20180926"; /* spc5r19+ */
+static const char * version_str = "0.65 20190108"; /* spc5r20 */
#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
@@ -59,7 +59,7 @@ static struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"hex", no_argument, 0, 'H'},
{"mask", no_argument, 0, 'm'},
- {"mlu", no_argument, 0, 'M'},
+ {"mlu", no_argument, 0, 'M'}, /* added in spc5r20 */
{"no-inquiry", no_argument, 0, 'n'},
{"no_inquiry", no_argument, 0, 'n'},
{"new", no_argument, 0, 'N'},
@@ -784,7 +784,7 @@ list_all_codes(uint8_t * rsoc_buff, int rsoc_len, struct opts_t * op,
else
printf(" %.2x %.4s %3d %2d %s\n", bp[0],
sa_buff, sg_get_unaligned_be16(bp + 6),
- (byt5 >> 2) & 0x3, name_buff);
+ ((byt5 >> 4) & 0x3) /* MLU */, name_buff);
}
if (op->do_mask) {
int cdb_sz;
diff --git a/src/sg_read_buffer.c b/src/sg_read_buffer.c
index 823fed73..812de8c2 100644
--- a/src/sg_read_buffer.c
+++ b/src/sg_read_buffer.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006-2018 Luben Tuikov and Douglas Gilbert.
+ * Copyright (c) 2006-2019 Luben Tuikov and 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.
@@ -35,7 +35,7 @@
* device.
*/
-static const char * version_str = "1.25 20180523";
+static const char * version_str = "1.26 20190108"; /* spc5r20 */
#ifndef SG_READ_BUFFER_10_CMD
@@ -108,6 +108,7 @@ usage()
#define MODE_DESCRIPTOR 3
#define MODE_ECHO_BUFFER 0x0A
#define MODE_ECHO_BDESC 0x0B
+#define MODE_READ_MICROCODE_ST 0x0F
#define MODE_EN_EX_ECHO 0x1A
#define MODE_ERR_HISTORY 0x1C
@@ -123,6 +124,8 @@ static struct mode_s {
{ "echo", MODE_ECHO_BUFFER, "read data from echo buffer "
"(spc-2)"},
{ "echo_desc", MODE_ECHO_BDESC, "echo buffer descriptor (spc-2)"},
+ { "rd_microc_st", MODE_READ_MICROCODE_ST, "read microcode status "
+ "(spc-5)"},
{ "en_ex", MODE_EN_EX_ECHO,
"enable expander communications protocol and echo buffer (spc-3)"},
{ "err_hist", MODE_ERR_HISTORY, "error history (spc-4)"},
diff --git a/src/sg_vpd.c b/src/sg_vpd.c
index 70e1fddf..04d5110d 100644
--- a/src/sg_vpd.c
+++ b/src/sg_vpd.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006-2018 Douglas Gilbert.
+ * Copyright (c) 2006-2019 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
@@ -40,7 +40,7 @@
*/
-static const char * version_str = "1.48 20180910"; /* spc5r19 + sbc4r15 */
+static const char * version_str = "1.49 20190109"; /* spc5r20 + sbc4r15 */
/* standard VPD pages, in ascending page number order */
#define VPD_SUPPORTED_VPDS 0x0
@@ -1268,7 +1268,7 @@ decode_x_inq_vpd(uint8_t * b, int len, int do_hex, bool do_long,
printf(" POA_SUP=%d\n", !!(b[12] & 0x80)); /* spc4r32 */
printf(" HRA_SUP=%d\n", !!(b[12] & 0x40)); /* spc4r32 */
printf(" VSA_SUP=%d\n", !!(b[12] & 0x20)); /* spc4r32 */
- printf(" DMS_VALID=%d\n", !!(b[12] & 0x10)); /* 17-142r5 */
+ printf(" DMS_VALID=%d\n", !!(b[12] & 0x10)); /* spc5r20 */
printf(" Maximum supported sense data length=%d\n",
b[13]); /* spc4r34 */
printf(" IBS=%d\n", !!(b[14] & 0x80)); /* spc5r09 */
@@ -1280,13 +1280,13 @@ decode_x_inq_vpd(uint8_t * b, int len, int do_hex, bool do_long,
sg_get_unaligned_be16(b + 15)); /* spc5r17 */
printf(" Maximum mode page change logs=%u\n",
sg_get_unaligned_be16(b + 17)); /* spc5r17 */
- printf(" DM_MD_4=%d\n", !!(b[19] & 0x80)); /* 17-142r5 */
- printf(" DM_MD_5=%d\n", !!(b[19] & 0x40)); /* 17-142r5 */
- printf(" DM_MD_6=%d\n", !!(b[19] & 0x20)); /* 17-142r5 */
- printf(" DM_MD_7=%d\n", !!(b[19] & 0x10)); /* 17-142r5 */
- printf(" DM_MD_D=%d\n", !!(b[19] & 0x8)); /* 17-142r5 */
- printf(" DM_MD_E=%d\n", !!(b[19] & 0x4)); /* 17-142r5 */
- printf(" DM_MD_F=%d\n", !!(b[19] & 0x2)); /* 17-142r5 */
+ printf(" DM_MD_4=%d\n", !!(b[19] & 0x80)); /* spc5r20 */
+ printf(" DM_MD_5=%d\n", !!(b[19] & 0x40)); /* spc5r20 */
+ printf(" DM_MD_6=%d\n", !!(b[19] & 0x20)); /* spc5r20 */
+ printf(" DM_MD_7=%d\n", !!(b[19] & 0x10)); /* spc5r20 */
+ printf(" DM_MD_D=%d\n", !!(b[19] & 0x8)); /* spc5r20 */
+ printf(" DM_MD_E=%d\n", !!(b[19] & 0x4)); /* spc5r20 */
+ printf(" DM_MD_F=%d\n", !!(b[19] & 0x2)); /* spc5r20 */
return;
}
printf(" ACTIVATE_MICROCODE=%d SPT=%d GRD_CHK=%d APP_CHK=%d "
@@ -1309,7 +1309,7 @@ decode_x_inq_vpd(uint8_t * b, int len, int do_hex, bool do_long,
sg_get_unaligned_be16(b + 10)); /* spc4r27 */
printf(" POA_SUP=%d HRA_SUP=%d VSA_SUP=%d DMS_VALID=%d\n",
!!(b[12] & 0x80), !!(b[12] & 0x40), !!(b[12] & 0x20),
- !!(b[12] & 0x10)); /* spc4r32 + 17-142r5 */
+ !!(b[12] & 0x10)); /* spc5r20 */
printf(" Maximum supported sense data length=%d\n", b[13]); /* spc4r34 */
printf(" IBS=%d IAS=%d SAC=%d NRD1=%d NRD0=%d\n", !!(b[14] & 0x80),
!!(b[14] & 0x40), !!(b[14] & 0x4), !!(b[14] & 0x2),
@@ -1320,7 +1320,7 @@ decode_x_inq_vpd(uint8_t * b, int len, int do_hex, bool do_long,
sg_get_unaligned_be16(b + 17)); /* spc5r17 */
printf(" DM_MD_4=%d DM_MD_5=%d DM_MD_6=%d DM_MD_7=%d\n",
!!(b[19] & 0x80), !!(b[19] & 0x40), !!(b[19] & 0x20),
- !!(b[19] & 0x10)); /* 17-142r5 */
+ !!(b[19] & 0x10)); /* spc5r20 */
printf(" DM_MD_D=%d DM_MD_E=%d DM_MD_F=%d\n",
!!(b[19] & 0x8), !!(b[19] & 0x4), !!(b[19] & 0x2));
}
@@ -1763,12 +1763,12 @@ static void
decode_3party_copy_vpd(uint8_t * buff, int len, int do_hex, int pdt,
int verbose)
{
- int j, k, m, bump, desc_type, desc_len, sa_len;
+ int j, k, m, bump, desc_type, desc_len, sa_len, blen;
unsigned int u;
const uint8_t * bp;
const char * cp;
uint64_t ull;
- char b[80];
+ char b[120];
if (len < 4) {
pr2serr("Third-party Copy VPD page length too short=%d\n", len);
@@ -1778,6 +1778,7 @@ decode_3party_copy_vpd(uint8_t * buff, int len, int do_hex, int pdt,
hex2stdout(buff, len, -1);
return;
}
+ blen = sizeof(b);
len -= 4;
bp = buff + 4;
for (k = 0; k < len; k += bump, bp += bump) {
@@ -1851,11 +1852,11 @@ decode_3party_copy_vpd(uint8_t * buff, int len, int do_hex, int pdt,
sa_len = bp[6 + j];
for (m = 0; (m < sa_len) && ((j + m) < csll); ++m) {
sg_get_opcode_sa_name(bp[5 + j], bp[7 + j + m],
- pdt, sizeof(b), b);
+ pdt, blen, b);
printf(" %s\n", b);
}
if (0 == sa_len) {
- sg_get_opcode_name(bp[5 + j], pdt, sizeof(b), b);
+ sg_get_opcode_name(bp[5 + j], pdt, blen, b);
printf(" %s\n", b);
} else if (m < sa_len)
pr2serr("Supported service actions list length (%d) "
@@ -1895,6 +1896,13 @@ decode_3party_copy_vpd(uint8_t * buff, int len, int do_hex, int pdt,
printf(" 0x%04x\n", u);
}
break;
+ case 0x000D:
+ printf(" Copy group identifier:\n");
+ u = bp[4];
+ sg_t10_uuid_desig2str(bp + 5, u, 1 /* c_set */, false,
+ false, NULL, blen, b);
+ printf("%s", b);
+ break;
case 0x0106:
printf(" ROD token features:\n");
printf(" Remote tokens: %d\n", bp[4] & 0x0f);
diff --git a/testing/Makefile b/testing/Makefile
index e337e66b..e94a441f 100644
--- a/testing/Makefile
+++ b/testing/Makefile
@@ -5,7 +5,7 @@ INSTDIR=$(DESTDIR)/$(PREFIX)/bin
MANDIR=$(DESTDIR)/$(PREFIX)/man
EXECS = sg_iovec_tst sg_sense_test sg_queue_tst bsg_queue_tst sg_chk_asc \
- sg_tst_nvme sg_tst_ioctl tst_sg_lib sgh_dd sgs_dd
+ sg_tst_nvme sg_tst_ioctl sg_tst_bidi tst_sg_lib sgh_dd sgs_dd
EXTRAS =
@@ -87,6 +87,9 @@ sgh_dd: sgh_dd.o $(LIBFILESNEW)
sgs_dd: sgs_dd.o $(LIBFILESOLD)
$(LD) -o $@ $(LDFLAGS) $^
+sg_tst_bidi: sg_tst_bidi.o $(LIBFILESOLD)
+ $(LD) -o $@ $(LDFLAGS) $^
+
install: $(EXECS)
install -d $(INSTDIR)
for name in $^; \
diff --git a/testing/sg_queue_tst.c b/testing/sg_queue_tst.c
index a3f8dc0c..dcdeabdd 100644
--- a/testing/sg_queue_tst.c
+++ b/testing/sg_queue_tst.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2018 D. Gilbert
+ * Copyright (C) 2010-2019 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)
@@ -15,19 +15,42 @@
* Invocation: sg_queue_tst [-l=Q_LEN] [-t] <sg_device>
* -t queue at tail
*
- * Version 0.93 (20181207)
+ * Version 0.94 (20190109)
*/
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
+#ifndef HAVE_LINUX_SG_V4_HDR
+
+/* Kernel uapi header contain __user decorations on user space pointers
+ * to indicate they are unsafe in the kernel space. However glibc takes
+ * all those __user decorations out from headers in /usr/include/linux .
+ * So to stop compile errors when directly importing include/uapi/scsi/sg.h
+ * undef __user before doing that include. */
+#define __user
+
+/* Want to block the original sg.h header from also being included. That
+ * causes lots of multiple definition errors. This will only work if this
+ * header is included _before_ the original sg.h header. */
+#define _SCSI_GENERIC_H /* original kernel header guard */
+#define _SCSI_SG_H /* glibc header guard */
+
+#include "uapi_sg.h" /* local copy of include/uapi/scsi/sg.h */
+
+#else
+#define __user
+#endif /* end of: ifndef HAVE_LINUX_SG_V4_HDR */
#include "sg_lib.h"
#include "sg_io_linux.h"
@@ -53,9 +76,30 @@
#define DEF_Q_LEN 16 /* max in sg v3 and earlier */
#define MAX_Q_LEN 256
+static void
+set_nanosecs(int sg_fd)
+{
+ struct sg_extended_info sei;
+ struct sg_extended_info * seip;
+
+ seip = &sei;
+ memset(seip, 0, sizeof(*seip));
+ seip->valid_wr_mask |= SG_SEIM_CTL_FLAGS;
+ seip->valid_rd_mask |= SG_SEIM_CTL_FLAGS; /* this or previous optional */
+ seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_TIME_IN_NS;
+ seip->ctl_flags |= SG_CTL_FLAGM_TIME_IN_NS;
+
+ if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
+ fprintf(stderr, "ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n",
+ errno, strerror(errno));
+ }
+}
+
int main(int argc, char * argv[])
{
+ bool q_at_tail = false;
+ bool dur_in_nanosecs = false;
int sg_fd, k, ok;
uint8_t inq_cdb[INQ_CMD_LEN] =
{0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
@@ -67,12 +111,13 @@ int main(int argc, char * argv[])
char * file_name = 0;
char ebuff[EBUFF_SZ];
uint8_t sense_buffer[MAX_Q_LEN][SENSE_BUFFER_LEN];
- int q_at_tail = 0;
int q_len = DEF_Q_LEN;
for (k = 1; k < argc; ++k) {
- if (0 == memcmp("-t", argv[k], 2))
- ++q_at_tail;
+ if (0 == memcmp("-n", argv[k], 2))
+ dur_in_nanosecs = true;
+ else if (0 == memcmp("-t", argv[k], 2))
+ q_at_tail = true;
else if (0 == memcmp("-l=", argv[k], 3)) {
q_len = atoi(argv[k] + 3);
if ((q_len > 511) || (q_len < 1)) {
@@ -96,10 +141,12 @@ int main(int argc, char * argv[])
}
}
if (0 == file_name) {
- printf("Usage: 'sg_queue_tst [-l=Q_LEN] [-t] <sg_device>'\n"
- "where:\n -l=Q_LEN queue length, between 1 and 511 "
+ printf("Usage: 'sg_queue_tst [-l=Q_LEN] [-n] [-t] <sg_device>'\n"
+ "where:\n"
+ " -l=Q_LEN queue length, between 1 and 511 "
"(def: 16)\n"
- " -t queue_at_tail (def: q_at_head)\n");
+ " -n duration in nanosecs (def: milliseconds)\n"
+ " -t queue_at_tail (def: q_at_head)\n");
return 1;
}
@@ -110,6 +157,8 @@ int main(int argc, char * argv[])
perror(ebuff);
return 1;
}
+ if (dur_in_nanosecs)
+ set_nanosecs(sg_fd);
for (k = 0; k < q_len; ++k) {
/* Prepare INQUIRY command */
@@ -172,11 +221,11 @@ int main(int argc, char * argv[])
if (ok) { /* output result if it is available */
/* if (0 == rio_hdr.pack_id) */
if (0 == (rio_hdr.pack_id % 3))
- printf("SEND DIAGNOSTIC %d duration=%u\n", rio_hdr.pack_id,
- rio_hdr.duration);
+ printf("SEND DIAGNOSTIC %d duration=%u %s\n", rio_hdr.pack_id,
+ rio_hdr.duration, (dur_in_nanosecs ? "ns" : "ms"));
else
- printf("INQUIRY %d duration=%u\n", rio_hdr.pack_id,
- rio_hdr.duration);
+ printf("INQUIRY %d duration=%u %s\n", rio_hdr.pack_id,
+ rio_hdr.duration, (dur_in_nanosecs ? "ns" : "ms"));
}
}
diff --git a/testing/sg_tst_async.cpp b/testing/sg_tst_async.cpp
index d16b22fc..6b7a3e84 100644
--- a/testing/sg_tst_async.cpp
+++ b/testing/sg_tst_async.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2018 Douglas Gilbert.
+ * Copyright (c) 2014-2019 Douglas Gilbert.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -61,6 +61,8 @@
#include "config.h"
#endif
+#ifndef HAVE_LINUX_SG_V4_HDR
+
/* Kernel uapi header contain __user decorations on user space pointers
* to indicate they are unsafe in the kernel space. However glibc takes
* all those __user decorations out from headers in /usr/include/linux .
@@ -76,13 +78,17 @@
#include "uapi_sg.h" /* local copy of include/uapi/scsi/sg.h */
+#else
+#define __user
+#endif /* end of: ifndef HAVE_LINUX_SG_V4_HDR */
+
#include "sg_lib.h"
#include "sg_io_linux.h"
#include "sg_unaligned.h"
#include "sg_pt.h"
#include "sg_cmds.h"
-static const char * version_str = "1.23 20181216";
+static const char * version_str = "1.24 20190104";
static const char * util_name = "sg_tst_async";
/* This is a test program for checking the async usage of the Linux sg
diff --git a/testing/sg_tst_bidi.c b/testing/sg_tst_bidi.c
new file mode 100644
index 00000000..7f6f54db
--- /dev/null
+++ b/testing/sg_tst_bidi.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2019 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)
+ * any later version.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Invocation: See usage() function below.
+ *
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef HAVE_LINUX_SG_V4_HDR
+
+/* Kernel uapi header contain __user decorations on user space pointers
+ * to indicate they are unsafe in the kernel space. However glibc takes
+ * all those __user decorations out from headers in /usr/include/linux .
+ * So to stop compile errors when directly importing include/uapi/scsi/sg.h
+ * undef __user before doing that include. */
+#define __user
+
+/* Want to block the original sg.h header from also being included. That
+ * causes lots of multiple definition errors. This will only work if this
+ * header is included _before_ the original sg.h header. */
+#define _SCSI_GENERIC_H /* original kernel header guard */
+#define _SCSI_SG_H /* glibc header guard */
+
+#include "uapi_sg.h" /* local copy of include/uapi/scsi/sg.h */
+
+#else
+#define __user
+#endif /* end of: ifndef HAVE_LINUX_SG_V4_HDR */
+
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+#include "sg_linux_inc.h"
+#include "sg_pr2serr.h"
+
+/* This program tests bidirectional (bidi) SCSI command support in version 4.0
+ * and later of the Linux sg driver. The SBC-3 command XDWRITEREAD(10) that
+ is implemented by the scsi_debug driver is used. */
+
+
+static const char * version_str = "Version: 1.00 20190110";
+
+#define INQ_REPLY_LEN 96
+#define INQ_CMD_OP 0x12
+#define INQ_CMD_LEN 6
+#define SDIAG_CMD_OP 0x1d
+#define SDIAG_CMD_LEN 6
+#define SENSE_BUFFER_LEN 96
+#define XDWRITEREAD_10_OP 0x53
+#define XDWRITEREAD_10_LEN 10
+
+#define EBUFF_SZ 256
+
+#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 DEF_Q_LEN 16 /* max in sg v3 and earlier */
+#define MAX_Q_LEN 256
+
+#define DEF_RESERVE_BUFF_SZ (256 * 1024)
+
+
+static bool ioctl_only = false;
+static bool q_at_tail = false;
+static bool write_only = false;
+
+static int q_len = DEF_Q_LEN;
+static int sleep_secs = 0;
+static int reserve_buff_sz = DEF_RESERVE_BUFF_SZ;
+static int verbose = 0;
+
+static const char * relative_cp = NULL;
+
+
+static void
+usage(void)
+{
+ printf("Usage: 'sg_tst_bidi [-h] [-l=Q_LEN] [-o] [-r=SZ] [-s=SEC] "
+ "[-t]\n"
+ " [-v] [-V] [-w] <sg_device> [<sg_device2>]'\n"
+ " where:\n"
+ " -h help: print usage message then exit\n"
+ " -l=Q_LEN queue length, between 1 and 511 (def: 16)\n"
+ " -o ioctls only, then exit\n"
+ " -r=SZ reserve buffer size in KB (def: 256 --> 256 "
+ "KB)\n"
+ " -s=SEC sleep between writes and reads (def: 0)\n"
+ " -t queue_at_tail (def: q_at_head)\n"
+ " -v increase verbosity of output\n"
+ " -V print version string then exit\n"
+ " -w write (submit) only then exit\n");
+}
+
+static int
+tst_ioctl(int sg_fd, const char * fn2p, int sg_fd2, const char * cp)
+{
+ uint32_t cflags;
+ struct sg_extended_info sei;
+ struct sg_extended_info * seip;
+
+ seip = &sei;
+ memset(seip, 0, sizeof(*seip));
+ seip->valid_wr_mask |= SG_SEIM_RESERVED_SIZE;
+ seip->reserved_sz = reserve_buff_sz;
+ seip->sgat_elem_sz = 64 * 1024;;
+ seip->valid_rd_mask |= SG_SEIM_RESERVED_SIZE;
+ seip->valid_rd_mask |= SG_SEIM_RQ_REM_THRESH;
+ seip->valid_rd_mask |= SG_SEIM_TOT_FD_THRESH;
+ seip->valid_wr_mask |= SG_SEIM_CTL_FLAGS;
+ seip->valid_rd_mask |= SG_SEIM_CTL_FLAGS; /* this or previous optional */
+ seip->valid_rd_mask |= SG_SEIM_MINOR_INDEX;
+ seip->valid_wr_mask |= SG_SEIM_SGAT_ELEM_SZ;
+ seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_TIME_IN_NS;
+ seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_TIME_IN_NS;
+ seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_OTHER_OPENS;
+ seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_ORPHANS;
+ seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_Q_TAIL;
+ seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_IS_SHARE;
+ seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_IS_MASTER;
+ seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_UNSHARE;
+ seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_MASTER_FINI;
+ seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_MASTER_ERR;
+ seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_CHECK_FOR_MORE;
+ seip->ctl_flags |= SG_CTL_FLAGM_TIME_IN_NS;
+
+ if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
+ pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
+ strerror(errno));
+ return 1;
+ }
+#if 1
+ printf("%sSG_SET_GET_EXTENDED ioctl ok\n", cp);
+ if (SG_SEIM_RESERVED_SIZE & seip->valid_rd_mask)
+ printf(" %sreserved size: %u\n", cp, seip->reserved_sz);
+ if (SG_SEIM_MINOR_INDEX & seip->valid_rd_mask)
+ printf(" %sminor index: %u\n", cp, seip->minor_index);
+ if (SG_SEIM_RQ_REM_THRESH & seip->valid_rd_mask)
+ printf(" %srq_rem_sgat_thresh: %u\n", cp, seip->rq_rem_sgat_thresh);
+ if (SG_SEIM_TOT_FD_THRESH & seip->valid_rd_mask)
+ printf(" %stot_fd_thresh: %u\n", cp, seip->tot_fd_thresh);
+ if ((SG_SEIM_CTL_FLAGS & seip->valid_rd_mask) ||
+ (SG_SEIM_CTL_FLAGS & seip->valid_wr_mask)) {
+ cflags = seip->ctl_flags;
+ if (SG_CTL_FLAGM_TIME_IN_NS & seip->ctl_flags_rd_mask)
+ printf(" %sTIME_IN_NS: %s\n", cp,
+ (SG_CTL_FLAGM_TIME_IN_NS & cflags) ? "true" : "false");
+ if (SG_CTL_FLAGM_OTHER_OPENS & seip->ctl_flags_rd_mask)
+ printf(" %sOTHER_OPENS: %s\n", cp,
+ (SG_CTL_FLAGM_OTHER_OPENS & cflags) ? "true" : "false");
+ if (SG_CTL_FLAGM_ORPHANS & seip->ctl_flags_rd_mask)
+ printf(" %sORPHANS: %s\n", cp,
+ (SG_CTL_FLAGM_ORPHANS & cflags) ? "true" : "false");
+ if (SG_CTL_FLAGM_Q_TAIL & seip->ctl_flags_rd_mask)
+ printf(" %sQ_TAIL: %s\n", cp,
+ (SG_CTL_FLAGM_Q_TAIL & cflags) ? "true" : "false");
+ if (SG_CTL_FLAGM_IS_SHARE & seip->ctl_flags_rd_mask)
+ printf(" %sIS_SHARE: %s\n", cp,
+ (SG_CTL_FLAGM_IS_SHARE & cflags) ? "true" : "false");
+ if (SG_CTL_FLAGM_IS_MASTER & seip->ctl_flags_rd_mask)
+ printf(" %sIS_MASTER: %s\n", cp,
+ (SG_CTL_FLAGM_IS_MASTER & cflags) ? "true" : "false");
+ if (SG_CTL_FLAGM_UNSHARE & seip->ctl_flags_rd_mask)
+ printf(" %sUNSHARE: %s\n", cp,
+ (SG_CTL_FLAGM_UNSHARE & cflags) ? "true" : "false");
+ if (SG_CTL_FLAGM_MASTER_FINI & seip->ctl_flags_rd_mask)
+ printf(" %sMASTER_FINI: %s\n", cp,
+ (SG_CTL_FLAGM_MASTER_FINI & cflags) ? "true" : "false");
+ if (SG_CTL_FLAGM_MASTER_ERR & seip->ctl_flags_rd_mask)
+ printf(" %sMASTER_ERR: %s\n", cp,
+ (SG_CTL_FLAGM_MASTER_ERR & cflags) ? "true" : "false");
+ if (SG_CTL_FLAGM_CHECK_FOR_MORE & seip->ctl_flags_rd_mask)
+ printf(" %sCHECK_FOR_MORE: %s\n", cp,
+ (SG_CTL_FLAGM_CHECK_FOR_MORE & cflags) ? "true" : "false");
+ }
+ if (SG_SEIM_MINOR_INDEX & seip->valid_rd_mask)
+ printf(" %sminor_index: %u\n", cp, seip->minor_index);
+ printf("\n");
+#endif
+
+ memset(seip, 0, sizeof(*seip));
+ seip->valid_wr_mask |= SG_SEIM_READ_VAL;
+ seip->valid_rd_mask |= SG_SEIM_READ_VAL;
+ seip->read_value = SG_SEIRV_INT_MASK;
+ if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
+ pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
+ strerror(errno));
+ return 1;
+ }
+ printf(" %sread_value[SG_SEIRV_INT_MASK]= %u\n", cp, seip->read_value);
+
+ memset(seip, 0, sizeof(*seip));
+ seip->valid_wr_mask |= SG_SEIM_READ_VAL;
+ seip->valid_rd_mask |= SG_SEIM_READ_VAL;
+ seip->read_value = SG_SEIRV_BOOL_MASK;
+ if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
+ pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
+ strerror(errno));
+ return 1;
+ }
+ printf(" %sread_value[SG_SEIRV_BOOL_MASK]= %u\n", cp, seip->read_value);
+
+ memset(seip, 0, sizeof(*seip));
+ seip->valid_wr_mask |= SG_SEIM_READ_VAL;
+ seip->valid_rd_mask |= SG_SEIM_READ_VAL;
+ seip->read_value = SG_SEIRV_VERS_NUM;
+ if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
+ pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
+ strerror(errno));
+ return 1;
+ }
+ printf(" %sread_value[SG_SEIRV_VERS_NUM]= %u\n", cp, seip->read_value);
+
+ memset(seip, 0, sizeof(*seip));
+ seip->valid_wr_mask |= SG_SEIM_READ_VAL;
+ seip->valid_rd_mask |= SG_SEIM_READ_VAL;
+ seip->read_value = SG_SEIRV_FL_RQS;
+ if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
+ pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
+ strerror(errno));
+ return 1;
+ }
+ printf(" %sread_value[SG_SEIRV_FL_RQS]= %u\n", cp, seip->read_value);
+
+ memset(seip, 0, sizeof(*seip));
+ seip->valid_wr_mask |= SG_SEIM_READ_VAL;
+ seip->valid_rd_mask |= SG_SEIM_READ_VAL;
+ seip->read_value = SG_SEIRV_DEV_FL_RQS;
+ if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
+ pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
+ strerror(errno));
+ return 1;
+ }
+ printf(" %sread_value[SG_SEIRV_DEV_FL_RQS]= %u\n", cp, seip->read_value);
+
+ memset(seip, 0, sizeof(*seip));
+ seip->valid_wr_mask |= SG_SEIM_READ_VAL;
+ seip->valid_rd_mask |= SG_SEIM_READ_VAL;
+ seip->read_value = SG_SEIRV_TRC_SZ;
+ if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
+ pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
+ strerror(errno));
+ return 1;
+ }
+ printf(" %sread_value[SG_SEIRV_TRC_SZ]= %u\n", cp, seip->read_value);
+
+ memset(seip, 0, sizeof(*seip));
+ seip->valid_wr_mask |= SG_SEIM_READ_VAL;
+ seip->valid_rd_mask |= SG_SEIM_READ_VAL;
+ seip->read_value = SG_SEIRV_TRC_MAX_SZ;
+ if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
+ pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
+ strerror(errno));
+ return 1;
+ }
+ printf(" %sread_value[SG_SEIRV_TRC_MAX_SZ]= %u\n", cp, seip->read_value);
+
+ memset(seip, 0, sizeof(*seip));
+ seip->valid_wr_mask |= SG_SEIM_SHARE_FD;
+ seip->valid_rd_mask |= SG_SEIM_SHARE_FD;
+#if 1
+ seip->share_fd = sg_fd2;
+#else
+ seip->share_fd = sg_fd;
+#endif
+ if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
+ pr2serr("%sioctl(SG_SET_GET_EXTENDED) shared_fd=%d, failed errno=%d "
+ "%s\n", cp, sg_fd2, errno, strerror(errno));
+ }
+ printf(" %sshare successful, read back shared_fd= %d\n", cp,
+ (int)seip->share_fd);
+
+ // printf("SG_IOSUBMIT value=0x%lx\n", SG_IOSUBMIT);
+ // printf("SG_IORECEIVE value=0x%lx\n", SG_IORECEIVE);
+ if (ioctl(sg_fd, SG_GET_TRANSFORM, NULL) < 0)
+ pr2serr("ioctl(SG_GET_TRANSFORM) fail expected, errno=%d %s\n",
+ errno, strerror(errno));
+ else
+ printf("%sSG_GET_TRANSFORM okay (does nothing)\n", cp);
+ if (ioctl(sg_fd, SG_SET_TRANSFORM, NULL) < 0)
+ pr2serr("ioctl(SG_SET_TRANSFORM) fail expected, errno=%d %s\n",
+ errno, strerror(errno));
+ else
+ printf("%sSG_SET_TRANSFORM okay (does nothing)\n", cp);
+ printf("\n");
+
+ return 0;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+ bool done;
+ int sg_fd, k, ok, ver_num, pack_id, num_waiting, access_count;
+ int sg_fd2 = -1;
+ uint8_t inq_cdb[INQ_CMD_LEN] =
+ {INQ_CMD_OP, 0, 0, 0, INQ_REPLY_LEN, 0};
+ uint8_t sdiag_cdb[SDIAG_CMD_LEN] =
+ {SDIAG_CMD_OP, 0, 0, 0, 0, 0};
+ uint8_t xdwrrd10_cdb[XDWRITEREAD_10_LEN] =
+ {XDWRITEREAD_10_OP, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
+ uint8_t inqBuff[MAX_Q_LEN][INQ_REPLY_LEN];
+ struct sg_io_v4 io_v4[MAX_Q_LEN];
+ struct sg_io_v4 rio_v4;
+ struct sg_io_v4 * io_v4p;
+ char * file_name = 0;
+ char ebuff[EBUFF_SZ];
+ uint8_t sense_buffer[MAX_Q_LEN][SENSE_BUFFER_LEN];
+ const char * second_fname = NULL;
+ const char * cp;
+ struct sg_scsi_id ssi;
+
+ for (k = 1; k < argc; ++k) {
+ if (0 == memcmp("-h", argv[k], 2)) {
+ file_name = 0;
+ break;
+ } else if (0 == memcmp("-l=", argv[k], 3)) {
+ q_len = atoi(argv[k] + 3);
+ if ((q_len > 511) || (q_len < 1)) {
+ printf("Expect -l= to take a number (q length) between 1 "
+ "and 511\n");
+ file_name = 0;
+ break;
+ }
+ } else if (0 == memcmp("-o", argv[k], 2))
+ ioctl_only = true;
+ else if (0 == memcmp("-r=", argv[k], 3)) {
+ reserve_buff_sz = atoi(argv[k] + 3);
+ if (reserve_buff_sz < 0) {
+ printf("Expect -r= to take a number 0 or higher\n");
+ file_name = 0;
+ break;
+ }
+ } else if (0 == memcmp("-s=", argv[k], 3)) {
+ sleep_secs = atoi(argv[k] + 3);
+ if (sleep_secs < 0) {
+ printf("Expect -s= to take a number 0 or higher\n");
+ file_name = 0;
+ break;
+ }
+ } else if (0 == memcmp("-t", argv[k], 2))
+ q_at_tail = true;
+ else if (0 == memcmp("-vvvv", argv[k], 5))
+ verbose += 4;
+ else if (0 == memcmp("-vvv", argv[k], 4))
+ verbose += 3;
+ else if (0 == memcmp("-vv", argv[k], 3))
+ verbose += 2;
+ else if (0 == memcmp("-v", argv[k], 2))
+ verbose += 1;
+ else if (0 == memcmp("-V", argv[k], 2)) {
+ printf("%s\n", version_str);
+ file_name = 0;
+ break;
+ } else if (0 == memcmp("-w", argv[k], 2))
+ write_only = true;
+ else if (*argv[k] == '-') {
+ printf("Unrecognized switch: %s\n", argv[k]);
+ file_name = 0;
+ break;
+ }
+ else if (0 == file_name)
+ file_name = argv[k];
+ else if (NULL == second_fname)
+ second_fname = argv[k];
+ else {
+ printf("too many arguments\n");
+ file_name = 0;
+ break;
+ }
+ }
+ if (0 == file_name) {
+ printf("No filename (sg device) given\n\n");
+ usage();
+ return 1;
+ }
+
+ /* An access mode of O_RDWR is required for write()/read() interface */
+ if ((sg_fd = open(file_name, O_RDWR)) < 0) {
+ snprintf(ebuff, EBUFF_SZ,
+ "error opening file: %s", file_name);
+ perror(ebuff);
+ return 1;
+ }
+ if (verbose)
+ fprintf(stderr, "opened given file: %s successfully, fd=%d\n",
+ file_name, sg_fd);
+
+ if (ioctl(sg_fd, SG_GET_VERSION_NUM, &ver_num) < 0) {
+ pr2serr("ioctl(SG_GET_VERSION_NUM) failed, errno=%d %s\n", errno,
+ strerror(errno));
+ goto out;
+ }
+ printf("Linux sg driver version: %d\n", ver_num);
+
+ if (second_fname) {
+ if ((sg_fd2 = open(second_fname, O_RDWR)) < 0) {
+ snprintf(ebuff, EBUFF_SZ,
+ "%s: error opening file: %s", __func__, second_fname);
+ perror(ebuff);
+ return 1;
+ }
+ if (verbose)
+ fprintf(stderr, "opened second file: %s successfully, fd=%d\n",
+ second_fname, sg_fd2);
+ }
+
+#if 0
+ printf("start write() calls\n");
+ for (k = 0; k < q_len; ++k) {
+ io_v4p = &io_v4[k];
+ /* Prepare INQUIRY command */
+ memset(io_v4p, 0, sizeof(*io_v4p));
+ io_v4p->guard = 'Q';
+ /* io_v4p->iovec_count = 0; */ /* memset takes care of this */
+ io_v4p->max_response_len = sizeof(sense_buffer);
+ if (0 == (k % 3)) {
+ io_v4p->request_len = XDWRITEREAD_10_LEN;
+ io_v4p->request = (uint64_t)xdwrrd10_cdb;
+ io_hdr[k].dxfer_direction = SG_DXFER_NONE;
+ } else {
+ io_hdr[k].cmd_len = sizeof(inq_cdb);
+ io_hdr[k].cmdp = inq_cdb;
+ io_hdr[k].dxfer_direction = SG_DXFER_FROM_DEV;
+ io_hdr[k].dxfer_len = INQ_REPLY_LEN;
+ io_hdr[k].dxferp = inqBuff[k];
+ }
+ io_hdr[k].sbp = sense_buffer[k];
+ io_hdr[k].mx_sb_len = SENSE_BUFFER_LEN;
+ io_hdr[k].timeout = 20000; /* 20000 millisecs == 20 seconds */
+ io_hdr[k].pack_id = k + 3; /* so pack_id doesn't start at 0 */
+ /* default is to queue at head (in SCSI mid level) */
+ if (q_at_tail)
+ io_hdr[k].flags |= SG_FLAG_Q_AT_TAIL;
+ else
+ io_hdr[k].flags |= SG_FLAG_Q_AT_HEAD;
+ /* io_hdr[k].usr_ptr = NULL; */
+
+ if (write(sg_fd, &io_hdr[k], sizeof(sg_io_hdr_t)) < 0) {
+ pr2serr("%ssg write errno=%d [%s]\n", cp, errno, strerror(errno));
+ close(sg_fd);
+ return 1;
+ }
+ }
+#endif
+
+ memset(&ssi, 0, sizeof(ssi));
+ if (ioctl(sg_fd, SG_GET_SCSI_ID, &ssi) < 0)
+ pr2serr("ioctl(SG_GET_SCSI_ID) failed, errno=%d %s\n",
+ errno, strerror(errno));
+ else {
+ printf("host_no: %d\n", ssi.host_no);
+ printf(" channel: %d\n", ssi.channel);
+ printf(" scsi_id: %d\n", ssi.scsi_id);
+ printf(" lun: %d\n", ssi.lun);
+ printf(" pdt: %d\n", ssi.scsi_type);
+ printf(" h_cmd_per_lun: %d\n", ssi.h_cmd_per_lun);
+ printf(" d_queue_depth: %d\n", ssi.d_queue_depth);
+ }
+ if (ioctl(sg_fd, SG_GET_PACK_ID, &pack_id) < 0)
+ pr2serr("ioctl(SG_GET_PACK_ID) failed, errno=%d %s\n",
+ errno, strerror(errno));
+ else
+ printf("first available pack_id: %d\n", pack_id);
+ if (ioctl(sg_fd, SG_GET_NUM_WAITING, &num_waiting) < 0)
+ pr2serr("ioctl(SG_GET_NUM_WAITING) failed, errno=%d %s\n",
+ errno, strerror(errno));
+ else
+ printf("num_waiting: %d\n", num_waiting);
+
+ sleep(sleep_secs);
+
+ if (write_only)
+ goto out;
+
+ if (ioctl(sg_fd, SG_GET_PACK_ID, &pack_id) < 0)
+ pr2serr("ioctl(SG_GET_PACK_ID) failed, errno=%d %s\n",
+ errno, strerror(errno));
+ else
+ printf("first available pack_id: %d\n", pack_id);
+ if (ioctl(sg_fd, SG_GET_NUM_WAITING, &num_waiting) < 0)
+ pr2serr("ioctl(SG_GET_NUM_WAITING) failed, errno=%d %s\n",
+ errno, strerror(errno));
+ else
+ printf("num_waiting: %d\n", num_waiting);
+
+ printf("\nstart read() calls\n");
+ for (k = 0, done = false; k < q_len; ++k) {
+ if ((! done) && (k == q_len / 2)) {
+ done = true;
+ printf("\n>>> half way through read\n");
+ if (ioctl(sg_fd, SG_GET_PACK_ID, &pack_id) < 0)
+ pr2serr("ioctl(SG_GET_PACK_ID) failed, errno=%d %s\n",
+ errno, strerror(errno));
+ else
+ printf("first available pack_id: %d\n", pack_id);
+ if (ioctl(sg_fd, SG_GET_NUM_WAITING, &num_waiting) < 0)
+ pr2serr("ioctl(SG_GET_NUM_WAITING) failed, errno=%d %s\n",
+ errno, strerror(errno));
+ else
+ printf("num_waiting: %d\n", num_waiting);
+ if (ioctl(sg_fd, SG_GET_ACCESS_COUNT, &access_count) < 0)
+ pr2serr("ioctl(SG_GET_ACCESS_COUNT) failed, errno=%d %s\n",
+ errno, strerror(errno));
+ else
+ printf("access_count: %d\n", access_count);
+ }
+#if 0
+ memset(&rio_hdr, 0, sizeof(sg_io_hdr_t));
+ rio_hdr.interface_id = 'S';
+ if (read(sg_fd, &rio_hdr, sizeof(sg_io_hdr_t)) < 0) {
+ perror("sg read error");
+ close(sg_fd);
+ return 1;
+ }
+ /* now for the error processing */
+ ok = 0;
+ switch (sg_err_category3(&rio_hdr)) {
+ case SG_LIB_CAT_CLEAN:
+ ok = 1;
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ printf("Recovered error, continuing\n");
+ ok = 1;
+ break;
+ default: /* won't bother decoding other categories */
+ sg_chk_n_print3("command error", &rio_hdr, 1);
+ break;
+ }
+
+ if (ok) { /* output result if it is available */
+ if (0 == (rio_hdr.pack_id % 3))
+ printf("SEND DIAGNOSTIC %d duration=%u\n", rio_hdr.pack_id,
+ rio_hdr.duration);
+ else
+ printf("INQUIRY %d duration=%u\n", rio_hdr.pack_id,
+ rio_hdr.duration);
+ }
+#endif
+ }
+
+out:
+ close(sg_fd);
+ if (sg_fd2 >= 0)
+ close(sg_fd2);
+ return 0;
+}
diff --git a/testing/sg_tst_ioctl.c b/testing/sg_tst_ioctl.c
index 180db0a1..e7127597 100644
--- a/testing/sg_tst_ioctl.c
+++ b/testing/sg_tst_ioctl.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 D. Gilbert
+ * Copyright (C) 2018-2019 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)
@@ -23,6 +23,8 @@
#include <sys/socket.h> /* For passing fd_s via Unix sockets */
+#ifndef HAVE_LINUX_SG_V4_HDR
+
/* Kernel uapi header contain __user decorations on user space pointers
* to indicate they are unsafe in the kernel space. However glibc takes
* all those __user decorations out from headers in /usr/include/linux .
@@ -38,16 +40,20 @@
#include "uapi_sg.h" /* local copy of include/uapi/scsi/sg.h */
+#else
+#define __user
+#endif /* end of: ifndef HAVE_LINUX_SG_V4_HDR */
+
#include "sg_lib.h"
#include "sg_io_linux.h"
#include "sg_linux_inc.h"
#include "sg_pr2serr.h"
-/* This program tests ioctl() calls added and modified in version 3.9 and
+/* This program tests ioctl() calls added and modified in version 4.0 and
* later of the Linux sg driver. */
-static const char * version_str = "Version: 1.01 20181216";
+static const char * version_str = "Version: 1.02 20190106";
#define INQ_REPLY_LEN 96
#define INQ_CMD_LEN 6
@@ -104,7 +110,7 @@ usage(void)
" -w write (submit) only then exit\n");
}
-/* This function taken from Keith Parkard's blog dated 2101205 */
+/* This function taken from Keith Parkard's blog dated 20121005 */
static ssize_t
sock_fd_write(int sock, const void *buf, ssize_t buflen, int fd)
{
diff --git a/testing/sgh_dd.c b/testing/sgh_dd.c
index 72c37530..6d0ae7a7 100644
--- a/testing/sgh_dd.c
+++ b/testing/sgh_dd.c
@@ -1,7 +1,7 @@
/* A utility program for copying files. Specialised for "files" that
* represent devices that understand the SCSI command set.
*
- * Copyright (C) 2018 D. Gilbert
+ * Copyright (C) 2018-2019 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)
@@ -69,6 +69,7 @@
#include "config.h"
#endif
+#ifndef HAVE_LINUX_SG_V4_HDR
/* Kernel uapi header contain __user decorations on user space pointers
* to indicate they are unsafe in the kernel space. However glibc takes
* all those __user decorations out from headers in /usr/include/linux .
@@ -84,6 +85,10 @@
#include "uapi_sg.h" /* local copy of include/uapi/scsi/sg.h */
+#else
+#define __user
+#endif /* end of: ifndef HAVE_LINUX_SG_V4_HDR */
+
#include "sg_lib.h"
#include "sg_cmds_basic.h"
#include "sg_io_linux.h"
@@ -91,7 +96,7 @@
#include "sg_pr2serr.h"
-static const char * version_str = "1.12 20181227";
+static const char * version_str = "1.14 20190107";
#ifdef __GNUC__
#ifndef __clang__
@@ -1632,8 +1637,8 @@ sg_prepare_resbuf(int fd, int bs, int bpt, bool def_res, int elem_sz,
uint8_t *mmp;
res = ioctl(fd, SG_GET_VERSION_NUM, &t);
- if ((res < 0) || (t < 30902)) {
- pr2serr_lk("%ssg driver prior to 3.9.02\n", my_name);
+ if ((res < 0) || (t < 40000)) {
+ pr2serr_lk("%ssg driver prior to 4.0.00\n", my_name);
return 0;
}
if (elem_sz >= 4096) {
@@ -2089,6 +2094,11 @@ main(int argc, char * argv[])
pr2serr("can't have both 'mmap' and 'same_fds' flags\n");
return SG_LIB_SYNTAX_ERROR;
}
+ if (((! clp->in_flags.noshare) && clp->in_flags.dio) ||
+ ((! clp->out_flags.noshare) && clp->out_flags.dio)) {
+ pr2serr("dio flag can only be used with noshare flag\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
/* defaulting transfer size to 128*2048 for CD/DVDs is too large
for the block layer in lk 2.6 and results in an EIO on the
SG_IO ioctl. So reduce it in that case. */
diff --git a/testing/sgs_dd.c b/testing/sgs_dd.c
index 2d4cbe17..6642ac1f 100644
--- a/testing/sgs_dd.c
+++ b/testing/sgs_dd.c
@@ -1,33 +1,15 @@
-/* We need F_SETSIG, (signal redirect), so following define */
-#define _GNU_SOURCE 1
-
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <stdarg.h>
-#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-#include <poll.h>
-#include <signal.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include "sg_lib.h"
-#include "sg_linux_inc.h"
-#include "sg_io_linux.h"
-
-/* Test code for the extensions to the Linux OS SCSI generic ("sg")
+/*
+ * Test code for the extensions to the Linux OS SCSI generic ("sg")
* device driver.
* Copyright (C) 1999-2018 D. Gilbert and P. Allworth
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
* This program is a specialization of the Unix "dd" command in which
* one or both of the given files is a scsi generic device. A block size
* ('bs') is assumed to be 512 if not given. This program complains if
@@ -51,8 +33,52 @@
* chosen. SIGIO is a synonym for SIGPOLL; SIGIO seems to be deprecated.
*/
+/* We need F_SETSIG, (signal redirect), so following define */
+#define _GNU_SOURCE 1
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <poll.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef HAVE_LINUX_SG_V4_HDR
+/* Kernel uapi header contain __user decorations on user space pointers
+ * to indicate they are unsafe in the kernel space. However glibc takes
+ * all those __user decorations out from headers in /usr/include/linux .
+ * So to stop compile errors when directly importing include/uapi/scsi/sg.h
+ * undef __user before doing that include. */
+#define __user
-static const char * version_str = "4.01 20181223";
+/* Want to block the original sg.h header from also being included. That
+ * causes lots of multiple definition errors. This will only work if this
+ * header is included _before_ the original sg.h header. */
+#define _SCSI_GENERIC_H /* original kernel header guard */
+#define _SCSI_SG_H /* glibc header guard */
+
+#include "uapi_sg.h" /* local copy of include/uapi/scsi/sg.h */
+
+#else
+#define __user
+#endif /* end of: ifndef HAVE_LINUX_SG_V4_HDR */
+
+#include "sg_lib.h"
+#include "sg_linux_inc.h"
+#include "sg_io_linux.h"
+#include "sg_pr2serr.h"
+#include "sg_unaligned.h"
+
+
+static const char * version_str = "4.03 20190105";
static const char * my_name = "sgs_dd";
#define DEF_BLOCK_SIZE 512
@@ -83,6 +109,15 @@ static const char * my_name = "sgs_dd";
#define INOUTF_SZ 512
#define EBUFF_SZ 512
+struct flags_t {
+ bool dio;
+ bool excl;
+ bool immed;
+ bool mmap;
+ bool noxfer;
+ bool v3;
+ bool v4;
+};
typedef struct request_element
{
@@ -93,7 +128,11 @@ typedef struct request_element
int blk;
int num_blks;
uint8_t * buffp;
+ uint8_t * free_buffp;
sg_io_hdr_t io_hdr;
+ struct sg_io_v4 io_v4;
+ struct flags_t * iflagp;
+ struct flags_t * oflagp;
uint8_t cmd[S_RW_LEN];
uint8_t sb[SENSE_BUFF_LEN];
int result;
@@ -103,7 +142,6 @@ typedef struct request_collection
{
bool in_is_sg;
bool out_is_sg;
- bool dio;
bool use_rt_sig;
int infd;
int in_blk; /* most recent read */
@@ -127,30 +165,35 @@ typedef struct request_collection
int sigs_io_received;
Rq_elem * rd_posp;
Rq_elem * wr_posp;
+ struct flags_t iflag;
+ struct flags_t oflag;
Rq_elem elem[SGQ_NUM_ELEMS];
} Rq_coll;
+static bool sgs_old_sg_driver = false;
+
static void
usage(void)
{
printf("Usage: "
- "sgs_dd [if=<ifile>] [skip=<n>] [of=<ofile>] [seek=<n>]\n"
- " [bs=<num>] [bpt=<num>] [count=<n>]"
- " [deb=<n>] [dio=0|1]\n"
- " [rt_sig=0|1] [--version]\n"
+ "sgs_dd [bpt=BPT] [bs=BS] [count=NUM] [deb=DEB] [if=IFILE]\n"
+ " [iflag=FLAGS] [of=OFILE] [oflag=FLAGS] "
+ "[rt_sig=0|1]\n"
+ " [seek=SEEK] [skip=SKIP] [--version]\n"
"where:\n"
" bpt blocks_per_transfer (default: 65536/bs (or 128 for "
"bs=512))\n"
- " bs not just any block size, the logical block size of "
- "device\n"
- " dio direct IO, 1->attempt, 0->indirect IO (def)\n"
- " rt_sig 0->use SIGIO (def); 1->use RT sig (SIGRTMIN + 1)\n"
+ " bs must be the logical block size of device (def: 512)\n"
" deb debug: 0->no debug (def); > 0 -> more debug\n"
+ " iflag comma separated list from: dio,excl,immed,mmap,noxfer,"
+ "null,v3,v4\n"
+ " oflag same flags as iflag but bound to OFILE\n"
+ " rt_sig 0->use SIGIO (def); 1->use RT sig (SIGRTMIN + 1)\n"
" <other operands> as per dd command\n\n");
printf("dd clone for testing Linux sg driver SIGPOLL and friends. Either "
- "'if' or 'of'\nmust be a scsi generic device. If 'of' not given "
- "then /dev/null assumed.\n");
+ "IFILE or\nOFILE must be a scsi generic device. If OFILE not given "
+ "then /dev/null\nassumed.\n");
}
/* Return of 0 -> success, -1 -> failure, 2 -> try again */
@@ -200,17 +243,19 @@ read_capacity(int sg_fd, int * num_sect, int * sect_sz)
static int
sg_start_io(Rq_coll * clp, Rq_elem * rep)
{
- sg_io_hdr_t * hp = &rep->io_hdr;
+ int fd = rep->wr ? clp->outfd : clp->infd;
+ struct flags_t * flagp = rep->wr ? rep->oflagp : rep->iflagp;
int res;
+ sg_io_hdr_t * hp = &rep->io_hdr;
+ struct sg_io_v4 * h4p = &rep->io_v4;
memset(rep->cmd, 0, sizeof(rep->cmd));
rep->cmd[0] = rep->wr ? 0x2a : 0x28;
- rep->cmd[2] = (uint8_t)((rep->blk >> 24) & 0xFF);
- rep->cmd[3] = (uint8_t)((rep->blk >> 16) & 0xFF);
- rep->cmd[4] = (uint8_t)((rep->blk >> 8) & 0xFF);
- rep->cmd[5] = (uint8_t)(rep->blk & 0xFF);
- rep->cmd[7] = (uint8_t)((rep->num_blks >> 8) & 0xff);
- rep->cmd[8] = (uint8_t)(rep->num_blks & 0xff);
+ sg_put_unaligned_be32((uint32_t)rep->blk, rep->cmd + 2);
+ sg_put_unaligned_be16((uint16_t)rep->num_blks, rep->cmd + 7);
+ if (flagp->v4)
+ goto do_v4;
+
memset(hp, 0, sizeof(sg_io_hdr_t));
hp->interface_id = 'S';
hp->cmd_len = sizeof(rep->cmd);
@@ -223,8 +268,14 @@ sg_start_io(Rq_coll * clp, Rq_elem * rep)
hp->timeout = DEF_TIMEOUT;
hp->usr_ptr = rep;
hp->pack_id = rep->blk;
- if (clp->dio)
+ if (flagp->dio)
hp->flags |= SG_FLAG_DIRECT_IO;
+ if (flagp->noxfer)
+ hp->flags |= SG_FLAG_NO_DXFER;
+ if (flagp->immed)
+ hp->flags |= SGV4_FLAG_IMMED;
+ if (flagp->mmap)
+ hp->flags |= SG_FLAG_MMAP_IO;
#ifdef SG_DEBUG
fprintf(stderr, "%s: SCSI %s, blk=%d num_blks=%d\n", __func__,
rep->wr ? "WRITE" : "READ", rep->blk, rep->num_blks);
@@ -233,8 +284,8 @@ sg_start_io(Rq_coll * clp, Rq_elem * rep)
hp->dxfer_direction, hp->dxfer_len, hp->dxferp, hp->cmd_len);
#endif
- while (((res = write(rep->wr ? clp->outfd : clp->infd, hp,
- sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno))
+ while (((res = write(fd, hp, sizeof(sg_io_hdr_t))) < 0) &&
+ (EINTR == errno))
;
if (res < 0) {
if (ENOMEM == errno)
@@ -251,22 +302,73 @@ sg_start_io(Rq_coll * clp, Rq_elem * rep)
rep->state = SGQ_IO_STARTED;
clp->sigs_waiting++;
return 0;
+do_v4:
+ memset(h4p, 0, sizeof(struct sg_io_v4));
+ h4p->guard = 'Q';
+ h4p->request_len = sizeof(rep->cmd);
+ h4p->request = (uint64_t)rep->cmd;
+ if (rep->wr) {
+ h4p->dout_xfer_len = clp->bs * rep->num_blks;
+ h4p->dout_xferp = (uint64_t)rep->buffp;
+ } else if (rep->num_blks > 0) {
+ h4p->din_xfer_len = clp->bs * rep->num_blks;
+ h4p->din_xferp = (uint64_t)rep->buffp;
+ }
+ h4p->max_response_len = sizeof(rep->sb);
+ h4p->response = (uint64_t)rep->sb;
+ h4p->timeout = DEF_TIMEOUT;
+ h4p->usr_ptr = (uint64_t)rep;
+ h4p->request_extra = rep->blk;/* N.B. blk --> pack_id --> request_extra */
+ if (flagp->dio)
+ h4p->flags |= SG_FLAG_DIRECT_IO;
+ if (flagp->noxfer)
+ h4p->flags |= SG_FLAG_NO_DXFER;
+ if (flagp->immed)
+ h4p->flags |= SGV4_FLAG_IMMED;
+ if (flagp->mmap)
+ h4p->flags |= SG_FLAG_MMAP_IO;
+ while (((res = ioctl(fd, SG_IOSUBMIT, h4p)) < 0) && (EINTR == errno))
+ ;
+ if (res < 0) {
+ if (ENOMEM == errno)
+ return 1;
+ if ((EDOM == errno) || (EAGAIN == errno)) {
+ rep->state = SGQ_IO_WAIT; /* busy so wait */
+ return 0;
+ }
+ fprintf(stderr, "%s: ioctl(SG_IOSUBMIT): %s [%d]\n", __func__,
+ strerror(errno), errno);
+ rep->state = SGQ_IO_ERR;
+ return res;
+ }
+ rep->state = SGQ_IO_STARTED;
+ clp->sigs_waiting++;
+ return 0;
}
/* -1 -> unrecoverable error, 0 -> successful, 1 -> try again */
static int
sg_finish_io(Rq_coll * clp, bool wr, Rq_elem ** repp)
{
+ bool dio = false;
+ bool is_v4 = wr ? clp->oflag.v4 : clp->iflag.v4;
+ int fd = wr ? clp->outfd : clp->infd;
int res;
sg_io_hdr_t io_hdr;
sg_io_hdr_t * hp;
+ struct sg_io_v4 io_v4;
+ struct sg_io_v4 * h4p;
Rq_elem * rep;
+ if (is_v4)
+ goto do_v4;
memset(&io_hdr, 0 , sizeof(sg_io_hdr_t));
- while (((res = read(wr ? clp->outfd : clp->infd, &io_hdr,
- sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno))
+ while (((res = read(fd, &io_hdr, sizeof(sg_io_hdr_t))) < 0) &&
+ (EINTR == errno))
;
rep = (Rq_elem *)io_hdr.usr_ptr;
+ if (rep)
+ dio = rep->wr ? clp->oflag.dio : clp->iflag.dio;
if (res < 0) {
fprintf(stderr, "%s: read(): %s [%d]\n", __func__, strerror(errno),
errno);
@@ -299,8 +401,7 @@ sg_finish_io(Rq_coll * clp, bool wr, Rq_elem ** repp)
rep->state = SGQ_IO_ERR;
return -1;
}
- if (clp->dio &&
- ((hp->info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
+ if (dio && ((hp->info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
++clp->dio_incomplete; /* count dios done as indirect IO */
clp->sum_of_resids += hp->resid;
rep->state = SGQ_IO_FINISHED;
@@ -309,6 +410,62 @@ sg_finish_io(Rq_coll * clp, bool wr, Rq_elem ** repp)
fprintf(stderr, " SGQ_IO_FINISHED elem idx=%zd\n", rep - clp->elem);
#endif
return 0;
+do_v4:
+ memset(&io_v4, 0 , sizeof(io_v4));
+ io_v4.guard = 'Q';
+ while (((res = ioctl(fd, SG_IORECEIVE, &io_v4)) < 0) && (EINTR == errno))
+ ;
+ rep = (Rq_elem *)io_v4.usr_ptr;
+ if (res < 0) {
+ fprintf(stderr, "%s: ioctl(SG_IORECEIVE): %s [%d]\n", __func__,
+ strerror(errno),
+ errno);
+ if (rep)
+ rep->state = SGQ_IO_ERR;
+ return -1;
+ }
+ if (! (rep && (SGQ_IO_STARTED == rep->state))) {
+ fprintf(stderr, "%s: bad usr_ptr=0x%p\n", __func__, rep);
+ if (rep)
+ rep->state = SGQ_IO_ERR;
+ return -1;
+ }
+ memcpy(&rep->io_v4, &io_v4, sizeof(struct sg_io_v4));
+ h4p = &rep->io_v4;
+ if (repp)
+ *repp = rep;
+
+ res = sg_err_category_new(h4p->device_status, h4p->transport_status,
+ h4p->driver_status,
+ (const uint8_t *)h4p->response,
+ h4p->response_len);
+ switch (res) {
+ case SG_LIB_CAT_CLEAN:
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ fprintf(stderr, "Recovered error on block=%d, num=%d\n",
+ rep->blk, rep->num_blks);
+ break;
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ return 1;
+ default:
+ sg_linux_sense_print(rep->wr ? "writing": "reading",
+ h4p->device_status, h4p->transport_status,
+ h4p->driver_status,
+ (const uint8_t *)h4p->response,
+ h4p->response_len, true);
+ rep->state = SGQ_IO_ERR;
+ return -1;
+ }
+ if (dio && ((h4p->info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
+ ++clp->dio_incomplete; /* count dios done as indirect IO */
+ clp->sum_of_resids += h4p->din_resid;
+ rep->state = SGQ_IO_FINISHED;
+#ifdef SG_DEBUG
+ fprintf(stderr, "%s: %s ", __func__, wr ? "writing" : "reading");
+ fprintf(stderr, " SGQ_IO_FINISHED elem idx=%zd\n", rep - clp->elem);
+#endif
+ return 0;
}
static int
@@ -318,9 +475,13 @@ sz_reserve(int fd, int bs, int bpt, bool rt_sig)
res = ioctl(fd, SG_GET_VERSION_NUM, &t);
if ((res < 0) || (t < 30000)) {
- fprintf(stderr, "sgs_dd: sg driver prior to 3.x.y\n");
+ fprintf(stderr, "sgs_dd: sg driver prior to 3.0.00\n");
return 1;
}
+ else if (t < 40000) {
+ fprintf(stderr, "sgs_dd: warning: sg driver prior to 4.0.00\n");
+ sgs_old_sg_driver = true;
+ }
res = 0;
t = bs * bpt;
res = ioctl(fd, SG_SET_RESERVED_SIZE, &t);
@@ -342,10 +503,11 @@ sz_reserve(int fd, int bs, int bpt, bool rt_sig)
return 0;
}
-static void
+static int
init_elems(Rq_coll * clp)
{
Rq_elem * rep;
+ int res = 0;
int k;
clp->wr_posp = &clp->elem[0]; /* making ring buffer */
@@ -356,8 +518,28 @@ init_elems(Rq_coll * clp)
for (k = 0; k < SGQ_NUM_ELEMS; ++k) {
rep = &clp->elem[k];
rep->state = SGQ_FREE;
- if (NULL == (rep->buffp = malloc(clp->bpt * clp->bs)))
+ rep->iflagp = &clp->iflag;
+ rep->oflagp = &clp->oflag;
+ rep->buffp = sg_memalign(clp->bpt * clp->bs, 0, &rep->free_buffp,
+ false);
+ if (NULL == rep->buffp) {
fprintf(stderr, "out of memory creating user buffers\n");
+ res = -ENOMEM;
+ }
+ }
+ return res;
+}
+
+static void
+remove_elems(Rq_coll * clp)
+{
+ Rq_elem * rep;
+ int k;
+
+ for (k = 0; k < SGQ_NUM_ELEMS; ++k) {
+ rep = &clp->elem[k];
+ if (rep->free_buffp)
+ free(rep->free_buffp);
}
}
@@ -678,6 +860,49 @@ can_read_write(Rq_coll * clp)
return SGQ_CAN_DO_NOTHING;
}
+static bool
+process_flags(const char * arg, struct flags_t * fp)
+{
+ char buff[256];
+ char * cp;
+ char * np;
+
+ strncpy(buff, arg, sizeof(buff));
+ buff[sizeof(buff) - 1] = '\0';
+ if ('\0' == buff[0]) {
+ pr2serr("no flag found\n");
+ return false;
+ }
+ cp = buff;
+ do {
+ np = strchr(cp, ',');
+ if (np)
+ *np++ = '\0';
+ if (0 == strcmp(cp, "dio"))
+ fp->dio = true;
+ else if (0 == strcmp(cp, "excl"))
+ fp->excl = true;
+ else if (0 == strcmp(cp, "immed"))
+ fp->immed = true;
+ else if (0 == strcmp(cp, "mmap"))
+ fp->mmap = true;
+ else if (0 == strcmp(cp, "noxfer"))
+ fp->noxfer = true;
+ else if (0 == strcmp(cp, "null"))
+ ;
+ else if (0 == strcmp(cp, "v3"))
+ fp->v3 = true;
+ else if (0 == strcmp(cp, "v4"))
+ fp->v4 = true;
+ else {
+ pr2serr("unrecognised flag: %s\n", cp);
+ return false;
+ }
+ cp = np;
+ } while (cp);
+ return true;
+}
+
int
main(int argc, char * argv[])
@@ -688,15 +913,14 @@ main(int argc, char * argv[])
int ibs = 0;
int obs = 0;
int count = -1;
+ int in_num_sect = 0;
+ int out_num_sect = 0;
+ int res, k, in_sect_sz, out_sect_sz, crw, open_fl;
char str[STR_SZ];
char * key;
char * buf;
char inf[INOUTF_SZ];
char outf[INOUTF_SZ];
- int res, k;
- int in_num_sect = 0;
- int out_num_sect = 0;
- int in_sect_sz, out_sect_sz, crw;
char ebuff[EBUFF_SZ];
Rq_coll rcoll;
Rq_coll * clp = &rcoll;
@@ -721,30 +945,38 @@ main(int argc, char * argv[])
buf++;
if (*buf)
*buf++ = '\0';
- if (strcmp(key,"if") == 0)
- strncpy(inf, buf, INOUTF_SZ);
- else if (strcmp(key,"of") == 0)
- strncpy(outf, buf, INOUTF_SZ);
- else if (0 == strcmp(key,"ibs"))
- ibs = sg_get_num(buf);
- else if (0 == strcmp(key,"obs"))
- obs = sg_get_num(buf);
+ if (0 == strcmp(key,"bpt"))
+ clp->bpt = sg_get_num(buf);
else if (0 == strcmp(key,"bs"))
clp->bs = sg_get_num(buf);
- else if (0 == strcmp(key,"bpt"))
- clp->bpt = sg_get_num(buf);
- else if (0 == strcmp(key,"skip"))
- skip = sg_get_num(buf);
- else if (0 == strcmp(key,"seek"))
- seek = sg_get_num(buf);
else if (0 == strcmp(key,"count"))
count = sg_get_num(buf);
- else if (0 == strcmp(key,"dio"))
- clp->dio = !!sg_get_num(buf);
- else if (0 == strcmp(key,"rt_sig"))
- clp->use_rt_sig = !!sg_get_num(buf);
else if (0 == strcmp(key,"deb"))
clp->debug = sg_get_num(buf);
+ else if (0 == strcmp(key,"ibs"))
+ ibs = sg_get_num(buf);
+ else if (strcmp(key,"if") == 0)
+ strncpy(inf, buf, INOUTF_SZ);
+ else if (0 == strcmp(key, "iflag")) {
+ if (! process_flags(buf, &clp->iflag)) {
+ pr2serr("%sbad argument to 'iflag='\n", my_name);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ } else if (0 == strcmp(key,"obs"))
+ obs = sg_get_num(buf);
+ else if (strcmp(key,"of") == 0)
+ strncpy(outf, buf, INOUTF_SZ);
+ else if (0 == strcmp(key, "oflag")) {
+ if (! process_flags(buf, &clp->oflag)) {
+ pr2serr("%sbad argument to 'oflag='\n", my_name);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ } else if (0 == strcmp(key,"rt_sig"))
+ clp->use_rt_sig = !!sg_get_num(buf);
+ else if (0 == strcmp(key,"seek"))
+ seek = sg_get_num(buf);
+ else if (0 == strcmp(key,"skip"))
+ skip = sg_get_num(buf);
else if ((0 == strcmp(key,"-V")) || (0 == strcmp(key,"--version"))) {
fprintf(stderr, "%s: version: %s\n", my_name, version_str);
return 0;
@@ -793,7 +1025,8 @@ main(int argc, char * argv[])
clp->infd = STDIN_FILENO;
clp->outfd = STDOUT_FILENO;
if (inf[0] && ('-' != inf[0])) {
- if ((clp->infd = open(inf, O_RDONLY)) < 0) {
+ open_fl = clp->iflag.excl ? O_EXCL : 0;
+ if ((clp->infd = open(inf, open_fl | O_RDONLY)) < 0) {
snprintf(ebuff, EBUFF_SZ, "sgs_dd: could not open %s for reading",
inf);
perror(ebuff);
@@ -815,7 +1048,9 @@ main(int argc, char * argv[])
}
else { /* looks like sg device so close then re-open it RW */
close(clp->infd);
- if ((clp->infd = open(inf, O_RDWR | O_NONBLOCK)) < 0) {
+ open_fl = clp->iflag.excl ? O_EXCL : 0;
+ open_fl |= (O_RDWR | O_NONBLOCK);
+ if ((clp->infd = open(inf, open_fl)) < 0) {
fprintf(stderr, "If %s is a sg device, need read+write "
"permissions, even to read it!\n", inf);
return 1;
@@ -823,10 +1058,17 @@ main(int argc, char * argv[])
clp->in_is_sg = true;
if (sz_reserve(clp->infd, clp->bs, clp->bpt, clp->use_rt_sig))
return 1;
+ if (sgs_old_sg_driver && (clp->iflag.v4 || clp->oflag.v4)) {
+ pr2serr("Unable to implement v4 flag because sg driver too "
+ "old\n");
+ return 1;
+ }
}
}
if (outf[0] && ('-' != outf[0])) {
- if ((clp->outfd = open(outf, O_RDWR | O_NONBLOCK)) >= 0) {
+ open_fl = clp->oflag.excl ? O_EXCL : 0;
+ open_fl |= (O_RDWR | O_NONBLOCK);
+ if ((clp->outfd = open(outf, open_fl)) >= 0) {
if (ioctl(clp->outfd, SG_GET_TIMEOUT, 0) < 0) {
/* not a scsi generic device so now try and open RDONLY */
close(clp->outfd);
@@ -836,10 +1078,19 @@ main(int argc, char * argv[])
if (sz_reserve(clp->outfd, clp->bs, clp->bpt,
clp->use_rt_sig))
return 1;
+ if (sgs_old_sg_driver && (clp->iflag.v4 || clp->oflag.v4)) {
+ pr2serr("Unable to implement v4 flag because sg driver "
+ "too old\n");
+ return 1;
+ }
}
}
if (! clp->out_is_sg) {
- if ((clp->outfd = open(outf, O_WRONLY | O_CREAT, 0666)) < 0) {
+ if (clp->outfd >= 0)
+ close(clp->outfd);
+ open_fl = clp->oflag.excl ? O_EXCL : 0;
+ open_fl |= (O_WRONLY | O_CREAT);
+ if ((clp->outfd = open(outf, open_fl, 0666)) < 0) {
snprintf(ebuff, EBUFF_SZ,
"sgs_dd: could not open %s for writing", outf);
perror(ebuff);
@@ -937,7 +1188,8 @@ main(int argc, char * argv[])
clp->out_count = count;
clp->out_done_count = count;
clp->out_blk = seek;
- init_elems(clp);
+ res = init_elems(clp);
+ res = 0;
/* vvvvvvvvvvvvvvvvv Main Loop vvvvvvvvvvvvvvvvvvvvvvvv */
while (clp->out_done_count > 0) {
@@ -967,7 +1219,7 @@ main(int argc, char * argv[])
if (0 != clp->out_count) {
fprintf(stderr, "Some error occurred, remaining blocks=%d\n",
clp->out_count);
- return 1;
+ res = 1;
}
fprintf(stderr, "%d+%d records in\n", count - clp->in_done_count,
clp->in_partial);
@@ -982,5 +1234,6 @@ main(int argc, char * argv[])
if (clp->debug > 0)
fprintf(stderr, "SIGIO/SIGPOLL signals received: %d, RT sigs: %d\n",
clp->sigs_io_received, clp->sigs_rt_received);
- return 0;
+ remove_elems(clp);
+ return res;
}
diff --git a/testing/tst_sg_lib.c b/testing/tst_sg_lib.c
index ff59ebc7..a90bcac3 100644
--- a/testing/tst_sg_lib.c
+++ b/testing/tst_sg_lib.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2018 Douglas Gilbert.
+ * Copyright (c) 2013-2019 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
@@ -44,7 +44,7 @@
* related to snprintf().
*/
-static const char * version_str = "1.12 20181207";
+static const char * version_str = "1.13 20190108";
#define MAX_LINE_LEN 1024
@@ -363,7 +363,7 @@ main(int argc, char * argv[])
sg_print_sense(leadin, desc_sense_data5,
(int)sizeof(desc_sense_data5), vb);
printf("\n");
- printf("desc_sense UA subsidiary binfing changed test6\n");
+ printf("desc_sense UA subsidiary binding changed test6\n");
sg_print_sense(leadin, desc_sense_data6,
(int)sizeof(desc_sense_data6), vb);
printf("\n");