]> mj.ucw.cz Git - pciutils.git/commitdiff
In verbose mode, display contents of VPD if possible
authorBen Hutchings <bhutchings@solarflare.com>
Tue, 18 Nov 2008 20:25:46 +0000 (20:25 +0000)
committerMartin Mares <mj@ucw.cz>
Fri, 21 Nov 2008 21:00:38 +0000 (22:00 +0100)
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
lspci.c

diff --git a/lspci.c b/lspci.c
index e86fe77b94a288a850673a5421f128218d5af58f..9f18a9b466f3ccbe5b318e5520290f5fdae70c6d 100644 (file)
--- a/lspci.c
+++ b/lspci.c
@@ -457,6 +457,161 @@ cap_agp(struct device *d, int where, int cap)
         rate);
 }
 
+static void
+print_vpd_string(const byte *buf, word len)
+{
+  while (len--)
+    {
+      byte ch = *buf++;
+      if (ch == '\\')
+        printf("\\\\");
+      else if (ch < 32 || ch == 127)
+        printf("\\x%02x", ch);
+      else
+        putchar(ch);
+    }
+}
+
+static int
+read_vpd(struct device *d, int pos, byte *buf, int len, byte *csum)
+{
+  if (!pci_read_vpd(d->dev, pos, buf, len))
+    return 0;
+  while (len--)
+    *csum += *buf++;
+  return 1;
+}
+
+static void
+cap_vpd(struct device *d)
+{
+  word res_addr = 0, res_len, part_pos, part_len;
+  byte key[2], buf[256];
+  byte tag;
+  byte csum = 0;
+
+  printf("Vital Product Data\n");
+
+  while (res_addr <= PCI_VPD_ADDR_MASK)
+    {
+      if (!read_vpd(d, res_addr, &tag, 1, &csum))
+       break;
+      if (tag & 0x80)
+       {
+         if (res_addr > PCI_VPD_ADDR_MASK + 1 - 3)
+           break;
+         if (!read_vpd(d, res_addr + 1, buf, 2, &csum))
+           break;
+         res_len = buf[0] + (buf[1] << 8);
+         res_addr += 3;
+       }
+      else
+       {
+         res_len = tag & 7;
+         tag >>= 3;
+         res_addr += 1;
+       }
+      if (res_len > PCI_VPD_ADDR_MASK + 1 - res_addr)
+       break;
+
+      part_pos = 0;
+
+      switch (tag)
+       {
+       case 0x0f:
+         printf("\t\tEnd\n");
+         return;
+
+       case 0x82:
+         printf("\t\tProduct Name: ");
+         while (part_pos < res_len)
+           {
+             part_len = res_len - part_pos;
+             if (part_len > sizeof(buf))
+               part_len = sizeof(buf);
+             if (!read_vpd(d, res_addr + part_pos, buf, part_len, &csum))
+               break;
+             print_vpd_string(buf, part_len);
+             part_pos += part_len;
+           }
+         printf("\n");
+         break;
+
+       case 0x90:
+       case 0x91:
+         printf("\t\t%s fields:\n",
+                (tag == 0x90) ? "Read-only" : "Read/write");
+
+         while (part_pos + 3 <= res_len)
+           {
+             word read_len;
+
+             if (!read_vpd(d, res_addr + part_pos, buf, 3, &csum))
+               break;
+             part_pos += 3;
+             key[0] = buf[0];
+             key[1] = buf[1];
+             part_len = buf[2];
+             if (part_len > res_len - part_pos)
+               break;
+
+             /* Only read the first byte of the RV field because the
+              * remaining bytes are not included in the checksum. */
+             read_len = (key[0] == 'R' && key[1] == 'V') ? 1 : part_len;
+             if (!read_vpd(d, res_addr + part_pos, buf, read_len, &csum))
+               break;
+
+             if ((key[0] == 'E' && key[1] == 'C') ||
+                 (key[0] == 'P' && key[1] == 'N') ||
+                 (key[0] == 'S' && key[1] == 'N') ||
+                 key[0] == 'V' ||
+                 key[0] == 'Y')
+               {
+                 /* Alphanumeric content */
+                 printf("\t\t\t%c%c: ", key[0], key[1]);
+                 print_vpd_string(buf, part_len);
+                 printf("\n");
+               }
+             else if (key[0] == 'R' && key[1] == 'V')
+               {
+                 /* Reserved and checksum */
+                 printf("\t\t\tRV: checksum %s, %d byte(s) reserved\n",
+                        csum ? "bad" : "good", part_len - 1);
+               }
+             else if (key[0] == 'R' && key[1] == 'W')
+               {
+                 /* Read-write area */
+                 printf("\t\t\tRW: %d byte(s) free\n", part_len);
+               }
+             else
+               {
+                 /* Binary or unknown content */
+                 int i;
+                 printf("\t\t\t%c%c:", key[0], key[1]);
+                 for (i = 0; i < part_len; i++)
+                   printf(" %02x", buf[i]);
+                 printf("\n");
+               }
+
+             part_pos += part_len;
+           }
+         break;
+
+       default:
+         printf("\t\tUnknown %s resource type %02x\n",
+                (tag & 0x80) ? "large" : "small", tag & ~0x80);
+         break;
+       }
+
+      res_addr += res_len;
+    }
+
+  if (res_addr == 0)
+    printf("\t\tNot readable\n");
+  else
+    printf("\t\tNo end tag found\n");
+}
+
 static void
 cap_pcix_nobridge(struct device *d, int where)
 {
@@ -1766,7 +1921,7 @@ show_caps(struct device *d)
              cap_agp(d, where, cap);
              break;
            case PCI_CAP_ID_VPD:
-             printf("Vital Product Data <?>\n");
+             cap_vpd(d);
              break;
            case PCI_CAP_ID_SLOTID:
              cap_slotid(cap);