1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #5793 from OTAkeys/pr/can_stack

can: add a CAN stack for RIOT
This commit is contained in:
Alexandre Abadie 2017-06-26 16:02:35 +02:00 committed by GitHub
commit 067231936f
45 changed files with 7196 additions and 2 deletions

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

1
drivers/can_trx/Makefile Normal file
View File

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

53
drivers/can_trx/can_trx.c Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 Normal file
View File

@ -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 Normal file
View File

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

419
sys/can/conn/isotp.c Normal file
View File

@ -0,0 +1,419 @@
/*
* 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.
*/
/**
* @file
* @brief Implementation of isotp CAN connection
*
* @author Vincent Dupont <vincent@otakeys.com>
*/
#ifdef MODULE_CAN_ISOTP
#include <errno.h>
#include <string.h>
#include "can/conn/isotp.h"
#include "can/isotp.h"
#include "can/device.h"
#ifdef MODULE_CONN_CAN_ISOTP_MULTI
#include "utlist.h"
#endif
#define ENABLE_DEBUG (0)
#include "debug.h"
#include "xtimer.h"
#define _TIMEOUT_TX_MSG_TYPE (0x8000)
#define _TIMEOUT_RX_MSG_TYPE (0x8001)
#define _CLOSE_CONN_MSG_TYPE (0x8002)
#define _TIMEOUT_MSG_VALUE (0xABCDEFAB)
#ifndef CONN_CAN_ISOTP_TIMEOUT_TX_CONF
#define CONN_CAN_ISOTP_TIMEOUT_TX_CONF (10 * US_PER_SEC)
#endif
static inline void put_msg(conn_can_isotp_t *conn, msg_t *msg)
{
#ifdef MODULE_CONN_CAN_ISOTP_MULTI
mbox_put(&conn->master->mbox, msg);
#else
mbox_put(&conn->mbox, msg);
#endif
}
static inline void get_msg(conn_can_isotp_t *conn, msg_t *msg)
{
#ifdef MODULE_CONN_CAN_ISOTP_MULTI
mbox_get(&conn->master->mbox, msg);
#else
mbox_get(&conn->mbox, msg);
#endif
}
int conn_can_isotp_create(conn_can_isotp_t *conn, struct isotp_options *options, int ifnum)
{
assert(conn != NULL);
assert(options != NULL);
assert(ifnum < CAN_DLL_NUMOF);
#ifdef MODULE_CONN_CAN_ISOTP_MULTI
DEBUG("conn_can_isotp_create: conn=%p, conn->master=%p, ifnum=%d\n",
(void *)conn, (void *)conn->master, ifnum);
if (conn->master == conn || conn->master == NULL) {
conn->master = conn;
conn->master->next = NULL;
mutex_init(&conn->master->lock);
mutex_lock(&conn->master->lock);
DEBUG("conn_can_isotp_create: init master conn\n");
mbox_init(&conn->master->mbox, conn->master->mbox_queue, CONN_CAN_ISOTP_MBOX_SIZE);
mutex_unlock(&conn->master->lock);
}
#else
mbox_init(&conn->mbox, conn->mbox_queue, CONN_CAN_ISOTP_MBOX_SIZE);
#endif
conn->ifnum = ifnum;
memset(&conn->isotp, 0, sizeof(struct isotp));
conn->isotp.opt = *options;
return 0;
}
int conn_can_isotp_bind(conn_can_isotp_t *conn)
{
assert(conn != NULL);
assert(conn->isotp.opt.tx_id != 0 || conn->isotp.opt.rx_id != 0);
DEBUG("conn_can_isotp_bind: conn=%p, ifnum=%d\n",
(void *)conn, conn->ifnum);
if (conn->bound) {
return -EALREADY;
}
msg_t msg;
int ret;
can_reg_entry_t entry;
entry.ifnum = conn->ifnum;
entry.type = CAN_TYPE_MBOX;
#ifdef MODULE_CONN_CAN_ISOTP_MULTI
assert(conn->master != NULL);
entry.target.mbox = &(conn->master->mbox);
if (conn != conn->master) {
mutex_lock(&conn->master->lock);
LL_APPEND(conn->master->next, (conn_can_isotp_slave_t *)conn);
mutex_unlock(&conn->master->lock);
}
ret = mbox_try_get(&conn->master->mbox, &msg);
#else
entry.target.mbox = &conn->mbox;
ret = mbox_try_get(&conn->mbox, &msg);
#endif
if ((ret == 1) && (msg.type != _CLOSE_CONN_MSG_TYPE)) {
DEBUG("conn_can_isotp_bind: msg in queue type=%x\n", msg.type);
put_msg(conn, &msg);
}
ret = isotp_bind(&conn->isotp, &entry, conn);
if (!ret) {
conn->bound = 1;
}
return ret;
}
static void _tx_conf_timeout(void *arg)
{
conn_can_isotp_t *conn = arg;
msg_t msg;
msg.type = _TIMEOUT_TX_MSG_TYPE;
msg.content.value = _TIMEOUT_MSG_VALUE;
put_msg(conn, &msg);
}
int conn_can_isotp_send(conn_can_isotp_t *conn, const void *buf, size_t size, int flags)
{
assert(conn != NULL);
assert(buf != NULL || size == 0);
int ret = 0;
if (!conn->bound) {
return -ENOTCONN;
}
if (flags & CAN_ISOTP_TX_DONT_WAIT) {
return isotp_send(&conn->isotp, buf, size, flags);
}
else {
xtimer_t timer;
timer.callback = _tx_conf_timeout;
timer.arg = conn;
xtimer_set(&timer, CONN_CAN_ISOTP_TIMEOUT_TX_CONF);
ret = isotp_send(&conn->isotp, buf, size, flags);
msg_t msg;
while (1) {
get_msg(conn, &msg);
switch (msg.type) {
case CAN_MSG_TX_ERROR:
if (msg.content.ptr == conn) {
ret = -EIO;
}
/* No break */
case CAN_MSG_TX_CONFIRMATION:
#ifdef MODULE_CONN_CAN_ISOTP_MULTI
if (msg.content.ptr != conn) {
mbox_put(&conn->master->mbox, &msg);
break;
}
#endif
xtimer_remove(&timer);
return ret;
case _TIMEOUT_TX_MSG_TYPE:
return -ETIMEDOUT;
default:
DEBUG("conn_can_isotp_send: unexpected msg %x, requeing\n", msg.type);
put_msg(conn, &msg);
break;
}
}
}
return ret;
}
static void _rx_timeout(void *arg)
{
conn_can_isotp_t *conn = arg;
msg_t msg;
msg.type = _TIMEOUT_RX_MSG_TYPE;
msg.content.value = _TIMEOUT_MSG_VALUE;
put_msg(conn, &msg);
}
int conn_can_isotp_recv(conn_can_isotp_t *conn, void *buf, size_t size, uint32_t timeout)
{
assert(conn != NULL);
assert(buf != NULL);
int ret = 0;
gnrc_pktsnip_t *snip;
if (!conn->bound) {
return -ENOTCONN;
}
#ifdef MODULE_CONN_CAN_ISOTP_MULTI
if (conn->rx) {
snip = conn->rx->data.iov_base;
if (snip->size <= size) {
memcpy(buf, snip->data, snip->size);
ret = snip->size;
}
else {
ret = -EOVERFLOW;
}
isotp_free_rx(conn->rx);
conn->rx = NULL;
return ret;
}
#endif
xtimer_t timer;
if (timeout != 0) {
timer.callback = _rx_timeout;
timer.arg = conn;
xtimer_set(&timer, timeout);
}
msg_t msg;
can_rx_data_t *rx;
while (1) {
get_msg(conn, &msg);
switch (msg.type) {
case CAN_MSG_RX_INDICATION:
DEBUG("conn_can_isotp_recv: CAN_MSG_RX_INDICATION\n");
rx = msg.content.ptr;
snip = rx->data.iov_base;
#ifdef MODULE_CONN_CAN_ISOTP_MULTI
if (rx->arg != conn) {
mbox_put(&conn->master->mbox, &msg);
break;
}
#endif
if (timeout != 0) {
xtimer_remove(&timer);
}
if (snip->size <= size) {
memcpy(buf, snip->data, snip->size);
ret = snip->size;
}
else {
ret = -EOVERFLOW;
}
isotp_free_rx(rx);
return ret;
case _TIMEOUT_RX_MSG_TYPE:
DEBUG("conn_can_isotp_recv: _TIMEOUT_RX_MSG_TYPE\n");
if (msg.content.value == _TIMEOUT_MSG_VALUE) {
ret = -ETIMEDOUT;
}
else {
ret = -EINTR;
}
return ret;
case _CLOSE_CONN_MSG_TYPE:
DEBUG("conn_can_isotp_recv: _CLOSE_CONN_MSG_TYPE\n");
#ifdef MODULE_CONN_CAN_ISOTP_MULTI
if ((msg.content.ptr == conn) || (msg.content.ptr == conn->master)) {
#endif
if (timeout != 0) {
xtimer_remove(&timer);
}
return -ECONNABORTED;
#ifdef MODULE_CONN_CAN_ISOTP_MULTI
}
#endif
break;
default:
DEBUG("conn_can_isotp_recv: unexpected msg %x\n", msg.type);
if (timeout != 0) {
xtimer_remove(&timer);
}
ret = -EINTR;
return ret;
}
}
return ret;
}
int conn_can_isotp_close(conn_can_isotp_t *conn)
{
assert(conn != NULL);
msg_t msg;
DEBUG("conn_can_isotp_close: conn=%p, ifnum=%d\n",
(void *)conn, conn->ifnum);
if (!conn->bound) {
return -EALREADY;
}
#ifdef MODULE_CONN_CAN_ISOTP_MULTI
assert(conn->master != NULL);
if (conn->master != conn) {
mutex_lock(&conn->master->lock);
LL_DELETE(conn->master->next, (conn_can_isotp_slave_t *)conn);
mutex_unlock(&conn->master->lock);
}
else {
if (conn->master->next) {
return -EBUSY;
}
}
#endif
isotp_release(&conn->isotp);
#ifdef MODULE_CONN_CAN_ISOTP_MULTI
if (conn->rx) {
isotp_free_rx(conn->rx);
}
if (conn->master == conn) {
while (mbox_try_get(&conn->master->mbox, &msg)) {
if (msg.type == CAN_MSG_RX_INDICATION) {
DEBUG("conn_can_isotp_close: freeing %p\n", msg.content.ptr);
isotp_free_rx(msg.content.ptr);
}
}
}
#else
while (mbox_try_get(&conn->mbox, &msg)) {
if (msg.type == CAN_MSG_RX_INDICATION) {
DEBUG("conn_can_isotp_close: freeing %p\n", msg.content.ptr);
isotp_free_rx(msg.content.ptr);
}
}
#endif
msg.type = _CLOSE_CONN_MSG_TYPE;
msg.content.ptr = conn;
put_msg(conn, &msg);
conn->bound = 0;
return 0;
}
#ifdef MODULE_CONN_CAN_ISOTP_MULTI
int conn_can_isotp_select(conn_can_isotp_slave_t **conn, conn_can_isotp_t *master, uint32_t timeout)
{
assert(master != NULL);
assert(conn != NULL);
int ret;
xtimer_t timer;
if (timeout != 0) {
timer.callback = _rx_timeout;
timer.arg = master;
xtimer_set(&timer, timeout);
}
msg_t msg;
can_rx_data_t *rx;
mbox_get(&master->mbox, &msg);
if (timeout != 0) {
xtimer_remove(&timer);
}
switch (msg.type) {
case CAN_MSG_RX_INDICATION:
DEBUG("conn_can_isotp_select: CAN_MSG_RX_INDICATION\n");
rx = msg.content.ptr;
*conn = rx->arg;
(*conn)->rx = rx;
ret = 0;
break;
case _TIMEOUT_RX_MSG_TYPE:
DEBUG("conn_can_isotp_select: _TIMEOUT_MSG_VALUE\n");
if (msg.content.value == _TIMEOUT_MSG_VALUE) {
ret = -ETIMEDOUT;
}
else {
ret = -EINTR;
}
*conn = NULL;
break;
default:
DEBUG("conn_can_isotp_select: %d\n", msg.type);
*conn = NULL;
ret = -EINTR;
break;
}
return ret;
}
#endif /* MODULE_CONN_CAN_ISOTP_MULTI */
#else
typedef int dont_be_pedantic;
#endif /* MODULE_CAN_ISOTP */

279
sys/can/conn/raw.c Normal file
View File

@ -0,0 +1,279 @@
/*
* 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.
*/
/**
* @file
* @brief Implementation of raw CAN connection
*
* @author Vincent Dupont <vincent@otakeys.com>
*/
#include <errno.h>
#include <string.h>
#include "can/conn/raw.h"
#include "can/can.h"
#include "can/raw.h"
#include "timex.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
#include "xtimer.h"
#define _TIMEOUT_TX_MSG_TYPE (0x8000)
#define _TIMEOUT_RX_MSG_TYPE (0x8001)
#define _CLOSE_CONN_MSG_TYPE (0x8002)
#define _TIMEOUT_MSG_VALUE (0xABCDEFAB)
#ifndef CONN_CAN_RAW_TIMEOUT_TX_CONF
#define CONN_CAN_RAW_TIMEOUT_TX_CONF (1 * US_PER_SEC)
#endif
int conn_can_raw_create(conn_can_raw_t *conn, struct can_filter *filter, size_t count,
int ifnum, int flags)
{
assert(conn != NULL);
assert(ifnum < CAN_DLL_NUMOF);
DEBUG("conn_can_raw_create: create conn=%p, ifnum=%d flags=%d\n", (void *)conn, ifnum, flags);
mbox_init(&conn->mbox, conn->mbox_queue, CONN_CAN_RAW_MBOX_SIZE);
conn->flags = flags;
conn->count = 0;
conn->ifnum = ifnum;
if (flags & CONN_CAN_RECVONLY) {
can_opt_t opt;
opt.opt = CANOPT_STATE;
canopt_state_t state = CANOPT_STATE_LISTEN_ONLY;
opt.data = &state;
opt.data_len = sizeof(state);
int ret = raw_can_set_can_opt(ifnum, &opt);
if (ret < 0) {
return ret;
}
}
return conn_can_raw_set_filter(conn, filter, count);
}
int conn_can_raw_set_filter(conn_can_raw_t *conn, struct can_filter *filter, size_t count)
{
assert(conn != NULL);
assert(filter != NULL || count == 0);
DEBUG("conn_can_raw_set_filter: conn=%p, filter=%p, count=%d\n",
(void *)conn, (void *)filter, count);
DEBUG("conn_can_raw_set_filter: conn->filter=%p, conn->count=%d\n",
(void *)conn->filter, conn->count);
/* unset previous filters */
if (conn->count) {
for (size_t i = 0; i < conn->count; i++) {
DEBUG("conn_can_raw_set_filter: unsetting filter=0x%" PRIx32 ", mask=0x%" PRIx32 "\n",
conn->filter[i].can_id, conn->filter[i].can_mask);
raw_can_unsubscribe_rx_mbox(conn->ifnum, &conn->filter[i], &conn->mbox, conn);
}
}
for (size_t i = 0; i < count; i++) {
DEBUG("conn_can_raw_set_filter: setting filter=0x%" PRIx32 ", mask=0x%" PRIx32 "\n",
filter[i].can_id, filter[i].can_mask);
int ret = raw_can_subscribe_rx_mbox(conn->ifnum, &filter[i], &conn->mbox, conn);
if (ret < 0) {
DEBUG("conn_can_raw_set_filter: error setting filters %d\n", ret);
for (size_t j = 0; j < i; j++) {
DEBUG("conn_can_raw_set_filter: unsetting filter=0x%" PRIx32 ", mask=0x%" PRIx32 "\n",
filter[j].can_id, filter[j].can_mask);
raw_can_unsubscribe_rx_mbox(conn->ifnum, &filter[j], &conn->mbox, conn);
}
return ret;
}
}
conn->filter = filter;
conn->count = count;
return 0;
}
static void _tx_conf_timeout(void *arg)
{
conn_can_raw_t *conn = arg;
msg_t msg;
msg.type = _TIMEOUT_TX_MSG_TYPE;
msg.content.value = _TIMEOUT_MSG_VALUE;
mbox_put(&conn->mbox, &msg);
}
int conn_can_raw_send(conn_can_raw_t *conn, const struct can_frame *frame, int flags)
{
assert(conn != NULL);
assert(conn->ifnum < CAN_DLL_NUMOF);
assert((conn->flags & CONN_CAN_RECVONLY) == 0);
assert(frame != NULL);
int ret = 0;
int handle;
DEBUG("conn_can_raw_send: conn=%p, frame=%p, flags=%d\n",
(void *)conn, (void *)frame, flags);
if (flags & CONN_CAN_DONTWAIT) {
handle = ret = raw_can_send(conn->ifnum, frame, 0);
if (ret >= 0) {
ret = 0;
}
}
else {
xtimer_t timer;
timer.callback = _tx_conf_timeout;
timer.arg = conn;
xtimer_set(&timer, CONN_CAN_RAW_TIMEOUT_TX_CONF);
handle = raw_can_send_mbox(conn->ifnum, frame, &conn->mbox);
if (handle < 0) {
xtimer_remove(&timer);
return handle;
}
msg_t msg;
int timeout = 5;
while (1) {
mbox_get(&conn->mbox, &msg);
switch (msg.type) {
case CAN_MSG_TX_ERROR:
xtimer_remove(&timer);
return -EIO;
case CAN_MSG_TX_CONFIRMATION:
xtimer_remove(&timer);
if ((int)msg.content.value == handle) {
DEBUG("conn_can_raw_send: frame sent correctly\n");
return 0;
}
else {
raw_can_abort(conn->ifnum, handle);
return -EINTR;
}
break;
case _TIMEOUT_TX_MSG_TYPE:
DEBUG("conn_can_raw_send: timeout\n");
return -ETIMEDOUT;
break;
default:
DEBUG("conn_can_raw_send: unexpected msg=%x, requeing\n", msg.type);
mbox_put(&conn->mbox, &msg);
if (!timeout--) {
return -EINTR;
}
break;
}
}
}
return ret;
}
static void _rx_timeout(void *arg)
{
conn_can_raw_t *conn = arg;
msg_t msg;
msg.type = _TIMEOUT_RX_MSG_TYPE;
msg.content.value = _TIMEOUT_MSG_VALUE;
mbox_put(&conn->mbox, &msg);
}
int conn_can_raw_recv(conn_can_raw_t *conn, struct can_frame *frame, uint32_t timeout)
{
assert(conn != NULL);
assert(conn->ifnum < CAN_DLL_NUMOF);
assert(frame != NULL);
xtimer_t timer;
if (timeout != 0) {
timer.callback = _rx_timeout;
timer.arg = conn;
xtimer_set(&timer, timeout);
}
int ret;
msg_t msg;
can_rx_data_t *rx;
mbox_get(&conn->mbox, &msg);
if (timeout != 0) {
xtimer_remove(&timer);
}
switch (msg.type) {
case CAN_MSG_RX_INDICATION:
DEBUG("conn_can_raw_recv: CAN_MSG_RX_INDICATION\n");
rx = msg.content.ptr;
memcpy(frame, rx->data.iov_base, rx->data.iov_len);
ret = rx->data.iov_len;
raw_can_free_frame(rx);
break;
case _TIMEOUT_RX_MSG_TYPE:
if (msg.content.value == _TIMEOUT_MSG_VALUE) {
ret = -ETIMEDOUT;
}
else {
ret = -EINTR;
}
break;
case _CLOSE_CONN_MSG_TYPE:
if (msg.content.ptr == conn) {
ret = -ECONNABORTED;
}
else {
ret = -EINTR;
}
break;
default:
mbox_put(&conn->mbox, &msg);
ret = -EINTR;
break;
}
return ret;
}
int conn_can_raw_close(conn_can_raw_t *conn)
{
assert(conn != NULL);
assert(conn->ifnum < CAN_DLL_NUMOF);
DEBUG("conn_can_raw_close: conn=%p\n", (void *)conn);
if (conn->count) {
for (size_t i = 0; i < conn->count; i++) {
DEBUG("conn_can_raw_close: unsetting filter=0x%" PRIx32 ", mask=0x%" PRIx32 "\n",
conn->filter[i].can_id, conn->filter[i].can_mask);
raw_can_unsubscribe_rx_mbox(conn->ifnum, &conn->filter[i], &conn->mbox, conn);
}
conn->count = 0;
msg_t msg;
while (mbox_try_get(&conn->mbox, &msg)) {
if (msg.type == CAN_MSG_RX_INDICATION) {
DEBUG("conn_can_raw_close: incoming msg pending, freeing\n");
raw_can_free_frame(msg.content.ptr);
}
}
msg.type = _CLOSE_CONN_MSG_TYPE;
msg.content.ptr = conn;
mbox_try_put(&conn->mbox, &msg);
}
return 0;
}

590
sys/can/device.c Normal file
View File

@ -0,0 +1,590 @@
/*
* 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.
*/
/**
* @file
* @brief CAN device interface
*
* @author Toon Stegen <toon.stegen@altran.com>
* @author Vincent Dupont <vincent@otakeys.com>
* @author Aurelien Gonce <aurelien.gonce@altran.com>
*/
#include <errno.h>
#include "thread.h"
#include "can/device.h"
#include "can/common.h"
#include "can/pkt.h"
#include "can/dll.h"
#ifdef MODULE_CAN_TRX
#include "can/can_trx.h"
#endif
#define ENABLE_DEBUG (0)
#include "debug.h"
#ifndef CAN_DEVICE_MSG_QUEUE_SIZE
#define CAN_DEVICE_MSG_QUEUE_SIZE 64
#endif
#ifdef MODULE_CAN_PM
#define CAN_DEVICE_PM_DEFAULT_RX_TIMEOUT (10 * US_PER_SEC)
#define CAN_DEVICE_PM_DEFAULT_TX_TIMEOUT (2 * US_PER_SEC)
#endif
static int power_up(candev_dev_t *candev_dev);
static int power_down(candev_dev_t *candev_dev);
#ifdef MODULE_CAN_PM
static void pm_cb(void *arg);
static void pm_reset(candev_dev_t *candev_dev, uint32_t value);
#endif
static inline enum can_msg _can_event_error_to_msg(candev_event_t error)
{
switch (error) {
case CANDEV_EVENT_TX_ERROR:
return CAN_MSG_TX_ERROR;
case CANDEV_EVENT_RX_ERROR:
return CAN_MSG_RX_ERROR;
case CANDEV_EVENT_BUS_OFF:
return CAN_MSG_BUS_OFF;
case CANDEV_EVENT_ERROR_PASSIVE:
return CAN_MSG_ERROR_PASSIVE;
case CANDEV_EVENT_ERROR_WARNING:
return CAN_MSG_ERROR_WARNING;
default:
return 0;
}
}
static void _can_event(candev_t *dev, candev_event_t event, void *arg)
{
msg_t msg;
struct can_frame *frame;
can_pkt_t *pkt;
candev_dev_t *candev_dev = dev->isr_arg;
DEBUG("_can_event: dev=%p, params=%p\n", (void*)dev, (void*)candev_dev);
DEBUG("_can_event: params->ifnum=%d, params->pid=%" PRIkernel_pid ", params->dev=%p\n",
candev_dev->ifnum, candev_dev->pid, (void*)candev_dev->dev);
switch (event) {
case CANDEV_EVENT_ISR:
DEBUG("_can_event: CANDEV_EVENT_ISR\n");
msg.type = CAN_MSG_EVENT;
if (msg_send(&msg, candev_dev->pid) <= 0) {
DEBUG("can device: isr lost\n");
}
break;
case CANDEV_EVENT_WAKE_UP:
DEBUG("_can_event: CANDEV_EVENT_WAKE_UP\n");
power_up(candev_dev);
#ifdef MODULE_CAN_PM
pm_reset(candev_dev, candev_dev->rx_inactivity_timeout);
#endif
break;
case CANDEV_EVENT_TX_CONFIRMATION:
DEBUG("_can_event: CANDEV_EVENT_TX_CONFIRMATION\n");
/* frame pointer in arg */
pkt = container_of((struct can_frame *)arg, can_pkt_t, frame);
can_dll_dispatch_tx_conf(pkt);
break;
case CANDEV_EVENT_TX_ERROR:
DEBUG("_can_event: CANDEV_EVENT_TX_ERROR\n");
/* frame pointer in arg */
pkt = container_of((struct can_frame *)arg, can_pkt_t, frame);
can_dll_dispatch_tx_error(pkt);
break;
case CANDEV_EVENT_RX_INDICATION:
DEBUG("_can_event: CANDEV_EVENT_RX_INDICATION\n");
#ifdef MODULE_CAN_PM
pm_reset(candev_dev, candev_dev->rx_inactivity_timeout);
#endif
/* received frame in arg */
frame = (struct can_frame *) arg;
can_dll_dispatch_rx_frame(frame, candev_dev->pid);
break;
case CANDEV_EVENT_RX_ERROR:
DEBUG("_can_event: CANDEV_EVENT_RX_ERROR\n");
break;
case CANDEV_EVENT_BUS_OFF:
dev->state = CAN_STATE_BUS_OFF;
break;
case CANDEV_EVENT_ERROR_PASSIVE:
dev->state = CAN_STATE_ERROR_PASSIVE;
break;
case CANDEV_EVENT_ERROR_WARNING:
dev->state = CAN_STATE_ERROR_WARNING;
break;
default:
DEBUG("_can_event: unknown event\n");
break;
}
}
static int power_up(candev_dev_t *candev_dev)
{
candev_t *dev = candev_dev->dev;
DEBUG("candev: power up\n");
#ifdef MODULE_CAN_TRX
can_trx_set_mode(candev_dev->trx, TRX_NORMAL_MODE);
#endif
canopt_state_t state = CANOPT_STATE_ON;
int res = dev->driver->set(dev, CANOPT_STATE, &state, sizeof(state));
dev->state = CAN_STATE_ERROR_ACTIVE;
return res;
}
static int power_down(candev_dev_t *candev_dev)
{
candev_t *dev = candev_dev->dev;
DEBUG("candev: power down\n");
#ifdef MODULE_CAN_TRX
can_trx_set_mode(candev_dev->trx, TRX_SLEEP_MODE);
#endif
canopt_state_t state = CANOPT_STATE_SLEEP;
int res = dev->driver->set(dev, CANOPT_STATE, &state, sizeof(state));
dev->state = CAN_STATE_SLEEPING;
#ifdef MODULE_CAN_PM
xtimer_remove(&candev_dev->pm_timer);
candev_dev->last_pm_update = 0;
#endif
return res;
}
#ifdef MODULE_CAN_PM
static void pm_cb(void *arg)
{
candev_dev_t *dev = arg;
msg_t msg;
msg.type = CAN_MSG_PM;
msg_send(&msg, dev->pid);
}
static void pm_reset(candev_dev_t *candev_dev, uint32_t value)
{
DEBUG("pm_reset: dev=%p, value=%" PRIu32 ", last_pm_value=%" PRIu32
", last_pm_update=%" PRIu32 "\n", (void *)candev_dev, value,
candev_dev->last_pm_value, candev_dev->last_pm_update);
if (value == 0) {
candev_dev->last_pm_value = 0;
xtimer_remove(&candev_dev->pm_timer);
return;
}
if (candev_dev->last_pm_update == 0 ||
value > (candev_dev->last_pm_value - (xtimer_now_usec() - candev_dev->last_pm_update))) {
candev_dev->last_pm_value = value;
candev_dev->last_pm_update = xtimer_now_usec();
xtimer_set(&candev_dev->pm_timer, value);
}
}
#endif
static void *_can_device_thread(void *args)
{
candev_dev_t *candev_dev = (candev_dev_t *) args;
candev_t *dev = candev_dev->dev;
DEBUG("_can_device_thread: starting thread for ifnum=%d, pid=%" PRIkernel_pid "\n",
candev_dev->ifnum, thread_getpid());
DEBUG("_cand_device_thread: dev=%p, params=%p\n", (void*)dev, (void*)candev_dev);
candev_dev->pid = thread_getpid();
#ifdef MODULE_CAN_PM
if (candev_dev->rx_inactivity_timeout == 0) {
candev_dev->rx_inactivity_timeout = CAN_DEVICE_PM_DEFAULT_RX_TIMEOUT;
}
if (candev_dev->tx_wakeup_timeout == 0) {
candev_dev->tx_wakeup_timeout = CAN_DEVICE_PM_DEFAULT_TX_TIMEOUT;
}
candev_dev->pm_timer.callback = pm_cb;
candev_dev->pm_timer.arg = candev_dev;
pm_reset(candev_dev, candev_dev->rx_inactivity_timeout);
#endif
#ifdef MODULE_CAN_TRX
can_trx_init(candev_dev->trx);
#endif
int res;
can_pkt_t *pkt;
can_opt_t *opt;
msg_t msg, reply, msg_queue[CAN_DEVICE_MSG_QUEUE_SIZE];
/* setup the device layers message queue */
msg_init_queue(msg_queue, CAN_DEVICE_MSG_QUEUE_SIZE);
dev->event_callback = _can_event;
dev->isr_arg = candev_dev;
candev_dev->ifnum = can_dll_register_candev(candev_dev);
dev->driver->init(dev);
dev->state = CAN_STATE_ERROR_ACTIVE;
while (1) {
msg_receive(&msg);
switch (msg.type) {
case CAN_MSG_EVENT:
DEBUG("can device: CAN_MSG_EVENT received\n");
dev->driver->isr(dev);
break;
case CAN_MSG_ABORT_FRAME:
DEBUG("can device: CAN_MSG_ABORT_FRAME received\n");
pkt = (can_pkt_t *) msg.content.ptr;
dev->driver->abort(dev, &pkt->frame);
reply.type = CAN_MSG_ACK;
reply.content.value = 0;
msg_reply(&msg, &reply);
break;
case CAN_MSG_SEND_FRAME:
DEBUG("can device: CAN_MSG_SEND_FRAME received\n");
pkt = (can_pkt_t *) msg.content.ptr;
if (dev->state == CAN_STATE_BUS_OFF || dev->state == CAN_STATE_SLEEPING) {
DEBUG("can device: waking up driver\n");
power_up(candev_dev);
}
#ifdef MODULE_CAN_PM
pm_reset(candev_dev, candev_dev->tx_wakeup_timeout);
#endif
dev->driver->send(dev, &pkt->frame);
break;
case CAN_MSG_SET:
DEBUG("can device: CAN_MSG_SET received\n");
/* read incoming options */
opt = (can_opt_t *)msg.content.ptr;
/* set option for device driver */
res = dev->driver->set(dev, opt->opt, opt->data, opt->data_len);
/* send reply to calling thread */
reply.type = CAN_MSG_ACK;
reply.content.value = (uint32_t)res;
msg_reply(&msg, &reply);
break;
case CAN_MSG_GET:
DEBUG("can device: CAN_MSG_GET received\n");
/* read incoming options */
opt = (can_opt_t *)msg.content.ptr;
/* get option for device driver */
res = dev->driver->get(dev, opt->opt, opt->data, opt->data_len);
/* send reply to calling thread */
reply.type = CAN_MSG_ACK;
reply.content.value = (uint32_t)res;
msg_reply(&msg, &reply);
break;
case CAN_MSG_SET_FILTER:
DEBUG("can device: CAN_MSG_SET_FILTER received\n");
/* set filter for device driver */
res = dev->driver->set_filter(dev, msg.content.ptr);
/* send reply to calling thread */
reply.type = CAN_MSG_ACK;
reply.content.value = (uint32_t)res;
msg_reply(&msg, &reply);
break;
case CAN_MSG_REMOVE_FILTER:
DEBUG("can device: CAN_MSG_REMOVE_FILTER received\n");
/* set filter for device driver */
res = dev->driver->remove_filter(dev, msg.content.ptr);
/* send reply to calling thread */
reply.type = CAN_MSG_ACK;
reply.content.value = (uint32_t)res;
msg_reply(&msg, &reply);
break;
case CAN_MSG_POWER_UP:
DEBUG("can device: CAN_MSG_POWER_UP received\n");
res = power_up(candev_dev);
#ifdef MODULE_CAN_PM
pm_reset(candev_dev, 0);
#endif
/* send reply to calling thread */
reply.type = CAN_MSG_ACK;
reply.content.value = (uint32_t)res;
msg_reply(&msg, &reply);
break;
case CAN_MSG_POWER_DOWN:
DEBUG("can device: CAN_MSG_POWER_DOWN received\n");
res = power_down(candev_dev);
/* send reply to calling thread */
reply.type = CAN_MSG_ACK;
reply.content.value = (uint32_t)res;
msg_reply(&msg, &reply);
break;
#ifdef MODULE_CAN_TRX
case CAN_MSG_SET_TRX:
DEBUG("can device: CAN_MSG_SET_TRX received\n");
reply.type = CAN_MSG_ACK;
if (dev->state != CAN_STATE_SLEEPING) {
reply.content.value = -EBUSY;
}
else {
candev_dev->trx = msg.content.ptr;
reply.content.value = 0;
}
msg_reply(&msg, &reply);
break;
#endif
#ifdef MODULE_CAN_PM
case CAN_MSG_PM:
DEBUG("can device: pm power down\n");
power_down(candev_dev);
break;
#endif
default:
break;
}
}
return NULL;
}
kernel_pid_t can_device_init(char *stack, int stacksize, char priority,
const char *name, candev_dev_t *params)
{
kernel_pid_t res;
/* check if given device is defined and the driver is set */
if (params == NULL || params->dev == NULL || params->dev->driver == NULL) {
return -ENODEV;
}
/* create new can device thread */
res = thread_create(stack, stacksize, priority, THREAD_CREATE_STACKTEST,
_can_device_thread, (void *)params, name);
if (res <= 0) {
return -EINVAL;
}
return res;
}
#define SJW 2
#define CAN_SYNC_SEG 1
static inline uint32_t min(uint32_t x, uint32_t y)
{
return x < y ? x : y;
}
static inline uint32_t max(uint32_t x, uint32_t y)
{
return x > y ? x : y;
}
static inline uint32_t clamp(uint32_t val, uint32_t lo, uint32_t hi)
{
return min(max(val, lo), hi);
}
/**
* @brief Compute tseg1 and tseg2 and returns the sample point
*
* tseg1 and tseg2 are calculated from the nominal sample point and tseg
*
* @param[in] btc the bittiming const
* @param[in] spt_nominal the nominal sample point
* @param[in] tseg number of tq in the nbt minus the SYNC_SEG
* @param[out] p_tseg1 number of tq in tseg1 (PHASE_SEG_1 + PROP_SEG)
* @param[out] p_tseg2 number of tq in tseg2 (PHASE_SEG_2)
* @param[out] p_spt_error (optional) the sample point difference between @p spt_nominal
* and computed sample point from @p tseg1 and @p tseg2
*
* @return the computed sample point from @p tseg1 and @p tseg2
*/
static uint32_t update_sample_point(const struct can_bittiming_const *btc, uint32_t spt_nominal,
uint32_t tseg, uint32_t *p_tseg1, uint32_t *p_tseg2, uint32_t *p_spt_error)
{
uint32_t best_spt = 0;
uint32_t min_spt_error = UINT32_MAX;
for (int i = 0; i <= 1; i++) {
uint32_t tseg1;
uint32_t tseg2;
uint32_t spt;
uint32_t spt_error;
tseg2 = tseg + CAN_SYNC_SEG - (spt_nominal * (tseg + CAN_SYNC_SEG)) / 1000 - i;
tseg2 = clamp(tseg2, btc->tseg2_min, btc->tseg2_max);
tseg1 = tseg - tseg2;
if (tseg1 > btc->tseg1_max) {
tseg1 = btc->tseg1_max;
tseg2 = tseg - tseg1;
}
spt = 1000 * (tseg1 + CAN_SYNC_SEG) / (tseg + CAN_SYNC_SEG);
spt_error = max(spt, spt_nominal) - min(spt, spt_nominal);
if (spt <= spt_nominal && spt_error < min_spt_error) {
best_spt = spt;
min_spt_error = spt_error;
*p_tseg1 = tseg1;
*p_tseg2 = tseg2;
}
if (p_spt_error) {
*p_spt_error = min_spt_error;
}
DEBUG("tseg1=%" PRIu32 ", tseg2=%" PRIu32 ", spt_error=%" PRIu32 "\n",
tseg1, tseg2, spt_error);
}
return best_spt;
}
/*
* Nominal bit time (nbt) composed of 8 time quantum (tq)
* |<------------------------------------------------------------------------------------->|
* | |
* +----------+----------+-------------------------------------------+---------------------+
* | SYNC_SEG | PROP_SEG | PHASE_SEG_1 | PHASE_SEG_2 |
* +----------+----------+-------------------------------------------+---------------------+
* | ^ |
* | Sample point | at 75% |
* |----------|----------|----------|----------|----------|----------|----------|----------|
* | Time quanta 6 | 2 |
*
* Synchronization segment = always 1 tq
* SYNC_SEG + PROP_SEG + PHASE_SEG1
* Sample point = --------------------------------
* nbt
*
* tseg1 = PROP_SEG + PHASE_SEG_1
* tseg2 = PHASE_SEG_2
* tseg = tseg1 + tseg2
* nbt = tseg + SYNC_SEG
*
*/
int can_device_calc_bittiming(uint32_t clock, const struct can_bittiming_const *timing_const,
struct can_bittiming *timing)
{
uint32_t spt; /* nominal sample point, in one-tenth of a percent */
uint32_t spt_error;
uint32_t min_spt_error = UINT32_MAX;
uint32_t best_brp = 0;
uint32_t tseg;
uint32_t tseg1;
uint32_t tseg2;
uint32_t best_tseg = 0;
uint32_t rate; /* current bitrate */
uint32_t rate_error;
uint32_t min_rate_error;
assert((timing != NULL) && (timing->bitrate != 0));
assert(timing_const != NULL);
if (timing->sample_point) {
spt = timing->sample_point;
}
else {
/* Use recommended sample points */
/* See CiA 301 (https://www.can-cia.org/standardization/technical-documents/) */
/* 87.5% is recommended from 10kbit/s to 1Mbit/s */
spt = 875;
}
rate_error = min_rate_error = timing->bitrate;
DEBUG("init_bittiming: rate=%" PRIu32 ", clock=%" PRIu32 ", spt=%" PRIu32 "\n",
timing->bitrate, clock, timing->sample_point);
/* Starting from higher tq per nbt */
for (tseg = timing_const->tseg1_max + timing_const->tseg2_max;
tseg >= timing_const->tseg1_min + timing_const->tseg2_min; tseg--) {
uint32_t nbt = tseg + CAN_SYNC_SEG;
/* theoritical brp */
uint32_t brp = clock / (timing->bitrate * nbt);
/* brp according to brp_inc */
brp = (brp / timing_const->brp_inc) * timing_const->brp_inc;
DEBUG("tsegall=%" PRIu32 ", brp=%" PRIu32 "\n", nbt, brp);
if (brp < timing_const->brp_min || brp > timing_const->brp_max) {
/* Invalid brp */
DEBUG("invalid brp\n");
continue;
}
rate = clock / (brp * nbt);
rate_error = max(timing->bitrate, rate) - min(timing->bitrate, rate);
if (rate_error > min_rate_error) {
DEBUG("timing->rate=%" PRIu32 ", rate=%" PRIu32 ", rate_error=%" PRIu32 " > min_rate_error=%" PRIu32 ", continuing\n",
timing->bitrate, rate, rate_error, min_rate_error);
continue;
}
if (rate_error < min_rate_error) {
min_spt_error = UINT32_MAX;
}
update_sample_point(timing_const, spt, tseg, &tseg1, &tseg2, &spt_error);
if (spt_error > min_spt_error) {
DEBUG("spt_error=%" PRIu32 " > min_spt_error=%" PRIu32 ", continuing\n",
spt_error, min_spt_error);
continue;
}
min_spt_error = spt_error;
min_rate_error = rate_error;
best_tseg = tseg;
best_brp = brp;
DEBUG("rate_error=%" PRIu32 ", spt_error=%" PRIu32 "\n", rate_error, spt_error);
if (rate_error == 0 && spt_error == 0) {
break;
}
}
DEBUG("computed values: min_rate_error=%" PRIu32 ", min_spt_error=%" PRIu32 "\n", min_rate_error, min_spt_error);
if (min_rate_error) {
rate_error = min_rate_error * 1000 / timing->bitrate;
if (rate_error > CAN_MAX_RATE_ERROR) {
return -1;
}
}
timing->sample_point = update_sample_point(timing_const, spt,
best_tseg, &tseg1, &tseg2, NULL);
timing->prop_seg = tseg1 / 2;
timing->phase_seg1 = tseg1 - timing->prop_seg;
timing->phase_seg2 = tseg2;
if (!timing->sjw || !timing_const->sjw_max) {
timing->sjw = SJW;
}
else {
if (timing->sjw > timing_const->sjw_max) {
timing->sjw = timing_const->sjw_max;
}
if (timing->sjw > tseg2) {
timing->sjw = tseg2;
}
}
timing->brp = best_brp;
timing->bitrate = clock / (timing->brp * (CAN_SYNC_SEG + tseg1 + tseg2));
DEBUG("bitrate=%" PRIu32 ", sample_point=%" PRIu32 ", brp=%" PRIu32 ", prop_seg=%" PRIu32
", phase_seg1=%" PRIu32 ", phase_seg2=%" PRIu32 "\n", timing->bitrate, timing->sample_point,
timing->brp, timing->prop_seg, timing->phase_seg1, timing->phase_seg2);
return 0;
}

521
sys/can/dll.c Normal file
View File

@ -0,0 +1,521 @@
/*
* 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.
*/
/**
* @file
* @brief CAN Data Link Layer module
*
* This module contains the DLL interfaces for upper layer (raw_can_*)
* and devices (can_dll_*).
* It manages the connection between an device number and its candev thread.
*
*
* @author Toon Stegen <toon.stegen@altran.com>
* @author Vincent Dupont <vincent@otakeys.com>
* @author Aurelien Gonce <aurelien.gonce@altran.com>
*/
#include <errno.h>
#include <string.h>
#include "thread.h"
#include "can/dll.h"
#include "can/raw.h"
#include "can/device.h"
#include "can/pkt.h"
#include "can/common.h"
#include "can/router.h"
#include "utlist.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static candev_dev_t *candev_list[CAN_DLL_NUMOF];
static int candev_nb = 0;
static can_reg_entry_t *tx_list[CAN_DLL_NUMOF];
static mutex_t tx_lock = MUTEX_INIT;
static int _get_ifnum(kernel_pid_t pid)
{
for (int i = 0; i < candev_nb; i++) {
if (candev_list[i]->pid == pid) {
return i;
}
}
return -ENODEV;
}
int _send_pkt(can_pkt_t *pkt)
{
if (!pkt) {
return -ENOMEM;
}
msg_t msg;
int handle = pkt->handle;
mutex_lock(&tx_lock);
LL_APPEND(tx_list[pkt->entry.ifnum], &pkt->entry);
mutex_unlock(&tx_lock);
msg.type = CAN_MSG_SEND_FRAME;
msg.content.ptr = (void*) pkt;
if (msg_send(&msg, candev_list[pkt->entry.ifnum]->pid) <= 0) {
return -EOVERFLOW;
}
return handle;
}
int raw_can_send(int ifnum, const struct can_frame *frame, kernel_pid_t pid)
{
can_pkt_t *pkt;
assert(frame);
assert(ifnum < candev_nb);
pkt = can_pkt_alloc_tx(ifnum, frame, pid);
DEBUG("raw_can_send: ifnum=%d, id=0x%" PRIx32 " from pid=%" PRIkernel_pid ", handle=%d\n",
ifnum, frame->can_id, pid, pkt->handle);
return _send_pkt(pkt);
}
#ifdef MODULE_CAN_MBOX
int raw_can_send_mbox(int ifnum, const struct can_frame *frame, mbox_t *mbox)
{
can_pkt_t *pkt;
assert(frame);
assert(ifnum < candev_nb);
pkt = can_pkt_alloc_mbox_tx(ifnum, frame, mbox);
DEBUG("raw_can_send: ifnum=%d, id=0x%" PRIx32 ", handle=%d\n", ifnum, frame->can_id, pkt->handle);
return _send_pkt(pkt);
}
#endif
int raw_can_abort(int ifnum, int handle)
{
msg_t msg, reply;
can_pkt_t *pkt = NULL;
can_reg_entry_t *entry;
assert(ifnum < candev_nb);
DEBUG("raw_can_abort: ifnum=%u, handle=%d\n", ifnum, handle);
mutex_lock(&tx_lock);
LL_FOREACH(tx_list[ifnum], entry) {
pkt = container_of(entry, can_pkt_t, entry);
if (pkt->handle == handle) {
break;
}
}
LL_DELETE(tx_list[ifnum], entry);
mutex_unlock(&tx_lock);
if (pkt == NULL) {
DEBUG("raw_can_abort: no pkt\n");
return -ENODEV;
}
msg.type = CAN_MSG_ABORT_FRAME;
msg.content.ptr = pkt;
msg_send_receive(&msg, &reply, candev_list[ifnum]->pid);
can_pkt_free(pkt);
return 0;
}
static int register_filter_entry(can_reg_entry_t *entry, struct can_filter *filter, void *param)
{
msg_t msg, reply;
int ret;
DEBUG("register_filter_entry: ifnum=%d, filter=0x%" PRIx32 ", mask=0x%" PRIx32 ", param=%p\n",
entry->ifnum, filter->can_id, filter->can_mask, param);
ret = can_router_register(entry, filter->can_id, filter->can_mask, param);
if (ret < 0) {
return -ENOMEM;
}
else if (ret == 1) {
DEBUG("raw_can_subscribe_rx: filter=0x%" PRIx32 " already in use\n", filter->can_id);
return 0;
}
msg.type = CAN_MSG_SET_FILTER;
msg.content.ptr = filter;
msg_send_receive(&msg, &reply, candev_list[entry->ifnum]->pid);
if ((int) reply.content.value < 0) {
can_router_unregister(entry, filter->can_id, filter->can_mask, param);
return -ENOMEM;
}
return 0;
}
static int unregister_filter_entry(can_reg_entry_t *entry, struct can_filter *filter, void *param)
{
msg_t msg, reply;
int ret;
DEBUG("unregister_filter_entry: ifnum=%d, filter=0x%" PRIx32 ", mask=0x%" PRIx32 ", param=%p\n",
entry->ifnum, filter->can_id, filter->can_mask, param);
ret = can_router_unregister(entry, filter->can_id, filter->can_mask, param);
if (ret < 0) {
return -ENOMEM;
}
else if (ret == 1) {
DEBUG("raw_can_unsubscribe_rx: filter=0x%" PRIx32 " still in use\n", filter->can_id);
return 0;
}
msg.type = CAN_MSG_REMOVE_FILTER;
msg.content.ptr = filter;
msg_send_receive(&msg, &reply, candev_list[entry->ifnum]->pid);
if ((int) reply.content.value < 0) {
return -ENOMEM;
}
return 0;
}
int raw_can_subscribe_rx(int ifnum, struct can_filter *filter, kernel_pid_t pid, void *param)
{
assert(ifnum < candev_nb);
assert(filter);
can_reg_entry_t entry;
entry.ifnum = ifnum;
entry.target.pid = pid;
#ifdef MODULE_CAN_MBOX
entry.type = CAN_TYPE_DEFAULT;
#endif
return register_filter_entry(&entry, filter, param);
}
#ifdef MODULE_CAN_MBOX
int raw_can_subscribe_rx_mbox(int ifnum, struct can_filter *filter, mbox_t *mbox, void *param)
{
assert(ifnum < candev_nb);
assert(filter);
can_reg_entry_t entry;
entry.ifnum = ifnum;
entry.target.mbox = mbox;
entry.type = CAN_TYPE_MBOX;
return register_filter_entry(&entry, filter, param);
}
#endif
int raw_can_unsubscribe_rx(int ifnum, struct can_filter *filter, kernel_pid_t pid, void *param)
{
assert(ifnum < candev_nb);
assert(filter);
can_reg_entry_t entry;
entry.ifnum = ifnum;
entry.target.pid = pid;
#ifdef MODULE_CAN_MBOX
entry.type = CAN_TYPE_DEFAULT;
#endif
return unregister_filter_entry(&entry, filter, param);
}
#ifdef MODULE_CAN_MBOX
int raw_can_unsubscribe_rx_mbox(int ifnum, struct can_filter *filter, mbox_t *mbox, void *param)
{
assert(ifnum < candev_nb);
assert(filter);
can_reg_entry_t entry;
entry.ifnum = ifnum;
entry.target.mbox = mbox;
entry.type = CAN_TYPE_MBOX;
return unregister_filter_entry(&entry, filter, param);
}
#endif
int raw_can_free_frame(can_rx_data_t *frame)
{
int ret = can_router_free_frame((struct can_frame *)frame->data.iov_base);
can_pkt_free_rx_data(frame);
return ret;
}
int raw_can_get_can_opt(int ifnum, can_opt_t *opt)
{
msg_t msg, reply;
assert(ifnum < CAN_DLL_NUMOF);
if (!opt) {
return -ENOMEM;
}
opt->context = (uint16_t)candev_list[ifnum]->pid;
msg.type = CAN_MSG_GET;
msg.content.ptr = (void *)opt;
if (msg_send_receive(&msg, &reply, opt->context) != 1) {
return -EBUSY;
}
return (int) reply.content.value;
}
int raw_can_set_can_opt(int ifnum, can_opt_t *opt)
{
msg_t msg, reply;
assert(ifnum < CAN_DLL_NUMOF);
if (!opt) {
return -ENOMEM;
}
opt->context = (uint16_t)candev_list[ifnum]->pid;
msg.type = CAN_MSG_SET;
msg.content.ptr = (void *)opt;
if (msg_send_receive(&msg, &reply, opt->context) != 1) {
return -EBUSY;
}
return (int) reply.content.value;
}
int can_dll_register_candev(candev_dev_t *candev)
{
if (candev_nb >= CAN_DLL_NUMOF) {
return -ENODEV;
}
DEBUG("can_dll_register_candev: candev=%p, ifnum=%d, pid=%" PRIkernel_pid "\n",
(void *)candev, candev_nb, candev->pid);
candev_list[candev_nb] = candev;
return candev_nb++;
}
int can_dll_dispatch_rx_frame(struct can_frame *frame, kernel_pid_t pid)
{
can_pkt_t *pkt = can_pkt_alloc_rx(_get_ifnum(pid), frame);
return can_router_dispatch_rx_indic(pkt);
}
int can_dll_dispatch_tx_conf(can_pkt_t *pkt)
{
DEBUG("can_dll_dispatch_tx_conf: pkt=0x%p\n", (void*)pkt);
can_router_dispatch_tx_conf(pkt);
mutex_lock(&tx_lock);
LL_DELETE(tx_list[pkt->entry.ifnum], &pkt->entry);
mutex_unlock(&tx_lock);
can_pkt_free(pkt);
return 0;
}
int can_dll_dispatch_tx_error(can_pkt_t *pkt)
{
DEBUG("can_dll_dispatch_tx_error: pkt=0x%p\n", (void*)pkt);
can_router_dispatch_tx_error(pkt);
mutex_lock(&tx_lock);
LL_DELETE(tx_list[pkt->entry.ifnum], &pkt->entry);
mutex_unlock(&tx_lock);
can_pkt_free(pkt);
return 0;
}
int can_dll_dispatch_bus_off(kernel_pid_t pid)
{
int ifnum = _get_ifnum(pid);
can_reg_entry_t *entry = tx_list[ifnum];
DEBUG("can_dll_dispatch_bus_off: ifnum=%d, pid=%" PRIkernel_pid "\n", ifnum, pid);
mutex_lock(&tx_lock);
while (entry) {
can_pkt_t *pkt = container_of(entry, can_pkt_t, entry);
can_router_dispatch_tx_error(pkt);
LL_DELETE(tx_list[ifnum], entry);
entry = tx_list[ifnum];
}
mutex_unlock(&tx_lock);
return 0;
}
int can_dll_init(void)
{
can_pkt_init();
return 0;
}
int raw_can_power_down(int ifnum)
{
msg_t msg, reply;
assert(ifnum < candev_nb);
msg.type = CAN_MSG_POWER_DOWN;
if (msg_send_receive(&msg, &reply, candev_list[ifnum]->pid) != 1) {
return -EBUSY;
}
return (int) reply.content.value;
}
int raw_can_power_up(int ifnum)
{
msg_t msg, reply;
assert(ifnum < candev_nb);
msg.type = CAN_MSG_POWER_UP;
if (msg_send_receive(&msg, &reply, candev_list[ifnum]->pid) != 1) {
return -EBUSY;
}
return (int) reply.content.value;
}
int raw_can_set_bitrate(int ifnum, uint32_t bitrate, uint32_t sample_point)
{
assert(ifnum < candev_nb);
int res = 0;
int ret;
uint32_t clock;
struct can_bittiming_const btc;
struct can_bittiming bittiming;
bittiming.bitrate = bitrate;
bittiming.sample_point = sample_point;
can_opt_t opt;
opt.opt = CANOPT_CLOCK;
opt.data = &clock;
opt.data_len = sizeof(clock);
ret = raw_can_get_can_opt(ifnum, &opt);
if (ret < 0) {
DEBUG("raw_can_set_bitrate: error when getting clock (%d)\n", ret);
return -1;
}
DEBUG("raw_can_set_bitrate: clock=%" PRIu32 " Hz\n", clock);
opt.opt = CANOPT_BITTIMING_CONST;
opt.data = &btc;
opt.data_len = sizeof(btc);
ret = raw_can_get_can_opt(ifnum, &opt);
if (ret < 0) {
DEBUG("raw_can_set_bitrate: error when getting const (%d)\n", ret);
return -1;
}
ret = can_device_calc_bittiming(clock, &btc, &bittiming);
if (ret < 0) {
DEBUG("raw_can_set_bitrate: bittiming might be wrong, ret=%d\n", ret);
res = 1;
}
opt.data = &bittiming;
opt.data_len = sizeof(bittiming);
opt.opt = CANOPT_BITTIMING;
ret = raw_can_set_can_opt(ifnum, &opt);
if (ret < 0) {
DEBUG("raw_can_set_bitrate: error when setting bitrate (%d)\n", ret);
return -1;
}
DEBUG("raw_can_set_bitrate: success bitrate=%" PRIu32 ", spt=%" PRIu32 "\n",
bittiming.bitrate, bittiming.sample_point);
return res;
}
#ifdef MODULE_CAN_TRX
int raw_can_set_trx(int ifnum, can_trx_t *trx)
{
msg_t msg, reply;
assert(ifnum < candev_nb);
msg.type = CAN_MSG_SET_TRX;
msg.content.ptr = trx;
if (msg_send_receive(&msg, &reply, candev_list[ifnum]->pid) != 1) {
return -EBUSY;
}
return (int) reply.content.value;
}
#endif
int raw_can_get_ifnum_by_name(const char *name)
{
for (int i = 0; i < candev_nb; i++) {
if ((strcmp(name, candev_list[i]->name) == 0) &&
(strlen(name) == strlen(candev_list[i]->name))) {
return i;
}
}
return RAW_CAN_DEV_UNDEF;
}
const char *raw_can_get_name_by_ifnum(int ifnum)
{
assert(ifnum >= 0);
if (ifnum >= candev_nb) {
return NULL;
}
return candev_list[ifnum]->name;
}
candev_dev_t *raw_can_get_dev_by_ifnum(int ifnum)
{
assert(ifnum >= 0);
if (ifnum >= candev_nb) {
return NULL;
}
return candev_list[ifnum];
}

3
sys/can/isotp/Makefile Normal file
View File

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

898
sys/can/isotp/isotp.c Normal file
View File

@ -0,0 +1,898 @@
/*
* 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.
*/
/**
* @file
* @brief ISO TP high level interface
*
* @author Vincent Dupont <vincent@otakeys.com>
*/
#include <errno.h>
#include <string.h>
#include "net/gnrc/pktbuf.h"
#include "can/isotp.h"
#include "can/common.h"
#include "can/raw.h"
#include "can/router.h"
#include "thread.h"
#include "mutex.h"
#include "timex.h"
#include "utlist.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
#ifndef CAN_ISOTP_BS
#define CAN_ISOTP_BS 10
#endif
#ifndef CAN_ISOTP_STMIN
#define CAN_ISOTP_STMIN 5
#endif
#ifndef CAN_ISOTP_WFTMAX
#define CAN_ISOTP_WFTMAX 0
#endif
#ifndef CAN_ISOTP_MSG_QUEUE_SIZE
#define CAN_ISOTP_MSG_QUEUE_SIZE 64
#endif
#ifndef CAN_ISOTP_TIMEOUT_N_As
#define CAN_ISOTP_TIMEOUT_N_As (1 * US_PER_SEC)
#endif
#ifndef CAN_ISOTP_TIMEOUT_N_Bs
#define CAN_ISOTP_TIMEOUT_N_Bs (1 * US_PER_SEC)
#endif
#ifndef CAN_ISOTP_TIMEOUT_N_Ar
#define CAN_ISOTP_TIMEOUT_N_Ar (1 * US_PER_SEC)
#endif
#ifndef CAN_ISOTP_TIMEOUT_N_Cr
#define CAN_ISOTP_TIMEOUT_N_Cr (1 * US_PER_SEC)
#endif
enum {
ISOTP_IDLE = 0,
ISOTP_WAIT_FC,
ISOTP_WAIT_CF,
ISOTP_SENDING_SF,
ISOTP_SENDING_FF,
ISOTP_SENDING_CF,
ISOTP_SENDING_FC,
ISOTP_SENDING_NEXT_CF,
};
#define MAX_MSG_LENGTH 4095
/* N_PCI type values in bits 7-4 of N_PCI bytes */
#define N_PCI_SF 0x00 /* single frame */
#define N_PCI_FF 0x10 /* first frame */
#define N_PCI_CF 0x20 /* consecutive frame */
#define N_PCI_FC 0x30 /* flow control */
#define N_PCI_SZ 1 /* size of the PCI byte #1 */
#define SF_PCI_SZ 1 /* size of SingleFrame PCI including 4 bit SF_DL */
#define FF_PCI_SZ 2 /* size of FirstFrame PCI including 12 bit FF_DL */
#define FC_CONTENT_SZ 3 /* flow control content size in byte (FS/BS/STmin) */
/* Flow Status given in FC frame */
#define ISOTP_FC_CTS 0 /* clear to send */
#define ISOTP_FC_WT 1 /* wait */
#define ISOTP_FC_OVFLW 2 /* overflow */
static kernel_pid_t isotp_pid = KERNEL_PID_UNDEF;
static struct isotp *isotp_list = NULL;
static mutex_t lock = MUTEX_INIT;
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
static void _rx_timeout(void *arg);
static int _isotp_send_fc(struct isotp *isotp, int ae, uint8_t status);
static int _isotp_tx_send(struct isotp *isotp, struct can_frame *frame);
static int _send_msg(msg_t *msg, can_reg_entry_t *entry)
{
#ifdef MODULE_CAN_MBOX
switch (entry->type) {
case CAN_TYPE_DEFAULT:
return msg_try_send(msg, entry->target.pid);
case CAN_TYPE_MBOX:
DEBUG("_send_msg: sending msg=%p to mbox=%p\n", (void *)msg, (void *)entry->target.mbox);
return mbox_try_put(entry->target.mbox, msg);
default:
return -ENOTSUP;
}
#else
return msg_try_send(msg, entry->target.pid);
#endif
}
static int _isotp_dispatch_rx(struct isotp *isotp)
{
msg_t msg;
int ret = 0;
can_rx_data_t *data;
msg.type = CAN_MSG_RX_INDICATION;
data = can_pkt_alloc_rx_data(isotp->rx.snip,
isotp->rx.snip->size + sizeof(*isotp->rx.snip),
isotp->arg);
if (!data) {
return -ENOMEM;
}
msg.content.ptr = data;
if (_send_msg(&msg, &isotp->entry) < 1) {
DEBUG("_isotp_dispatch_rx: msg lost, freeing rx buf\n");
gnrc_pktbuf_release(((gnrc_pktsnip_t *)data->data.iov_base));
can_pkt_free_rx_data(data);
ret = -EOVERFLOW;
}
isotp->rx.snip = NULL;
return ret;
}
static int _isotp_dispatch_tx(struct isotp *isotp, int err)
{
msg_t msg;
gnrc_pktbuf_release(isotp->tx.snip);
isotp->tx.snip = NULL;
if (isotp->opt.flags & CAN_ISOTP_TX_DONT_WAIT) {
return 0;
}
if (!err) {
msg.type = CAN_MSG_TX_CONFIRMATION;
}
else {
msg.type = CAN_MSG_TX_ERROR;
}
msg.content.ptr = isotp->arg;
if (_send_msg(&msg, &isotp->entry) < 1) {
DEBUG("_isotp_dispatch_tx: msg lost\n");
return -EOVERFLOW;
}
return 0;
}
static void _rx_timeout(void *arg)
{
msg_t msg;
DEBUG("_rx_timeout: arg=%p\n", arg);
msg.type = CAN_MSG_ISOTP_RX_TIMEOUT;
msg.content.ptr = arg;
msg_send(&msg, isotp_pid);
}
static void _tx_timeout(void *arg)
{
msg_t msg;
DEBUG("_tx_timeout: arg=%p\n", arg);
msg.type = CAN_MSG_ISOTP_TX_TIMEOUT;
msg.content.ptr = arg;
msg_send(&msg, isotp_pid);
}
static int _isotp_rcv_fc(struct isotp *isotp, struct can_frame *frame, int ae)
{
if (isotp->tx.state != ISOTP_WAIT_FC) {
return 0;
}
xtimer_remove(&isotp->tx_timer);
if (frame->can_dlc < ae + FC_CONTENT_SZ) {
/* Invalid length */
isotp->tx.state = ISOTP_IDLE;
return 1;
}
isotp->txfc.bs = frame->data[ae + 1];
isotp->txfc.stmin = frame->data[ae + 2];
DEBUG("_isotp_rcv_fc: first FC: bs=0x%" PRIx8 ", stmin=0x%" PRIx8 "\n",
isotp->txfc.bs, isotp->txfc.stmin);
if ((isotp->txfc.stmin > 0x7F) &&
((isotp->txfc.stmin < 0xF1) || (isotp->txfc.stmin > 0xF9))) {
/* according to ISO15765-2 8.5.5.6 */
isotp->txfc.stmin = 0x7F;
}
/* ISO15765-2 8.5.5.5 */
/* Range 0x0 - 0x7F -> 0 ms - 127 ms */
if (isotp->txfc.stmin < 0x80) {
isotp->tx_gap = isotp->txfc.stmin * US_PER_MS;
}
/* Range 0xF1 - 0xF9 -> 100 us - 900 us */
else {
isotp->tx_gap = (isotp->txfc.stmin - 0xF0) * 100;
}
switch (frame->data[ae] & 0xF) {
case ISOTP_FC_CTS:
isotp->tx_wft = 0;
isotp->tx.bs = 0;
isotp->tx.state = ISOTP_SENDING_NEXT_CF;
xtimer_set(&isotp->tx_timer, isotp->tx_gap);
break;
case ISOTP_FC_WT:
if (isotp->tx_wft++ >= isotp->txfc.wftmax) {
isotp->tx.state = ISOTP_IDLE;
_isotp_dispatch_tx(isotp, ETIMEDOUT);
return 1;
}
/* BS and STmin shall be ignored */
xtimer_set(&isotp->tx_timer, CAN_ISOTP_TIMEOUT_N_Bs);
break;
case ISOTP_FC_OVFLW:
/* overflow on receiver side -> error */
default:
isotp->tx.state = ISOTP_IDLE;
_isotp_dispatch_tx(isotp, EOVERFLOW);
break;
}
return 0;
}
static int _isotp_rcv_sf(struct isotp *isotp, struct can_frame *frame, int ae)
{
xtimer_remove(&isotp->rx_timer);
isotp->rx.state = ISOTP_IDLE;
int len = (frame->data[ae] & 0x0F);
if (len > frame->can_dlc - (SF_PCI_SZ + ae)) {
return 1;
}
gnrc_pktsnip_t *snip = gnrc_pktbuf_add(NULL, NULL, len, GNRC_NETTYPE_UNDEF);
if (!snip) {
return 1;
}
isotp->rx.snip = snip;
isotp->rx.idx = 0;
for (size_t i = SF_PCI_SZ + ae; i < isotp->rx.snip->size + ae + SF_PCI_SZ; i++) {
((uint8_t *)isotp->rx.snip->data)[isotp->rx.idx++] = frame->data[i];
}
return _isotp_dispatch_rx(isotp);
}
static int _isotp_rcv_ff(struct isotp *isotp, struct can_frame *frame, int ae)
{
isotp->rx.state = ISOTP_IDLE;
int len = (frame->data[ae] & 0x0F) << 8;
len += frame->data[ae + 1];
if (isotp->rx.snip) {
DEBUG("_isotp_rcv_ff: freeing previous rx buf\n");
gnrc_pktbuf_release(isotp->rx.snip);
}
if (len > MAX_MSG_LENGTH) {
if (!(isotp->opt.flags & CAN_ISOTP_LISTEN_MODE)) {
_isotp_send_fc(isotp, ae, ISOTP_FC_OVFLW);
}
return 1;
}
gnrc_pktsnip_t *snip = gnrc_pktbuf_add(NULL, NULL, len, GNRC_NETTYPE_UNDEF);
if (!snip) {
if (!(isotp->opt.flags & CAN_ISOTP_LISTEN_MODE)) {
_isotp_send_fc(isotp, ae, ISOTP_FC_OVFLW);
}
return 1;
}
isotp->rx.snip = snip;
isotp->rx.idx = 0;
for (int i = ae + FF_PCI_SZ; i < frame->can_dlc; i++) {
((uint8_t *)isotp->rx.snip->data)[isotp->rx.idx++] = frame->data[i];
}
#if ENABLE_DEBUG
DEBUG("_isotp_rcv_ff: rx.buf=");
for (unsigned i = 0; i < isotp->rx.idx; i++) {
DEBUG("%02hhx", ((uint8_t *)isotp->rx.snip->data)[i]);
}
DEBUG("\n");
#endif
isotp->rx.sn = 1;
if (isotp->opt.flags & CAN_ISOTP_LISTEN_MODE) {
isotp->rx.state = ISOTP_WAIT_CF;
return 0;
}
isotp->rx.state = ISOTP_SENDING_FC;
_isotp_send_fc(isotp, ae, ISOTP_FC_CTS);
return 0;
}
static int _isotp_rcv_cf(struct isotp *isotp, struct can_frame *frame, int ae)
{
DEBUG("_isotp_rcv_cf: state=%d\n", isotp->rx.state);
if (isotp->rx.state != ISOTP_WAIT_CF) {
return 1;
}
xtimer_remove(&isotp->rx_timer);
if ((frame->data[ae] & 0x0F) != isotp->rx.sn) {
DEBUG("_isotp_rcv_cf: wrong seq number %d, expected %d\n", frame->data[ae] & 0x0F, isotp->rx.sn);
isotp->rx.state = ISOTP_IDLE;
gnrc_pktbuf_release(isotp->rx.snip);
isotp->rx.snip = NULL;
return 1;
}
isotp->rx.sn++;
isotp->rx.sn %= 16;
for (int i = ae + N_PCI_SZ; i < frame->can_dlc; i++) {
((uint8_t *)isotp->rx.snip->data)[isotp->rx.idx++] = frame->data[i];
if (isotp->rx.idx >= isotp->rx.snip->size) {
break;
}
}
#if ENABLE_DEBUG
DEBUG("_isotp_rcv_cf: rx.buf=");
for (unsigned i = 0; i < isotp->rx.idx; i++) {
DEBUG("%02hhx", ((uint8_t *)isotp->rx.snip->data)[i]);
}
DEBUG("\n");
#endif
if (isotp->rx.idx >= isotp->rx.snip->size) {
isotp->rx.state = ISOTP_IDLE;
return _isotp_dispatch_rx(isotp);
}
if (isotp->opt.flags & CAN_ISOTP_LISTEN_MODE) {
return 0;
}
DEBUG("_isotp_rcv_cf: rxfc.bs=%" PRIx8 " rx.bs=%" PRIx8 "\n", isotp->rxfc.bs, isotp->rx.bs);
if (!isotp->rxfc.bs || (++isotp->rx.bs < isotp->rxfc.bs)) {
xtimer_set(&isotp->rx_timer, CAN_ISOTP_TIMEOUT_N_Cr);
return 0;
}
return _isotp_send_fc(isotp, ae, ISOTP_FC_CTS);
}
static int _isotp_rcv(struct isotp *isotp, struct can_frame *frame)
{
int ae = (isotp->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0;
uint8_t n_pci_type;
#if ENABLE_DEBUG
DEBUG("_isotp_rcv: id=%" PRIx32 " data=", frame->can_id);
for (int i = 0; i < frame->can_dlc; i++) {
DEBUG("%02hhx", frame->data[i]);
}
DEBUG("\n");
#endif
if (ae && frame->data[0] != isotp->opt.rx_ext_address) {
return 1;
}
n_pci_type = frame->data[ae] & 0xF0;
switch (n_pci_type) {
case N_PCI_FC:
return _isotp_rcv_fc(isotp, frame, ae);
case N_PCI_SF:
return _isotp_rcv_sf(isotp, frame, ae);
case N_PCI_FF:
return _isotp_rcv_ff(isotp, frame, ae);
case N_PCI_CF:
return _isotp_rcv_cf(isotp, frame, ae);
}
return 1;
}
static int _isotp_send_fc(struct isotp *isotp, int ae, uint8_t status)
{
struct can_frame fc;
fc.can_id = isotp->opt.tx_id;
if (isotp->opt.flags & CAN_ISOTP_TX_PADDING) {
memset(fc.data, isotp->opt.txpad_content, CAN_MAX_DLEN);
fc.can_dlc = CAN_MAX_DLEN;
}
else {
fc.can_dlc = ae + FC_CONTENT_SZ;
}
fc.data[ae] = N_PCI_FC | status;
fc.data[ae + 1] = isotp->rxfc.bs;
fc.data[ae + 2] = isotp->rxfc.stmin;
if (ae) {
fc.data[0] = isotp->opt.ext_address;
}
isotp->rx.bs = 0;
#if ENABLE_DEBUG
DEBUG("_isotp_send_fc: id=%" PRIx32 " data=", fc.can_id);
for (int i = 0; i < fc.can_dlc; i++) {
DEBUG("%02hhx", fc.data[i]);
}
DEBUG("\n");
#endif
xtimer_set(&isotp->rx_timer, CAN_ISOTP_TIMEOUT_N_Ar);
isotp->rx.tx_handle = raw_can_send(isotp->entry.ifnum, &fc, isotp_pid);
if (isotp->rx.tx_handle >= 0) {
return 0;
}
else {
isotp->rx.state = ISOTP_IDLE;
xtimer_remove(&isotp->rx_timer);
return isotp->rx.tx_handle;
}
}
static void _isotp_create_ff(struct isotp *isotp, struct can_frame *frame, int ae)
{
frame->can_id = isotp->opt.tx_id;
frame->can_dlc = CAN_MAX_DLEN;
if (ae) {
frame->data[0] = isotp->opt.ext_address;
}
frame->data[ae] = (uint8_t)(isotp->tx.snip->size >> 8) | N_PCI_FF;
frame->data[ae + 1] = (uint8_t) isotp->tx.snip->size & 0xFFU;
for (int i = ae + FF_PCI_SZ; i < CAN_MAX_DLEN; i++) {
frame->data[i] = ((uint8_t *)isotp->tx.snip->data)[isotp->tx.idx++];
}
isotp->tx.sn = 1;
}
static void _isotp_fill_dataframe(struct isotp *isotp, struct can_frame *frame, int ae)
{
size_t pci_len = N_PCI_SZ + ae;
size_t space = CAN_MAX_DLEN - pci_len;
size_t num_bytes = MIN(space, isotp->tx.snip->size - isotp->tx.idx);
frame->can_id = isotp->opt.tx_id;
frame->can_dlc = num_bytes + pci_len;
DEBUG("_isotp_fill_dataframe: num_bytes=%d, pci_len=%d\n", num_bytes, pci_len);
if (num_bytes < space) {
if (isotp->opt.flags & CAN_ISOTP_TX_PADDING) {
frame->can_dlc = CAN_MAX_DLEN;
memset(frame->data, isotp->opt.txpad_content, frame->can_dlc);
}
}
for (size_t i = 0; i < num_bytes; i++) {
frame->data[pci_len + i] = ((uint8_t *)isotp->tx.snip->data)[isotp->tx.idx++];
}
if (ae) {
frame->data[0] = isotp->opt.ext_address;
}
}
static void _isotp_tx_timeout_task(struct isotp *isotp)
{
int ae = (isotp->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0;
struct can_frame frame;
DEBUG("_isotp_tx_timeout_task: state=%d\n", isotp->tx.state);
switch (isotp->tx.state) {
case ISOTP_WAIT_FC:
DEBUG("_isotp_tx_timeout_task: FC not received on time\n");
isotp->tx.state = ISOTP_IDLE;
_isotp_dispatch_tx(isotp, ETIMEDOUT);
break;
case ISOTP_SENDING_NEXT_CF:
DEBUG("_isotp_tx_timeout_task: sending next CF\n");
_isotp_fill_dataframe(isotp, &frame, ae);
frame.data[ae] = N_PCI_CF | isotp->tx.sn++;
isotp->tx.sn %= 16;
isotp->tx.bs++;
isotp->tx.state = ISOTP_SENDING_CF;
_isotp_tx_send(isotp, &frame);
break;
case ISOTP_SENDING_CF:
case ISOTP_SENDING_FF:
case ISOTP_SENDING_SF:
DEBUG("_isotp_tx_timeout_task: timeout on DLL\n");
isotp->tx.state = ISOTP_IDLE;
raw_can_abort(isotp->entry.ifnum, isotp->tx.tx_handle);
_isotp_dispatch_tx(isotp, ETIMEDOUT);
break;
}
}
static void _isotp_tx_tx_conf(struct isotp *isotp)
{
xtimer_remove(&isotp->tx_timer);
isotp->tx.tx_handle = 0;
DEBUG("_isotp_tx_tx_conf: state=%d\n", isotp->tx.state);
switch (isotp->tx.state) {
case ISOTP_SENDING_SF:
isotp->tx.state = ISOTP_IDLE;
_isotp_dispatch_tx(isotp, 0);
break;
case ISOTP_SENDING_FF:
isotp->tx.state = ISOTP_WAIT_FC;
xtimer_set(&isotp->tx_timer, CAN_ISOTP_TIMEOUT_N_Bs);
break;
case ISOTP_SENDING_CF:
if (isotp->tx.idx >= isotp->tx.snip->size) {
/* Finished */
isotp->tx.state = ISOTP_IDLE;
_isotp_dispatch_tx(isotp, 0);
break;
}
if (isotp->txfc.bs && (isotp->tx.bs >= isotp->txfc.bs)) {
/* wait for FC */
isotp->tx.state = ISOTP_WAIT_FC;
xtimer_set(&isotp->tx_timer, CAN_ISOTP_TIMEOUT_N_Bs);
break;
}
isotp->tx.state = ISOTP_SENDING_NEXT_CF;
xtimer_set(&isotp->tx_timer, isotp->tx_gap);
break;
}
}
static void _isotp_rx_timeout_task(struct isotp *isotp)
{
switch (isotp->rx.state) {
case ISOTP_SENDING_FC:
DEBUG("_isotp_rx_timeout_task: FC tx conf timeout\n");
raw_can_abort(isotp->entry.ifnum, isotp->rx.tx_handle);
case ISOTP_WAIT_CF:
DEBUG("_isotp_rx_timeout_task: free rx buf\n");
gnrc_pktbuf_release(isotp->rx.snip);
isotp->rx.snip = NULL;
isotp->rx.state = ISOTP_IDLE;
/* TODO dispatch rx error ? */
break;
}
}
static void _isotp_rx_tx_conf(struct isotp *isotp)
{
xtimer_remove(&isotp->rx_timer);
isotp->rx.tx_handle = 0;
DEBUG("_isotp_rx_tx_conf: state=%d\n", isotp->rx.state);
switch (isotp->rx.state) {
case ISOTP_SENDING_FC:
isotp->rx.state = ISOTP_WAIT_CF;
xtimer_set(&isotp->rx_timer, CAN_ISOTP_TIMEOUT_N_Cr);
break;
}
}
static int _isotp_tx_send(struct isotp *isotp, struct can_frame *frame)
{
xtimer_set(&isotp->tx_timer, CAN_ISOTP_TIMEOUT_N_As);
isotp->tx.tx_handle = raw_can_send(isotp->entry.ifnum, frame, isotp_pid);
DEBUG("isotp_send: FF/SF/CF sent handle=%d\n", isotp->tx.tx_handle);
if (isotp->tx.tx_handle < 0) {
xtimer_remove(&isotp->tx_timer);
isotp->tx.state = ISOTP_IDLE;
return _isotp_dispatch_tx(isotp, isotp->tx.tx_handle);
}
return 0;
}
static int _isotp_send_sf_ff(struct isotp *isotp)
{
struct can_frame frame;
unsigned ae = (isotp->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0;
if (isotp->tx.snip->size <= CAN_MAX_DLEN - SF_PCI_SZ - ae) {
/* Fits into a single frame */
_isotp_fill_dataframe(isotp, &frame, ae);
frame.data[ae] = N_PCI_SF;
frame.data[ae] |= isotp->tx.snip->size;
isotp->tx.state = ISOTP_SENDING_SF;
}
else {
isotp->tx.state = ISOTP_SENDING_FF;
/* Must send a First frame */
_isotp_create_ff(isotp, &frame, ae);
}
return _isotp_tx_send(isotp, &frame);
}
static void *_isotp_thread(void *args)
{
(void)args;
msg_t msg, msg_queue[CAN_ISOTP_MSG_QUEUE_SIZE];
struct can_rx_data *rx_frame;
struct isotp *isotp;
/* setup the device layers message queue */
msg_init_queue(msg_queue, CAN_ISOTP_MSG_QUEUE_SIZE);
isotp_pid = sched_active_pid;
while (1) {
msg_receive(&msg);
switch (msg.type) {
case CAN_MSG_SEND_FRAME:
_isotp_send_sf_ff(msg.content.ptr);
break;
case CAN_MSG_RX_INDICATION:
rx_frame = msg.content.ptr;
if (!rx_frame) {
DEBUG("_isotp_thread: CAN_MSG_RX_INDICATION with NULL ptr\n");
break;
}
DEBUG("_isotp_thread: CAN_MSG_RX_INDICATION, frame=%p, data=%p\n",
(void *)rx_frame->data.iov_base, rx_frame->arg);
_isotp_rcv((struct isotp *)rx_frame->arg, rx_frame->data.iov_base);
raw_can_free_frame(rx_frame);
break;
case CAN_MSG_TX_CONFIRMATION:
DEBUG("_isotp_thread: CAN_MSG_TX_CONFIRMATION, handle=%d\n", (int)msg.content.value);
mutex_lock(&lock);
LL_FOREACH(isotp_list, isotp) {
if (isotp->tx.tx_handle == (int)msg.content.value) {
mutex_unlock(&lock);
_isotp_tx_tx_conf(isotp);
break;
}
else if (isotp->rx.tx_handle == (int)msg.content.value) {
mutex_unlock(&lock);
_isotp_rx_tx_conf(isotp);
break;
}
}
if (isotp == NULL) {
mutex_unlock(&lock);
}
break;
case CAN_MSG_ISOTP_RX_TIMEOUT:
isotp = msg.content.ptr;
DEBUG("_isotp_thread: RX TIMEOUT arg=%p\n", (void *)isotp);
_isotp_rx_timeout_task(isotp);
break;
case CAN_MSG_ISOTP_TX_TIMEOUT:
isotp = msg.content.ptr;
DEBUG("_isotp_thread: TX_TIMEOUT arg=%p\n", (void *)isotp);
_isotp_tx_timeout_task(isotp);
break;
}
}
return NULL;
}
kernel_pid_t isotp_init(char *stack, int stacksize, char priority, const char *name)
{
kernel_pid_t res;
DEBUG("isotp_init\n");
/* create new can device thread */
res = thread_create(stack, stacksize, priority, THREAD_CREATE_STACKTEST,
_isotp_thread, NULL, name);
if (res <= 0) {
return -EINVAL;
}
return res;
}
int isotp_send(struct isotp *isotp, const void *buf, int len, int flags)
{
assert(isotp != NULL);
#ifdef MODULE_CAN_MBOX
assert((isotp->entry.type == CAN_TYPE_DEFAULT && pid_is_valid(isotp->entry.target.pid)) ||
(isotp->entry.type == CAN_TYPE_MBOX && isotp->entry.target.mbox != NULL));
#else
assert(isotp->entry.target.pid != KERNEL_PID_UNDEF);
#endif
assert (len && len <= MAX_MSG_LENGTH);
if (isotp->tx.state != ISOTP_IDLE) {
return -EBUSY;
}
if (flags) {
isotp->opt.flags &= CAN_ISOTP_RX_FLAGS_MASK;
isotp->opt.flags |= (flags & CAN_ISOTP_TX_FLAGS_MASK);
}
gnrc_pktsnip_t *snip = gnrc_pktbuf_add(NULL, NULL, len, GNRC_NETTYPE_UNDEF);
if (!snip) {
return -ENOMEM;
}
isotp->tx.snip = snip;
memcpy(isotp->tx.snip->data, buf, len);
isotp->tx.idx = 0;
isotp->tx_wft = 0;
msg_t msg;
msg.type = CAN_MSG_SEND_FRAME;
msg.content.ptr = isotp;
msg_send(&msg, isotp_pid);
return len;
}
int isotp_bind(struct isotp *isotp, can_reg_entry_t *entry, void *arg)
{
int ret;
assert(isotp != NULL);
#ifdef MODULE_CAN_MBOX
assert((entry->type == CAN_TYPE_DEFAULT && pid_is_valid(entry->target.pid)) ||
(entry->type == CAN_TYPE_MBOX && entry->target.mbox != NULL));
#else
assert(pid_is_valid(entry->target.pid));
#endif
assert(isotp->opt.tx_id != isotp->opt.rx_id);
assert(!((isotp->opt.tx_id | isotp->opt.rx_id) & (CAN_RTR_FLAG | CAN_ERR_FLAG)));
assert(entry->ifnum < CAN_DLL_NUMOF);
isotp->rx_timer.callback = _rx_timeout;
isotp->rx_timer.arg = isotp;
isotp->tx_timer.callback = _tx_timeout;
isotp->tx_timer.arg = isotp;
memset(&isotp->rx, 0, sizeof(struct tpcon));
memset(&isotp->tx, 0, sizeof(struct tpcon));
isotp->rxfc.bs = CAN_ISOTP_BS;
isotp->rxfc.stmin = CAN_ISOTP_STMIN;
isotp->rxfc.wftmax = 0;
isotp->txfc.bs = 0;
isotp->txfc.stmin = 0;
isotp->txfc.wftmax = CAN_ISOTP_WFTMAX;
isotp->entry.ifnum = entry->ifnum;
#ifdef MODULE_CAN_MBOX
isotp->entry.type = entry->type;
isotp->entry.target.mbox = entry->target.mbox;
#else
isotp->entry.target.pid = entry->target.pid;
#endif
isotp->arg = arg;
isotp->next = NULL;
DEBUG("isotp_bind: ifnum=%d, txid=%" PRIx32 ", rxid=%" PRIx32 ", flags=0x%" PRIx16 "\n",
isotp->entry.ifnum, isotp->opt.tx_id, isotp->opt.rx_id, isotp->opt.flags);
DEBUG("isotp_bind: pid=%" PRIkernel_pid "\n", entry->target.pid);
struct can_filter filter = {
.can_id = isotp->opt.rx_id,
.can_mask = 0xFFFFFFFF,
};
ret = raw_can_subscribe_rx(isotp->entry.ifnum, &filter, isotp_pid, isotp);
if (ret < 0) {
return ret;
}
mutex_lock(&lock);
LL_APPEND(isotp_list, isotp);
mutex_unlock(&lock);
return 0;
}
void isotp_free_rx(can_rx_data_t *rx)
{
DEBUG("isotp_free_rx: rx=%p\n", (void *)rx);
gnrc_pktbuf_release(rx->data.iov_base);
can_pkt_free_rx_data(rx);
}
int isotp_release(struct isotp *isotp)
{
assert(isotp != NULL);
#ifdef MODULE_CAN_MBOX
assert((isotp->entry.type == CAN_TYPE_DEFAULT && pid_is_valid(isotp->entry.target.pid)) ||
(isotp->entry.type == CAN_TYPE_MBOX && isotp->entry.target.mbox != NULL));
#else
assert(isotp->entry.target.pid != KERNEL_PID_UNDEF);
#endif
DEBUG("isotp_release: isotp=%p\n", (void *)isotp);
struct can_filter filter = {
.can_id = isotp->opt.rx_id,
.can_mask = 0xFFFFFFFF,
};
raw_can_unsubscribe_rx(isotp->entry.ifnum, &filter, isotp_pid, isotp);
if (isotp->rx.snip) {
DEBUG("isotp_release: freeing rx buf\n");
gnrc_pktbuf_release(isotp->rx.snip);
isotp->rx.snip = NULL;
}
isotp->rx.state = ISOTP_IDLE;
isotp->entry.target.pid = KERNEL_PID_UNDEF;
mutex_lock(&lock);
LL_DELETE(isotp_list, isotp);
mutex_unlock(&lock);
if (isotp->tx.snip) {
DEBUG("isotp_release: freeing rx buf\n");
gnrc_pktbuf_release(isotp->tx.snip);
isotp->tx.snip = NULL;
}
isotp->tx.state = ISOTP_IDLE;
return 0;
}

163
sys/can/pkt.c Normal file
View File

@ -0,0 +1,163 @@
/*
* 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.
*/
/**
* @file
* @brief CAN memory allocation module
*
* @author Vincent Dupont <vincent@otakeys.com>
* @author Toon Stegen <toon.stegen@altran.com>
*/
#include <string.h>
#include <limits.h>
#include <errno.h>
#include "net/gnrc/pktbuf.h"
#include "can/pkt.h"
#include "mutex.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
#define HANDLE_UNUSED 0
static int handle;
static mutex_t _mutex = MUTEX_INIT;
void can_pkt_init(void)
{
mutex_lock(&_mutex);
handle = 1;
mutex_unlock(&_mutex);
}
static can_pkt_t *_pkt_alloc(int ifnum, const struct can_frame *frame)
{
can_pkt_t *pkt;
gnrc_pktsnip_t *snip = gnrc_pktbuf_add(NULL, NULL, sizeof(*pkt), GNRC_NETTYPE_UNDEF);
if (!snip) {
DEBUG("can_pkt_alloc: out of memory\n");
return NULL;
}
pkt = snip->data;
pkt->entry.ifnum = ifnum;
pkt->frame = *frame;
pkt->snip = snip;
DEBUG("can_pkt_alloc: pkt allocated\n");
return pkt;
}
static void _init_pkt(can_pkt_t *pkt, int tx)
{
if (tx) {
mutex_lock(&_mutex);
pkt->handle = handle++;
if (handle == INT_MAX) {
handle = 1;
}
pkt->entry.next = NULL;
mutex_unlock(&_mutex);
}
else {
pkt->handle = 0;
atomic_store(&pkt->ref_count, 0);
}
}
can_pkt_t *can_pkt_alloc_tx(int ifnum, const struct can_frame *frame, kernel_pid_t tx_pid)
{
can_pkt_t *pkt = _pkt_alloc(ifnum, frame);
if (!pkt) {
return NULL;
}
_init_pkt(pkt, 1);
pkt->entry.target.pid = tx_pid;
#ifdef MODULE_CAN_MBOX
pkt->entry.type = CAN_TYPE_DEFAULT;
#endif
return pkt;
}
can_pkt_t *can_pkt_alloc_rx(int ifnum, const struct can_frame *frame)
{
can_pkt_t *pkt = _pkt_alloc(ifnum, frame);
if (!pkt) {
return NULL;
}
_init_pkt(pkt, 0);
return pkt;
}
#ifdef MODULE_CAN_MBOX
can_pkt_t *can_pkt_alloc_mbox_tx(int ifnum, const struct can_frame *frame, mbox_t *tx_mbox)
{
can_pkt_t *pkt = _pkt_alloc(ifnum, frame);
if (!pkt) {
return NULL;
}
_init_pkt(pkt, 1);
pkt->entry.target.mbox = tx_mbox;
pkt->entry.type = CAN_TYPE_MBOX;
return pkt;
}
#endif
void can_pkt_free(can_pkt_t *pkt)
{
if (!pkt) {
return;
}
DEBUG("can_pkt_free: free pkt=%p\n", (void*)pkt);
gnrc_pktbuf_release(pkt->snip);
}
can_rx_data_t *can_pkt_alloc_rx_data(void *data, size_t len, void *arg)
{
can_rx_data_t *rx;
gnrc_pktsnip_t *snip = gnrc_pktbuf_add(NULL, NULL, sizeof(*rx), GNRC_NETTYPE_UNDEF);
if (!snip) {
DEBUG("can_pkt_alloc_rx_data: out of memory\n");
return NULL;
}
rx = snip->data;
DEBUG("can_pkt_alloc_rx_data: rx=%p\n", (void *)rx);
rx->data.iov_base = data;
rx->data.iov_len = len;
rx->arg = arg;
rx->snip = snip;
return rx;
}
void can_pkt_free_rx_data(can_rx_data_t *data)
{
if (!data) {
return;
}
gnrc_pktbuf_release(data->snip);
}

400
sys/can/router.c Normal file
View File

@ -0,0 +1,400 @@
/*
* 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.
*/
/**
*
* @file
* @brief Functions for routing RX can frames
*
* @author Toon Stegen <toon.stegen@altran.com>
* @author Vincent Dupont <vincent@otakeys.com>
*/
#include <stdint.h>
#include <errno.h>
#include "kernel_defines.h"
#include "net/gnrc/pktbuf.h"
#include "can/router.h"
#include "can/pkt.h"
#include "can/device.h"
#include "utlist.h"
#include "mutex.h"
#include "assert.h"
#ifdef MODULE_CAN_MBOX
#include "mbox.h"
#endif
#define ENABLE_DEBUG (0)
#include "debug.h"
#if ENABLE_DEBUG
#include <inttypes.h>
#endif
/**
* This is a can_id element
*/
typedef struct filter_el {
can_reg_entry_t entry; /**< filter entry */
canid_t can_id; /**< CAN ID of the element */
canid_t mask; /**< Mask of the element */
void *data; /**< Private data */
gnrc_pktsnip_t *snip; /**< Pointer to the allocated snip */
} filter_el_t;
/**
* This table contains @p CAN_ROUTER_APP_MAX lists of CAN IDs per interface
*/
static can_reg_entry_t *table[CAN_DLL_NUMOF];
static mutex_t lock = MUTEX_INIT;
static filter_el_t *_alloc_filter_el(canid_t can_id, canid_t mask, void *data);
static void _free_filter_el(filter_el_t *el);
static void _insert_to_list(can_reg_entry_t **list, filter_el_t *el);
static filter_el_t *_find_filter_el(can_reg_entry_t *list, can_reg_entry_t *entry, canid_t can_id, canid_t mask, void *data);
static int _filter_is_used(unsigned int ifnum, canid_t can_id, canid_t mask);
#if ENABLE_DEBUG
static void _print_filters(void)
{
for (int i = 0; i < (int)CAN_DLL_NUMOF; i++) {
DEBUG("--- Ifnum: %d ---\n", i);
can_reg_entry_t *entry;
LL_FOREACH(table[i], entry) {
filter_el_t *el = container_of(entry, filter_el_t, entry);
DEBUG("App pid=%" PRIkernel_pid ", el=%p, can_id=0x%" PRIx32 ", mask=0x%" PRIx32 ", data=%p\n",
el->entry.target.pid, (void*)el, el->can_id, el->mask, el->data);
}
}
}
#define PRINT_FILTERS() _print_filters()
#else
#define PRINT_FILTERS()
#endif
static filter_el_t *_alloc_filter_el(canid_t can_id, canid_t mask, void *data)
{
filter_el_t *el;
gnrc_pktsnip_t *snip = gnrc_pktbuf_add(NULL, NULL, sizeof(*el), GNRC_NETTYPE_UNDEF);
if (!snip) {
DEBUG("can_router: _alloc_canid_el: out of memory\n");
return NULL;
}
el = snip->data;
el->can_id = can_id;
el->mask = mask;
el->data = data;
el->entry.next = NULL;
el->snip = snip;
DEBUG("_alloc_canid_el: el allocated with can_id=0x%" PRIx32 ", mask=0x%" PRIx32
", data=%p\n", can_id, mask, data);
return el;
}
static void _free_filter_el(filter_el_t *el)
{
DEBUG("_free_canid_el: el freed with can_id=0x%" PRIx32 ", mask=0x%" PRIx32
", data=%p\n", el->can_id, el->mask, el->data);
gnrc_pktbuf_release(el->snip);
}
/* Insert to the list in a sorted way
* Lower CAN IDs are inserted first */
static void _insert_to_list(can_reg_entry_t **list, filter_el_t *el)
{
can_reg_entry_t *next_entry = *list;
filter_el_t *next_el = container_of(next_entry, filter_el_t, entry);
DEBUG("_insert_to_list: list=%p, el=%p\n", (void *)list, (void *)el);
if (!(*list) || (next_el->can_id > el->can_id)) {
LL_PREPEND(*list, &el->entry);
DEBUG("_insert_to_list: inserting first el, list=%p\n", (void *)list);
}
else {
do {
if (el->can_id <= next_el->can_id) {
DEBUG("_insert_to_list: found next_el can_id:0x%" PRIx32
"\n", next_el->can_id);
LL_PREPEND_ELEM(*list, next_entry, &el->entry);
return;
}
else if (next_el->entry.next == NULL) {
DEBUG("_insert_to_list: insert at the end\n");
LL_APPEND(next_entry, &el->entry);
return;
}
else {
next_entry = next_entry->next;
next_el = container_of(next_entry, filter_el_t, entry);
DEBUG("_insert_to_list: going to next el: %p\n", (void*) next_el);
}
} while (next_el);
}
}
#ifdef MODULE_CAN_MBOX
#define ENTRY_MATCHES(e1, e2) (((e1)->type == (e2)->type) && \
(((e1)->type == CAN_TYPE_DEFAULT && (e1)->target.pid == (e2)->target.pid) ||\
((e1)->type == CAN_TYPE_MBOX && (e1)->target.mbox == (e2)->target.mbox)))
#else
#define ENTRY_MATCHES(e1, e2) ((e1)->target.pid == (e2)->target.pid)
#endif
static filter_el_t *_find_filter_el(can_reg_entry_t *list, can_reg_entry_t *entry, canid_t can_id, canid_t mask, void *data)
{
filter_el_t *el = container_of(list, filter_el_t, entry);
if (!el) {
return el;
}
do {
if ((el->can_id == can_id) && (el->mask == mask) && (el->data == data) &&
ENTRY_MATCHES(&el->entry, entry)) {
DEBUG("_find_filter_el: found el=%p, can_id=%" PRIx32 ", mask=%" PRIx32 ", data=%p\n",
(void *)el, el->can_id, el->mask, el->data);
return el;
}
el = container_of(el->entry.next, filter_el_t, entry);
} while (el);
return NULL;
}
static int _filter_is_used(unsigned int ifnum, canid_t can_id, canid_t mask)
{
filter_el_t *el = container_of(table[ifnum], filter_el_t, entry);
if (!el) {
DEBUG("_filter_is_used: empty list\n");
return 0;
}
do {
if ((el->can_id == can_id) && (el->mask == mask)) {
DEBUG("_filter_is_used: found el=%p, can_id=%" PRIx32 ", mask=%" PRIx32 ", data=%p\n",
(void *)el, el->can_id, el->mask, el->data);
return 1;
}
el = container_of(el->entry.next, filter_el_t, entry);
} while (el);
DEBUG("_filter_is_used: filter not found\n");
return 0;
}
/* register interested users */
int can_router_register(can_reg_entry_t *entry, canid_t can_id, canid_t mask, void *param)
{
filter_el_t *filter;
int ret;
#if ENABLE_DEBUG
if (entry->type == CAN_TYPE_DEFAULT) {
DEBUG("can_router_register: ifnum=%d, pid=%" PRIkernel_pid ", can_id=0x%" PRIx32
", mask=0x%" PRIx32 ", data=%p\n", entry->ifnum, entry->target.pid, can_id, mask, param);
} else if (entry->type == CAN_TYPE_MBOX) {
DEBUG("can_router_register: ifnum=%d, mbox=%p, can_id=0x%" PRIx32
", mask=0x%" PRIx32 ", data=%p\n", entry->ifnum, (void *)entry->target.mbox, can_id, mask, param);
}
#endif
mutex_lock(&lock);
ret = _filter_is_used(entry->ifnum, can_id, mask);
filter = _alloc_filter_el(can_id, mask, param);
if (!filter) {
mutex_unlock(&lock);
return -ENOMEM;
}
#ifdef MODULE_CAN_MBOX
filter->entry.type = entry->type;
switch (entry->type) {
case CAN_TYPE_DEFAULT:
filter->entry.target.pid = entry->target.pid;
break;
case CAN_TYPE_MBOX:
filter->entry.target.mbox = entry->target.mbox;
break;
}
#else
filter->entry.target.pid = entry->target.pid;
#endif
filter->entry.ifnum = entry->ifnum;
_insert_to_list(&table[entry->ifnum], filter);
mutex_unlock(&lock);
PRINT_FILTERS();
return ret;
}
/* unregister interested users */
int can_router_unregister(can_reg_entry_t *entry, canid_t can_id,
canid_t mask, void *param)
{
filter_el_t *el;
int ret;
#if ENABLE_DEBUG
if (entry->type == CAN_TYPE_DEFAULT) {
DEBUG("can_router_unregister: ifnum=%d, pid=%" PRIkernel_pid ", can_id=0x%" PRIx32
", mask=0x%" PRIx32 ", data=%p", entry->ifnum, entry->target.pid, can_id, mask, param);
} else if (entry->type == CAN_TYPE_MBOX) {
DEBUG("can_router_unregister: ifnum=%d, mbox=%p, can_id=0x%" PRIx32
", mask=0x%" PRIx32 ", data=%p\n", entry->ifnum, (void *)entry->target.mbox, can_id, mask, param);
}
#endif
mutex_lock(&lock);
el = _find_filter_el(table[entry->ifnum], entry, can_id, mask, param);
if (!el) {
mutex_unlock(&lock);
return -EINVAL;
}
LL_DELETE(table[entry->ifnum], &el->entry);
_free_filter_el(el);
ret = _filter_is_used(entry->ifnum, can_id, mask);
mutex_unlock(&lock);
PRINT_FILTERS();
return ret;
}
static int _send_msg(msg_t *msg, can_reg_entry_t *entry)
{
#ifdef MODULE_CAN_MBOX
switch (entry->type) {
case CAN_TYPE_DEFAULT:
return msg_try_send(msg, entry->target.pid);
case CAN_TYPE_MBOX:
DEBUG("_send_msg: sending msg=%p to mbox=%p\n", (void *)msg, (void *)entry->target.mbox);
return mbox_try_put(entry->target.mbox, msg);
default:
return -ENOTSUP;
}
#else
return msg_try_send(msg, entry->target.pid);
#endif
}
/* send received pkt to all interested users */
int can_router_dispatch_rx_indic(can_pkt_t *pkt)
{
if (!pkt) {
DEBUG("can_router_dispatch_rx_indic: invalid pkt\n");
return -EINVAL;
}
int res = 0;
msg_t msg;
msg.type = CAN_MSG_RX_INDICATION;
#if ENABLE_DEBUG
int msg_cnt = 0;
#endif
DEBUG("can_router_dispatch_rx_indic: pkt=%p, ifnum=%d, can_id=%" PRIx32 "\n",
(void *)pkt, pkt->entry.ifnum, pkt->frame.can_id);
mutex_lock(&lock);
can_reg_entry_t *entry;
filter_el_t *el;
LL_FOREACH(table[pkt->entry.ifnum], entry) {
el = container_of(entry, filter_el_t, entry);
if ((pkt->frame.can_id & el->mask) == el->can_id) {
DEBUG("can_router_dispatch_rx_indic: found el=%p, data=%p\n",
(void *)el, (void *)el->data);
DEBUG("can_router_dispatch_rx_indic: rx_ind to pid: %"
PRIkernel_pid "\n", entry->target.pid);
atomic_fetch_add(&pkt->ref_count, 1);
msg.content.ptr = can_pkt_alloc_rx_data(&pkt->frame, sizeof(pkt->frame), el->data);
#if ENABLE_DEBUG
msg_cnt++;
#endif
if (!msg.content.ptr || (_send_msg(&msg, entry) <= 0)) {
can_pkt_free_rx_data(msg.content.ptr);
atomic_fetch_sub(&pkt->ref_count, 1);
DEBUG("can_router_dispatch_rx_indic: failed to send msg to "
"pid=%" PRIkernel_pid "\n", entry->target.pid);
res = -EBUSY;
break;
}
}
}
mutex_unlock(&lock);
#if ENABLE_DEBUG
DEBUG("can_router_dispatch_rx: msg send to %d threads\n", msg_cnt);
#endif
if (atomic_load(&pkt->ref_count) == 0) {
can_pkt_free(pkt);
}
return res;
}
int can_router_dispatch_tx_conf(can_pkt_t *pkt)
{
msg_t msg;
msg.type = CAN_MSG_TX_CONFIRMATION;
msg.content.value = pkt->handle;
DEBUG("can_router_dispatch_tx_conf: frame=%p, pid=%" PRIkernel_pid "\n",
(void *)&pkt->frame, pkt->entry.target.pid);
if (_send_msg(&msg, &pkt->entry) <= 0) {
return -1;
}
return 0;
}
int can_router_dispatch_tx_error(can_pkt_t *pkt)
{
msg_t msg;
msg.type = CAN_MSG_TX_ERROR;
msg.content.value = pkt->handle;
DEBUG("can_router_dispatch_tx_error: frame=%p, pid=%" PRIkernel_pid "\n",
(void *)&pkt->frame, pkt->entry.target.pid);
if (_send_msg(&msg, &pkt->entry) <= 0) {
return -1;
}
return 0;
}
int can_router_free_frame(struct can_frame *frame)
{
can_pkt_t *pkt = NULL;
pkt = container_of(frame, can_pkt_t, frame);
DEBUG("can_router_free_frame: pkt=%p\n", (void*) pkt);
if (!pkt || (atomic_load(&pkt->ref_count) <= 0)) {
return -1;
}
atomic_fetch_sub(&pkt->ref_count, 1);
if (atomic_load(&pkt->ref_count) == 0) {
can_pkt_free(pkt);
}
return 0;
}

149
sys/include/can/can.h Normal file
View File

@ -0,0 +1,149 @@
/*
* 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
* @defgroup can_dll Data Link Layer
* @brief CAN Data Link Layer
*
* The Data Link Layer is composed of the device, router, pkt and dll files.
* It can be used to send and receive raw CAN frames through multiple CAN controllers.
*
* @{
*
*
* @file
* @brief Definitions high-level CAN interface
*
* @author Vincent Dupont <vincent@otakeys.com>
* @author Toon Stegen <toon.stegen@altran.com>
*/
#ifndef CAN_CAN_H
#define CAN_CAN_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#if defined(__linux__)
#include <linux/can.h>
#include <libsocketcan.h>
#else
/**
* @brief Max data length for a CAN frame
*/
#define CAN_MAX_DLEN (8)
/**
* @name CAN_ID flags and masks
* @{
*/
/* special address description flags for the CAN_ID */
#define CAN_EFF_FLAG (0x80000000U) /**< EFF/SFF is set in the MSB */
#define CAN_RTR_FLAG (0x40000000U) /**< remote transmission request */
#define CAN_ERR_FLAG (0x20000000U) /**< error message frame */
/* valid bits in CAN ID for frame formats */
#define CAN_SFF_MASK (0x000007FFU) /**< standard frame format (SFF) */
#define CAN_EFF_MASK (0x1FFFFFFFU) /**< extended frame format (EFF) */
#define CAN_ERR_MASK (0x1FFFFFFFU) /**< omit EFF, RTR, ERR flags */
/** @} */
/**
* @brief CAN operational and error states
*/
enum can_state {
CAN_STATE_ERROR_ACTIVE = 0, /**< RX/TX error count < 96 */
CAN_STATE_ERROR_WARNING, /**< RX/TX error count < 128 */
CAN_STATE_ERROR_PASSIVE, /**< RX/TX error count < 256 */
CAN_STATE_BUS_OFF, /**< RX/TX error count >= 256 */
CAN_STATE_STOPPED, /**< Device is stopped */
CAN_STATE_SLEEPING, /**< Device is sleeping */
CAN_STATE_MAX
};
/**
* @brief Controller Area Network Identifier structure
*
* bit 0-28 : CAN identifier (11/29 bit) right aligned for 11 bit
* bit 29 : error message frame flag (0 = data frame, 1 = error message)
* bit 30 : remote transmission request flag (1 = rtr frame)
* bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
*/
typedef uint32_t canid_t;
/**
* @brief Controller Area Network frame
*/
struct can_frame {
canid_t can_id; /**< 32 bit CAN_ID + EFF/RTR/ERR flags */
uint8_t can_dlc; /**< frame payload length in byte (0 .. CAN_MAX_DLEN) */
uint8_t __pad; /**< padding */
uint8_t __res0; /**< reserved / padding */
uint8_t __res1; /**< reserved / padding */
/** Frame data */
uint8_t data[CAN_MAX_DLEN] __attribute__((aligned(8)));
};
/**
* @brief Controller Area Network filter
*/
struct can_filter {
canid_t can_id; /**< CAN ID */
canid_t can_mask; /**< Mask */
};
/**
* @brief CAN bit-timing parameters
*
* For further information, please read chapter "8 BIT TIMING
* REQUIREMENTS" of the "Bosch CAN Specification version 2.0"
* at http://www.semiconductors.bosch.de/pdf/can2spec.pdf.
*/
struct can_bittiming {
uint32_t bitrate; /**< Bit-rate in bits/second */
uint32_t sample_point; /**< Sample point in one-tenth of a percent */
uint32_t tq; /**< Time quanta (TQ) in nanoseconds */
uint32_t prop_seg; /**< Propagation segment in TQs */
uint32_t phase_seg1; /**< Phase buffer segment 1 in TQs */
uint32_t phase_seg2; /**< Phase buffer segment 2 in TQs */
uint32_t sjw; /**< Synchronisation jump width in TQs */
uint32_t brp; /**< Bit-rate prescaler */
};
/**
* @brief CAN hardware-dependent bit-timing constant
*
* Used for calculating and checking bit-timing parameters
*/
struct can_bittiming_const {
uint32_t tseg1_min; /**< Time segment 1 = prop_seg + phase_seg1, min value */
uint32_t tseg1_max; /**< Time segment 1, max value */
uint32_t tseg2_min; /**< Time segment 2 = phase_seg2, min value */
uint32_t tseg2_max; /**< Time segment 2, max value */
uint32_t sjw_max; /**< Synchronisation jump width */
uint32_t brp_min; /**< Bit-rate prescaler, min value */
uint32_t brp_max; /**< Bit-rate prescaler, max value */
uint32_t brp_inc; /**< Bit-rate prescaler, increment */
};
#endif /* defined(__linux__) */
#ifdef __cplusplus
}
#endif
#endif /* CAN_CAN_H */
/** @} */

165
sys/include/can/common.h Normal file
View File

@ -0,0 +1,165 @@
/*
* 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
* @defgroup can_common Common
* @brief CAN stack common definitions
*
* This module defines the common part of the CAN stack, including structures
* and messages.
*
* @{
*
*
* @file
* @brief Definitions of high-level CAN interface
*
* @author Vincent Dupont <vincent@otakeys.com>
*/
#ifndef CAN_COMMON_H
#define CAN_COMMON_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <sys/uio.h>
#include "timex.h"
#include "thread.h"
#ifdef MODULE_CAN_MBOX
#include "mbox.h"
#endif
#include "net/gnrc/pktbuf.h"
/**
* @brief CAN options
*/
typedef enum {
CANOPT_BITTIMING, /**< bit timing parameter */
CANOPT_RX_FILTERS, /**< rx filters */
CANOPT_TEC, /**< Transmit Error Counter */
CANOPT_REC, /**< Receive Error Counter*/
CANOPT_LEC, /**< Last Error Code */
CANOPT_CLOCK, /**< controller main clock */
CANOPT_BITTIMING_CONST, /**< controller bittiming parameters */
CANOPT_STATE, /**< set controller state @ref canopt_state_t */
} canopt_t;
/**
* @brief CAN state options
*
* CAN state options to be used with @p CANOPT_STATE
*/
typedef enum {
CANOPT_STATE_OFF, /**< powered off */
CANOPT_STATE_SLEEP, /**< sleep mode */
CANOPT_STATE_LISTEN_ONLY, /**< listen only mode */
CANOPT_STATE_ON, /**< power on, rx / tx mode */
} canopt_state_t;
/**
* @brief Structure to pass a CAN option
*/
typedef struct {
canopt_t opt; /**< the option to get/set */
uint16_t context; /**< (optional) context for that option */
void *data; /**< data to set or buffer to read into */
uint16_t data_len; /**< size of the data / the buffer */
} can_opt_t;
/**
* @brief Messages which can be sent through the CAN stack
*/
enum can_msg {
/* High level messages */
CAN_MSG_ACK = 0x100, /**< acknowledgment */
CAN_MSG_SEND_FRAME, /**< send a frame */
CAN_MSG_ABORT_FRAME, /**< abort a frame */
CAN_MSG_SET, /**< set an option */
CAN_MSG_GET, /**< get an option */
CAN_MSG_SET_FILTER, /**< set a filter */
CAN_MSG_REMOVE_FILTER, /**< remove a filter */
CAN_MSG_POWER_UP, /**< power up */
CAN_MSG_POWER_DOWN, /**< power down */
#if defined(MODULE_CAN_TRX) || defined(DOXYGEN)
CAN_MSG_SET_TRX, /**< set a transceiver */
#endif
/* candev internal messages */
CAN_MSG_EVENT = 0x200, /**< driver event */
CAN_MSG_WAKE_UP, /**< driver has been woken up by bus */
CAN_MSG_TX_CONFIRMATION, /**< a frame has been sent */
CAN_MSG_RX_INDICATION, /**< a frame has been received */
CAN_MSG_TX_ERROR, /**< there was an error when transmitting */
CAN_MSG_RX_ERROR, /**< there was an error when receiving */
CAN_MSG_BUS_OFF, /**< bus-off detected */
CAN_MSG_ERROR_PASSIVE, /**< driver switched in error passive */
CAN_MSG_ERROR_WARNING, /**< driver reached error warning */
#if defined(MODULE_CAN_PM) || defined(DOXYGEN)
CAN_MSG_PM, /**< power management event */
#endif
/* isotp messages */
#if defined(MODULE_CAN_ISOTP) || defined(DOXYGEN)
CAN_MSG_ISOTP_RX_TIMEOUT = 0x400, /**< isotp rx timeout */
CAN_MSG_ISOTP_TX_TIMEOUT, /**< isotp tx timeout */
#endif
};
/**
* @brief Received data structure
*
* This structure is used when a layer sends received data
* to the upper layer
*/
typedef struct can_rx_data {
struct iovec data; /**< iovec containing received data */
void *arg; /**< upper layer private param */
gnrc_pktsnip_t *snip; /**< pointer to the allocated snip */
} can_rx_data_t;
/**
* @brief registry entry types
*/
typedef enum {
CAN_TYPE_DEFAULT = 0, /**< default entry (use msg) */
#if defined(MODULE_CAN_MBOX) || defined(DOXYGEN)
CAN_TYPE_MBOX, /**< mbox entry */
#endif
} can_reg_type_t;
/**
* @brief registry entry
*
* This structure is used through the stack to describe how to contact
* the upper layer and which CAN interface to use
*/
typedef struct can_reg_entry {
struct can_reg_entry *next; /**< next for linked list */
int ifnum; /**< interface number for the entry */
union {
kernel_pid_t pid; /**< pid of the thread when using msg */
#if defined(MODULE_CAN_MBOX) || defined(DOXYGEN)
mbox_t *mbox; /**< mbox pointer */
#endif
} target; /**< entry target */
#if defined(MODULE_CAN_MBOX) || defined(DOXYGEN)
can_reg_type_t type; /**< entry type */
#endif
} can_reg_entry_t;
#ifdef __cplusplus
}
#endif
#endif /* CAN_COMMON_H */
/** @} */

View File

@ -0,0 +1,202 @@
/*
* 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 conn_can
* @{
*
*
* @file
* @brief Definitions of generic CAN interface
*
* @author Vincent Dupont <vincent@otakeys.com>
*
*/
#ifndef CAN_CONN_ISOTP_H
#define CAN_CONN_ISOTP_H
#ifdef __cplusplus
extern "C" {
#endif
#include "can/can.h"
#include "can/isotp.h"
#include "mbox.h"
#if defined(MODULE_CONN_CAN_ISOTP_MULTI) || defined(DOXYGEN)
#include "mutex.h"
#ifndef CONN_CAN_ISOTP_MBOX_SIZE
/**
* @brief Mailbox size of a conn_can_isotp_t
*/
#define CONN_CAN_ISOTP_MBOX_SIZE (16)
#endif
/**
* @brief ISO-TP connection
*
* When conn_can_isotp_multi module is used, this is a 'master' connection
* which can be used to send and receive with multiple connections within
* a single thread.
*
* If conn_can_isotp_multi is not used, this is a simple ISO-TP connection
*/
typedef struct conn_can_isotp_master conn_can_isotp_t;
/**
* @brief ISO-TP salve connection
*
* This is a slave connection which exists only when conn_can_isotp_multi
* module is used.
*/
typedef struct conn_can_isotp_slave {
struct conn_can_isotp_slave *next; /**< Next slave in the list */
struct conn_can_isotp_master *master; /**< Master connection holding the mailbox */
struct isotp isotp; /**< ISO-TP parameters and status */
int ifnum; /**< interface number */
int bound; /**< 1 if connection is bound */
can_rx_data_t *rx; /**< Buffered rx data */
} conn_can_isotp_slave_t;
/**
* @brief ISO-TP master connection
*/
struct conn_can_isotp_master {
/* slave fields */
struct conn_can_isotp_slave *next; /**< First slave in the list */
struct conn_can_isotp_master *master; /**< Master connection */
struct isotp isotp; /**< ISO-TP parameters and status */
int ifnum; /**< interface number */
int bound; /**< 1 if connection is bound */
can_rx_data_t *rx; /**< Buffered rx data */
/* slave fields end */
mutex_t lock; /**< Master lock */
mbox_t mbox; /**< mailbox for the connection list */
/** Connection list message queue */
msg_t mbox_queue[CONN_CAN_ISOTP_MBOX_SIZE];
};
/**
* @brief Initialize a slave connection
*
* This initializes a slave connection.
*
* This must be called on slave connections when conn_can_isotp_multi is used.
* Does not exist otherwise.
*
* @param[in] master the master connection
* @param[inout] slave the slave connection to initialize
*/
static inline void conn_can_isotp_init_slave(conn_can_isotp_t *master, conn_can_isotp_slave_t *slave)
{
slave->next = NULL;
slave->master = master;
slave->rx = NULL;
}
#else
#ifndef CONN_CAN_ISOTP_MBOX_SIZE
/**
* @brief Mailbox size of a conn_can_isotp_t
*/
#define CONN_CAN_ISOTP_MBOX_SIZE (16)
#endif
/**
* @brief ISOTP connection
*/
typedef struct conn_can_isotp {
struct isotp isotp; /**< ISO-TP connection */
int ifnum; /**< interface number */
int bound; /**< 1 if connection is bound */
mbox_t mbox; /**< mbox */
/** message queue */
msg_t mbox_queue[CONN_CAN_ISOTP_MBOX_SIZE];
} conn_can_isotp_t;
#endif /* MODULE_CONN_CAN_ISOTP_MULTI */
/**
* @brief Create can isotp connection socket
*
* @param[inout] conn ISO-TP connection
* @param[in] options ISO-TP options
* @param[in] ifnum can device Interface
*
* @return 0 if socket was successfully connected
* @return any other negative number in case of an error
*/
int conn_can_isotp_create(conn_can_isotp_t *conn, struct isotp_options *options, int ifnum);
/**
* @brief Bind a can isotp connection
*
* @param[inout] conn ISO-TP connection
*
* @return 0 on success
* @return any other negative number in case of an error
*/
int conn_can_isotp_bind(conn_can_isotp_t *conn);
/**
* @brief Close can isotp connection socket
*
* @param[in] conn ISO-TP connection
*
* @return 0 if conn is closed correctly
* @return any other negative number in case of an error
*/
int conn_can_isotp_close(conn_can_isotp_t *conn);
/**
* @brief Receive isotp data
*
* @param[in] conn ISO-TP connection
* @param[out] buf buf to fill in with received data
* @param[in] size size of the buffer in bytes
* @param[in] timeout timeout in us, 0 for infinite
*
* @return the number of bytes received
* @return any other negative number in case of an error
*/
int conn_can_isotp_recv(conn_can_isotp_t *conn, void *buf, size_t size, uint32_t timeout);
/**
* @brief Generic can send
*
* @param[in] conn ISO-TP connection
* @param[in] buf data to send
* @param[in] size size of the buffer in bytes
* @param[in] flags make function blocked or not
* (CAN_ISOTP_TX_DONT_WAIT to ignore tx confirmation)
*
* @return the number of bytes sent
* @return any other negative number in case of an error
*/
int conn_can_isotp_send(conn_can_isotp_t *conn, const void *buf, size_t size, int flags);
#if defined(MODULE_CONN_CAN_ISOTP_MULTI) || defined(DOXYGEN)
/**
* @brief Wait for reception from multiple connections
*
* @param[out] conn ISO-TP connection which received data
* @param[in] master the master connection
* @param[in] timeout timeout in us, 0 for infinite wait
*
* @return 0 if OK, < 0 if error
*/
int conn_can_isotp_select(conn_can_isotp_slave_t **conn, conn_can_isotp_t *master, uint32_t timeout);
#endif
#ifdef __cplusplus
}
#endif
#endif /* CAN_CONN_ISOTP_H */
/** @} */

138
sys/include/can/conn/raw.h Normal file
View File

@ -0,0 +1,138 @@
/*
* 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
* @defgroup conn_can Connection
* @brief conn interface for CAN stack
*
* This is the user interface to send and receive raw CAN frames or ISO-TP datagrams
*
* @{
*
*
* @file
* @brief Definitions of generic CAN interface
*
* @author Vincent Dupont <vincent@otakeys.com>
*
*/
#ifndef CAN_CONN_RAW_H
#define CAN_CONN_RAW_H
#ifdef __cplusplus
extern "C" {
#endif
#include "can/can.h"
#include "can/raw.h"
#include "mbox.h"
#ifndef CONN_CAN_RAW_MBOX_SIZE
/**
* @brief Mailbox size of a conn_can_raw_t
*/
#define CONN_CAN_RAW_MBOX_SIZE (16)
#endif
/**
* @name flags values
* @{
*/
#define CONN_CAN_DONTWAIT (1) /**< Do not wait for Tx confirmation when sending */
#define CONN_CAN_RECVONLY (2) /**< Do not send anything on the bus */
/** @} */
/**
* @brief RAW CAN connection
*/
typedef struct conn_can_raw {
int ifnum; /**< Interface number of the can device */
int flags; /**< Config flags for that conn object */
size_t count; /**< number of filters set */
struct can_filter *filter; /**< list of filter */
mbox_t mbox; /**< mbox */
/**
* message queue
*/
msg_t mbox_queue[CONN_CAN_RAW_MBOX_SIZE];
} conn_can_raw_t;
/**
* @brief Create can connection socket
*
* @param[inout] conn CAN connection
* @param[in] filter list of filters to set
* @param[in] count number of filters in @p filter
* @param[in] ifnum can device Interface
* @param[in] flags conn flags to set (CONN_CAN_RECVONLY)
*
* @return 0 if socket was successfully connected
* @return any other negative number in case of an error
*/
int conn_can_raw_create(conn_can_raw_t *conn, struct can_filter *filter, size_t count,
int ifnum, int flags);
/**
* @brief Close can connection socket
*
* @param[in] conn CAN connection
*
* @return 0 if conn is closed correctly
* @return any other negative number in case of an error.
*/
int conn_can_raw_close(conn_can_raw_t *conn);
/**
* @brief Generic can receive
*
* @param[in] conn CAN connection
* @param[out] frame CAN frame to receive
* @param[in] timeout timeout in us, 0 for infinite
*
* @return the number of bytes received
* @return any other negative number in case of an error
*/
int conn_can_raw_recv(conn_can_raw_t *conn, struct can_frame *frame, uint32_t timeout);
/**
* @brief Generic can send
*
* @param[in] conn CAN connection
* @param[in] frame frame to send
* @param[in] flags make function blocked or not
* (CONN_CAN_DONTWAIT to ignore tx confirmation)
*
* @return the number of bytes sent
* @return any other negative number in case of an error
*/
int conn_can_raw_send(conn_can_raw_t *conn, const struct can_frame *frame, int flags);
/**
* @brief Set raw CAN filters
*
* If filters were already set for this connection, it first unsets the previous filters
* and sets the new ones.
*
* @param[in] conn CAN connection
* @param[in] filter list of filters to set
* @param[in] count number of filters in @p filter
*
* @return 0 if can filters were successfully set
* @return any other negative number in case of an error
*/
int conn_can_raw_set_filter(conn_can_raw_t *conn, struct can_filter *filter, size_t count);
#ifdef __cplusplus
}
#endif
#endif /* CAN_CONN_RAW_H */
/** @} */

122
sys/include/can/device.h Normal file
View File

@ -0,0 +1,122 @@
/*
* 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_dll
* @{
*
*
* @file
* @brief Definitions of CAN device interface
*
* @author Vincent Dupont <vincent@otakeys.com>
* @author Toon Stegen <toon.stegen@altran.com>
*/
#ifndef CAN_DEVICE_H
#define CAN_DEVICE_H
#ifdef __cplusplus
extern "C" {
#endif
#include "can/candev.h"
#include "kernel_types.h"
#ifdef MODULE_CAN_PM
#include "xtimer.h"
#endif
#ifdef MODULE_CAN_TRX
#include "can/can_trx.h"
#endif
#ifndef CAN_MAX_RATE_ERROR
/**
* Maximum bit-rate error allowed when computing bittimings
* in tenth of percent
*/
#define CAN_MAX_RATE_ERROR (50) /* 5 % */
#endif
#ifndef CAN_DLL_NUMOF
/**
* Maximum number of interfaces which can be registered on DLL
*/
#define CAN_DLL_NUMOF (1U)
#endif
/**
* @brief Parameters to initialize a candev
*/
typedef struct candev_params {
const char *name; /**< candev name to set */
#if defined(MODULE_CAN_TRX) || defined(DOXYGEN)
can_trx_t *trx; /**< transceiver to set */
#endif
#if defined(MODULE_CAN_PM) || defined(DOXYGEN)
uint32_t rx_inactivity_timeout; /**< power management rx timeout value */
uint32_t tx_wakeup_timeout; /**< power management tx wake up value */
#endif
} candev_params_t;
/**
* @brief candev descriptor to pass to the device thread
*/
typedef struct candev_dev {
candev_t *dev; /**< the device */
int ifnum; /**< interface number */
kernel_pid_t pid; /**< pid */
const char *name; /**< device name */
#if defined(MODULE_CAN_TRX) || defined(DOXYGEN)
can_trx_t *trx; /**< transceiver attached to the device */
#endif
#if defined(MODULE_CAN_PM) || defined(DOXYGEN)
uint32_t rx_inactivity_timeout; /**< Min timeout loaded when a frame is received */
uint32_t tx_wakeup_timeout; /**< Min timeout loaded when a frame is sent */
uint32_t last_pm_update; /**< time when the pm was updated */
uint32_t last_pm_value; /**< last pm timer value set */
xtimer_t pm_timer; /**< timer for power management */
#endif
} candev_dev_t;
/**
* @brief Initialize a CAN device thread
*
* This function sets up a CAN device thread
*
* @param[in] stack the device thread stack
* @param[in] stacksize the device thread stack size
* @param[in] priority the device thread priority
* @param[in] name the device thread name
* @param[in] params the parameters containing the device pointer and the ifnum
*
* @return the pid of the created thread
*/
kernel_pid_t can_device_init(char *stack, int stacksize, char priority,
const char *name, candev_dev_t *params);
/**
* @brief Fill in a @p bittiming structure from @p bittiming->bitrate and @p timing_const
*
* @param[in] clock the clock of the CAN controller
* @param[in] timing_const the timing parameter of the CAN controller
* @param[in,out] bittiming the calculated bittiming (bitrate field must be set)
*
* @return 0 on success
* @return < 0 on error
*/
int can_device_calc_bittiming(uint32_t clock, const struct can_bittiming_const *timing_const,
struct can_bittiming *bittiming);
#ifdef __cplusplus
}
#endif
#endif /* CAN_DEVICE_H */
/** @} */

110
sys/include/can/dll.h Normal file
View File

@ -0,0 +1,110 @@
/*
* 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_dll
* @{
*
*
* @file
* @brief Definitions of low-level CAN DLL interface
*
* @author Vincent Dupont <vincent@otakeys.com>
* @author Toon Stegen <toon.stegen@altran.com>
*/
#ifndef CAN_DLL_H
#define CAN_DLL_H
#ifdef __cplusplus
extern "C" {
#endif
#include "can/common.h"
#include "can/pkt.h"
#include "can/device.h"
#include "thread.h"
/**
* @brief Initialize the CAN DLL
*
* @return 0 on success
*/
int can_dll_init(void);
/**
* @brief Register a CAN device into the DLL
*
* This function must be called by the device thread to register the device into the DLL
*
* @param[in] candev the candev to register
*
* @return interface number on success
* @return -ENODEV if ifnum is invalid
*/
int can_dll_register_candev(candev_dev_t *candev);
/**
* @brief Dispatch a received frame
*
* This function is used to send a message to the DLL thread when a @p frame is received
* from the device identified by its @p pid
*
* @param[in] frame the received frame
* @param[in] pid the pid of the receiver device
*
* @return 0 on success
* @return -ENOMEM if the message can not be sent
*/
int can_dll_dispatch_rx_frame(struct can_frame *frame, kernel_pid_t pid);
/**
* @brief Dispatch a tx confirmation
*
* This function is used to send a message to the sender thread when the
* @p pkt has been sent correctly.
*
* @param[in] pkt the pkt which has been sent
*
* @return 0 on success
* @return -ENOMEM if the message can not be sent
*/
int can_dll_dispatch_tx_conf(can_pkt_t *pkt);
/**
* @brief Dispatch a tx error
*
* This function is used to send a message to the sender thread when the
* @p pkt has not been sent correctly
*
* @param[in] pkt the pkt which has not been sent correctly
*
* @return 0 on success
* @return -ENOMEM if the message can not be sent
*/
int can_dll_dispatch_tx_error(can_pkt_t *pkt);
/**
* @brief Dispatch RX error from a device
*
* Dispatch RX error from a device to receivers threads
* which have subscribed to frames on that interface
*
* @param[in] pid the device thread pid
*
* @return 0 on success
*/
int can_dll_dispatch_bus_off(kernel_pid_t pid);
#ifdef __cplusplus
}
#endif
#endif /* CAN_DLL_H */
/** @} */

23
sys/include/can/doc.txt Normal file
View File

@ -0,0 +1,23 @@
/**
* @defgroup can CAN
* @brief RIOT CAN stack
*
* This module is a full CAN stack integrated to RIOT.
* It includes a low-level interface, a data link layer, an ISO-TP layer and
* a user interface.
*
* The low-level interface, candev, must be implemented by controler drivers.
* The optional transceiver support can also be activated. Transceiver drivers must
* then implement the trx_can interface.
*
* The data link layer is built around a device thread (one thread per CAN device),
* and a common part. The common part is composed of the dll interface, for low-level
* calls (from the device) and the raw interface for upper-level calls.
* Internally it also uses the pkt module to allocate frames and the router module
* to manage CAN filters.
*
* The ISO-TP layer uses the data link layer to send and receive CAN frames.
*
* Finally, the connection layer is the user interface to send and receive raw
* CAN frames or ISO-TP datagrams.
*/

196
sys/include/can/isotp.h Normal file
View File

@ -0,0 +1,196 @@
/*
* 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
* @defgroup isotp ISOTP
* @brief ISO transport protocol over CAN (ISO15765)
* @{
*
*
* @file
* @brief ISO TP high level interface
*
* @author Vincent Dupont <vincent@otakeys.com>
*/
#ifndef CAN_ISOTP_H
#define CAN_ISOTP_H
#ifdef __cplusplus
extern "C" {
#endif
#include "can/can.h"
#include "can/common.h"
#include "thread.h"
#include "xtimer.h"
#include "net/gnrc/pktbuf.h"
/**
* @brief The isotp_fc_options struct
*
* It describes the flow control options
*/
struct isotp_fc_options {
uint8_t bs; /**< blocksize provided in FC frame, 0 = off */
/** separation time provided in FC frame
* 0x00 - 0x7F : 0 - 127 ms
* 0x80 - 0xF0 : reserved
* 0xF1 - 0xF9 : 100 us - 900 us
* 0xFA - 0xFF : reserved */
uint8_t stmin;
uint8_t wftmax; /**< max. number of wait frame transmiss., 0 = ignored */
};
/**
* @brief The isotp_options struct
*
* It describes the ISO-TP options
*/
struct isotp_options {
canid_t tx_id; /**< transmit CAN ID */
canid_t rx_id; /**< Receive CAN ID */
uint16_t flags; /**< set flags for isotp behaviour. */
uint8_t ext_address; /**< set address for extended addressing */
uint8_t txpad_content; /**< set content of padding byte (tx) */
uint8_t rx_ext_address; /**< set address for extended addressing */
};
/**
* @brief The tpcon struct
*
* It describes the current connection status
*/
struct tpcon {
unsigned idx; /**< current index in @p buf */
uint8_t state; /**< the protocol state */
uint8_t bs; /**< block size */
uint8_t sn; /**< current sequence number */
int tx_handle; /**< handle of the last sent frame */
gnrc_pktsnip_t *snip; /**< allocated snip containing data buffer */
};
/**
* @brief The isotp struct
*
* This is the main struct used by an ISO-TP channel
*/
struct isotp {
struct isotp *next; /**< next bound channel */
struct isotp_options opt; /**< channel options */
struct isotp_fc_options rxfc; /**< rx flow control options (defined locally) */
struct isotp_fc_options txfc; /**< tx flow control options (defined remotely) */
struct tpcon tx; /**< transmit state */
struct tpcon rx; /**< receive state */
xtimer_t tx_timer; /**< timer for tx operations */
xtimer_t rx_timer; /**< timer for rx operations */
can_reg_entry_t entry; /**< entry containing ifnum and upper layer msg system */
uint32_t tx_gap; /**< transmit gap from fc (in us) */
uint8_t tx_wft; /**< transmit wait counter */
void *arg; /**< upper layer private arg */
};
/**
* @name flags for isotp behaviour
* @{
*/
#define CAN_ISOTP_RX_FLAGS_MASK 0x0000FFFF /**< rx flags mask */
#define CAN_ISOTP_LISTEN_MODE 0x0001 /**< listen only flag (do not send FC) */
#define CAN_ISOTP_EXTEND_ADDR 0x0002 /**< enable extended addressing */
#define CAN_ISOTP_TX_PADDING 0x0004 /**< enable CAN frame padding tx path */
#define CAN_ISOTP_HALF_DUPLEX 0x0040 /**< half duplex error state handling */
#define CAN_ISOTP_RX_EXT_ADDR 0x0200 /**< different rx extended addressing */
#define CAN_ISOTP_TX_FLAGS_MASK 0xFFFF0000 /**< tx flags mask */
#define CAN_ISOTP_TX_DONT_WAIT 0x00010000 /**< do not send a tx confirmation msg */
/** @} */
/**
* @name default configuration values
* @{
*/
#define CAN_ISOTP_DEFAULT_FLAGS 0
#define CAN_ISOTP_DEFAULT_EXT_ADDRESS 0x00
#define CAN_ISOTP_DEFAULT_PAD_CONTENT 0xCC /* prevent bit-stuffing */
#define CAN_ISOTP_DEFAULT_FRAME_TXTIME 0
#define CAN_ISOTP_DEFAULT_RECV_BS 0
#define CAN_ISOTP_DEFAULT_RECV_STMIN 0x00
#define CAN_ISOTP_DEFAULT_RECV_WFTMAX 0
/** @} */
/**
* @brief Initialize the isotp layer
*
* @param stack stack for the isotp thread
* @param stacksize size of @p stack
* @param priority priority of the isotp thread
* @param name name of the isotp thread
*
* @return the pid of the isotp thread
*/
kernel_pid_t isotp_init(char *stack, int stacksize, char priority, const char *name);
/**
* @brief Send data through an isotp channel
*
* @param isotp the channel to use
* @param buf the data to send
* @param len length of the data to send
* @param flags flags for sending
*
* @return the number of bytes sent
* @return < 0 if an error occured (-EBUSY, -ENOMEM)
*/
int isotp_send(struct isotp *isotp, const void *buf, int len, int flags);
/**
* @brief Bind an isotp channel
*
* Initialize the channel, set the filter on the DLL and add the
* channel to the list of bound channels
*
* @param isotp the channel to bind
* @param entry entry identifying the CAN ifnum and the upper layer
* either by its pid or its mailbox
* @param arg upper layer private parameter
*
* @return 0 on success, < 0 on error
*/
int isotp_bind(struct isotp *isotp, can_reg_entry_t *entry, void *arg);
/**
* @brief Release a bound isotp channel
*
* Unset the filter on the DLL and remove the channel from the list
* of bound channels
*
* @param isotp the channel to relase
*
* @return 0 on success, < 0 on error
*/
int isotp_release(struct isotp *isotp);
/**
* @brief Free a received buffer
*
* This MUST be called by the upper layer when the received data are read
*
* @param rx the received data
*/
void isotp_free_rx(can_rx_data_t *rx);
#ifdef __cplusplus
}
#endif
#endif /* CAN_ISOTP_H */
/** @} */

152
sys/include/can/pkt.h Normal file
View File

@ -0,0 +1,152 @@
/*
* 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_dll
* @{
*
*
* @file
* @brief CAN memory allocation module
*
* @author Vincent Dupont <vincent@otakeys.com>
* @author Toon Stegen <toon.stegen@altran.com>
*/
#ifndef CAN_PKT_H
#define CAN_PKT_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdatomic.h>
#include "net/gnrc/pktbuf.h"
#include "can/common.h"
#include "can/can.h"
#include "msg.h"
#ifdef MODULE_CAN_MBOX
#include "mbox.h"
#endif
/**
* @brief A CAN packet
*
* A CAN packet is used to add stack-related data around a CAN frame
*/
typedef struct {
can_reg_entry_t entry; /**< entry containing ifnum and upper layer info */
atomic_uint ref_count; /**< Reference counter (for rx frames) */
int handle; /**< handle (for tx frames */
struct can_frame frame; /**< CAN Frame */
gnrc_pktsnip_t *snip; /**< Pointer to the allocated snip */
} can_pkt_t;
/**
* @brief Initialize the CAN packet module
*
* This must be called by the DLL to initialize the module
*/
void can_pkt_init(void);
/**
* @brief Allocate a CAN packet to transmit
*
* This function allocates a CAN packet and associates it to the @p ifnum and @p tx_pid.
* The provided @p frame is copied into the CAN packet and a unique handle is set.
*
* @param[in] ifnum the interface number
* @param[in] frame the frame to copy
* @param[in] tx_pid the pid of the sender's device thread
*
* @return an allocated CAN packet, NULL if an error occured
*/
can_pkt_t *can_pkt_alloc_tx(int ifnum, const struct can_frame *frame, kernel_pid_t tx_pid);
/**
* @brief Allocate an incoming CAN packet
*
* @param[in] ifnum the interface number
* @param[in] frame the received frame
*
* @return an allocated CAN packet, NULL if an error occured
*/
can_pkt_t *can_pkt_alloc_rx(int ifnum, const struct can_frame *frame);
#if defined(MODULE_CAN_MBOX) || defined(DOXYGEN)
/**
* @brief Allocate a CAN packet for a mbox to transmit
*
* This function allocates a CAN packet and associate it to the @p ifnum and @p mbox.
* The provided @p frame is copied into the CAN packet and a unique handle is set.
*
* @param[in] ifnum the interface number
* @param[in] frame the frame to copy
* @param[in] mbox the pointer to the sender's mbox
*
* @return an allocated CAN packet, NULL if an error occured
*/
can_pkt_t *can_pkt_alloc_mbox_tx(int ifnum, const struct can_frame *frame, mbox_t *mbox);
#endif
/**
* @brief Free a CAN packet
*
* @param[in] pkt the packet to free, it must be a pointer returned
* by @ref can_pkt_alloc_tx or @ref can_pkt_alloc_rx
*/
void can_pkt_free(can_pkt_t *pkt);
/**
* @brief Allocate a @p can_rx_data_t and initialize it with gieven parameters
*
* This is used to allocate a return value to the upper layer
*
* @param[in] data data which will be returned
* @param[in] len length of @p data
* @param[in] arg optional argument for the upper layer
*
* @return a @p can_rx_data_t pointer, NULL if out of memory
*/
can_rx_data_t *can_pkt_alloc_rx_data(void *data, size_t len, void *arg);
/**
* @brief Free rx data previously allocated by can_pkt_alloc_rx_data()
*
* @param[in] data the pointer to free
*/
void can_pkt_free_rx_data(can_rx_data_t *data);
/**
* @brief Allocate @p size bytes and return the pointer
*
* This function has been copied from gnrc_pktbuf_static
*
* @param[in] size the number of bytes to allocate
*
* @return the pointer to thje allocated data, NULL if out of memory
*/
void *can_pkt_buf_alloc(size_t size);
/**
* @brief Free the data allocated by can_pkt_buf_alloc()
*
* @param[in] data the pointer to free
* @param[in] size the size of the data to free
*/
void can_pkt_buf_free(void *data, size_t size);
#ifdef __cplusplus
}
#endif
#endif /* CAN_PKT_H */
/** @} */

279
sys/include/can/raw.h Normal file
View File

@ -0,0 +1,279 @@
/*
* 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_dll
* @{
*
*
* @file
* @brief Definitions high-level RAW CAN interface
*
* This file defines the hig-level CAN interface to send and receive RAW CAN frame.
*
* @author Vincent Dupont <vincent@otakeys.com>
* @author Toon Stegen <toon.stegen@altran.com>
* @author Aurelien Gonce <aurelien.gonce@altran.com>
*/
#ifndef CAN_RAW_H
#define CAN_RAW_H
#ifdef __cplusplus
extern "C" {
#endif
#include "kernel_types.h"
#include "can/can.h"
#include "can/common.h"
#include "can/device.h"
#ifdef MODULE_CAN_MBOX
#include "mbox.h"
#endif
#ifdef MODULE_TRX
#include "can/can_trx.h"
#endif
/**
* @brief Default value for undefined interface number
*/
#define RAW_CAN_DEV_UNDEF (-1)
/**
* @brief Send a CAN frame
*
* Send a CAN @p frame through the @p ifnum interface. The result is
* sent to the @p pid thread via IPC.
*
* @param[in] ifnum the interface number to send to
* @param[in] frame the frame to send
* @param[in] pid the user thread id to whom the result msg will be sent
* it can be THREAD_PID_UNDEF if no feedback is expected
*
* @return a positive handle identifying the sent frame on success
* @return < 0 on error
*/
int raw_can_send(int ifnum, const struct can_frame *frame, kernel_pid_t pid);
/**
* @brief Abort a CAN frame
*
* Abort the frame identified by @p handle in the interface @p ifnum
* If no tx confirmation is received, this function must be called by the upper layer
* to ensure the driver frees its tx mailbox. The driver is not responsible of tx timeouts.
*
* @param[in] ifnum the interface number used to send the frame
* @param[in] handle the handle of the frame to abort,
* it must be the value returned by raw_can_send
* @return 0 on succes
* @return < 0 on error (-ENODEV)
*/
int raw_can_abort(int ifnum, int handle);
/**
* @brief Subscribe to a CAN filter
*
* This function must be called if a user thread @p pid wants to receive the CAN frame matching @p filter
* on the interface @p ifnum.
* The user thread will then receive msg via IPC on reception of frame matching @p filters.
*
* @param[in] ifnum the interface number to listen
* @param[in] filter the list of filter to receive
* @param[in] pid the thread id of the user
* @param[in] param optional user parameter
*
* @return the @p ifnum on success
* @return < 0 on error
*/
int raw_can_subscribe_rx(int ifnum, struct can_filter *filter, kernel_pid_t pid, void *param);
/**
* @brief Unsubscribe from reception for the given CAN @p filter on @p pid thread
*
* @param[in] ifnum the interface number
* @param[in] filter the filter to remove
* @param[in] pid the thread id of the user
* @param[in] param optional user parameter
*
* @return 0 on success
* @return < 0 on error
*/
int raw_can_unsubscribe_rx(int ifnum, struct can_filter *filter, kernel_pid_t pid, void *param);
/**
* @brief Free a received frame
*
* This function must be called by the user when a received frame is not needed anymore.
*
* @param[in] frame the frame to free, it must be a pointer to a frame received by the stack
*
* @return 0 on success
* @return < 0 on error
*/
int raw_can_free_frame(can_rx_data_t *frame);
/**
* @brief Get a CAN option @p opt from interface @p ifnum
*
* @param[in] ifnum the interface number
* @param[in,out] opt the option to get
*
* @return 0 on success
* @return < 0 on error
*/
int raw_can_get_can_opt(int ifnum, can_opt_t *opt);
/**
* @brief Set a CAN option @p opt to interface @p ifnum
*
* @param[in] ifnum the interface number
* @param[in,out] opt the option to set
*
* @return 0 on success
* @return < 0 on error
*/
int raw_can_set_can_opt(int ifnum, can_opt_t *opt);
#if defined(MODULE_CAN_MBOX) || defined(DOXYGEN)
/**
* @brief Send a CAN frame
*
* Send a CAN @p frame through the @p ifnum interface. The result is
* sent to the @p mbox thread via mailbox IPC.
*
* @param[in] ifnum the interface number to send to
* @param[in] frame the frame to send
* @param[in] mbox the user mbox to whom the result msg will be sent
* it can be NULL if no feedback is expected
*
* @return a positive handle identifying the sent frame on success
* @return < 0 on error
*/
int raw_can_send_mbox(int ifnum, const struct can_frame *frame, mbox_t *mbox);
/**
* @brief Subscribe to a CAN filter
*
* This function must be called if a user thread waiting on @p mbox wants to receive
* the CAN frame matching @p filter on the interface @p ifnum.
* The user thread will then receive msg via mailbox IPC on reception of frame matching @p filters.
*
* Currently only single frame ID (i.e. filters->can_mask = 0xFFFFFFFF) are supported.
*
* @param[in] ifnum the interface number to listen
* @param[in] filter the list of filter to receive
* @param[in] mbox the mbox of the user
* @param[in] param optional user parameter
*
* @return the @p ifnum on success
* @return < 0 on error
*/
int raw_can_subscribe_rx_mbox(int ifnum, struct can_filter *filter, mbox_t *mbox, void *param);
/**
* @brief Unsubscribe from reception for the given CAN @p filter and @p mbox
*
* @param[in] ifnum the interface number
* @param[in] filter the filter to remove
* @param[in] mbox the mbox of the user
* @param[in] param optional user parameter
*
* @return 0 on success
* @return < 0 on error
*/
int raw_can_unsubscribe_rx_mbox(int ifnum, struct can_filter *filter, mbox_t *mbox, void *param);
#endif
/**
* @brief Power down a given interface
*
* @param[in] ifnum the interface number to power down
*
* @return 0 on success
* @return < 0 on error
*/
int raw_can_power_down(int ifnum);
/**
* @brief Power up a given interface
*
* @param[in] ifnum the interface number to power up
*
* @return 0 on success
* @return < 0 on error
*/
int raw_can_power_up(int ifnum);
/**
* @brief Get the interface number of a given interface
*
* @param[in] name interface name
*
* @return the interface number, RAW_CAN_DEV_UNDEF if not defined
*/
int raw_can_get_ifnum_by_name(const char *name);
/**
* @brief Get the interface name of a given interface number
*
* @param[in] ifnum interface number
*
* @return the interface name, NULL if no interface registered with this number
*/
const char *raw_can_get_name_by_ifnum(int ifnum);
/**
* @brief Get the candev descriptor from a given interface number
*
* @param[in] ifnum interface number
*
* @return pointer to a candev descriptor, NULL if no interface is registered with
* this number
*/
candev_dev_t *raw_can_get_dev_by_ifnum(int ifnum);
/**
* @brief Set the given bitrate/sample_point to the given ifnum
*
* Set the given @p bitrate and @p sample_point to the given @p ifnum. This is a
* helper function which calculates the right bittiming from @p bitrate and
* @p sample_point.
*
* @param[in] ifnum the interface number
* @param[in] bitrate the bitrate in bits/s
* @param[in] sample_point the sample point in tenth of percent (875 = 87.5%)
* if not set, the default value of 87.5% is used
* @return 0 on success
* @return 1 if the bitrate/sample_point couple can not be reached precisely but the bitrate is set
* @return < 0 on error
*/
int raw_can_set_bitrate(int ifnum, uint32_t bitrate, uint32_t sample_point);
#if defined(MODULE_CAN_TRX) || defined(DOXYGEN)
/**
* @brief Set a transceiver for a given interface
*
* The interface must be powered down before changing the transceiver.
*
* @param[in] ifnum the interface number
* @param[in] trx the transceiver to set
*
* @return 0 on success
* @return < 0 on error (-EBUSY if device is not powered down)
*/
int raw_can_set_trx(int ifnum, can_trx_t *trx);
#endif
#ifdef __cplusplus
}
#endif
#endif /* CAN_RAW_H */
/** @} */

116
sys/include/can/router.h Normal file
View File

@ -0,0 +1,116 @@
/*
* 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_dll
* @{
*
*
* @file
* @brief Functions for routing RX can frames
*
* @author Toon Stegen <toon.stegen@altran.com>
* @author Vincent Dupont <vincent@otakeys.com>
*/
#ifndef CAN_ROUTER_H
#define CAN_ROUTER_H
#ifdef __cplusplus
extern "C" {
#endif
#include "kernel_types.h"
#include "can/can.h"
#include "can/pkt.h"
/**
* @brief Register a user @p entry to receive a frame @p can_id
*
* @param[in] entry the entry containing ifnum and user info
* @param[in] can_id the CAN ID of the frame to receive
* @param[in] mask the mask of the frame to receive
* @param[in] param a user private pointer
*
* @return 0 on success
* @return < 0 on error
*/
int can_router_register(can_reg_entry_t *entry, canid_t can_id, canid_t mask, void *param);
/**
* @brief Unregister a user @p entry from receiving @p can_id
*
* The filter is unregistered from the 'router' layer if @p can_id, @p mask and @p param
* matches a registered entry.
*
* @param[in] entry the entry containing ifnum and user info which was registered
* @param[in] can_id the CAN ID of the frame to stop receiving
* @param[in] mask the mask of the frame to stop receiving
* @param[in] param a user private pointer
*
* @return 0 if @p can_id is not used anymore
* @return 1 if @p can_id is still used by another pid
* @return < 0 on error
*/
int can_router_unregister(can_reg_entry_t *entry, canid_t can_id, canid_t mask, void *param);
/**
* @brief Free a received frame
*
* This function decrements the ref counter of the packet and frees it if the packet
* is no more in use.
*
* @param[in] frame the frame to free, it must be a frame returned by the stack
*
* @return 0 on success
* @return < 0 on error
*/
int can_router_free_frame(struct can_frame *frame);
/**
* @brief Dispatch a RX indication to subscribers threads
*
* This function goes through the list of subscribed filters to send a message to each
* subscriber's thread. If all the subscriber's threads cannot receive message,
* the packet is freed.
*
* @param[in] pkt the packet to dispatch
*
* @return 0 on success
* @return < 0 on error, if at least a thread cannot receive message
*/
int can_router_dispatch_rx_indic(can_pkt_t *pkt);
/**
* @brief Dispatch a TX confirmation to the sender's thread
*
* @param[in] pkt the correctly sent packet
*
* @return 0 on success
* @return < 0 on error
*/
int can_router_dispatch_tx_conf(can_pkt_t *pkt);
/**
* @brief Dispatch a TX error to the sender's thread
*
* @param[in] pkt the error packet
*
* @return 0 on success
* @return < 0 on error
*/
int can_router_dispatch_tx_error(can_pkt_t *pkt);
#ifdef __cplusplus
}
#endif
#endif /* CAN_ROUTER_H */
/** @} */

View File

@ -64,6 +64,9 @@ endif
ifneq (,$(filter vfs,$(USEMODULE)))
SRC += sc_vfs.c
endif
ifneq (,$(filter conn_can,$(USEMODULE)))
SRC += sc_can.c
endif
# TODO
# Conditional building not possible at the moment due to

184
sys/shell/commands/sc_can.c Normal file
View File

@ -0,0 +1,184 @@
/*
* Copyright 2017 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 sys_shell_commands
* @{
*
* @file
* @brief Shell command implementation for CAN stack
*
* @author Vincent Dupont <vincent@otakeys.com>
*
* @}
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "can/conn/raw.h"
#include "can/raw.h"
#define SC_CAN_MAX_FILTERS 10
#define xstr(a) str(a)
#define str(a) #a
static int _can_usage(void);
static int _list(int argc, char **argv)
{
(void)argc;
(void)argv;
for (int i = 0; i < CAN_DLL_NUMOF; i++) {
const char *name = raw_can_get_name_by_ifnum(i);
if (name) {
printf("CAN #%d: %s\n", i, name);
}
else {
break;
}
}
return 0;
}
static int _send(int argc, char **argv)
{
if (argc < 3 || argc > 11) {
_can_usage();
return 1;
}
conn_can_raw_t conn;
struct can_frame frame;
int ifnum = atoi(argv[1]);
if (ifnum >= CAN_DLL_NUMOF) {
puts("Invalid ifnum");
return 1;
}
frame.can_id = strtoul(argv[2], NULL, 16);
frame.can_dlc = argc - 3;
for (int i = 0; i < frame.can_dlc; i++) {
frame.data[i] = strtoul(argv[3 + i], NULL, 16);
}
conn_can_raw_create(&conn, NULL, 0, ifnum, 0);
conn_can_raw_send(&conn, &frame, 0);
return 0;
}
static int _dump(int argc, char **argv)
{
if (argc < 4) {
_can_usage();
return 0;
}
int ret;
struct can_filter filters[SC_CAN_MAX_FILTERS];
conn_can_raw_t conn;
struct can_frame frame;
int ifnum = atoi(argv[1]);
int cnt = atoi(argv[2]);
uint32_t ms = strtoul(argv[3], NULL, 0);
size_t nb_filters = 1;
if (ifnum >= CAN_DLL_NUMOF) {
puts("Invalid ifnum");
return 1;
}
if (cnt) {
cnt++;
}
if (argc > 4) {
char *p = argv[4];
char *end;
int i = 0;
while (*p != '\0' && i < SC_CAN_MAX_FILTERS) {
filters[i].can_id = strtoul(p, &end, 16);
if (*end == ':') {
p = end + 1;
filters[i].can_mask = strtoul(p, &end, 16);
}
else {
filters[i].can_mask = 0xffffffff;
}
if (*end == ',') {
p = end + 1;
}
else {
p = end;
}
i++;
}
nb_filters = i;
}
else {
filters[0].can_id = 0;
filters[0].can_mask = 0;
}
conn_can_raw_create(&conn, filters, nb_filters, ifnum, 0);
while ((cnt != 1) &&
((ret = conn_can_raw_recv(&conn, &frame, ms * US_PER_MS))
== sizeof(struct can_frame))) {
printf("%-8s(%d) %8" PRIX32 " [%x] ",
raw_can_get_name_by_ifnum(ifnum), ifnum,
frame.can_id, frame.can_dlc);
for (int i = 0; i < frame.can_dlc; i++) {
printf(" %02X", frame.data[i]);
}
printf("\n");
if (cnt) {
cnt--;
}
}
if (ret < 0) {
puts("Timeout");
}
puts("Closing");
conn_can_raw_close(&conn);
return 0;
}
static int _can_usage(void)
{
puts("usage: can <command> [arguments]");
puts("commands:");
puts("\tlist");
puts("\tsend ifnum id [B1 .. B8]");
puts("\tdump ifnum nb ms [id1[:mask1][,id2[:mask2], .. id"
xstr(SC_CAN_MAX_FILTERS) ":[mask" xstr(SC_CAN_MAX_FILTERS) "]]");
return 0;
}
int _can_handler(int argc, char **argv)
{
if (argc < 2) {
_can_usage();
return 1;
}
else if (strncmp(argv[1], "list", 5) == 0) {
return _list(argc - 1, argv + 1);
}
else if (strncmp(argv[1], "send", 5) == 0) {
return _send(argc - 1, argv + 1);
}
else if (strncmp(argv[1], "dump", 5) == 0) {
return _dump(argc - 1, argv + 1);
}
else {
printf("unknown command: %s\n", argv[1]);
return 1;
}
return 0;
}

View File

@ -132,6 +132,10 @@ extern int _vfs_handler(int argc, char **argv);
extern int _ls_handler(int argc, char **argv);
#endif
#ifdef MODULE_CONN_CAN
extern int _can_handler(int argc, char **argv);
#endif
const shell_command_t _shell_command_list[] = {
{"reboot", "Reboot the node", _reboot_handler},
#ifdef MODULE_CONFIG
@ -220,6 +224,9 @@ const shell_command_t _shell_command_list[] = {
#ifdef MODULE_VFS
{"vfs", "virtual file system operations", _vfs_handler},
{"ls", "list files", _ls_handler},
#endif
#ifdef MODULE_CONN_CAN
{"can", "CAN commands", _can_handler},
#endif
{NULL, NULL, NULL}
};

24
tests/conn_can/Makefile Normal file
View File

@ -0,0 +1,24 @@
export APPLICATION = can
include ../Makefile.tests_common
BOARD_INSUFFICIENT_MEMORY := chronos msb-430 msb-430h nucleo32-f031 nucleo32-f042 \
nucleo32-f303 nucleo32-l031 nucleo-f030 nucleo-f070 \
nucleo-f072 nucleo-f302 nucleo-f303 nucleo-f334 \
nucleo-l053 stm32f0discovery telosb weio wsn430-v1_3b \
wsn430-v1_4 z1
CFLAGS += -DDEVELHELP
CFLAGS += -DLOG_LEVEL=LOG_ALL
USEMODULE += shell
USEMODULE += shell_commands
USEMODULE += ps
USEMODULE += conn_can
USEMODULE += can_isotp
USEMODULE += conn_can_isotp_multi
USEMODULE += can_pm
USEMODULE += can_trx
include $(RIOTBASE)/Makefile.include

152
tests/conn_can/README.md Normal file
View File

@ -0,0 +1,152 @@
tests/conn_can
================
Demo application for the CAN stack with conn_can interface.
Native prerequisites
============
For using the can stack on top of socketCAN, available for linux, you need:
- socketCAN (part of kernel starting from 2.6.25)
- install the 32bit version of libsocketcan:
if you're on a 64bit system:
```
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install libsocketcan-dev:i386
```
On 32 bit you can just do the following:
```
sudo apt-get install libsocketcan-dev
```
Alternatively, you can compile from source:
```
wget http://www.pengutronix.de/software/libsocketcan/download/libsocketcan-0.0.10.tar.bz2
$ sudo tar xvjf libsocketcan-0.0.10.tar.bz2
$ sudo rm -rf libsocketcan-0.0.10.tar.bz2
$ sudo cd libsocketcan-0.0.10
$ sudo ./configure
compile in 32bits
./configure --build=i686-pc-linux-gnu "CFLAGS=-m32" "CXXFLAG
$ sudo make
$ sudo make install
sudo ldconfig
/usr/local/lib
```
The default native configuration defines two virtual can ifaces to be used.
Before running this test on native, you should create those:
```
sudo modprobe vcan
sudo ip link add dev vcan0 type vcan
sudo ip link add dev vcan1 type vcan
sudo ip link set vcan0 up
sudo ip link set vcan1 up
```
Usage
=====
Build, flash and start the application:
```
export BOARD=your_board
make
make flash
make term
```
The CAN interfaces are registered at startup to the dll. The list of registered
interfaces and their RIOT names can be retrieved with:
```
can list
```
To send a raw CAN frame, id 0x100 with 2 bytes of data 01 02 on interface 0:
```
can send 0 100 01 02
```
Two threads are launched to enable receiving frames. To receive raw CAN frames,
ids 0x100 and 0x500 with thread 0 on interface 1, with 10s timeout:
```
can recv 1 0 10000000 100 500
```
A connection can be closed with its thread id, for instance:
```
can close 0
```
To send an ISO-TP datagram, first bind a connection with one of the threads,
source id 700, dest id 708, thread 1 and interface 0:
```
can bind_isotp 0 1 700 708
```
Then send the data 01 02 03 04 05 0a 0b 0c:
```
can send_isotp 1 01 02 03 04 05 0a 0b 0c
```
To receive from an ISO-TP channel, it must be bound, then with the previous channel,
and 10s timeout:
```
can recv_isotp 1 10000000
```
An ISO-TP channel can be closed with:
```
can close_isotp 1
```
You can also set a bitrate (this won't work on native with vcan, only with real
interfaces, but then root access are needed), for instance 250000 bit/s with
sampling point 87.5%:
```
can set_bitrate 250000 875
```
Linux CAN basic commands
========================
Once the interfaces are set up, can-utils commands provide a way to send and receive
raw CAN frames and ISO-TP datagrams.
For ISO-TP, an experimental module for linux can be found [here](https://github.com/hartkopp/can-isotp).
It needs to be loaded before trying to use ISO-TP protocol.
Here are some basics examples.
Send a raw CAN frame, id 0x100, data 00 11 22:
```
cansend vcan0 100#001122
```
Dump the traffic on a CAN interface:
```
candump vcan0
```
Send an ISO-TP datagram, source id 700, dest id 708, data 00 11 22 33 aa bb cc dd:
```
echo 00 11 22 33 aa bb cc dd | isotpsend -s 700 -d 708 vcan0
```
Receive ISO-TP datagram:
```
isotprecv -s 708 -d 700 vcan0
```
Please read commands help for more details on usage.

634
tests/conn_can/main.c Normal file
View File

@ -0,0 +1,634 @@
/*
* 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 tests
* @{
*
* @file
* @brief main
*
* @author Vincent Dupont <vincent@otakeys.com>
*
* @}
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include "shell.h"
#include "board.h"
#include "periph/gpio.h"
#include "thread.h"
#include "can/can.h"
#include "can/conn/raw.h"
#include "can/conn/isotp.h"
#include "can/device.h"
#define THREAD_STACKSIZE (THREAD_STACKSIZE_MAIN)
#define RECEIVE_THREAD_MSG_QUEUE_SIZE (8)
#include "timex.h"
#define TEST_CONN_CAN_RECV_TIMEOUT (30 * US_PER_SEC)
#define RCV_THREAD_NUMOF (2)
#define MAX_FILTER (16)
#define CAN_MSG_RECV 0x400
#define CAN_MSG_BIND_ISOTP 0x401
#define CAN_MSG_RECV_ISOTP 0x402
#define CAN_MSG_CLOSE_ISOTP 0x403
#define CAN_MSG_SEND_ISOTP 0x404
static char thread_stack[RCV_THREAD_NUMOF][THREAD_STACKSIZE];
static kernel_pid_t receive_pid[RCV_THREAD_NUMOF];
static conn_can_raw_t conn[RCV_THREAD_NUMOF];
static struct can_filter filters[RCV_THREAD_NUMOF][MAX_FILTER];
#ifdef MODULE_CAN_ISOTP
#define ISOTP_BUF_SIZE 1024
static uint8_t isotp_buf[RCV_THREAD_NUMOF][ISOTP_BUF_SIZE];
static conn_can_isotp_t conn_isotp[RCV_THREAD_NUMOF];
#endif
static int thread_busy[RCV_THREAD_NUMOF];
static void print_usage(void)
{
puts("test_can list");
puts("test_can send ifnum can_id [B1 [B2 [B3 [B4 [B5 [B6 [B7 [B8]]]]]]]]");
printf("test_can recv ifnum user_id timeout can_id1 [can_id2..can_id%d]\n", MAX_FILTER);
puts("test_can close user_id");
#ifdef MODULE_CAN_ISOTP
puts("test_can bind_isotp ifnum user_id source_id dest_id");
puts("test_can send_isotp user_id [B1 [.. [ Bn ]]]");
puts("test_can recv_isotp user_id timeout");
puts("test_can close_isotp user_id");
#endif
puts("test_can get_filter ifnum");
puts("test_can set_bitrate ifnum bitrate [sample_point]");
puts("test_can get_bitrate ifnum");
puts("test_can get_counter ifnum");
puts("test_can power_up ifnum");
puts("test_can power_down ifnum");
}
static int _list(int argc, char **argv) {
(void)argc;
(void)argv;
for (int i = 0; i < CAN_DLL_NUMOF; i++) {
const char *name = raw_can_get_name_by_ifnum(i);
if (name) {
printf("CAN #%d: %s\n", i, name);
}
else {
break;
}
}
return 0;
}
static int _send(int argc, char **argv)
{
if (argc < 5) {
print_usage();
return 1;
}
struct can_frame frame;
int ifnum = strtol(argv[2], NULL, 0);
if (ifnum >= CAN_DLL_NUMOF) {
puts("Invalid interface number");
return 1;
}
frame.can_id = strtoul(argv[3], NULL, 16);
frame.can_dlc = argc - 4;
if (frame.can_dlc > 8) {
puts("Invalid length");
return 1;
}
for (int i = 0; i < frame.can_dlc; i++) {
frame.data[i] = strtol(argv[4 + i], NULL, 16);
}
conn_can_raw_t conn;
conn_can_raw_create(&conn, NULL, 0, ifnum, 0);
int ret = conn_can_raw_send(&conn, &frame, 0);
if (ret < 0) {
puts("Error when trying to send");
}
return 0;
}
static int _receive(int argc, char **argv)
{
if (argc < 4) {
print_usage();
return 1;
}
int res;
int ifnum = strtol(argv[2], NULL, 0);
if (ifnum >= CAN_DLL_NUMOF) {
puts("Invalid interface number");
return 1;
}
int thread_nb = strtol(argv[3], NULL, 0);
int filt_num = argc - 5;
if (thread_nb >= RCV_THREAD_NUMOF) {
printf("Invalid thread number, range=0..%d\n", RCV_THREAD_NUMOF - 1);
return 1;
}
if (thread_busy[thread_nb]) {
puts("Thread already in use");
return 1;
}
if (filt_num > MAX_FILTER) {
puts("Too many filters");
return 1;
}
for (int i = 0; i < filt_num; i++) {
filters[thread_nb][i].can_id = strtoul(argv[5 + i], NULL, 16);
filters[thread_nb][i].can_mask = 0xffffffff;
}
uint32_t timeout = strtoul(argv[4], NULL, 0);
msg_t msg;
msg.type = CAN_MSG_RECV;
msg.content.value = timeout;
res = conn_can_raw_create(&conn[thread_nb], filters[thread_nb],
filt_num, ifnum, 0);
if (res < 0) {
puts("Error when setting filters");
return 1;
}
thread_busy[thread_nb] = 1;
msg_send(&msg, receive_pid[thread_nb]);
return 0;
}
static int _close(int argc, char **argv)
{
if (argc < 2) {
print_usage();
return 1;
}
int thread_nb = strtol(argv[2], NULL, 0);
if (thread_nb >= RCV_THREAD_NUMOF) {
printf("Invalid thread number, range=0..%d\n", RCV_THREAD_NUMOF - 1);
return 1;
}
conn_can_raw_close(&conn[thread_nb]);
thread_busy[thread_nb] = 0;
return 0;
}
#ifdef MODULE_CAN_ISOTP
static int _bind_isotp(int argc, char **argv)
{
if (argc < 4) {
print_usage();
return 1;
}
int ret;
int ifnum = strtol(argv[2], NULL, 0);
if (ifnum >= CAN_DLL_NUMOF) {
puts("Invalid interface number");
return 1;
}
int thread_nb = strtol(argv[3], NULL, 0);
if (thread_nb >= RCV_THREAD_NUMOF) {
printf("Invalid thread number, range=0..%d\n", RCV_THREAD_NUMOF - 1);
return 1;
}
if (thread_busy[thread_nb]) {
puts("Thread already in use");
return 1;
}
struct isotp_options isotp_opt;
memset(&isotp_opt, 0, sizeof(isotp_opt));
isotp_opt.tx_id = strtoul(argv[4], NULL, 16);
isotp_opt.rx_id = strtoul(argv[5], NULL, 16);
#ifdef MODULE_CONN_CAN_ISOTP_MULTI
conn_can_isotp_init_slave(&conn_isotp[thread_nb], (conn_can_isotp_slave_t *)&conn_isotp[thread_nb]);
#endif
ret = conn_can_isotp_create(&conn_isotp[thread_nb], &isotp_opt, ifnum);
if (ret == 0) {
ret = conn_can_isotp_bind(&conn_isotp[thread_nb]);
}
if (ret < 0) {
puts("Error when binding connection");
return 1;
}
thread_busy[thread_nb] = 1;
return 0;
}
static int _send_isotp(int argc, char **argv)
{
if (argc < 4) {
print_usage();
return 1;
}
int thread_nb = strtoul(argv[2], NULL, 0);
if (thread_nb >= RCV_THREAD_NUMOF) {
printf("Invalid thread number, range=0..%d\n", RCV_THREAD_NUMOF - 1);
return 1;
}
int len = argc - 3;
uint8_t data[len];
for (int i = 0; i < len; i++) {
data[i] = strtol(argv[3 + i], NULL, 16);
}
msg_t msg, reply;
can_opt_t opt;
opt.data = data;
opt.data_len = len;
msg.type = CAN_MSG_SEND_ISOTP;
msg.content.ptr = &opt;
int res = msg_send_receive(&msg, &reply, receive_pid[thread_nb]);
if (res < 0 || (int)reply.content.value < 0) {
puts("Error when sending");
return 1;
}
return 0;
}
static int _receive_isotp(int argc, char **argv)
{
if (argc < 4) {
print_usage();
return 1;
}
int thread_nb = strtol(argv[2], NULL, 0);
if (thread_nb >= RCV_THREAD_NUMOF) {
printf("Invalid thread number, range=0..%d\n", RCV_THREAD_NUMOF - 1);
return 1;
}
uint32_t timeout = strtoul(argv[3], NULL, 0);
msg_t msg;
msg.type = CAN_MSG_RECV_ISOTP;
msg.content.value = timeout;
msg_send(&msg, receive_pid[thread_nb]);
return 0;
}
static int _close_isotp(int argc, char **argv)
{
if (argc < 2) {
print_usage();
return 1;
}
int thread_nb = strtol(argv[2], NULL, 0);
if (thread_nb >= RCV_THREAD_NUMOF) {
printf("Invalid thread number, range=0..%d\n", RCV_THREAD_NUMOF - 1);
return 1;
}
conn_can_isotp_close(&conn_isotp[thread_nb]);
thread_busy[thread_nb] = 0;
return 0;
}
#endif /* MODULE_CAN_ISOTP */
static int _get_filter(int argc, char **argv)
{
if (argc < 3) {
print_usage();
return 1;
}
int res;
int ifnum = strtol(argv[2], NULL, 0);
struct can_filter filters[32];
can_opt_t opt;
opt.data = (void *)filters;
opt.data_len = sizeof(filters);
opt.opt = CANOPT_RX_FILTERS;
res = raw_can_get_can_opt(ifnum, &opt);
if (res < 0) {
puts("Error when reading filters");
}
else if (res == 0) {
puts("No filter set");
}
else {
for (unsigned int i = 0; i < res / sizeof(filters[0]); i++) {
printf("Filter %d: 0x%" PRIx32"\n", i, filters[i].can_id);
printf("Mask %d: 0x%" PRIx32"\n", i, filters[i].can_mask);
}
}
return 0;
}
static int _set_bitrate(int argc, char **argv)
{
if (argc < 4) {
print_usage();
return 1;
}
int ifnum = strtol(argv[2], NULL, 0);
if (ifnum >= CAN_DLL_NUMOF) {
printf("Invalid ifnum %d\n", ifnum);
return 1;
}
uint32_t bitrate = strtoul(argv[3], NULL, 0);
uint32_t sample_point = 0;
int ret;
if (argc > 4) {
sample_point = strtoul(argv[4], NULL, 0);
}
printf("Setting bitrate=%" PRIu32 ", sample point=%" PRIu32 "\n",
bitrate, sample_point);
ret = raw_can_set_bitrate(ifnum, bitrate, sample_point);
if (ret < 0) {
printf("Error when setting bitrate: res=%d\n", ret);
return 1;
}
else if (ret == 1) {
puts("Bitrate/sample_point cannot be reached");
}
puts("Bittimings successfully set");
return 0;
}
static int _get_bitrate(int argc, char **argv)
{
if (argc < 3) {
print_usage();
return 1;
}
int ifnum = strtol(argv[2], NULL, 0);
struct can_bittiming bittiming;
can_opt_t opt;
opt.data = &bittiming;
opt.data_len = sizeof(bittiming);
opt.opt = CANOPT_BITTIMING;
int ret = raw_can_get_can_opt(ifnum, &opt);
if (ret < 0) {
printf("Error when getting bitrate: res=%d\n", ret);
return 1;
}
printf("Bitrate read: bitrate=%" PRIu32 ", sample_point=%" PRIu32
"\nbrp=%" PRIu32 "phase-seg1=%" PRIu32
", phase-seg2=%" PRIu32 ", sjw=%" PRIu32 "\n", bittiming.bitrate,
bittiming.sample_point, bittiming.brp, bittiming.phase_seg1,
bittiming.phase_seg2, bittiming.sjw);
return 0;
}
static int _get_counter(int argc, char **argv)
{
if (argc < 3) {
print_usage();
return 1;
}
int res = 0;
int ifnum = strtol(argv[2], NULL, 0);
if (ifnum >= CAN_DLL_NUMOF) {
puts("Invalid interface number");
return 1;
}
uint16_t cnt;
can_opt_t opt;
opt.data = &cnt;
opt.data_len = sizeof(cnt);
opt.opt = CANOPT_TEC;
int ret = raw_can_get_can_opt(ifnum, &opt);
if (ret < 0) {
printf("Error when getting TEC: res=%d\n", ret);
res = 1;
}
else {
printf("TEC=%" PRIu16, cnt);
}
opt.opt = CANOPT_REC;
ret = raw_can_get_can_opt(ifnum, &opt);
if (ret < 0) {
printf("\nError when getting REC: res=%d\n", ret);
res = 1;
}
else {
printf(", REC=%" PRIu16 "\n", cnt);
}
return res;
}
static int _power_up(int argc, char **argv)
{
if (argc < 3) {
print_usage();
return 1;
}
int res = 0;
int ifnum = strtol(argv[2], NULL, 0);
if (ifnum >= CAN_DLL_NUMOF) {
puts("Invalid interface number");
return 1;
}
int ret = raw_can_power_up(ifnum);
if (ret < 0) {
printf("Error when powering up: res=%d\n", ret);
res = 1;
}
return res;
}
static int _power_down(int argc, char **argv)
{
if (argc < 3) {
print_usage();
return 1;
}
int res = 0;
int ifnum = strtol(argv[2], NULL, 0);
if (ifnum >= CAN_DLL_NUMOF) {
puts("Invalid interface number");
return 1;
}
int ret = raw_can_power_down(ifnum);
if (ret < 0) {
printf("Error when powering up: res=%d\n", ret);
res = 1;
}
return res;
}
static int _can_handler(int argc, char **argv)
{
if (argc < 2) {
print_usage();
return 1;
}
else if (strncmp(argv[1], "list", 5) == 0) {
return _list(argc, argv);
}
else if (strncmp(argv[1], "send", 5) == 0) {
return _send(argc, argv);
}
else if (strncmp(argv[1], "recv", 5) == 0) {
return _receive(argc, argv);
}
else if (strncmp(argv[1], "close", 6) == 0) {
return _close(argc, argv);
}
#ifdef MODULE_CAN_ISOTP
else if (strncmp(argv[1], "bind_isotp", 11) == 0) {
return _bind_isotp(argc, argv);
}
else if (strncmp(argv[1], "send_isotp", 11) == 0) {
return _send_isotp(argc, argv);
}
else if (strncmp(argv[1], "recv_isotp", 11) == 0) {
return _receive_isotp(argc, argv);
}
else if (strncmp(argv[1], "close_isotp", 12) == 0) {
return _close_isotp(argc, argv);
}
#endif
else if (strncmp(argv[1], "get_filter", 10) == 0) {
return _get_filter(argc, argv);
}
else if (strncmp(argv[1], "set_bitrate", 11) == 0) {
return _set_bitrate(argc, argv);
}
else if (strncmp(argv[1], "get_bitrate", 11) == 0) {
return _get_bitrate(argc, argv);
}
else if (strncmp(argv[1], "get_counter", 11) == 0) {
return _get_counter(argc, argv);
}
else if (strncmp(argv[1], "power_up", 9) == 0) {
return _power_up(argc, argv);
}
else if (strncmp(argv[1], "power_down", 11) == 0) {
return _power_down(argc, argv);
}
else {
printf("unknown command: %s\n", argv[1]);
return 1;
}
}
static void *_receive_thread(void *args)
{
int thread_nb = (int)args;
struct can_frame frame;
msg_t msg, msg_queue[RECEIVE_THREAD_MSG_QUEUE_SIZE];
/* setup the device layers message queue */
msg_init_queue(msg_queue, RECEIVE_THREAD_MSG_QUEUE_SIZE);
printf("%d: launching receive_thread\n", thread_nb);
while (1) {
msg_receive(&msg);
switch (msg.type) {
case CAN_MSG_RECV:
{
int ret;
while ((ret = conn_can_raw_recv(&conn[thread_nb], &frame, msg.content.value))
== sizeof(struct can_frame)) {
printf("%d: %-8s %" PRIx32 " [%x] ",
thread_nb, raw_can_get_name_by_ifnum(conn[thread_nb].ifnum),
frame.can_id, frame.can_dlc);
for (int i = 0; i < frame.can_dlc; i++) {
printf(" %02X", frame.data[i]);
}
printf("\n");
}
printf("%d: recv terminated: ret=%d\n", thread_nb, ret);
conn_can_raw_close(&conn[thread_nb]);
thread_busy[thread_nb] = 0;
break;
}
#ifdef MODULE_CAN_ISOTP
case CAN_MSG_RECV_ISOTP:
{
int ret;
while ((ret = conn_can_isotp_recv(&conn_isotp[thread_nb], isotp_buf[thread_nb],
ISOTP_BUF_SIZE, msg.content.value))
<= ISOTP_BUF_SIZE && ret >= 0) {
printf("%d: %-8s ISOTP [%d] ",
thread_nb, raw_can_get_name_by_ifnum(conn_isotp[thread_nb].ifnum), ret);
for (int i = 0; i < ret; i++) {
printf(" %02X", isotp_buf[thread_nb][i]);
}
printf("\n");
}
printf("%d: recv terminated: ret=%d\n", thread_nb, ret);
break;
}
case CAN_MSG_SEND_ISOTP:
{
msg_t reply;
can_opt_t *opt = msg.content.ptr;
int ret = conn_can_isotp_send(&conn_isotp[thread_nb], opt->data, opt->data_len, 0);
reply.type = msg.type;
reply.content.value = ret;
msg_reply(&msg, &reply);
break;
}
#endif /* MODULE_CAN_ISOTP */
default:
printf("%d: _receive_thread: received unknown message\n", thread_nb);
break;
}
}
return NULL;
}
static const shell_command_t _commands[] = {
{"test_can", "Test CAN functions", _can_handler},
{ NULL, NULL, NULL},
};
int main(void)
{
for (int i = 0; i < RCV_THREAD_NUMOF; i++) {
receive_pid[i] = thread_create(thread_stack[i], THREAD_STACKSIZE,
THREAD_PRIORITY_MAIN - 1,
THREAD_CREATE_STACKTEST, _receive_thread,
(void*)i, "receive_thread");
}
char line_buf[SHELL_DEFAULT_BUFSIZE];
shell_run(_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
return 0;
}