pax_global_header00006660000000000000000000000064142326306440014516gustar00rootroot0000000000000052 comment=147072f30266a85b037da161aa7427a6e9351571 golang-github-jaypipes-pcidb-1.0.0/000077500000000000000000000000001423263064400171445ustar00rootroot00000000000000golang-github-jaypipes-pcidb-1.0.0/.github/000077500000000000000000000000001423263064400205045ustar00rootroot00000000000000golang-github-jaypipes-pcidb-1.0.0/.github/workflows/000077500000000000000000000000001423263064400225415ustar00rootroot00000000000000golang-github-jaypipes-pcidb-1.0.0/.github/workflows/go.yml000066400000000000000000000033721423263064400236760ustar00rootroot00000000000000name: CI tests on: push: branches: [ main ] pull_request: branches: [ main ] # see: https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners jobs: # tier 0: system-independent checks format: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 with: fetch-depth: 0 - name: set up golang uses: actions/setup-go@v2 with: go-version: 1.17 - name: format run: ./hack/check-format.sh lint: runs-on: ubuntu-20.04 steps: - name: Check out code uses: actions/checkout@v2 with: fetch-depth: 0 - name: Verify uses: golangci/golangci-lint-action@v2 with: version: v1.41.1 args: --timeout=15m0s --verbose build-ubuntu-2004: runs-on: ubuntu-20.04 strategy: matrix: go: [ '1.15', '1.16', '1.17' ] steps: - uses: actions/checkout@v2 with: fetch-depth: 0 - name: set up go uses: actions/setup-go@v2 with: go-version: ${{ matrix.go }} - name: run unit-tests run: go test -v ./... build-windows-2019: runs-on: windows-2019 strategy: matrix: go: [ '1.16' ] steps: - uses: actions/checkout@v2 - name: set up go uses: actions/setup-go@v2 with: go-version: ${{ matrix.go }} - name: run unit-tests env: PCIDB_ENABLE_NETWORK_FETCH: "1" run: go test -v ./... build-macos-1015: runs-on: macos-10.15 strategy: matrix: go: [ '1.16' ] steps: - uses: actions/checkout@v2 - name: set up go uses: actions/setup-go@v2 with: go-version: ${{ matrix.go }} - name: run unit-tests run: go test -v ./... golang-github-jaypipes-pcidb-1.0.0/.gitignore000066400000000000000000000000241423263064400211300ustar00rootroot00000000000000vendor/ coverage*.* golang-github-jaypipes-pcidb-1.0.0/COPYING000066400000000000000000000236371423263064400202120ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. golang-github-jaypipes-pcidb-1.0.0/LICENSE000066400000000000000000000261351423263064400201600ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. golang-github-jaypipes-pcidb-1.0.0/Makefile000066400000000000000000000015571423263064400206140ustar00rootroot00000000000000VENDOR := vendor PKGS := $(shell go list ./... | grep -v /$(VENDOR)/) SRC = $(shell find . -type f -name '*.go' -not -path "*/$(VENDOR)/*") BIN_DIR := $(GOPATH)/bin GOMETALINTER := $(BIN_DIR)/gometalinter .PHONY: test test: vet go test $(PKGS) $(GOMETALINTER): go get -u github.com/alecthomas/gometalinter $(GOMETALINTER) --install &> /dev/null .PHONY: lint lint: $(GOMETALINTER) $(GOMETALINTER) ./... --vendor .PHONY: fmt fmt: @gofmt -s -l -w $(SRC) .PHONY: fmtcheck fmtcheck: @bash -c "diff -u <(echo -n) <(gofmt -d $(SRC))" .PHONY: vet vet: go vet $(PKGS) .PHONY: cover cover: $(shell [ -e coverage.out ] && rm coverage.out) @echo "mode: count" > coverage-all.out @$(foreach pkg,$(PKGS),\ go test -coverprofile=coverage.out -covermode=count $(pkg);\ tail -n +2 coverage.out >> coverage-all.out;) go tool cover -html=coverage-all.out -o=coverage-all.html golang-github-jaypipes-pcidb-1.0.0/README.md000066400000000000000000000334571423263064400204370ustar00rootroot00000000000000# `pcidb` - the Golang PCI DB library [![Build Status](https://github.com/jaypipes/pcidb/actions/workflows/go.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 ``` golang-github-jaypipes-pcidb-1.0.0/context.go000066400000000000000000000042341423263064400211620ustar00rootroot00000000000000package pcidb import ( "fmt" "os" "path/filepath" "runtime" homedir "github.com/mitchellh/go-homedir" ) // Concrete merged set of configuration switches that get passed to pcidb // internal functions type context struct { chroot string cacheOnly bool cachePath string path string enableNetworkFetch bool searchPaths []string } func contextFromOptions(merged *WithOption) *context { ctx := &context{ chroot: *merged.Chroot, cacheOnly: *merged.CacheOnly, cachePath: getCachePath(), enableNetworkFetch: *merged.EnableNetworkFetch, path: *merged.Path, searchPaths: make([]string, 0), } ctx.setSearchPaths() return ctx } func getCachePath() string { hdir, err := homedir.Dir() if err != nil { fmt.Fprintf(os.Stderr, "Failed getting homedir.Dir(): %v", err) return "" } fp, err := homedir.Expand(filepath.Join(hdir, ".cache", "pci.ids")) if err != nil { fmt.Fprintf(os.Stderr, "Failed expanding local cache path: %v", err) return "" } return fp } // Depending on the operating system, sets the context's searchPaths to a set // of local filepaths to search for a pci.ids database file func (ctx *context) setSearchPaths() { // Look in direct path first, if set if ctx.path != "" { ctx.searchPaths = append(ctx.searchPaths, ctx.path) return } // A set of filepaths we will first try to search for the pci-ids DB file // on the local machine. If we fail to find one, we'll try pulling the // latest pci-ids file from the network ctx.searchPaths = append(ctx.searchPaths, ctx.cachePath) if ctx.cacheOnly { return } rootPath := ctx.chroot if runtime.GOOS != "windows" { ctx.searchPaths = append( ctx.searchPaths, filepath.Join(rootPath, "usr", "share", "hwdata", "pci.ids"), ) ctx.searchPaths = append( ctx.searchPaths, filepath.Join(rootPath, "usr", "share", "misc", "pci.ids"), ) ctx.searchPaths = append( ctx.searchPaths, filepath.Join(rootPath, "usr", "share", "hwdata", "pci.ids.gz"), ) ctx.searchPaths = append( ctx.searchPaths, filepath.Join(rootPath, "usr", "share", "misc", "pci.ids.gz"), ) } } golang-github-jaypipes-pcidb-1.0.0/discover.go000066400000000000000000000043501423263064400213130ustar00rootroot00000000000000// // Use and distribution licensed under the Apache license version 2. // // See the COPYING file in the root project directory for full text. // package pcidb import ( "bufio" "compress/gzip" "io" "net/http" "os" "path/filepath" "strings" ) const ( PCIIDS_URI = "https://pci-ids.ucw.cz/v2.2/pci.ids.gz" USER_AGENT = "golang-jaypipes-pcidb" ) func (db *PCIDB) load(ctx *context) error { var foundPath string for _, fp := range ctx.searchPaths { if _, err := os.Stat(fp); err == nil { foundPath = fp break } } if foundPath == "" { if !ctx.enableNetworkFetch { return ERR_NO_DB } // OK, so we didn't find any host-local copy of the pci-ids DB file. Let's // try fetching it from the network and storing it if err := cacheDBFile(ctx.cachePath); err != nil { return err } foundPath = ctx.cachePath } f, err := os.Open(foundPath) if err != nil { return err } defer f.Close() var scanner *bufio.Scanner if strings.HasSuffix(foundPath, ".gz") { var zipReader *gzip.Reader if zipReader, err = gzip.NewReader(f); err != nil { return err } defer zipReader.Close() scanner = bufio.NewScanner(zipReader) } else { scanner = bufio.NewScanner(f) } return parseDBFile(db, scanner) } func ensureDir(fp string) error { fpDir := filepath.Dir(fp) if _, err := os.Stat(fpDir); os.IsNotExist(err) { err = os.MkdirAll(fpDir, os.ModePerm) if err != nil { return err } } return nil } // Pulls down the latest copy of the pci-ids file from the network and stores // it in the local host filesystem func cacheDBFile(cacheFilePath string) error { ensureDir(cacheFilePath) client := new(http.Client) request, err := http.NewRequest("GET", PCIIDS_URI, nil) if err != nil { return err } request.Header.Set("User-Agent", USER_AGENT) response, err := client.Do(request) if err != nil { return err } defer response.Body.Close() f, err := os.Create(cacheFilePath) if err != nil { return err } defer func() { if err != nil { os.Remove(cacheFilePath) } }() defer f.Close() // write the gunzipped contents to our local cache file zr, err := gzip.NewReader(response.Body) if err != nil { return err } defer zr.Close() if _, err = io.Copy(f, zr); err != nil { return err } return err } golang-github-jaypipes-pcidb-1.0.0/go.mod000066400000000000000000000001321423263064400202460ustar00rootroot00000000000000module github.com/jaypipes/pcidb go 1.17 require github.com/mitchellh/go-homedir v1.0.0 golang-github-jaypipes-pcidb-1.0.0/go.sum000066400000000000000000000002651423263064400203020ustar00rootroot00000000000000github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= golang-github-jaypipes-pcidb-1.0.0/internal_test.go000066400000000000000000000030031423263064400223420ustar00rootroot00000000000000// // Use and distribution licensed under the Apache license version 2. // // See the COPYING file in the root project directory for full text. // package pcidb import ( "testing" ) func TestMergeOptions(t *testing.T) { // Verify the default values are set if no overrides are passed opts := mergeOptions() if opts.Chroot == nil { t.Fatalf("Expected opts.Chroot to be non-nil.") } if opts.CacheOnly == nil { t.Fatalf("Expected opts.CacheOnly to be non-nil.") } if opts.EnableNetworkFetch == nil { t.Fatalf("Expected opts.EnableNetworkFetch to be non-nil.") } if opts.Path == nil { t.Fatalf("Expected opts.DirectPath to be non-nil.") } // Verify if we pass an override, that value is used not the default opts = mergeOptions(WithChroot("/override")) if opts.Chroot == nil { t.Fatalf("Expected opts.Chroot to be non-nil.") } else if *opts.Chroot != "/override" { t.Fatalf("Expected opts.Chroot to be /override.") } opts = mergeOptions(WithDirectPath("/mnt/direct/pci.ids")) if opts.Path == nil { t.Fatalf("Expected opts.DirectPath to be non-nil.") } else if *opts.Path != "/mnt/direct/pci.ids" { t.Fatalf("Expected opts.DirectPath to be /mnt/direct/pci.ids") } } func TestLoad(t *testing.T) { // Start with a context with no search paths intentionally to test the // disabling of the network fetch ctx := &context{ enableNetworkFetch: false, searchPaths: []string{}, } db := &PCIDB{} err := db.load(ctx) if err != ERR_NO_DB { t.Fatalf("Expected ERR_NO_DB but got %s.", err) } } golang-github-jaypipes-pcidb-1.0.0/main.go000066400000000000000000000126651423263064400204310ustar00rootroot00000000000000// // Use and distribution licensed under the Apache license version 2. // // See the COPYING file in the root project directory for full text. // package pcidb import ( "fmt" "os" "strconv" ) var ( ERR_NO_DB = fmt.Errorf("No pci-ids DB files found (and network fetch disabled)") trueVar = true ) // ProgrammingInterface is the PCI programming interface for a class of PCI // devices type ProgrammingInterface struct { // hex-encoded PCI_ID of the programming interface ID string `json:"id"` // common string name for the programming interface Name string `json:"name"` } // Subclass is a subdivision of a PCI class type Subclass struct { // hex-encoded PCI_ID for the device subclass ID string `json:"id"` // common string name for the subclass Name string `json:"name"` // any programming interfaces this subclass might have ProgrammingInterfaces []*ProgrammingInterface `json:"programming_interfaces"` } // Class is the PCI class type Class struct { // hex-encoded PCI_ID for the device class ID string `json:"id"` // common string name for the class Name string `json:"name"` // any subclasses belonging to this class Subclasses []*Subclass `json:"subclasses"` } // Product provides information about a PCI device model // NOTE(jaypipes): In the hardware world, the PCI "device_id" is the identifier // for the product/model type Product struct { // vendor ID for the product VendorID string `json:"vendor_id"` // hex-encoded PCI_ID for the product/model ID string `json:"id"` // common string name of the vendor Name string `json:"name"` // "subdevices" or "subsystems" for the product Subsystems []*Product `json:"subsystems"` } // Vendor provides information about a device vendor type Vendor struct { // hex-encoded PCI_ID for the vendor ID string `json:"id"` // common string name of the vendor Name string `json:"name"` // all top-level devices for the vendor Products []*Product `json:"products"` } type PCIDB struct { // hash of class ID -> class information Classes map[string]*Class `json:"classes"` // hash of vendor ID -> vendor information Vendors map[string]*Vendor `json:"vendors"` // hash of vendor ID + product/device ID -> product information Products map[string]*Product `json:"products"` } // WithOption is used to represent optionally-configured settings type WithOption struct { // Chroot is the directory that pcidb uses when attempting to discover // pciids DB files Chroot *string // CacheOnly is mostly just useful for testing. It essentially disables // looking for any non ~/.cache/pci.ids filepaths (which is useful when we // want to test the fetch-from-network code paths CacheOnly *bool // Enables fetching a pci-ids from a known location on the network if no // local pci-ids DB files can be found. EnableNetworkFetch *bool // Path points to the absolute path of a pci.ids file in a non-standard // location. Path *string } func WithChroot(dir string) *WithOption { return &WithOption{Chroot: &dir} } func WithCacheOnly() *WithOption { return &WithOption{CacheOnly: &trueVar} } func WithDirectPath(path string) *WithOption { return &WithOption{Path: &path} } func WithEnableNetworkFetch() *WithOption { return &WithOption{EnableNetworkFetch: &trueVar} } func mergeOptions(opts ...*WithOption) *WithOption { // Grab options from the environs by default defaultChroot := "/" if val, exists := os.LookupEnv("PCIDB_CHROOT"); exists { defaultChroot = val } path := "" if val, exists := os.LookupEnv("PCIDB_PATH"); exists { path = val } defaultCacheOnly := false if val, exists := os.LookupEnv("PCIDB_CACHE_ONLY"); exists { if parsed, err := strconv.ParseBool(val); err != nil { fmt.Fprintf( os.Stderr, "Failed parsing a bool from PCIDB_CACHE_ONLY "+ "environ value of %s", val, ) } else if parsed { defaultCacheOnly = parsed } } defaultEnableNetworkFetch := false if val, exists := os.LookupEnv("PCIDB_ENABLE_NETWORK_FETCH"); exists { if parsed, err := strconv.ParseBool(val); err != nil { fmt.Fprintf( os.Stderr, "Failed parsing a bool from PCIDB_ENABLE_NETWORK_FETCH "+ "environ value of %s", val, ) } else if parsed { defaultEnableNetworkFetch = parsed } } merged := &WithOption{} for _, opt := range opts { if opt.Chroot != nil { merged.Chroot = opt.Chroot } if opt.CacheOnly != nil { merged.CacheOnly = opt.CacheOnly } if opt.EnableNetworkFetch != nil { merged.EnableNetworkFetch = opt.EnableNetworkFetch } if opt.Path != nil { merged.Path = opt.Path } } // Set the default value if missing from merged if merged.Chroot == nil { merged.Chroot = &defaultChroot } if merged.CacheOnly == nil { merged.CacheOnly = &defaultCacheOnly } if merged.EnableNetworkFetch == nil { merged.EnableNetworkFetch = &defaultEnableNetworkFetch } if merged.Path == nil { merged.Path = &path } return merged } // New returns a pointer to a PCIDB struct which contains information you can // use to query PCI vendor, product and class information. It accepts zero or // more pointers to WithOption structs. If you want to modify the behaviour of // pcidb, use one of the option modifiers when calling New. For example, to // change the root directory that pcidb uses when discovering pciids DB files, // call New(WithChroot("/my/root/override")) func New(opts ...*WithOption) (*PCIDB, error) { ctx := contextFromOptions(mergeOptions(opts...)) db := &PCIDB{} if err := db.load(ctx); err != nil { return nil, err } return db, nil } golang-github-jaypipes-pcidb-1.0.0/parse.go000066400000000000000000000114511423263064400206070ustar00rootroot00000000000000// // Use and distribution licensed under the Apache license version 2. // // See the COPYING file in the root project directory for full text. // package pcidb import ( "bufio" "strings" ) func parseDBFile(db *PCIDB, scanner *bufio.Scanner) error { inClassBlock := false db.Classes = make(map[string]*Class, 20) db.Vendors = make(map[string]*Vendor, 200) db.Products = make(map[string]*Product, 1000) subclasses := make([]*Subclass, 0) progIfaces := make([]*ProgrammingInterface, 0) var curClass *Class var curSubclass *Subclass var curProgIface *ProgrammingInterface vendorProducts := make([]*Product, 0) var curVendor *Vendor var curProduct *Product var curSubsystem *Product productSubsystems := make([]*Product, 0) for scanner.Scan() { line := scanner.Text() // skip comments and blank lines if line == "" || strings.HasPrefix(line, "#") { continue } lineBytes := []rune(line) // Lines starting with an uppercase "C" indicate a PCI top-level class // dbrmation block. These lines look like this: // // C 02 Network controller if lineBytes[0] == 'C' { if curClass != nil { // finalize existing class because we found a new class block curClass.Subclasses = subclasses subclasses = make([]*Subclass, 0) } inClassBlock = true classID := string(lineBytes[2:4]) className := string(lineBytes[6:]) curClass = &Class{ ID: classID, Name: className, Subclasses: subclasses, } db.Classes[curClass.ID] = curClass continue } // Lines not beginning with an uppercase "C" or a TAB character // indicate a top-level vendor dbrmation block. These lines look like // this: // // 0a89 BREA Technologies Inc if lineBytes[0] != '\t' { if curVendor != nil { // finalize existing vendor because we found a new vendor block curVendor.Products = vendorProducts vendorProducts = make([]*Product, 0) } inClassBlock = false vendorID := string(lineBytes[0:4]) vendorName := string(lineBytes[6:]) curVendor = &Vendor{ ID: vendorID, Name: vendorName, Products: vendorProducts, } db.Vendors[curVendor.ID] = curVendor continue } // Lines beginning with only a single TAB character are *either* a // subclass OR are a device dbrmation block. If we're in a class // block (i.e. the last parsed block header was for a PCI class), then // we parse a subclass block. Otherwise, we parse a device dbrmation // block. // // A subclass dbrmation block looks like this: // // \t00 Non-VGA unclassified device // // A device dbrmation block looks like this: // // \t0002 PCI to MCA Bridge if len(lineBytes) > 1 && lineBytes[1] != '\t' { if inClassBlock { if curSubclass != nil { // finalize existing subclass because we found a new subclass block curSubclass.ProgrammingInterfaces = progIfaces progIfaces = make([]*ProgrammingInterface, 0) } subclassID := string(lineBytes[1:3]) subclassName := string(lineBytes[5:]) curSubclass = &Subclass{ ID: subclassID, Name: subclassName, ProgrammingInterfaces: progIfaces, } subclasses = append(subclasses, curSubclass) } else { if curProduct != nil { // finalize existing product because we found a new product block curProduct.Subsystems = productSubsystems productSubsystems = make([]*Product, 0) } productID := string(lineBytes[1:5]) productName := string(lineBytes[7:]) productKey := curVendor.ID + productID curProduct = &Product{ VendorID: curVendor.ID, ID: productID, Name: productName, } vendorProducts = append(vendorProducts, curProduct) db.Products[productKey] = curProduct } } else { // Lines beginning with two TAB characters are *either* a subsystem // (subdevice) OR are a programming interface for a PCI device // subclass. If we're in a class block (i.e. the last parsed block // header was for a PCI class), then we parse a programming // interface block, otherwise we parse a subsystem block. // // A programming interface block looks like this: // // \t\t00 UHCI // // A subsystem block looks like this: // // \t\t0e11 4091 Smart Array 6i if inClassBlock { progIfaceID := string(lineBytes[2:4]) progIfaceName := string(lineBytes[6:]) curProgIface = &ProgrammingInterface{ ID: progIfaceID, Name: progIfaceName, } progIfaces = append(progIfaces, curProgIface) } else { vendorID := string(lineBytes[2:6]) subsystemID := string(lineBytes[7:11]) subsystemName := string(lineBytes[13:]) curSubsystem = &Product{ VendorID: vendorID, ID: subsystemID, Name: subsystemName, } productSubsystems = append(productSubsystems, curSubsystem) } } } return nil } golang-github-jaypipes-pcidb-1.0.0/parse_test.go000066400000000000000000000100671423263064400216500ustar00rootroot00000000000000// // Use and distribution licensed under the Apache license version 2. // // See the COPYING file in the root project directory for full text. // package pcidb_test import ( "os" "testing" "github.com/jaypipes/pcidb" ) func TestPCI(t *testing.T) { if _, ok := os.LookupEnv("GHW_TESTING_SKIP_PCI"); ok { t.Skip("Skipping PCI tests.") } info, err := pcidb.New() if err != nil { t.Fatalf("Expected no error creating PciInfo, but got %v", err) } if len(info.Classes) == 0 { t.Fatalf("Expected >0 PCI classes, but found 0.") } sbController, exists := info.Classes["0c"] if !exists { t.Fatalf("Expected to find serial bus controller class in hash for identifier '0c'") } if sbController.Name != "Serial bus controller" { t.Fatalf("Expected serial bus controller class name to be 'Serial bus controller' but got '%v'", sbController.Name) } if len(sbController.Subclasses) == 0 { t.Fatalf("Expected >0 Subclasses for sbController, but found 0.") } var firewireSubclass *pcidb.Subclass for _, sc := range sbController.Subclasses { if sc.ID == "00" { firewireSubclass = sc } } if firewireSubclass == nil { t.Fatalf("Failed to find Firewire subclass to sbController with ID of '10'.") } // Check the firewire programming interfaces were found if len(firewireSubclass.ProgrammingInterfaces) == 0 { t.Fatalf("Expected >0 Firewire programming interfaces, but found 0.") } var ohciIface *pcidb.ProgrammingInterface for _, progIface := range firewireSubclass.ProgrammingInterfaces { if progIface.ID == "10" { ohciIface = progIface } } if ohciIface == nil { t.Fatalf("Failed to find OHCI programming interface to firewire subclass with ID of '10'.") } if ohciIface.Name != "OHCI" { t.Fatalf("Expected OHCI programming interface name to be 'OHCI' but got '%v'", ohciIface.Name) } if len(info.Vendors) == 0 { t.Fatalf("Expected >0 PCI vendors, but found 0.") } intelInc, exists := info.Vendors["8086"] if !exists { t.Fatalf("Expected to find Intel vendor in hash for identifier '8086'") } if intelInc.Name != "Intel Corporation" { t.Fatalf("Expected Intel vendor name to be 'Intel Corporation' but got '%v'", intelInc.Name) } if len(info.Products) == 0 { t.Fatalf("Expected >0 PCI products, but found 0.") } intel10GBackplaneKey := "808610f8" intel10GBackplane, exists := info.Products[intel10GBackplaneKey] if !exists { t.Fatalf("Failed to find Intel 10GB Backplane Connection in products hash for key '808610f8'") } if intel10GBackplane.Name != "82599 10 Gigabit Dual Port Backplane Connection" { t.Fatalf("Expected Intel product '10f8' to have name '82599 10 Gigabit Dual Port Backplane Connection' but got %v", intel10GBackplane.Name) } if intel10GBackplane.VendorID != "8086" { t.Fatalf("Expected Intel product '10f8' to have vendor ID of '8086' but got '%v'", intel10GBackplane.VendorID) } // Make sure this product is linked in the Intel PCIVendor.Products array foundBackplane := false for _, prod := range intelInc.Products { if prod.ID == "10f8" { foundBackplane = true } } if !foundBackplane { t.Fatalf("Failed to find 82599 10G backplane connection in Intel vendor products array.") } // Test subsystems. We'll be testing that the "NetRAID-1M" product, which // is an HP product subsystem for the American Megatrends Inc. "MegaRAID" // product, appears in that products list of subsystems. The relevant // pci.ids file snippet looks like this (cut for brevity) // // 101e American Megatrends Inc. // \t1960 MegaRAID // \t\t103c 60e7 NetRAID-1M megaRaidProdKey := "101e1960" megaRaidProd, exists := info.Products[megaRaidProdKey] if !exists { t.Fatalf("Failed to find MegaRAID in products hash for key '101e1960'") } if len(megaRaidProd.Subsystems) == 0 { t.Fatalf("Expected >0 PCI subsystems for MegaRAID product, but found 0.") } foundNetRaid := false for _, subsystem := range megaRaidProd.Subsystems { if subsystem.VendorID == "103c" && subsystem.ID == "60e7" { foundNetRaid = true } } if !foundNetRaid { t.Fatalf("Failed to find NetRAID subsystem in MegaRAID product subsystems array.") } }