]> mj.ucw.cz Git - pciutils.git/blob - ls-vpd.c
cc279c3bf945b76dc8e1c635c10ad964440553cd
[pciutils.git] / ls-vpd.c
1 /*
2  *      The PCI Utilities -- Show Vital Product Data
3  *
4  *      Copyright (c) 2008 Solarflare Communications
5  *
6  *      Written by Ben Hutchings <bhutchings@solarflare.com>
7  *      Improved by Martin Mares <mj@ucw.cz>
8  *
9  *      Can be freely distributed and used under the terms of the GNU GPL.
10  */
11
12 #include <stdio.h>
13
14 #include "lspci.h"
15
16 /*
17  *  The list of all known VPD items and their formats.
18  *  Technically, this belongs to the pci.ids file, but the VPD does not seem
19  *  to be developed any longer, so we have chosen the easier way.
20  */
21
22 enum vpd_format {
23   F_BINARY,
24   F_TEXT,
25   F_RESVD,
26   F_RDWR,
27 };
28
29 static const struct vpd_item {
30   byte id1, id2;
31   byte format;
32   const char *name;
33 } vpd_items[] = {
34   { 'C','P', F_BINARY,  "Extended capability" },
35   { 'E','C', F_TEXT,    "Engineering changes" },
36   { 'M','N', F_TEXT,    "Manufacture ID" },
37   { 'P','N', F_TEXT,    "Part number" },
38   { 'R','V', F_RESVD,   "Reserved" },
39   { 'R','W', F_RDWR,    "Read-write area" },
40   { 'S','N', F_TEXT,    "Serial number" },
41   { 'Y','A', F_TEXT,    "Asset tag" },
42   { 'V', 0 , F_TEXT,    "Vendor specific" },
43   { 'Y', 0 , F_TEXT,    "System specific" },
44 /*
45  *  The following VPD keywords are vendor specific or not part of any
46  *  current PCI-SIG specification
47  */
48   { 'C','C', F_TEXT,   "CCIN" },
49   { 'F','C', F_TEXT,   "Feature code" },
50   { 'F','N', F_TEXT,   "FRU" },
51   { 'N','A', F_TEXT,   "Network address" },
52   { 'R','M', F_TEXT,   "Firmware version" },
53   { 'Z', 0 , F_TEXT,   "Product specific" },
54 /*
55  *  End vendor specific VPD keywords
56  */
57   {  0,  0 , F_BINARY,  "Unknown" }
58 };
59
60 static void
61 print_vpd_string(const byte *buf, word len)
62 {
63   while (len--)
64     {
65       byte ch = *buf++;
66       if (ch == '\\')
67         printf("\\\\");
68       else if (!ch && !len)
69         ;  /* Cards with null-terminated strings have been observed */
70       else if (ch < 32 || ch == 127)
71         printf("\\x%02x", ch);
72       else
73         putchar(ch);
74     }
75 }
76
77 static void
78 print_vpd_binary(const byte *buf, word len)
79 {
80   int i;
81   for (i = 0; i < len; i++)
82     {
83       if (i)
84         putchar(' ');
85       printf("%02x", buf[i]);
86     }
87 }
88
89 static int
90 read_vpd(struct device *d, int pos, byte *buf, int len, byte *csum)
91 {
92   if (!pci_read_vpd(d->dev, pos, buf, len))
93     return 0;
94   while (len--)
95     *csum += *buf++;
96   return 1;
97 }
98
99 void
100 cap_vpd(struct device *d)
101 {
102   word res_addr = 0, res_len, part_pos, part_len;
103   byte buf[256];
104   byte tag;
105   byte csum = 0;
106
107   printf("Vital Product Data\n");
108   if (verbose < 2)
109     return;
110
111   while (res_addr <= PCI_VPD_ADDR_MASK)
112     {
113       if (!read_vpd(d, res_addr, &tag, 1, &csum))
114         break;
115       if (tag & 0x80)
116         {
117           if (res_addr > PCI_VPD_ADDR_MASK + 1 - 3)
118             break;
119           if (!read_vpd(d, res_addr + 1, buf, 2, &csum))
120             break;
121           res_len = buf[0] + (buf[1] << 8);
122           res_addr += 3;
123         }
124       else
125         {
126           res_len = tag & 7;
127           tag >>= 3;
128           res_addr += 1;
129         }
130       if (res_len > PCI_VPD_ADDR_MASK + 1 - res_addr)
131         break;
132
133       part_pos = 0;
134
135       switch (tag)
136         {
137         case 0x0f:
138           printf("\t\tEnd\n");
139           return;
140
141         case 0x82:
142           printf("\t\tProduct Name: ");
143           while (part_pos < res_len)
144             {
145               part_len = res_len - part_pos;
146               if (part_len > sizeof(buf))
147                 part_len = sizeof(buf);
148               if (!read_vpd(d, res_addr + part_pos, buf, part_len, &csum))
149                 break;
150               print_vpd_string(buf, part_len);
151               part_pos += part_len;
152             }
153           printf("\n");
154           break;
155
156         case 0x90:
157         case 0x91:
158           printf("\t\t%s fields:\n",
159                  (tag == 0x90) ? "Read-only" : "Read/write");
160
161           while (part_pos + 3 <= res_len)
162             {
163               word read_len;
164               const struct vpd_item *item;
165               byte id1, id2;
166
167               if (!read_vpd(d, res_addr + part_pos, buf, 3, &csum))
168                 break;
169               part_pos += 3;
170               id1 = buf[0];
171               id2 = buf[1];
172               part_len = buf[2];
173               if (part_len > res_len - part_pos)
174                 break;
175
176               /* Is this item known? */
177               for (item=vpd_items; item->id1 && item->id1 != id1 ||
178                                    item->id2 && item->id2 != id2; item++)
179                 ;
180
181               /* Only read the first byte of the RV field because the
182                * remaining bytes are not included in the checksum. */
183               read_len = (item->format == F_RESVD) ? 1 : part_len;
184               if (!read_vpd(d, res_addr + part_pos, buf, read_len, &csum))
185                 break;
186
187               printf("\t\t\t[%c%c] %s: ", id1, id2, item->name);
188
189               switch (item->format)
190                 {
191                 case F_TEXT:
192                   print_vpd_string(buf, part_len);
193                   printf("\n");
194                   break;
195                 case F_BINARY:
196                   print_vpd_binary(buf, part_len);
197                   printf("\n");
198                   break;
199                 case F_RESVD:
200                   printf("checksum %s, %d byte(s) reserved\n", csum ? "bad" : "good", part_len - 1);
201                   break;
202                 case F_RDWR:
203                   printf("%d byte(s) free\n", part_len);
204                   break;
205                 }
206
207               part_pos += part_len;
208             }
209           break;
210
211         default:
212           printf("\t\tUnknown %s resource type %02x, will not decode more.\n",
213                  (tag & 0x80) ? "large" : "small", tag & ~0x80);
214           return;
215         }
216
217       res_addr += res_len;
218     }
219
220   if (res_addr == 0)
221     printf("\t\tNot readable\n");
222   else
223     printf("\t\tNo end tag found\n");
224 }