An open API service indexing awesome lists of open source software.

https://github.com/jaypipes/pcidb

Small golang library for querying PCI database (pciids) information
https://github.com/jaypipes/pcidb

discovery golang-library hardware inspection-library pci-devices pci-id

Last synced: over 1 year ago
JSON representation

Small golang library for querying PCI database (pciids) information

Awesome Lists containing this project

README

          

# `pcidb` - the Golang PCI DB library

[![Build Status](https://github.com/jaypipes/pcidb/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/jaypipes/pcidb/actions)
[![Go Report Card](https://goreportcard.com/badge/github.com/jaypipes/pcidb)](https://goreportcard.com/report/github.com/jaypipes/pcidb)
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md)

`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.

## Usage

`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. If `pcidb` cannot find a
> pci-ids DB file on the local host system, you can configure `pcidb` to fetch
> a current pci-ids DB file from the network. You can enable this
> network-fetching behaviour with the `pcidb.WithEnableNetworkFetch()` function
> or set the `PCIDB_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 to `pcidb.Class` structs, one for each class of PCI
device known to `pcidb`
* `pcidb.PCIDB.Vendors` is a map, keyed by the PCI vendor ID (a hex-encoded
string) of pointers to `pcidb.Vendor` structs, one for each PCI vendor
known to `pcidb`
* `pcidb.PCIDB.Products` is a map, keyed by the PCI product ID* (a hex-encoded
string) of pointers to `pcidb.Product` structs, one for each PCI product
known to `pcidb`

**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.

### Overriding the root mountpoint `pcidb` uses

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:

```go
pci := pcidb.New(pcidb.WithChroot("/host"))
```

### PCI device classes

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
class
* `pcidb.Class.Name` is the common name/description of the class
* `pcidb.Class.Subclasses` is an array of pointers to
`pcidb.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
subclass
* `pcidb.Subclass.Name` is the common name/description of the subclass
* `pcidb.Subclass.ProgrammingInterfaces` is an array of pointers to
`pcidb.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 interface
* `pcidb.ProgrammingInterface.Name` is the common name/description for the
programming interface

```go
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')
...
```

### PCI vendors and products

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 vendor
* `pcidb.Vendor.Name` is the common name/description of the vendor
* `pcidb.Vendor.Products` is an array of pointers to `pcidb.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 vendor
* `pcidb.Product.ID` is the hex-encoded string identifier for the product
* `pcidb.Product.Name` is the common name/description of the subclass
* `pcidb.Product.Subsystems` is an array of pointers to
`pcidb.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:

```go
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.

```go
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')
```

## Developers

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.

### Running tests

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
```