Skip to content

Commit 17c2c71

Browse files
authored
Merge pull request #1464 from jluebbe/lcus-hid
add support for the LCTech LCUS USB
2 parents 16db15a + a9d2d1c commit 17c2c71

File tree

3 files changed

+55
-16
lines changed

3 files changed

+55
-16
lines changed

doc/configuration.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ HIDRelay
480480
++++++++
481481
An :any:`HIDRelay` resource describes a single output of an HID protocol based
482482
USB relays.
483-
It currently supports the widely used *dcttech USBRelay*.
483+
It currently supports the widely used *dcttech USBRelay* and *lctech LCUS*
484484

485485
.. code-block:: yaml
486486

labgrid/resource/udev.py

+9-4
Original file line numberDiff line numberDiff line change
@@ -671,10 +671,15 @@ class HIDRelay(USBResource):
671671
index = attr.ib(default=1, validator=attr.validators.instance_of(int))
672672
invert = attr.ib(default=False, validator=attr.validators.instance_of(bool))
673673

674-
def __attrs_post_init__(self):
675-
self.match['ID_VENDOR_ID'] = '16c0'
676-
self.match['ID_MODEL_ID'] = '05df'
677-
super().__attrs_post_init__()
674+
def filter_match(self, device):
675+
match = (device.properties.get('ID_VENDOR_ID'), device.properties.get('ID_MODEL_ID'))
676+
677+
if match not in [("16c0", "05df"), # dcttech USBRelay2
678+
("5131", "2007"), # LC-US8
679+
]:
680+
return False
681+
682+
return super().filter_match(device)
678683

679684
@target_factory.reg_resource
680685
@attr.s(eq=False)

labgrid/util/agents/usb_hid_relay.py

+45-11
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
1111
- Turn digital output on and off
1212
"""
13+
1314
import usb.core
1415
import usb.util
1516

@@ -23,46 +24,79 @@ def __init__(self, **args):
2324
self._dev = usb.core.find(**args)
2425
if self._dev is None:
2526
raise ValueError("Device not found")
27+
28+
if self._dev.idVendor == 0x16C0:
29+
self.set_output = self.set_output_dcttech
30+
self.get_output = self.get_output_dcttech
31+
elif self._dev.idVendor == 0x5131:
32+
self.set_output = self.set_output_lcus
33+
self.get_output = self.get_output_lcus
34+
else:
35+
raise ValueError(f"Unknown vendor/protocol for VID {self._dev.idVendor:x}")
36+
2637
if self._dev.is_kernel_driver_active(0):
2738
self._dev.detach_kernel_driver(0)
2839

29-
def set_output(self, number, status):
40+
def set_output_dcttech(self, number, status):
3041
assert 1 <= number <= 8
3142
req = [0xFF if status else 0xFD, number]
3243
self._dev.ctrl_transfer(
3344
usb.util.CTRL_TYPE_CLASS | usb.util.CTRL_RECIPIENT_DEVICE | usb.util.ENDPOINT_OUT,
3445
SET_REPORT,
35-
(REPORT_TYPE_FEATURE << 8) | 0, # no report ID
46+
(REPORT_TYPE_FEATURE << 8) | 0, # no report ID
3647
0,
37-
req, # payload
48+
req, # payload
3849
)
3950

40-
def get_output(self, number):
51+
def get_output_dcttech(self, number):
4152
assert 1 <= number <= 8
4253
resp = self._dev.ctrl_transfer(
4354
usb.util.CTRL_TYPE_CLASS | usb.util.CTRL_RECIPIENT_DEVICE | usb.util.ENDPOINT_IN,
4455
GET_REPORT,
45-
(REPORT_TYPE_FEATURE << 8) | 0, # no report ID
56+
(REPORT_TYPE_FEATURE << 8) | 0, # no report ID
4657
0,
47-
8, # size
58+
8, # size
4859
)
49-
return bool(resp[7] & (1 << (number-1)))
60+
return bool(resp[7] & (1 << (number - 1)))
61+
62+
def set_output_lcus(self, number, status):
63+
assert 1 <= number <= 8
64+
ep_in = self._dev[0][(0, 0)][0]
65+
ep_out = self._dev[0][(0, 0)][1]
66+
req = [0xA0, number, 0x01 if status else 0x00, 0x00]
67+
req[3] = sum(req) & 0xFF
68+
ep_out.write(req)
69+
ep_in.read(64)
70+
71+
def get_output_lcus(self, number):
72+
assert 1 <= number <= 8
73+
# we have no information on how to read the current value
74+
return False
5075

5176
def __del__(self):
5277
usb.util.release_interface(self._dev, 0)
5378

5479

80+
_relays = {}
81+
82+
83+
def _get_relay(busnum, devnum):
84+
if (busnum, devnum) not in _relays:
85+
_relays[(busnum, devnum)] = USBHIDRelay(bus=busnum, address=devnum)
86+
return _relays[(busnum, devnum)]
87+
88+
5589
def handle_set(busnum, devnum, number, status):
56-
relay = USBHIDRelay(bus=busnum, address=devnum)
90+
relay = _get_relay(busnum, devnum)
5791
relay.set_output(number, status)
5892

5993

6094
def handle_get(busnum, devnum, number):
61-
relay = USBHIDRelay(bus=busnum, address=devnum)
95+
relay = _get_relay(busnum, devnum)
6296
return relay.get_output(number)
6397

6498

6599
methods = {
66-
'set': handle_set,
67-
'get': handle_get,
100+
"set": handle_set,
101+
"get": handle_get,
68102
}

0 commit comments

Comments
 (0)