Module: LinuxStat::PCI

Defined in:
lib/linux_stat/pci.rb

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.



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/linux_stat/pci.rb', line 211

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 system_stat for more results.

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

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

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.



52
53
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
# File 'lib/linux_stat/pci.rb', line 52

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.



128
129
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
# File 'lib/linux_stat/pci.rb', line 128

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|
		_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
	}
end