pcidb
is a small Golang library for programmatic querying of PCI vendor,
product and class information.
We currently test pcidb
on Linux, Windows and MacOSX.
pcidb
contains a PCI database inspection and querying facility that allows
developers to query for information about hardware device classes, vendor and
product information.
The pcidb.New()
function returns a pcidb.PCIDB
struct or an error if the
PCI database could not be loaded.
pcidb
's default behaviour is to first search for pci-ids DB files on the local host system in well-known filesystem paths. Ifpcidb
cannot find a pci-ids DB file on the local host system, you can configurepcidb
to fetch a current pci-ids DB file from the network. You can enable this network-fetching behaviour with thepcidb.WithEnableNetworkFetch()
function or set thePCIDB_ENABLE_NETWORK_FETCH
to a non-0 value.
The pcidb.PCIDB
struct contains a number of fields that may be queried for
PCI information:
pcidb.PCIDB.Classes
is a map, keyed by the PCI class ID (a hex-encoded string) of pointers topcidb.Class
structs, one for each class of PCI device known topcidb
pcidb.PCIDB.Vendors
is a map, keyed by the PCI vendor ID (a hex-encoded string) of pointers topcidb.Vendor
structs, one for each PCI vendor known topcidb
pcidb.PCIDB.Products
is a map, keyed by the PCI product ID* (a hex-encoded string) of pointers topcidb.Product
structs, one for each PCI product known topcidb
NOTE: PCI products are often referred to by their "device ID". We use
the term "product ID" in pcidb
because it more accurately reflects what the
identifier is for: a specific product line produced by the vendor.
The default root mountpoint that pcidb
uses when looking for information
about the host system is /
. So, for example, when looking up known PCI IDS DB
files on Linux, pcidb
will attempt to discover a pciids DB file at
/usr/share/misc/pci.ids
. If you are calling pcidb
from a system that has an
alternate root mountpoint, you can either set the PCIDB_CHROOT
environment
variable to that alternate path, or call the pcidb.New()
function with the
pcidb.WithChroot()
modifier.
For example, if you are executing from within an application container that has
bind-mounted the root host filesystem to the mount point /host
, you would set
PCIDB_CHROOT
to /host
so that pcidb can find files like
/usr/share/misc/pci.ids
at /host/usr/share/misc/pci.ids
.
Alternately, you can use the pcidb.WithChroot()
function like so:
pci := pcidb.New(pcidb.WithChroot("/host"))
Let's take a look at the PCI device class information and how to query the PCI database for class, subclass, and programming interface information.
Each pcidb.Class
struct contains the following fields:
pcidb.Class.ID
is the hex-encoded string identifier for the device classpcidb.Class.Name
is the common name/description of the classpcidb.Class.Subclasses
is an array of pointers topcidb.Subclass
structs, one for each subclass in the device class
Each pcidb.Subclass
struct contains the following fields:
pcidb.Subclass.ID
is the hex-encoded string identifier for the device subclasspcidb.Subclass.Name
is the common name/description of the subclasspcidb.Subclass.ProgrammingInterfaces
is an array of pointers topcidb.ProgrammingInterface
structs, one for each programming interface for the device subclass
Each pcidb.ProgrammingInterface
struct contains the following fields:
pcidb.ProgrammingInterface.ID
is the hex-encoded string identifier for the programming interfacepcidb.ProgrammingInterface.Name
is the common name/description for the programming interface
package main
import (
"fmt"
"github.com/jaypipes/pcidb"
)
func main() {
pci, err := pcidb.New()
if err != nil {
fmt.Printf("Error getting PCI info: %v", err)
}
for _, devClass := range pci.Classes {
fmt.Printf(" Device class: %v ('%v')\n", devClass.Name, devClass.ID)
for _, devSubclass := range devClass.Subclasses {
fmt.Printf(" Device subclass: %v ('%v')\n", devSubclass.Name, devSubclass.ID)
for _, progIface := range devSubclass.ProgrammingInterfaces {
fmt.Printf(" Programming interface: %v ('%v')\n", progIface.Name, progIface.ID)
}
}
}
}
Example output from my personal workstation, snipped for brevity:
...
Device class: Serial bus controller ('0c')
Device subclass: FireWire (IEEE 1394) ('00')
Programming interface: Generic ('00')
Programming interface: OHCI ('10')
Device subclass: ACCESS Bus ('01')
Device subclass: SSA ('02')
Device subclass: USB controller ('03')
Programming interface: UHCI ('00')
Programming interface: OHCI ('10')
Programming interface: EHCI ('20')
Programming interface: XHCI ('30')
Programming interface: Unspecified ('80')
Programming interface: USB Device ('fe')
Device subclass: Fibre Channel ('04')
Device subclass: SMBus ('05')
Device subclass: InfiniBand ('06')
Device subclass: IPMI SMIC interface ('07')
Device subclass: SERCOS interface ('08')
Device subclass: CANBUS ('09')
...
Let's take a look at the PCI vendor information and how to query the PCI database for vendor information and the products a vendor supplies.
Each pcidb.Vendor
struct contains the following fields:
pcidb.Vendor.ID
is the hex-encoded string identifier for the vendorpcidb.Vendor.Name
is the common name/description of the vendorpcidb.Vendor.Products
is an array of pointers topcidb.Product
structs, one for each product supplied by the vendor
Each pcidb.Product
struct contains the following fields:
pcidb.Product.VendorID
is the hex-encoded string identifier for the product's vendorpcidb.Product.ID
is the hex-encoded string identifier for the productpcidb.Product.Name
is the common name/description of the subclasspcidb.Product.Subsystems
is an array of pointers topcidb.Product
structs, one for each "subsystem" (sometimes called "sub-device" in PCI literature) for the product
NOTE: A subsystem product may have a different vendor than its "parent" PCI product. This is sometimes referred to as the "sub-vendor".
Here's some example code that demonstrates listing the PCI vendors with the most known products:
package main
import (
"fmt"
"sort"
"github.com/jaypipes/pcidb"
)
type ByCountProducts []*pcidb.Vendor
func (v ByCountProducts) Len() int {
return len(v)
}
func (v ByCountProducts) Swap(i, j int) {
v[i], v[j] = v[j], v[i]
}
func (v ByCountProducts) Less(i, j int) bool {
return len(v[i].Products) > len(v[j].Products)
}
func main() {
pci, err := pcidb.New()
if err != nil {
fmt.Printf("Error getting PCI info: %v", err)
}
vendors := make([]*pcidb.Vendor, len(pci.Vendors))
x := 0
for _, vendor := range pci.Vendors {
vendors[x] = vendor
x++
}
sort.Sort(ByCountProducts(vendors))
fmt.Println("Top 5 vendors by product")
fmt.Println("====================================================")
for _, vendor := range vendors[0:5] {
fmt.Printf("%v ('%v') has %d products\n", vendor.Name, vendor.ID, len(vendor.Products))
}
}
which yields (on my local workstation as of July 7th, 2018):
Top 5 vendors by product
====================================================
Intel Corporation ('8086') has 3389 products
NVIDIA Corporation ('10de') has 1358 products
Advanced Micro Devices, Inc. [AMD/ATI] ('1002') has 886 products
National Instruments ('1093') has 601 products
Chelsio Communications Inc ('1425') has 525 products
The following is an example of querying the PCI product and subsystem information to find the products which have the most number of subsystems that have a different vendor than the top-level product. In other words, the two products which have been re-sold or re-manufactured with the most number of different companies.
package main
import (
"fmt"
"sort"
"github.com/jaypipes/pcidb"
)
type ByCountSeparateSubvendors []*pcidb.Product
func (v ByCountSeparateSubvendors) Len() int {
return len(v)
}
func (v ByCountSeparateSubvendors) Swap(i, j int) {
v[i], v[j] = v[j], v[i]
}
func (v ByCountSeparateSubvendors) Less(i, j int) bool {
iVendor := v[i].VendorID
iSetSubvendors := make(map[string]bool, 0)
iNumDiffSubvendors := 0
jVendor := v[j].VendorID
jSetSubvendors := make(map[string]bool, 0)
jNumDiffSubvendors := 0
for _, sub := range v[i].Subsystems {
if sub.VendorID != iVendor {
iSetSubvendors[sub.VendorID] = true
}
}
iNumDiffSubvendors = len(iSetSubvendors)
for _, sub := range v[j].Subsystems {
if sub.VendorID != jVendor {
jSetSubvendors[sub.VendorID] = true
}
}
jNumDiffSubvendors = len(jSetSubvendors)
return iNumDiffSubvendors > jNumDiffSubvendors
}
func main() {
pci, err := pcidb.New()
if err != nil {
fmt.Printf("Error getting PCI info: %v", err)
}
products := make([]*pcidb.Product, len(pci.Products))
x := 0
for _, product := range pci.Products {
products[x] = product
x++
}
sort.Sort(ByCountSeparateSubvendors(products))
fmt.Println("Top 2 products by # different subvendors")
fmt.Println("====================================================")
for _, product := range products[0:2] {
vendorID := product.VendorID
vendor := pci.Vendors[vendorID]
setSubvendors := make(map[string]bool, 0)
for _, sub := range product.Subsystems {
if sub.VendorID != vendorID {
setSubvendors[sub.VendorID] = true
}
}
fmt.Printf("%v ('%v') from %v\n", product.Name, product.ID, vendor.Name)
fmt.Printf(" -> %d subsystems under the following different vendors:\n", len(setSubvendors))
for subvendorID, _ := range setSubvendors {
subvendor, exists := pci.Vendors[subvendorID]
subvendorName := "Unknown subvendor"
if exists {
subvendorName = subvendor.Name
}
fmt.Printf(" - %v ('%v')\n", subvendorName, subvendorID)
}
}
}
which yields (on my local workstation as of July 7th, 2018):
Top 2 products by # different subvendors
====================================================
RTL-8100/8101L/8139 PCI Fast Ethernet Adapter ('8139') from Realtek Semiconductor Co., Ltd.
-> 34 subsystems under the following different vendors:
- OVISLINK Corp. ('149c')
- EPoX Computer Co., Ltd. ('1695')
- Red Hat, Inc ('1af4')
- Mitac ('1071')
- Netgear ('1385')
- Micro-Star International Co., Ltd. [MSI] ('1462')
- Hangzhou Silan Microelectronics Co., Ltd. ('1904')
- Compex ('11f6')
- Edimax Computer Co. ('1432')
- KYE Systems Corporation ('1489')
- ZyXEL Communications Corporation ('187e')
- Acer Incorporated [ALI] ('1025')
- Matsushita Electric Industrial Co., Ltd. ('10f7')
- Ruby Tech Corp. ('146c')
- Belkin ('1799')
- Allied Telesis ('1259')
- Unex Technology Corp. ('1429')
- CIS Technology Inc ('1436')
- D-Link System Inc ('1186')
- Ambicom Inc ('1395')
- AOPEN Inc. ('a0a0')
- TTTech Computertechnik AG (Wrong ID) ('0357')
- Gigabyte Technology Co., Ltd ('1458')
- Packard Bell B.V. ('1631')
- Billionton Systems Inc ('14cb')
- Kingston Technologies ('2646')
- Accton Technology Corporation ('1113')
- Samsung Electronics Co Ltd ('144d')
- Biostar Microtech Int'l Corp ('1565')
- U.S. Robotics ('16ec')
- KTI ('8e2e')
- Hewlett-Packard Company ('103c')
- ASUSTeK Computer Inc. ('1043')
- Surecom Technology ('10bd')
Bt878 Video Capture ('036e') from Brooktree Corporation
-> 30 subsystems under the following different vendors:
- iTuner ('aa00')
- Nebula Electronics Ltd. ('0071')
- DViCO Corporation ('18ac')
- iTuner ('aa05')
- iTuner ('aa0d')
- LeadTek Research Inc. ('107d')
- Avermedia Technologies Inc ('1461')
- Chaintech Computer Co. Ltd ('270f')
- iTuner ('aa07')
- iTuner ('aa0a')
- Microtune, Inc. ('1851')
- iTuner ('aa01')
- iTuner ('aa04')
- iTuner ('aa06')
- iTuner ('aa0f')
- iTuner ('aa02')
- iTuner ('aa0b')
- Pinnacle Systems, Inc. (Wrong ID) ('bd11')
- Rockwell International ('127a')
- Askey Computer Corp. ('144f')
- Twinhan Technology Co. Ltd ('1822')
- Anritsu Corp. ('1852')
- iTuner ('aa08')
- Hauppauge computer works Inc. ('0070')
- Pinnacle Systems Inc. ('11bd')
- Conexant Systems, Inc. ('14f1')
- iTuner ('aa09')
- iTuner ('aa03')
- iTuner ('aa0c')
- iTuner ('aa0e')
Contributions to pcidb
are welcomed! Fork the repo on GitHub and submit a pull
request with your proposed changes. Or, feel free to log an issue for a feature
request or bug report.
You can run unit tests easily using the make test
command, like so:
[jaypipes@uberbox pcidb]$ make test
go test github.com/jaypipes/pcidb
ok github.com/jaypipes/pcidb 0.045s