From e93bc66d1acf344afed1ccf8a6148d170a8716c5 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Thu, 28 Feb 2019 16:54:13 +0200 Subject: [PATCH 1/2] drivers: can: Add support for native_posix board This initial version supports SocketCAN. The driver passes CANBUS data between Linux vcan virtual CAN driver and Zephyr. You also need to use can-setup.sh script from the net-tools project in order to make communication work with Linux vcan driver. Signed-off-by: Jukka Rissanen --- drivers/can/CMakeLists.txt | 14 +- drivers/can/Kconfig | 1 + drivers/can/Kconfig.native_posix | 28 +++ drivers/can/canbus_native_posix.c | 313 ++++++++++++++++++++++++ drivers/can/canbus_native_posix_adapt.c | 179 ++++++++++++++ drivers/can/canbus_native_posix_priv.h | 24 ++ 6 files changed, 558 insertions(+), 1 deletion(-) create mode 100644 drivers/can/Kconfig.native_posix create mode 100644 drivers/can/canbus_native_posix.c create mode 100644 drivers/can/canbus_native_posix_adapt.c create mode 100644 drivers/can/canbus_native_posix_priv.h diff --git a/drivers/can/CMakeLists.txt b/drivers/can/CMakeLists.txt index 7f598a0e2729e..d4632a3ce5b2e 100644 --- a/drivers/can/CMakeLists.txt +++ b/drivers/can/CMakeLists.txt @@ -1,5 +1,17 @@ # SPDX-License-Identifier: Apache-2.0 -zephyr_sources_ifdef(CONFIG_CAN_STM32 stm32_can.c) +zephyr_sources_ifdef(CONFIG_CAN_STM32 stm32_can.c) zephyr_sources_ifdef(CONFIG_CAN_MCP2515 mcp2515.c) zephyr_sources_ifdef(CONFIG_USERSPACE can_handlers.c) + +if(CONFIG_CAN_NATIVE_POSIX) + zephyr_library() + zephyr_library_include_directories(${ZEPHYR_BASE}/subsys/net/l2) + zephyr_library_compile_definitions(NO_POSIX_CHEATS) + zephyr_library_compile_definitions(_BSD_SOURCE) + zephyr_library_compile_definitions(_DEFAULT_SOURCE) + zephyr_library_sources( + canbus_native_posix.c + canbus_native_posix_adapt.c + ) +endif() diff --git a/drivers/can/Kconfig b/drivers/can/Kconfig index 73155db78511d..a2e757c155290 100644 --- a/drivers/can/Kconfig +++ b/drivers/can/Kconfig @@ -37,5 +37,6 @@ config CAN_1 source "drivers/can/Kconfig.stm32" source "drivers/can/Kconfig.mcp2515" +source "drivers/can/Kconfig.native_posix" endif # CAN diff --git a/drivers/can/Kconfig.native_posix b/drivers/can/Kconfig.native_posix new file mode 100644 index 0000000000000..1efede3a36a2c --- /dev/null +++ b/drivers/can/Kconfig.native_posix @@ -0,0 +1,28 @@ +# Kconfig.native_posix - native_posix CAN configuration options + +# +# Copyright (c) 2019 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +config CAN_NATIVE_POSIX + bool "native_posix CAN Driver" + depends on ARCH_POSIX + help + Enable native_posix CAN driver + +config CAN_MAX_FILTER + int "Maximum number of concurrent active filters" + depends on CAN_NATIVE_POSIX + default 32 + range 1 56 + help + Defines the array size of the callback/msgq pointers. + Must be at least the size of concurrent reads. + +config CAN_NATIVE_POSIX_INTERFACE_NAME + string "CANBUS interface name in Linux side" + default "zcan" + help + This option sets the CANBUS network interface name in host system. diff --git a/drivers/can/canbus_native_posix.c b/drivers/can/canbus_native_posix.c new file mode 100644 index 0000000000000..81bdaa9fc2658 --- /dev/null +++ b/drivers/can/canbus_native_posix.c @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * + * CANBUS driver for native_posix board. This is meant to test CANBUS + * connectivity between host and Zephyr. + */ + +#define LOG_MODULE_NAME canbus_posix +#define LOG_LEVEL CONFIG_CAN_LOG_LEVEL + +#include +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "canbus_native_posix_priv.h" + +#define NET_BUF_TIMEOUT K_MSEC(100) +#define DT_CAN_1_NAME "CAN_1" + +struct canbus_np_context { + u8_t recv[CAN_MTU]; + + struct device *can_dev; + struct k_msgq *msgq; + struct net_if *iface; + const char *if_name; + int dev_fd; + bool init_done; +}; + +NET_STACK_DEFINE(RX_ZCAN, canbus_rx_stack, + CONFIG_ARCH_POSIX_RECOMMENDED_STACK_SIZE, + CONFIG_ARCH_POSIX_RECOMMENDED_STACK_SIZE); +static struct k_thread rx_thread_data; + +/* TODO: support multiple interfaces */ +static struct canbus_np_context canbus_context_data; + +static int read_data(struct canbus_np_context *ctx, int fd) +{ + struct net_pkt *pkt; + int count; + + count = canbus_np_read_data(fd, ctx->recv, sizeof(ctx->recv)); + if (count <= 0) { + return 0; + } + + pkt = net_pkt_rx_alloc_with_buffer(ctx->iface, count, + AF_CAN, 0, NET_BUF_TIMEOUT); + if (!pkt) { + return -ENOMEM; + } + + if (net_pkt_write_new(pkt, ctx->recv, count)) { + net_pkt_unref(pkt); + return -ENOBUFS; + } + + if (net_recv_data(ctx->iface, pkt) < 0) { + net_pkt_unref(pkt); + } + + return 0; +} + +static void canbus_np_rx(struct canbus_np_context *ctx) +{ + int ret; + + LOG_DBG("Starting ZCAN RX thread"); + + while (1) { + if (ctx->iface && net_if_is_up(ctx->iface)) { + ret = canbus_np_wait_data(ctx->dev_fd); + if (!ret) { + read_data(ctx, ctx->dev_fd); + } + } + + k_sleep(K_MSEC(50)); + } +} + +static void create_rx_handler(struct canbus_np_context *ctx) +{ + k_thread_create(&rx_thread_data, canbus_rx_stack, + K_THREAD_STACK_SIZEOF(canbus_rx_stack), + (k_thread_entry_t)canbus_np_rx, + ctx, NULL, NULL, K_PRIO_COOP(14), + 0, K_NO_WAIT); +} + +static int canbus_np_init(struct device *dev) +{ + struct canbus_np_context *ctx = dev->driver_data; + + ctx->if_name = CONFIG_CAN_NATIVE_POSIX_INTERFACE_NAME; + + ctx->dev_fd = canbus_np_iface_open(ctx->if_name); + if (ctx->dev_fd < 0) { + LOG_ERR("Cannot open %s (%d)", ctx->if_name, ctx->dev_fd); + } else { + /* Create a thread that will handle incoming data from host */ + create_rx_handler(ctx); + } + + return 0; +} + +static int canbus_np_runtime_configure(struct device *dev, enum can_mode mode, + u32_t bitrate) +{ + ARG_UNUSED(dev); + ARG_UNUSED(mode); + ARG_UNUSED(bitrate); + + return 0; +} + +static int canbus_np_send(struct device *dev, const struct zcan_frame *msg, + s32_t timeout, can_tx_callback_t callback) +{ + struct canbus_np_context *ctx = dev->driver_data; + int ret = -ENODEV; + + ARG_UNUSED(timeout); + ARG_UNUSED(callback); + + if (ctx->dev_fd > 0) { + struct can_frame frame; + + can_copy_zframe_to_frame(msg, &frame); + + ret = canbus_np_write_data(ctx->dev_fd, &frame, sizeof(frame)); + if (ret < 0) { + LOG_ERR("Cannot send CAN data len %d (%d)", + frame.can_dlc, -errno); + } + } + + return ret < 0 ? ret : 0; +} + +static int canbus_np_attach_msgq(struct device *dev, struct k_msgq *msgq, + const struct zcan_filter *filter) +{ + ARG_UNUSED(dev); + ARG_UNUSED(msgq); + ARG_UNUSED(filter); + + return 0; +} + +static int canbus_np_attach_isr(struct device *dev, can_rx_callback_t isr, + const struct zcan_filter *filter) +{ + ARG_UNUSED(dev); + ARG_UNUSED(isr); + ARG_UNUSED(filter); + + return 0; +} + +static void canbus_np_detach(struct device *dev, int filter_nr) +{ + ARG_UNUSED(dev); + ARG_UNUSED(filter_nr); +} + +static const struct can_driver_api can_api_funcs = { + .configure = canbus_np_runtime_configure, + .send = canbus_np_send, + .attach_msgq = canbus_np_attach_msgq, + .attach_isr = canbus_np_attach_isr, + .detach = canbus_np_detach, +}; + +#ifdef CONFIG_CAN_1 + +DEVICE_AND_API_INIT(canbus_np_1, DT_CAN_1_NAME, + canbus_np_init, &canbus_context_data, NULL, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &can_api_funcs); + +#if defined(CONFIG_NET_SOCKETS_CAN) + +#define SOCKET_CAN_NAME_1 "SOCKET_CAN_1" +#define SEND_TIMEOUT K_MSEC(100) +#define BUF_ALLOC_TIMEOUT K_MSEC(50) + +/* TODO: make msgq size configurable */ +CAN_DEFINE_MSGQ(socket_can_msgq, 5); + +static void socket_can_iface_init(struct net_if *iface) +{ + struct device *dev = net_if_get_device(iface); + struct canbus_np_context *socket_context = dev->driver_data; + + socket_context->iface = iface; + + LOG_DBG("Init CAN interface %p dev %p", iface, dev); +} + +static void tx_irq_callback(u32_t error_flags) +{ + if (error_flags) { + LOG_DBG("Callback! error-code: %d", error_flags); + } +} + +/* This is called by net_if.c when packet is about to be sent */ +static int socket_can_send(struct device *dev, struct net_pkt *pkt) +{ + struct canbus_np_context *socket_context = dev->driver_data; + int ret; + + if (net_pkt_family(pkt) != AF_CAN) { + return -EPFNOSUPPORT; + } + + ret = can_send(socket_context->can_dev, + (struct zcan_frame *)pkt->frags->data, + SEND_TIMEOUT, tx_irq_callback); + if (ret) { + LOG_DBG("Cannot send socket CAN msg (%d)", ret); + } + + /* If something went wrong, then we need to return negative value to + * net_if.c:net_if_tx() so that the net_pkt will get released. + */ + return -ret; +} + +static int socket_can_setsockopt(struct device *dev, void *obj, + int level, int optname, + const void *optval, socklen_t optlen) +{ + struct canbus_np_context *socket_context = dev->driver_data; + struct can_filter filter; + + if (level != SOL_CAN_RAW && optname != CAN_RAW_FILTER) { + errno = EINVAL; + return -1; + } + + /* Our userspace can send either zcan_filter or can_filter struct. + * They are different sizes so we need to convert them if needed. + */ + if (optlen != sizeof(struct can_filter) && + optlen != sizeof(struct zcan_filter)) { + errno = EINVAL; + return -1; + } + + if (optlen == sizeof(struct zcan_filter)) { + can_copy_zfilter_to_filter((struct zcan_filter *)optval, + &filter); + } else { + memcpy(&filter, optval, sizeof(filter)); + } + + return canbus_np_setsockopt(socket_context->dev_fd, level, optname, + &filter, sizeof(filter)); +} + +static struct canbus_api socket_can_api = { + .iface_api.init = socket_can_iface_init, + .send = socket_can_send, + .setsockopt = socket_can_setsockopt, +}; + +static int socket_can_init_1(struct device *dev) +{ + struct device *can_dev = DEVICE_GET(canbus_np_1); + struct canbus_np_context *socket_context = dev->driver_data; + + LOG_DBG("Init socket CAN device %p (%s) for dev %p (%s)", + dev, dev->config->name, can_dev, can_dev->config->name); + + socket_context->can_dev = can_dev; + socket_context->msgq = &socket_can_msgq; + + return 0; +} + +NET_DEVICE_INIT(socket_can_native_posix_1, SOCKET_CAN_NAME_1, + socket_can_init_1, &canbus_context_data, NULL, + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &socket_can_api, + CANBUS_L2, NET_L2_GET_CTX_TYPE(CANBUS_L2), CAN_MTU); + +#endif /* CONFIG_NET_SOCKETS_CAN */ + +#endif /* CONFIG_CAN_1 */ diff --git a/drivers/can/canbus_native_posix_adapt.c b/drivers/can/canbus_native_posix_adapt.c new file mode 100644 index 0000000000000..3d06b30e0ba63 --- /dev/null +++ b/drivers/can/canbus_native_posix_adapt.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * + * Routines setting up the host system. Those are placed in separate file + * because there is naming conflicts between host and zephyr network stacks. + */ + +/* Host include files */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "posix_trace.h" + +#ifdef __linux +#include +#endif + +/* Zephyr include files. Be very careful here and only include minimum + * things needed. + */ +#define LOG_MODULE_NAME canbus_posix_adapt +#define LOG_LEVEL CONFIG_CAN_LOG_LEVEL + +#include +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +#include + +#include "canbus_native_posix_priv.h" + +int canbus_np_iface_open(const char *if_name) +{ + struct sockaddr_can addr; + struct ifreq ifr; + int fd, ret = -EINVAL; + + fd = socket(PF_CAN, SOCK_RAW, CAN_RAW); + if (fd < 0) { + return -errno; + } + + (void)memset(&ifr, 0, sizeof(ifr)); + (void)memset(&addr, 0, sizeof(addr)); + +#ifdef __linux + strncpy(ifr.ifr_name, if_name, IFNAMSIZ); + + ret = ioctl(fd, SIOCGIFINDEX, (void *)&ifr); + if (ret < 0) { + ret = -errno; + close(fd); + return ret; + } + + /* Setup address for bind */ + addr.can_ifindex = ifr.ifr_ifindex; + addr.can_family = PF_CAN; + + /* bind socket to the zcan interface */ + ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); + if (ret < 0) { + ret = -errno; + close(fd); + return ret; + } +#endif + + return fd; +} + +int canbus_np_iface_remove(int fd) +{ + return close(fd); +} + +static int ssystem(const char *fmt, ...) + __attribute__((__format__(__printf__, 1, 2))); + +static int ssystem(const char *fmt, ...) +{ + char cmd[255]; + va_list ap; + int ret; + + va_start(ap, fmt); + vsnprintf(cmd, sizeof(cmd), fmt, ap); + va_end(ap); + + posix_print_trace("%s\n", cmd); + + ret = system(cmd); + + return -WEXITSTATUS(ret); +} + +int canbus_np_wait_data(int fd) +{ + struct timeval timeout; + fd_set rset; + int ret; + + FD_ZERO(&rset); + + FD_SET(fd, &rset); + + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + ret = select(fd + 1, &rset, NULL, NULL, &timeout); + if (ret < 0 && errno != EINTR) { + return -errno; + } else if (ret > 0) { + if (FD_ISSET(fd, &rset)) { + return 0; + } + } + + return -EAGAIN; +} + +ssize_t canbus_np_read_data(int fd, void *buf, size_t buf_len) +{ + return read(fd, buf, buf_len); +} + +ssize_t canbus_np_write_data(int fd, void *buf, size_t buf_len) +{ + return write(fd, buf, buf_len); +} + +int canbus_np_setsockopt(int fd, int level, int optname, + const void *optval, socklen_t optlen) +{ + return setsockopt(fd, level, optname, optval, optlen); +} + +int canbus_np_getsockopt(int fd, int level, int optname, + void *optval, socklen_t *optlen) +{ + return getsockopt(fd, level, optname, optval, optlen); +} + +#if defined(CONFIG_NET_PROMISCUOUS_MODE) +int canbus_np_promisc_mode(const char *if_name, bool enable) +{ + return ssystem("ip link set dev %s promisc %s", + if_name, enable ? "on" : "off"); +} +#endif /* CONFIG_NET_PROMISCUOUS_MODE */ + +/* If we have enabled manual setup, then interface cannot be + * taken up or down by the driver as we normally do not have + * enough permissions. + */ + +int canbus_np_if_up(const char *if_name) +{ + return ssystem("ip link set dev %s up", if_name); +} + +int canbus_np_if_down(const char *if_name) +{ + return ssystem("ip link set dev %s down", if_name); +} diff --git a/drivers/can/canbus_native_posix_priv.h b/drivers/can/canbus_native_posix_priv.h new file mode 100644 index 0000000000000..1a42c22fb5bc1 --- /dev/null +++ b/drivers/can/canbus_native_posix_priv.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** @file + * @brief Private functions for native posix canbus driver. + */ + +#ifndef ZEPHYR_DRIVERS_CAN_NATIVE_POSIX_PRIV_H_ +#define ZEPHYR_DRIVERS_CAN_NATIVE_POSIX_PRIV_H_ + +int canbus_np_iface_open(const char *if_name); +int canbus_np_iface_close(int fd); +int canbus_np_wait_data(int fd); +ssize_t canbus_np_read_data(int fd, void *buf, size_t buf_len); +ssize_t canbus_np_write_data(int fd, void *buf, size_t buf_len); +int canbus_np_setsockopt(int fd, int level, int optname, + const void *optval, socklen_t optlen); +int canbus_np_getsockopt(int fd, int level, int optname, + void *optval, socklen_t *optlen); + +#endif /* ZEPHYR_DRIVERS_CAN_NATIVE_POSIX_PRIV_H_ */ From 1814682cee6bf221eef6d18330c44b4f3bf37be2 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Fri, 8 Mar 2019 16:19:42 +0200 Subject: [PATCH 2/2] samples: net: sockets: can: Add native_posix board support Add native_posix board support to socket-can sample application. Signed-off-by: Jukka Rissanen --- samples/net/sockets/can/boards/native_posix.conf | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 samples/net/sockets/can/boards/native_posix.conf diff --git a/samples/net/sockets/can/boards/native_posix.conf b/samples/net/sockets/can/boards/native_posix.conf new file mode 100644 index 0000000000000..e3f7d0a941002 --- /dev/null +++ b/samples/net/sockets/can/boards/native_posix.conf @@ -0,0 +1,2 @@ +CONFIG_CAN_NATIVE_POSIX=y +CONFIG_ETH_NATIVE_POSIX=n