hwmon: identify hidraw devices with a matching hwmon device

For now only HIDRAW is supported: this means that only HIDs are
supported, and hidapi must have been compiled with support for HIDRAW.

Related: #403
This commit is contained in:
Jonas Malaco 2022-02-16 10:10:24 -03:00
parent c1f4221cd7
commit b9bd0e6dbc
6 changed files with 95 additions and 4 deletions

53
liquidctl/driver/hwmon.py Normal file
View File

@ -0,0 +1,53 @@
# uses the psf/black style
import logging
import sys
from pathlib import Path
_LOGGER = logging.getLogger(__name__)
_IS_LINUX = sys.platform == "linux"
class HwmonDevice:
"""Unstable API."""
__slots__ = [
"module",
"path",
]
def __init__(self, module, path):
self.module = module
self.path = path
@property
def name(self):
return self.path.name
@classmethod
def from_hidraw(cls, path):
"""Find the `HwmonDevice` for `path`."""
if not _IS_LINUX:
return None
if not path.startswith(b"/dev/hidraw"):
_LOGGER.debug("cannot search hwmon device for %s: unsupported path", path)
return None
path = path.decode()
name = path[5:]
class_path = Path("/sys/class/hidraw", name)
sys_device = class_path / "device"
hwmon_devices = (sys_device / "hwmon").iterdir()
hwmon_path = next(hwmon_devices)
if next(hwmon_devices, None) is not None:
_LOGGER.debug("cannot pick hwmon device for %s: more than one alternative", path)
return None
module = (sys_device / "driver" / "module").readlink().name
return HwmonDevice(module, hwmon_path)

View File

@ -68,6 +68,7 @@ except ModuleNotFoundError:
import hid
from liquidctl.driver.base import BaseDriver, BaseBus, find_all_subclasses
from liquidctl.driver.hwmon import HwmonDevice
from liquidctl.util import LazyHexRepr
_LOGGER = logging.getLogger(__name__)
@ -196,6 +197,10 @@ class UsbHidDriver(BaseUsbDriver):
assert hidinfo, 'Could not find device in HID bus'
device = HidapiDevice(hid, hidinfo)
super().__init__(device, description, **kwargs)
self._hwmon = HwmonDevice.from_hidraw(device.path)
if self._hwmon:
_LOGGER.debug('HID is bound to %s kernel driver: %s', self._hwmon.module,
self._hwmon.path)
class UsbDriver(BaseUsbDriver):
@ -482,6 +487,10 @@ class HidapiDevice:
for info in infos:
yield cls(api, info)
@property
def path(self):
return self.hidinfo['path']
@property
def vendor_id(self):
return self.hidinfo['vendor_id']

View File

@ -24,13 +24,14 @@ class MockRuntimeStorage(RuntimeStorage):
class MockHidapiDevice:
def __init__(self, vendor_id=None, product_id=None, release_number=None,
serial_number=None, bus=None, address=None):
serial_number=None, bus=None, address=None, path=None):
self.vendor_id = vendor_id
self.product_id = product_id
self.release_number = release_number
self.serial_number = serial_number
self.bus = bus
self.address = address
self.path = path or b'<placeholder path>'
self.port = None
self.open = noop

View File

@ -23,14 +23,14 @@ class _MockPyUsbHandle(usb.core.Device):
def _mock_enumerate(vendor_id=0, product_id=0):
return [
{'vendor_id': vendor_id, 'product_id': product_id, 'serial_number': '987654321'},
{'vendor_id': vendor_id, 'product_id': product_id, 'serial_number': '123456789'}
{'vendor_id': vendor_id, 'product_id': product_id, 'serial_number': '_21', 'path': b'/_21'},
{'vendor_id': vendor_id, 'product_id': product_id, 'serial_number': '_89', 'path': b'/_89'}
]
def test_construct_with_raw_pyusb_handle(monkeypatch):
monkeypatch.setattr(hid, 'enumerate', _mock_enumerate)
pyusb_handle = _MockPyUsbHandle(serial_number='123456789')
pyusb_handle = _MockPyUsbHandle(serial_number='_89')
liquidctl_device = Kraken2(pyusb_handle, 'Some device')
assert liquidctl_device.device.vendor_id == pyusb_handle.idVendor, \
'<driver instance>.device points to incorrect physical device'

View File

@ -16,6 +16,7 @@ class MockCommanderCoreDevice:
self.vendor_id = 0x1b1c
self.product_id = 0x0c1c
self.address = 'addr'
self.path = b'path'
self.release_number = None
self.serial_number = None
self.bus = None

27
tests/test_hwmon.py Normal file
View File

@ -0,0 +1,27 @@
# uses the psf/black style
import pytest
from liquidctl.driver.hwmon import HwmonDevice
@pytest.fixture
def mock_hwmon(tmp_path):
hwmon = tmp_path / "hwmon7"
hwmon.mkdir()
(hwmon / "fan1_input").write_text("1499\n")
return HwmonDevice("mock_module", hwmon)
def test_has_module(mock_hwmon):
assert mock_hwmon.module == "mock_module"
def test_has_path(mock_hwmon):
assert mock_hwmon.path.is_dir()
def test_has_name(mock_hwmon):
assert mock_hwmon.name == "hwmon7"