From 52dc8a8d49ccda0d8d3ccd10ee6044b82357509c Mon Sep 17 00:00:00 2001 From: Peter Johennecken Date: Wed, 19 Nov 2025 15:27:11 +0100 Subject: [PATCH] sdwire3: initial implementation Signed-off-by: Peter Johennecken --- doc/configuration.rst | 40 ++++++++++++++++ labgrid/driver/__init__.py | 1 + labgrid/driver/usbsdwire3driver.py | 77 ++++++++++++++++++++++++++++++ labgrid/driver/usbstoragedriver.py | 2 + labgrid/remote/client.py | 4 +- labgrid/remote/exporter.py | 20 ++++++++ labgrid/resource/__init__.py | 1 + labgrid/resource/remote.py | 12 +++++ labgrid/resource/suggest.py | 2 + labgrid/resource/udev.py | 53 ++++++++++++++++++++ 10 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 labgrid/driver/usbsdwire3driver.py diff --git a/doc/configuration.rst b/doc/configuration.rst index 08429d67f..ca2c5d7cb 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -1103,6 +1103,29 @@ NetworkUSBSDWireDevice A :any:`NetworkUSBSDWireDevice` resource describes a `USBSDWireDevice`_ resource available on a remote computer. +USBSDWire3Device +~~~~~~~~~~~~~~~~ +A :any:`USBSDWire3Device` resource describes a Tizen +`SD Wire3 device `_. + +.. code-block:: yaml + + USBSDWire3Device: + match: + '@ID_PATH': 'pci-0000:00:14.0-usb-0:1.2' + +Arguments: + - match (dict): key and value pairs for a udev match, see `udev Matching`_ + +Used by: + - `USBSDWire3Driver`_ + - `USBStorageDriver`_ + +NetworkUSBSDWire3Device +~~~~~~~~~~~~~~~~~~~~~~~ +A :any:`NetworkUSBSDWire3Device` resource describes a `USBSDWire3Device`_ +resource available on a remote computer. + USBVideo ~~~~~~~~ A :any:`USBVideo` resource describes a USB video camera which is supported by a @@ -3009,6 +3032,23 @@ Arguments: The driver can be used in test cases by calling its ``set_mode()`` method with argument being "dut", "host", "off", or "client". +USBSDWire3Driver +~~~~~~~~~~~~~~~~ +The :any:`USBSDWire3Driver` uses a `USBSDWire3Device`_ resource to control a +USB-SD-Wire3 device via `sdwire `_ +tool. + +Binds to: + mux: + - `USBSDWire3Device`_ + - `NetworkUSBSDWire3Device`_ + +Implements: + - None yet + +Arguments: + - None + USBVideoDriver ~~~~~~~~~~~~~~ The :any:`USBVideoDriver` is used to show a video stream from a remote USB diff --git a/labgrid/driver/__init__.py b/labgrid/driver/__init__.py index edf1ad2b1..ade0f2d27 100644 --- a/labgrid/driver/__init__.py +++ b/labgrid/driver/__init__.py @@ -19,6 +19,7 @@ from .usbloader import MXSUSBDriver, IMXUSBDriver, BDIMXUSBDriver, RKUSBDriver, UUUDriver from .usbsdmuxdriver import USBSDMuxDriver from .usbsdwiredriver import USBSDWireDriver +from .usbsdwire3driver import USBSDWire3Driver from .common import Driver from .qemudriver import QEMUDriver from .modbusdriver import ModbusCoilDriver, WaveShareModbusCoilDriver diff --git a/labgrid/driver/usbsdwire3driver.py b/labgrid/driver/usbsdwire3driver.py new file mode 100644 index 000000000..c2241e3b0 --- /dev/null +++ b/labgrid/driver/usbsdwire3driver.py @@ -0,0 +1,77 @@ +import subprocess +import re + +import attr + +from .common import Driver +from ..factory import target_factory +from ..step import step +from .exception import ExecutionError +from ..util.helper import processwrapper + +@target_factory.reg_driver +@attr.s(eq=False) +class USBSDWire3Driver(Driver): + """The USBSDWire3Driver uses the sdwire tool to control SDWire hardware + + Args: + bindings (dict): driver to use with usbsdmux + """ + bindings = { + "mux": {"USBSDWire3Device", "NetworkUSBSDWire3Device"}, + } + + def __attrs_post_init__(self): + super().__attrs_post_init__() + if self.target.env: + self.tool = self.target.env.config.get_tool('sdwire') + else: + self.tool = 'sdwire' + if self.mux.control_serial is None: + raise ExecutionError("USBSDWire3Driver requires 'control_serial' to be set in the resource") + self.control_serial = self.match_control_serial() + + @Driver.check_active + @step(title='sdmux_set', args=['mode']) + def set_mode(self, mode): + if not mode.lower() in ['dut', 'host']: + raise ExecutionError(f"Setting mode '{mode}' not supported by USBSDWire3Driver") + cmd = self.mux.command_prefix + [ + self.tool, + "switch", + "-s", + self.control_serial, + "dut" if mode.lower() == "dut" else "ts", + ] + processwrapper.check_output(cmd) + + def match_control_serial(self): + cmd = self.mux.command_prefix + [ + self.tool, + "list" + ] + proc = subprocess.run( + cmd, + stdout=subprocess.PIPE, + check=True + ) + output = proc.stdout.strip().decode() + for line in output.splitlines(): + if self.mux.control_serial is not None and line.find(self.mux.control_serial) >= 0: + return line.split()[0] + raise ExecutionError(f"Could not find control serial {self.mux.control_serial} in sdwire list output") + + @Driver.check_active + @step(title='sdmux_get') + def get_mode(self): + cmd = self.mux.command_prefix + [ + self.tool, + "state", + "-s", + self.control_serial, + ] + result = processwrapper.check_output(cmd) + for line in result.decode().splitlines(): + if re.match(self.control_serial, line): + return line.split(" ", maxsplit=1)[1].strip() + raise ExecutionError(f"Could not find control serial {self.mux.control_serial} in sdwire list output") diff --git a/labgrid/driver/usbstoragedriver.py b/labgrid/driver/usbstoragedriver.py index ac16be5b1..0d2382240 100644 --- a/labgrid/driver/usbstoragedriver.py +++ b/labgrid/driver/usbstoragedriver.py @@ -37,6 +37,8 @@ class USBStorageDriver(Driver): "NetworkUSBSDMuxDevice", "USBSDWireDevice", "NetworkUSBSDWireDevice", + "USBSDWire3Device", + "NetworkUSBSDWire3Device", }, } image = attr.ib( diff --git a/labgrid/remote/client.py b/labgrid/remote/client.py index 9b6e602da..4ca121068 100755 --- a/labgrid/remote/client.py +++ b/labgrid/remote/client.py @@ -1206,7 +1206,7 @@ def sd_mux(self): action = self.args.action target = self._get_target(place) name = self.args.name - from ..resource.remote import NetworkUSBSDMuxDevice, NetworkUSBSDWireDevice + from ..resource.remote import NetworkUSBSDMuxDevice, NetworkUSBSDWireDevice, NetworkUSBSDWire3Device drv = None for resource in target.resources: @@ -1216,6 +1216,8 @@ def sd_mux(self): drv = self._get_driver_or_new(target, "USBSDMuxDriver", name=name) elif isinstance(resource, NetworkUSBSDWireDevice): drv = self._get_driver_or_new(target, "USBSDWireDriver", name=name) + elif isinstance(resource, NetworkUSBSDWire3Device): + drv = self._get_driver_or_new(target, "USBSDWire3Driver", name=name) if drv: break diff --git a/labgrid/remote/exporter.py b/labgrid/remote/exporter.py index d3b406503..7219bb133 100755 --- a/labgrid/remote/exporter.py +++ b/labgrid/remote/exporter.py @@ -409,6 +409,25 @@ def _get_params(self): class USBSDWireExport(USBGenericExport): """ResourceExport for USB devices accessed directly from userspace""" + def __attrs_post_init__(self): + super().__attrs_post_init__() + + def _get_params(self): + """Helper function to return parameters""" + return { + "host": self.host, + "busnum": self.local.busnum, + "devnum": self.local.devnum, + "path": self.local.path, + "vendor_id": self.local.vendor_id, + "model_id": self.local.model_id, + } + + +@attr.s(eq=False) +class USBSDWire3Export(USBGenericExport): + """ResourceExport for USB devices accessed directly from userspace""" + def __attrs_post_init__(self): super().__attrs_post_init__() @@ -557,6 +576,7 @@ def __attrs_post_init__(self): exports["SigrokUSBSerialDevice"] = USBSigrokExport exports["USBSDMuxDevice"] = USBSDMuxExport exports["USBSDWireDevice"] = USBSDWireExport +exports["USBSDWire3Device"] = USBSDWire3Export exports["USBDebugger"] = USBGenericExport exports["USBHub"] = USBGenericRemoteExport exports["USBMassStorage"] = USBGenericExport diff --git a/labgrid/resource/__init__.py b/labgrid/resource/__init__.py index 6ec9d5db8..f5d77e711 100644 --- a/labgrid/resource/__init__.py +++ b/labgrid/resource/__init__.py @@ -29,6 +29,7 @@ USBPowerPort, USBSDMuxDevice, USBSDWireDevice, + USBSDWire3Device, USBSerialPort, USBTMC, USBVideo, diff --git a/labgrid/resource/remote.py b/labgrid/resource/remote.py index a29e58ee8..d2a63d5e9 100644 --- a/labgrid/resource/remote.py +++ b/labgrid/resource/remote.py @@ -250,6 +250,18 @@ def __attrs_post_init__(self): self.timeout = 10.0 super().__attrs_post_init__() +@target_factory.reg_resource +@attr.s(eq=False) +class NetworkUSBSDWire3Device(RemoteUSBResource): + """The NetworkUSBSDWire3Device describes a remotely accessible USBSDWire3 device""" + control_serial = attr.ib( + default=None, + validator=attr.validators.optional(attr.validators.instance_of(str)) + ) + def __attrs_post_init__(self): + self.timeout = 10.0 + super().__attrs_post_init__() + @target_factory.reg_resource @attr.s(eq=False) class NetworkSiSPMPowerPort(RemoteUSBResource): diff --git a/labgrid/resource/suggest.py b/labgrid/resource/suggest.py index 707779bf8..0aa93033d 100644 --- a/labgrid/resource/suggest.py +++ b/labgrid/resource/suggest.py @@ -14,6 +14,7 @@ DFUDevice, USBSDMuxDevice, USBSDWireDevice, + USBSDWire3Device, AlteraUSBBlaster, RKUSBLoader, USBNetworkInterface, @@ -48,6 +49,7 @@ def __init__(self, args): self.resources.append(USBMassStorage(**args)) self.resources.append(USBSDMuxDevice(**args)) self.resources.append(USBSDWireDevice(**args)) + self.resources.append(USBSDWire3Device(**args)) self.resources.append(AlteraUSBBlaster(**args)) self.resources.append(RKUSBLoader(**args)) self.resources.append(USBNetworkInterface(**args)) diff --git a/labgrid/resource/udev.py b/labgrid/resource/udev.py index b370245d1..16fd6cd7f 100644 --- a/labgrid/resource/udev.py +++ b/labgrid/resource/udev.py @@ -526,6 +526,59 @@ def update(self): def path(self): return self.disk_path +@target_factory.reg_resource +@attr.s(eq=False) +class USBSDWire3Device(USBResource): + """The USBSDWire3Device describes an attached SDWire3 device, + it is identified via USB using udev + """ + + control_serial = attr.ib( + default=None, + validator=attr.validators.optional(str) + ) + disk_path = attr.ib( + default=None, + validator=attr.validators.optional(str) + ) + + def __attrs_post_init__(self): + self.match['ID_VENDOR_ID'] = '0bda' + self.match['ID_MODEL_ID'] = '0316' + self.match['@ID_VENDOR_ID'] = '1d6b' + self.match['@ID_MODEL_ID'] = '0002' + super().__attrs_post_init__() + + # Overwrite the avail attribute with our internal property + @property + def avail(self): + return bool(self.control_serial) + + # Forbid the USBResource super class to set the avail property + @avail.setter + def avail(self, prop): + pass + + # Overwrite the poll function. Only mark the SDWire3 as available if both + # paths are available. + def poll(self): + super().poll() + if self.device is not None and not self.avail: + for child in self.device.parent.children: + if child.subsystem == 'block' and child.device_type == 'disk': + self.disk_path = child.device_node + self.control_serial = self.device.properties.get('ID_SERIAL_SHORT') + + def update(self): + super().update() + if self.device is None: + self.disk_path = None + self.control_serial = None + + @property + def path(self): + return self.disk_path + @target_factory.reg_resource @attr.s(eq=False) class USBSDMuxDevice(USBResource):