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:
parent
c1f4221cd7
commit
b9bd0e6dbc
|
@ -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)
|
|
@ -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']
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
Loading…
Reference in New Issue