1/*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License as
4 * published by the Free Software Foundation; either version 2 of
5 * the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
15 * MA 02111-1307 USA
16 */
17/*
18 * Copyright (c) 2005-2007 Douglas Gilbert.
19 * All rights reserved.
20 *
21 * Redistribution and use in source and binary forms, with or without
22 * modification, are permitted provided that the following conditions
23 * are met:
24 * 1. Redistributions of source code must retain the above copyright
25 *    notice, this list of conditions and the following disclaimer.
26 * 2. Redistributions in binary form must reproduce the above copyright
27 *    notice, this list of conditions and the following disclaimer in the
28 *    documentation and/or other materials provided with the distribution.
29 * 3. The name of the author may not be used to endorse or promote products
30 *    derived from this software without specific prior written permission.
31 *
32 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 * SUCH DAMAGE.
43 *
44 */
45
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <errno.h>
50#include <ctype.h>
51#define __STDC_FORMAT_MACROS 1
52#include <inttypes.h>
53
54#include "sdparm.h"
55#include "sg_lib.h"
56#include "sg_cmds_basic.h"
57
58/* sdparm_vpd.c : does mainly VPD page processing associated with the
59 * INQUIRY SCSI command.
60 */
61
62/* Prints outs an abridged set of device identification designators
63   selected by association, designator type and/or code set. */
64static int
65decode_dev_ids_quiet(unsigned char * buff, int len, int m_assoc,
66                     int m_desig_type, int m_code_set)
67{
68    int m, p_id, c_set, piv, assoc, desig_type, i_len, is_sas;
69    int naa, off, u, rtp;
70    const unsigned char * ucp;
71    const unsigned char * ip;
72    unsigned char sas_tport_addr[8];
73
74    rtp = 0;
75    memset(sas_tport_addr, 0, sizeof(sas_tport_addr));
76    off = -1;
77    while ((u = sg_vpd_dev_id_iter(buff, len, &off, m_assoc, m_desig_type,
78                                   m_code_set)) == 0) {
79        ucp = buff + off;
80        i_len = ucp[3];
81        if ((off + i_len + 4) > len) {
82            fprintf(stderr, "    VPD page error: designator length longer "
83                    "than\n     remaining response length=%d\n", (len - off));
84            return SG_LIB_CAT_MALFORMED;
85        }
86        ip = ucp + 4;
87        p_id = ((ucp[0] >> 4) & 0xf);
88        c_set = (ucp[0] & 0xf);
89        piv = ((ucp[1] & 0x80) ? 1 : 0);
90        is_sas = (piv && (6 == p_id)) ? 1 : 0;
91        assoc = ((ucp[1] >> 4) & 0x3);
92        desig_type = (ucp[1] & 0xf);
93        switch (desig_type) {
94        case 0: /* vendor specific */
95            break;
96        case 1: /* T10 vendor identification */
97            break;
98        case 2: /* EUI-64 based */
99            if ((8 != i_len) && (12 != i_len) && (16 != i_len))
100                fprintf(stderr, "      << expect 8, 12 and 16 byte "
101                        "EUI, got %d>>\n", i_len);
102            printf("0x");
103            for (m = 0; m < i_len; ++m)
104                printf("%02x", (unsigned int)ip[m]);
105            printf("\n");
106            break;
107        case 3: /* NAA */
108            if (1 != c_set) {
109                fprintf(stderr, "      << unexpected code set %d for "
110                        "NAA>>\n", c_set);
111                dStrHex((const char *)ip, i_len, 0);
112                break;
113            }
114            naa = (ip[0] >> 4) & 0xff;
115            if (! ((2 == naa) || (5 == naa) || (6 == naa))) {
116                fprintf(stderr, "      << unexpected NAA [0x%x]>>\n", naa);
117                dStrHex((const char *)ip, i_len, 0);
118                break;
119            }
120            if (2 == naa) {
121                if (8 != i_len) {
122                    fprintf(stderr, "      << unexpected NAA 2 identifier "
123                            "length: 0x%x>>\n", i_len);
124                    dStrHex((const char *)ip, i_len, 0);
125                    break;
126                }
127                printf("0x");
128                for (m = 0; m < 8; ++m)
129                    printf("%02x", (unsigned int)ip[m]);
130                printf("\n");
131            } else if (5 == naa) {
132                if (8 != i_len) {
133                    fprintf(stderr, "      << unexpected NAA 5 identifier "
134                            "length: 0x%x>>\n", i_len);
135                    dStrHex((const char *)ip, i_len, 0);
136                    break;
137                }
138                if ((0 == is_sas) || (1 != assoc)) {
139                    printf("0x");
140                    for (m = 0; m < 8; ++m)
141                        printf("%02x", (unsigned int)ip[m]);
142                    printf("\n");
143                } else if (rtp) {
144                    printf("0x");
145                    for (m = 0; m < 8; ++m)
146                        printf("%02x", (unsigned int)ip[m]);
147                    printf(",0x%x\n", rtp);
148                    rtp = 0;
149                } else {
150                    if (sas_tport_addr[0]) {
151                        printf("0x");
152                        for (m = 0; m < 8; ++m)
153                            printf("%02x", (unsigned int)sas_tport_addr[m]);
154                        printf("\n");
155                    }
156                    memcpy(sas_tport_addr, ip, sizeof(sas_tport_addr));
157                }
158            } else if (6 == naa) {
159                if (16 != i_len) {
160                    fprintf(stderr, "      << unexpected NAA 6 identifier "
161                            "length: 0x%x>>\n", i_len);
162                    dStrHex((const char *)ip, i_len, 0);
163                    break;
164                }
165                printf("0x");
166                for (m = 0; m < 16; ++m)
167                    printf("%02x", (unsigned int)ip[m]);
168                printf("\n");
169            }
170            break;
171        case 4: /* Relative target port */
172            if ((0 == is_sas) || (1 != c_set) || (1 != assoc) || (4 != i_len))
173                break;
174            rtp = ((ip[2] << 8) | ip[3]);
175            if (sas_tport_addr[0]) {
176                printf("0x");
177                for (m = 0; m < 8; ++m)
178                    printf("%02x", (unsigned int)sas_tport_addr[m]);
179                printf(",0x%x\n", rtp);
180                memset(sas_tport_addr, 0, sizeof(sas_tport_addr));
181                rtp = 0;
182            }
183            break;
184        case 5: /* (primary) Target port group */
185            break;
186        case 6: /* Logical unit group */
187            break;
188        case 7: /* MD5 logical unit identifier */
189            break;
190        case 8: /* SCSI name string */
191            if (3 != c_set) {
192                fprintf(stderr, "      << expected UTF-8 code_set>>\n");
193                dStrHex((const char *)ip, i_len, 0);
194                break;
195            }
196            /* does %s print out UTF-8 ok??
197             * Seems to depend on the locale. Looks ok here with my
198             * locale setting: en_AU.UTF-8
199             */
200            printf("%s\n", (const char *)ip);
201            break;
202        default: /* reserved */
203            break;
204        }
205    }
206    if (sas_tport_addr[0]) {
207        printf("0x");
208        for (m = 0; m < 8; ++m)
209            printf("%02x", (unsigned int)sas_tport_addr[m]);
210        printf("\n");
211    }
212    if (-2 == u) {
213        fprintf(stderr, "VPD page error: short designator near "
214                "offset %d\n", off);
215        return SG_LIB_CAT_MALFORMED;
216    }
217    return 0;
218}
219
220/* Prints outs device identification designators selected by association,
221   designator type and/or code set. */
222static int
223decode_dev_ids(const char * print_if_found, unsigned char * buff, int len,
224               int m_assoc, int m_desig_type, int m_code_set, int long_out,
225               int quiet)
226{
227    int m, p_id, c_set, piv, assoc, desig_type, i_len;
228    int ci_off, c_id, d_id, naa, vsi, printed, off, u;
229    unsigned long long vsei;
230    unsigned long long id_ext;
231    const unsigned char * ucp;
232    const unsigned char * ip;
233
234    if (quiet)
235        return decode_dev_ids_quiet(buff, len, m_assoc, m_desig_type,
236                                    m_code_set);
237    off = -1;
238    printed = 0;
239    while ((u = sg_vpd_dev_id_iter(buff, len, &off, m_assoc, m_desig_type,
240                                   m_code_set)) == 0) {
241        ucp = buff + off;
242        i_len = ucp[3];
243        if ((off + i_len + 4) > len) {
244            fprintf(stderr, "    VPD page error: designator length longer "
245                    "than\n     remaining response length=%d\n", (len - off));
246            return SG_LIB_CAT_MALFORMED;
247        }
248        ip = ucp + 4;
249        p_id = ((ucp[0] >> 4) & 0xf);
250        c_set = (ucp[0] & 0xf);
251        piv = ((ucp[1] & 0x80) ? 1 : 0);
252        assoc = ((ucp[1] >> 4) & 0x3);
253        desig_type = (ucp[1] & 0xf);
254        if (print_if_found && (0 == printed)) {
255            printed = 1;
256            printf("  %s:\n", print_if_found);
257        }
258        if (NULL == print_if_found)
259            printf("  %s:\n", sdparm_assoc_arr[assoc]);
260        printf("    designator type: %s,  code set: %s\n",
261               sdparm_desig_type_arr[desig_type], sdparm_code_set_arr[c_set]);
262        if (piv && ((1 == assoc) || (2 == assoc)))
263            printf("     transport: %s\n", sdparm_transport_proto_arr[p_id]);
264        /* printf("    associated with the %s\n", sdparm_assoc_arr[assoc]); */
265        switch (desig_type) {
266        case 0: /* vendor specific */
267            dStrHex((const char *)ip, i_len, 0);
268            break;
269        case 1: /* T10 vendor identification */
270            printf("      vendor id: %.8s\n", ip);
271            if (i_len > 8)
272                printf("      vendor specific: %.*s\n", i_len - 8, ip + 8);
273            break;
274        case 2: /* EUI-64 based */
275            if (! long_out) {
276                if ((8 != i_len) && (12 != i_len) && (16 != i_len)) {
277                    fprintf(stderr, "      << expect 8, 12 and 16 byte "
278                            "EUI, got %d>>\n", i_len);
279                    dStrHex((const char *)ip, i_len, 0);
280                    break;
281                }
282                printf("      0x");
283                for (m = 0; m < i_len; ++m)
284                    printf("%02x", (unsigned int)ip[m]);
285                printf("\n");
286                break;
287            }
288            printf("      EUI-64 based %d byte identifier\n", i_len);
289            if (1 != c_set) {
290                fprintf(stderr, "      << expected binary code_set (1)>>\n");
291                dStrHex((const char *)ip, i_len, 0);
292                break;
293            }
294            ci_off = 0;
295            if (16 == i_len) {
296                ci_off = 8;
297                id_ext = 0;
298                for (m = 0; m < 8; ++m) {
299                    if (m > 0)
300                        id_ext <<= 8;
301                    id_ext |= ip[m];
302                }
303                printf("      Identifier extension: 0x%" PRIx64 "\n", id_ext);
304            } else if ((8 != i_len) && (12 != i_len)) {
305                fprintf(stderr, "      << can only decode 8, 12 and 16 "
306                        "byte ids>>\n");
307                dStrHex((const char *)ip, i_len, 0);
308                break;
309            }
310            c_id = ((ip[ci_off] << 16) | (ip[ci_off + 1] << 8) |
311                    ip[ci_off + 2]);
312            printf("      IEEE Company_id: 0x%x\n", c_id);
313            vsei = 0;
314            for (m = 0; m < 5; ++m) {
315                if (m > 0)
316                    vsei <<= 8;
317                vsei |= ip[ci_off + 3 + m];
318            }
319            printf("      Vendor Specific Extension Identifier: 0x%" PRIx64
320                   "\n", vsei);
321            if (12 == i_len) {
322                d_id = ((ip[8] << 24) | (ip[9] << 16) | (ip[10] << 8) |
323                        ip[11]);
324                printf("      Directory ID: 0x%x\n", d_id);
325            }
326            break;
327        case 3: /* NAA */
328            if (1 != c_set) {
329                fprintf(stderr, "      << unexpected code set %d for "
330                        "NAA>>\n", c_set);
331                dStrHex((const char *)ip, i_len, 0);
332                break;
333            }
334            naa = (ip[0] >> 4) & 0xff;
335            if (! ((2 == naa) || (5 == naa) || (6 == naa))) {
336                fprintf(stderr, "      << unexpected NAA [0x%x]>>\n", naa);
337                dStrHex((const char *)ip, i_len, 0);
338                break;
339            }
340            if (2 == naa) {
341                if (8 != i_len) {
342                    fprintf(stderr, "      << unexpected NAA 2 identifier "
343                            "length: 0x%x>>\n", i_len);
344                    dStrHex((const char *)ip, i_len, 0);
345                    break;
346                }
347                d_id = (((ip[0] & 0xf) << 8) | ip[1]);
348                c_id = ((ip[2] << 16) | (ip[3] << 8) | ip[4]);
349                vsi = ((ip[5] << 16) | (ip[6] << 8) | ip[7]);
350                if (long_out) {
351                    printf("      NAA 2, vendor specific identifier A: "
352                           "0x%x\n", d_id);
353                    printf("      IEEE Company_id: 0x%x\n", c_id);
354                    printf("      vendor specific identifier B: 0x%x\n", vsi);
355                    printf("      [0x");
356                    for (m = 0; m < 8; ++m)
357                        printf("%02x", (unsigned int)ip[m]);
358                    printf("]\n");
359                }
360                printf("      0x");
361                for (m = 0; m < 8; ++m)
362                    printf("%02x", (unsigned int)ip[m]);
363                printf("\n");
364            } else if (5 == naa) {
365                if (8 != i_len) {
366                    fprintf(stderr, "      << unexpected NAA 5 identifier "
367                            "length: 0x%x>>\n", i_len);
368                    dStrHex((const char *)ip, i_len, 0);
369                    break;
370                }
371                c_id = (((ip[0] & 0xf) << 20) | (ip[1] << 12) |
372                        (ip[2] << 4) | ((ip[3] & 0xf0) >> 4));
373                vsei = ip[3] & 0xf;
374                for (m = 1; m < 5; ++m) {
375                    vsei <<= 8;
376                    vsei |= ip[3 + m];
377                }
378                if (long_out) {
379                    printf("      NAA 5, IEEE Company_id: 0x%x\n", c_id);
380                    printf("      Vendor Specific Identifier: 0x%" PRIx64
381                           "\n", vsei);
382                    printf("      [0x");
383                    for (m = 0; m < 8; ++m)
384                        printf("%02x", (unsigned int)ip[m]);
385                    printf("]\n");
386                } else {
387                    printf("      0x");
388                    for (m = 0; m < 8; ++m)
389                        printf("%02x", (unsigned int)ip[m]);
390                    printf("\n");
391                }
392            } else if (6 == naa) {
393                if (16 != i_len) {
394                    fprintf(stderr, "      << unexpected NAA 6 identifier "
395                            "length: 0x%x>>\n", i_len);
396                    dStrHex((const char *)ip, i_len, 0);
397                    break;
398                }
399                c_id = (((ip[0] & 0xf) << 20) | (ip[1] << 12) |
400                        (ip[2] << 4) | ((ip[3] & 0xf0) >> 4));
401                vsei = ip[3] & 0xf;
402                for (m = 1; m < 5; ++m) {
403                    vsei <<= 8;
404                    vsei |= ip[3 + m];
405                }
406                if (long_out) {
407                    printf("      NAA 6, IEEE Company_id: 0x%x\n", c_id);
408                    printf("      Vendor Specific Identifier: 0x%" PRIx64
409                           "\n", vsei);
410                    vsei = 0;
411                    for (m = 0; m < 8; ++m) {
412                        if (m > 0)
413                            vsei <<= 8;
414                        vsei |= ip[8 + m];
415                    }
416                    printf("      Vendor Specific Identifier Extension: "
417                           "0x%" PRIx64 "\n", vsei);
418                    printf("      [0x");
419                    for (m = 0; m < 16; ++m)
420                        printf("%02x", (unsigned int)ip[m]);
421                    printf("]\n");
422                } else {
423                    printf("      0x");
424                    for (m = 0; m < 16; ++m)
425                        printf("%02x", (unsigned int)ip[m]);
426                    printf("\n");
427                }
428            }
429            break;
430        case 4: /* Relative target port */
431            if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
432                fprintf(stderr, "      << expected binary code_set, target "
433                        "port association, length 4>>\n");
434                dStrHex((const char *)ip, i_len, 0);
435                break;
436            }
437            d_id = ((ip[2] << 8) | ip[3]);
438            printf("      Relative target port: 0x%x\n", d_id);
439            break;
440        case 5: /* (primary) Target port group */
441            if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
442                fprintf(stderr, "      << expected binary code_set, target "
443                        "port association, length 4>>\n");
444                dStrHex((const char *)ip, i_len, 0);
445                break;
446            }
447            d_id = ((ip[2] << 8) | ip[3]);
448            printf("      Target port group: 0x%x\n", d_id);
449            break;
450        case 6: /* Logical unit group */
451            if ((1 != c_set) || (0 != assoc) || (4 != i_len)) {
452                fprintf(stderr, "      << expected binary code_set, logical "
453                        "unit association, length 4>>\n");
454                dStrHex((const char *)ip, i_len, 0);
455                break;
456            }
457            d_id = ((ip[2] << 8) | ip[3]);
458            printf("      Logical unit group: 0x%x\n", d_id);
459            break;
460        case 7: /* MD5 logical unit identifier */
461            if ((1 != c_set) || (0 != assoc)) {
462                printf("      << expected binary code_set, logical "
463                       "unit association>>\n");
464                dStrHex((const char *)ip, i_len, 0);
465                break;
466            }
467            printf("      MD5 logical unit identifier:\n");
468            dStrHex((const char *)ip, i_len, 0);
469            break;
470        case 8: /* SCSI name string */
471            if (3 != c_set) {
472                fprintf(stderr, "      << expected UTF-8 code_set>>\n");
473                dStrHex((const char *)ip, i_len, 0);
474                break;
475            }
476            printf("      SCSI name string:\n");
477            /* does %s print out UTF-8 ok??
478             * Seems to depend on the locale. Looks ok here with my
479             * locale setting: en_AU.UTF-8
480             */
481            printf("      %s\n", (const char *)ip);
482            break;
483        default: /* reserved */
484            dStrHex((const char *)ip, i_len, 0);
485            break;
486        }
487    }
488    if (-2 == u) {
489        fprintf(stderr, "VPD page error: short designator around "
490                "offset %d\n", off);
491        return SG_LIB_CAT_MALFORMED;
492    }
493    return 0;
494}
495
496static int
497decode_mode_policy_vpd(unsigned char * buff, int len)
498{
499    int k, bump;
500    unsigned char * ucp;
501
502    if (len < 4) {
503        fprintf(stderr, "Mode page policy VPD page length too short=%d\n",
504                len);
505        return SG_LIB_CAT_MALFORMED;
506    }
507    len -= 4;
508    ucp = buff + 4;
509    for (k = 0; k < len; k += bump, ucp += bump) {
510        bump = 4;
511        if ((k + bump) > len) {
512            fprintf(stderr, "Mode page policy VPD page, short "
513                    "descriptor length=%d, left=%d\n", bump, (len - k));
514            return SG_LIB_CAT_MALFORMED;
515        }
516        printf("  Policy page code: 0x%x", (ucp[0] & 0x3f));
517        if (ucp[1])
518            printf(",  subpage code: 0x%x\n", ucp[1]);
519        else
520            printf("\n");
521        printf("    MLUS=%d,  Policy: %s\n", !!(ucp[2] & 0x80),
522               sdparm_mode_page_policy_arr[ucp[2] & 0x3]);
523    }
524    return 0;
525}
526
527static int
528decode_man_net_vpd(unsigned char * buff, int len)
529{
530    int k, bump, na_len;
531    unsigned char * ucp;
532
533    if (len < 4) {
534        fprintf(stderr, "Management network addresses VPD page length too "
535                "short=%d\n", len);
536        return SG_LIB_CAT_MALFORMED;
537    }
538    len -= 4;
539    ucp = buff + 4;
540    for (k = 0; k < len; k += bump, ucp += bump) {
541        printf("  %s, Service type: %s\n",
542               sdparm_assoc_arr[(ucp[0] >> 5) & 0x3],
543               sdparm_network_service_type_arr[ucp[0] & 0x1f]);
544        na_len = (ucp[2] << 8) + ucp[3];
545        bump = 4 + na_len;
546        if ((k + bump) > len) {
547            fprintf(stderr, "Management network addresses VPD page, short "
548                    "descriptor length=%d, left=%d\n", bump, (len - k));
549            return SG_LIB_CAT_MALFORMED;
550        }
551        if (na_len > 0) {
552            printf("    %s\n", ucp + 4);
553        }
554    }
555    return 0;
556}
557
558static int
559decode_proto_lu_vpd(unsigned char * buff, int len)
560{
561    int k, bump, rel_port, desc_len, proto;
562    unsigned char * ucp;
563
564    if (len < 4) {
565        fprintf(stderr, "Protocol-specific logical unit information VPD "
566                "page length too short=%d\n", len);
567        return SG_LIB_CAT_MALFORMED;
568    }
569    len -= 4;
570    ucp = buff + 4;
571    for (k = 0; k < len; k += bump, ucp += bump) {
572        rel_port = (ucp[0] << 8) + ucp[1];
573        printf("Relative port=%d\n", rel_port);
574        proto = ucp[2] & 0xf;
575        desc_len = (ucp[6] << 8) + ucp[7];
576        bump = 8 + desc_len;
577        if ((k + bump) > len) {
578            fprintf(stderr, "Protocol-specific logical unit information VPD "
579                    "page, short descriptor length=%d, left=%d\n", bump,
580                    (len - k));
581            return SG_LIB_CAT_MALFORMED;
582        }
583        if (desc_len > 0) {
584            switch (proto) {
585            case TPROTO_SAS:
586                printf(" Protocol identifier: SAS\n");
587                printf(" TLR control supported: %d\n", !!(ucp[8] & 0x1));
588                break;
589            default:
590                fprintf(stderr, "Unexpected proto=%d\n", proto);
591                dStrHex((const char *)ucp, bump, 1);
592                break;
593            }
594        }
595    }
596    return 0;
597}
598
599static int
600decode_proto_port_vpd(unsigned char * buff, int len)
601{
602    int k, bump, rel_port, desc_len, proto;
603    unsigned char * ucp;
604
605    if (len < 4) {
606        fprintf(stderr, "Protocol-specific port information VPD "
607                "page length too short=%d\n", len);
608        return SG_LIB_CAT_MALFORMED;
609    }
610    len -= 4;
611    ucp = buff + 4;
612    for (k = 0; k < len; k += bump, ucp += bump) {
613        rel_port = (ucp[0] << 8) + ucp[1];
614        printf("Relative port=%d\n", rel_port);
615        proto = ucp[2] & 0xf;
616        desc_len = (ucp[6] << 8) + ucp[7];
617        bump = 8 + desc_len;
618        if ((k + bump) > len) {
619            fprintf(stderr, "Protocol-specific port information VPD "
620                    "page, short descriptor length=%d, left=%d\n", bump,
621                    (len - k));
622            return SG_LIB_CAT_MALFORMED;
623        }
624        if (desc_len > 0) {
625            switch (proto) {
626            case TPROTO_SAS:
627            default:
628                fprintf(stderr, "Unexpected proto=%d\n", proto);
629                dStrHex((const char *)ucp, bump, 1);
630                break;
631            }
632        }
633    }
634    return 0;
635}
636
637static int
638decode_scsi_ports_vpd(unsigned char * buff, int len, int long_out, int quiet)
639{
640    int k, bump, rel_port, ip_tid_len, tpd_len, res;
641    unsigned char * ucp;
642
643    if (len < 4) {
644        fprintf(stderr, "SCSI Ports VPD page length too short=%d\n", len);
645        return SG_LIB_CAT_MALFORMED;
646    }
647    len -= 4;
648    ucp = buff + 4;
649    for (k = 0; k < len; k += bump, ucp += bump) {
650        rel_port = (ucp[2] << 8) + ucp[3];
651        printf("Relative port=%d\n", rel_port);
652        ip_tid_len = (ucp[6] << 8) + ucp[7];
653        bump = 8 + ip_tid_len;
654        if ((k + bump) > len) {
655            fprintf(stderr, "SCSI Ports VPD page, short descriptor "
656                    "length=%d, left=%d\n", bump, (len - k));
657            return SG_LIB_CAT_MALFORMED;
658        }
659        if (ip_tid_len > 0) {
660            /*
661             * SCSI devices that are both target and initiator are rare.
662             * Only target devices can receive this command, so if they
663             * are also initiators then print out the "Initiator port
664             * transport id" in hex. sg_inq in sg3_utils decodes it.
665             */
666            printf(" Initiator port transport id:\n");
667            dStrHex((const char *)(ucp + 8), ip_tid_len, 1);
668        }
669        tpd_len = (ucp[bump + 2] << 8) + ucp[bump + 3];
670        if ((k + bump + tpd_len + 4) > len) {
671            fprintf(stderr, "SCSI Ports VPD page, short descriptor(tgt) "
672                    "length=%d, left=%d\n", bump, (len - k));
673            return SG_LIB_CAT_MALFORMED;
674        }
675        if (tpd_len > 0) {
676            res = decode_dev_ids(" Target port descriptor(s)",
677                                 ucp + bump + 4, tpd_len, VPD_ASSOC_TPORT,
678                                 -1, -1, long_out, quiet);
679            if (res)
680                return res;
681        }
682        bump += tpd_len + 4;
683    }
684    return 0;
685}
686
687static int
688decode_ext_inq_vpd(unsigned char * buff, int len, int quiet)
689{
690    if (len < 7) {
691        fprintf(stderr, "Extended INQUIRY data VPD page length too "
692                "short=%d\n", len);
693        return SG_LIB_CAT_MALFORMED;
694    }
695    if (quiet) {
696        printf("spt=%d\n", ((buff[4] >> 3) & 0x7));
697        printf("grd_chk=%d\n", !!(buff[4] & 0x4));
698        printf("app_chk=%d\n", !!(buff[4] & 0x2));
699        printf("ref_chk=%d\n", !!(buff[4] & 0x1));
700        printf("GRP_SUP=%d\n", !!(buff[5] & 0x10));
701        printf("prior_sup=%d\n", !!(buff[5] & 0x8));
702        printf("headsup=%d\n", !!(buff[5] & 0x4));
703        printf("ordsup=%d\n", !!(buff[5] & 0x2));
704        printf("simpsup=%d\n", !!(buff[5] & 0x1));
705        printf("corr_d_sup=%d\n", !!(buff[6] & 0x4));
706        printf("nv_sup=%d\n", !!(buff[6] & 0x2));
707        printf("v_sup=%d\n", !!(buff[6] & 0x1));
708        printf("luiclr=%d\n", !!(buff[7] & 0x1));
709    } else {
710        printf("  SPT: %d  GRD_CHK: %d  APP_CHK: %d  REF_CHK: %d\n",
711               ((buff[4] >> 3) & 0x7), !!(buff[4] & 0x4), !!(buff[4] & 0x2),
712               !!(buff[4] & 0x1));
713        printf("  GRP_SUP: %d  PRIOR_SUP: %d  HEADSUP: %d  ORDSUP: %d  "
714               "SIMPSUP: %d\n", !!(buff[5] & 0x10), !!(buff[5] & 0x8),
715               !!(buff[5] & 0x4), !!(buff[5] & 0x2), !!(buff[5] & 0x1));
716        printf("  CORR_D_SUP: %d  NV_SUP: %d  V_SUP: %d  LUICLR: %d\n",
717               !!(buff[6] & 0x4), !!(buff[6] & 0x2), !!(buff[6] & 0x1),
718               !!(buff[7] & 0x1));
719    }
720    return 0;
721}
722
723static int decode_ata_info_vpd(unsigned char * buff, int len, int long_out,
724                               int do_hex)
725{
726    char b[80];
727    int num, is_be;
728    const char * cp;
729
730    if (len < 36) {
731        fprintf(stderr, "ATA information VPD page length too "
732                "short=%d\n", len);
733        return SG_LIB_CAT_MALFORMED;
734    }
735    memcpy(b, buff + 8, 8);
736    b[8] = '\0';
737    printf("  SAT Vendor identification: %s\n", b);
738    memcpy(b, buff + 16, 16);
739    b[16] = '\0';
740    printf("  SAT Product identification: %s\n", b);
741    memcpy(b, buff + 32, 4);
742    b[4] = '\0';
743    printf("  SAT Product revision level: %s\n", b);
744    if (len < 56)
745        return SG_LIB_CAT_MALFORMED;
746    if (long_out) {
747        printf("  Signature (Device to host FIS):\n");
748        dStrHex((const char *)buff + 36, 20, 1);
749    }
750    if (len < 60)
751        return SG_LIB_CAT_MALFORMED;
752    is_be = sg_is_big_endian();
753    if ((0xec == buff[56]) || (0xa1 == buff[56])) {
754        cp = (0xa1 == buff[56]) ? "PACKET " : "";
755        printf("  ATA command IDENTIFY %sDEVICE response summary:\n", cp);
756        num = sg_ata_get_chars((const unsigned short *)(buff + 60), 27, 20,
757                               is_be, b);
758        b[num] = '\0';
759        printf("    model: %s\n", b);
760        num = sg_ata_get_chars((const unsigned short *)(buff + 60), 10, 10,
761                               is_be, b);
762        b[num] = '\0';
763        printf("    serial number: %s\n", b);
764        num = sg_ata_get_chars((const unsigned short *)(buff + 60), 23, 4,
765                               is_be, b);
766        b[num] = '\0';
767        printf("    firmware revision: %s\n", b);
768        if (long_out)
769            printf("  ATA command IDENTIFY %sDEVICE response in hex:\n", cp);
770    } else if (long_out)
771        printf("  ATA command 0x%x got following response:\n",
772               (unsigned int)buff[56]);
773    if (len < 572)
774        return SG_LIB_CAT_MALFORMED;
775    if (do_hex)
776        dStrHex((const char *)(buff + 60), 512, 0);
777    else if (long_out)
778        dWordHex((const unsigned short *)(buff + 60), 256, 0, is_be);
779    return 0;
780}
781
782static int
783decode_block_limits_vpd(unsigned char * buff, int len)
784{
785    unsigned int u;
786
787    if (len < 16) {
788        fprintf(stderr, "Block limits VPD page length too "
789                "short=%d\n", len);
790        return SG_LIB_CAT_MALFORMED;
791    }
792    u = (buff[6] << 8) | buff[7];
793    printf("  Optimal transfer length granularity: %u blocks\n", u);
794    u = (buff[8] << 24) | (buff[9] << 16) | (buff[10] << 8) |
795        buff[11];
796    printf("  Maximum transfer length: %u blocks\n", u);
797    u = (buff[12] << 24) | (buff[13] << 16) | (buff[14] << 8) |
798        buff[15];
799    printf("  Optimal transfer length: %u blocks\n", u);
800    if (len > 19) {     /* added in sbc3r09 */
801        u = (buff[16] << 24) | (buff[17] << 16) | (buff[18] << 8) |
802            buff[19];
803        printf("  Maximum prefetch, xdread, xdwrite transfer length: %u "
804               "blocks\n", u);
805    }
806    return 0;
807}
808
809static int
810decode_block_dev_chars_vpd(unsigned char * buff, int len)
811{
812    unsigned int u;
813
814    if (len < 64) {
815        fprintf(stderr, "Block device capabilities VPD page length too "
816                "short=%d\n", len);
817        return SG_LIB_CAT_MALFORMED;
818    }
819    u = (buff[4] << 8) | buff[5];
820    if (0 == u)
821        printf("  Medium rotation rate is not reported\n");
822    else if (1 == u)
823        printf("  Non-rotating medium (e.g. solid state)\n");
824    else if ((u < 0x401) || (0xffff == u))
825        printf("  Reserved [0x%x]\n", u);
826    else
827        printf("  Nominal rotation rate: %d rpm\n", u);
828    return 0;
829}
830
831static int
832decode_tape_dev_caps_vpd(unsigned char * buff, int len)
833{
834    if (len < 6) {
835        fprintf(stderr, "Sequential access device capabilities VPD page "
836                "length too short=%d\n", len);
837        return SG_LIB_CAT_MALFORMED;
838    }
839    printf("  Worm: %d\n", !!(buff[4] & 0x1));
840    return 0;
841}
842
843static int
844decode_tape_man_ass_sn_vpd(unsigned char * buff, int len)
845{
846    if (len < 64) {
847        fprintf(stderr, "Manufacturer-assigned serial number VPD page "
848                "length too short=%d\n", len);
849        return SG_LIB_CAT_MALFORMED;
850    }
851    printf("  Manufacturer-assigned serial number: %.*s\n",
852                   len - 4, buff + 4);
853    return 0;
854}
855
856static int
857decode_tapealert_supported_vpd(unsigned char * b, int len)
858{
859    if (len < 12) {
860        fprintf(stderr, "TapeAlert supported flags length too short=%d\n",
861                len);
862        return SG_LIB_CAT_MALFORMED;
863    }
864    printf("  Flag01h: %d  02h: %d  03h: %d  04h: %d  05h: %d  06h: %d  "
865           "07h: %d  08h: %d\n", !!(b[4] & 0x80), !!(b[4] & 0x40),
866           !!(b[4] & 0x20), !!(b[4] & 0x10), !!(b[4] & 0x8), !!(b[4] & 0x4),
867           !!(b[4] & 0x2), !!(b[4] & 0x1));
868    printf("  Flag09h: %d  0ah: %d  0bh: %d  0ch: %d  0dh: %d  0eh: %d  "
869           "0fh: %d  10h: %d\n", !!(b[5] & 0x80), !!(b[5] & 0x40),
870           !!(b[5] & 0x20), !!(b[5] & 0x10), !!(b[5] & 0x8), !!(b[5] & 0x4),
871           !!(b[5] & 0x2), !!(b[5] & 0x1));
872    printf("  Flag11h: %d  12h: %d  13h: %d  14h: %d  15h: %d  16h: %d  "
873           "17h: %d  18h: %d\n", !!(b[6] & 0x80), !!(b[6] & 0x40),
874           !!(b[6] & 0x20), !!(b[6] & 0x10), !!(b[6] & 0x8), !!(b[6] & 0x4),
875           !!(b[6] & 0x2), !!(b[6] & 0x1));
876    printf("  Flag19h: %d  1ah: %d  1bh: %d  1ch: %d  1dh: %d  1eh: %d  "
877           "1fh: %d  20h: %d\n", !!(b[7] & 0x80), !!(b[7] & 0x40),
878           !!(b[7] & 0x20), !!(b[7] & 0x10), !!(b[7] & 0x8), !!(b[7] & 0x4),
879           !!(b[7] & 0x2), !!(b[7] & 0x1));
880    printf("  Flag21h: %d  22h: %d  23h: %d  24h: %d  25h: %d  26h: %d  "
881           "27h: %d  28h: %d\n", !!(b[8] & 0x80), !!(b[8] & 0x40),
882           !!(b[8] & 0x20), !!(b[8] & 0x10), !!(b[8] & 0x8), !!(b[8] & 0x4),
883           !!(b[8] & 0x2), !!(b[8] & 0x1));
884    printf("  Flag29h: %d  2ah: %d  2bh: %d  2ch: %d  2dh: %d  2eh: %d  "
885           "2fh: %d  30h: %d\n", !!(b[9] & 0x80), !!(b[9] & 0x40),
886           !!(b[9] & 0x20), !!(b[9] & 0x10), !!(b[9] & 0x8), !!(b[9] & 0x4),
887           !!(b[9] & 0x2), !!(b[9] & 0x1));
888    printf("  Flag31h: %d  32h: %d  33h: %d  34h: %d  35h: %d  36h: %d  "
889           "37h: %d  38h: %d\n", !!(b[10] & 0x80), !!(b[10] & 0x40),
890           !!(b[10] & 0x20), !!(b[10] & 0x10), !!(b[10] & 0x8),
891           !!(b[10] & 0x4), !!(b[10] & 0x2), !!(b[10] & 0x1));
892    printf("  Flag39h: %d  3ah: %d  3bh: %d  3ch: %d  3dh: %d  3eh: %d  "
893           "3fh: %d  40h: %d\n", !!(b[11] & 0x80), !!(b[11] & 0x40),
894           !!(b[11] & 0x20), !!(b[11] & 0x10), !!(b[11] & 0x8),
895           !!(b[11] & 0x4), !!(b[11] & 0x2), !!(b[11] & 0x1));
896    return 0;
897}
898
899/* Returns 0 if successful, else error */
900int
901sdp_process_vpd_page(int sg_fd, int pn, int spn,
902                     const struct sdparm_opt_coll * opts, int req_pdt)
903{
904    int res, len, k, verb, dev_pdt, pdt;
905    unsigned char b[VPD_ATA_INFO_RESP_LEN];
906    int sz;
907    unsigned char * up;
908    const struct sdparm_vpd_page_t * vpp;
909
910    verb = (opts->verbose > 0) ? opts->verbose - 1 : 0;
911    sz = sizeof(b);
912    memset(b, 0, sz);
913    if (pn < 0) {
914        if (opts->all)
915            pn = VPD_SUPPORTED_VPDS;  /* if '--all' list supported vpds */
916        else
917            pn = VPD_DEVICE_ID;  /* default to device identification page */
918    }
919    sz = (VPD_ATA_INFO == pn) ? VPD_ATA_INFO_RESP_LEN : DEF_INQ_RESP_LEN;
920    res = sg_ll_inquiry(sg_fd, 0, 1, pn, b, sz, 0, verb);
921    if (res) {
922        fprintf(stderr, "INQUIRY fetching VPD page=0x%x failed\n", pn);
923        return res;
924    }
925    dev_pdt = b[0] & 0x1f;
926    if ((req_pdt >= 0) && (req_pdt != (dev_pdt))) {
927        fprintf(stderr, "given peripheral device type [%d] differs "
928                "from reported [%d]\n", req_pdt, dev_pdt);
929        fprintf(stderr, "  start with given pdt\n");
930        pdt = req_pdt;
931    } else
932        pdt = dev_pdt;
933    switch (pn) {
934    case VPD_SUPPORTED_VPDS:
935        if (b[1] != pn)
936            goto dumb_inq;
937        len = b[3];
938        printf("Supported VPD pages VPD page:\n");
939        if (opts->hex) {
940            dStrHex((const char *)b, len + 4, 0);
941            return 0;
942        }
943        if (len > 0) {
944            for (k = 0; k < len; ++k) {
945                vpp = sdp_get_vpd_detail(b[4 + k], -1, pdt);
946                if (vpp) {
947                    if (opts->long_out)
948                        printf("  [0x%02x] %s [%s]\n", b[4 + k],
949                               vpp->name, vpp->acron);
950                    else
951                        printf("  %s [%s]\n", vpp->name, vpp->acron);
952                } else
953                    printf("  0x%x\n", b[4 + k]);
954            }
955        } else
956            printf("  <empty>\n");
957        break;
958    case VPD_ATA_INFO:
959        if (b[1] != pn)
960            goto dumb_inq;
961        len = (b[2] << 8) + b[3];
962        if (len > sz) {
963            fprintf(stderr, "Response to ATA information VPD page "
964                    "truncated\n");
965            len = sz;
966        }
967        if (3 == opts->hex) {   /* output suitable for "hdparm --Istdin" */
968            dWordHex((const unsigned short *)(b + 60), 256, -2,
969                     sg_is_big_endian());
970            return 0;
971        }
972        if (opts->long_out)
973            printf("ATA information [0x89] VPD page:\n");
974        else
975            printf("ATA information VPD page:\n");
976        if (opts->hex && (2 != opts->hex)) {
977            dStrHex((const char *)b, len + 4, 0);
978            return 0;
979        }
980        res = decode_ata_info_vpd(b, len + 4, opts->long_out, opts->hex);
981        if (res)
982            return res;
983        break;
984    case VPD_DEVICE_ID:
985        if (b[1] != pn)
986            goto dumb_inq;
987        len = (b[2] << 8) + b[3];
988        if (len > sz) {
989            fprintf(stderr, "Response to device identification VPD page "
990                    "truncated\n");
991            len = sz;
992        }
993        if (opts->long_out)
994            printf("Device identification [0x83] VPD page:\n");
995        else if (! opts->quiet)
996            printf("Device identification VPD page:\n");
997        if (opts->hex) {
998            dStrHex((const char *)b, len + 4, 0);
999            return 0;
1000        }
1001        res = 0;
1002        if ((0 == spn) || (VPD_DI_SEL_LU & spn))
1003            res = decode_dev_ids(sdparm_assoc_arr[VPD_ASSOC_LU], b + 4, len,
1004                                 VPD_ASSOC_LU, -1, -1, opts->long_out,
1005                                 opts->quiet);
1006        if ((0 == spn) || (VPD_DI_SEL_TPORT & spn))
1007            res = decode_dev_ids(sdparm_assoc_arr[VPD_ASSOC_TPORT], b + 4,
1008                                 len, VPD_ASSOC_TPORT, -1, -1,
1009                                 opts->long_out, opts->quiet);
1010        if ((0 == spn) || (VPD_DI_SEL_TARGET & spn))
1011            res = decode_dev_ids(sdparm_assoc_arr[VPD_ASSOC_TDEVICE], b + 4,
1012                                 len, VPD_ASSOC_TDEVICE, -1, -1,
1013                                 opts->long_out, opts->quiet);
1014        if (VPD_DI_SEL_AS_IS & spn)
1015            res = decode_dev_ids(NULL, b + 4, len, -1, -1, -1,
1016                                 opts->long_out, opts->quiet);
1017        if (res)
1018            return res;
1019        break;
1020    case VPD_EXT_INQ:
1021        if (b[1] != pn)
1022            goto dumb_inq;
1023        len = (b[2] << 8) + b[3];
1024        if (len > sz) {
1025            fprintf(stderr, "Response to Extended inquiry data VPD page "
1026                    "truncated\n");
1027            len = sz;
1028        }
1029        if (opts->long_out)
1030            printf("Extended inquiry data [0x86] VPD page:\n");
1031        else if (! opts->quiet)
1032            printf("Extended inquiry data VPD page:\n");
1033        if (opts->hex) {
1034            dStrHex((const char *)b, len + 4, 0);
1035            return 0;
1036        }
1037        res = decode_ext_inq_vpd(b, len + 4, opts->quiet);
1038        if (res)
1039            return res;
1040        break;
1041    case VPD_MAN_NET_ADDR:
1042        if (b[1] != pn)
1043            goto dumb_inq;
1044        len = (b[2] << 8) + b[3];
1045        if (len > sz) {
1046            fprintf(stderr, "Response to Management network addresses VPD "
1047                    "page truncated\n");
1048            len = sz;
1049        }
1050        if (opts->long_out)
1051            printf("Management network addresses [0x85] VPD page:\n");
1052        else
1053            printf("Management network addresses VPD page:\n");
1054        if (opts->hex) {
1055            dStrHex((const char *)b, len + 4, 0);
1056            return 0;
1057        }
1058        res = decode_man_net_vpd(b, len + 4);
1059        if (res)
1060            return res;
1061        break;
1062    case VPD_MODE_PG_POLICY:
1063        if (b[1] != pn)
1064            goto dumb_inq;
1065        len = (b[2] << 8) + b[3];
1066        if (len > sz) {
1067            fprintf(stderr, "Response to Mode page policy VPD page "
1068                    "truncated\n");
1069            len = sz;
1070        }
1071        if (opts->long_out)
1072            printf("Mode page policy [0x87] VPD page:\n");
1073        else
1074            printf("mode page policy VPD page:\n");
1075        if (opts->hex) {
1076            dStrHex((const char *)b, len + 4, 0);
1077            return 0;
1078        }
1079        res = decode_mode_policy_vpd(b, len + 4);
1080        if (res)
1081            return res;
1082        break;
1083    case VPD_PROTO_LU:
1084        if (b[1] != pn)
1085            goto dumb_inq;
1086        len = (b[2] << 8) + b[3];
1087        if (len > sz) {
1088            fprintf(stderr, "Response to Protocol-specific logical unit "
1089                    "information VPD page truncated\n");
1090            len = sz;
1091        }
1092        if (opts->long_out)
1093            printf("Protocol-specific logical unit information [0x90] VPD "
1094                   "page:\n");
1095        else
1096            printf("Protocol-specific logical unit information VPD page:\n");
1097        if (opts->hex) {
1098            dStrHex((const char *)b, len + 4, 0);
1099            return 0;
1100        }
1101        res = decode_proto_lu_vpd(b, len + 4);
1102        if (res)
1103            return res;
1104        break;
1105    case VPD_PROTO_PORT:
1106        if (b[1] != pn)
1107            goto dumb_inq;
1108        len = (b[2] << 8) + b[3];
1109        if (len > sz) {
1110            fprintf(stderr, "Response to Protocol-specific port "
1111                    "information VPD page truncated\n");
1112            len = sz;
1113        }
1114        if (opts->long_out)
1115            printf("Protocol-specific port information [0x91] VPD "
1116                   "page:\n");
1117        else
1118            printf("Protocol-specific port information VPD page:\n");
1119        if (opts->hex) {
1120            dStrHex((const char *)b, len + 4, 0);
1121            return 0;
1122        }
1123        res = decode_proto_port_vpd(b, len + 4);
1124        if (res)
1125            return res;
1126        break;
1127    case VPD_SCSI_PORTS:
1128        if (b[1] != pn)
1129            goto dumb_inq;
1130        len = (b[2] << 8) + b[3];
1131        if (len > sz) {
1132            fprintf(stderr, "Response to SCSI Ports VPD page "
1133                    "truncated\n");
1134            len = sz;
1135        }
1136        if (opts->long_out)
1137            printf("SCSI Ports [0x88] VPD page:\n");
1138        else
1139            printf("SCSI Ports VPD page:\n");
1140        if (opts->hex) {
1141            dStrHex((const char *)b, len + 4, 0);
1142            return 0;
1143        }
1144        res = decode_scsi_ports_vpd(b, len + 4, opts->long_out, opts->quiet);
1145        if (res)
1146            return res;
1147        break;
1148    case VPD_SOFTW_INF_ID:
1149        if (b[1] != pn)
1150            goto dumb_inq;
1151        len = b[3];
1152        if (len > sz) {
1153            fprintf(stderr, "Response to Software interface identification "
1154                    "VPD page truncated\n");
1155            len = sz;
1156        }
1157        if (opts->long_out)
1158            printf("Software interface identification [0x84] VPD page:\n");
1159        else
1160            printf("Software interface identification VPD page:\n");
1161        if (opts->hex) {
1162            dStrHex((const char *)b, len + 4, 0);
1163            return 0;
1164        }
1165        up = b + 4;
1166        for ( ; len > 5; len -= 6, up += 6) {
1167            printf("    ");
1168            for (k = 0; k < 6; ++k)
1169                printf("%02x", (unsigned int)up[k]);
1170            printf("\n");
1171        }
1172        break;
1173    case VPD_UNIT_SERIAL_NUM:
1174        if (b[1] != pn)
1175            goto dumb_inq;
1176        len = b[3];
1177        if (opts->long_out)
1178            printf("Unit serial number [0x80] VPD page:\n");
1179        else
1180            printf("Unit serial number VPD page:\n");
1181        if (opts->hex)
1182            dStrHex((const char *)b, len + 4, 0);
1183        else if (len > 0) {
1184            if (len + 4 < (int)sizeof(b))
1185                b[len + 4] = '\0';
1186            else
1187                b[sizeof(b) - 1] = '\0';
1188            printf("  %s\n", b + 4);
1189        } else
1190            printf("  <empty>\n");
1191        break;
1192    case 0xb0:          /* VPD page depends on pdt */
1193        {
1194            int osd = 0;
1195            int sbc = 0;
1196            int ssc = 0;
1197            const char * vpd_name;
1198
1199            switch (pdt)
1200            {
1201            case 0: case 4: case 7:
1202                vpd_name = "Block limits";
1203                sbc = 1;
1204                break;
1205            case 1: case 8:
1206                vpd_name = "Sequential access device capabilities";
1207                ssc = 1;
1208                break;
1209            case 0x11:
1210                vpd_name = "OSD information";
1211                osd = 1;
1212                break;
1213            default:
1214                vpd_name = "unexpected pdt for B0h";
1215                break;
1216            }
1217            if (b[1] != pn)
1218                goto dumb_inq;
1219            len = (b[2] << 8) + b[3];
1220            if (len > sz) {
1221                fprintf(stderr, "Response to %s VPD page truncated\n",
1222                        vpd_name);
1223                len = sz;
1224            }
1225            if (opts->long_out)
1226                printf("%s [0xb0] VPD page:\n", vpd_name);
1227            else
1228                printf("%s VPD page:\n", vpd_name);
1229            if (opts->hex) {
1230                dStrHex((const char *)b, len + 4, 0);
1231                return 0;
1232            }
1233            if (ssc)
1234                res = decode_tape_dev_caps_vpd(b, len + 4);
1235            else if (sbc)
1236                res = decode_block_limits_vpd(b, len + 4);
1237            else
1238                dStrHex((const char *)b, len + 4, 0);
1239            if (res)
1240                return res;
1241        }
1242        break;
1243    case 0xb1:          /* VPD page depends on pdt */
1244        {
1245            int adc = 0;
1246            int osd = 0;
1247            int sbc = 0;
1248            int ssc = 0;
1249            const char * vpd_name;
1250
1251            switch (pdt)
1252            {
1253            case 0: case 4: case 7:
1254                vpd_name = "Block device characteristics";
1255                sbc = 1;
1256                break;
1257            case 1: case 8:
1258                vpd_name = "Manufactured assigned serial number";
1259                ssc = 1;
1260                break;
1261            case 0x11:
1262                vpd_name = "Security token";
1263                osd = 1;
1264                break;
1265            case 0x12:
1266                vpd_name = "Manufactured assigned serial number";
1267                adc = 1;
1268                break;
1269            default:
1270                vpd_name = "unexpected pdt for B1h";
1271                break;
1272            }
1273            if (b[1] != pn)
1274                goto dumb_inq;
1275            len = (b[2] << 8) + b[3];
1276            if (len > sz) {
1277                fprintf(stderr, "Response to %s VPD page truncated\n",
1278                        vpd_name);
1279                len = sz;
1280            }
1281            if (opts->long_out)
1282                printf("%s [0xb1] VPD page:\n", vpd_name);
1283            else
1284                printf("%s VPD page:\n", vpd_name);
1285            if (opts->hex) {
1286                dStrHex((const char *)b, len + 4, 0);
1287                return 0;
1288            }
1289            if (ssc || adc)
1290                res = decode_tape_man_ass_sn_vpd(b, len + 4);
1291            else if (sbc)
1292                res = decode_block_dev_chars_vpd(b, len + 4);
1293            else
1294                dStrHex((const char *)b, len + 4, 0);
1295            if (res)
1296                return res;
1297        }
1298        break;
1299    case 0xb2:          /* VPD page depends on pdt, only VPD_TA_SUPPORTED */
1300        if (b[1] != pn)
1301            goto dumb_inq;
1302        len = b[3];
1303        if (opts->long_out)
1304            printf("TapeAlert supported flags [0xb2] VPD page:\n");
1305        else
1306            printf("TapeAlert supported flags VPD page:\n");
1307        if (opts->hex) {
1308            dStrHex((const char *)b, len + 4, 0);
1309            return 0;
1310        }
1311        res = decode_tapealert_supported_vpd(b, len + 4);
1312        if (res)
1313            return res;
1314        break;
1315    default:
1316        if (b[1] != pn)
1317            goto dumb_inq;
1318        len = (b[2] << 8) + b[3] + 4;
1319        vpp = sdp_get_vpd_detail(pn, -1, pdt);
1320        if (vpp)
1321            fprintf(stderr, "%s VPD page in hex:\n", vpp->name);
1322        else
1323            fprintf(stderr, "VPD page 0x%x in hex:\n", pn);
1324        if (len > (int)sizeof(b)) {
1325            if (opts->verbose)
1326                fprintf(stderr, "page length=%d too long, trim\n", len);
1327            len = sizeof(b);
1328        }
1329        dStrHex((const char *)b, len, 0);
1330        break;
1331    }
1332    return 0;
1333
1334dumb_inq:
1335    fprintf(stderr, "malformed VPD response, VPD pages probably not "
1336            "supported\n");
1337    return SG_LIB_CAT_MALFORMED;
1338}
1339