Module: LinuxStat::PCI

Defined in:
lib/linux_stat/pci.rb

Overview

Shows various PCI device related information of the current system.

Class Method Summary collapse

Class Method Details

.countObject Also known as: count_devices

Reads /proc/bus/pci/devices, counts and returns the total number of lines.

But if the information isn’t available, it will look into the contents of /sys/bus/pci/devices, and counts the total number of devices connected to the PCI. It checks for devices with vendor and device id files. If there’s no such file, it will not count them as a PCI connected device.

The return type is an integer.

But if the information isn’t available, it will return nil.



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/linux_stat/pci.rb', line 216

def count
  @@proc_pci_readable ||= File.readable?('/proc/bus/pci/devices')
  @@sys_pci_readable ||= File.readable?('/sys/bus/pci/devices/')

  if @@proc_pci_readable
    IO.readlines('/proc/bus/pci/devices'.freeze).length

  elsif @@sys_pci_readable
    Dir['/sys/bus/pci/devices/*/'.freeze].count { |x|
      id_vendor_file = File.join(x, 'vendor'.freeze)
      id_product_file = File.join(x, 'device'.freeze)
      File.readable?(id_vendor_file) && File.readable?(id_product_file)
    }

  else
    nil
  end
end

.devices_info(hwdata: true) ⇒ Object

devices_info(hwdata: true)

Not to be confused with devices_stat

Take a look at LinuxStat::PCI.devices_stat for more info.

Returns details about the devices found in /proc/bus/pci/devices file.

The details doesn’t contain a lot of details, it opens just one file.

The return value is an Array of multiple Hashes. If there’s no info available, it will rather return an empty Array.

On Android Termux for example, it can not list the directories because they are not readable the the regular user.

It can have information like:

id, vendor, device, irq, and kernel_driver

An example of the returned sample from a test machine is:

LinuxStat::PCI.devices_info

=> [{:id=>"8086:1904", :vendor=>"8086", :device=>"1904", :irq=>0, :kernel_driver=>"skl_uncore", :hwdata=>{:vendor=>"Intel Corporation", :product=>"Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers"}}]

Right, it’s an Array of Hashes.

It also takes one option. The hwdata, which is true by default.

Information about usb devices is found inside /usr/share/hwdata/pci.ids

The data contains the vendor and the product, the subvendor and the subproduct.

If the option is enabled, it will try read /usr/share/hwdata/pci.ids

But the file will be read only once. The consecutive calls to this method won’t open the hwdata all the times.

But if there’s no hwdata, the Hash returned by this method will not contain hwdata key.

The data is only populated if it’s available. For example, if there’s no manufacturer available for the product, the returned Hash will not contain the information about the manufacturer.

Also note that if there’s no info available or no PCI enabled devices, it will return an empty Hash.



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/linux_stat/pci.rb', line 54

def devices_info(hwdata: true)
  @@proc_pci_readable ||= File.readable?('/proc/bus/pci/devices')
  return {} unless @@proc_pci_readable

  IO.readlines('/proc/bus/pci/devices'.freeze).map! { |dev|
    x = dev.split

    vendor = x[1][0..3]
    device = x[1][4..-1]
    irq = x[2].to_i(16)
    kernel_driver = x[-1]

    query = if hwdata
      query_hwdata(vendor, device)
    else
      {}
    end

    ret = {
      id: "#{vendor}:#{device}",
      vendor: vendor, device: device,
      irq: irq,
      kernel_driver: kernel_driver
    }

    ret.merge!(hwdata: query) unless query.empty?

    ret
  }
end

.devices_stat(hwdata: true) ⇒ Object

devices_stat(hwdata: true)

Returns details about the devices found in /sys/bus/pci/devices/

The return value is an Array of multiple Hashes. If there’s no info available, it will rather return an empty Array.

On Android Termux for example, it can not list the directories because they are not readable the the regular user.

It can have information like:

path, id, vendor, device, subvendor, sub_device, kernel_driver, revision, irq, enable, hwdata.

An example of the returned sample from a test machine is:

LinuxStat::PCI.devices_stat

=> [{:path=>"/sys/bus/pci/devices/0000:00:00.0/", :id=>"8086:1904", :vendor=>"8086", :device=>"1904", :sub_vendor=>"1028", :sub_device=>"077d", :kernel_driver=>"skl_uncore", :revision=>"0x08", :irq=>0, :enable=>false, :hwdata=>{:vendor=>"Intel Corporation", :product=>"Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers", :sub_system=>nil}}]

Right, it’s an Array of Hashes.

It also takes one option. The hwdata, which is true by default.

Information about usb devices is found inside /usr/share/hwdata/pci.ids

The data contains the vendor and the product, the subvendor and the subproduct.

If the option is enabled, it will try read /usr/share/hwdata/pci.ids

But the file will be read only once. The consecutive calls to this method won’t open the hwdata all the times.

But if there’s no hwdata, the Hash returned by this method will not contain hwdata key.

The data is only populated if it’s available. For example, if there’s no manufacturer available for the product, the returned Hash will not contain the information about the manufacturer.

If there’s no /sys/bus/pci/devices/, it will call LinuxStat::PCI.devices_info

Also note that if there’s no info available or no PCI enabled devices, it will return an empty Hash.



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/linux_stat/pci.rb', line 130

def devices_stat(hwdata: true)
  @@sys_pci_readable ||= File.readable?('/sys/bus/pci/devices/')
  return devices_info(hwdata: hwdata) unless @@sys_pci_readable

  Dir['/sys/bus/pci/devices/*/'.freeze].sort!.map! { |x|
    begin
      _vendor_file = File.join(x, 'vendor'.freeze)
      next unless File.readable?(_vendor_file)
      vendor = IO.read(_vendor_file).to_i(16).to_s(16)
      prepend_0(vendor)

      _device_file = File.join(x, 'device'.freeze)
      next unless File.readable?(_device_file)
      device = IO.read(_device_file).to_i(16).to_s(16)
      prepend_0(device)

      _sub_vendor_file = File.join(x, 'subsystem_vendor'.freeze)
      sub_vendor = File.readable?(_sub_vendor_file) ? IO.read(_sub_vendor_file).to_i(16).to_s(16) : nil
      prepend_0(sub_vendor) if sub_vendor

      _sub_device_file = File.join(x, 'subsystem_device'.freeze)
      sub_device = File.readable?(_sub_device_file) ? IO.read(_sub_device_file).to_i(16).to_s(16) : nil
      prepend_0(sub_device) if sub_device

      _uevent = File.join(x, 'uevent'.freeze)
      uevent = File.readable?(_uevent) ? IO.foreach(_uevent) : nil

      kernel_driver = if uevent
        uevent.find { |_x|
          _x.split(?=.freeze)[0].to_s.tap(&:strip!) == 'DRIVER'.freeze
        } &.split(?=) &.[](1) &.tap(&:strip!)
      else
        nil
      end

      _revision_file = File.join(x, 'revision'.freeze)
      revision = File.readable?(_revision_file) ? IO.read(_revision_file).tap(&:strip!) : ''.freeze

      _irq_file = File.join(x, 'irq'.freeze)
      irq = File.readable?(_irq_file) ? IO.read(_irq_file).to_i : nil

      _enable_file = File.join(x, 'enable'.freeze)
      enable = File.readable?(_enable_file) ? IO.read(_enable_file).to_i  == 1 : nil

      query = if hwdata && sub_vendor && sub_device
        query_hwdata(vendor, device, sub_vendor, sub_device)
      elsif hwdata && sub_vendor
        query_hwdata(vendor, device, sub_vendor)
      elsif hwdata
        query_hwdata(vendor, device)
      else
        {}
      end

      ret = {
        path: x, id: "#{vendor}:#{device}",
        vendor: vendor, device: device
      }

      ret.merge!(sub_vendor: sub_vendor) if sub_vendor
      ret.merge!(sub_device: sub_device) if sub_device

      ret.merge!(kernel_driver: kernel_driver) if kernel_driver
      ret.merge!(revision: revision) unless revision.empty?
      ret.merge!(irq: irq) if irq
      ret.merge!(enable: enable) unless enable.nil?
      ret.merge!(hwdata: query) unless query.empty?

      ret
    rescue StandardError
    end
  }.tap(&:compact!)
end

.hwdata_fileObject

Returns the hwdata_file as string.

If hwdata_file isn’t set, it will return an empty frozen string.

Once it’s set, it can’t be changed.



264
265
266
267
# File 'lib/linux_stat/pci.rb', line 264

def hwdata_file
  @@hwdata_file ||= nil
  @@hwdata_file ? @@hwdata_file : ''.freeze
end

.hwdata_file=(file) ⇒ Object

hwdata_file = file

Lets you set the hwdata_file about pci.ids.

The hwdata file about pci.ids contains vendor name and product name information about devices. This is then mapped by the other methods that utilizes hwdata/pci.ids.

Do note that this method is intended to run only once, at the beginning. If you use any other method that utilizes hwdata/pci.ids, before calling this method, this method will not work.



246
247
248
# File 'lib/linux_stat/pci.rb', line 246

def hwdata_file=(file)
  @@hwdata_file ||= file.freeze
end

.hwdata_file_set?Boolean

Checks if hwdata_file is already initialized or not. Once it’s initialized, calling hwdata_file = ‘something/pci.ids’ is futile.



253
254
255
256
# File 'lib/linux_stat/pci.rb', line 253

def hwdata_file_set?
  @@hwdata_file ||= false
  !!@@hwdata_file
end

.initialize_hwdataObject

Initializes hwdata

hwdata can take upto 0.1 to 0.2 seconds to get initialized.

Calling this method will load hwdata for future use.

Once it’s initialized, hwdata_file can’t be changed.

If this method initializes hwdata, it will return true Othewise this method will return false.



280
281
282
283
284
285
# File 'lib/linux_stat/pci.rb', line 280

def initialize_hwdata
  @@hwdata ||= nil
  init = !@@hwdata
  hwdata
  init
end