diff --git a/boards/posix/native_sim/doc/index.rst b/boards/posix/native_sim/doc/index.rst index 9621ba5d2c6a07..0ec668a6e3e665 100644 --- a/boards/posix/native_sim/doc/index.rst +++ b/boards/posix/native_sim/doc/index.rst @@ -487,10 +487,14 @@ The following peripherals are currently provided with this board: :ref:`its section `. **CAN controller** - It is possible to use a host CAN controller with the native SockerCAN Linux driver. It can be + It is possible to use a host CAN controller with the native SocketCAN Linux driver. It can be enabled with :kconfig:option:`CONFIG_CAN_NATIVE_LINUX` and configured with the device tree binding :dtcompatible:`zephyr,native-linux-can`. + It is possible to specify which CAN interface will be used by the app using the ``--can-if`` + command-line option. This option overrides **every** Linux SocketCAN driver instance to use the specified + interface. + .. _native_ptty_uart: PTTY UART diff --git a/drivers/can/can_native_linux.c b/drivers/can/can_native_linux.c index a2014fca0be796..12d23c87722956 100644 --- a/drivers/can/can_native_linux.c +++ b/drivers/can/can_native_linux.c @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include @@ -46,6 +48,8 @@ struct can_native_linux_config { const char *if_name; }; +static const char *if_name_cmd_opt; + static void dispatch_frame(const struct device *dev, struct can_frame *frame) { struct can_native_linux_data *data = dev->data; @@ -465,13 +469,21 @@ static int can_native_linux_init(const struct device *dev) { const struct can_native_linux_config *cfg = dev->config; struct can_native_linux_data *data = dev->data; + const char *if_name; k_mutex_init(&data->filter_mutex); k_sem_init(&data->tx_idle, 1, 1); - data->dev_fd = linux_socketcan_iface_open(cfg->if_name); + if (if_name_cmd_opt != NULL) { + if_name = if_name_cmd_opt; + } else { + if_name = cfg->if_name; + } + + LOG_DBG("Opening %s", if_name); + data->dev_fd = linux_socketcan_iface_open(if_name); if (data->dev_fd < 0) { - LOG_ERR("Cannot open %s (%d)", cfg->if_name, data->dev_fd); + LOG_ERR("Cannot open %s (%d)", if_name, data->dev_fd); return -ENODEV; } @@ -502,3 +514,22 @@ CAN_DEVICE_DT_INST_DEFINE(inst, can_native_linux_init, NULL, \ &can_native_linux_driver_api); DT_INST_FOREACH_STATUS_OKAY(CAN_NATIVE_LINUX_INIT) + +static void add_native_posix_options(void) +{ + static struct args_struct_t can_native_posix_options[] = { + { + .is_mandatory = false, + .option = "can-if", + .name = "name", + .type = 's', + .dest = (void *)&if_name_cmd_opt, + .descript = "Name of the host CAN interface to use", + }, + ARG_TABLE_ENDMARKER, + }; + + native_add_command_line_opts(can_native_posix_options); +} + +NATIVE_TASK(add_native_posix_options, PRE_BOOT_1, 10); diff --git a/dts/bindings/can/zephyr,native-linux-can.yaml b/dts/bindings/can/zephyr,native-linux-can.yaml index 4175617403b34f..690d7e6d41152f 100644 --- a/dts/bindings/can/zephyr,native-linux-can.yaml +++ b/dts/bindings/can/zephyr,native-linux-can.yaml @@ -11,4 +11,7 @@ properties: host-interface: type: string required: true - description: Linux host interface name (e.g. zcan0, vcan0, can0, ...) + description: | + Linux host interface name (e.g. zcan0, vcan0, can0, ...). + This property can be overridden using the --can-if command-line + option. Note that it applies for every instance of this driver. diff --git a/scripts/pylib/pytest-twister-harness/src/twister_harness/device/binary_adapter.py b/scripts/pylib/pytest-twister-harness/src/twister_harness/device/binary_adapter.py index 228981be0f170b..702b4c4ec7d68c 100755 --- a/scripts/pylib/pytest-twister-harness/src/twister_harness/device/binary_adapter.py +++ b/scripts/pylib/pytest-twister-harness/src/twister_harness/device/binary_adapter.py @@ -118,7 +118,22 @@ class NativeSimulatorAdapter(BinaryAdapterBase): def generate_command(self) -> None: """Set command to run.""" - self.command = [str(self.device_config.build_dir / 'zephyr' / 'zephyr.exe')] + base_command = [str(self.device_config.build_dir / 'zephyr' / 'zephyr.exe')] + cli_flags = self._get_execution_cli_flags() + self.command = base_command + cli_flags + + def _get_execution_cli_flags(self) -> list[str]: + """Add additional flags for execution""" + return [f'--can-if={self._extract_can()}'] + + def _extract_can(self) -> str: + device_properties = self.device_config.properties + for property in device_properties: + key, value = property.split(":") + if key == 'host_can': + return value + + return 'vcan0' class UnitSimulatorAdapter(BinaryAdapterBase): diff --git a/scripts/pylib/twister/twisterlib/harness.py b/scripts/pylib/twister/twisterlib/harness.py index 28804fe6bbf77b..db6c4376688a9a 100644 --- a/scripts/pylib/twister/twisterlib/harness.py +++ b/scripts/pylib/twister/twisterlib/harness.py @@ -15,6 +15,8 @@ import threading import time import shutil +import multiprocessing as mp +import itertools from twisterlib.error import ConfigurationError from twisterlib.environment import ZEPHYR_BASE, PYTEST_PLUGIN_INSTALLED @@ -27,6 +29,21 @@ _WINDOWS = platform.system() == 'Windows' +# this is done globally, so each process later has access to the +# same instance. This can and should be refactored for 'upstream' (if wanted) +vcan_queue = mp.Queue() +for i in itertools.count(): + if not os.path.exists(f"/sys/class/net/vcan{i}"): + break + vcan_queue.put(f"vcan{i}") + +initial_number_of_vcans = vcan_queue.qsize() +if initial_number_of_vcans == 0: + logger.warning("No virtual CAN in form `vcan$i` found! " + "If you execute tests with 'CAN' on native_sim, " + "this may lead to interfering test executions!" + ) + result_re = re.compile(r".*(PASS|FAIL|SKIP) - (test_)?(\S*) in (\d*[.,]?\d*) seconds") class Harness: @@ -297,6 +314,7 @@ def _configure(self, instance: TestInstance, tool_name: str, report_filename: st self.running_dir = instance.build_dir self.source_dir = instance.testsuite.source_dir self.reserved_serial = None + self.reserved_vcan = None self.tool_name = tool_name @@ -323,6 +341,13 @@ def generate_command(self) -> list[str]: ) elif handler.type_str in SUPPORTED_SIMS_IN_PYTEST: command.append(f'--device-type={handler.type_str}') + # if no vcans are created, do not pass anything and use defaults + # better way would be to filter based on test tags, but this information + # is not existing at this point anymore + if initial_number_of_vcans: + self.reserved_vcan = vcan_queue.get() + command.extend(['--device-properties', f'host_can:{self.reserved_vcan}']) + elif handler.type_str == 'build': command.append('--device-type=custom') else: @@ -341,6 +366,8 @@ def _run(self, timeout): finally: if self.reserved_serial: self.instance.handler.make_device_available(self.reserved_serial) + if self.reserved_vcan: + vcan_queue.put(self.reserved_vcan) self._update_test_status() def _generate_parameters_for_hardware(self, handler: Handler):