class ComputeUnit::Device

Constants

PROC_PATH
SYSFS_DEVICES_PATH

We can supply a mock sysfs path in order to test with containers and other scenarios

Attributes

device_class_id[R]
device_id[R]
device_path[R]
device_vendor_id[R]
make[R]
model[R]
subsystem_device_id[R]
subsystem_vendor_id[R]
vendor[R]

Public Class Methods

create_from_path(device_path) click to toggle source

@return [Device] - creates a device from the given path @param device_path [String] - the sysfs path to of the device

# File lib/compute_unit/device.rb, line 183
def self.create_from_path(device_path)
  opts = {
    device_class_id: device_class(device_path),
    device_id: device(device_path),
    device_vendor_id: device_vendor(device_path),
    subsystem_vendor_id: subsystem_vendor(device_path),
    subsystem_device_id: subsystem_device(device_path)
  }
  new(device_path, opts)
end
device(device_path) click to toggle source

@return [String] - the device number ie. 1002

# File lib/compute_unit/device.rb, line 246
def self.device(device_path)
  read_kernel_setting(device_path, 'device', '').slice(2, 6)
end
device_class(device_path) click to toggle source

@return [String] - the class number ie. 040300 @note helps determine what kind of device this is ie. graphics, storage, …

# File lib/compute_unit/device.rb, line 257
def self.device_class(device_path)
  read_kernel_setting(device_path, 'class', '').slice(2, 8)
end
device_lookup(device_id) click to toggle source

@param [String] - the device id such as '686f' queries the pci_database that is shipped with this gem and looks up the device @return [String] - the device name

# File lib/compute_unit/device.rb, line 382
def self.device_lookup(device_id)
  # "\t687f  Vega 10 XT [Radeon RX Vega 64]\n"
  re = Regexp.new(/\A\t#{device_id}/)
  d = pci_database.find do |line|
    re.match(line)
  rescue ArgumentError
    next
  end
  return d unless d

  name = d[/\t\w+\s+(.*)\n/, 1]
  name[/.*\[(.*)\]/, 1] || name if name
end
device_vendor(device_path) click to toggle source

@return [String] - the vendor number ie. 1002

# File lib/compute_unit/device.rb, line 251
def self.device_vendor(device_path)
  read_kernel_setting(device_path, 'vendor', '').slice(2, 6)
end
find_all(device_class_id = nil) click to toggle source

@return [Array] - an array of pci bus device locations (every device on the pci bus) @param [String] - the device class for filtering out devices @note there is not a filter applied

# File lib/compute_unit/device.rb, line 197
def self.find_all(device_class_id = nil)
  Dir.glob(File.join(ComputeUnit::Device::SYSFS_DEVICES_PATH, '*')).map do |device_path|
    next create_from_path(device_path) unless device_class_id
    next create_from_path(device_path) if device_class(device_path) == device_class_id
  end.compact
end
logger() click to toggle source
# File lib/compute_unit/device.rb, line 396
def self.logger
  ComputeUnit::Logger.logger
end
manual_device_database() click to toggle source
# File lib/compute_unit/device.rb, line 315
def self.manual_device_database
  @manual_device_database ||= {
    '687f_0b36_1002' => 'Radeon RX Vega 64'
  }
end
manual_device_lookup(device_id, subsystem_device_id, vendor_id) click to toggle source
# File lib/compute_unit/device.rb, line 321
def self.manual_device_lookup(device_id, subsystem_device_id, vendor_id)
  key = "#{device_id}_#{subsystem_device_id}_#{vendor_id}"
  manual_device_database.fetch(key, nil)
end
manual_vendor_lookup(id) click to toggle source

@return [String] - return the string of the device vendor @param id [String] - the vendor id

# File lib/compute_unit/device.rb, line 360
def self.manual_vendor_lookup(id)
  manual_vendors[id]
end
manual_vendors() click to toggle source

@return [Hash] - a hash of vendors names and ids

# File lib/compute_unit/device.rb, line 354
def self.manual_vendors
  @manual_vendors ||= { '196e' => 'PNY' }
end
name_map() click to toggle source

@return [Hash] - Name translation map sometimes we just wamnt a shorter name to see

# File lib/compute_unit/device.rb, line 211
def self.name_map
  @name_map ||= {
    'ASUSTeK COMPUTER INC.' => 'Asus',
    'ASUSTeK Computer Inc.' => 'Asus',
    'Advanced Micro Devices, Inc. [AMD/ATI]' => 'AMD',
    'XFX Pine Group Inc.' => 'XFX',
    'NVIDIA Corporation' => 'Nvidia',
    'Gigabyte Technology Co., Ltd' => 'Gigabyte',
    'Sapphire Technology Limited' => 'Sapphire',
    'eVga.com. Corp.' => 'Evga',
    'Micro-Star International Co., Ltd. [MSI]' => 'MSI',
    'Micro-Star International Co., Ltd.' => 'MSI',
    'Intel Corporation' => 'Intel',
    'ASMedia Technology Inc.' => 'AsMedia Tech',
    'Advanced Micro Devices, Inc. [AMD]' => 'AMD',
    'Tul Corporation / PowerColor' => 'PowerColor',
    'PC Partner Limited / Sapphire Technology' => 'Sapphire',
    'Realtek Semiconductor Co., Ltd.' => 'Realtek',
    'Samsung Electronics Co Ltd' => 'Samsung',
    'ZOTAC International (MCO) Ltd.' => 'Zotac'
  }
end
name_translation(name) click to toggle source

@return [String] - translation of vendor names to how we known them

# File lib/compute_unit/device.rb, line 205
def self.name_translation(name)
  name_map[name] || name
end
new(device_path, opts = {}) click to toggle source
# File lib/compute_unit/device.rb, line 26
def initialize(device_path, opts = {})
  @device_path = device_path
  @device_class_id = opts[:device_class_id]
  @device_id = opts[:device_id]
  @device_vendor_id = opts[:device_vendor_id]
  @subsystem_vendor_id = opts[:subsystem_vendor_id]
  @subsystem_device_id = opts[:subsystem_device_id]
end
pci_database() click to toggle source

Syntax: vendor vendor_name

device  device_name                             <-- single tab
        subvendor subdevice  subsystem_name     <-- two tabs

@return [Array] - array of lines of the pci database

# File lib/compute_unit/device.rb, line 309
def self.pci_database
  @pci_database ||= begin
    IO.foreach(ComputeUnit::PCI_DATABASE_PATH).lazy
  end
end
read_kernel_setting(device_path, setting, default = 0) click to toggle source

@return [String] - read a kernel setting using the device_path @param [String] - the device_path to read from @param [String] - the name of the kernel file in the device path to read @param [Object] - a default value to use if there is a problem reading the setting

# File lib/compute_unit/device.rb, line 265
def self.read_kernel_setting(device_path, setting, default = 0)
  path = File.join(device_path, setting)
  logger.debug("reading kernel file #{path}")
  value = begin
    File.read(path).chomp
          rescue Errno::EINVAL, Errno::EPERM
            logger.fatal(e.message)
            default
          rescue Errno::ENOENT
            logger.debug("File #{path} does not exist")
            default
          rescue Errno::EACCES
            logger.fatal('Run this command as root or with sudo')
            default
  end
end
subsystem_device(device_path) click to toggle source

@return [String] - the device number ie. 1002 (can be different from vendor)

# File lib/compute_unit/device.rb, line 235
def self.subsystem_device(device_path)
  read_kernel_setting(device_path, 'subsystem_device', '').slice(2, 6)
end
subsystem_device_lookup(device_id, subsystem_device_id, vendor_id) click to toggle source

@return [String] - the name of the device @param device_id [String] - the device id of the device @param subsystem_device_id [String] - the subsystem_device_id of the device @param vendor_id [String] - the subsystem vendor id

# File lib/compute_unit/device.rb, line 330
def self.subsystem_device_lookup(device_id, subsystem_device_id, vendor_id)
  # "\t\t1002 687f  Vega 10 XT [Radeon RX Vega 64]\n"
  p = manual_device_lookup(device_id, subsystem_device_id, vendor_id)
  return p if p

  re = Regexp.new(/\A\t\t#{vendor_id}\s+#{subsystem_device_id}/)
  d = pci_database.find do |line|
    re.match(line)
  rescue ArgumentError
    next
  end
  return d unless d

  name = d[/\t\t\w+\s+\w+\s+(.*)\n/, 1]
  name[/.*\[(.*)\]/, 1] || name if name
end
subsystem_vendor(device_path) click to toggle source

@return [String] - the vendor number ie. 1002 (can be different from vendor) example: XFX is the reseller of the AMD chips

# File lib/compute_unit/device.rb, line 241
def self.subsystem_vendor(device_path)
  read_kernel_setting(device_path, 'subsystem_vendor', '').slice(2, 6)
end
subsystem_vendor_lookup(vendor_id) click to toggle source

@return [String] - the name of the subsystem vendor with a possible translation

# File lib/compute_unit/device.rb, line 348
def self.subsystem_vendor_lookup(vendor_id)
  # "1002  Advanced Micro Devices, Inc. [AMD/ATI]\n"
  vendor_lookup(vendor_id)
end
system_checksum() click to toggle source

@return [String] - a sha1 digest that represents the pci devices found on the system

# File lib/compute_unit/device.rb, line 298
def self.system_checksum
  @system_checksum ||= Dir.chdir(ComputeUnit::SYS_DEVICE_PATH) do
    Digest::SHA1.hexdigest(Dir.glob('*').sort.join)
  end
end
vendor_lookup(vendor_id) click to toggle source

@return [String] - the name of the subsystem vendor with a possible translation

# File lib/compute_unit/device.rb, line 365
def self.vendor_lookup(vendor_id)
  # "1002  Advanced Micro Devices, Inc. [AMD/ATI]\n"
  re = Regexp.new(/\A#{vendor_id}/)
  d = pci_database.find do |line|
    re.match(line)
  rescue ArgumentError
    next
  end
  return manual_vendor_lookup(vendor_id) unless d

  name = d[/\w+\s+(.*)\n/, 1]
  name_translation(name)
end
write_kernel_setting(device_path, setting, value) click to toggle source

@return [String] - read a kernel setting using the device_path @param [String] - the device_path to write to @param [String] - the name of the kernel file in the device path to read

# File lib/compute_unit/device.rb, line 285
def self.write_kernel_setting(device_path, setting, value)
  path = File.join(device_path, setting)
  File.write(path, value)
  read_kernel_setting(device_path, setting)
rescue Errno::EINVAL, Errno::EPERM => e
  logger.fatal(e.message)
rescue Errno::ENOENT
  logger.warn("File #{path} does not exist")
rescue Errno::EACCES
  logger.fatal('Run this command as root or with sudo')
end

Public Instance Methods

base_hwmon_path() click to toggle source

@return [String] - base hwmon path of the device @note this is used mainly for easier mocking

# File lib/compute_unit/device.rb, line 159
def base_hwmon_path
  File.join(device_path, 'hwmon')
end
expired_metadata?() click to toggle source
# File lib/compute_unit/device.rb, line 173
def expired_metadata?
  timestamp + CACHE_TIMEOUT < Time.now.to_i
end
generic_model() click to toggle source

@return [String] - the name of the device model (sometimes not specific)

# File lib/compute_unit/device.rb, line 121
def generic_model
  @generic_model ||= begin
    name = self.class.device_lookup(device_id)
    name[/\[?(.*)\]?/, 1] if name
  end
end
hwmon_path() click to toggle source

@return [String] - the directory path to the hwmon dir for this device @note this path can be different for each device @note returns the base_hwmon_path if no path is found

# File lib/compute_unit/device.rb, line 166
def hwmon_path
  @hwmon_path ||= begin
    paths = Dir.glob(File.join(base_hwmon_path, '*'))
    paths.first || base_hwmon_path
  end
end
lock_rom() click to toggle source

@return [String] - writes a 0 to the rom file, therby locking it must be root

# File lib/compute_unit/device.rb, line 56
def lock_rom
  File.write(rom_path, '0') if File.exist?(rom_path)
end
read_file(path, default = nil) click to toggle source
# File lib/compute_unit/device.rb, line 128
def read_file(path, default = nil)
  File.read(path).chomp
rescue Errno::EINVAL, Errno::EPERM
  default
rescue Errno::ENOENT
  logger.debug("File #{path} does not exist, using defaults")
  default
rescue Errno::EACCES
  logger.fatal('run this command as root or with sudo, using default value')
  default
end
read_hwmon_data(item, default = nil) click to toggle source

@param item [String] - the name of the hwmon file to read from @param default [Object] - the default value to return if the file is empty or not readable @return [String] - the value of the item looked up

# File lib/compute_unit/device.rb, line 143
def read_hwmon_data(item, default = nil)
  path = File.join(hwmon_path, item)
  read_file(path, default)
end
read_kernel_setting(setting, default = 0) click to toggle source

@return [String] - read a kernel setting using the device_path @param [String] - the name of the kernel file in the device path to read @param [Object] - a default value to use if there is a problem reading the setting

# File lib/compute_unit/device.rb, line 75
def read_kernel_setting(setting, default = 0)
  self.class.read_kernel_setting(device_path, setting, default)
end
rom_data() click to toggle source

@return [String::IO] - the contents of the rom file

# File lib/compute_unit/device.rb, line 61
def rom_data
  return unless File.exist?(rom_path)

  begin
    unlock_rom
    IO.read(rom_path, mode: 'rb')
  ensure
    lock_rom
  end
end
rom_path() click to toggle source

@return [String] - the path to the rom file if available

# File lib/compute_unit/device.rb, line 44
def rom_path
  @rom_path ||= File.join(device_path, 'rom')
end
sysfs_model_name() click to toggle source

@return [String] - the name of the device model (specific name)

# File lib/compute_unit/device.rb, line 107
def sysfs_model_name
  name = self.class.subsystem_device_lookup(device_id, subsystem_device_id, subsystem_vendor_id)
  m = name[/\[?(.*)\]?/, 1] if name
  m || generic_model
end
to_h() click to toggle source
# File lib/compute_unit/device.rb, line 35
def to_h
  { chip_make: make, make: vendor, model: model,
    device_id: device_id, vendor_id: device_vendor_id,
    subsystem_device_id: subsystem_device_id,
    subsystem_vendor_id: subsystem_vendor_id,
    device_class: device_class_id }
end
to_json(c = nil) click to toggle source
# File lib/compute_unit/device.rb, line 177
def to_json(c = nil)
  to_h.to_json(c)
end
unlock_rom() click to toggle source

@return [String] - writes a 1 to the rom file, therby unlocking it for reading must be root

# File lib/compute_unit/device.rb, line 50
def unlock_rom
  File.write(rom_path, '1') if File.exist?(rom_path)
end
write_hwmon_data(item, value) click to toggle source

@param item [String] - the name of the hwmon file to write to @param value [String] - the value you want to write to the hwmon file

# File lib/compute_unit/device.rb, line 150
def write_hwmon_data(item, value)
  File.write(File.join(hwmon_path, item), value)
rescue Errno::EACCES => e
  logger.info(e.message)
  check_for_root
end
write_kernel_setting(setting, value) click to toggle source

@return [String] - a reading of the kernel setting using the device_path @param value [String] - the value to assign to the setting @param setting [String] - the name of the kernel file in the device path to read

# File lib/compute_unit/device.rb, line 82
def write_kernel_setting(setting, value)
  self.class.write_kernel_setting(device_path, setting, value)
end