From 8a58158303a7c56db9437049eb3006287e567920 Mon Sep 17 00:00:00 2001
From: Hannes Reinecke <hare@suse.de>
Date: Thu, 30 Jan 2014 13:43:42 +0100
Subject: sg_xcopy: Update to version 0.41

References: bnc#852420

Signed-off-by: Hannes Reinecke <hare@suse.de>
---
 include/sg_cmds_extra.h |  20 +-
 include/sg_lib_data.h   |   4 +-
 lib/sg_cmds_extra.c     | 166 ++++++++--
 src/sg_xcopy.c          | 804 +++++++++++++++++++++++++++++-------------------
 4 files changed, 657 insertions(+), 337 deletions(-)

diff --git a/include/sg_cmds_extra.h b/include/sg_cmds_extra.h
index c11198b..afc8a7b 100644
--- a/include/sg_cmds_extra.h
+++ b/include/sg_cmds_extra.h
@@ -274,7 +274,8 @@ extern int sg_ll_write_long16(int sg_fd, int cor_dis, int wr_uncor, int pblock,
                               uint64_t llba, void * data_out, int xfer_len,
                               int * offsetp, int noisy, int verbose);
 
-/* Invokes a SCSI RECEIVE COPY RESULTS command. Return of 0 -> success,
+/* Invokes a SPC-3 SCSI RECEIVE COPY RESULTS command. In SPC-4 this function
+ * supports all service action variants of the THIRD-PARTY COPY IN opcode.
  * SG_LIB_CAT_INVALID_OP -> Receive copy results not supported,
  * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
  * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
@@ -283,14 +284,27 @@ extern int sg_ll_receive_copy_results(int sg_fd, int sa, int list_id,
                                       void * resp, int mx_resp_len,
                                       int noisy, int verbose);
 
-/* Invokes a SCSI EXTENDEd COPY command. Return of 0 -> success,
+/* Invokes a SCSI EXTENDED COPY(LID1) command. For EXTENDED COPY(LID4)
+ * including POPULATE TOKEN and WRITE USING TOKEN use
+ * sg_ll_3party_copy_out().  Return of 0 -> success,
  * SG_LIB_CAT_INVALID_OP -> Extended copy not supported,
  * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
  * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
  * -1 -> other failure */
-extern int sg_ll_extended_copy(int sg_fd, void * resp, int mx_resp_len,
+extern int sg_ll_extended_copy(int sg_fd, void * paramp, int param_len,
                                int noisy, int verbose);
 
+/* Handles various service actions associated with opcode 0x83 which is
+ * called THIRD PARTY COPY OUT. These include the EXTENDED COPY(LID4),
+ * POPULATE TOKEN and WRITE USING TOKEN commands. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> opcode 0x83 not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_3party_copy_out(int sg_fd, int sa, unsigned int list_id,
+                          int group_num, int timeout_secs, void * paramp,
+                          int param_len, int noisy, int verbose);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/sg_lib_data.h b/include/sg_lib_data.h
index 5761131..68fd1c8 100644
--- a/include/sg_lib_data.h
+++ b/include/sg_lib_data.h
@@ -26,8 +26,8 @@ extern "C" {
 #define SG_MAINTENANCE_OUT 0xa4
 #define SG_PERSISTENT_RESERVE_IN 0x5e
 #define SG_PERSISTENT_RESERVE_OUT 0x5f
-#define SG_EXTENDED_COPY 0x83
-#define SG_RECEIVE_COPY 0x84
+#define SG_EXTENDED_COPY 0x83 /* since spc4r34 called: Third party copy out */
+#define SG_RECEIVE_COPY 0x84  /* since spc4r34 called: Third party copy in */
 #define SG_SERVICE_ACTION_IN_12 0xab
 #define SG_SERVICE_ACTION_OUT_12 0xa9
 #define SG_SERVICE_ACTION_BIDI 0x9d
diff --git a/lib/sg_cmds_extra.c b/lib/sg_cmds_extra.c
index d162bb7..cd766e2 100644
--- a/lib/sg_cmds_extra.c
+++ b/lib/sg_cmds_extra.c
@@ -82,10 +82,6 @@
 #define READ_MEDIA_SERIAL_NUM_SA 0x1
 #define REPORT_IDENTIFYING_INFORMATION_SA 0x5
 #define REPORT_TGT_PRT_GRP_SA 0xa
-#define RECEIVE_COPY_RES_COPY_STATUS_SA 0x00
-#define RECEIVE_COPY_RES_RECEIVE_DATA_SA 0x01
-#define RECEIVE_COPY_RES_OPERATING_PARMS_SA 0x03
-#define RECEIVE_COPY_RES_FAILED_SEGMENT_DETAILS_SA 0x04
 #define SET_IDENTIFYING_INFORMATION_SA 0x6
 #define SET_TGT_PRT_GRP_SA 0xa
 #define WRITE_LONG_16_SA 0x11
@@ -2211,7 +2207,8 @@ sg_ll_read_block_limits(int sg_fd, void * resp, int mx_resp_len,
     return ret;
 }
 
-/* Invokes a SCSI RECEIVE COPY RESULTS command. Return of 0 -> success,
+/* Invokes a SCSI RECEIVE COPY RESULTS command. Actually cover all current
+ * uses of opcode 0x84 (Third-party copy IN). Return of 0 -> success,
  * SG_LIB_CAT_INVALID_OP -> Receive copy results not supported,
  * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
  * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
@@ -2225,9 +2222,18 @@ sg_ll_receive_copy_results(int sg_fd, int sa, int list_id, void * resp,
       {RECEIVE_COPY_RESULTS_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     unsigned char sense_b[SENSE_BUFF_LEN];
     struct sg_pt_base * ptvp;
+    char b[64];
 
+    sg_get_opcode_sa_name(RECEIVE_COPY_RESULTS_CMD, sa, 0, (int)sizeof(b), b);
     rcvcopyresCmdBlk[1] = (unsigned char)(sa & 0x1f);
-    rcvcopyresCmdBlk[2] = (unsigned char)(list_id);
+    if (sa <= 4)        /* LID1 variants */
+        rcvcopyresCmdBlk[2] = (unsigned char)(list_id);
+    else if ((sa >= 5) && (sa <= 7)) {  /* LID4 variants */
+        rcvcopyresCmdBlk[2] = (unsigned char)((list_id >> 24) & 0xff);
+        rcvcopyresCmdBlk[3] = (unsigned char)((list_id >> 16) & 0xff);
+        rcvcopyresCmdBlk[4] = (unsigned char)((list_id >> 8) & 0xff);
+        rcvcopyresCmdBlk[5] = (unsigned char)(list_id & 0xff);
+    }
     rcvcopyresCmdBlk[10] = (unsigned char)((mx_resp_len >> 24) & 0xff);
     rcvcopyresCmdBlk[11] = (unsigned char)((mx_resp_len >> 16) & 0xff);
     rcvcopyresCmdBlk[12] = (unsigned char)((mx_resp_len >> 8) & 0xff);
@@ -2236,7 +2242,7 @@ sg_ll_receive_copy_results(int sg_fd, int sa, int list_id, void * resp,
     if (NULL == sg_warnings_strm)
         sg_warnings_strm = stderr;
     if (verbose) {
-        fprintf(sg_warnings_strm, "    Receive copy results cmd: ");
+        fprintf(sg_warnings_strm, "    %s cmd: ", b);
         for (k = 0; k < RECEIVE_COPY_RESULTS_CMDLEN; ++k)
             fprintf(sg_warnings_strm, "%02x ", rcvcopyresCmdBlk[k]);
         fprintf(sg_warnings_strm, "\n");
@@ -2244,17 +2250,15 @@ sg_ll_receive_copy_results(int sg_fd, int sa, int list_id, void * resp,
 
     ptvp = construct_scsi_pt_obj();
     if (NULL == ptvp) {
-        fprintf(sg_warnings_strm, "receive copy results: out of "
-                "memory\n");
+        fprintf(sg_warnings_strm, "%s: out of memory\n", b);
         return -1;
     }
     set_scsi_pt_cdb(ptvp, rcvcopyresCmdBlk, sizeof(rcvcopyresCmdBlk));
     set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
     set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
     res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
-    ret = sg_cmds_process_resp(ptvp, "receive copy results", res,
-                               mx_resp_len, sense_b, noisy, verbose,
-                               &sense_cat);
+    ret = sg_cmds_process_resp(ptvp, b, res, mx_resp_len, sense_b, noisy,
+                               verbose, &sense_cat);
     if (-1 == ret)
         ;
     else if (-2 == ret) {
@@ -2280,46 +2284,160 @@ sg_ll_receive_copy_results(int sg_fd, int sa, int list_id, void * resp,
     return ret;
 }
 
-/* Invokes a SCSI RECEIVE COPY RESULTS command. Return of 0 -> success,
+
+/* SPC-4 rev 35 and later calls this opcode (0x83) "Third-party copy OUT"
+ * The original EXTENDED COPY command (now called EXTENDED COPY (LID1))
+ * is the only one supported by sg_ll_extended_copy(). See function
+ * sg_ll_3party_copy_out() for the other service actions ( > 0 ). */
+
+/* Invokes a SCSI EXTENDED COPY (LID1) command. Return of 0 -> success,
  * SG_LIB_CAT_INVALID_OP -> Receive copy results not supported,
  * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
  * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
  * -1 -> other failure */
 int
-sg_ll_extended_copy(int sg_fd, void * resp,
-                    int mx_resp_len, int noisy, int verbose)
+sg_ll_extended_copy(int sg_fd, void * paramp, int param_len, int noisy,
+                    int verbose)
 {
     int k, res, ret, sense_cat;
     unsigned char xcopyCmdBlk[EXTENDED_COPY_CMDLEN] =
       {EXTENDED_COPY_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     unsigned char sense_b[SENSE_BUFF_LEN];
     struct sg_pt_base * ptvp;
+    const char * opcode_name = "Extended copy (LID1)";
 
-    xcopyCmdBlk[10] = (unsigned char)((mx_resp_len >> 24) & 0xff);
-    xcopyCmdBlk[11] = (unsigned char)((mx_resp_len >> 16) & 0xff);
-    xcopyCmdBlk[12] = (unsigned char)((mx_resp_len >> 8) & 0xff);
-    xcopyCmdBlk[13] = (unsigned char)(mx_resp_len & 0xff);
+    xcopyCmdBlk[10] = (unsigned char)((param_len >> 24) & 0xff);
+    xcopyCmdBlk[11] = (unsigned char)((param_len >> 16) & 0xff);
+    xcopyCmdBlk[12] = (unsigned char)((param_len >> 8) & 0xff);
+    xcopyCmdBlk[13] = (unsigned char)(param_len & 0xff);
 
     if (NULL == sg_warnings_strm)
         sg_warnings_strm = stderr;
     if (verbose) {
-        fprintf(sg_warnings_strm, "    Extended copy cmd: ");
+        fprintf(sg_warnings_strm, "    %s cmd: ", opcode_name);
         for (k = 0; k < EXTENDED_COPY_CMDLEN; ++k)
             fprintf(sg_warnings_strm, "%02x ", xcopyCmdBlk[k]);
         fprintf(sg_warnings_strm, "\n");
+        if ((verbose > 1) && paramp && param_len) {
+            fprintf(sg_warnings_strm, "    %s parameter list:\n",
+                    opcode_name);
+            dStrHex((const char *)paramp, param_len, -1);
+        }
     }
 
     ptvp = construct_scsi_pt_obj();
     if (NULL == ptvp) {
-        fprintf(sg_warnings_strm, "extended copy: out of memory\n");
+        fprintf(sg_warnings_strm, "%s: out of memory\n", opcode_name);
         return -1;
     }
     set_scsi_pt_cdb(ptvp, xcopyCmdBlk, sizeof(xcopyCmdBlk));
     set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
-    set_scsi_pt_data_out(ptvp, (unsigned char *)resp, mx_resp_len);
+    set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len);
     res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
-    ret = sg_cmds_process_resp(ptvp, "extended copy", res,
-                               mx_resp_len, sense_b, noisy, verbose,
+    ret = sg_cmds_process_resp(ptvp, opcode_name, res, 0, sense_b,
+                               noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_NOT_READY:
+        case SG_LIB_CAT_INVALID_OP:
+        case SG_LIB_CAT_ILLEGAL_REQ:
+        case SG_LIB_CAT_UNIT_ATTENTION:
+        case SG_LIB_CAT_ABORTED_COMMAND:
+            ret = sense_cat;
+            break;
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = -1;
+            break;
+        }
+    } else
+        ret = 0;
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Handles various service actions associated with opcode 0x83 which is
+ * called THIRD PARTY COPY OUT. These include the EXTENDED COPY(LID4) and
+ * WRITE USING TOKEN commands. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> opcode 0x83 not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int
+sg_ll_3party_copy_out(int sg_fd, int sa, unsigned int list_id, int group_num,
+                      int timeout_secs, void * paramp, int param_len,
+                      int noisy, int verbose)
+{
+    int k, res, ret, sense_cat, tmout;
+    unsigned char xcopyCmdBlk[EXTENDED_COPY_CMDLEN] =
+      {EXTENDED_COPY_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+    char cname[80];
+
+    if (NULL == sg_warnings_strm)
+        sg_warnings_strm = stderr;
+    sg_get_opcode_sa_name(EXTENDED_COPY_CMD, sa, 0, sizeof(cname), cname);
+    xcopyCmdBlk[1] = (unsigned char)(sa & 0x1f);
+    switch (sa) {
+    case 0x0:   /* XCOPY(LID1) */
+    case 0x1:   /* XCOPY(LID4) */
+        xcopyCmdBlk[10] = (unsigned char)((param_len >> 24) & 0xff);
+        xcopyCmdBlk[11] = (unsigned char)((param_len >> 16) & 0xff);
+        xcopyCmdBlk[12] = (unsigned char)((param_len >> 8) & 0xff);
+        xcopyCmdBlk[13] = (unsigned char)(param_len & 0xff);
+        break;
+    case 0x10:  /* POPULATE TOKEN (SBC-3) */
+    case 0x11:  /* WRITE USING TOKEN (SBC-3) */
+        xcopyCmdBlk[6] = (unsigned char)((list_id >> 24) & 0xff);
+        xcopyCmdBlk[7] = (unsigned char)((list_id >> 16) & 0xff);
+        xcopyCmdBlk[8] = (unsigned char)((list_id >> 8) & 0xff);
+        xcopyCmdBlk[9] = (unsigned char)(list_id & 0xff);
+        xcopyCmdBlk[10] = (unsigned char)((param_len >> 24) & 0xff);
+        xcopyCmdBlk[11] = (unsigned char)((param_len >> 16) & 0xff);
+        xcopyCmdBlk[12] = (unsigned char)((param_len >> 8) & 0xff);
+        xcopyCmdBlk[13] = (unsigned char)(param_len & 0xff);
+        xcopyCmdBlk[14] = (unsigned char)(group_num & 0x1f);
+        break;
+    case 0x1c:  /* COPY OPERATION ABORT */
+        xcopyCmdBlk[2] = (unsigned char)((list_id >> 24) & 0xff);
+        xcopyCmdBlk[3] = (unsigned char)((list_id >> 16) & 0xff);
+        xcopyCmdBlk[4] = (unsigned char)((list_id >> 8) & 0xff);
+        xcopyCmdBlk[5] = (unsigned char)(list_id & 0xff);
+        break;
+    default:
+        fprintf(sg_warnings_strm, "sg_ll_3party_copy_out: unknown service "
+                "action 0x%x\n", sa);
+        return -1;
+    }
+    tmout = (timeout_secs > 0) ? timeout_secs : DEF_PT_TIMEOUT;
+
+    if (verbose) {
+        fprintf(sg_warnings_strm, "    %s cmd: ", cname);
+        for (k = 0; k < EXTENDED_COPY_CMDLEN; ++k)
+            fprintf(sg_warnings_strm, "%02x ", xcopyCmdBlk[k]);
+        fprintf(sg_warnings_strm, "\n");
+        if ((verbose > 1) && paramp && param_len) {
+            fprintf(sg_warnings_strm, "    %s parameter list:\n", cname);
+            dStrHex((const char *)paramp, param_len, -1);
+        }
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        fprintf(sg_warnings_strm, "%s: out of memory\n", cname);
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, xcopyCmdBlk, sizeof(xcopyCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len);
+    res = do_scsi_pt(ptvp, sg_fd, tmout, verbose);
+    ret = sg_cmds_process_resp(ptvp, cname, res, 0, sense_b, noisy, verbose,
                                &sense_cat);
     if (-1 == ret)
         ;
diff --git a/src/sg_xcopy.c b/src/sg_xcopy.c
index bb76e14..bcd4321 100644
--- a/src/sg_xcopy.c
+++ b/src/sg_xcopy.c
@@ -1,7 +1,7 @@
 /* A utility program for copying files. Similar to 'dd' but using
  * the 'Extended Copy' command.
  *
- *  Copyright (c) 2011-2012 Hannes Reinecke, SUSE Labs
+ *  Copyright (c) 2011-2014 Hannes Reinecke, SUSE Labs
  *
  *  Largely taken from 'sg_dd', which has the
  *
@@ -37,6 +37,7 @@
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdarg.h>
 #include <string.h>
 #include <signal.h>
 #include <ctype.h>
@@ -61,26 +62,24 @@
 #include "sg_cmds_extra.h"
 #include "sg_io_linux.h"
 
-static char * version_str = "0.31 20120919";
+static const char * version_str = "0.41 20140105";
 
 #define ME "sg_xcopy: "
 
-#define SG_DEBUG
-
 #define STR_SZ 1024
 #define INOUTF_SZ 512
 #define EBUFF_SZ 512
 
 #define DEF_BLOCK_SIZE 512
 #define DEF_BLOCKS_PER_TRANSFER 128
-#define DEF_BLOCKS_PER_2048TRANSFER 32
+#define MAX_BLOCKS_PER_TRANSFER 65535
 
 #define DEF_MODE_RESP_LEN 252
 #define RW_ERR_RECOVERY_MP 1
 #define CACHING_MP 8
 #define CONTROL_MP 0xa
 
-#define SENSE_BUFF_LEN 32       /* Arbitrary, could be larger */
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
 #define READ_CAP_REPLY_LEN 8
 #define RCAP16_REPLY_LEN 32
 
@@ -93,8 +92,7 @@ static char * version_str = "0.31 20120919";
 #define SG_LIB_FLOCK_ERR 90
 
 #define FT_OTHER 1              /* filetype is probably normal */
-#define FT_SG 2                 /* filetype is sg char device or supports
-                                   SG_IO ioctl */
+#define FT_SG 2                 /* filetype is sg or bsg char device */
 #define FT_RAW 4                /* filetype is raw char device */
 #define FT_DEV_NULL 8           /* either "/dev/null" or "." as filename */
 #define FT_ST 16                /* filetype is st char device (tape) */
@@ -112,6 +110,13 @@ static char * version_str = "0.31 20120919";
 #define TD_RDMA 128
 #define TD_FW 256
 #define TD_SAS 512
+#define TD_IPV6 1024
+#define TD_IP_COPY_SERVICE 2048
+#define TD_ROD 4096
+
+#define XCOPY_TO_SRC "XCOPY_TO_SRC"
+#define XCOPY_TO_DST "XCOPY_TO_DST"
+#define DEF_XCOPY_SRC0_DST1 1
 
 #define DEV_NULL_MINOR_NUM 3
 
@@ -125,11 +130,6 @@ static int64_t in_full = 0;
 static int in_partial = 0;
 static int64_t out_full = 0;
 static int out_partial = 0;
-#if 0
-static int recovered_errs = 0;
-static int unrecovered_errs = 0;
-static int num_retries = 0;
-#endif
 
 static int do_time = 0;
 static int verbose = 0;
@@ -156,17 +156,17 @@ struct xcopy_fp_t {
     int flock;
     int pad;     /* Data descriptor PAD bit (residual data treatment) */
     int pdt;     /* Peripheral device type */
-#if 0
-    int retries;
-#endif
+    int xcopy_given;
 };
 
-static struct xcopy_fp_t ifp;
-static struct xcopy_fp_t ofp;
-
-static void calc_duration_throughput(int contin);
+static struct xcopy_fp_t ixcf;
+static struct xcopy_fp_t oxcf;
 
+static const char * read_cap_str = "Read capacity";
+static const char * rec_copy_op_params_str = "Receive copy operating "
+                                             "parameters";
 
+static void calc_duration_throughput(int contin);
 static void
 install_handler(int sig_num, void (*sig_handler) (int sig))
 {
@@ -186,20 +186,11 @@ static void
 print_stats(const char * str)
 {
     if (0 != dd_count)
-        fprintf(stderr, "  remaining block count=%"PRId64"\n", dd_count);
-    fprintf(stderr, "%s%"PRId64"+%d records in\n", str, in_full - in_partial,
+        fprintf(stderr, "  remaining block count=%" PRId64 "\n", dd_count);
+    fprintf(stderr, "%s%" PRId64 "+%d records in\n", str, in_full - in_partial,
             in_partial);
-    fprintf(stderr, "%s%"PRId64"+%d records out\n", str,
+    fprintf(stderr, "%s%" PRId64 "+%d records out\n", str,
             out_full - out_partial, out_partial);
-#if 0
-    if (recovered_errs > 0)
-        fprintf(stderr, "%s%d recovered errors\n", str, recovered_errs);
-    if (num_retries > 0)
-        fprintf(stderr, "%s%d retries attempted\n", str, num_retries);
-    else if (unrecovered_errs)
-        fprintf(stderr, "%s%d unrecovered error(s)\n", str,
-                unrecovered_errs);
-#endif
 }
 
 
@@ -223,7 +214,7 @@ interrupt_handler(int sig)
 static void
 siginfo_handler(int sig)
 {
-    sig = sig;  /* dummy to stop -W warning messages */
+    if (sig) { ; }      /* unused, dummy to suppress warning */
     fprintf(stderr, "Progress report, continuing ...\n");
     if (do_time)
         calc_duration_throughput(1);
@@ -322,8 +313,9 @@ open_sg(struct xcopy_fp_t * fp, int verbose)
 
     fp->pdt = sir.peripheral_type;
     if (verbose)
-        fprintf(stderr, "    %s: %.8s  %.16s  %.4s  [pdt=%d]\n",
-                fp->fname, sir.vendor, sir.product, sir.revision, fp->pdt);
+        fprintf(stderr, "    %s: %.8s  %.16s  %.4s  [pdt=%d, 3pc=%d]\n",
+                fp->fname, sir.vendor, sir.product, sir.revision, fp->pdt,
+                !! (0x8 & sir.byte_5));
 
     return fp->sg_fd;
 }
@@ -393,6 +385,30 @@ dd_filetype_str(int ft, char * buff)
 }
 
 static int
+simplified_ft(const struct xcopy_fp_t * xfp)
+{
+    int ftype = xfp->sg_type;
+
+    switch (ftype) {
+    case FT_BLOCK:
+    case FT_ST:
+    case FT_OTHER:      /* typically regular file */
+    case FT_DEV_NULL:
+    case FT_FIFO:
+    case FT_ERROR:
+        return ftype;
+    default:
+        if (FT_SG & ftype) {
+            if ((0 == xfp->pdt) || (0xe == xfp->pdt)) /* D-A or RBC */
+            return FT_BLOCK;
+        else if (0x1 == xfp->pdt)
+            return FT_ST;
+        }
+        return FT_OTHER;
+    }
+}
+
+static int
 seg_desc_from_dd_type(int in_ft, int in_off, int out_ft, int out_off)
 {
     int desc_type = -1;
@@ -451,54 +467,80 @@ seg_desc_from_dd_type(int in_ft, int in_off, int out_ft, int out_off)
 }
 
 static void
-usage()
+usage(int n_help)
 {
+    if (n_help < 2)
+        goto primary_help;
+    else
+        goto secondary_help;
+
+primary_help:
     fprintf(stderr, "Usage: "
-           "sg_xcopy  [bs=BS] [count=COUNT] [ibs=BS] [if=IFILE]"
-           " [iflag=FLAGS]\n"
-           "                 [obs=BS] [of=OFILE] [oflag=FLAGS] "
-           "[seek=SEEK] [skip=SKIP]\n"
-           "                 [--help] [--version]\n\n"
-           "                 [bpt=BPT] [cat=0|1] [dc=0|1] "
-           "[id_usage=hold|discard]\n"
-           "                 [list_id=ID] [prio=PRIO] [time=0|1] "
-           "[verbose=VERB]\n"
-           "  where:\n"
-           "    bpt         is blocks_per_transfer (default is 128 or 32 "
-           "when BS>=2048)\n"
-           "    bs          block size (default is 512)\n");
-    fprintf(stderr,
-           "    cat         segment descriptor CAT bit (default: 0)\n"
-           "    count       number of blocks to copy (def: device size)\n"
-           "    dc          segment descriptor DC bit (default: 0)\n"
-           "    ibs         input block size (if given must be same as "
-           "'bs=')\n"
-           "    id_usage    sets list id usage field to hold (0) or "
-           "discard (2)\n"
-           "    if          file or device to read from (def: stdin)\n"
-           "    iflag       comma separated list from: [cat,dc,excl,"
-           "flock,null]\n"
-           "    list_id     sets list identifier field to ID (default: 1)\n"
-           "    obs         output block size (if given must be same as "
-           "'bs=')\n"
-           "    of          file or device to write to (def: stdout), "
-           "OFILE of '.'\n");
-    fprintf(stderr,
-           "                treated as /dev/null\n"
-           "    oflag       comma separated list from: [append,cat,pad,dc,"
-           "excl,flock,\n"
-           "                null]\n"
-           "    prio        set priority field to PRIO (def: 1)\n"
-           "    seek        block position to start writing to OFILE\n"
-           "    skip        block position to start reading from IFILE\n"
-           "    time        0->no timing(def), 1->time plus calculate "
-           "throughput\n"
-           "    verbose     0->quiet(def), 1->some noise, 2->more noise, "
-           "etc\n"
-           "    --help      print out this usage message then exit\n"
-           "    --version   print version information then exit\n\n"
-           "copy from IFILE to OFILE, similar to dd command; "
-           "but using the SCSI\nEXTENDED COPY command\n");
+            "sg_xcopy  [bpt=BPT] [bs=BS] [cat=0|1] [count=COUNT] [dc=0|1] "
+            "[ibs=BS]\n"
+            "                 [id_usage=hold|discard|disable] [if=IFILE] "
+            "[iflag=FLAGS]\n"
+            "                 [list_id=ID] [obs=BS] [of=OFILE] [oflag=FLAGS] "
+            "[prio=PRIO]\n"
+            "                 [seek=SEEK] [skip=SKIP] [time=0|1] "
+            "[verbose=VERB] [--help]\n"
+            "                 [--on_dst|--on_src] [--verbose] [--version]\n\n"
+            "  where:\n"
+            "    bpt         is blocks_per_transfer (default: 128)\n"
+            "    bs          block size (default is 512)\n");
+    fprintf(stderr, "    cat         xcopy segment descriptor CAT bit (default: "
+            "0)\n"
+            "    count       number of blocks to copy (def: device size)\n"
+            "    dc          xcopy segment descriptor DC bit (default: 0)\n"
+            "    ibs         input block size (if given must be same as "
+            "'bs=')\n"
+            "    id_usage    sets list_id_usage field to hold (0), "
+            "discard (2) or\n"
+            "                disable (3)\n"
+            "    if          file or device to read from (def: stdin)\n"
+            "    iflag       comma separated list of flags applying to "
+            "IFILE\n"
+            "    list_id     sets list_id field to ID (default: 1 or 0)\n"
+            "    obs         output block size (if given must be same as "
+            "'bs=')\n"
+            "    of          file or device to write to (def: stdout), "
+            "OFILE of '.'\n");
+    fprintf(stderr, "                treated as /dev/null\n"
+            "    oflag       comma separated list of flags applying to "
+            "OFILE\n"
+            "    prio        set xcopy priority field to PRIO (def: 1)\n"
+            "    seek        block position to start writing to OFILE\n"
+            "    skip        block position to start reading from IFILE\n"
+            "    time        0->no timing(def), 1->time plus calculate "
+            "throughput\n"
+            "    verbose     0->quiet(def), 1->some noise, 2->more noise, "
+            "etc\n"
+            "    --help      print out this usage message then exit\n"
+            "    --on_dst    send XCOPY command to OFILE\n"
+            "    --on_src    send XCOPY command to IFILE\n"
+            "    --verbose   same action as verbose=1\n"
+            "    --version   print version information then exit\n\n"
+            "Copy from IFILE to OFILE, similar to dd command; "
+            "but using the SCSI\nEXTENDED COPY (XCOPY) command. For list "
+            "of flags, use '-hh'.\n");
+    return;
+
+secondary_help:
+    fprintf(stderr, "FLAGS:\n"
+            "  append (o)     ignored\n"
+            "  excl           open corresponding device with O_EXCL\n"
+            "  flock          call flock(LOCK_EX|LOCK_NB)\n"
+            "  null           does nothing, placeholder\n"
+            "  pad            set xcopy data descriptor PAD bit on\n"
+            "                 corresponding device\n"
+            "  xcopy          send XCOPY command to corresponding device\n"
+            "\n"
+            "ENVIRONMENT VARIABLES:\n"
+            "  XCOPY_TO_DST   send XCOPY command to OFILE (destination) "
+            "if no other\n"
+            "                 indication\n"
+            "  XCOPY_TO_SRC   send XCOPY command to IFILE (source)\n"
+           );
 }
 
 static int
@@ -551,8 +593,7 @@ scsi_extended_copy(int sg_fd, unsigned char list_id,
     int seg_desc_len;
     int verb;
 
-    verb = (verbose ? verbose - 1: 0);
-
+    verb = (verbose > 1) ? (verbose - 2) : 0;
     memset(xcopyBuff, 0, 256);
     xcopyBuff[0] = list_id;
     xcopyBuff[1] = (list_id_usage << 3) | priority;
@@ -567,11 +608,8 @@ scsi_extended_copy(int sg_fd, unsigned char list_id,
                                         src_lba, dst_lba);
     xcopyBuff[11] = seg_desc_len; /* One segment descriptor */
     desc_offset += seg_desc_len;
-    if (verbose > 3) {
-        fprintf(stderr, "\nParameter list in hex (length %d):\n", desc_offset);
-        dStrHex((const char *)xcopyBuff, desc_offset, 1);
-    }
-    return sg_ll_extended_copy(sg_fd, xcopyBuff, desc_offset, 0, verb);
+    /* set noisy so if a UA happens it will be printed to stderr */
+    return sg_ll_extended_copy(sg_fd, xcopyBuff, desc_offset, 1, verb);
 }
 
 /* Return of 0 -> success, see sg_ll_read_capacity*() otherwise */
@@ -585,7 +623,7 @@ scsi_read_capacity(struct xcopy_fp_t *xfp)
 
     verb = (verbose ? verbose - 1: 0);
     res = sg_ll_readcap_10(xfp->sg_fd, 0, 0, rcBuff,
-                           READ_CAP_REPLY_LEN, 0, verb);
+                           READ_CAP_REPLY_LEN, 1, verb);
     if (0 != res)
         return res;
 
@@ -594,7 +632,7 @@ scsi_read_capacity(struct xcopy_fp_t *xfp)
         int64_t ls;
 
         res = sg_ll_readcap_16(xfp->sg_fd, 0, 0, rcBuff,
-                               RCAP16_REPLY_LEN, 0, verb);
+                               RCAP16_REPLY_LEN, 1, verb);
         if (0 != res)
             return res;
         for (k = 0, ls = 0; k < 8; ++k) {
@@ -613,7 +651,7 @@ scsi_read_capacity(struct xcopy_fp_t *xfp)
                        (rcBuff[6] << 8) | rcBuff[7];
     }
     if (verbose)
-        fprintf(stderr, "    %s: number of blocks=%"PRId64" [0x%"PRIx64"], "
+        fprintf(stderr, "    %s: number of blocks=%" PRId64 " [0x%" PRIx64 "], "
                 "block size=%d\n", xfp->fname, xfp->num_sect, xfp->num_sect,
                 xfp->sect_sz);
     return 0;
@@ -622,7 +660,7 @@ scsi_read_capacity(struct xcopy_fp_t *xfp)
 static int
 scsi_operating_parameter(struct xcopy_fp_t *xfp, int is_target)
 {
-    int res;
+    int res, ftype, snlid;
     unsigned char rcBuff[256];
     unsigned int rcBuffLen = 256, len, n, td_list = 0;
     unsigned long num, max_target_num, max_segment_num, max_segment_len;
@@ -630,8 +668,17 @@ scsi_operating_parameter(struct xcopy_fp_t *xfp, int is_target)
     int verb, valid = 0;
 
     verb = (verbose ? verbose - 1: 0);
+    ftype = xfp->sg_type;
+    if (FT_SG & ftype) {
+        if ((0 == xfp->pdt) || (0xe == xfp->pdt)) /* direct-access or RBC */
+            ftype |= FT_BLOCK;
+        else if (0x1 == xfp->pdt)
+            ftype |= FT_ST;
+    }
+    /* In SPC-4 opcode 0x84, service action 0x3 is called RECEIVE COPY
+     * OPERATING PARAMETERS */
     res = sg_ll_receive_copy_results(xfp->sg_fd, 0x03, 0, rcBuff, rcBuffLen,
-                                     0, verb);
+                                     1, verb);
     if (0 != res)
         return -res;
 
@@ -645,19 +692,22 @@ scsi_operating_parameter(struct xcopy_fp_t *xfp, int is_target)
         fprintf(stderr, "\nOutput response in hex:\n");
         dStrHex((const char *)rcBuff, len, 1);
     }
+    snlid = rcBuff[4] & 0x1;
     max_target_num = rcBuff[8] << 8 | rcBuff[9];
     max_segment_num = rcBuff[10] << 8 | rcBuff[11];
     max_desc_len = rcBuff[12] << 24 | rcBuff[13] << 16 | rcBuff[14] << 8 |
                    rcBuff[15];
     max_segment_len = rcBuff[16] << 24 | rcBuff[17] << 16 |
         rcBuff[18] << 8 | rcBuff[19];
-    xfp->max_bytes = max_segment_len;
+    xfp->max_bytes = max_segment_len ? max_segment_len : ULONG_MAX;
     max_inline_data = rcBuff[20] << 24 | rcBuff[21] << 16 | rcBuff[22] << 8 |
                       rcBuff[23];
     if (verbose) {
-        printf(" >> Receive copy results (report operating parameters):\n");
+        printf(" >> %s response:\n", rec_copy_op_params_str);
+        printf("    Support No List IDentifier (SNLID): %d\n", snlid);
         printf("    Maximum target descriptor count: %lu\n", max_target_num);
-        printf("    Maximum segment descriptor count: %lu\n", max_segment_num);
+        printf("    Maximum segment descriptor count: %lu\n",
+                max_segment_num);
         printf("    Maximum descriptor list length: %lu\n", max_desc_len);
         printf("    Maximum segment length: %lu\n", max_segment_len);
         printf("    Maximum inline data length: %lu\n", max_inline_data);
@@ -671,8 +721,8 @@ scsi_operating_parameter(struct xcopy_fp_t *xfp, int is_target)
             list_id_usage = 0;
     }
     if (verbose) {
-        printf("    Held data limit: %lu (usage: %d)\n",
-               held_data_limit, list_id_usage);
+        printf("    Held data limit: %lu (list_id_usage: %d)\n",
+                held_data_limit, list_id_usage);
         num = rcBuff[28] << 24 | rcBuff[29] << 16 | rcBuff[30] << 8 |
               rcBuff[31];
         printf("    Maximum stream device transfer size: %lu\n", num);
@@ -688,82 +738,82 @@ scsi_operating_parameter(struct xcopy_fp_t *xfp, int is_target)
     for (n = 0; n < rcBuff[43]; n++) {
         switch(rcBuff[44 + n]) {
         case 0x00: /* copy block to stream device */
-            if (!is_target && (xfp->sg_type & FT_BLOCK))
+            if (!is_target && (ftype & FT_BLOCK))
                 valid++;
-            if (is_target && (xfp->sg_type & FT_ST))
+            if (is_target && (ftype & FT_ST))
                 valid++;
             if (verbose)
                 printf("        Copy Block to Stream device\n");
             break;
         case 0x01: /* copy stream to block device */
-            if (!is_target && (xfp->sg_type & FT_ST))
+            if (!is_target && (ftype & FT_ST))
                 valid++;
-            if (is_target && (xfp->sg_type & FT_BLOCK))
+            if (is_target && (ftype & FT_BLOCK))
                 valid++;
             if (verbose)
                 printf("        Copy Stream to Block device\n");
             break;
         case 0x02: /* copy block to block device */
-            if (!is_target && (xfp->sg_type & FT_BLOCK))
+            if (!is_target && (ftype & FT_BLOCK))
                 valid++;
-            if (is_target && (xfp->sg_type & FT_BLOCK))
+            if (is_target && (ftype & FT_BLOCK))
                 valid++;
             if (verbose)
                 printf("        Copy Block to Block device\n");
             break;
         case 0x03: /* copy stream to stream device */
-            if (!is_target && (xfp->sg_type & FT_ST))
+            if (!is_target && (ftype & FT_ST))
                 valid++;
-            if (is_target && (xfp->sg_type & FT_ST))
+            if (is_target && (ftype & FT_ST))
                 valid++;
             if (verbose)
                 printf("        Copy Stream to Stream device\n");
             break;
         case 0x04: /* copy inline data to stream device */
-            if (!is_target && (xfp->sg_type & FT_OTHER))
+            if (!is_target && (ftype & FT_OTHER))
                 valid++;
-            if (is_target && (xfp->sg_type & FT_ST))
+            if (is_target && (ftype & FT_ST))
                 valid++;
             if (verbose)
                 printf("        Copy inline data to Stream device\n");
             break;
         case 0x05: /* copy embedded data to stream device */
-            if (!is_target && (xfp->sg_type & FT_OTHER))
+            if (!is_target && (ftype & FT_OTHER))
                 valid++;
-            if (is_target && (xfp->sg_type & FT_ST))
+            if (is_target && (ftype & FT_ST))
                 valid++;
             if (verbose)
                 printf("        Copy embedded data to Stream device\n");
             break;
         case 0x06: /* Read from stream device and discard */
-            if (!is_target && (xfp->sg_type & FT_ST))
+            if (!is_target && (ftype & FT_ST))
                 valid++;
-            if (is_target && (xfp->sg_type & FT_DEV_NULL))
+            if (is_target && (ftype & FT_DEV_NULL))
                 valid++;
             if (verbose)
                 printf("        Read from stream device and discard\n");
             break;
         case 0x07: /* Verify block or stream device operation */
-            if (!is_target && (xfp->sg_type & (FT_ST | FT_BLOCK)))
+            if (!is_target && (ftype & (FT_ST | FT_BLOCK)))
                 valid++;
-            if (is_target && (xfp->sg_type & (FT_ST | FT_BLOCK)))
+            if (is_target && (ftype & (FT_ST | FT_BLOCK)))
                 valid++;
             if (verbose)
                 printf("        Verify block or stream device operation\n");
             break;
         case 0x08: /* copy block device with offset to stream device */
-            if (!is_target && (xfp->sg_type & FT_BLOCK))
+            if (!is_target && (ftype & FT_BLOCK))
                 valid++;
-            if (is_target && (xfp->sg_type & FT_ST))
+            if (is_target && (ftype & FT_ST))
                 valid++;
             if (verbose)
                 printf("        Copy block device with offset to stream "
                        "device\n");
             break;
         case 0x09: /* copy stream device to block device with offset */
-            if (!is_target && (xfp->sg_type & FT_ST))
+            if (!is_target && (ftype & FT_ST))
                 valid++;
-            if (is_target && (xfp->sg_type & FT_BLOCK))
+            if (is_target && (ftype & FT_BLOCK))
                 valid++;
             if (verbose)
                 printf("        Copy stream device to block device with "
@@ -771,54 +821,54 @@ scsi_operating_parameter(struct xcopy_fp_t *xfp, int is_target)
             break;
         case 0x0a: /* copy block device with offset to block device with
                     * offset */
-            if (!is_target && (xfp->sg_type & FT_BLOCK))
+            if (!is_target && (ftype & FT_BLOCK))
                 valid++;
-            if (is_target && (xfp->sg_type & FT_BLOCK))
+            if (is_target && (ftype & FT_BLOCK))
                 valid++;
             if (verbose)
                 printf("        Copy block device with offset to block "
                        "device with offset\n");
             break;
         case 0x0b: /* copy block device to stream device and hold data */
-            if (!is_target && (xfp->sg_type & FT_BLOCK))
+            if (!is_target && (ftype & FT_BLOCK))
                 valid++;
-            if (is_target && (xfp->sg_type & FT_ST))
+            if (is_target && (ftype & FT_ST))
                 valid++;
             if (verbose)
                 printf("        Copy block device to stream device and hold "
                        "data\n");
             break;
         case 0x0c: /* copy stream device to block device and hold data */
-            if (!is_target && (xfp->sg_type & FT_ST))
+            if (!is_target && (ftype & FT_ST))
                 valid++;
-            if (is_target && (xfp->sg_type & FT_BLOCK))
+            if (is_target && (ftype & FT_BLOCK))
                 valid++;
             if (verbose)
                 printf("        Copy stream device to block device and hold "
                        "data\n");
             break;
         case 0x0d: /* copy block device to block device and hold data */
-            if (!is_target && (xfp->sg_type & FT_BLOCK))
+            if (!is_target && (ftype & FT_BLOCK))
                 valid++;
-            if (is_target && (xfp->sg_type & FT_BLOCK))
+            if (is_target && (ftype & FT_BLOCK))
                 valid++;
             if (verbose)
                 printf("        Copy block device to block device and hold "
                        "data\n");
             break;
         case 0x0e: /* copy stream device to stream device and hold data */
-            if (!is_target && (xfp->sg_type & FT_ST))
+            if (!is_target && (ftype & FT_ST))
                 valid++;
-            if (is_target && (xfp->sg_type & FT_ST))
+            if (is_target && (ftype & FT_ST))
                 valid++;
             if (verbose)
                 printf("        Copy block device to block device and hold "
                        "data\n");
             break;
         case 0x0f: /* read from stream device and hold data */
-            if (!is_target && (xfp->sg_type & FT_ST))
+            if (!is_target && (ftype & FT_ST))
                 valid++;
-            if (is_target && (xfp->sg_type & FT_DEV_NULL))
+            if (is_target && (ftype & FT_DEV_NULL))
                 valid++;
             if (verbose)
                 printf("        Read from stream device and hold data\n");
@@ -874,9 +924,24 @@ scsi_operating_parameter(struct xcopy_fp_t *xfp, int is_target)
                 printf("        SAS target descriptor\n");
             td_list |= TD_SAS;
             break;
+        case 0xea: /* IPv6 */
+            if (verbose)
+                printf("        IPv6 target descriptor\n");
+            td_list |= TD_IPV6;
+            break;
+        case 0xeb: /* IP Copy Service */
+            if (verbose)
+                printf("        IP Copy Service target descriptor\n");
+            td_list |= TD_IP_COPY_SERVICE;
+            break;
+        case 0xfe: /* ROD */
+            if (verbose)
+                printf("        ROD target descriptor\n");
+            td_list |= TD_ROD;
+            break;
         default:
             fprintf(stderr, ">> Unhandled target descriptor 0x%02x\n",
-                   rcBuff[44 + n]);
+                    rcBuff[44 + n]);
             break;
         }
     }
@@ -905,7 +970,7 @@ decode_designation_descriptor(const unsigned char * ucp, int i_len)
     printf("    designator type: %d,  code set: %d\n", desig_type, c_set);
     if (piv && ((1 == assoc) || (2 == assoc)))
         printf("     transport: %s\n",
-               sg_get_trans_proto_str(p_id, sizeof(b), b));
+                sg_get_trans_proto_str(p_id, sizeof(b), b));
 
     switch (desig_type) {
     case 0: /* vendor specific */
@@ -918,8 +983,10 @@ decode_designation_descriptor(const unsigned char * ucp, int i_len)
         }
         if (k)
             printf("      vendor specific: %.*s\n", i_len, ip);
-        else
+        else {
+            printf("      vendor specific:\n");
             dStrHex((const char *)ip, i_len, 0);
+        }
         break;
     case 1: /* T10 vendor identification */
         printf("      vendor id: %.8s\n", ip);
@@ -928,8 +995,8 @@ decode_designation_descriptor(const unsigned char * ucp, int i_len)
         break;
     case 2: /* EUI-64 based */
         if ((8 != i_len) && (12 != i_len) && (16 != i_len)) {
-            fprintf(stderr, "      << expect 8, 12 and 16 byte "
-                    "EUI, got %d>>\n", i_len);
+            fprintf(stderr, "      << expect 8, 12 and 16 byte EUI, got %d>>\n",
+                    i_len);
             dStrHex((const char *)ip, i_len, 0);
             break;
         }
@@ -973,8 +1040,8 @@ decode_designation_descriptor(const unsigned char * ucp, int i_len)
             printf("\n");
         } else if (5 == naa) {
             if (8 != i_len) {
-                fprintf(stderr, "      << unexpected NAA 5 identifier "
-                        "length: 0x%x>>\n", i_len);
+                fprintf(stderr, "      << unexpected NAA 5 identifier length: "
+                        "0x%x>>\n", i_len);
                 dStrHex((const char *)ip, i_len, 0);
                 break;
             }
@@ -1011,8 +1078,8 @@ decode_designation_descriptor(const unsigned char * ucp, int i_len)
         break;
     case 4: /* Relative target port */
         if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
-            fprintf(stderr, "      << expected binary code_set, target "
-                    "port association, length 4>>\n");
+            fprintf(stderr, "      << expected binary code_set, target port "
+                    "association, length 4>>\n");
             dStrHex((const char *)ip, i_len, 0);
             break;
         }
@@ -1041,8 +1108,8 @@ decode_designation_descriptor(const unsigned char * ucp, int i_len)
         break;
     case 7: /* MD5 logical unit identifier */
         if ((1 != c_set) || (0 != assoc)) {
-            printf("      << expected binary code_set, logical "
-                   "unit association>>\n");
+            fprintf(stderr, "      << expected binary code_set, logical unit "
+                    "association>>\n");
             dStrHex((const char *)ip, i_len, 0);
             break;
         }
@@ -1062,7 +1129,28 @@ decode_designation_descriptor(const unsigned char * ucp, int i_len)
          */
         printf("      %s\n", (const char *)ip);
         break;
+    case 9: /* Protocol specific port identifier */
+        /* added in spc4r36, PIV must be set, proto_id indicates */
+        /* whether UAS (USB) or SOP (PCIe) or ... */
+        if (! piv)
+            printf("      >>>> Protocol specific port identifier "
+                    "expects protocol\n"
+                    "           identifier to be valid and it is not\n");
+        if (TPROTO_UAS == p_id) {
+            printf("      USB device address: 0x%x\n", 0x7f & ip[0]);
+            printf("      USB interface number: 0x%x\n", ip[2]);
+        } else if (TPROTO_SOP == p_id) {
+            printf("      PCIe routing ID, bus number: 0x%x\n", ip[0]);
+            printf("          function number: 0x%x\n", ip[1]);
+            printf("          [or device number: 0x%x, function number: "
+                    "0x%x]\n", (0x1f & (ip[1] >> 3)), 0x7 & ip[1]);
+        } else
+            printf("      >>>> unexpected protocol indentifier: 0x%x\n"
+                    "           with Protocol specific port "
+                    "identifier\n", p_id);
+        break;
     default: /* reserved */
+        printf("      reserved designator=0x%x\n", desig_type);
         dStrHex((const char *)ip, i_len, 0);
         break;
     }
@@ -1072,13 +1160,14 @@ static int
 desc_from_vpd_id(int sg_fd, unsigned char *desc, int desc_len,
                  unsigned int block_size, int pad)
 {
-    int res;
+    int res, verb;
     unsigned char rcBuff[256], *ucp, *best = NULL;
     unsigned int len = 254;
     int off = -1, u, i_len, best_len = 0, assoc, desig, f_desig = 0;
 
+    verb = (verbose ? verbose - 1: 0);
     memset(rcBuff, 0xff, len);
-    res = sg_ll_inquiry(sg_fd, 0, 1, 0x83, rcBuff, 4, 1, verbose);
+    res = sg_ll_inquiry(sg_fd, 0, 1, 0x83, rcBuff, 4, 1, verb);
     if (0 != res) {
         fprintf(stderr, "VPD inquiry failed with %d\n", res);
         return res;
@@ -1087,7 +1176,7 @@ desc_from_vpd_id(int sg_fd, unsigned char *desc, int desc_len,
         return SG_LIB_CAT_MALFORMED;
     }
     len = ((rcBuff[2] << 8) + rcBuff[3]) + 4;
-    res = sg_ll_inquiry(sg_fd, 0, 1, 0x83, rcBuff, len, 1, verbose);
+    res = sg_ll_inquiry(sg_fd, 0, 1, 0x83, rcBuff, len, 1, verb);
     if (0 != res) {
         fprintf(stderr, "VPD inquiry failed with %d\n", res);
         return res;
@@ -1112,9 +1201,9 @@ desc_from_vpd_id(int sg_fd, unsigned char *desc, int desc_len,
         }
         assoc = ((ucp[1] >> 4) & 0x3);
         desig = (ucp[1] & 0xf);
-        if (verbose)
-            fprintf(stderr, "    Desc %d: assoc %u desig %u len %d\n", off,
-                    assoc, desig, i_len);
+        if (verbose > 2)
+            fprintf(stderr, "    Desc %d: assoc %u desig %u len %d\n", off, assoc,
+                    desig, i_len);
         /* Descriptor must be less than 16 bytes */
         if (i_len > 16)
             continue;
@@ -1217,14 +1306,16 @@ process_flags(const char * arg, struct xcopy_fp_t * fp)
             *np++ = '\0';
         if (0 == strcmp(cp, "append"))
             fp->append = 1;
-        else if (0 == strcmp(cp, "pad"))
-            fp->pad = 1;
         else if (0 == strcmp(cp, "excl"))
             fp->excl = 1;
-        else if (0 == strcmp(cp, "null"))
-            ;
         else if (0 == strcmp(cp, "flock"))
             ++fp->flock;
+        else if (0 == strcmp(cp, "null"))
+            ;
+        else if (0 == strcmp(cp, "pad"))
+            fp->pad = 1;
+        else if (0 == strcmp(cp, "xcopy"))
+            ++fp->xcopy_given;   /* for ddpt compatibility */
         else {
             fprintf(stderr, "unrecognised flag: %s\n", cp);
             return 1;
@@ -1267,8 +1358,7 @@ open_if(struct xcopy_fp_t * ifp, int verbose)
         }
     }
     if (verbose)
-        fprintf(stderr, "        open input(sg_io), flags=0x%x\n",
-                fl | flags);
+        fprintf(stderr, "        open input(sg_io), flags=0x%x\n", fl | flags);
 
     if (ifp->flock) {
         res = flock(infd, LOCK_EX | LOCK_NB);
@@ -1315,8 +1405,7 @@ open_of(struct xcopy_fp_t * ofp, int verbose)
             goto file_err;
         }
         if (verbose)
-            fprintf(stderr, "        open output(sg_io), flags=0x%x\n",
-                    flags);
+            fprintf(stderr, "        open output(sg_io), flags=0x%x\n", flags);
     } else {
         outfd = -1; /* don't bother opening */
     }
@@ -1336,6 +1425,18 @@ file_err:
     return -SG_LIB_FILE_ERROR;
 }
 
+static int
+num_chs_in_str(const char * s, int slen, int ch)
+{
+    int res = 0;
+
+    while (--slen >= 0) {
+        if (ch == s[slen])
+            ++res;
+    }
+    return res;
+}
+
 
 int
 main(int argc, char * argv[])
@@ -1350,25 +1451,28 @@ main(int argc, char * argv[])
     char * key;
     char * buf;
     int blocks = 0;
+    int num_help = 0;
     int num_xcopy = 0;
-    int res, k;
-    int infd, outfd;
+    int res, k, n, keylen;
+    int infd, outfd, xcopy_fd;
     int ret = 0;
     unsigned char list_id = 1;
+    int list_id_given = 0;
     unsigned char src_desc[256];
     unsigned char dst_desc[256];
     int src_desc_len;
     int dst_desc_len;
     int seg_desc_type;
+    int on_src = 0;
+    int on_dst = 0;
 
-    ifp.fname[0] = '\0';
-    ofp.fname[0] = '\0';
-    ifp.num_sect = -1;
-    ofp.num_sect = -1;
+    ixcf.fname[0] = '\0';
+    oxcf.fname[0] = '\0';
+    ixcf.num_sect = -1;
+    oxcf.num_sect = -1;
 
     if (argc < 2) {
-        fprintf(stderr,
-                "Won't default both IFILE to stdin _and_ OFILE to stdout\n");
+        fprintf(stderr, "Won't default both IFILE to stdin _and_ OFILE to stdout\n");
         fprintf(stderr, "For more information use '--help'\n");
         return SG_LIB_SYNTAX_ERROR;
     }
@@ -1383,9 +1487,10 @@ main(int argc, char * argv[])
             buf++;
         if (*buf)
             *buf++ = '\0';
+        keylen = (int)strlen(key);
         if (0 == strncmp(key, "app", 3)) {
-            ifp.append = sg_get_num(buf);
-            ofp.append = ifp.append;
+            ixcf.append = sg_get_num(buf);
+            oxcf.append = ixcf.append;
         } else if (0 == strcmp(key, "bpt")) {
             bpt = sg_get_num(buf);
             if (-1 == bpt) {
@@ -1406,13 +1511,16 @@ main(int argc, char * argv[])
                 return SG_LIB_SYNTAX_ERROR;
             }
             list_id = (ret & 0xff);
+            list_id_given = 1;
         } else if (0 == strcmp(key, "id_usage")) {
             if (!strncmp(buf, "hold", 4))
                 list_id_usage = 0;
             else if (!strncmp(buf, "discard", 7))
                 list_id_usage = 2;
+            else if (!strncmp(buf, "disable", 7))
+                list_id_usage = 3;
             else {
-                fprintf(stderr, ME "bad argument to 'list_id_usage='\n");
+                fprintf(stderr, ME "bad argument to 'id_usage='\n");
                 return SG_LIB_SYNTAX_ERROR;
             }
         } else if (0 == strcmp(key, "conv"))
@@ -1442,38 +1550,29 @@ main(int argc, char * argv[])
         } else if (0 == strcmp(key, "ibs")) {
             ibs = sg_get_num(buf);
         } else if (strcmp(key, "if") == 0) {
-            if ('\0' != ifp.fname[0]) {
+            if ('\0' != ixcf.fname[0]) {
                 fprintf(stderr, "Second IFILE argument??\n");
                 return SG_LIB_SYNTAX_ERROR;
             } else
-                strncpy(ifp.fname, buf, INOUTF_SZ);
+                strncpy(ixcf.fname, buf, INOUTF_SZ);
         } else if (0 == strcmp(key, "iflag")) {
-            if (process_flags(buf, &ifp)) {
+            if (process_flags(buf, &ixcf)) {
                 fprintf(stderr, ME "bad argument to 'iflag='\n");
                 return SG_LIB_SYNTAX_ERROR;
             }
         } else if (0 == strcmp(key, "obs")) {
             obs = sg_get_num(buf);
         } else if (strcmp(key, "of") == 0) {
-            if ('\0' != ofp.fname[0]) {
+            if ('\0' != oxcf.fname[0]) {
                 fprintf(stderr, "Second OFILE argument??\n");
                 return SG_LIB_SYNTAX_ERROR;
             } else
-                strncpy(ofp.fname, buf, INOUTF_SZ);
+                strncpy(oxcf.fname, buf, INOUTF_SZ);
         } else if (0 == strcmp(key, "oflag")) {
-            if (process_flags(buf, &ofp)) {
+            if (process_flags(buf, &oxcf)) {
                 fprintf(stderr, ME "bad argument to 'oflag='\n");
                 return SG_LIB_SYNTAX_ERROR;
             }
-#if 0
-        } else if (0 == strcmp(key, "retries")) {
-            ifp.retries = sg_get_num(buf);
-            ofp.retries = ifp.retries;
-            if (-1 == ifp.retries) {
-                fprintf(stderr, ME "bad argument to 'retries='\n");
-                return SG_LIB_SYNTAX_ERROR;
-            }
-#endif
         } else if (0 == strcmp(key, "seek")) {
             seek = sg_get_llnum(buf);
             if (-1LL == seek) {
@@ -1490,21 +1589,90 @@ main(int argc, char * argv[])
             do_time = sg_get_num(buf);
         else if (0 == strncmp(key, "verb", 4))
             verbose = sg_get_num(buf);
-        else if ((0 == strncmp(key, "--help", 7)) ||
-                 (0 == strncmp(key, "-h", 2)) ||
-                   (0 == strcmp(key, "-?"))) {
-            usage();
-            return 0;
-        } else if ((0 == strncmp(key, "--vers", 6)) ||
-                   (0 == strcmp(key, "-V"))) {
+        /* look for long options that start with '--' */
+        else if (0 == strncmp(key, "--help", 6))
+            ++num_help;
+        else if (0 == strncmp(key, "--on_dst", 8))
+            ++on_dst;
+        else if (0 == strncmp(key, "--on_src", 8))
+            ++on_src;
+        else if (0 == strncmp(key, "--verb", 6))
+            verbose += 1;
+        else if (0 == strncmp(key, "--vers", 6)) {
             fprintf(stderr, ME "%s\n", version_str);
             return 0;
+        }
+        else if (0 == strncmp(key, "--xcopy", 7))
+            ;   /* ignore; for compatibility with ddpt */
+        /* look for short options that start with a single '-', they can be
+         * concaternated (e.g. '-vvvV') */
+        else if ((keylen > 1) && ('-' == key[0]) && ('-' != key[1])) {
+            res = 0;
+            n = num_chs_in_str(key + 1, keylen - 1, 'h');
+            num_help += n;
+            res += n;
+            n = num_chs_in_str(key + 1, keylen - 1, 'v');
+            verbose += n;
+            res += n;
+            if (num_chs_in_str(key + 1, keylen - 1, 'V')) {
+                fprintf(stderr, "%s\n", version_str);
+                return -1;
+            }
+            n = num_chs_in_str(key + 1, keylen - 1, 'x');
+            /* accept and ignore; for compatibility with ddpt */
+            res += n;
+            if (res < (keylen - 1)) {
+                fprintf(stderr, ME "Unrecognised short option in '%s', try "
+                        "'--help'\n", key);
+                if (0 == num_help)
+                    return -1;
+            }
         } else {
             fprintf(stderr, "Unrecognized option '%s'\n", key);
-            fprintf(stderr, "For more information use '--help'\n");
+            if (num_help)
+                usage(num_help);
+            else
+                fprintf(stderr, "For more information use '--help'\n");
             return SG_LIB_SYNTAX_ERROR;
         }
     }
+    if (num_help) {
+        usage(num_help);
+        return 0;
+    }
+    if (on_src && on_dst) {
+        fprintf(stderr, "Syntax error - either specify --on_src OR "
+                "--on_dst\n");
+        fprintf(stderr, "For more information use '--help'\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if ((! on_src) && (! on_dst)) {
+        if ((!! ixcf.xcopy_given) == (!! oxcf.xcopy_given)) {
+            char * csp;
+            char * cdp;
+
+            csp = getenv(XCOPY_TO_SRC);
+            cdp = getenv(XCOPY_TO_DST);
+            if ((!! csp) == (!! cdp)) {
+#if DEF_XCOPY_SRC0_DST1 == 0
+                on_src = 1;
+#else
+                on_dst = 1;
+#endif
+            } else if (csp)
+                on_src = 1;
+            else
+                on_dst = 1;
+        } else if (ixcf.xcopy_given)
+            on_src = 1;
+        else
+            on_dst = 1;
+    }
+    if (verbose > 1)
+        fprintf(stderr, " >>> Extended Copy(LID1) command will be sent to %s device "
+                "[%s]\n", (on_src ? "src" : "dst"),
+                (on_src ? ixcf.fname : oxcf.fname));
+
     if ((ibs && blk_sz && (ibs != blk_sz)) ||
         (obs && blk_sz && (obs != blk_sz))) {
         fprintf(stderr, "If 'ibs' or 'obs' given must be same as 'bs'\n");
@@ -1520,20 +1688,31 @@ main(int argc, char * argv[])
         fprintf(stderr, "skip and seek cannot be negative\n");
         return SG_LIB_SYNTAX_ERROR;
     }
-    if ((ofp.append > 0) && (seek > 0)) {
+    if ((oxcf.append > 0) && (seek > 0)) {
         fprintf(stderr, "Can't use both append and seek switches\n");
         return SG_LIB_SYNTAX_ERROR;
     }
     if (bpt < 1) {
         fprintf(stderr, "bpt must be greater than 0\n");
         return SG_LIB_SYNTAX_ERROR;
+    } else if (bpt > MAX_BLOCKS_PER_TRANSFER) {
+        fprintf(stderr, "bpt must be less than or equal to %d\n",
+                MAX_BLOCKS_PER_TRANSFER);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (list_id_usage == 3) { /* list_id usage disabled */
+        if (!list_id_given)
+            list_id = 0;
+        if (list_id) {
+            fprintf(stderr, "list_id disabled by id_usage flag\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
     }
 
-#ifdef SG_DEBUG
-    fprintf(stderr, ME "if=%s skip=%" PRId64 " of=%s seek=%" PRId64
-            " count=%" PRId64 "\n", ifp.fname, skip, ofp.fname, seek,
-            dd_count);
-#endif
+    if (verbose > 1)
+        fprintf(stderr, " >>> " ME " if=%s skip=%" PRId64 " of=%s seek=%" PRId64
+                " count=%" PRId64 "\n", ixcf.fname, skip, oxcf.fname, seek,
+                dd_count);
     install_handler(SIGINT, interrupt_handler);
     install_handler(SIGQUIT, interrupt_handler);
     install_handler(SIGPIPE, interrupt_handler);
@@ -1541,134 +1720,141 @@ main(int argc, char * argv[])
 
     infd = STDIN_FILENO;
     outfd = STDOUT_FILENO;
-    ifp.pdt = -1;
-    ofp.pdt = -1;
-    if (ifp.fname[0] && ('-' != ifp.fname[0])) {
-        infd = open_if(&ifp, verbose);
+    ixcf.pdt = -1;
+    oxcf.pdt = -1;
+    if (ixcf.fname[0] && ('-' != ixcf.fname[0])) {
+        infd = open_if(&ixcf, verbose);
         if (infd < 0)
             return -infd;
+    } else {
+        fprintf(stderr, "stdin not acceptable for IFILE\n");
+        return SG_LIB_FILE_ERROR;
     }
 
-    if (ofp.fname[0] && ('-' != ofp.fname[0])) {
-        outfd = open_of(&ofp, verbose);
+    if (oxcf.fname[0] && ('-' != oxcf.fname[0])) {
+        outfd = open_of(&oxcf, verbose);
         if (outfd < -1)
             return -outfd;
+    } else {
+        fprintf(stderr, "stdout not acceptable for OFILE\n");
+        return SG_LIB_FILE_ERROR;
     }
 
-    if (open_sg(&ifp, verbose) < 0)
+    if (open_sg(&ixcf, verbose) < 0)
         return SG_LIB_CAT_INVALID_OP;
 
-    if (open_sg(&ofp, verbose) < 0)
+    if (open_sg(&oxcf, verbose) < 0)
         return SG_LIB_CAT_INVALID_OP;
 
     if ((STDIN_FILENO == infd) && (STDOUT_FILENO == outfd)) {
-        fprintf(stderr,
-                "Can't have both 'if' as stdin _and_ 'of' as stdout\n");
+        fprintf(stderr, "Can't have both 'if' as stdin _and_ 'of' as stdout\n");
         fprintf(stderr, "For more information use '--help'\n");
         return SG_LIB_SYNTAX_ERROR;
     }
 
-    res = scsi_read_capacity(&ifp);
+    res = scsi_read_capacity(&ixcf);
     if (SG_LIB_CAT_UNIT_ATTENTION == res) {
-        fprintf(stderr, "Unit attention (readcap in), continuing\n");
-        res = scsi_read_capacity(&ifp);
+        fprintf(stderr, "Unit attention (%s in), continuing\n", read_cap_str);
+        res = scsi_read_capacity(&ixcf);
     } else if (SG_LIB_CAT_ABORTED_COMMAND == res) {
-        fprintf(stderr, "Aborted command (readcap in), continuing\n");
-        res = scsi_read_capacity(&ifp);
+        fprintf(stderr, "Aborted command (%s in), continuing\n", read_cap_str);
+        res = scsi_read_capacity(&ixcf);
     }
     if (0 != res) {
         if (res == SG_LIB_CAT_INVALID_OP)
-            fprintf(stderr, "read capacity not supported on %s\n",
-                    ifp.fname);
+            fprintf(stderr, "%s command not supported on %s\n", read_cap_str,
+                    ixcf.fname);
         else if (res == SG_LIB_CAT_NOT_READY)
-            fprintf(stderr, "read capacity failed on %s - not "
-                    "ready\n", ifp.fname);
+            fprintf(stderr, "%s failed on %s - not ready\n", read_cap_str,
+                    ixcf.fname);
         else
-            fprintf(stderr, "Unable to read capacity on %s\n", ifp.fname);
-        ifp.num_sect = -1;
-    } else if (ibs && ifp.sect_sz != ibs) {
+            fprintf(stderr, "Unable to %s on %s\n", read_cap_str, ixcf.fname);
+        ixcf.num_sect = -1;
+    } else if (ibs && ixcf.sect_sz != ibs) {
         fprintf(stderr, ">> warning: block size on %s confusion: "
-                "ibs=%d, device claims=%d\n", ifp.fname, ibs, ifp.sect_sz);
+                "ibs=%d, device claims=%d\n", ixcf.fname, ibs, ixcf.sect_sz);
     }
-    if (skip && ifp.num_sect < skip) {
-        fprintf(stderr, "argument to 'skip=' exceeds device size "
-                "(max %"PRId64")\n", ifp.num_sect);
+    if (skip && ixcf.num_sect < skip) {
+        fprintf(stderr, "argument to 'skip=' exceeds device size (max %" PRId64 ")\n",
+                ixcf.num_sect);
         return SG_LIB_SYNTAX_ERROR;
     }
 
-    res = scsi_read_capacity(&ofp);
+    res = scsi_read_capacity(&oxcf);
     if (SG_LIB_CAT_UNIT_ATTENTION == res) {
-        fprintf(stderr, "Unit attention (readcap out), continuing\n");
-        res = scsi_read_capacity(&ofp);
+        fprintf(stderr, "Unit attention (%s out), continuing\n", read_cap_str);
+        res = scsi_read_capacity(&oxcf);
     } else if (SG_LIB_CAT_ABORTED_COMMAND == res) {
-        fprintf(stderr,
-                "Aborted command (readcap out), continuing\n");
-        res = scsi_read_capacity(&ofp);
+        fprintf(stderr, "Aborted command (%s out), continuing\n", read_cap_str);
+        res = scsi_read_capacity(&oxcf);
     }
     if (0 != res) {
         if (res == SG_LIB_CAT_INVALID_OP)
-            fprintf(stderr, "read capacity not supported on %s\n",
-                    ofp.fname);
+            fprintf(stderr, "%s command not supported on %s\n", read_cap_str,
+                    oxcf.fname);
         else
-            fprintf(stderr, "Unable to read capacity on %s\n", ofp.fname);
-        ofp.num_sect = -1;
-    } else if (obs && obs != ofp.sect_sz) {
-        fprintf(stderr, ">> warning: block size on %s confusion: "
-                "obs=%d, device claims=%d\n", ofp.fname, obs, ofp.sect_sz);
+            fprintf(stderr, "Unable to %s on %s\n", read_cap_str, oxcf.fname);
+        oxcf.num_sect = -1;
+    } else if (obs && obs != oxcf.sect_sz) {
+        fprintf(stderr, ">> warning: block size on %s confusion: obs=%d, device "
+                "claims=%d\n", oxcf.fname, obs, oxcf.sect_sz);
     }
-    if (seek && ofp.num_sect < seek) {
-        fprintf(stderr, "argument to 'seek=' exceeds device size "
-                "(max %"PRId64")\n", ofp.num_sect);
+    if (seek && oxcf.num_sect < seek) {
+        fprintf(stderr, "argument to 'seek=' exceeds device size (max %" PRId64 ")\n",
+                oxcf.num_sect);
         return SG_LIB_SYNTAX_ERROR;
     }
     if ((dd_count < 0) || ((verbose > 0) && (0 == dd_count))) {
         if (xcopy_flag_dc == 0) {
-            dd_count = ifp.num_sect - skip;
-            if (dd_count * ifp.sect_sz > (ofp.num_sect - seek) * ofp.sect_sz)
-                dd_count = (ofp.num_sect - seek) * ofp.sect_sz / ifp.sect_sz;
+            dd_count = ixcf.num_sect - skip;
+            if ((dd_count * ixcf.sect_sz) >
+                ((oxcf.num_sect - seek) * oxcf.sect_sz))
+                dd_count = (oxcf.num_sect - seek) * oxcf.sect_sz /
+                           ixcf.sect_sz;
         } else {
-            dd_count = ofp.num_sect - seek;
-            if (dd_count * ofp.sect_sz > (ifp.num_sect - skip) * ifp.sect_sz)
-                dd_count = (ifp.num_sect - skip) * ifp.sect_sz / ofp.sect_sz;
+            dd_count = oxcf.num_sect - seek;
+            if ((dd_count * oxcf.sect_sz) >
+                ((ixcf.num_sect - skip) * ixcf.sect_sz))
+                dd_count = (ixcf.num_sect - skip) * ixcf.sect_sz /
+                           oxcf.sect_sz;
         }
     } else {
         int64_t dd_bytes;
 
         if (xcopy_flag_dc)
-            dd_bytes = dd_count * ofp.sect_sz;
+            dd_bytes = dd_count * oxcf.sect_sz;
         else
-            dd_bytes = dd_count * ifp.sect_sz;
+            dd_bytes = dd_count * ixcf.sect_sz;
 
-        if (dd_bytes > ifp.num_sect * ifp.sect_sz) {
-            fprintf(stderr, "access beyond end of source device "
-                    "(max %"PRId64")\n", ifp.num_sect);
+        if (dd_bytes > ixcf.num_sect * ixcf.sect_sz) {
+            fprintf(stderr, "access beyond end of source device (max %" PRId64 ")\n",
+                    ixcf.num_sect);
             return SG_LIB_SYNTAX_ERROR;
         }
-        if (dd_bytes > ofp.num_sect * ofp.sect_sz) {
-            fprintf(stderr, "access beyond end of target device "
-                    "(max %"PRId64")\n", ofp.num_sect);
+        if (dd_bytes > oxcf.num_sect * oxcf.sect_sz) {
+            fprintf(stderr, "access beyond end of target device (max %" PRId64 ")\n",
+                    oxcf.num_sect);
             return SG_LIB_SYNTAX_ERROR;
         }
     }
 
-    res = scsi_operating_parameter(&ifp, 0);
+    res = scsi_operating_parameter(&ixcf, 0);
     if (res < 0) {
         if (SG_LIB_CAT_UNIT_ATTENTION == -res) {
-            fprintf(stderr, "Unit attention (oper parm), continuing\n");
-            res = scsi_operating_parameter(&ifp, 0);
+            fprintf(stderr, "Unit attention (%s), continuing\n",
+                    rec_copy_op_params_str);
+            res = scsi_operating_parameter(&ixcf, 0);
         } else {
             if (-res == SG_LIB_CAT_INVALID_OP) {
-                fprintf(stderr, "receive copy results not supported on %s\n",
-                        ifp.fname);
-#ifndef SG_DEBUG
+                fprintf(stderr, "%s command not supported on %s\n",
+                        rec_copy_op_params_str, ixcf.fname);
                 return EINVAL;
-#endif
             } else if (-res == SG_LIB_CAT_NOT_READY)
-                fprintf(stderr, "receive copy results failed on %s - not "
-                        "ready\n", ifp.fname);
+                fprintf(stderr, "%s failed on %s - not ready\n",
+                        rec_copy_op_params_str, ixcf.fname);
             else {
-                fprintf(stderr, "Unable to receive copy results on %s\n",
-                        ifp.fname);
+                fprintf(stderr, "Unable to %s on %s\n", rec_copy_op_params_str,
+                        ixcf.fname);
                 return -res;
             }
         }
@@ -1677,10 +1863,11 @@ main(int argc, char * argv[])
 
     if (res & TD_VPD) {
         if (verbose)
-            printf("  >> using VPD identification for source %s\n", ifp.fname);
-        src_desc_len = desc_from_vpd_id(ifp.sg_fd, src_desc, 256,
-                                        ifp.sect_sz, ifp.pad);
-        if (src_desc_len > 256) {
+            printf("  >> using VPD identification for source %s\n",
+                    ixcf.fname);
+        src_desc_len = desc_from_vpd_id(ixcf.sg_fd, src_desc,
+                                 sizeof(src_desc), ixcf.sect_sz, ixcf.pad);
+        if (src_desc_len > (int)sizeof(src_desc)) {
             fprintf(stderr, "source descriptor too large (%d bytes)\n", res);
             return SG_LIB_CAT_MALFORMED;
         }
@@ -1688,24 +1875,23 @@ main(int argc, char * argv[])
         return SG_LIB_CAT_INVALID_OP;
     }
 
-    res = scsi_operating_parameter(&ofp, 1);
+    res = scsi_operating_parameter(&oxcf, 1);
     if (res < 0) {
         if (SG_LIB_CAT_UNIT_ATTENTION == -res) {
-            fprintf(stderr, "Unit attention (oper parm), continuing\n");
-            res = scsi_operating_parameter(&ofp, 1);
+            fprintf(stderr, "Unit attention (%s), continuing\n",
+                    rec_copy_op_params_str);
+            res = scsi_operating_parameter(&oxcf, 1);
         } else {
             if (-res == SG_LIB_CAT_INVALID_OP) {
-                fprintf(stderr, "receive copy results not supported on %s\n",
-                        ofp.fname);
-#ifndef SG_DEBUG
+                fprintf(stderr, "%s command not supported on %s\n",
+                        rec_copy_op_params_str, oxcf.fname);
                 return EINVAL;
-#endif
             } else if (-res == SG_LIB_CAT_NOT_READY)
-                fprintf(stderr, "receive copy results failed on %s - not "
-                        "ready\n", ofp.fname);
+                fprintf(stderr, "%s failed on %s - not ready\n",
+                        rec_copy_op_params_str, oxcf.fname);
             else {
-                fprintf(stderr, "Unable to receive copy results on %s\n",
-                        ofp.fname);
+                fprintf(stderr, "Unable to %s on %s\n", rec_copy_op_params_str,
+                        oxcf.fname);
                 return -res;
             }
         }
@@ -1715,12 +1901,11 @@ main(int argc, char * argv[])
     if (res & TD_VPD) {
         if (verbose)
             printf("  >> using VPD identification for destination %s\n",
-                   ofp.fname);
-        dst_desc_len = desc_from_vpd_id(ofp.sg_fd, dst_desc, 256,
-                                        ofp.sect_sz, ofp.pad);
-        if (dst_desc_len > 256) {
-            fprintf(stderr, "destination descriptor too large (%d bytes)\n",
-                    res);
+                   oxcf.fname);
+        dst_desc_len = desc_from_vpd_id(oxcf.sg_fd, dst_desc,
+                                 sizeof(dst_desc), oxcf.sect_sz, oxcf.pad);
+        if (dst_desc_len > (int)sizeof(dst_desc)) {
+            fprintf(stderr, "destination descriptor too large (%d bytes)\n", res);
             return SG_LIB_CAT_MALFORMED;
         }
     } else {
@@ -1732,39 +1917,41 @@ main(int argc, char * argv[])
         return SG_LIB_CAT_OTHER;
     }
 
-    if ((unsigned long)dd_count < ifp.min_bytes / ifp.sect_sz) {
-        fprintf(stderr, "not enough data to read (min %ld bytes)\n",
-                ofp.min_bytes);
+    if ((unsigned long)dd_count < ixcf.min_bytes / ixcf.sect_sz) {
+        fprintf(stderr, "not enough data to read (min %ld bytes)\n", oxcf.min_bytes);
         return SG_LIB_CAT_OTHER;
     }
-    if ((unsigned long)dd_count < ofp.min_bytes / ofp.sect_sz) {
-        fprintf(stderr, "not enough data to write (min %ld bytes)\n",
-                ofp.min_bytes);
+    if ((unsigned long)dd_count < oxcf.min_bytes / oxcf.sect_sz) {
+        fprintf(stderr, "not enough data to write (min %ld bytes)\n", oxcf.min_bytes);
         return SG_LIB_CAT_OTHER;
     }
 
     if (bpt_given) {
         if (xcopy_flag_dc) {
-            if ((unsigned long)bpt * ofp.sect_sz > ofp.max_bytes) {
+            if ((unsigned long)bpt * oxcf.sect_sz > oxcf.max_bytes) {
                 fprintf(stderr, "bpt too large (max %ld blocks)\n",
-                        ofp.max_bytes / ofp.sect_sz);
+                        oxcf.max_bytes / oxcf.sect_sz);
                 return SG_LIB_SYNTAX_ERROR;
             }
         } else {
-            if ((unsigned long)bpt * ifp.sect_sz > ifp.max_bytes) {
+            if ((unsigned long)bpt * ixcf.sect_sz > ixcf.max_bytes) {
                 fprintf(stderr, "bpt too large (max %ld blocks)\n",
-                        ifp.max_bytes / ifp.sect_sz);
+                        ixcf.max_bytes / ixcf.sect_sz);
                 return SG_LIB_SYNTAX_ERROR;
             }
         }
     } else {
+        unsigned long r;
+
         if (xcopy_flag_dc)
-            bpt = ofp.max_bytes / ofp.sect_sz;
+            r = oxcf.max_bytes / (unsigned long)oxcf.sect_sz;
         else
-            bpt = ifp.max_bytes / ifp.sect_sz;
+            r = ixcf.max_bytes / (unsigned long)ixcf.sect_sz;
+        bpt = (r > MAX_BLOCKS_PER_TRANSFER) ? MAX_BLOCKS_PER_TRANSFER : r;
     }
 
-    seg_desc_type = seg_desc_from_dd_type(ifp.sg_type, 0, ofp.sg_type, 0);
+    seg_desc_type = seg_desc_from_dd_type(simplified_ft(&ixcf), 0,
+                                          simplified_ft(&oxcf), 0);
 
     if (do_time) {
         start_tm.tv_sec = 0;
@@ -1773,24 +1960,25 @@ main(int argc, char * argv[])
         start_tm_valid = 1;
     }
 
-#ifdef SG_DEBUG
-    fprintf(stderr,
-            "Start of loop, count=%"PRId64", bpt=%d, "
-            "lba_in=%"PRId64", lba_out=%"PRId64"\n",
-            dd_count, bpt, skip, skip + seek);
-#endif
+    if (verbose)
+        fprintf(stderr, "Start of loop, count=%" PRId64 ", bpt=%d, lba_in=%" PRId64
+                ", lba_out=%" PRId64 "\n", dd_count, bpt, skip, seek);
+
+    xcopy_fd = (on_src) ? infd : outfd;
+
     while (dd_count > 0) {
         if (dd_count > bpt)
             blocks = bpt;
         else
             blocks = dd_count;
-        res = scsi_extended_copy(infd, list_id, src_desc, src_desc_len,
+        res = scsi_extended_copy(xcopy_fd, list_id, src_desc, src_desc_len,
                                  dst_desc, dst_desc_len, seg_desc_type,
-                                 blocks, skip, skip + seek);
+                                 blocks, skip, seek);
         if (res != 0)
             break;
         in_full += blocks;
         skip += blocks;
+        seek += blocks;
         dd_count -= blocks;
         num_xcopy++;
     }
@@ -1798,11 +1986,11 @@ main(int argc, char * argv[])
     if (do_time)
         calc_duration_throughput(0);
     if (res)
-        fprintf(stderr, "sg_xcopy: failed with error %d (%"PRId64
-                " blocks left)\n", res, dd_count);
+        fprintf(stderr, "sg_xcopy: failed with error %d (%" PRId64 " blocks left)\n",
+                res, dd_count);
     else
-        fprintf(stderr, "sg_xcopy: %"PRId64" blocks, %d command%s\n",
-                in_full, num_xcopy, ((num_xcopy > 1) ? "s" : ""));
+        fprintf(stderr, "sg_xcopy: %" PRId64 " blocks, %d command%s\n", in_full,
+                num_xcopy, ((num_xcopy > 1) ? "s" : ""));
 
     return res;
 }
-- 
1.8.1.4

