Skip to content

Commit ec55819

Browse files
Merge pull request #1526 from jluebbe/usb-hid-multiuser
util/agents/usb_hid_relay: fix concurrent access
2 parents 4d8ce28 + 7ab6240 commit ec55819

File tree

1 file changed

+36
-10
lines changed

1 file changed

+36
-10
lines changed

labgrid/util/agents/usb_hid_relay.py

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
- Turn digital output on and off
1212
"""
1313

14+
import errno
15+
from contextlib import contextmanager
16+
from time import monotonic, sleep
17+
1418
import usb.core
1519
import usb.util
1620

@@ -26,18 +30,35 @@ def __init__(self, **args):
2630
raise ValueError("Device not found")
2731

2832
if self._dev.idVendor == 0x16C0:
29-
self.set_output = self.set_output_dcttech
30-
self.get_output = self.get_output_dcttech
33+
self._set_output = self._set_output_dcttech
34+
self._get_output = self._get_output_dcttech
3135
elif self._dev.idVendor == 0x5131:
32-
self.set_output = self.set_output_lcus
33-
self.get_output = self.get_output_lcus
36+
self._set_output = self._set_output_lcus
37+
self._get_output = self._get_output_lcus
3438
else:
3539
raise ValueError(f"Unknown vendor/protocol for VID {self._dev.idVendor:x}")
3640

3741
if self._dev.is_kernel_driver_active(0):
3842
self._dev.detach_kernel_driver(0)
3943

40-
def set_output_dcttech(self, number, status):
44+
@contextmanager
45+
def _claimed(self):
46+
timeout = monotonic() + 1.0
47+
while True:
48+
try:
49+
usb.util.claim_interface(self._dev, 0)
50+
break
51+
except usb.core.USBError as e:
52+
if monotonic() > timeout:
53+
raise e
54+
if e.errno == errno.EBUSY:
55+
sleep(0.01)
56+
else:
57+
raise e
58+
yield
59+
usb.util.release_interface(self._dev, 0)
60+
61+
def _set_output_dcttech(self, number, status):
4162
assert 1 <= number <= 8
4263
req = [0xFF if status else 0xFD, number]
4364
self._dev.ctrl_transfer(
@@ -48,7 +69,7 @@ def set_output_dcttech(self, number, status):
4869
req, # payload
4970
)
5071

51-
def get_output_dcttech(self, number):
72+
def _get_output_dcttech(self, number):
5273
assert 1 <= number <= 8
5374
resp = self._dev.ctrl_transfer(
5475
usb.util.CTRL_TYPE_CLASS | usb.util.CTRL_RECIPIENT_DEVICE | usb.util.ENDPOINT_IN,
@@ -59,7 +80,7 @@ def get_output_dcttech(self, number):
5980
)
6081
return bool(resp[7] & (1 << (number - 1)))
6182

62-
def set_output_lcus(self, number, status):
83+
def _set_output_lcus(self, number, status):
6384
assert 1 <= number <= 8
6485
ep_in = self._dev[0][(0, 0)][0]
6586
ep_out = self._dev[0][(0, 0)][1]
@@ -68,13 +89,18 @@ def set_output_lcus(self, number, status):
6889
ep_out.write(req)
6990
ep_in.read(64)
7091

71-
def get_output_lcus(self, number):
92+
def _get_output_lcus(self, number):
7293
assert 1 <= number <= 8
7394
# we have no information on how to read the current value
7495
return False
7596

76-
def __del__(self):
77-
usb.util.release_interface(self._dev, 0)
97+
def set_output(self, number, status):
98+
with self._claimed():
99+
self._set_output(number, status)
100+
101+
def get_output(self, number):
102+
with self._claimed():
103+
self._get_output(number)
78104

79105

80106
_relays = {}

0 commit comments

Comments
 (0)