

45 changed files with 7196 additions and 2 deletions
@ -0,0 +1,3 @@
|
||||
MODULE = can_linux
|
||||
|
||||
include $(RIOTBASE)/Makefile.base |
@ -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__) */ |
@ -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 */ |
@ -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 */ |
||||
/** @} */ |
@ -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; |
||||
} |
||||
} |
@ -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 */ |
||||
/** @} */ |
@ -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 */ |
||||
/** @} */ |
@ -0,0 +1,3 @@
|
||||
MODULE = auto_init_can
|
||||
|
||||
include $(RIOTBASE)/Makefile.base |
@ -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 |
||||
} |
@ -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 |
@ -0,0 +1,10 @@
|
||||
|
||||
ifneq (,$(filter can_isotp,$(USEMODULE))) |
||||
DIRS += isotp
|
||||
endif |
||||
|
||||
ifneq (,$(filter conn_can,$(USEMODULE))) |
||||
DIRS += conn
|
||||
endif |
||||
|
||||
include $(RIOTBASE)/Makefile.base |
@ -0,0 +1,3 @@
|
||||
MODULE = conn_can
|
||||
|
||||
include $(RIOTBASE)/Makefile.base |