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