From 200048943d1cb7df68bbc70e2cb091f7034656d8 Mon Sep 17 00:00:00 2001 From: Jan Luebbe Date: Thu, 8 Aug 2024 09:22:02 +0200 Subject: [PATCH 1/5] resource/udev: use filter_match for HIDRelay This makes it easier to add more VID/PID pairs later. Signed-off-by: Jan Luebbe --- labgrid/resource/udev.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/labgrid/resource/udev.py b/labgrid/resource/udev.py index d3903eb95..1bb34f951 100644 --- a/labgrid/resource/udev.py +++ b/labgrid/resource/udev.py @@ -671,10 +671,14 @@ class HIDRelay(USBResource): index = attr.ib(default=1, validator=attr.validators.instance_of(int)) invert = attr.ib(default=False, validator=attr.validators.instance_of(bool)) - def __attrs_post_init__(self): - self.match['ID_VENDOR_ID'] = '16c0' - self.match['ID_MODEL_ID'] = '05df' - super().__attrs_post_init__() + def filter_match(self, device): + match = (device.properties.get('ID_VENDOR_ID'), device.properties.get('ID_MODEL_ID')) + + if match not in [("16c0", "05df"), # dcttech USBRelay2 + ]: + return False + + return super().filter_match(device) @target_factory.reg_resource @attr.s(eq=False) From cb1e88c617fdde7e4cbf0d9c98ee540b2fadd3e5 Mon Sep 17 00:00:00 2001 From: Jan Luebbe Date: Thu, 8 Aug 2024 09:42:54 +0200 Subject: [PATCH 2/5] util/agents/usb_hid_relay: run ruff format Signed-off-by: Jan Luebbe --- labgrid/util/agents/usb_hid_relay.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/labgrid/util/agents/usb_hid_relay.py b/labgrid/util/agents/usb_hid_relay.py index f20645774..ffbe51157 100644 --- a/labgrid/util/agents/usb_hid_relay.py +++ b/labgrid/util/agents/usb_hid_relay.py @@ -10,6 +10,7 @@ - Turn digital output on and off """ + import usb.core import usb.util @@ -32,9 +33,9 @@ def set_output(self, number, status): self._dev.ctrl_transfer( usb.util.CTRL_TYPE_CLASS | usb.util.CTRL_RECIPIENT_DEVICE | usb.util.ENDPOINT_OUT, SET_REPORT, - (REPORT_TYPE_FEATURE << 8) | 0, # no report ID + (REPORT_TYPE_FEATURE << 8) | 0, # no report ID 0, - req, # payload + req, # payload ) def get_output(self, number): @@ -42,11 +43,11 @@ def get_output(self, number): resp = self._dev.ctrl_transfer( usb.util.CTRL_TYPE_CLASS | usb.util.CTRL_RECIPIENT_DEVICE | usb.util.ENDPOINT_IN, GET_REPORT, - (REPORT_TYPE_FEATURE << 8) | 0, # no report ID + (REPORT_TYPE_FEATURE << 8) | 0, # no report ID 0, - 8, # size + 8, # size ) - return bool(resp[7] & (1 << (number-1))) + return bool(resp[7] & (1 << (number - 1))) def __del__(self): usb.util.release_interface(self._dev, 0) @@ -63,6 +64,6 @@ def handle_get(busnum, devnum, number): methods = { - 'set': handle_set, - 'get': handle_get, + "set": handle_set, + "get": handle_get, } From e592a376657e3aa77d41667e627561353de981ea Mon Sep 17 00:00:00 2001 From: Jan Luebbe Date: Thu, 8 Aug 2024 09:35:22 +0200 Subject: [PATCH 3/5] util/agents/usb_hid_relay: use VID to detect the protocol Signed-off-by: Jan Luebbe --- labgrid/util/agents/usb_hid_relay.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/labgrid/util/agents/usb_hid_relay.py b/labgrid/util/agents/usb_hid_relay.py index ffbe51157..b32f28477 100644 --- a/labgrid/util/agents/usb_hid_relay.py +++ b/labgrid/util/agents/usb_hid_relay.py @@ -24,10 +24,17 @@ def __init__(self, **args): self._dev = usb.core.find(**args) if self._dev is None: raise ValueError("Device not found") + + if self._dev.idVendor == 0x16C0: + self.set_output = self.set_output_dcttech + self.get_output = self.get_output_dcttech + else: + raise ValueError(f"Unknown vendor/protocol for VID {self._dev.idVendor:x}") + if self._dev.is_kernel_driver_active(0): self._dev.detach_kernel_driver(0) - def set_output(self, number, status): + def set_output_dcttech(self, number, status): assert 1 <= number <= 8 req = [0xFF if status else 0xFD, number] self._dev.ctrl_transfer( @@ -38,7 +45,7 @@ def set_output(self, number, status): req, # payload ) - def get_output(self, number): + def get_output_dcttech(self, number): assert 1 <= number <= 8 resp = self._dev.ctrl_transfer( usb.util.CTRL_TYPE_CLASS | usb.util.CTRL_RECIPIENT_DEVICE | usb.util.ENDPOINT_IN, From c9fc5bfdb9daf728f6e5302a767875917907651b Mon Sep 17 00:00:00 2001 From: Jan Luebbe Date: Thu, 8 Aug 2024 11:02:02 +0200 Subject: [PATCH 4/5] labgrid/util/agents/usb_hid_relay: keep the USB device open This avoids device busy errors when accessing the device in a loop. Signed-off-by: Jan Luebbe --- labgrid/util/agents/usb_hid_relay.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/labgrid/util/agents/usb_hid_relay.py b/labgrid/util/agents/usb_hid_relay.py index b32f28477..5a083e81d 100644 --- a/labgrid/util/agents/usb_hid_relay.py +++ b/labgrid/util/agents/usb_hid_relay.py @@ -60,13 +60,22 @@ def __del__(self): usb.util.release_interface(self._dev, 0) +_relays = {} + + +def _get_relay(busnum, devnum): + if (busnum, devnum) not in _relays: + _relays[(busnum, devnum)] = USBHIDRelay(bus=busnum, address=devnum) + return _relays[(busnum, devnum)] + + def handle_set(busnum, devnum, number, status): - relay = USBHIDRelay(bus=busnum, address=devnum) + relay = _get_relay(busnum, devnum) relay.set_output(number, status) def handle_get(busnum, devnum, number): - relay = USBHIDRelay(bus=busnum, address=devnum) + relay = _get_relay(busnum, devnum) return relay.get_output(number) From a9d2d1c45a37e6f4048638b831b70a1a8ee80d31 Mon Sep 17 00:00:00 2001 From: Jan Luebbe Date: Thu, 8 Aug 2024 11:02:50 +0200 Subject: [PATCH 5/5] util/agents/usb_hid_relay: add support for the LCTech USB HID relay Signed-off-by: Jan Luebbe --- doc/configuration.rst | 2 +- labgrid/resource/udev.py | 1 + labgrid/util/agents/usb_hid_relay.py | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/doc/configuration.rst b/doc/configuration.rst index 3697dc744..f70bf2b66 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -480,7 +480,7 @@ HIDRelay ++++++++ An :any:`HIDRelay` resource describes a single output of an HID protocol based USB relays. -It currently supports the widely used *dcttech USBRelay*. +It currently supports the widely used *dcttech USBRelay* and *lctech LCUS* .. code-block:: yaml diff --git a/labgrid/resource/udev.py b/labgrid/resource/udev.py index 1bb34f951..54c6a9fd3 100644 --- a/labgrid/resource/udev.py +++ b/labgrid/resource/udev.py @@ -675,6 +675,7 @@ def filter_match(self, device): match = (device.properties.get('ID_VENDOR_ID'), device.properties.get('ID_MODEL_ID')) if match not in [("16c0", "05df"), # dcttech USBRelay2 + ("5131", "2007"), # LC-US8 ]: return False diff --git a/labgrid/util/agents/usb_hid_relay.py b/labgrid/util/agents/usb_hid_relay.py index 5a083e81d..9cbcaf2c5 100644 --- a/labgrid/util/agents/usb_hid_relay.py +++ b/labgrid/util/agents/usb_hid_relay.py @@ -28,6 +28,9 @@ def __init__(self, **args): if self._dev.idVendor == 0x16C0: self.set_output = self.set_output_dcttech self.get_output = self.get_output_dcttech + elif self._dev.idVendor == 0x5131: + self.set_output = self.set_output_lcus + self.get_output = self.get_output_lcus else: raise ValueError(f"Unknown vendor/protocol for VID {self._dev.idVendor:x}") @@ -56,6 +59,20 @@ def get_output_dcttech(self, number): ) return bool(resp[7] & (1 << (number - 1))) + def set_output_lcus(self, number, status): + assert 1 <= number <= 8 + ep_in = self._dev[0][(0, 0)][0] + ep_out = self._dev[0][(0, 0)][1] + req = [0xA0, number, 0x01 if status else 0x00, 0x00] + req[3] = sum(req) & 0xFF + ep_out.write(req) + ep_in.read(64) + + def get_output_lcus(self, number): + assert 1 <= number <= 8 + # we have no information on how to read the current value + return False + def __del__(self): usb.util.release_interface(self._dev, 0)