Browse Source

Merge pull request #5793 from OTAkeys/pr/can_stack

can: add a CAN stack for RIOT
master
Alexandre Abadie 5 years ago committed by GitHub
parent
commit
067231936f
  1. 25
      Makefile.dep
  2. 7
      boards/native/Makefile.dep
  3. 4
      cpu/native/Makefile
  4. 3
      cpu/native/can/Makefile
  5. 558
      cpu/native/can/candev_linux.c
  6. 104
      cpu/native/include/candev_linux.h
  7. 43
      cpu/native/include/candev_linux_params.h
  8. 36
      cpu/native/startup.c
  9. 1
      drivers/can_trx/Makefile
  10. 53
      drivers/can_trx/can_trx.c
  11. 108
      drivers/include/can/can_trx.h
  12. 182
      drivers/include/can/candev.h
  13. 4
      makefiles/pseudomodules.inc.mk
  14. 4
      sys/auto_init/Makefile
  15. 8
      sys/auto_init/auto_init.c
  16. 3
      sys/auto_init/can/Makefile
  17. 56
      sys/auto_init/can/auto_init_can.c
  18. 57
      sys/auto_init/can/auto_init_can_native.c
  19. 10
      sys/can/Makefile
  20. 3
      sys/can/conn/Makefile
  21. 419
      sys/can/conn/isotp.c
  22. 279
      sys/can/conn/raw.c
  23. 590
      sys/can/device.c
  24. 521
      sys/can/dll.c
  25. 3
      sys/can/isotp/Makefile
  26. 898
      sys/can/isotp/isotp.c
  27. 163
      sys/can/pkt.c
  28. 400
      sys/can/router.c
  29. 149
      sys/include/can/can.h
  30. 165
      sys/include/can/common.h
  31. 202
      sys/include/can/conn/isotp.h
  32. 138
      sys/include/can/conn/raw.h
  33. 122
      sys/include/can/device.h
  34. 110
      sys/include/can/dll.h
  35. 23
      sys/include/can/doc.txt
  36. 196
      sys/include/can/isotp.h
  37. 152
      sys/include/can/pkt.h
  38. 279
      sys/include/can/raw.h
  39. 116
      sys/include/can/router.h
  40. 3
      sys/shell/commands/Makefile
  41. 184
      sys/shell/commands/sc_can.c
  42. 7
      sys/shell/commands/shell_commands.c
  43. 24
      tests/conn_can/Makefile
  44. 152
      tests/conn_can/README.md
  45. 634
      tests/conn_can/main.c

25
Makefile.dep

@ -56,7 +56,7 @@ ifneq (,$(filter nordic_softdevice_ble,$(USEPKG)))
USEMODULE += gnrc_ipv6_netif
endif
ifneq (,$(filter gnrc_%,$(filter-out gnrc_netapi gnrc_netreg gnrc_netif% gnrc_pktbuf,$(USEMODULE))))
ifneq (,$(filter gnrc_%,$(filter-out gnrc_netapi gnrc_netreg gnrc_netif% gnrc_pkt%,$(USEMODULE))))
USEMODULE += gnrc
endif
@ -572,6 +572,29 @@ 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
ifneq (,$(filter can_mbox,$(USEMODULE)))
USEMODULE += core_mbox
endif
USEMODULE += gnrc_pktbuf_static
endif
ifneq (,$(filter can_isotp,$(USEMODULE)))
USEMODULE += xtimer
endif
ifneq (,$(filter conn_can,$(USEMODULE)))
USEMODULE += can
USEMODULE += can_mbox
USEMODULE += xtimer
endif
ifneq (,$(filter random,$(USEMODULE)))
# select default prng
ifeq (,$(filter prng_%,$(USEMODULE)))

7
boards/native/Makefile.dep

@ -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

4
cpu/native/Makefile

@ -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

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

558
cpu/native/can/candev_linux.c

@ -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

@ -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

@ -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 */
/** @} */

36
cpu/native/startup.c

@ -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);

1
drivers/can_trx/Makefile

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

53
drivers/can_trx/can_trx.c

@ -0,0 +1,53 @@
/*
* 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.
*/
/**
* @defgroup drivers_can transceiver
* @ingroup drivers
* @brief generic transceiver interface
*
* @{
*
* @file
* @brief generic transceiver interface
*
* @author Vincent Dupont <vincent@otakeys.com>
*/
#include <errno.h>
#include <stdlib.h>
#include "can/can_trx.h"
int can_trx_init(can_trx_t *dev)
{
if (dev == NULL) {
return -ENODEV;
}
if (dev->driver->init) {
return dev->driver->init(dev);
}
else {
return -ENOTSUP;
}
}
int can_trx_set_mode(can_trx_t *dev, can_trx_mode_t mode)
{
if (dev == NULL) {
return -ENODEV;
}
if (dev->driver->set_mode) {
return dev->driver->set_mode(dev, mode);
}
else {
return -ENOTSUP;
}
}

108
drivers/include/can/can_trx.h

@ -0,0 +1,108 @@
/*
* 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.
*/
/**
* @defgroup trx_can CAN transceiver
* @ingroup can
* @ingroup drivers
* @brief generic transceiver interface
*
* @{
*
* @file
* @brief generic transceiver interface
*
* @author Aurelien Gonce <aurelien.gonce@altran.com>
* @author Vincent Dupont <vincent@otakeys.com>
*/
#ifndef CAN_CAN_TRX_H
#define CAN_CAN_TRX_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* trx transceiver mode
*/
typedef enum {
TRX_NORMAL_MODE = 0,
TRX_SILENT_MODE,
TRX_SLEEP_MODE,
/* single wire can modes */
TRX_HIGH_SPEED_MODE,
TRX_HIGH_VOLTAGE_WAKE_UP_MODE
} can_trx_mode_t;
/**
* @brief forward declaration of trx_driver
*/
typedef struct trx_driver trx_driver_t;
/**
* @brief Generic transceiver descriptor
*/
typedef struct can_trx {
const trx_driver_t *driver; /**< driver */
can_trx_mode_t mode; /**< current mode */
} can_trx_t;
/**
* @brief Generic transceiver driver
*/
struct trx_driver {
/**
* @brief initialize the trx device
*
* @param[in] dev Transceiver to initialize
*
* @return 0 on success
* @return < 0 on error
*/
int (*init)(can_trx_t *dev);
/**
* @brief set mode interface
*
* @param[in] dev Transceiver to set
* @param[in] mode Mode to set
*
* @return 0 on success
* @return < 0 on error
*/
int (*set_mode)(can_trx_t *dev, can_trx_mode_t mode);
};
/**
* @brief initialize a transceiver
*
* @param[in] dev Transceiver to initialize
*
* @return 0 on success
* @return < 0 on error
*/
int can_trx_init(can_trx_t *dev);
/**
* @brief transceiver set mode
*
* @param[in] dev Transceiver to set
* @param[in] mode Mode to set
*
* @return 0 on success
* @return < 0 on error
*/
int can_trx_set_mode(can_trx_t *dev, can_trx_mode_t mode);
#ifdef __cplusplus
}
#endif
#endif /* CAN_CAN_TRX_H */
/** @} */

182
drivers/include/can/candev.h

@ -0,0 +1,182 @@
/*
* 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 can
* @ingroup drivers
* @defgroup drivers_can CAN drivers
* @{
*
* This is the CAN controller driver interface
*
* @file
* @brief Definitions low-level CAN driver interface
*
* @author Vincent Dupont <vincent@otakeys.com>
* @author Toon Stegen <toon.stegen@altran.com>
*/
#ifndef CAN_CANDEV_H
#define CAN_CANDEV_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdlib.h>
#include "can/can.h"
#include "can/common.h"
#include "mutex.h"
/**
* @brief Possible event types that are send from the device driver to the
* upper layer
*/
typedef enum {
CANDEV_EVENT_NOEVENT, /**< no event, used internally */
CANDEV_EVENT_ISR, /**< driver needs it's ISR handled */
CANDEV_EVENT_WAKE_UP, /**< driver has been woken up by bus */
CANDEV_EVENT_TX_CONFIRMATION, /**< a packet has been sent */
CANDEV_EVENT_TIMEOUT_TX_CONF, /**< tx conf timeout received */
CANDEV_EVENT_RX_INDICATION, /**< a packet has been received */
CANDEV_EVENT_TX_ERROR, /**< there was an error when transmitting */
CANDEV_EVENT_RX_ERROR, /**< there was an error when receiving */
CANDEV_EVENT_BUS_OFF, /**< bus-off detected */
CANDEV_EVENT_ERROR_PASSIVE, /**< driver switched in error passive */
CANDEV_EVENT_ERROR_WARNING, /**< driver reached error warning */
/* expand this list if needed */
} candev_event_t;
/**
* @brief Forward declaration for candev struct
*/
typedef struct candev candev_t;
/**
* @brief Event callback for signaling event to upper layers
*
* @param[in] dev CAN device descriptor
* @param[in] type type of the event
* @param[in] arg event argument
*/
typedef void (*candev_event_cb_t)(candev_t *dev, candev_event_t event, void *arg);
/**
* @brief Structure to hold driver state
*
* Supposed to be extended by driver implementations.
* The extended structure should contain all variable driver state.
*/
struct candev {
const struct candev_driver *driver; /**< ptr to that driver's interface. */
candev_event_cb_t event_callback; /**< callback for device events */
void *isr_arg; /**< argument to pass on isr event */
struct can_bittiming bittiming; /**< device bittimings */
enum can_state state; /**< device state */
};
/**
* @brief Structure to hold driver interface -> function mapping
*/
typedef struct candev_driver {
/**
* @brief Send packet
*
* @param[in] dev CAN device descriptor
* @param[in] frame CAN frame to send
*
* @return < 0 on error
* @return mailbox id >= 0 if OK
*/
int (*send)(candev_t *dev, const struct can_frame *frame);
/**
* @brief Abort a packet sending
*
* @param[in] dev CAN device descriptor
* @param[in] frame CAN frame to abort
*
* @return < 0 on error
* @return 0 on OK
*/
int (*abort)(candev_t *dev, const struct can_frame *frame);
/**
* @brief the driver's initialization function
*
* @param[in] dev CAN device descriptor
*
* @return < 0 on error, 0 on success
*/
int (*init)(candev_t *dev);
/**
* @brief a driver's user-space ISR handler
*
* @param[in] dev CAN device descriptor
*/
void (*isr)(candev_t *dev);
/**
* @brief Get an option value from a given CAN device
*
* @param[in] dev CAN device descriptor
* @param[in] opt option type
* @param[out] value pointer to store the option's value in
* @param[in] max_len maximal amount of byte that fit into @p value
*
* @return number of bytes written to @p value
* @return <0 on error
*/
int (*get)(candev_t *dev, canopt_t opt, void *value, size_t max_len);
/**
* @brief Set an option value for a given CAN device
*
* @param[in] dev CAN device descriptor
* @param[in] opt option type
* @param[in] value value to set
* @param[in] value_len the length of @p value
*
* @return number of bytes used from @p value
* @return <0 on error
*/
int (*set)(candev_t *dev, canopt_t opt, void *value, size_t value_len);
/**
* @brief Set a receive @p filter
*
* @param[in] dev CAN device descriptor
* @param[in] filter filter to set
*
* @return a positive filter number
* @return <0 on error
*/
int (*set_filter)(candev_t *dev, const struct can_filter *filter);
/**
* @brief Remove a @p filter
*
* @param[in] dev CAN device descriptor
* @param[in] filter filter to remove
*
* @return 0 on success
* @return <0 on error
*/
int (*remove_filter)(candev_t *dev, const struct can_filter *filter);
} candev_driver_t;
#ifdef __cplusplus
}
#endif
#endif /* CAN_CANDEV_H */
/** @} */

4
makefiles/pseudomodules.inc.mk

@ -1,4 +1,8 @@
PSEUDOMODULES += auto_init_gnrc_rpl
PSEUDOMODULES += can_mbox
PSEUDOMODULES += can_pm
PSEUDOMODULES += can_raw
PSEUDOMODULES += conn_can_isotp_multi
PSEUDOMODULES += core_%
PSEUDOMODULES += emb6_router
PSEUDOMODULES += gnrc_ipv6_default

4
sys/auto_init/Makefile

@ -12,4 +12,8 @@ ifneq (,$(filter auto_init_storage,$(USEMODULE)))
DIRS += storage
endif
ifneq (,$(filter auto_init_can,$(USEMODULE)))
DIRS += can
endif
include $(RIOTBASE)/Makefile.base

8
sys/auto_init/auto_init.c

@ -365,4 +365,12 @@ void auto_init(void)
#endif
#endif /* MODULE_AUTO_INIT_STORAGE */
#ifdef MODULE_AUTO_INIT_CAN
DEBUG("auto_init CAN\n");
extern void auto_init_candev(void);
auto_init_candev();
#endif /* MODULE_AUTO_INIT_CAN */
}

3
sys/auto_init/can/Makefile

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

56
sys/auto_init/can/auto_init_can.c

@ -0,0 +1,56 @@
/*
* 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 can device init function
*
* @author Toon Stegen <toon.stegen@altran.com>
* @author Vincent Dupont <vincent@otakeys.com>
* @author Aurelien Gonce <aurelien.gonce@altran.com>
* @}
*/
#include <stdio.h>
#define ENABLE_DEBUG (0)
#include "debug.h"
#include "can/dll.h"
#ifdef MODULE_CAN_ISOTP
#include "can/isotp.h"
#ifndef ISOTP_STACK_SIZE
#define ISOTP_STACK_SIZE (THREAD_STACKSIZE_DEFAULT + THREAD_EXTRA_STACKSIZE_PRINTF)
#endif
#ifndef ISOTP_PRIORITY
#define ISOTP_PRIORITY (THREAD_PRIORITY_MAIN - 2)
#endif
static char isotp_stack[ISOTP_STACK_SIZE];
#endif
void auto_init_candev(void)
{
DEBUG("auto_init_can: init dll\n");
can_dll_init();
#ifdef MODULE_CAN_ISOTP
DEBUG("auto_init_can: init isotp\n");
isotp_init(isotp_stack, ISOTP_STACK_SIZE, ISOTP_PRIORITY, "isotp");
#endif
#ifdef MODULE_CAN_LINUX
extern void auto_init_can_native(void);
auto_init_can_native();
#endif
}

57
sys/auto_init/can/auto_init_can_native.c

@ -0,0 +1,57 @@
/*
* 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_TRX
candev_dev_linux[i].trx = candev_linux_params[i].trx;
#endif
#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

10
sys/can/Makefile

@ -0,0 +1,10 @@
ifneq (,$(filter can_isotp,$(USEMODULE)))
DIRS += isotp
endif
ifneq (,$(filter conn_can,$(USEMODULE)))
DIRS += conn
endif
include $(RIOTBASE)/Makefile.base

3
sys/can/conn/Makefile

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

419
sys/can/conn/isotp.c

@ -0,0 +1,419 @@
/*
* Copyright (C) 2016 OTA keys S.A.
*