Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Native SIM Socket virtual can #17

Merged
merged 2 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion boards/posix/native_sim/doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -487,10 +487,14 @@ The following peripherals are currently provided with this board:
:ref:`its section <nsim_per_disp_sdl>`.

**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
Expand Down
35 changes: 33 additions & 2 deletions drivers/can/can_native_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <cmdline.h>
#include <posix_native_task.h>

#include <zephyr/drivers/can.h>
#include <zephyr/kernel.h>
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
5 changes: 4 additions & 1 deletion dts/bindings/can/zephyr,native-linux-can.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
27 changes: 27 additions & 0 deletions scripts/pylib/twister/twisterlib/harness.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand Down Expand Up @@ -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

Expand All @@ -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:
Expand All @@ -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):
Expand Down
Loading