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:
parent
144a0700f5
commit
68f1ea8fd0
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
3
cpu/native/can/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
MODULE = can_linux
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
558
cpu/native/can/candev_linux.c
Normal file
558
cpu/native/can/candev_linux.c
Normal 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__) */
|
104
cpu/native/include/candev_linux.h
Normal file
104
cpu/native/include/candev_linux.h
Normal 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 */
|
43
cpu/native/include/candev_linux_params.h
Normal file
43
cpu/native/include/candev_linux_params.h
Normal 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 */
|
||||
/** @} */
|
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
|
54
sys/auto_init/can/auto_init_can_native.c
Normal file
54
sys/auto_init/can/auto_init_can_native.c
Normal 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
|
Loading…
Reference in New Issue
Block a user