1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-17 05:12:57 +01:00

native: add a CAN device using socketCAN

This commit is contained in:
Vincent Dupont 2016-11-23 19:06:59 +01:00
parent 144a0700f5
commit 68f1ea8fd0
10 changed files with 816 additions and 1 deletions

View File

@ -566,6 +566,10 @@ ifneq (,$(filter evtimer,$(USEMODULE)))
USEMODULE += xtimer
endif
ifneq (,$(filter can_linux,$(USEMODULE)))
export LINKFLAGS += -lsocketcan
endif
ifneq (,$(filter can,$(USEMODULE)))
USEMODULE += can_raw
USEMODULE += auto_init_can

View File

@ -5,3 +5,10 @@ endif
ifneq (,$(filter mtd,$(USEMODULE)))
USEMODULE += mtd_native
endif
ifneq (,$(filter can,$(USEMODULE)))
ifeq ($(shell uname -s),Linux)
USEMODULE += can_linux
CFLAGS += -DCAN_DLL_NUMOF=2
endif
endif

View File

@ -10,6 +10,10 @@ ifneq (,$(filter mtd_native,$(USEMODULE)))
DIRS += mtd
endif
ifneq (,$(filter can_linux,$(USEMODULE)))
DIRS += can
endif
include $(RIOTBASE)/Makefile.base
INCLUDES = $(NATIVEINCLUDES)

3
cpu/native/can/Makefile Normal file
View File

@ -0,0 +1,3 @@
MODULE = can_linux
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,558 @@
/*
* Copyright (C) 2016 OTA keys S.A.
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup native_cpu
* @{
*
* @file
* @brief Implementation of simulated CAN controller driver using SocketCAN on Linux
*
* @author Hermann Lelong <hermann@otakeys.com>
* @author Aurelien Gonce <aurelien.gonce@altran.com>
* @author Vincent Dupont <vincent@otakeys.com>
* @}
*/
#if !defined(__linux__)
#error "MODULE can_linux is only available on Linux"
#else
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <linux/can/raw.h>
#include <linux/can/error.h>
#include "native_internal.h"
#include "can/device.h"
#include "candev_linux.h"
#include "thread.h"
#include "mutex.h"
#include "async_read.h"
#include "sched.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static int _init(candev_t *candev);
static int _send(candev_t *candev, const struct can_frame *frame);
static void _isr(candev_t *candev);
static int _set(candev_t *candev, canopt_t opt, void *value, size_t value_len);
static int _get(candev_t *candev, canopt_t opt, void *value, size_t max_len);
static int _abort(candev_t *candev, const struct can_frame *frame);
static int _set_filter(candev_t *candev, const struct can_filter *filter);
static int _remove_filter(candev_t *candev, const struct can_filter *filter);
static int _power_up(candev_t *candev);
static int _power_down(candev_t *candev);
static int _set_bittiming(candev_linux_t *dev, struct can_bittiming *bittiming);
static const candev_driver_t candev_linux_driver = {
.send = _send,
.init = _init,
.isr = _isr,
.get = _get,
.set = _set,
.abort = _abort,
.set_filter = _set_filter,
.remove_filter = _remove_filter,
};
static candev_event_t _can_error_to_can_evt(struct can_frame can_frame_err);
static void _callback_can_sigio(int sock, void *arg);
candev_linux_conf_t candev_linux_conf[CAN_DLL_NUMOF] = {
#if CAN_DLL_NUMOF >= 1
{
.interface_name = "vcan0",
},
#endif
#if CAN_DLL_NUMOF >= 2
{
.interface_name = "vcan1",
}
#endif
};
int candev_linux_init(candev_linux_t *dev, const candev_linux_conf_t *conf)
{
memset(dev, 0, sizeof(candev_linux_t));
dev->candev.driver = &candev_linux_driver;
dev->conf = conf;
dev->candev.bittiming.bitrate = CANDEV_LINUX_DEFAULT_BITRATE;
dev->candev.bittiming.sample_point = CANDEV_LINUX_DEFAULT_SPT;
return 0;
}
static candev_event_t _can_error_to_can_evt(struct can_frame can_frame_err)
{
candev_event_t can_evt = CANDEV_EVENT_NOEVENT;
can_err_mask_t can_err_type = can_frame_err.can_id & CAN_ERR_MASK;
if (can_err_type & CAN_ERR_TX_TIMEOUT) {
can_evt = CANDEV_EVENT_TX_ERROR;
}
else if (can_err_type & CAN_ERR_CRTL) {
switch(can_frame_err.data[1]) {
case CAN_ERR_CRTL_RX_OVERFLOW:
can_evt = CANDEV_EVENT_RX_ERROR;
break;
case CAN_ERR_CRTL_TX_OVERFLOW:
can_evt = CANDEV_EVENT_TX_ERROR;
break;
case CAN_ERR_CRTL_RX_PASSIVE:
case CAN_ERR_CRTL_TX_PASSIVE:
can_evt = CANDEV_EVENT_ERROR_PASSIVE;
break;
case CAN_ERR_CRTL_RX_WARNING:
case CAN_ERR_CRTL_TX_WARNING:
can_evt = CANDEV_EVENT_ERROR_WARNING;
break;
}
}
else if (can_err_type & CAN_ERR_BUSOFF) {
can_evt = CANDEV_EVENT_BUS_OFF;
}
return can_evt;
}
static void _callback_can_sigio(int sockfd, void *arg)
{
(void) sockfd;
candev_linux_t *dev = (candev_linux_t *) arg;
if (dev->candev.event_callback) {
dev->candev.event_callback(&dev->candev, CANDEV_EVENT_ISR, NULL);
}
native_async_read_continue(sockfd);
if (sched_context_switch_request) {
thread_yield_higher();
}
}
static int _init(candev_t *candev)
{
struct sockaddr_can addr;
struct ifreq ifr;
int ret;
DEBUG("Will start linux CAN init\n");
candev_linux_t *dev = (candev_linux_t *)candev;
if ((strlen(dev->conf->interface_name) == 0)
|| (strlen(dev->conf->interface_name) > CAN_MAX_SIZE_INTERFACE_NAME)) {
real_printf("Error: Invalid can iface, too short or too long \n");
return -1;
}
dev->sock = real_socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (dev->sock < 0) {
real_printf("CAN config KO, socket nr = %i \n", dev->sock);
return -1;
}
can_err_mask_t err_mask = CAN_ERR_TX_TIMEOUT |
CAN_ERR_BUSOFF |
CAN_ERR_CRTL;
ret = real_setsockopt(dev->sock, SOL_CAN_RAW, CAN_RAW_ERR_FILTER,
&err_mask, sizeof(err_mask));
if (ret < 0) {
real_printf("Error: setsockopt failed\n");
real_close(dev->sock);
return -1;
}
strcpy(ifr.ifr_name, dev->conf->interface_name);
ret = real_ioctl(dev->sock, SIOCGIFINDEX, &ifr);
if (ret < 0) {
real_printf("Error: Invalid can iface %s\n", dev->conf->interface_name);
real_close(dev->sock);
return -1;
}
native_async_read_setup();
/* This func will also automatically configure socket to be asynchronous */
/* and to activate SIGIO */
native_async_read_add_handler(dev->sock, (void *) dev, _callback_can_sigio);
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
real_bind(dev->sock, (struct sockaddr *)&addr, sizeof(addr));
_set_bittiming(dev, &candev->bittiming);
DEBUG("CAN linux device ready\n");
return 0;
}
static int _send(candev_t *candev, const struct can_frame *frame)
{
int nbytes;
candev_linux_t *dev = (candev_linux_t *)candev;
nbytes = real_write(dev->sock, frame, sizeof(struct can_frame));
if (nbytes < frame->can_dlc) {
real_printf("CAN write op failed, nbytes=%i\n", nbytes);
return -1;
}
if (dev->candev.event_callback) {
dev->candev.event_callback(&dev->candev, CANDEV_EVENT_TX_CONFIRMATION, (void *)frame);
}
return 0;
}
static void _isr(candev_t *candev)
{
int nbytes;
struct can_frame rcv_frame;
candev_linux_t *dev = (candev_linux_t *)candev;
if (dev == NULL) {
return;
}
DEBUG("candev_native _isr: CAN SIGIO interrupt received, sock = %i\n", dev->sock);
nbytes = real_read(dev->sock, &rcv_frame, sizeof(struct can_frame));
if (nbytes < 0) { /* SIGIO signal was probably due to an error with the socket */
DEBUG("candev_native _isr: read: error during read\n");
return;
}
if (nbytes < (int)sizeof(struct can_frame)) {
DEBUG("candev_native _isr: read: incomplete CAN frame\n");
return;
}
if (rcv_frame.can_id & CAN_ERR_FLAG) {
DEBUG("candev_native _isr: error frame\n");
candev_event_t evt = _can_error_to_can_evt(rcv_frame);
if ((evt != CANDEV_EVENT_NOEVENT) && (dev->candev.event_callback)) {
dev->candev.event_callback(&dev->candev, evt, NULL);
}
return;
}
if (rcv_frame.can_id & CAN_RTR_FLAG) {
DEBUG("candev_native _isr: rtr frame\n");
return;
}
if (dev->candev.event_callback) {
DEBUG("candev_native _isr: calling event callback\n");
dev->candev.event_callback(&dev->candev, CANDEV_EVENT_RX_INDICATION, &rcv_frame);
}
}
static int _set_bittiming(candev_linux_t *dev, struct can_bittiming *bittiming)
{
int res;
dev->candev.bittiming = *bittiming;
DEBUG("bitrate = %d, brp= %d, phase_seg1 = %d, phase_seg2 = %d, sjw = %d\n",
dev->candev.bittiming.bitrate, dev->candev.bittiming.brp,
dev->candev.bittiming.phase_seg1, dev->candev.bittiming.phase_seg2,
dev->candev.bittiming.sjw);
/* bitrate setting */
DEBUG("_set: setting %s down\n", dev->conf->interface_name);
res = can_do_stop(dev->conf->interface_name);
if (res < 0) {
return res;
}
DEBUG("_set: setting bittiming to %s\n", dev->conf->interface_name);
res = can_set_bitrate(dev->conf->interface_name, dev->candev.bittiming.bitrate);
can_get_bittiming(dev->conf->interface_name, &dev->candev.bittiming);
DEBUG("_set: setting %s up\n", dev->conf->interface_name);
can_do_start(dev->conf->interface_name);
return res;
}
static int _set(candev_t *candev, canopt_t opt, void *value, size_t value_len)
{
candev_linux_t *dev = (candev_linux_t *) candev;
int res = 0;
switch (opt) {
case CANOPT_BITTIMING:
DEBUG("candev_linux: CANOPT_BITTIMING\n");
if (strncmp(dev->conf->interface_name, "can", strlen("can"))) {
DEBUG("candev_native: _set: error interface is not real can\n");
return -EINVAL;
}
if (value == NULL) {
DEBUG("candev_native: _set: error value NULL\n");
return -EOVERFLOW;
}
if (value_len < sizeof(struct can_bittiming)) {
DEBUG("candev_native: _set: error size of bittiming\n");
return -EOVERFLOW;
}
res = _set_bittiming(dev, value);
break;
case CANOPT_STATE:
switch (*((canopt_state_t *)value)) {
case CANOPT_STATE_SLEEP:
case CANOPT_STATE_OFF:
_power_down(candev);
break;
default:
_power_up(candev);
break;
}
break;
default:
DEBUG("CAN set, not supported opt\n");
res = -ENOTSUP;
break;
}
return res;
}
static int _get(candev_t *candev, canopt_t opt, void *value, size_t max_len)
{
candev_linux_t *dev = (candev_linux_t *) candev;
int res = 0;
switch (opt) {
case CANOPT_BITTIMING:
if (max_len < sizeof(struct can_bittiming)) {
res = -EOVERFLOW;
break;
}
if (value == NULL) {
res = -EINVAL;
break;
}
if (can_get_bittiming(dev->conf->interface_name, value) == 0) {
res = sizeof(struct can_bittiming);
}
else {
res = -ENOTSUP;
}
break;
case CANOPT_BITTIMING_CONST:
if (max_len < sizeof(struct can_bittiming_const)) {
res = -EOVERFLOW;
break;
}
if (value == NULL) {
res = -EINVAL;
break;
}
if (can_get_bittiming_const(dev->conf->interface_name, value) == 0) {
res = sizeof(struct can_bittiming_const);
}
else {
res = -ENOTSUP;
}
break;
case CANOPT_CLOCK:
if (max_len < sizeof(uint32_t)) {
res = -EOVERFLOW;
break;
}
if (value == NULL) {
res = -EINVAL;
break;
}
{
struct can_clock clock;
if (can_get_clock(dev->conf->interface_name, &clock) == 0) {
*((uint32_t *)value) = clock.freq;
res = sizeof(uint32_t);
}
else {
res = -ENOTSUP;
}
}
break;
case CANOPT_TEC:
case CANOPT_REC:
if (max_len < sizeof(uint16_t)) {
res = -EOVERFLOW;
break;
}
if (value == NULL) {
res = -EINVAL;
break;
}
{
struct can_berr_counter bc;
if (can_get_berr_counter(dev->conf->interface_name, &bc) == 0) {
if (opt == CANOPT_TEC) {
*((uint16_t *)value) = bc.txerr;
}
else {
*((uint16_t *)value) = bc.rxerr;
}
res = sizeof(uint16_t);
}
else {
res = -ENOTSUP;
}
}
break;
case CANOPT_RX_FILTERS: {
if (max_len % sizeof(struct can_filter) != 0) {
res = -EOVERFLOW;
break;
}
struct can_filter *list = value;
size_t i;
for (i = 0; (i < CANDEV_LINUX_MAX_FILTERS_RX)
&& (dev->filters[i].can_id != 0)
&& (i < (max_len / sizeof(struct can_filter))); i++) {
list[i] = dev->filters[i];
}
res = i * sizeof(struct can_filter);
break; }
default:
DEBUG("CAN get, not supported op\n");
res = -ENOTSUP;
break;
}
return res;
}
static int _set_filter(candev_t *candev, const struct can_filter *filter)
{
candev_linux_t *dev = (candev_linux_t *)candev;
if (filter == NULL) {
DEBUG("candev_native: _set_filter: error filter NULL\n");
return -EOVERFLOW;
}
DEBUG("candev_native: _set_filter: candev=%p, filter: f=%x m=%x on sock: %i\n",
(void *)candev, filter->can_id, filter->can_mask, dev->sock
);
uint32_t i;
for (i = 0; i < CANDEV_LINUX_MAX_FILTERS_RX; i++) {
if (dev->filters[i].can_id == filter->can_id) {
DEBUG("candev_native: _set_filter: filter already set\n");
return 0;
}
else if (dev->filters[i].can_id == 0) {
break;
}
}
if (i == CANDEV_LINUX_MAX_FILTERS_RX) {
DEBUG("candev_native: _set_filter: no more filters available\n");
return -EOVERFLOW;
}
for (i = 0; i < CANDEV_LINUX_MAX_FILTERS_RX; i++) {
if (dev->filters[i].can_id == 0) {
/* Only 29 bits must be used for masks in SocketCAN */
dev->filters[i] = *filter;
dev->filters[i].can_mask &= CAN_EFF_MASK;
DEBUG("candev_native: _set_filter: filter:ID=0x%x\n", filter->can_id);
DEBUG("candev_native: _set_filter: mask=0x%x\n", filter->can_mask);
break;
}
}
i++;
DEBUG("%" PRIu32 " filters will be set\n", i);
real_setsockopt(dev->sock, SOL_CAN_RAW, CAN_RAW_FILTER, dev->filters,
sizeof(struct can_filter) * i);
return i;
}
static int _remove_filter(candev_t *candev, const struct can_filter *filter)
{
candev_linux_t *dev = (candev_linux_t *)candev;
if (filter == NULL) {
DEBUG("candev_native: _remove_filter: error filter NULL\n");
return -EOVERFLOW;
}
uint32_t i;
for (i = 0; i < CANDEV_LINUX_MAX_FILTERS_RX; i++) {
if ((dev->filters[i].can_id == filter->can_id )
&& (dev->filters[i].can_mask == (filter->can_mask & CAN_EFF_MASK))) {
if (i < CANDEV_LINUX_MAX_FILTERS_RX - 1) {
memmove(&dev->filters[i], &dev->filters[i + 1],
sizeof(dev->filters[i]) * (CANDEV_LINUX_MAX_FILTERS_RX - i - 1));
}
dev->filters[CANDEV_LINUX_MAX_FILTERS_RX - 1].can_id = 0;
break;
}
else if (dev->filters[i].can_id == 0) {
DEBUG("candev_native: _remove_filter: error filter not found\n");
return -EOVERFLOW;
}
}
if (i == CANDEV_LINUX_MAX_FILTERS_RX) {
DEBUG("candev_native: _remove_filter: error filter not found\n");
return -EOVERFLOW;
}
for (i = 0; i < CANDEV_LINUX_MAX_FILTERS_RX; i++) {
if (dev->filters[i].can_id == 0) {
break;
}
}
DEBUG("%" PRIu32 " filters will be set\n", i);
real_setsockopt(dev->sock, SOL_CAN_RAW, CAN_RAW_FILTER, dev->filters,
sizeof(struct can_filter) * i);
return 0;
}
static int _abort(candev_t *candev, const struct can_frame *frame)
{
(void)frame;
(void)candev;
return 0;
}
static int _power_down(candev_t *candev)
{
(void)candev;
return 0;
}
static int _power_up(candev_t *candev)
{
(void)candev;
return 0;
}
#endif /* defined(__linux__) */

View File

@ -0,0 +1,104 @@
/*
* Copyright (C) 2016 OTA keys S.A.
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup native_cpu
* @ingroup drivers_can
* @defgroup candev_linux SocketCAN driver
* @{
*
* @file
* @brief Implementation of simulated CAN controller driver using SocketCAN on Linux
*
* @author Hermann Lelong <hermann@otakeys.com>
* @author Aurelien Gonce <aurelien.gonce@altran.com>
* @author Vincent Dupont <vincent@otakeys.com>
* @}
*/
#ifndef CANDEV_LINUX_H
#define CANDEV_LINUX_H
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__linux__) /* SocketCAN is supported only on Linux */ || defined(DOXYGEN)
#include <stdbool.h>
#include "can/candev.h"
#include "mutex.h"
/**
* Maximum size of an interface name
*/
#define CAN_MAX_SIZE_INTERFACE_NAME (5)
/**
* Linux candev configuration
*/
typedef struct candev_linux_conf {
/** local interface name */
char interface_name[CAN_MAX_SIZE_INTERFACE_NAME + 1];
} candev_linux_conf_t;
#ifndef CANDEV_LINUX_MAX_FILTERS_RX
/**
* Max number of rx filters which can be set
*/
#define CANDEV_LINUX_MAX_FILTERS_RX (16)
#endif
#ifndef CANDEV_LINUX_DEFAULT_BITRATE
/**
* Default bitrate setup
*/
#define CANDEV_LINUX_DEFAULT_BITRATE (500000)
#endif
#ifndef CANDEV_LINUX_DEFAULT_SPT
/**
* Default sampling point setup
*/
#define CANDEV_LINUX_DEFAULT_SPT (875)
#endif
/**
* @brief The candev_linux struct
*/
typedef struct candev_linux {
candev_t candev; /**< candev base structure */
int sock; /**< local socket id */
const candev_linux_conf_t *conf; /**< device configuration */
/** filter list */
struct can_filter filters[CANDEV_LINUX_MAX_FILTERS_RX];
} candev_linux_t;
/**
* @brief Device specific initialization function
*
* @param[inout] dev the device to initialize
* @param[in] conf the device configuration
*
* @return 0 on success
*/
int candev_linux_init(candev_linux_t *dev, const candev_linux_conf_t *conf);
/**
* @brief Array containing socketCAN device names
*/
extern candev_linux_conf_t candev_linux_conf[CAN_DLL_NUMOF];
#endif /* defined(__linux__) */
#ifdef __cplusplus
}
#endif
#endif /* CANDEV_LINUX_H */

View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2016 OTA keys S.A.
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup candev_linux
* @{
*
* @file
* @brief Default linux can config
*
* @author Vincent Dupont <vincent@otakeys.com>
* @}
*/
#ifndef CANDEV_LINUX_PARAMS_H
#define CANDEV_LINUX_PARAMS_H
#include "candev_linux.h"
#include "can/device.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Default parameters (device names)
*/
static candev_params_t candev_linux_params[] = {
{ .name = "can0", },
{ .name = "can1", },
};
#ifdef __cplusplus
}
#endif
#endif /* CANDEV_LINUX_PARAMS_H */
/** @} */

View File

@ -67,17 +67,23 @@ const char *_native_unix_socket_path = NULL;
netdev_tap_params_t netdev_tap_params[NETDEV_TAP_MAX];
#endif
#ifdef MODULE_MTD_NATIVE
#include "board.h"
#include "mtd_native.h"
#endif
#ifdef MODULE_CAN_LINUX
#include "candev_linux.h"
#endif
static const char short_opts[] = ":hi:s:deEoc:"
#ifdef MODULE_MTD_NATIVE
"m:"
#endif
#ifdef MODULE_CAN_LINUX
"n:"
#endif
"";
static const struct option long_opts[] = {
{ "help", no_argument, NULL, 'h' },
{ "id", required_argument, NULL, 'i' },
@ -89,6 +95,9 @@ static const struct option long_opts[] = {
{ "uart-tty", required_argument, NULL, 'c' },
#ifdef MODULE_MTD_NATIVE
{ "mtd", required_argument, NULL, 'm' },
#endif
#ifdef MODULE_CAN_LINUX
{ "can", required_argument, NULL, 'n' },
#endif
{ NULL, 0, NULL, '\0' },
};
@ -249,6 +258,12 @@ void usage_exit(int status)
real_printf(
" -m <mtd>, --mtd=<mtd>\n"
" specify the file name of mtd emulated device\n");
#endif
#if defined(MODULE_CAN_LINUX)
real_printf(
" -n <ifnum>:<ifname>, --can <ifnum>:<ifname>\n"
" specify CAN interface <ifname> to use for CAN device #<ifnum>\n"
" max number of CAN device: %d\n", CAN_DLL_NUMOF);
#endif
real_exit(status);
}
@ -325,6 +340,25 @@ __attribute__((constructor)) static void startup(int argc, char **argv, char **e
case 'm':
((mtd_native_dev_t *)mtd0)->fname = strndup(optarg, PATH_MAX - 1);
break;
#endif
#if defined(MODULE_CAN_LINUX)
case 'n':{
int i;
i = atol(optarg);
if (i >= (int)CAN_DLL_NUMOF) {
usage_exit(EXIT_FAILURE);
}
while ((*optarg != ':') && (*optarg != '\0')) {
optarg++;
}
if (*optarg == '\0') {
usage_exit(EXIT_FAILURE);
}
optarg++;
strncpy(candev_linux_conf[i].interface_name, optarg,
CAN_MAX_SIZE_INTERFACE_NAME);
}
break;
#endif
default:
usage_exit(EXIT_FAILURE);

View File

@ -29,4 +29,8 @@ void auto_init_candev(void)
{
DEBUG("auto_init_can: init dll\n");
can_dll_init();
#ifdef MODULE_CAN_LINUX
extern void auto_init_can_native(void);
auto_init_can_native();
#endif
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (C) 2016 OTA keys S.A.
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup auto_init
* @{
* @file
* @brief initializes native can device
*
* @author Vincent Dupont <vincent@otakeys.com>
* @}
*/
#ifdef MODULE_CAN_LINUX
#include "can/device.h"
#include "candev_linux_params.h"
#define CANDEV_LINUX_NUMOF ((sizeof(candev_linux_params) / sizeof(candev_params_t)))
#ifndef CANDEV_LINUX_STACKSIZE
#define CANDEV_LINUX_STACKSIZE (THREAD_STACKSIZE_DEFAULT + THREAD_EXTRA_STACKSIZE_PRINTF)
#endif
#ifndef CANDEV_LINUX_BASE_PRIORITY
#define CANDEV_LINUX_BASE_PRIORITY (THREAD_PRIORITY_MAIN - CANDEV_LINUX_NUMOF - 2)
#endif
static candev_dev_t candev_dev_linux[CANDEV_LINUX_NUMOF];
static char _can_linux_stacks[CANDEV_LINUX_NUMOF][CANDEV_LINUX_STACKSIZE];
static candev_linux_t candev_linux[CANDEV_LINUX_NUMOF];
void auto_init_can_native(void) {
for (size_t i = 0; i < CANDEV_LINUX_NUMOF; i++) {
candev_linux_init(&candev_linux[i], &candev_linux_conf[i]);
candev_dev_linux[i].dev = (candev_t *)&candev_linux[i];
candev_dev_linux[i].name = candev_linux_params[i].name;
#ifdef MODULE_CAN_PM
candev_dev_linux[i].rx_inactivity_timeout = candev_linux_params[i].rx_inactivity_timeout;
candev_dev_linux[i].tx_wakeup_timeout = candev_linux_params[i].tx_wakeup_timeout;
#endif
can_device_init(_can_linux_stacks[i], CANDEV_LINUX_STACKSIZE, CANDEV_LINUX_BASE_PRIORITY + i,
candev_linux_params[i].name, &candev_dev_linux[i]);
}
}
#else
typedef int dont_be_pedantic;
#endif