1/*	$OpenBSD: pci_subr.c,v 1.22 2017/03/22 07:21:39 jsg Exp $	*/
2/*	$NetBSD: pci_subr.c,v 1.19 1996/10/13 01:38:29 christos Exp $	*/
3
4/*
5 * Copyright (c) 1995, 1996 Christopher G. Demetriou.  All rights reserved.
6 * Copyright (c) 1994 Charles Hannum.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by Charles Hannum.
19 * 4. The name of the author may not be used to endorse or promote products
20 *    derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34/*
35 * PCI autoconfiguration support functions.
36 */
37
38#include <sys/param.h>
39#include <sys/systm.h>
40#include <sys/device.h>
41
42#include <dev/pci/pcireg.h>
43#include <dev/pci/pcivar.h>
44#ifdef PCIVERBOSE
45#include <dev/pci/pcidevs.h>
46#include <dev/pci/pcidevs_data.h>
47#endif
48
49/*
50 * Descriptions of known PCI classes and subclasses.
51 *
52 * Subclasses are described in the same way as classes, but have a
53 * NULL subclass pointer.
54 */
55struct pci_class {
56	const char	*name;
57	int		val;		/* as wide as pci_{,sub}class_t */
58	const struct pci_class *subclasses;
59};
60
61const struct pci_class pci_subclass_prehistoric[] = {
62	{ "miscellaneous",	PCI_SUBCLASS_PREHISTORIC_MISC,		},
63	{ "VGA",		PCI_SUBCLASS_PREHISTORIC_VGA,		},
64	{ 0 }
65};
66
67const struct pci_class pci_subclass_mass_storage[] = {
68	{ "SCSI",		PCI_SUBCLASS_MASS_STORAGE_SCSI,		},
69	{ "IDE",		PCI_SUBCLASS_MASS_STORAGE_IDE,		},
70	{ "floppy",		PCI_SUBCLASS_MASS_STORAGE_FLOPPY,	},
71	{ "IPI",		PCI_SUBCLASS_MASS_STORAGE_IPI,		},
72	{ "RAID",		PCI_SUBCLASS_MASS_STORAGE_RAID,		},
73	{ "ATA",		PCI_SUBCLASS_MASS_STORAGE_ATA,		},
74	{ "SATA",		PCI_SUBCLASS_MASS_STORAGE_SATA,		},
75	{ "SAS",		PCI_SUBCLASS_MASS_STORAGE_SAS,		},
76	{ "UFS",		PCI_SUBCLASS_MASS_STORAGE_UFS,		},
77	{ "miscellaneous",	PCI_SUBCLASS_MASS_STORAGE_MISC,		},
78	{ 0 },
79};
80
81const struct pci_class pci_subclass_network[] = {
82	{ "ethernet",		PCI_SUBCLASS_NETWORK_ETHERNET,		},
83	{ "token ring",		PCI_SUBCLASS_NETWORK_TOKENRING,		},
84	{ "FDDI",		PCI_SUBCLASS_NETWORK_FDDI,		},
85	{ "ATM",		PCI_SUBCLASS_NETWORK_ATM,		},
86	{ "ISDN",		PCI_SUBCLASS_NETWORK_ISDN,		},
87	{ "WorldFip",		PCI_SUBCLASS_NETWORK_WORLDFIP,		},
88	{ "PCMIG Multi Computing", PCI_SUBCLASS_NETWORK_PCIMGMULTICOMP,	},
89	{ "InfiniBand",		PCI_SUBCLASS_NETWORK_INFINIBAND,	},
90	{ "miscellaneous",	PCI_SUBCLASS_NETWORK_MISC,		},
91	{ 0 },
92};
93
94const struct pci_class pci_subclass_display[] = {
95	{ "VGA",		PCI_SUBCLASS_DISPLAY_VGA,		},
96	{ "XGA",		PCI_SUBCLASS_DISPLAY_XGA,		},
97	{ "3D",			PCI_SUBCLASS_DISPLAY_3D,		},
98	{ "miscellaneous",	PCI_SUBCLASS_DISPLAY_MISC,		},
99	{ 0 },
100};
101
102const struct pci_class pci_subclass_multimedia[] = {
103	{ "video",		PCI_SUBCLASS_MULTIMEDIA_VIDEO,		},
104	{ "audio",		PCI_SUBCLASS_MULTIMEDIA_AUDIO,		},
105	{ "telephony",		PCI_SUBCLASS_MULTIMEDIA_TELEPHONY,	},
106	{ "hdaudio",		PCI_SUBCLASS_MULTIMEDIA_HDAUDIO,	},
107	{ "miscellaneous",	PCI_SUBCLASS_MULTIMEDIA_MISC,		},
108	{ 0 },
109};
110
111const struct pci_class pci_subclass_memory[] = {
112	{ "RAM",		PCI_SUBCLASS_MEMORY_RAM,		},
113	{ "flash",		PCI_SUBCLASS_MEMORY_FLASH,		},
114	{ "miscellaneous",	PCI_SUBCLASS_MEMORY_MISC,		},
115	{ 0 },
116};
117
118const struct pci_class pci_subclass_bridge[] = {
119	{ "host",		PCI_SUBCLASS_BRIDGE_HOST,		},
120	{ "ISA",		PCI_SUBCLASS_BRIDGE_ISA,		},
121	{ "EISA",		PCI_SUBCLASS_BRIDGE_EISA,		},
122	{ "MicroChannel",	PCI_SUBCLASS_BRIDGE_MC,			},
123	{ "PCI",		PCI_SUBCLASS_BRIDGE_PCI,		},
124	{ "PCMCIA",		PCI_SUBCLASS_BRIDGE_PCMCIA,		},
125	{ "NuBus",		PCI_SUBCLASS_BRIDGE_NUBUS,		},
126	{ "CardBus",		PCI_SUBCLASS_BRIDGE_CARDBUS,		},
127	{ "RACEway",		PCI_SUBCLASS_BRIDGE_RACEWAY,		},
128	{ "Semi-transparent PCI", PCI_SUBCLASS_BRIDGE_STPCI,		},
129	{ "InfiniBand",		PCI_SUBCLASS_BRIDGE_INFINIBAND,		},
130	{ "miscellaneous",	PCI_SUBCLASS_BRIDGE_MISC,		},
131	{ "advanced switching",	PCI_SUBCLASS_BRIDGE_AS,			},
132	{ 0 },
133};
134
135const struct pci_class pci_subclass_communications[] = {
136	{ "serial",		PCI_SUBCLASS_COMMUNICATIONS_SERIAL,	},
137	{ "parallel",		PCI_SUBCLASS_COMMUNICATIONS_PARALLEL,	},
138	{ "multi-port serial",	PCI_SUBCLASS_COMMUNICATIONS_MPSERIAL,	},
139	{ "modem",		PCI_SUBCLASS_COMMUNICATIONS_MODEM,	},
140	{ "GPIB",		PCI_SUBCLASS_COMMUNICATIONS_GPIB,	},
141	{ "smartcard",		PCI_SUBCLASS_COMMUNICATIONS_SMARTCARD,	},
142	{ "miscellaneous",	PCI_SUBCLASS_COMMUNICATIONS_MISC,	},
143	{ 0 },
144};
145
146const struct pci_class pci_subclass_system[] = {
147	{ "interrupt",		PCI_SUBCLASS_SYSTEM_PIC,		},
148	{ "8237 DMA",		PCI_SUBCLASS_SYSTEM_DMA,		},
149	{ "8254 timer",		PCI_SUBCLASS_SYSTEM_TIMER,		},
150	{ "RTC",		PCI_SUBCLASS_SYSTEM_RTC,		},
151	{ "PCI Hot-Plug",	PCI_SUBCLASS_SYSTEM_PCIHOTPLUG,		},
152	{ "SD Host Controller",	PCI_SUBCLASS_SYSTEM_SDHC,		},
153	{ "IOMMU",		PCI_SUBCLASS_SYSTEM_IOMMU,		},
154	{ "root complex event",	PCI_SUBCLASS_SYSTEM_ROOTCOMPEVENT,	},
155	{ "miscellaneous",	PCI_SUBCLASS_SYSTEM_MISC,		},
156	{ 0 },
157};
158
159const struct pci_class pci_subclass_input[] = {
160	{ "keyboard",		PCI_SUBCLASS_INPUT_KEYBOARD,		},
161	{ "digitizer",		PCI_SUBCLASS_INPUT_DIGITIZER,		},
162	{ "mouse",		PCI_SUBCLASS_INPUT_MOUSE,		},
163	{ "scanner",		PCI_SUBCLASS_INPUT_SCANNER,		},
164	{ "game port",		PCI_SUBCLASS_INPUT_GAMEPORT,		},
165	{ "miscellaneous",	PCI_SUBCLASS_INPUT_MISC,		},
166	{ 0 },
167};
168
169const struct pci_class pci_subclass_dock[] = {
170	{ "generic",		PCI_SUBCLASS_DOCK_GENERIC,		},
171	{ "miscellaneous",	PCI_SUBCLASS_DOCK_MISC,			},
172	{ 0 },
173};
174
175const struct pci_class pci_subclass_processor[] = {
176	{ "386",		PCI_SUBCLASS_PROCESSOR_386,		},
177	{ "486",		PCI_SUBCLASS_PROCESSOR_486,		},
178	{ "Pentium",		PCI_SUBCLASS_PROCESSOR_PENTIUM,		},
179	{ "Alpha",		PCI_SUBCLASS_PROCESSOR_ALPHA,		},
180	{ "PowerPC",		PCI_SUBCLASS_PROCESSOR_POWERPC,		},
181	{ "MIPS",		PCI_SUBCLASS_PROCESSOR_MIPS,		},
182	{ "Co-processor",	PCI_SUBCLASS_PROCESSOR_COPROC,		},
183	{ 0 },
184};
185
186const struct pci_class pci_subclass_serialbus[] = {
187	{ "Firewire",		PCI_SUBCLASS_SERIALBUS_FIREWIRE,	},
188	{ "ACCESS.bus",		PCI_SUBCLASS_SERIALBUS_ACCESS,		},
189	{ "SSA",		PCI_SUBCLASS_SERIALBUS_SSA,		},
190	{ "USB",		PCI_SUBCLASS_SERIALBUS_USB,		},
191	/* XXX Fiber Channel/_FIBRECHANNEL */
192	{ "Fiber Channel",	PCI_SUBCLASS_SERIALBUS_FIBER,		},
193	{ "SMBus",		PCI_SUBCLASS_SERIALBUS_SMBUS,		},
194	{ "InfiniBand",		PCI_SUBCLASS_SERIALBUS_INFINIBAND,	},
195	{ "IPMI",		PCI_SUBCLASS_SERIALBUS_IPMI,		},
196	{ "SERCOS",		PCI_SUBCLASS_SERIALBUS_SERCOS,		},
197	{ "CANbus",		PCI_SUBCLASS_SERIALBUS_CANBUS,		},
198	{ 0 },
199};
200
201const struct pci_class pci_subclass_wireless[] = {
202	{ "IrDA",		PCI_SUBCLASS_WIRELESS_IRDA,		},
203	{ "Consumer IR",	PCI_SUBCLASS_WIRELESS_CONSUMERIR,	},
204	{ "RF",			PCI_SUBCLASS_WIRELESS_RF,		},
205	{ "bluetooth",		PCI_SUBCLASS_WIRELESS_BLUETOOTH,	},
206	{ "broadband",		PCI_SUBCLASS_WIRELESS_BROADBAND,	},
207	{ "802.11a (5 GHz)",	PCI_SUBCLASS_WIRELESS_802_11A,		},
208	{ "802.11b (2.4 GHz)",	PCI_SUBCLASS_WIRELESS_802_11B,		},
209	{ "miscellaneous",	PCI_SUBCLASS_WIRELESS_MISC,		},
210	{ 0 },
211};
212
213const struct pci_class pci_subclass_i2o[] = {
214	{ "standard",		PCI_SUBCLASS_I2O_STANDARD,		},
215	{ 0 },
216};
217
218const struct pci_class pci_subclass_satcom[] = {
219	{ "TV",			PCI_SUBCLASS_SATCOM_TV,			},
220	{ "audio",		PCI_SUBCLASS_SATCOM_AUDIO,		},
221	{ "voice",		PCI_SUBCLASS_SATCOM_VOICE,		},
222	{ "data",		PCI_SUBCLASS_SATCOM_DATA,		},
223	{ 0 },
224};
225
226const struct pci_class pci_subclass_crypto[] = {
227	{ "network/computing",	PCI_SUBCLASS_CRYPTO_NETCOMP,		},
228	{ "entertainment",	PCI_SUBCLASS_CRYPTO_ENTERTAINMENT,	},
229	{ "miscellaneous",	PCI_SUBCLASS_CRYPTO_MISC,		},
230	{ 0 },
231};
232
233const struct pci_class pci_subclass_dasp[] = {
234	{ "DPIO",		PCI_SUBCLASS_DASP_DPIO,			},
235	{ "Time and Frequency",	PCI_SUBCLASS_DASP_TIMEFREQ,		},
236	{ "synchronization",	PCI_SUBCLASS_DASP_SYNC,			},
237	{ "management",		PCI_SUBCLASS_DASP_MGMT,			},
238	{ "miscellaneous",	PCI_SUBCLASS_DASP_MISC,			},
239	{ 0 },
240};
241
242const struct pci_class pci_class[] = {
243	{ "prehistoric",	PCI_CLASS_PREHISTORIC,
244	    pci_subclass_prehistoric,				},
245	{ "mass storage",	PCI_CLASS_MASS_STORAGE,
246	    pci_subclass_mass_storage,				},
247	{ "network",		PCI_CLASS_NETWORK,
248	    pci_subclass_network,				},
249	{ "display",		PCI_CLASS_DISPLAY,
250	    pci_subclass_display,				},
251	{ "multimedia",		PCI_CLASS_MULTIMEDIA,
252	    pci_subclass_multimedia,				},
253	{ "memory",		PCI_CLASS_MEMORY,
254	    pci_subclass_memory,				},
255	{ "bridge",		PCI_CLASS_BRIDGE,
256	    pci_subclass_bridge,				},
257	{ "communications",	PCI_CLASS_COMMUNICATIONS,
258	    pci_subclass_communications,			},
259	{ "system",		PCI_CLASS_SYSTEM,
260	    pci_subclass_system,				},
261	{ "input",		PCI_CLASS_INPUT,
262	    pci_subclass_input,					},
263	{ "dock",		PCI_CLASS_DOCK,
264	    pci_subclass_dock,					},
265	{ "processor",		PCI_CLASS_PROCESSOR,
266	    pci_subclass_processor,				},
267	{ "serial bus",		PCI_CLASS_SERIALBUS,
268	    pci_subclass_serialbus,				},
269	{ "wireless",		PCI_CLASS_WIRELESS,
270	    pci_subclass_wireless,				},
271	{ "I2O",		PCI_CLASS_I2O,
272	    pci_subclass_i2o,					},
273	{ "satellite comm",	PCI_CLASS_SATCOM,
274	    pci_subclass_satcom,				},
275	{ "crypto",		PCI_CLASS_CRYPTO,
276	    pci_subclass_crypto,				},
277	{ "DASP",		PCI_CLASS_DASP,
278	    pci_subclass_dasp,					},
279	{ "accelerator",	PCI_CLASS_ACCELERATOR,
280	    NULL,						},
281	{ "instrumentation",	PCI_CLASS_INSTRUMENTATION,
282	    NULL,						},
283	{ "undefined",		PCI_CLASS_UNDEFINED,
284	    0,							},
285	{ 0 },
286};
287
288const char *
289pci_findvendor(pcireg_t id_reg)
290{
291#ifdef PCIVERBOSE
292	pci_vendor_id_t vendor = PCI_VENDOR(id_reg);
293	const struct pci_known_vendor *kdp;
294
295	kdp = pci_known_vendors;
296        while (kdp->vendorname != NULL) {	/* all have vendor name */
297                if (kdp->vendor == vendor)
298                        break;
299		kdp++;
300	}
301        return (kdp->vendorname);
302#else
303	return (NULL);
304#endif
305}
306
307const char *
308pci_findproduct(pcireg_t id_reg)
309{
310#ifdef PCIVERBOSE
311	pci_vendor_id_t vendor = PCI_VENDOR(id_reg);
312	pci_product_id_t product = PCI_PRODUCT(id_reg);
313	const struct pci_known_product *pkp;
314
315	pkp = pci_known_products;
316	while (pkp->productname != NULL) {	/* all have product name */
317		if (pkp->vendor == vendor && pkp->product == product)
318			break;
319		pkp++;
320	}
321	return (pkp->productname);
322#else
323	return NULL;
324#endif
325}
326
327void
328pci_devinfo(pcireg_t id_reg, pcireg_t class_reg, int showclass, char *cp,
329	    size_t cp_max)
330{
331	pci_vendor_id_t vendor;
332	pci_product_id_t product;
333	pci_class_t class;
334	pci_subclass_t subclass;
335	pci_interface_t interface;
336	pci_revision_t revision;
337	const char *vendor_namep = NULL, *product_namep = NULL;
338	const struct pci_class *classp, *subclassp;
339	size_t cp_len = 0;
340#ifdef PCIVERBOSE
341	const char *unmatched = "unknown ";
342#else
343	const char *unmatched = "";
344#endif
345
346	vendor = PCI_VENDOR(id_reg);
347	product = PCI_PRODUCT(id_reg);
348
349	class = PCI_CLASS(class_reg);
350	subclass = PCI_SUBCLASS(class_reg);
351	interface = PCI_INTERFACE(class_reg);
352	revision = PCI_REVISION(class_reg);
353
354#ifdef PCIVERBOSE
355	vendor_namep = pci_findvendor(id_reg);
356	if (vendor_namep != NULL)
357		product_namep = pci_findproduct(id_reg);
358#endif /* PCIVERBOSE */
359
360	classp = pci_class;
361	while (classp->name != NULL) {
362		if (class == classp->val)
363			break;
364		classp++;
365	}
366
367	subclassp = (classp->name != NULL) ? classp->subclasses : NULL;
368	while (subclassp && subclassp->name != NULL) {
369		if (subclass == subclassp->val)
370			break;
371		subclassp++;
372	}
373
374	if (vendor_namep == NULL)
375		snprintf(cp, cp_max, "%svendor 0x%04x product 0x%04x",
376		    unmatched, vendor, product);
377	else if (product_namep != NULL)
378		snprintf(cp, cp_max, "\"%s %s\"", vendor_namep, product_namep);
379	else
380		snprintf(cp, cp_max, "vendor \"%s\", unknown product 0x%04x",
381		    vendor_namep, product);
382	if (showclass && product_namep == NULL) {
383		strlcat(cp, " (", cp_max);
384		cp_len = strlen(cp);
385		if (classp->name == NULL)
386			snprintf(cp + cp_len, cp_max - cp_len,
387			    "unknown class 0x%02x, subclass 0x%02x",
388			    class, subclass);
389		else if (subclassp == NULL || subclassp->name == NULL)
390			snprintf(cp + cp_len, cp_max - cp_len,
391			    "class %s unknown subclass 0x%02x", classp->name,
392			    subclass);
393		else
394			snprintf(cp + cp_len, cp_max - cp_len,
395			    "class %s subclass %s", classp->name,
396			    subclassp->name);
397#if 0 /* not very useful */
398		cp_len = strlen(cp);
399		snprintf(cp + cp_len, cp_max - cp_len,
400		    ", interface 0x%02x", interface);
401#endif
402		cp_len = strlen(cp);
403		snprintf(cp + cp_len, cp_max - cp_len,
404		    ", rev 0x%02x)", revision);
405	} else {
406		cp_len = strlen(cp);
407		snprintf(cp + cp_len, cp_max - cp_len, " rev 0x%02x",
408		    revision);
409	}
410}
411