sg_vpd: add --inhex=FN to read response in from a file; other cleanups

git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@554 6180dd3e-e324-4e3e-922d-17de1ae2f315
diff --git a/ChangeLog b/ChangeLog
index b0d1060..d76ebca 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,7 +2,7 @@
 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.38 [20140216] [svn: r553]
+Changelog for sg3_utils-1.38 [20140217] [svn: r554]
   - sg_ses: add --dev-slot-num= and --sas-addr=
     - fix --data=- problem with large buffers
     - new --data=@FN to read hex data from file FN
@@ -17,6 +17,7 @@
     - sync version descriptors dated 20131126
     - fix overflow in encode_whitespaces
   - sg_vpd: add LU_CONG to standard inquiry response output
+    - add --inhex=FN to read response in from a file
     - decode Third Party Copy (tpc) page
     - add LTO and DDS vendor pages
     - allow --page=num to restrict --enumerate output
diff --git a/doc/sg_inq.8 b/doc/sg_inq.8
index f5e1f25..4c75952 100644
--- a/doc/sg_inq.8
+++ b/doc/sg_inq.8
@@ -124,6 +124,10 @@
 the \fI\-\-ata\fR option, this utility outputs the ATA IDENTIFY (PACKET)
 DEVICE response in hexadecimal words suitable for input
 to 'hdparm \-\-Istdin'.  See note below.
+.br
+To generate output suitable for placing in a file that can be used by a
+later invocation with the \fI\-\-inhex=FN\fR option, use the '\-HHHH'
+option (e.g. 'sg_inq \-p di -HHHH /dev/sg3 > dev_id.hex').
 .TP
 \fB\-i\fR, \fB\-\-id\fR
 prints the device identification VPD page [0x83].
diff --git a/doc/sg_vpd.8 b/doc/sg_vpd.8
index 626c8bd..47b01c4 100644
--- a/doc/sg_vpd.8
+++ b/doc/sg_vpd.8
@@ -1,18 +1,22 @@
 .TH SG_VPD "8" "February 2014" "sg3_utils\-1.38" SG3_UTILS
 .SH NAME
-sg_vpd \- fetch Vital Product Data (VPD) pages via a SCSI INQUIRY command
+sg_vpd \- fetch SCSI VPD page and/or decode its response
 .SH SYNOPSIS
 .B sg_vpd
 [\fI\-\-enumerate\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-ident\fR]
-[\fI\-\-long\fR] [\fI\-\-maxlen=LEN\fR] [\fI\-\-page=PG\fR]
-[\fI\-\-quiet\fR] [\fI\-\-raw\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR]
-\fIDEVICE\fR
+[\fI\-\-inhex=FN\fR] [\fI\-\-long\fR] [\fI\-\-maxlen=LEN\fR]
+[\fI\-\-page=PG\fR] [\fI\-\-quiet\fR] [\fI\-\-raw\fR] [\fI\-\-verbose\fR]
+[\fI\-\-version\fR] \fIDEVICE\fR
 .SH DESCRIPTION
 .\" Add any additional description here
 .PP
-This utility fetches a Vital Product Data page and decodes it or
-outputs it in ASCII hexadecimal or binary. VPD pages are fetched
-with a SCSI INQUIRY command.
+This utility, when \fIDEVICE\fR is given, fetches a Vital Product Data (VPD)
+page and decodes it or outputs it in ASCII hexadecimal or binary. VPD pages
+are fetched with a SCSI INQUIRY command.
+.PP
+Alternatively the \fI\-\-inhex=FN\fR option can be given. In this case
+\fIFN\fR is assumed to be a file name ('\-' for stdin) containing ASCII
+hexadecimal representing a VPD page response.
 .PP
 Probably the most important page is the Device Identification
 VPD page (page number: 0x83). Since SPC\-3, support for this page
@@ -20,9 +24,8 @@
 using the \fI\-\-ident\fR option.
 .PP
 The reference document used for interpreting VPD pages (and the INQUIRY
-standard response) is T10/1713\-D Revision 36e (SPC\-4, 24 August 2012)
-found at http://www.t10.org . Obsolete and reserved items in the standard
-INQUIRY response output are displayed in square brackets.
+standard response) is T10/1713\-D Revision 36q (SPC\-4, 12 February 2014)
+found at http://www.t10.org .
 .PP
 When no options are given, other than a \fIDEVICE\fR, then the "Supported
 VPD pages" (0x0) VPD page is fetched and decoded.
@@ -46,6 +49,10 @@
 \fB\-H\fR, \fB\-\-hex\fR
 outputs the requested VPD page in ASCII hexadecimal. Can be used multiple
 times, see section on the ATA information vpd page.
+.br
+To generate output suitable for placing in a file that can be used by a
+later invocation with the \fI\-\-inhex=FN\fR option, use the '\-HHHH'
+option (e.g. 'sg_vpd \-p di -HHHH /dev/sg3 > dev_id.hex').
 .TP
 \fB\-i\fR, \fB\-\-ident\fR
 decode the device identification (0x83) VPD page. When used once this option
@@ -53,6 +60,14 @@
 the device identification VPD page's logical unit designator is decoded. In
 the latter case this option has the same effect as '\-\-quiet \-\-page=di_lu'.
 .TP
+\fB\-I\fR, \fB\-\-inhex\fR=\fIFN\fR
+\fIFN\fR is expected to be a file name (or '\-' for stdin) which contains
+ASCII hexadecimal respresenting a VPD page (or a standard INQUIRY) response.
+This utility will then decode that response. The hexadecimal should be
+arranged as 1 or 2 digits representing a byte each of which is whitespace
+or comma separated. Anything from and including a hash mark to the end
+of line is ignored.
+.TP
 \fB\-l\fR, \fB\-\-long\fR
 when decoding some VPD pages, give a little more output. For example the ATA
 Information VPD page only shows the signature (in hex) and the IDENTIFY
diff --git a/src/sg_inq.c b/src/sg_inq.c
index 6b1afbe..5a47f45 100644
--- a/src/sg_inq.c
+++ b/src/sg_inq.c
@@ -68,7 +68,7 @@
  * information [MAINTENANCE IN, service action = 0xc]; see sg_opcodes.
  */
 
-static const char * version_str = "1.28 20140214";    /* SPC-4 rev 36q */
+static const char * version_str = "1.29 20140216";    /* SPC-4 rev 36q */
 
 
 /* Following VPD pages are in ascending page number order */
@@ -2678,7 +2678,7 @@
             printf("    length=%d (0x%x), but only fetched %d bytes", len,
                    len, act_len);
         if ((ansi_version >= 2) && (len < SAFE_STD_INQ_RESP_LEN))
-            printf("  [for SCSI>=2, len>=36 is expected]");
+            printf("\n  [for SCSI>=2, len>=36 is expected]");
         cp = sg_get_pdt_str(peri_type, sizeof(buff), buff);
         if (strlen(cp) > 0)
             printf("   Peripheral device type: %s\n", cp);
@@ -2778,16 +2778,34 @@
     return 0;
 }
 
+/* When sg_fd >= 0 fetch VPD page from device; mxlen is command line
+ * --maxlen=LEN option (def: 0) or -1 for a VPD page with a short length
+ * (1 byte). When sg_fd < 0 then mxlen bytes have been read from
+ * --inhex=FN file. Returns 0 for success. */
 static int
 vpd_fetch_page_from_dev(int sg_fd, unsigned char * rp, int page,
-                        int short_len, int vb, int * rlenp)
+                        int mxlen, int vb, int * rlenp)
 {
-    int res, resid, rlen, len;
+    int res, resid, rlen, len, n;
 
-    res = pt_inquiry(sg_fd, 1, page, rp, DEF_ALLOC_LEN, &resid, 1, vb);
+    if (sg_fd < 0) {
+        len = ((rp[2] << 8) + rp[3]) + 4;
+        if (vb && (len > mxlen))
+            pr2serr("warning: VPD page's length (%d) > bytes in --inhex=FN "
+                    "file (%d)\n",  len , mxlen);
+        if (rlenp)
+            *rlenp = (len < mxlen) ? len : mxlen;
+        return 0;
+    }
+    if (mxlen > MX_ALLOC_LEN) {
+        pr2serr("--maxlen=LEN too long: %d > %d\n", mxlen, MX_ALLOC_LEN);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    n = (mxlen > 0) ? mxlen : DEF_ALLOC_LEN;
+    res = pt_inquiry(sg_fd, 1, page, rp, n, &resid, 1, vb);
     if (res)
         return res;
-    rlen = DEF_ALLOC_LEN - resid;
+    rlen = n - resid;
     if (rlen < 4) {
         pr2serr("VPD response too short (len=%d)\n", rlen);
         return SG_LIB_CAT_MALFORMED;
@@ -2797,7 +2815,7 @@
                 "response\n");
         return SG_LIB_CAT_MALFORMED;
     }
-    if (short_len)
+    if (mxlen < 0)
         len = rp[3] + 4;
     else
         len = ((rp[2] << 8) + rp[3]) + 4;
@@ -2805,7 +2823,7 @@
         if (rlenp)
             *rlenp = len;
         return 0;
-    } else if ((len <= DEF_ALLOC_LEN) && short_len) {
+    } else if (mxlen) {
         if (rlenp)
             *rlenp = rlen;
         return 0;
@@ -2826,7 +2844,7 @@
 }
 
 /* Returns 0 if Unit Serial Number VPD page contents found, else see
-   sg_ll_inquiry() return values */
+ * sg_ll_inquiry() return values */
 static int
 fetch_unit_serial_num(int sg_fd, char * obuff, int obuff_len, int verbose)
 {
@@ -2836,7 +2854,7 @@
     res = 0;
     memset(b, 0xff, 4); /* guard against empty response */
     /* first check if unit serial number VPD page is supported */
-    res = vpd_fetch_page_from_dev(sg_fd, b, VPD_SUPPORTED_VPDS, 1, verbose,
+    res = vpd_fetch_page_from_dev(sg_fd, b, VPD_SUPPORTED_VPDS, -1, verbose,
                                   &len);
     if (0 == res) {
         len -= 4;
@@ -2845,7 +2863,7 @@
                 break;
         }
         if (k < len) {
-            res = vpd_fetch_page_from_dev(sg_fd, b, VPD_UNIT_SERIAL_NUM, 1,
+            res = vpd_fetch_page_from_dev(sg_fd, b, VPD_UNIT_SERIAL_NUM, -1,
                                           verbose, &len);
             if (0 == res) {
                 len -= 4;
@@ -2873,12 +2891,14 @@
 
 /* Process a standard INQUIRY response. Returns 0 if successful */
 static int
-std_inq_process(int sg_fd, const struct opts_t * op)
+std_inq_process(int sg_fd, const struct opts_t * op, int inhex_len)
 {
     int res, len, rlen, act_len;
     char buff[48];
     int verb, resid;
 
+    if (sg_fd < 0)
+        return std_inq_response(op, inhex_len);
     rlen = (op->resp_len > 0) ? op->resp_len : SAFE_STD_INQ_RESP_LEN;
     verb = op->do_verbose;
     res = pt_inquiry(sg_fd, 0, 0, rsp_buff, rlen, &resid, 0, verb);
@@ -2945,6 +2965,8 @@
             pr2serr("unit attention (?)%s\n", buff);
         else if (SG_LIB_CAT_ABORTED_COMMAND == res)
             pr2serr("aborted command%s\n", buff);
+        else if (SG_LIB_CAT_MALFORMED == res)
+            pr2serr("malformed response%s\n", buff);
         else
             pr2serr("res=%d%s\n", res, buff);
         return res;
@@ -3081,7 +3103,7 @@
 
 /* Returns 0 if successful */
 static int
-vpd_mainly_hex(int sg_fd, const struct opts_t * op)
+vpd_mainly_hex(int sg_fd, const struct opts_t * op, int inhex_len)
 {
     int res, len;
     char b[48];
@@ -3089,11 +3111,19 @@
     unsigned char * rp;
 
     rp = rsp_buff;
-    memset(rp, 0, DEF_ALLOC_LEN);
     if ((! op->do_raw) && (op->do_hex < 2))
         printf("VPD INQUIRY, page code=0x%.2x:\n", op->page_num);
-    res = vpd_fetch_page_from_dev(sg_fd, rp, op->page_num, 0, op->do_verbose,
-                                  &len);
+    if (sg_fd < 0) {
+        len = ((rp[2] << 8) + rp[3]) + 4;
+        if (op->do_verbose && (len > inhex_len))
+            pr2serr("warning: VPD page's length (%d) > bytes in --inhex=FN "
+                    "file (%d)\n",  len , inhex_len);
+        res = 0;
+    } else {
+        memset(rp, 0, DEF_ALLOC_LEN);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, op->page_num, op->resp_len,
+                                      op->do_verbose, &len);
+    }
     if (0 == res) {
         if (op->do_raw)
             dStrRaw((const char *)rp, len);
@@ -3101,7 +3131,7 @@
             if (0 == op->page_num)
                 decode_supported_vpd(rp, len, op->do_hex);
             else {
-                if (op->do_hex < 2) {
+                if (op->do_verbose) {
                     cp = sg_get_pdt_str(rp[0] & 0x1f, sizeof(b), b);
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
                            (rp[0] & 0xe0) >> 5, cp);
@@ -3131,358 +3161,24 @@
 
 /* Returns 0 if successful */
 static int
-vpd_response(const struct opts_t * op, int rlen)
+vpd_decode(int sg_fd, const struct opts_t * op, int inhex_len)
 {
-    int len, pdt, pn;
-    unsigned char * rp;
-    const struct svpd_values_name_t * vnp;
-
-    rp = rsp_buff;
-    if (rlen < 4) {
-        pr2serr("INQUIRY VPD response too short\n");
-        return SG_LIB_CAT_MALFORMED;
-    }
-    pn = op->page_num;
-    if (pn != rp[1]) {
-        if (op->page_arg) {
-            vnp = sdp_find_vpd_by_acron(op->page_arg);
-            if (vnp) {
-                pr2serr("requested %s VPD page [0x%x]\n   but --inhex is "
-                        "for page number 0x%x\n", vnp->name, vnp->value,
-                        rp[1]);
-                return SG_LIB_CAT_MALFORMED;
-            }
-        }
-        pr2serr("requested VPD page number [0x%x] differs from that in FN "
-                "[0x%x]\n", pn, rp[1]);
-        return SG_LIB_CAT_MALFORMED;
-    }
-
-    switch (pn) {
-    case VPD_SUPPORTED_VPDS:
-        if (! op->do_raw && ! op->do_export && (op->do_hex < 2))
-            printf("VPD INQUIRY: Supported VPD pages page\n");
-        len = ((rp[2] << 8) + rp[3]) + 4; /* spc4r25 */
-        if (len > rlen) {
-            pr2serr("truncated VPD page in inhex file (%s)\n", op->inhex_fn);
-            len = rlen;
-        }
-        if (op->do_raw)
-            dStrRaw((const char *)rp, len);
-        else
-            decode_supported_vpd(rp, len, op->do_hex);
-        break;
-    case VPD_UNIT_SERIAL_NUM:
-        if (! op->do_raw && ! op->do_export && (op->do_hex < 2))
-            printf("VPD INQUIRY: Unit serial number page\n");
-        len = ((rp[2] << 8) + rp[3]) + 4; /* spc4r25 */
-        if (len > rlen) {
-            pr2serr("truncated VPD page in inhex file (%s)\n", op->inhex_fn);
-            len = rlen;
-        }
-        if (op->do_raw)
-            dStrRaw((const char *)rp, len);
-        else if (op->do_hex)
-            dStrHex((const char *)rp, len, (1 == op->do_hex) ? 0 : -1);
-        else {
-            char obuff[DEF_ALLOC_LEN];
-
-            memset(obuff, 0, sizeof(obuff));
-            len -= 4;
-            if (len >= (int)sizeof(obuff))
-                len = sizeof(obuff) - 1;
-            memcpy(obuff, rp + 4, len);
-            if (op->do_export) {
-                len = encode_whitespaces((unsigned char *)obuff, len);
-                printf("SCSI_IDENT_SERIAL=%s\n", obuff);
-            } else
-                printf("  Unit serial number: %s\n", obuff);
-        }
-        break;
-    case VPD_DEVICE_ID:
-        if (! op->do_raw && ! op->do_export && (op->do_hex < 3))
-            printf("VPD INQUIRY: Device Identification page\n");
-        len = ((rp[2] << 8) + rp[3]) + 4;
-        if (len > rlen) {
-            pr2serr("truncated VPD page in inhex file (%s)\n", op->inhex_fn);
-            len = rlen;
-        }
-        if (op->do_raw)
-            dStrRaw((const char *)rp, len);
-        else if (op->do_hex > 2)
-            dStrHex((const char *)rp, len, -1);
-        else if (op->do_export) {
-            if (len < 4) {
-                pr2serr("Device identification page length too "
-                        "short=%d\n", len);
-            } else {
-                export_dev_ids(rp + 4, len - 4);
-            }
-        } else
-            decode_id_vpd(rp, len, op->do_hex);
-        break;
-    case VPD_SOFTW_INF_ID:
-        if (! op->do_raw && (op->do_hex < 2))
-            printf("VPD INQUIRY: Software interface identification page\n");
-        len = ((rp[2] << 8) + rp[3]) + 4; /* spc4r25 */
-        if (len > rlen) {
-            pr2serr("truncated VPD page in inhex file (%s)\n", op->inhex_fn);
-            len = rlen;
-        }
-        if (op->do_raw)
-            dStrRaw((const char *)rp, len);
-        else
-            decode_softw_inf_id(rp, len, op->do_hex);
-        break;
-    case VPD_MAN_NET_ADDR:
-        if (!op->do_raw && (op->do_hex < 2))
-            printf("VPD INQUIRY: Management network addresses page\n");
-        len = ((rp[2] << 8) + rp[3]) + 4;
-        if (len > rlen) {
-            pr2serr("truncated VPD page in inhex file (%s)\n", op->inhex_fn);
-            len = rlen;
-        }
-        if (op->do_raw)
-            dStrRaw((const char *)rp, len);
-        else
-            decode_net_man_vpd(rp, len, op->do_hex);
-        break;
-    case VPD_MODE_PG_POLICY:
-        if (!op->do_raw && (op->do_hex < 2))
-            printf("VPD INQUIRY: Mode page policy\n");
-        len = ((rp[2] << 8) + rp[3]) + 4;
-        if (len > rlen) {
-            pr2serr("truncated VPD page in inhex file (%s)\n", op->inhex_fn);
-            len = rlen;
-        }
-        if (op->do_raw)
-            dStrRaw((const char *)rp, len);
-        else
-            decode_mode_policy_vpd(rp, len, op->do_hex);
-        break;
-    case VPD_EXT_INQ:
-        if (!op->do_raw && (op->do_hex < 2))
-            printf("VPD INQUIRY: extended INQUIRY data page\n");
-        len = ((rp[2] << 8) + rp[3]) + 4;
-        if (len > rlen) {
-            pr2serr("truncated VPD page in inhex file (%s)\n", op->inhex_fn);
-            len = rlen;
-        }
-        if (op->do_raw)
-            dStrRaw((const char *)rp, len);
-        else
-            decode_x_inq_vpd(rp, len, op->do_hex);
-        break;
-    case VPD_ATA_INFO:
-        if (!op->do_raw && (op->do_hex < 2))
-            printf("VPD INQUIRY: ATA information page\n");
-        len = ((rp[2] << 8) + rp[3]) + 4;
-        if (len > rlen) {
-            pr2serr("truncated VPD page in inhex file (%s)\n", op->inhex_fn);
-            len = rlen;
-        }
-        /* format output for 'hdparm --Istdin' with '-rr' or '-HHH' */
-        if ((2 == op->do_raw) || (3 == op->do_hex))
-            dWordHex((const unsigned short *)(rp + 60),
-                     256, -2, sg_is_big_endian());
-        else if (op->do_raw)
-            dStrRaw((const char *)rp, len);
-        else
-            decode_ata_info_vpd(rp, len, op->do_hex);
-        break;
-    case VPD_POWER_CONDITION:
-        if (!op->do_raw && (op->do_hex < 2))
-            printf("VPD INQUIRY: Power condition page\n");
-        len = ((rp[2] << 8) + rp[3]) + 4;
-        if (len > rlen) {
-            pr2serr("truncated VPD page in inhex file (%s)\n", op->inhex_fn);
-            len = rlen;
-        }
-        if (op->do_raw)
-            dStrRaw((const char *)rp, len);
-        else
-            decode_power_condition(rp, len, op->do_hex);
-        break;
-    case 0xb0:  /* VPD pages in B0h to BFh range depend on pdt */
-        pdt = rp[0] & 0x1f;
-        if (! op->do_raw && (op->do_hex < 2)) {
-            switch (pdt) {
-            case PDT_DISK: case PDT_WO: case PDT_OPTICAL:
-                printf("VPD INQUIRY: Block limits page (SBC)\n");
-                break;
-            case PDT_TAPE: case PDT_MCHANGER:
-                printf("VPD INQUIRY: Sequential access device "
-                       "capabilities (SSC)\n");
-                break;
-            case PDT_OSD:
-                printf("VPD INQUIRY: OSD information (OSD)\n");
-                break;
-            default:
-                printf("VPD INQUIRY: page=0x%x, pdt=0x%x\n", 0xb0, pdt);
-                break;
-            }
-        }
-        len = ((rp[2] << 8) + rp[3]) + 4;
-        if (len > rlen) {
-            pr2serr("truncated VPD page in inhex file (%s)\n", op->inhex_fn);
-            len = rlen;
-        }
-        if (op->do_raw)
-            dStrRaw((const char *)rp, len);
-        else
-            decode_b0_vpd(rp, len, op->do_hex);
-        break;
-    case 0xb1:  /* VPD pages in B0h to BFh range depend on pdt */
-        pdt = rp[0] & 0x1f;
-        if (! op->do_raw && (op->do_hex < 2)) {
-            switch (pdt) {
-            case PDT_DISK: case PDT_WO: case PDT_OPTICAL:
-                printf("VPD INQUIRY: Block device characteristics page "
-                       "(SBC)\n");
-                break;
-            case PDT_TAPE: case PDT_MCHANGER:
-                printf("Manufactured assigned serial number VPD page "
-                       "(SSC):\n");
-                break;
-            case PDT_OSD:
-                printf("Security token VPD page (OSD):\n");
-                break;
-            case PDT_ADC:
-                printf("Manufactured assigned serial number VPD page "
-                       "(ADC):\n");
-                break;
-            default:
-                printf("VPD INQUIRY: page=0x%x, pdt=0x%x\n", 0xb1, pdt);
-                break;
-            }
-        }
-        len = ((rp[2] << 8) + rp[3]) + 4;
-        if (len > rlen) {
-            pr2serr("truncated VPD page in inhex file (%s)\n", op->inhex_fn);
-            len = rlen;
-        }
-        if (op->do_raw)
-            dStrRaw((const char *)rp, len);
-        else
-            decode_b1_vpd(rp, len, op->do_hex);
-        break;
-    case 0xb2:  /* VPD pages in B0h to BFh range depend on pdt */
-        pr2serr(" Only hex output supported. sg_vpd decodes the B2h page.\n");
-        return SG_LIB_CAT_OTHER;
-    case 0xb3:  /* VPD pages in B0h to BFh range depend on pdt */
-        pdt = rp[0] & 0x1f;
-        if (! op->do_raw && (op->do_hex < 2)) {
-            switch (pdt) {
-            case PDT_DISK: case PDT_WO: case PDT_OPTICAL:
-                printf("VPD INQUIRY: Referrals VPD page (SBC)\n");
-                break;
-            default:
-                printf("VPD INQUIRY: page=0x%x, pdt=0x%x\n", 0xb3, pdt);
-                break;
-            }
-        }
-        len = ((rp[2] << 8) + rp[3]) + 4;
-        if (len > rlen) {
-            pr2serr("truncated VPD page in inhex file (%s)\n", op->inhex_fn);
-            len = rlen;
-        }
-        if (op->do_raw)
-            dStrRaw((const char *)rp, len);
-        else
-            decode_b3_vpd(rp, len, op->do_hex);
-        break;
-    case VPD_UPR_EMC:   /* 0xc0 */
-        if (!op->do_raw && (op->do_hex < 2))
-            printf("VPD INQUIRY: Unit Path Report Page (EMC)\n");
-        len = rp[3] + 4;
-        if (len > rlen) {
-            pr2serr("truncated VPD page in inhex file (%s)\n", op->inhex_fn);
-            len = rlen;
-        }
-        if (op->do_raw)
-            dStrRaw((const char *)rp, len);
-        else
-            decode_upr_vpd_c0_emc(rp, len, op->do_hex);
-        break;
-    case VPD_RDAC_VERS:         /* 0xc2 */
-        if (!op->do_raw && (op->do_hex < 2))
-            printf("VPD INQUIRY: Software Version (RDAC)\n");
-        len = rp[3] + 4;
-        if (len > rlen) {
-            pr2serr("truncated VPD page in inhex file (%s)\n", op->inhex_fn);
-            len = rlen;
-        }
-        if (op->do_raw)
-            dStrRaw((const char *)rp, len);
-        else
-            decode_rdac_vpd_c2(rp, len, op->do_hex);
-        break;
-    case VPD_RDAC_VAC:          /* 0xc9 */
-        if (!op->do_raw && (op->do_hex < 2))
-            printf("VPD INQUIRY: Volume Access Control (RDAC)\n");
-        len = rp[3] + 4;
-        if (len > rlen) {
-            pr2serr("truncated VPD page in inhex file (%s)\n", op->inhex_fn);
-            len = rlen;
-        }
-        if (op->do_raw)
-            dStrRaw((const char *)rp, len);
-        else
-            decode_rdac_vpd_c9(rp, len, op->do_hex);
-        break;
-    case VPD_SCSI_PORTS:
-        if (!op->do_raw && (op->do_hex < 2))
-            printf("VPD INQUIRY: SCSI Ports page\n");
-        len = ((rp[2] << 8) + rp[3]) + 4;
-        if (len > rlen) {
-            pr2serr("truncated VPD page in inhex file (%s)\n", op->inhex_fn);
-            len = rlen;
-        }
-        if (op->do_raw)
-            dStrRaw((const char *)rp, len);
-        else
-            decode_scsi_ports_vpd(rp, len, op->do_hex);
-        break;
-    default:
-        if ((pn > 0) && (pn < 0x80)) {
-            if (!op->do_raw && (op->do_hex < 2))
-                printf("VPD INQUIRY: ASCII information page, FRU code=0x%x\n",
-                       pn);
-            len = ((rp[2] << 8) + rp[3]) + 4;
-            if (len > rlen) {
-                pr2serr("truncated VPD page in inhex file (%s)\n",
-                        op->inhex_fn);
-                len = rlen;
-            }
-            if (op->do_raw)
-                dStrRaw((const char *)rp, len);
-            else
-                decode_ascii_inf(rp, len, op->do_hex);
-        } else {
-            pr2serr(" Only hex output supported. sg_vpd and sdparm decode "
-                    "more VPD pages.\n");
-            return SG_LIB_CAT_OTHER;
-        }
-    }
-    return 0;
-}
-
-/* Returns 0 if successful */
-static int
-vpd_get_and_decode(int sg_fd, const struct opts_t * op)
-{
-    int len, pdt, pn, vb;
+    int len, pdt, pn, vb, mxlen;
     int res = 0;
     unsigned char * rp;
 
     pn = op->page_num;
     rp = rsp_buff;
     vb = op->do_verbose;
+    if (sg_fd >= 0)
+        mxlen = op->resp_len;
+    else
+        mxlen = inhex_len;
     switch (pn) {
     case VPD_SUPPORTED_VPDS:
         if (!op->do_raw && (op->do_hex < 2))
             printf("VPD INQUIRY: Supported VPD pages page\n");
-        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, 0, vb, &len);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
         if (res)
             break;
         if (op->do_raw)
@@ -3496,7 +3192,7 @@
     case VPD_UNIT_SERIAL_NUM:
         if (! op->do_raw && ! op->do_export && (op->do_hex < 2))
             printf("VPD INQUIRY: Unit serial number page\n");
-        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, 0, vb, &len);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
         if (res)
             break;
         if (op->do_raw)
@@ -3523,7 +3219,7 @@
     case VPD_DEVICE_ID:
         if (! op->do_raw && ! op->do_export && (op->do_hex < 3))
             printf("VPD INQUIRY: Device Identification page\n");
-        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, 0, vb, &len);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
         if (res)
             break;
         if (op->do_raw)
@@ -3538,7 +3234,7 @@
     case VPD_SOFTW_INF_ID:
         if (! op->do_raw && (op->do_hex < 2))
             printf("VPD INQUIRY: Software interface identification page\n");
-        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, 0, vb, &len);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
         if (res)
             break;
         if (op->do_raw)
@@ -3549,7 +3245,7 @@
     case VPD_MAN_NET_ADDR:
         if (!op->do_raw && (op->do_hex < 2))
             printf("VPD INQUIRY: Management network addresses page\n");
-        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, 0, vb, &len);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
         if (res)
             break;
         if (op->do_raw)
@@ -3560,7 +3256,7 @@
     case VPD_MODE_PG_POLICY:
         if (!op->do_raw && (op->do_hex < 2))
             printf("VPD INQUIRY: Mode page policy\n");
-        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, 0, vb, &len);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
         if (res)
             break;
         if (op->do_raw)
@@ -3571,7 +3267,7 @@
     case VPD_EXT_INQ:
         if (!op->do_raw && (op->do_hex < 2))
             printf("VPD INQUIRY: extended INQUIRY data page\n");
-        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, 0, vb, &len);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
         if (res)
             break;
         if (op->do_raw)
@@ -3582,7 +3278,7 @@
     case VPD_ATA_INFO:
         if (!op->do_raw && (op->do_hex < 2))
             printf("VPD INQUIRY: ATA information page\n");
-        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, 0, vb, &len);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
         if (res)
             break;
         /* format output for 'hdparm --Istdin' with '-rr' or '-HHH' */
@@ -3597,7 +3293,7 @@
     case VPD_POWER_CONDITION:
         if (!op->do_raw && (op->do_hex < 2))
             printf("VPD INQUIRY: Power condition page\n");
-        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, 0, vb, &len);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
         if (res)
             break;
         if (op->do_raw)
@@ -3606,7 +3302,7 @@
             decode_power_condition(rp, len, op->do_hex);
         break;
     case 0xb0:  /* VPD pages in B0h to BFh range depend on pdt */
-        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, 0, vb, &len);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
         if (0 == res) {
             pdt = rp[0] & 0x1f;
             if (! op->do_raw && (op->do_hex < 2)) {
@@ -3634,7 +3330,7 @@
             pr2serr("VPD INQUIRY: page=0xb0\n");
         break;
     case 0xb1:  /* VPD pages in B0h to BFh range depend on pdt */
-        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, 0, vb, &len);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
         if (0 == res) {
             pdt = rp[0] & 0x1f;
             if (! op->do_raw && (op->do_hex < 2)) {
@@ -3670,9 +3366,9 @@
         if (!op->do_raw && (op->do_hex < 2))
             pr2serr(" Only hex output supported. sg_vpd decodes the B2h "
                     "page.\n");
-        return vpd_mainly_hex(sg_fd, op);
+        return vpd_mainly_hex(sg_fd, op, inhex_len);
     case 0xb3:  /* VPD pages in B0h to BFh range depend on pdt */
-        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, 0, vb, &len);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
         if (0 == res) {
             pdt = rp[0] & 0x1f;
             if (! op->do_raw && (op->do_hex < 2)) {
@@ -3695,7 +3391,7 @@
     case VPD_UPR_EMC:   /* 0xc0 */
         if (!op->do_raw && (op->do_hex < 2))
             printf("VPD INQUIRY: Unit Path Report Page (EMC)\n");
-        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, 1, vb, &len);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, -1, vb, &len);
         if (res)
             break;
         if (op->do_raw)
@@ -3706,7 +3402,7 @@
     case VPD_RDAC_VERS:         /* 0xc2 */
         if (!op->do_raw && (op->do_hex < 2))
             printf("VPD INQUIRY: Software Version (RDAC)\n");
-        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, 1, vb, &len);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, -1, vb, &len);
         if (res)
             break;
         if (op->do_raw)
@@ -3717,7 +3413,7 @@
     case VPD_RDAC_VAC:          /* 0xc9 */
         if (!op->do_raw && (op->do_hex < 2))
             printf("VPD INQUIRY: Volume Access Control (RDAC)\n");
-        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, 1, vb, &len);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, -1, vb, &len);
         if (res)
             break;
         if (op->do_raw)
@@ -3728,7 +3424,7 @@
     case VPD_SCSI_PORTS:
         if (!op->do_raw && (op->do_hex < 2))
             printf("VPD INQUIRY: SCSI Ports page\n");
-        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, 0, vb, &len);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
         if (res)
             break;
         if (op->do_raw)
@@ -3741,7 +3437,7 @@
             if (!op->do_raw && (op->do_hex < 2))
                 printf("VPD INQUIRY: ASCII information page, FRU code=0x%x\n",
                        pn);
-            res = vpd_fetch_page_from_dev(sg_fd, rp, pn, 0, vb, &len);
+            res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
             if (0 == res) {
                 if (op->do_raw)
                     dStrRaw((const char *)rp, len);
@@ -3752,7 +3448,7 @@
             if (op->do_hex < 2)
                 pr2serr(" Only hex output supported. sg_vpd and sdparm "
                         "decode more VPD pages.\n");
-            return vpd_mainly_hex(sg_fd, op);
+            return vpd_mainly_hex(sg_fd, op, inhex_len);
         }
     }
     if (res) {
@@ -3886,21 +3582,20 @@
                       sizeof(rsp_buff)))
             return SG_LIB_FILE_ERROR;
         if (-1 == op->page_num) {       /* may be able to deduce VPD page */
+            if (op->page_pdt < 0)
+                op->page_pdt = 0x1f & rsp_buff[0];
             if ((0x2 == (0xf & rsp_buff[3])) && (rsp_buff[2] > 2)) {
                 if (op->do_verbose)
                     pr2serr("Guessing from --inhex= this is a standard "
                             "INQUIRY\n");
-                if (op->page_pdt < 0)
-                    op->page_pdt = 0x1f & rsp_buff[0];
-            } else if (rsp_buff[2] < 2) {
+            } else if (rsp_buff[2] <= 2) {
                 if (op->do_verbose)
                     pr2serr("Guessing from --inhex this is VPD page 0x%x\n",
                             rsp_buff[1]);
                 op->page_num = rsp_buff[1];
-                if (op->page_pdt < 0)
-                    op->page_pdt = 0x1f & rsp_buff[0];
-                ++op->do_decode;
                 ++op->do_vpd;
+                if ((1 != op->do_hex) && (0 == op->do_raw))
+                    ++op->do_decode;
             } else {
                 if (op->do_verbose)
                     pr2serr("page number unclear from --inhex, hope it's a "
@@ -3976,10 +3671,13 @@
         }
     }
     if (op->inhex_fn) {
-        if (op->do_vpd)
-            return vpd_response(op, inhex_len);
-        else
-            return std_inq_response(op, inhex_len);
+        if (op->do_vpd) {
+            if (op->do_decode)
+                return vpd_decode(-1, op, inhex_len);
+            else
+                return vpd_mainly_hex(-1, op, inhex_len);
+        } else
+            return std_inq_process(-1, op, inhex_len);
     }
 
 #if defined(O_NONBLOCK) && defined(O_RDONLY)
@@ -4026,7 +3724,7 @@
 
     if ((! op->do_cmddt) && (! op->do_vpd)) {
         /* So it's a standard INQUIRY, try ATA IDENTIFY if that fails */
-        ret = std_inq_process(sg_fd, op);
+        ret = std_inq_process(sg_fd, op, -1);
         if (ret)
             goto err_out;
     } else if (op->do_cmddt) {
@@ -4037,11 +3735,11 @@
             goto err_out;
     } else if (op->do_vpd) {
         if (op->do_decode) {
-            ret = vpd_get_and_decode(sg_fd, op);
+            ret = vpd_decode(sg_fd, op, -1);
             if (ret)
                 goto err_out;
         } else {
-            ret = vpd_mainly_hex(sg_fd, op);
+            ret = vpd_mainly_hex(sg_fd, op, -1);
             if (ret)
                 goto err_out;
         }
diff --git a/src/sg_vpd.c b/src/sg_vpd.c
index 943adca..56ee7eb 100644
--- a/src/sg_vpd.c
+++ b/src/sg_vpd.c
@@ -21,6 +21,7 @@
 #endif
 #include "sg_lib.h"
 #include "sg_cmds_basic.h"
+#include "sg_pt.h"
 
 /* This utility program was originally written for the Linux OS SCSI subsystem.
 
@@ -31,7 +32,7 @@
 
 */
 
-static const char * version_str = "0.79 20140215";    /* spc4r36 + sbc3r35 */
+static const char * version_str = "0.79 20140216";    /* spc4r36 + sbc3r35 */
         /* And with sbc3r35, vale Mark Evans */
 
 void svpd_enumerate_vendor(void);
@@ -90,6 +91,11 @@
 #define MX_ALLOC_LEN (0xc000 + 0x80)
 #define VPD_ATA_INFO_LEN  572
 
+#define SENSE_BUFF_LEN  64       /* Arbitrary, could be larger */
+#define INQUIRY_CMD     0x12
+#define INQUIRY_CMDLEN  6
+#define DEF_PT_TIMEOUT  60       /* 60 seconds */
+
 
 /* This structure is a duplicate of one of the same name in sg_vpd_vendor.c .
    Take care that both have the same fields (and types). */
@@ -116,6 +122,7 @@
         {"help", no_argument, 0, 'h'},
         {"hex", no_argument, 0, 'H'},
         {"ident", no_argument, 0, 'i'},
+        {"inhex", required_argument, 0, 'I'},
         {"long", no_argument, 0, 'l'},
         {"maxlen", required_argument, 0, 'm'},
         {"page", required_argument, 0, 'p'},
@@ -206,10 +213,10 @@
 usage()
 {
     pr2serr("Usage: sg_vpd  [--enumerate] [--help] [--hex] [--ident] "
-            "[--long]\n"
-            "               [--maxlen=LEN] [--page=PG] [--quiet] [--raw] "
-            "[--verbose]\n"
-            "               [--version] DEVICE\n");
+            "[--inhex=fn]\n"
+            "               [--long] [--maxlen=LEN] [--page=PG] [--quiet] "
+            "[--raw]\n"
+            "               [--verbose] [--version] DEVICE\n");
     pr2serr("  where:\n"
             "    --enumerate|-e    enumerate known VPD pages names (ignore "
             "DEVICE),\n"
@@ -220,6 +227,8 @@
             "twice for\n"
             "                    short logical unit designator (equiv: "
             "'-qp di_lu')\n"
+            "    --inhex=FN|-I FN    use ASCII hex in file FN instead of "
+            "DEVICE\n"
             "    --long|-l       perform extra decoding\n"
             "    --maxlen=LEN|-m LEN    max response length (allocation "
             "length in cdb)\n"
@@ -232,9 +241,294 @@
             "    --raw|-r        output page in binary\n"
             "    --verbose|-v    increase verbosity\n"
             "    --version|-V    print version string and exit\n\n"
-            "Fetch Vital Product Data (VPD) page using SCSI INQUIRY. To "
-            "list available\npages use '-e'. And '-p -1' yields the "
-            "standard INQUIRY response.\n");
+            "Fetch Vital Product Data (VPD) page using SCSI INQUIRY or "
+            "decodes VPD\npage response held in file FN. To list available "
+            "pages use '-e'. Also\n'-p -1' yields the standard INQUIRY "
+            "response.\n");
+}
+
+/* Read ASCII hex bytes from fname (a file named '-' taken as stdin).
+ * There should be either one entry per line or a comma, space or tab
+ * separated list of bytes. If no_space is set then a string of ACSII hex
+ * digits is expected, 2 per byte. Everything from and including a '#'
+ * on a line is ignored.  Returns 0 if ok, or 1 if error. */
+static int
+f2hex_arr(const char * fname, int no_space, unsigned char * mp_arr,
+          int * mp_arr_len, int max_arr_len)
+{
+    int fn_len, in_len, k, j, m, split_line;
+    unsigned int h;
+    const char * lcp;
+    FILE * fp;
+    char line[512];
+    char carry_over[4];
+    int off = 0;
+
+    if ((NULL == fname) || (NULL == mp_arr) || (NULL == mp_arr_len))
+        return 1;
+    fn_len = strlen(fname);
+    if (0 == fn_len)
+        return 1;
+    if ((1 == fn_len) && ('-' == fname[0]))        /* read from stdin */
+        fp = stdin;
+    else {
+        fp = fopen(fname, "r");
+        if (NULL == fp) {
+            pr2serr("Unable to open %s for reading\n", fname);
+            return 1;
+        }
+    }
+
+    carry_over[0] = 0;
+    for (j = 0; j < 512; ++j) {
+        if (NULL == fgets(line, sizeof(line), fp))
+            break;
+        in_len = strlen(line);
+        if (in_len > 0) {
+            if ('\n' == line[in_len - 1]) {
+                --in_len;
+                line[in_len] = '\0';
+                split_line = 0;
+            } else
+                split_line = 1;
+        }
+        if (in_len < 1) {
+            carry_over[0] = 0;
+            continue;
+        }
+        if (carry_over[0]) {
+            if (isxdigit(line[0])) {
+                carry_over[1] = line[0];
+                carry_over[2] = '\0';
+                if (1 == sscanf(carry_over, "%x", &h))
+                    mp_arr[off - 1] = h;       /* back up and overwrite */
+                else {
+                    pr2serr("f2hex_arr: carry_over error ['%s'] around line "
+                            "%d\n", carry_over, j + 1);
+                    goto bad;
+                }
+                lcp = line + 1;
+                --in_len;
+            } else
+                lcp = line;
+            carry_over[0] = 0;
+        } else
+            lcp = line;
+
+        m = strspn(lcp, " \t");
+        if (m == in_len)
+            continue;
+        lcp += m;
+        in_len -= m;
+        if ('#' == *lcp)
+            continue;
+        k = strspn(lcp, "0123456789aAbBcCdDeEfF ,\t");
+        if ((k < in_len) && ('#' != lcp[k])) {
+            pr2serr("f2hex_arr: syntax error at line %d, pos %d\n",
+                    j + 1, m + k + 1);
+            goto bad;
+        }
+        if (no_space) {
+            for (k = 0; isxdigit(*lcp) && isxdigit(*(lcp + 1));
+                 ++k, lcp += 2) {
+                if (1 != sscanf(lcp, "%2x", &h)) {
+                    pr2serr("f2hex_arr: bad hex number in line %d, "
+                            "pos %d\n", j + 1, (int)(lcp - line + 1));
+                    goto bad;
+                }
+                if ((off + k) >= max_arr_len) {
+                    pr2serr("f2hex_arr: array length exceeded\n");
+                    goto bad;
+                }
+                mp_arr[off + k] = h;
+            }
+            if (isxdigit(*lcp) && (! isxdigit(*(lcp + 1))))
+                carry_over[0] = *lcp;
+            off += k;
+        } else {
+            for (k = 0; k < 1024; ++k) {
+                if (1 == sscanf(lcp, "%x", &h)) {
+                    if (h > 0xff) {
+                        pr2serr("f2hex_arr: hex number larger than "
+                                "0xff in line %d, pos %d\n", j + 1,
+                                (int)(lcp - line + 1));
+                        goto bad;
+                    }
+                    if (split_line && (1 == strlen(lcp))) {
+                        /* single trailing hex digit might be a split pair */
+                        carry_over[0] = *lcp;
+                    }
+                    if ((off + k) >= max_arr_len) {
+                        pr2serr("f2hex_arr: array length exceeded\n");
+                        goto bad;
+                    }
+                    mp_arr[off + k] = h;
+                    lcp = strpbrk(lcp, " ,\t");
+                    if (NULL == lcp)
+                        break;
+                    lcp += strspn(lcp, " ,\t");
+                    if ('\0' == *lcp)
+                        break;
+                } else {
+                    if ('#' == *lcp) {
+                        --k;
+                        break;
+                    }
+                    pr2serr("f2hex_arr: error in line %d, at pos %d\n", j + 1,
+                            (int)(lcp - line + 1));
+                    goto bad;
+                }
+            }
+            off += (k + 1);
+        }
+    }
+    *mp_arr_len = off;
+    if (stdin != fp)
+        fclose(fp);
+    return 0;
+bad:
+    if (stdin != fp)
+        fclose(fp);
+    return 1;
+}
+
+/* Local version of sg_ll_inquiry() [found in libsgutils] that additionally
+ * passes back resid. Same return values as sg_ll_inquiry() (0 is good). */
+static int
+pt_inquiry(int sg_fd, int evpd, int pg_op, void * resp, int mx_resp_len,
+           int * residp, int noisy, int verbose)
+{
+    int res, ret, k, sense_cat;
+    unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    unsigned char * up;
+    struct sg_pt_base * ptvp;
+
+    if (evpd)
+        inqCmdBlk[1] |= 1;
+    inqCmdBlk[2] = (unsigned char)pg_op;
+    /* 16 bit allocation length (was 8) is a recent SPC-3 addition */
+    inqCmdBlk[3] = (unsigned char)((mx_resp_len >> 8) & 0xff);
+    inqCmdBlk[4] = (unsigned char)(mx_resp_len & 0xff);
+    if (verbose) {
+        pr2serr("    inquiry cdb: ");
+        for (k = 0; k < INQUIRY_CMDLEN; ++k)
+            pr2serr("%02x ", inqCmdBlk[k]);
+        pr2serr("\n");
+    }
+    if (resp && (mx_resp_len > 0)) {
+        up = (unsigned char *)resp;
+        up[0] = 0x7f;   /* defensive prefill */
+        if (mx_resp_len > 4)
+            up[4] = 0;
+    }
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2serr("inquiry: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, inqCmdBlk, sizeof(inqCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "inquiry", res, mx_resp_len, sense_b,
+                               noisy, verbose, &sense_cat);
+    if (residp)
+        *residp = get_scsi_pt_resid(ptvp);
+    destruct_scsi_pt_obj(ptvp);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_INVALID_OP:
+        case SG_LIB_CAT_ILLEGAL_REQ:
+        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 if (ret < 4) {
+        if (verbose)
+            pr2serr("inquiry: got too few bytes (%d)\n", ret);
+        ret = SG_LIB_CAT_MALFORMED;
+    } else
+        ret = 0;
+
+    return ret;
+}
+
+/* mxlen is command line --maxlen=LEN option (def: 0) or -1 for a VPD page
+ * with a short length (1 byte). Returns 0 for success. */
+int     /* global: use by sg_vpd_vendor.c */
+vpd_fetch_page_from_dev(int sg_fd, unsigned char * rp, int page,
+                        int mxlen, int vb, int * rlenp)
+{
+    int res, resid, rlen, len, n;
+
+    if (sg_fd < 0) {
+        len = ((rp[2] << 8) + rp[3]) + 4;
+        if (vb && (len > mxlen))
+            pr2serr("warning: VPD page's length (%d) > bytes in --inhex=FN "
+                    "file (%d)\n",  len , mxlen);
+        if (rlenp)
+            *rlenp = (len < mxlen) ? len : mxlen;
+        return 0;
+    }
+    if (mxlen > MX_ALLOC_LEN) {
+        pr2serr("--maxlen=LEN too long: %d > %d\n", mxlen, MX_ALLOC_LEN);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    n = (mxlen > 0) ? mxlen : DEF_ALLOC_LEN;
+    res = pt_inquiry(sg_fd, 1, page, rp, n, &resid, 1, vb);
+    if (res)
+        return res;
+    rlen = n - resid;
+    if (rlen < 4) {
+        pr2serr("VPD response too short (len=%d)\n", rlen);
+        return SG_LIB_CAT_MALFORMED;
+    }
+    if (page != rp[1]) {
+        pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
+                "response\n");
+        n = (rlen < 32) ? rlen : 32;
+        if (vb) {
+            pr2serr("First %d bytes of bad response\n", n);
+            dStrHexErr((const char *)rp, n, 0);
+        }
+        return SG_LIB_CAT_MALFORMED;
+    }
+    if (mxlen < 0)
+        len = rp[3] + 4;
+    else
+        len = ((rp[2] << 8) + rp[3]) + 4;
+    if (len <= rlen) {
+        if (rlenp)
+            *rlenp = len;
+        return 0;
+    } else if (mxlen) {
+        if (rlenp)
+            *rlenp = rlen;
+        return 0;
+    }
+    if (len > MX_ALLOC_LEN) {
+        pr2serr("response length too long: %d > %d\n", len, MX_ALLOC_LEN);
+        return SG_LIB_CAT_MALFORMED;
+    } else {
+        res = pt_inquiry(sg_fd, 1, page, rp, len, &resid, 1, vb);
+        if (res)
+            return res;
+        rlen = len - resid;
+        /* assume it is well behaved: hence page and len still same */
+        if (rlenp)
+            *rlenp = rlen;
+        return 0;
+    }
 }
 
 static const struct svpd_values_name_t *
@@ -466,7 +760,7 @@
     unsigned char * ucp;
 
     if ((1 == do_hex) || (do_hex > 2)) {
-        dStrHex((const char *)buff, len, (1 == do_hex) ? 1 : -1);
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
         return;
     }
     if (len < 4) {
@@ -2236,7 +2530,7 @@
     if (do_quiet) { ; } /* unused, dummy to suppress warning */
     if ((! do_hex) && (! do_raw))
         printf("Only hex output supported\n");
-    if (!do_raw) {
+    if ((!do_raw) && (do_hex < 2)) {
         if (subvalue)
             printf("VPD page code=0x%.2x, subvalue=0x%.2x:\n", num_vpd,
                    subvalue);
@@ -2245,40 +2539,20 @@
         else
             printf("VPD page code=%d:\n", num_vpd);
     }
-    if (0 == alloc_len)
-        alloc_len = DEF_ALLOC_LEN;
-    res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len,
-                        1, verbose);
+    if (sg_fd >= 0) {
+        if (0 == alloc_len)
+            alloc_len = DEF_ALLOC_LEN;
+    }
+
+    res = vpd_fetch_page_from_dev(sg_fd, rsp_buff, num_vpd, alloc_len,
+                                  verbose, &len);
     if (0 == res) {
-        len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
-        if (num_vpd != rsp_buff[1]) {
-            pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
-                    "response\n");
-            if (verbose) {
-                pr2serr("First 32 bytes of bad response\n");
-                dStrHexErr((const char *)rsp_buff, 32, 0);
-            }
-            return SG_LIB_CAT_MALFORMED;
-        }
-        if (len > alloc_len) {
-            if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
-                res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len, 1,
-                                    verbose);
-                if (res) {
-                    pr2serr("fetching VPD page (2) code=0x%.2x "
-                            " (alloc_len=%d) failed\n", num_vpd, len);
-                    return res;
-                }
-            } else {
-                pr2serr("warning: response length (%d) longer than "
-                        "requested (%d)\n", len, maxlen);
-                len = alloc_len;
-            }
-        }
         if (do_raw)
             dStrRaw((const char *)rsp_buff, len);
         else {
-            if (VPD_ASCII_OP_DEF == num_vpd)
+            if (do_hex > 1)
+                dStrHex((const char *)rsp_buff, len, -1);
+            else if (VPD_ASCII_OP_DEF == num_vpd)
                 dStrHex((const char *)rsp_buff, len, 0);
             else
                 dStrHex((const char *)rsp_buff, len, (do_long ? 0 : 1));
@@ -2295,87 +2569,65 @@
 
 /* Returns 0 if successful, else see sg_ll_inquiry() */
 static int
-svpd_decode_t10(int sg_fd, int num_vpd, int subvalue, int maxlen, int do_hex,
-                int do_raw, int do_long, int do_quiet, int verbose)
+svpd_decode_t10(int sg_fd, int pn, int subvalue, int maxlen, int do_hex,
+                int do_raw, int do_long, int do_quiet, int vb)
 {
-    int len, pdt, num, k, pn;
-    char buff[48];
-    const struct svpd_values_name_t * vnp;
+    int len, pdt, num, k, resid, alloc_len;
     int res = 0;
-    int alloc_len = maxlen;
+    char b[48];
+    const struct svpd_values_name_t * vnp;
     char obuff[DEF_ALLOC_LEN];
+    unsigned char * rp;
 
-
-    if (0 == alloc_len)
-        alloc_len = (VPD_ATA_INFO == num_vpd) ?
-                    VPD_ATA_INFO_LEN : DEF_ALLOC_LEN;
-    switch(num_vpd) {
+    rp = rsp_buff;
+    switch(pn) {
     case VPD_NO_RATHER_STD_INQ:    /* -2 (want standard inquiry response) */
-        if (do_long)
-            alloc_len = DEF_ALLOC_LEN;
-        else if (0 == maxlen)
-            alloc_len = 36;
-        res = sg_ll_inquiry(sg_fd, 0, 0, 0, rsp_buff, alloc_len, 1,
-                            verbose);
+        if (sg_fd >= 0) {
+            if (maxlen > 0)
+                alloc_len = maxlen;
+            else if (do_long)
+                alloc_len = DEF_ALLOC_LEN;
+            else
+                alloc_len = 36;
+            res = pt_inquiry(sg_fd, 0, 0, rp, alloc_len, &resid, 1, vb);
+        } else {
+            alloc_len = maxlen;
+            resid = 0;
+            res = 0;
+        }
         if (0 == res) {
+            alloc_len -= resid;
             if (do_raw)
-                dStrRaw((const char *)rsp_buff, alloc_len);
+                dStrRaw((const char *)rp, alloc_len);
             else if (do_hex) {
                 if (! do_quiet && (do_hex < 3))
                     printf("Standard Inquiry reponse:\n");
-                dStrHex((const char *)rsp_buff, alloc_len,
-                        (1 == do_hex) ? 0 : -1);
+                dStrHex((const char *)rp, alloc_len, (1 == do_hex) ? 0 : -1);
             } else
-                decode_std_inq(rsp_buff, alloc_len, verbose);
+                decode_std_inq(rp, alloc_len, vb);
             return 0;
         }
         break;
     case VPD_SUPPORTED_VPDS:    /* 0x0 */
         if ((! do_raw) && (! do_quiet) && (do_hex < 3))
             printf("Supported VPD pages VPD page:\n");
-        res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
-                            verbose);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len);
         if (0 == res) {
-            len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4; /* spc4r25 */
-            if (num_vpd != rsp_buff[1]) {
-                pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
-                        "response\n");
-                if (verbose) {
-                    pr2serr("First 32 bytes of bad response\n");
-                    dStrHexErr((const char *)rsp_buff, 32, 0);
-                }
-                return SG_LIB_CAT_MALFORMED;
-            }
-            if (len > alloc_len) {
-                if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
-                    res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
-                                        1, verbose);
-                    if (res) {
-                        pr2serr("fetching Supported VPD pages "
-                                "(alloc_len=%d) failed\n", len);
-                        return res;
-                    }
-                } else {
-                    pr2serr(">>> warning: response length (%d) longer than "
-                            "requested (%d)\n", len, alloc_len);
-                    len = alloc_len;
-                }
-            }
             if (do_raw)
-                dStrRaw((const char *)rsp_buff, len);
+                dStrRaw((const char *)rp, len);
             else if (do_hex)
-                dStrHex((const char *)rsp_buff, len, (1 == do_hex) ? 0 : -1);
+                dStrHex((const char *)rp, len, (1 == do_hex) ? 0 : -1);
             else {
-                pdt = rsp_buff[0] & 0x1f;
-                if (verbose || do_long)
+                pdt = rp[0] & 0x1f;
+                if (vb || do_long)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rsp_buff[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(buff), buff));
-                num = rsp_buff[3];
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                num = rp[3];
                 if (num > (len - 4))
                     num = (len - 4);
                 for (k = 0; k < num; ++k) {
-                    pn = rsp_buff[4 + k];
+                    pn = rp[4 + k];
                     vnp = sdp_get_vpd_detail(pn, -1, pdt);
                     if (vnp) {
                         if (do_long)
@@ -2393,49 +2645,23 @@
     case VPD_UNIT_SERIAL_NUM:   /* 0x80 */
         if ((! do_raw) && (! do_quiet) && (do_hex < 3))
             printf("Unit serial number VPD page:\n");
-        res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
-                            verbose);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len);
         if (0 == res) {
-            len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4; /* spc4r25 */
-            if (num_vpd != rsp_buff[1]) {
-                pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
-                        "response\n");
-                if (verbose) {
-                    pr2serr("First 32 bytes of bad response\n");
-                    dStrHexErr((const char *)rsp_buff, 32, 0);
-                }
-                return SG_LIB_CAT_MALFORMED;
-            }
-            if (len > alloc_len) {
-                if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
-                    res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
-                                        1, verbose);
-                    if (res) {
-                        pr2serr("fetching Unit serial number page "
-                                "(alloc_len=%d) failed\n", len);
-                        return res;
-                    }
-                } else {
-                    pr2serr(">>> warning: response length (%d) longer than "
-                            "requested (%d)\n", len, alloc_len);
-                    len = alloc_len;
-                }
-            }
             if (do_raw)
-                dStrRaw((const char *)rsp_buff, len);
+                dStrRaw((const char *)rp, len);
             else if (do_hex)
-                dStrHex((const char *)rsp_buff, len, (1 == do_hex) ? 0 : -1);
+                dStrHex((const char *)rp, len, (1 == do_hex) ? 0 : -1);
             else {
-                pdt = rsp_buff[0] & 0x1f;
-                if (verbose || do_long)
+                pdt = rp[0] & 0x1f;
+                if (vb || do_long)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rsp_buff[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(buff), buff));
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
                 memset(obuff, 0, sizeof(obuff));
                 len -= 4;
                 if (len >= (int)sizeof(obuff))
                     len = sizeof(obuff) - 1;
-                memcpy(obuff, rsp_buff + 4, len);
+                memcpy(obuff, rp + 4, len);
                 printf("  Unit serial number: %s\n", obuff);
             }
             return 0;
@@ -2444,45 +2670,19 @@
     case VPD_DEVICE_ID:         /* 0x83 */
         if ((! do_raw) && (! do_quiet) && (do_hex < 3))
             printf("Device Identification VPD page:\n");
-        res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
-                            verbose);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len);
         if (0 == res) {
-            len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
-            if (num_vpd != rsp_buff[1]) {
-                pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
-                        "response\n");
-                if (verbose) {
-                    pr2serr("First 32 bytes of bad response\n");
-                    dStrHexErr((const char *)rsp_buff, 32, 0);
-                }
-                return SG_LIB_CAT_MALFORMED;
-            }
-            if (len > alloc_len) {
-                if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
-                    res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
-                                        1, verbose);
-                    if (res) {
-                        pr2serr("fetching Device Identification page "
-                                "(alloc_len=%d) failed\n", len);
-                        return res;
-                    }
-                } else {
-                    pr2serr(">>> warning: response length (%d) longer than "
-                            "requested (%d)\n", len, alloc_len);
-                    len = alloc_len;
-                }
-            }
             if (do_raw)
-                dStrRaw((const char *)rsp_buff, len);
+                dStrRaw((const char *)rp, len);
             else if (do_hex)
-                dStrHex((const char *)rsp_buff, len, (1 == do_hex) ? 0 : -1);
+                dStrHex((const char *)rp, len, (1 == do_hex) ? 0 : -1);
             else {
-                pdt = rsp_buff[0] & 0x1f;
-                if (verbose || do_long)
+                pdt = rp[0] & 0x1f;
+                if (vb || do_long)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rsp_buff[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(buff), buff));
-                decode_id_vpd(rsp_buff, len, subvalue, do_long, do_quiet);
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_id_vpd(rp, len, subvalue, do_long, do_quiet);
             }
             return 0;
         }
@@ -2490,43 +2690,17 @@
     case VPD_SOFTW_INF_ID:      /* 0x84 */
         if ((! do_raw) && (! do_quiet) && (do_hex < 3))
             printf("Software interface identification VPD page:\n");
-        res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
-                            verbose);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len);
         if (0 == res) {
-            len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4; /* spc4r25 */
-            if (num_vpd != rsp_buff[1]) {
-                pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
-                        "response\n");
-                if (verbose) {
-                    pr2serr("First 32 bytes of bad response\n");
-                    dStrHexErr((const char *)rsp_buff, 32, 0);
-                }
-                return SG_LIB_CAT_MALFORMED;
-            }
-            if (len > alloc_len) {
-                if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
-                    res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
-                                        1, verbose);
-                    if (res) {
-                        pr2serr("fetching Software interface id page "
-                                "(alloc_len=%d) failed\n", len);
-                        return res;
-                    }
-                } else {
-                    pr2serr(">>> warning: response length (%d) longer than "
-                            "requested (%d)\n", len, alloc_len);
-                    len = alloc_len;
-                }
-            }
             if (do_raw)
-                dStrRaw((const char *)rsp_buff, len);
+                dStrRaw((const char *)rp, len);
             else {
-                pdt = rsp_buff[0] & 0x1f;
-                if (verbose || do_long)
+                pdt = rp[0] & 0x1f;
+                if (vb || do_long)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rsp_buff[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(buff), buff));
-                decode_softw_inf_id(rsp_buff, len, do_hex);
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_softw_inf_id(rp, len, do_hex);
             }
             return 0;
         }
@@ -2534,90 +2708,38 @@
     case VPD_MAN_NET_ADDR:      /* 0x85 */
         if ((! do_raw) && (! do_quiet) && (do_hex < 3))
             printf("Management network addresses VPD page:\n");
-        res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
-                            verbose);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len);
         if (0 == res) {
-            len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
-            if (num_vpd != rsp_buff[1]) {
-                pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
-                        "response\n");
-                if (verbose) {
-                    pr2serr("First 32 bytes of bad response\n");
-                    dStrHexErr((const char *)rsp_buff, 32, 0);
-                }
-                return SG_LIB_CAT_MALFORMED;
-            }
-            if (len > alloc_len) {
-                if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
-                    res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
-                                        1, verbose);
-                    if (res) {
-                        pr2serr("fetching Management network addresses page "
-                                "(alloc_len=%d) failed\n", len);
-                        return res;
-                    }
-                } else {
-                    pr2serr(">>> warning: response length (%d) longer than "
-                            "requested (%d)\n", len, alloc_len);
-                    len = alloc_len;
-                }
-            }
             if (do_raw)
-                dStrRaw((const char *)rsp_buff, len);
+                dStrRaw((const char *)rp, len);
             else
-                decode_net_man_vpd(rsp_buff, len, do_hex);
+                decode_net_man_vpd(rp, len, do_hex);
             return 0;
         }
         break;
     case VPD_EXT_INQ:           /* 0x86 */
         if ((! do_raw) && (! do_quiet) && (do_hex < 3))
             printf("extended INQUIRY data VPD page:\n");
-        res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
-                            verbose);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len);
         if (0 == res) {
-            len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;   /* spc4r25 */
-            if (num_vpd != rsp_buff[1]) {
-                pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
-                        "response\n");
-                if (verbose) {
-                    pr2serr("First 32 bytes of bad response\n");
-                    dStrHexErr((const char *)rsp_buff, 32, 0);
-                }
-                return SG_LIB_CAT_MALFORMED;
-            }
-            if (len > alloc_len) {
-                if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
-                    res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
-                                        1, verbose);
-                    if (res) {
-                        pr2serr("fetching Extended INQUIRY data page "
-                                "(alloc_len=%d) failed\n", len);
-                        return res;
-                    }
-                } else {
-                    pr2serr(">>> warning: response length (%d) longer than "
-                            "requested (%d)\n", len, alloc_len);
-                    len = alloc_len;
-                }
-            }
             if (do_raw)
-                dStrRaw((const char *)rsp_buff, len);
+                dStrRaw((const char *)rp, len);
             else {
                 int protect = 0;
                 struct sg_simple_inquiry_resp sir;
 
                 if (do_long) {
-                    res = sg_simple_inquiry(sg_fd, &sir, 0, verbose);
+                    res = sg_simple_inquiry(sg_fd, &sir, 0, vb);
                     if (res)
                         break;
                     protect = sir.byte_5 & 0x1;     /* SPC-3 and later */
                 }
-                pdt = rsp_buff[0] & 0x1f;
-                if (verbose)
+                pdt = rp[0] & 0x1f;
+                if (vb)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rsp_buff[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(buff), buff));
-                decode_x_inq_vpd(rsp_buff, len, do_hex, do_long, protect);
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_x_inq_vpd(rp, len, do_hex, do_long, protect);
             }
             return 0;
         }
@@ -2625,43 +2747,17 @@
     case VPD_MODE_PG_POLICY:    /* 0x87 */
         if ((! do_raw) && (! do_quiet) && (do_hex < 3))
             printf("Mode page policy VPD page:\n");
-        res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
-                            verbose);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len);
         if (0 == res) {
-            len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
-            if (num_vpd != rsp_buff[1]) {
-                pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
-                        "response\n");
-                if (verbose) {
-                    pr2serr("First 32 bytes of bad response\n");
-                    dStrHexErr((const char *)rsp_buff, 32, 0);
-                }
-                return SG_LIB_CAT_MALFORMED;
-            }
-            if (len > alloc_len) {
-                if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
-                    res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
-                                        1, verbose);
-                    if (res) {
-                        pr2serr("fetching Mode page policy page "
-                                "(alloc_len=%d) failed\n", len);
-                        return res;
-                    }
-                } else {
-                    pr2serr(">>> warning: response length (%d) longer than "
-                            "requested (%d)\n", len, alloc_len);
-                    len = alloc_len;
-                }
-            }
             if (do_raw)
-                dStrRaw((const char *)rsp_buff, len);
+                dStrRaw((const char *)rp, len);
             else {
-                pdt = rsp_buff[0] & 0x1f;
-                if (verbose || do_long)
+                pdt = rp[0] & 0x1f;
+                if (vb || do_long)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rsp_buff[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(buff), buff));
-                decode_mode_policy_vpd(rsp_buff, len, do_hex);
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_mode_policy_vpd(rp, len, do_hex);
             }
             return 0;
         }
@@ -2669,44 +2765,17 @@
     case VPD_SCSI_PORTS:        /* 0x88 */
         if ((! do_raw) && (! do_quiet) && (do_hex < 3))
             printf("SCSI Ports VPD page:\n");
-        res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
-                            verbose);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len);
         if (0 == res) {
-            len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
-            if (num_vpd != rsp_buff[1]) {
-                pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
-                        "response\n");
-                if (verbose) {
-                    pr2serr("First 32 bytes of bad response\n");
-                    dStrHexErr((const char *)rsp_buff, 32, 0);
-                }
-                return SG_LIB_CAT_MALFORMED;
-            }
-            if (len > alloc_len) {
-                if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
-                    res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
-                                        1, verbose);
-                    if (res) {
-                        pr2serr("fetching SCSI ports page "
-                                "(alloc_len=%d) failed\n", len);
-                        return res;
-                    }
-                } else {
-                    pr2serr(">>> warning: response length (%d) "
-                            "longer than requested (%d)\n", len, alloc_len);
-                    len = alloc_len;
-                }
-            }
             if (do_raw)
-                dStrRaw((const char *)rsp_buff, len);
+                dStrRaw((const char *)rp, len);
             else {
-                pdt = rsp_buff[0] & 0x1f;
-                if (verbose || do_long)
+                pdt = rp[0] & 0x1f;
+                if (vb || do_long)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rsp_buff[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(buff), buff));
-                decode_scsi_ports_vpd(rsp_buff, len, do_hex, do_long,
-                                      do_quiet);
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_scsi_ports_vpd(rp, len, do_hex, do_long, do_quiet);
             }
             return 0;
         }
@@ -2714,46 +2783,22 @@
     case VPD_ATA_INFO:          /* 0x89 */
         if ((! do_raw) && (do_hex < 3) && (! do_quiet))
             printf("ATA information VPD page:\n");
-        res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
-                            verbose);
+        alloc_len = maxlen ? maxlen : VPD_ATA_INFO_LEN;
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, alloc_len, vb, &len);
         if (0 == res) {
-            len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
-            if (num_vpd != rsp_buff[1]) {
-                pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
-                        "response\n");
-                if (verbose) {
-                    pr2serr("First 32 bytes of bad response\n");
-                    dStrHexErr((const char *)rsp_buff, 32, 0);
-                }
-                return SG_LIB_CAT_MALFORMED;
-            }
-            if (len > alloc_len) {
-                if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
-                    res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
-                                        1, verbose);
-                    if (res) {
-                        pr2serr("fetching ATA info page "
-                                "(alloc_len=%d) failed\n", len);
-                        return res;
-                    }
-                } else {
-                    pr2serr(">>> warning: response length (%d) longer than "
-                            "requested (%d)\n", len, alloc_len);
-                    len = alloc_len;
-                }
-            }
             if ((2 == do_raw) || (3 == do_hex))  /* special for hdparm */
-                dWordHex((const unsigned short *)(rsp_buff + 60),
+// xxxxxxxxxx check len is long enough
+                dWordHex((const unsigned short *)(rp + 60),
                          256, -2, sg_is_big_endian());
             else if (do_raw)
-                dStrRaw((const char *)rsp_buff, len);
+                dStrRaw((const char *)rp, len);
             else {
-                pdt = rsp_buff[0] & 0x1f;
-                if (verbose || do_long)
+                pdt = rp[0] & 0x1f;
+                if (vb || do_long)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rsp_buff[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(buff), buff));
-                decode_ata_info_vpd(rsp_buff, len, do_long, do_hex);
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_ata_info_vpd(rp, len, do_long, do_hex);
             }
             return 0;
         }
@@ -2761,43 +2806,17 @@
     case VPD_POWER_CONDITION:          /* 0x8a */
         if ((! do_raw) && (! do_quiet) && (do_hex < 3))
             printf("Power condition VPD page:\n");
-        res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
-                            verbose);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len);
         if (0 == res) {
-            len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
-            if (num_vpd != rsp_buff[1]) {
-                pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
-                        "response\n");
-                if (verbose) {
-                    pr2serr("First 32 bytes of bad response\n");
-                    dStrHexErr((const char *)rsp_buff, 32, 0);
-                }
-                return SG_LIB_CAT_MALFORMED;
-            }
-            if (len > alloc_len) {
-                if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
-                    res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
-                                        1, verbose);
-                    if (res) {
-                        pr2serr("fetching ATA info page "
-                                "(alloc_len=%d) failed\n", len);
-                        return res;
-                    }
-                } else {
-                    pr2serr(">>> warning: response length (%d) longer than "
-                            "requested (%d)\n", len, alloc_len);
-                    len = alloc_len;
-                }
-            }
             if (do_raw)
-                dStrRaw((const char *)rsp_buff, len);
+                dStrRaw((const char *)rp, len);
             else {
-                pdt = rsp_buff[0] & 0x1f;
-                if (verbose || do_long)
+                pdt = rp[0] & 0x1f;
+                if (vb || do_long)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rsp_buff[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(buff), buff));
-                decode_power_condition(rsp_buff, len, do_hex);
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_power_condition(rp, len, do_hex);
             }
             return 0;
         }
@@ -2805,43 +2824,17 @@
     case VPD_POWER_CONSUMPTION:    /* 0x8d */
         if ((! do_raw) && (! do_quiet) && (do_hex < 3))
             printf("Power consumption VPD page:\n");
-        res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
-                            verbose);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len);
         if (0 == res) {
-            len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
-            if (num_vpd != rsp_buff[1]) {
-                pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
-                        "response\n");
-                if (verbose) {
-                    pr2serr("First 32 bytes of bad response\n");
-                    dStrHexErr((const char *)rsp_buff, 32, 0);
-                }
-                return SG_LIB_CAT_MALFORMED;
-            }
-            if (len > alloc_len) {
-                if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
-                    res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
-                                        1, verbose);
-                    if (res) {
-                        pr2serr("fetching Power consumption page "
-                                "(alloc_len=%d) failed\n", len);
-                        return res;
-                    }
-                } else {
-                    pr2serr(">>> warning: response length (%d) longer than "
-                            "requested (%d)\n", len, alloc_len);
-                    len = alloc_len;
-                }
-            }
             if (do_raw)
-                dStrRaw((const char *)rsp_buff, len);
+                dStrRaw((const char *)rp, len);
             else {
-                pdt = rsp_buff[0] & 0x1f;
-                if (verbose || do_long)
+                pdt = rp[0] & 0x1f;
+                if (vb || do_long)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rsp_buff[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(buff), buff));
-                decode_power_consumption_vpd(rsp_buff, len, do_hex);
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_power_consumption_vpd(rp, len, do_hex);
             }
             return 0;
         }
@@ -2849,45 +2842,19 @@
     case VPD_3PARTY_COPY:   /* 0x8f */
         if ((! do_raw) && (! do_quiet) && (do_hex < 3))
             printf("Third party copy VPD page:\n");
-        res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
-                            verbose);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len);
         if (0 == res) {
-            len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4; /* spc4r25 */
-            if (num_vpd != rsp_buff[1]) {
-                pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
-                        "response\n");
-                if (verbose) {
-                    pr2serr("First 32 bytes of bad response\n");
-                    dStrHexErr((const char *)rsp_buff, 32, 0);
-                }
-                return SG_LIB_CAT_MALFORMED;
-            }
-            if (len > alloc_len) {
-                if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
-                    res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
-                                        1, verbose);
-                    if (res) {
-                        pr2serr("fetching Third party copy page "
-                                "(alloc_len=%d) failed\n", len);
-                        return res;
-                    }
-                } else {
-                    pr2serr(">>> warning: response length (%d) longer than "
-                            "requested (%d)\n", len, alloc_len);
-                    len = alloc_len;
-                }
-            }
             if (do_raw)
-                dStrRaw((const char *)rsp_buff, len);
+                dStrRaw((const char *)rp, len);
             else if (1 == do_hex)
-                dStrHex((const char *)rsp_buff, len, 0);
+                dStrHex((const char *)rp, len, 0);
             else {
-                pdt = rsp_buff[0] & 0x1f;
-                if (verbose || do_long)
+                pdt = rp[0] & 0x1f;
+                if (vb || do_long)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rsp_buff[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(buff), buff));
-                decode_3party_copy_vpd(rsp_buff, len, do_hex, verbose);
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_3party_copy_vpd(rp, len, do_hex, vb);
             }
             return 0;
         }
@@ -2895,43 +2862,17 @@
     case VPD_PROTO_LU:          /* 0x90 */
         if ((! do_raw) && (! do_quiet) && (do_hex < 3))
             printf("Protocol-specific logical unit information:\n");
-        res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
-                            verbose);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len);
         if (0 == res) {
-            len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
-            if (num_vpd != rsp_buff[1]) {
-                pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
-                        "response\n");
-                if (verbose) {
-                    pr2serr("First 32 bytes of bad response\n");
-                    dStrHexErr((const char *)rsp_buff, 32, 0);
-                }
-                return SG_LIB_CAT_MALFORMED;
-            }
-            if (len > alloc_len) {
-                if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
-                    res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
-                                        1, verbose);
-                    if (res) {
-                        pr2serr("fetching Protocol-specific LU page "
-                                "(alloc_len=%d) failed\n", len);
-                        return res;
-                    }
-                } else {
-                    pr2serr(">>> warning: response length (%d) longer than "
-                            "requested (%d)\n", len, alloc_len);
-                    len = alloc_len;
-                }
-            }
             if (do_raw)
-                dStrRaw((const char *)rsp_buff, len);
+                dStrRaw((const char *)rp, len);
             else {
                 pdt = rsp_buff[0] & 0x1f;
-                if (verbose || do_long)
+                if (vb || do_long)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rsp_buff[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(buff), buff));
-                decode_proto_lu_vpd(rsp_buff, len, do_hex);
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_proto_lu_vpd(rp, len, do_hex);
             }
             return 0;
         }
@@ -2939,52 +2880,25 @@
     case VPD_PROTO_PORT:        /* 0x91 */
         if ((! do_raw) && (! do_quiet) && (do_hex < 3))
             printf("Protocol-specific port information:\n");
-        res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
-                            verbose);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len);
         if (0 == res) {
-            len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
-            if (num_vpd != rsp_buff[1]) {
-                pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
-                        "response\n");
-                if (verbose) {
-                    pr2serr("First 32 bytes of bad response\n");
-                    dStrHexErr((const char *)rsp_buff, 32, 0);
-                }
-                return SG_LIB_CAT_MALFORMED;
-            }
-            if (len > alloc_len) {
-                if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
-                    res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
-                                        1, verbose);
-                    if (res) {
-                        pr2serr("fetching Protocol-specific port page "
-                                "(alloc_len=%d) failed\n", len);
-                        return res;
-                    }
-                } else {
-                    pr2serr(">>> warning: response length (%d) longer than "
-                            "requested (%d)\n", len, alloc_len);
-                    len = alloc_len;
-                }
-            }
             if (do_raw)
-                dStrRaw((const char *)rsp_buff, len);
+                dStrRaw((const char *)rp, len);
             else {
-                pdt = rsp_buff[0] & 0x1f;
-                if (verbose || do_long)
+                pdt = rp[0] & 0x1f;
+                if (vb || do_long)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rsp_buff[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(buff), buff));
-                decode_proto_port_vpd(rsp_buff, len, do_hex);
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_proto_port_vpd(rp, len, do_hex);
             }
             return 0;
         }
         break;
     case 0xb0:  /* depends on pdt */
-        res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
-                            verbose);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len);
         if (0 == res) {
-            pdt = rsp_buff[0] & 0x1f;
+            pdt = rp[0] & 0x1f;
             if ((! do_raw) && (! do_quiet) && (do_hex < 3)) {
                 switch (pdt) {
                 case PDT_DISK: case PDT_WO: case PDT_OPTICAL:
@@ -3002,50 +2916,24 @@
                     break;
                 }
             }
-            len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
-            if (num_vpd != rsp_buff[1]) {
-                pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
-                        "response\n");
-                if (verbose) {
-                    pr2serr("First 32 bytes of bad response\n");
-                    dStrHexErr((const char *)rsp_buff, 32, 0);
-                }
-                return SG_LIB_CAT_MALFORMED;
-            }
-            if (len > alloc_len) {
-                if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
-                    res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
-                                        1, verbose);
-                    if (res) {
-                        pr2serr("fetching 0xb0 page "
-                                "(alloc_len=%d) failed\n", len);
-                        return res;
-                    }
-                } else {
-                    pr2serr(">>> warning: response length (%d) longer than "
-                            "requested (%d)\n", len, alloc_len);
-                    len = alloc_len;
-                }
-            }
             if (do_raw)
-                dStrRaw((const char *)rsp_buff, len);
+                dStrRaw((const char *)rp, len);
             else {
-                pdt = rsp_buff[0] & 0x1f;
-                if (verbose || do_long)
+                pdt = rp[0] & 0x1f;
+                if (vb || do_long)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rsp_buff[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(buff), buff));
-                decode_b0_vpd(rsp_buff, len, do_hex, pdt);
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_b0_vpd(rp, len, do_hex, pdt);
             }
             return 0;
-        } else if (! do_raw)
+        } else if ((! do_raw) && (! do_quiet) && (do_hex < 3))
             printf("VPD page=0xb0\n");
         break;
     case 0xb1:  /* depends on pdt */
-        res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
-                            verbose);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len);
         if (0 == res) {
-            pdt = rsp_buff[0] & 0x1f;
+            pdt = rp[0] & 0x1f;
             if ((! do_raw) && (! do_quiet) && (do_hex < 3)) {
                 switch (pdt) {
                 case PDT_DISK: case PDT_WO: case PDT_OPTICAL:
@@ -3067,50 +2955,23 @@
                     break;
                 }
             }
-            len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
-            if (num_vpd != rsp_buff[1]) {
-                pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
-                        "response\n");
-                if (verbose) {
-                    pr2serr("First 32 bytes of bad response\n");
-                    dStrHexErr((const char *)rsp_buff, 32, 0);
-                }
-                return SG_LIB_CAT_MALFORMED;
-            }
-            if (len > alloc_len) {
-                if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
-                    res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
-                                        1, verbose);
-                    if (res) {
-                        pr2serr("fetching 0xb1 page "
-                                "(alloc_len=%d) failed\n", len);
-                        return res;
-                    }
-                } else {
-                    pr2serr(">>> warning: response length (%d) longer than "
-                            "requested (%d)\n", len, alloc_len);
-                    len = alloc_len;
-                }
-            }
             if (do_raw)
-                dStrRaw((const char *)rsp_buff, len);
+                dStrRaw((const char *)rp, len);
             else {
-                pdt = rsp_buff[0] & 0x1f;
-                if (verbose || do_long)
+                if (vb || do_long)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rsp_buff[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(buff), buff));
-                decode_b1_vpd(rsp_buff, len, do_hex, pdt);
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_b1_vpd(rp, len, do_hex, pdt);
             }
             return 0;
-        } else if (! do_raw)
+        } else if ((! do_raw) && (! do_quiet) && (do_hex < 3))
             printf("VPD page=0xb1\n");
         break;
     case 0xb2:          /* VPD page depends on pdt */
-        res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
-                            verbose);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len);
         if (0 == res) {
-            pdt = rsp_buff[0] & 0x1f;
+            pdt = rp[0] & 0x1f;
             if ((! do_raw) && (! do_quiet) && (do_hex < 3)) {
                 switch (pdt) {
                 case PDT_DISK: case PDT_WO: case PDT_OPTICAL:
@@ -3124,50 +2985,23 @@
                     break;
                 }
             }
-            len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
-            if (num_vpd != rsp_buff[1]) {
-                pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
-                        "response\n");
-                if (verbose) {
-                    pr2serr("First 32 bytes of bad response\n");
-                    dStrHexErr((const char *)rsp_buff, 32, 0);
-                }
-                return SG_LIB_CAT_MALFORMED;
-            }
-            if (len > alloc_len) {
-                if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
-                    res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
-                                        1, verbose);
-                    if (res) {
-                        pr2serr("fetching 0xb2 page "
-                                "(alloc_len=%d) failed\n", len);
-                        return res;
-                    }
-                } else {
-                    pr2serr(">>> warning: response length (%d) longer than "
-                            "requested (%d)\n", len, alloc_len);
-                    len = alloc_len;
-                }
-            }
             if (do_raw)
-                dStrRaw((const char *)rsp_buff, len);
+                dStrRaw((const char *)rp, len);
             else {
-                pdt = rsp_buff[0] & 0x1f;
-                if (verbose || do_long)
+                if (vb || do_long)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rsp_buff[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(buff), buff));
-                decode_b2_vpd(rsp_buff, len, do_hex, pdt);
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_b2_vpd(rp, len, do_hex, pdt);
             }
             return 0;
-        } else if (! do_raw)
+        } else if ((! do_raw) && (! do_quiet) && (do_hex < 3))
             printf("VPD page=0xb2\n");
         break;
     case 0xb3:          /* VPD page depends on pdt */
-        res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
-                            verbose);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len);
         if (0 == res) {
-            pdt = rsp_buff[0] & 0x1f;
+            pdt = rp[0] & 0x1f;
             if ((! do_raw) && (! do_quiet) && (do_hex < 3)) {
                 switch (pdt) {
                 case PDT_DISK: case PDT_WO: case PDT_OPTICAL:
@@ -3182,87 +3016,35 @@
                     break;
                 }
             }
-            len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
-            if (num_vpd != rsp_buff[1]) {
-                pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
-                        "response\n");
-                if (verbose) {
-                    pr2serr("First 32 bytes of bad response\n");
-                    dStrHexErr((const char *)rsp_buff, 32, 0);
-                }
-                return SG_LIB_CAT_MALFORMED;
-            }
-            if (len > alloc_len) {
-                if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
-                    res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
-                                        1, verbose);
-                    if (res) {
-                        pr2serr("fetching VPD page 0x%x "
-                                "(alloc_len=%d) failed\n", num_vpd, len);
-                        return res;
-                    }
-                } else {
-                    pr2serr(">>> warning: response length (%d) longer than "
-                            "requested (%d)\n", len, alloc_len);
-                    len = alloc_len;
-                }
-            }
             if (do_raw)
-                dStrRaw((const char *)rsp_buff, len);
+                dStrRaw((const char *)rp, len);
             else {
-                pdt = rsp_buff[0] & 0x1f;
-                if (verbose || do_long)
+                if (vb || do_long)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rsp_buff[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(buff), buff));
-                decode_b3_vpd(rsp_buff, len, do_hex, pdt);
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_b3_vpd(rp, len, do_hex, pdt);
             }
             return 0;
-        } else if (! do_raw)
+        } else if ((! do_raw) && (! do_quiet) && (do_hex < 3))
             printf("VPD page=0xb3\n");
         break;
     case VPD_DTDE_ADDRESS:      /* 0xb4 */
         if ((! do_raw) && (! do_quiet) && (do_hex < 3))
             printf("Data transfer device element address (SSC):\n");
-        res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
-                            verbose);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, maxlen, vb, &len);
         if (0 == res) {
-            len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
-            if (num_vpd != rsp_buff[1]) {
-                pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
-                        "response\n");
-                if (verbose) {
-                    pr2serr("First 32 bytes of bad response\n");
-                    dStrHexErr((const char *)rsp_buff, 32, 0);
-                }
-                return SG_LIB_CAT_MALFORMED;
-            }
-            if (len > alloc_len) {
-                if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
-                    res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
-                                        1, verbose);
-                    if (res) {
-                        pr2serr("fetching Data transfer device element "
-                                "address page (alloc_len=%d) failed\n", len);
-                        return res;
-                    }
-                } else {
-                    pr2serr(">>> warning: response length (%d) longer than "
-                            "requested (%d)\n", len, alloc_len);
-                    len = alloc_len;
-                }
-            }
             if (do_raw)
-                dStrRaw((const char *)rsp_buff, len);
+                dStrRaw((const char *)rp, len);
             else {
-                pdt = rsp_buff[0] & 0x1f;
-                if (verbose || do_long)
+                pdt = rp[0] & 0x1f;
+                if (vb || do_long)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rsp_buff[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(buff), buff));
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
                 printf("  Data transfer device element address: 0x");
                 for (k = 4; k < len; ++k)
-                    printf("%02x", (unsigned int)rsp_buff[k]);
+                    printf("%02x", (unsigned int)rp[k]);
                 printf("\n");
             }
             return 0;
@@ -3278,10 +3060,11 @@
 int
 main(int argc, char * argv[])
 {
-    int sg_fd, c, res, matches;
+    int sg_fd, c, res, matches, inhex_len;
     const char * device_name = NULL;
     const struct svpd_values_name_t * vnp;
     const char * page_str = NULL;
+    const char * inhex_fn = NULL;
     const char * cp;
     int num_vpd = 0;
     int do_enum = 0;
@@ -3294,11 +3077,12 @@
     int do_verbose = 0;
     int ret = 0;
     int subvalue = 0;
+    int page_pdt = -1;
 
     while (1) {
         int option_index = 0;
 
-        c = getopt_long(argc, argv, "ehHilm:p:qrvV", long_options,
+        c = getopt_long(argc, argv, "ehHiI:lm:p:qrvV", long_options,
                         &option_index);
         if (c == -1)
             break;
@@ -3317,6 +3101,14 @@
         case 'i':
             ++do_ident;
             break;
+        case 'I':
+            if (inhex_fn) {
+                pr2serr("only one '--inhex=' option permitted\n");
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+            } else
+                inhex_fn = optarg;
+            break;
         case 'l':
             ++do_long;
             break;
@@ -3413,6 +3205,7 @@
             }
             num_vpd = vnp->value;
             subvalue = vnp->subvalue;
+            page_pdt = vnp->pdt;
         } else {
             cp = strchr(page_str, ',');
             num_vpd = sg_get_num_nomult(page_str);
@@ -3431,6 +3224,39 @@
             }
         }
     }
+    if (inhex_fn) {
+        if (device_name) {
+            pr2serr("Cannot have both a DEVICE and --inhex= option\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (f2hex_arr(inhex_fn, 0, rsp_buff, &inhex_len, sizeof(rsp_buff)))
+            return SG_LIB_FILE_ERROR;
+        if (NULL == page_str) {       /* may be able to deduce VPD page */
+            if ((0x2 == (0xf & rsp_buff[3])) && (rsp_buff[2] > 2)) {
+                if (do_verbose)
+                    pr2serr("Guessing from --inhex= this is a standard "
+                            "INQUIRY\n");
+                if (page_pdt < 0)
+                    page_pdt = 0x1f & rsp_buff[0];
+            } else if (rsp_buff[2] <= 2) {
+                if (do_verbose)
+                    pr2serr("Guessing from --inhex this is VPD page 0x%x\n",
+                            rsp_buff[1]);
+                num_vpd = rsp_buff[1];
+                if (page_pdt < 0)
+                    page_pdt = 0x1f & rsp_buff[0];
+            } else {
+                if (do_verbose)
+                    pr2serr("page number unclear from --inhex, hope it's a "
+                            "standard INQUIRY\n");
+                num_vpd = VPD_NO_RATHER_STD_INQ;
+            }
+        }
+    } else if (NULL == device_name) {
+        pr2serr("No DEVICE argument given\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
 
     if (do_raw && do_hex) {
         pr2serr("Can't do hex and raw at the same time\n");
@@ -3445,11 +3271,6 @@
             subvalue = VPD_DI_SEL_LU;
         }
     }
-    if (NULL == device_name) {
-        pr2serr("No DEVICE argument given\n");
-        usage();
-        return SG_LIB_SYNTAX_ERROR;
-    }
     if (do_raw) {
         if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
             perror("sg_set_binary_mode");
@@ -3457,6 +3278,20 @@
         }
     }
 
+    if (inhex_fn) {
+        res = svpd_decode_t10(-1, num_vpd, subvalue, inhex_len, do_hex,
+                              do_raw, do_long, do_quiet, do_verbose);
+        if (SG_LIB_SYNTAX_ERROR == res) {
+            res = svpd_decode_vendor(-1, num_vpd, subvalue, inhex_len, do_hex,
+                                     do_raw, do_long, do_quiet, do_verbose);
+            if (SG_LIB_SYNTAX_ERROR == res)
+                res = svpd_unable_to_decode(-1, num_vpd, subvalue, inhex_len,
+                                            do_hex, do_raw, do_long, do_quiet,
+                                            do_verbose);
+        }
+        return res;
+    }
+
     if ((sg_fd = sg_cmds_open_device(device_name, 1 /* ro */,
                                      do_verbose)) < 0) {
         pr2serr("error opening file: %s: %s\n", device_name,
diff --git a/src/sg_vpd_vendor.c b/src/sg_vpd_vendor.c
index 40bb3f5..67500cb 100644
--- a/src/sg_vpd_vendor.c
+++ b/src/sg_vpd_vendor.c
@@ -84,6 +84,9 @@
 };
 
 
+int vpd_fetch_page_from_dev(int sg_fd, unsigned char * rp, int page,
+                            int mxlen, int vb, int * rlenp);
+
 /* Size of this array must match the array of the same name in sg_vpd.c */
 static unsigned char rsp_buff[MX_ALLOC_LEN + 2];
 
@@ -933,37 +936,19 @@
         strcpy(name, vnp->name);
     else
         snprintf(name, sizeof(name) - 1, "Vendor VPD page=0x%x", num_vpd);
-    if (0 == alloc_len)
-        alloc_len = DEF_ALLOC_LEN;
+    if (sg_fd >= 0) {
+        if (0 == alloc_len)
+            alloc_len = DEF_ALLOC_LEN;
+    }
     if ((! do_raw) && (! do_quiet) && (do_hex < 2))
         printf("%s VPD Page:\n", name);
-    res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
-                        verbose);
+    res = vpd_fetch_page_from_dev(sg_fd, rsp_buff, num_vpd, alloc_len,
+                                  verbose, &len);
     if (0 == res) {
-        len = rsp_buff[3] + 4;
-        if (num_vpd != rsp_buff[1]) {
-            pr2serr("invalid VPD response; probably not supported\n");
-            return SG_LIB_CAT_MALFORMED;
-        }
-        if (len > alloc_len) {
-            if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
-                res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
-                                    1, verbose);
-                if (res) {
-                    pr2serr("fetching 0x%x page (alloc_len=%d) failed\n",
-                            num_vpd, len);
-                    return res;
-                }
-            } else {
-                pr2serr(">>> warning: response length (%d) longer than "
-                        "requested (%d)\n", len, alloc_len);
-                len = alloc_len;
-            }
-        }
         if (do_raw)
             dStrRaw((const char *)rsp_buff, len);
         else if (do_hex)
-            dStrHex((const char *)rsp_buff, len, 0);
+            dStrHex((const char *)rsp_buff, len, ((1 == do_hex) ? 0 : -1));
         else {
             switch(num_vpd) {
                 case 0xc0: