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