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:
commit
067231936f
25
Makefile.dep
25
Makefile.dep
@ -56,7 +56,7 @@ ifneq (,$(filter nordic_softdevice_ble,$(USEPKG)))
|
||||
USEMODULE += gnrc_ipv6_netif
|
||||
endif
|
||||
|
||||
ifneq (,$(filter gnrc_%,$(filter-out gnrc_netapi gnrc_netreg gnrc_netif% gnrc_pktbuf,$(USEMODULE))))
|
||||
ifneq (,$(filter gnrc_%,$(filter-out gnrc_netapi gnrc_netreg gnrc_netif% gnrc_pkt%,$(USEMODULE))))
|
||||
USEMODULE += gnrc
|
||||
endif
|
||||
|
||||
@ -572,6 +572,29 @@ ifneq (,$(filter evtimer,$(USEMODULE)))
|
||||
USEMODULE += xtimer
|
||||
endif
|
||||
|
||||
ifneq (,$(filter can_linux,$(USEMODULE)))
|
||||
export LINKFLAGS += -lsocketcan
|
||||
endif
|
||||
|
||||
ifneq (,$(filter can,$(USEMODULE)))
|
||||
USEMODULE += can_raw
|
||||
USEMODULE += auto_init_can
|
||||
ifneq (,$(filter can_mbox,$(USEMODULE)))
|
||||
USEMODULE += core_mbox
|
||||
endif
|
||||
USEMODULE += gnrc_pktbuf_static
|
||||
endif
|
||||
|
||||
ifneq (,$(filter can_isotp,$(USEMODULE)))
|
||||
USEMODULE += xtimer
|
||||
endif
|
||||
|
||||
ifneq (,$(filter conn_can,$(USEMODULE)))
|
||||
USEMODULE += can
|
||||
USEMODULE += can_mbox
|
||||
USEMODULE += xtimer
|
||||
endif
|
||||
|
||||
ifneq (,$(filter random,$(USEMODULE)))
|
||||
# select default prng
|
||||
ifeq (,$(filter prng_%,$(USEMODULE)))
|
||||
|
@ -5,3 +5,10 @@ endif
|
||||
ifneq (,$(filter mtd,$(USEMODULE)))
|
||||
USEMODULE += mtd_native
|
||||
endif
|
||||
|
||||
ifneq (,$(filter can,$(USEMODULE)))
|
||||
ifeq ($(shell uname -s),Linux)
|
||||
USEMODULE += can_linux
|
||||
CFLAGS += -DCAN_DLL_NUMOF=2
|
||||
endif
|
||||
endif
|
||||
|
@ -10,6 +10,10 @@ ifneq (,$(filter mtd_native,$(USEMODULE)))
|
||||
DIRS += mtd
|
||||
endif
|
||||
|
||||
ifneq (,$(filter can_linux,$(USEMODULE)))
|
||||
DIRS += can
|
||||
endif
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
|
||||
INCLUDES = $(NATIVEINCLUDES)
|
||||
|
3
cpu/native/can/Makefile
Normal file
3
cpu/native/can/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
MODULE = can_linux
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
558
cpu/native/can/candev_linux.c
Normal file
558
cpu/native/can/candev_linux.c
Normal file
@ -0,0 +1,558 @@
|
||||
/*
|
||||
* Copyright (C) 2016 OTA keys S.A.
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup native_cpu
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Implementation of simulated CAN controller driver using SocketCAN on Linux
|
||||
*
|
||||
* @author Hermann Lelong <hermann@otakeys.com>
|
||||
* @author Aurelien Gonce <aurelien.gonce@altran.com>
|
||||
* @author Vincent Dupont <vincent@otakeys.com>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#if !defined(__linux__)
|
||||
#error "MODULE can_linux is only available on Linux"
|
||||
#else
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#include <linux/can/raw.h>
|
||||
#include <linux/can/error.h>
|
||||
|
||||
#include "native_internal.h"
|
||||
#include "can/device.h"
|
||||
#include "candev_linux.h"
|
||||
#include "thread.h"
|
||||
#include "mutex.h"
|
||||
#include "async_read.h"
|
||||
#include "sched.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
static int _init(candev_t *candev);
|
||||
static int _send(candev_t *candev, const struct can_frame *frame);
|
||||
static void _isr(candev_t *candev);
|
||||
static int _set(candev_t *candev, canopt_t opt, void *value, size_t value_len);
|
||||
static int _get(candev_t *candev, canopt_t opt, void *value, size_t max_len);
|
||||
static int _abort(candev_t *candev, const struct can_frame *frame);
|
||||
static int _set_filter(candev_t *candev, const struct can_filter *filter);
|
||||
static int _remove_filter(candev_t *candev, const struct can_filter *filter);
|
||||
static int _power_up(candev_t *candev);
|
||||
static int _power_down(candev_t *candev);
|
||||
|
||||
static int _set_bittiming(candev_linux_t *dev, struct can_bittiming *bittiming);
|
||||
|
||||
static const candev_driver_t candev_linux_driver = {
|
||||
.send = _send,
|
||||
.init = _init,
|
||||
.isr = _isr,
|
||||
.get = _get,
|
||||
.set = _set,
|
||||
.abort = _abort,
|
||||
.set_filter = _set_filter,
|
||||
.remove_filter = _remove_filter,
|
||||
};
|
||||
|
||||
static candev_event_t _can_error_to_can_evt(struct can_frame can_frame_err);
|
||||
static void _callback_can_sigio(int sock, void *arg);
|
||||
|
||||
candev_linux_conf_t candev_linux_conf[CAN_DLL_NUMOF] = {
|
||||
#if CAN_DLL_NUMOF >= 1
|
||||
{
|
||||
.interface_name = "vcan0",
|
||||
},
|
||||
#endif
|
||||
#if CAN_DLL_NUMOF >= 2
|
||||
{
|
||||
.interface_name = "vcan1",
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
int candev_linux_init(candev_linux_t *dev, const candev_linux_conf_t *conf)
|
||||
{
|
||||
memset(dev, 0, sizeof(candev_linux_t));
|
||||
dev->candev.driver = &candev_linux_driver;
|
||||
dev->conf = conf;
|
||||
dev->candev.bittiming.bitrate = CANDEV_LINUX_DEFAULT_BITRATE;
|
||||
dev->candev.bittiming.sample_point = CANDEV_LINUX_DEFAULT_SPT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static candev_event_t _can_error_to_can_evt(struct can_frame can_frame_err)
|
||||
{
|
||||
candev_event_t can_evt = CANDEV_EVENT_NOEVENT;
|
||||
can_err_mask_t can_err_type = can_frame_err.can_id & CAN_ERR_MASK;
|
||||
|
||||
if (can_err_type & CAN_ERR_TX_TIMEOUT) {
|
||||
can_evt = CANDEV_EVENT_TX_ERROR;
|
||||
}
|
||||
else if (can_err_type & CAN_ERR_CRTL) {
|
||||
switch(can_frame_err.data[1]) {
|
||||
case CAN_ERR_CRTL_RX_OVERFLOW:
|
||||
can_evt = CANDEV_EVENT_RX_ERROR;
|
||||
break;
|
||||
case CAN_ERR_CRTL_TX_OVERFLOW:
|
||||
can_evt = CANDEV_EVENT_TX_ERROR;
|
||||
break;
|
||||
case CAN_ERR_CRTL_RX_PASSIVE:
|
||||
case CAN_ERR_CRTL_TX_PASSIVE:
|
||||
can_evt = CANDEV_EVENT_ERROR_PASSIVE;
|
||||
break;
|
||||
case CAN_ERR_CRTL_RX_WARNING:
|
||||
case CAN_ERR_CRTL_TX_WARNING:
|
||||
can_evt = CANDEV_EVENT_ERROR_WARNING;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (can_err_type & CAN_ERR_BUSOFF) {
|
||||
can_evt = CANDEV_EVENT_BUS_OFF;
|
||||
}
|
||||
|
||||
return can_evt;
|
||||
}
|
||||
|
||||
static void _callback_can_sigio(int sockfd, void *arg)
|
||||
{
|
||||
(void) sockfd;
|
||||
candev_linux_t *dev = (candev_linux_t *) arg;
|
||||
|
||||
if (dev->candev.event_callback) {
|
||||
dev->candev.event_callback(&dev->candev, CANDEV_EVENT_ISR, NULL);
|
||||
}
|
||||
|
||||
native_async_read_continue(sockfd);
|
||||
|
||||
if (sched_context_switch_request) {
|
||||
thread_yield_higher();
|
||||
}
|
||||
}
|
||||
|
||||
static int _init(candev_t *candev)
|
||||
{
|
||||
struct sockaddr_can addr;
|
||||
struct ifreq ifr;
|
||||
int ret;
|
||||
|
||||
DEBUG("Will start linux CAN init\n");
|
||||
candev_linux_t *dev = (candev_linux_t *)candev;
|
||||
|
||||
if ((strlen(dev->conf->interface_name) == 0)
|
||||
|| (strlen(dev->conf->interface_name) > CAN_MAX_SIZE_INTERFACE_NAME)) {
|
||||
real_printf("Error: Invalid can iface, too short or too long \n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dev->sock = real_socket(PF_CAN, SOCK_RAW, CAN_RAW);
|
||||
|
||||
if (dev->sock < 0) {
|
||||
real_printf("CAN config KO, socket nr = %i \n", dev->sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
can_err_mask_t err_mask = CAN_ERR_TX_TIMEOUT |
|
||||
CAN_ERR_BUSOFF |
|
||||
CAN_ERR_CRTL;
|
||||
ret = real_setsockopt(dev->sock, SOL_CAN_RAW, CAN_RAW_ERR_FILTER,
|
||||
&err_mask, sizeof(err_mask));
|
||||
|
||||
if (ret < 0) {
|
||||
real_printf("Error: setsockopt failed\n");
|
||||
real_close(dev->sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
strcpy(ifr.ifr_name, dev->conf->interface_name);
|
||||
ret = real_ioctl(dev->sock, SIOCGIFINDEX, &ifr);
|
||||
|
||||
if (ret < 0) {
|
||||
real_printf("Error: Invalid can iface %s\n", dev->conf->interface_name);
|
||||
real_close(dev->sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
native_async_read_setup();
|
||||
/* This func will also automatically configure socket to be asynchronous */
|
||||
/* and to activate SIGIO */
|
||||
native_async_read_add_handler(dev->sock, (void *) dev, _callback_can_sigio);
|
||||
|
||||
addr.can_family = AF_CAN;
|
||||
addr.can_ifindex = ifr.ifr_ifindex;
|
||||
|
||||
real_bind(dev->sock, (struct sockaddr *)&addr, sizeof(addr));
|
||||
|
||||
_set_bittiming(dev, &candev->bittiming);
|
||||
|
||||
DEBUG("CAN linux device ready\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _send(candev_t *candev, const struct can_frame *frame)
|
||||
{
|
||||
int nbytes;
|
||||
candev_linux_t *dev = (candev_linux_t *)candev;
|
||||
|
||||
nbytes = real_write(dev->sock, frame, sizeof(struct can_frame));
|
||||
|
||||
if (nbytes < frame->can_dlc) {
|
||||
real_printf("CAN write op failed, nbytes=%i\n", nbytes);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dev->candev.event_callback) {
|
||||
dev->candev.event_callback(&dev->candev, CANDEV_EVENT_TX_CONFIRMATION, (void *)frame);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _isr(candev_t *candev)
|
||||
{
|
||||
int nbytes;
|
||||
struct can_frame rcv_frame;
|
||||
candev_linux_t *dev = (candev_linux_t *)candev;
|
||||
|
||||
if (dev == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG("candev_native _isr: CAN SIGIO interrupt received, sock = %i\n", dev->sock);
|
||||
nbytes = real_read(dev->sock, &rcv_frame, sizeof(struct can_frame));
|
||||
|
||||
if (nbytes < 0) { /* SIGIO signal was probably due to an error with the socket */
|
||||
DEBUG("candev_native _isr: read: error during read\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (nbytes < (int)sizeof(struct can_frame)) {
|
||||
DEBUG("candev_native _isr: read: incomplete CAN frame\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (rcv_frame.can_id & CAN_ERR_FLAG) {
|
||||
DEBUG("candev_native _isr: error frame\n");
|
||||
candev_event_t evt = _can_error_to_can_evt(rcv_frame);
|
||||
if ((evt != CANDEV_EVENT_NOEVENT) && (dev->candev.event_callback)) {
|
||||
dev->candev.event_callback(&dev->candev, evt, NULL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (rcv_frame.can_id & CAN_RTR_FLAG) {
|
||||
DEBUG("candev_native _isr: rtr frame\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dev->candev.event_callback) {
|
||||
DEBUG("candev_native _isr: calling event callback\n");
|
||||
dev->candev.event_callback(&dev->candev, CANDEV_EVENT_RX_INDICATION, &rcv_frame);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int _set_bittiming(candev_linux_t *dev, struct can_bittiming *bittiming)
|
||||
{
|
||||
int res;
|
||||
|
||||
dev->candev.bittiming = *bittiming;
|
||||
|
||||
DEBUG("bitrate = %d, brp= %d, phase_seg1 = %d, phase_seg2 = %d, sjw = %d\n",
|
||||
dev->candev.bittiming.bitrate, dev->candev.bittiming.brp,
|
||||
dev->candev.bittiming.phase_seg1, dev->candev.bittiming.phase_seg2,
|
||||
dev->candev.bittiming.sjw);
|
||||
|
||||
/* bitrate setting */
|
||||
DEBUG("_set: setting %s down\n", dev->conf->interface_name);
|
||||
res = can_do_stop(dev->conf->interface_name);
|
||||
if (res < 0) {
|
||||
return res;
|
||||
}
|
||||
DEBUG("_set: setting bittiming to %s\n", dev->conf->interface_name);
|
||||
res = can_set_bitrate(dev->conf->interface_name, dev->candev.bittiming.bitrate);
|
||||
can_get_bittiming(dev->conf->interface_name, &dev->candev.bittiming);
|
||||
DEBUG("_set: setting %s up\n", dev->conf->interface_name);
|
||||
can_do_start(dev->conf->interface_name);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int _set(candev_t *candev, canopt_t opt, void *value, size_t value_len)
|
||||
{
|
||||
candev_linux_t *dev = (candev_linux_t *) candev;
|
||||
int res = 0;
|
||||
|
||||
switch (opt) {
|
||||
case CANOPT_BITTIMING:
|
||||
DEBUG("candev_linux: CANOPT_BITTIMING\n");
|
||||
|
||||
if (strncmp(dev->conf->interface_name, "can", strlen("can"))) {
|
||||
DEBUG("candev_native: _set: error interface is not real can\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (value == NULL) {
|
||||
DEBUG("candev_native: _set: error value NULL\n");
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
|
||||
if (value_len < sizeof(struct can_bittiming)) {
|
||||
DEBUG("candev_native: _set: error size of bittiming\n");
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
|
||||
res = _set_bittiming(dev, value);
|
||||
|
||||
break;
|
||||
case CANOPT_STATE:
|
||||
switch (*((canopt_state_t *)value)) {
|
||||
case CANOPT_STATE_SLEEP:
|
||||
case CANOPT_STATE_OFF:
|
||||
_power_down(candev);
|
||||
break;
|
||||
default:
|
||||
_power_up(candev);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DEBUG("CAN set, not supported opt\n");
|
||||
res = -ENOTSUP;
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int _get(candev_t *candev, canopt_t opt, void *value, size_t max_len)
|
||||
{
|
||||
candev_linux_t *dev = (candev_linux_t *) candev;
|
||||
int res = 0;
|
||||
|
||||
switch (opt) {
|
||||
case CANOPT_BITTIMING:
|
||||
if (max_len < sizeof(struct can_bittiming)) {
|
||||
res = -EOVERFLOW;
|
||||
break;
|
||||
}
|
||||
if (value == NULL) {
|
||||
res = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (can_get_bittiming(dev->conf->interface_name, value) == 0) {
|
||||
res = sizeof(struct can_bittiming);
|
||||
}
|
||||
else {
|
||||
res = -ENOTSUP;
|
||||
}
|
||||
break;
|
||||
case CANOPT_BITTIMING_CONST:
|
||||
if (max_len < sizeof(struct can_bittiming_const)) {
|
||||
res = -EOVERFLOW;
|
||||
break;
|
||||
}
|
||||
if (value == NULL) {
|
||||
res = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (can_get_bittiming_const(dev->conf->interface_name, value) == 0) {
|
||||
res = sizeof(struct can_bittiming_const);
|
||||
}
|
||||
else {
|
||||
res = -ENOTSUP;
|
||||
}
|
||||
break;
|
||||
case CANOPT_CLOCK:
|
||||
if (max_len < sizeof(uint32_t)) {
|
||||
res = -EOVERFLOW;
|
||||
break;
|
||||
}
|
||||
if (value == NULL) {
|
||||
res = -EINVAL;
|
||||
break;
|
||||
}
|
||||
{
|
||||
struct can_clock clock;
|
||||
if (can_get_clock(dev->conf->interface_name, &clock) == 0) {
|
||||
*((uint32_t *)value) = clock.freq;
|
||||
res = sizeof(uint32_t);
|
||||
}
|
||||
else {
|
||||
res = -ENOTSUP;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CANOPT_TEC:
|
||||
case CANOPT_REC:
|
||||
if (max_len < sizeof(uint16_t)) {
|
||||
res = -EOVERFLOW;
|
||||
break;
|
||||
}
|
||||
if (value == NULL) {
|
||||
res = -EINVAL;
|
||||
break;
|
||||
}
|
||||
{
|
||||
struct can_berr_counter bc;
|
||||
if (can_get_berr_counter(dev->conf->interface_name, &bc) == 0) {
|
||||
if (opt == CANOPT_TEC) {
|
||||
*((uint16_t *)value) = bc.txerr;
|
||||
}
|
||||
else {
|
||||
*((uint16_t *)value) = bc.rxerr;
|
||||
}
|
||||
res = sizeof(uint16_t);
|
||||
}
|
||||
else {
|
||||
res = -ENOTSUP;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CANOPT_RX_FILTERS: {
|
||||
if (max_len % sizeof(struct can_filter) != 0) {
|
||||
res = -EOVERFLOW;
|
||||
break;
|
||||
}
|
||||
struct can_filter *list = value;
|
||||
size_t i;
|
||||
for (i = 0; (i < CANDEV_LINUX_MAX_FILTERS_RX)
|
||||
&& (dev->filters[i].can_id != 0)
|
||||
&& (i < (max_len / sizeof(struct can_filter))); i++) {
|
||||
list[i] = dev->filters[i];
|
||||
}
|
||||
res = i * sizeof(struct can_filter);
|
||||
break; }
|
||||
default:
|
||||
DEBUG("CAN get, not supported op\n");
|
||||
res = -ENOTSUP;
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int _set_filter(candev_t *candev, const struct can_filter *filter)
|
||||
{
|
||||
candev_linux_t *dev = (candev_linux_t *)candev;
|
||||
|
||||
if (filter == NULL) {
|
||||
DEBUG("candev_native: _set_filter: error filter NULL\n");
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
|
||||
DEBUG("candev_native: _set_filter: candev=%p, filter: f=%x m=%x on sock: %i\n",
|
||||
(void *)candev, filter->can_id, filter->can_mask, dev->sock
|
||||
);
|
||||
uint32_t i;
|
||||
for (i = 0; i < CANDEV_LINUX_MAX_FILTERS_RX; i++) {
|
||||
if (dev->filters[i].can_id == filter->can_id) {
|
||||
DEBUG("candev_native: _set_filter: filter already set\n");
|
||||
return 0;
|
||||
}
|
||||
else if (dev->filters[i].can_id == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == CANDEV_LINUX_MAX_FILTERS_RX) {
|
||||
DEBUG("candev_native: _set_filter: no more filters available\n");
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
for (i = 0; i < CANDEV_LINUX_MAX_FILTERS_RX; i++) {
|
||||
if (dev->filters[i].can_id == 0) {
|
||||
/* Only 29 bits must be used for masks in SocketCAN */
|
||||
dev->filters[i] = *filter;
|
||||
dev->filters[i].can_mask &= CAN_EFF_MASK;
|
||||
DEBUG("candev_native: _set_filter: filter:ID=0x%x\n", filter->can_id);
|
||||
DEBUG("candev_native: _set_filter: mask=0x%x\n", filter->can_mask);
|
||||
break;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
DEBUG("%" PRIu32 " filters will be set\n", i);
|
||||
real_setsockopt(dev->sock, SOL_CAN_RAW, CAN_RAW_FILTER, dev->filters,
|
||||
sizeof(struct can_filter) * i);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int _remove_filter(candev_t *candev, const struct can_filter *filter)
|
||||
{
|
||||
candev_linux_t *dev = (candev_linux_t *)candev;
|
||||
|
||||
if (filter == NULL) {
|
||||
DEBUG("candev_native: _remove_filter: error filter NULL\n");
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
|
||||
uint32_t i;
|
||||
for (i = 0; i < CANDEV_LINUX_MAX_FILTERS_RX; i++) {
|
||||
if ((dev->filters[i].can_id == filter->can_id )
|
||||
&& (dev->filters[i].can_mask == (filter->can_mask & CAN_EFF_MASK))) {
|
||||
if (i < CANDEV_LINUX_MAX_FILTERS_RX - 1) {
|
||||
memmove(&dev->filters[i], &dev->filters[i + 1],
|
||||
sizeof(dev->filters[i]) * (CANDEV_LINUX_MAX_FILTERS_RX - i - 1));
|
||||
}
|
||||
dev->filters[CANDEV_LINUX_MAX_FILTERS_RX - 1].can_id = 0;
|
||||
break;
|
||||
|
||||
}
|
||||
else if (dev->filters[i].can_id == 0) {
|
||||
DEBUG("candev_native: _remove_filter: error filter not found\n");
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == CANDEV_LINUX_MAX_FILTERS_RX) {
|
||||
DEBUG("candev_native: _remove_filter: error filter not found\n");
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
|
||||
for (i = 0; i < CANDEV_LINUX_MAX_FILTERS_RX; i++) {
|
||||
if (dev->filters[i].can_id == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG("%" PRIu32 " filters will be set\n", i);
|
||||
real_setsockopt(dev->sock, SOL_CAN_RAW, CAN_RAW_FILTER, dev->filters,
|
||||
sizeof(struct can_filter) * i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _abort(candev_t *candev, const struct can_frame *frame)
|
||||
{
|
||||
(void)frame;
|
||||
(void)candev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _power_down(candev_t *candev)
|
||||
{
|
||||
(void)candev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _power_up(candev_t *candev)
|
||||
{
|
||||
(void)candev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* defined(__linux__) */
|
104
cpu/native/include/candev_linux.h
Normal file
104
cpu/native/include/candev_linux.h
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) 2016 OTA keys S.A.
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup native_cpu
|
||||
* @ingroup drivers_can
|
||||
* @defgroup candev_linux SocketCAN driver
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Implementation of simulated CAN controller driver using SocketCAN on Linux
|
||||
*
|
||||
* @author Hermann Lelong <hermann@otakeys.com>
|
||||
* @author Aurelien Gonce <aurelien.gonce@altran.com>
|
||||
* @author Vincent Dupont <vincent@otakeys.com>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifndef CANDEV_LINUX_H
|
||||
#define CANDEV_LINUX_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) /* SocketCAN is supported only on Linux */ || defined(DOXYGEN)
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "can/candev.h"
|
||||
#include "mutex.h"
|
||||
|
||||
/**
|
||||
* Maximum size of an interface name
|
||||
*/
|
||||
#define CAN_MAX_SIZE_INTERFACE_NAME (5)
|
||||
|
||||
/**
|
||||
* Linux candev configuration
|
||||
*/
|
||||
typedef struct candev_linux_conf {
|
||||
/** local interface name */
|
||||
char interface_name[CAN_MAX_SIZE_INTERFACE_NAME + 1];
|
||||
} candev_linux_conf_t;
|
||||
|
||||
#ifndef CANDEV_LINUX_MAX_FILTERS_RX
|
||||
/**
|
||||
* Max number of rx filters which can be set
|
||||
*/
|
||||
#define CANDEV_LINUX_MAX_FILTERS_RX (16)
|
||||
#endif
|
||||
|
||||
#ifndef CANDEV_LINUX_DEFAULT_BITRATE
|
||||
/**
|
||||
* Default bitrate setup
|
||||
*/
|
||||
#define CANDEV_LINUX_DEFAULT_BITRATE (500000)
|
||||
#endif
|
||||
|
||||
#ifndef CANDEV_LINUX_DEFAULT_SPT
|
||||
/**
|
||||
* Default sampling point setup
|
||||
*/
|
||||
#define CANDEV_LINUX_DEFAULT_SPT (875)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief The candev_linux struct
|
||||
*/
|
||||
typedef struct candev_linux {
|
||||
candev_t candev; /**< candev base structure */
|
||||
int sock; /**< local socket id */
|
||||
const candev_linux_conf_t *conf; /**< device configuration */
|
||||
/** filter list */
|
||||
struct can_filter filters[CANDEV_LINUX_MAX_FILTERS_RX];
|
||||
} candev_linux_t;
|
||||
|
||||
/**
|
||||
* @brief Device specific initialization function
|
||||
*
|
||||
* @param[inout] dev the device to initialize
|
||||
* @param[in] conf the device configuration
|
||||
*
|
||||
* @return 0 on success
|
||||
*/
|
||||
int candev_linux_init(candev_linux_t *dev, const candev_linux_conf_t *conf);
|
||||
|
||||
/**
|
||||
* @brief Array containing socketCAN device names
|
||||
*/
|
||||
extern candev_linux_conf_t candev_linux_conf[CAN_DLL_NUMOF];
|
||||
|
||||
#endif /* defined(__linux__) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CANDEV_LINUX_H */
|
43
cpu/native/include/candev_linux_params.h
Normal file
43
cpu/native/include/candev_linux_params.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2016 OTA keys S.A.
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup candev_linux
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Default linux can config
|
||||
*
|
||||
* @author Vincent Dupont <vincent@otakeys.com>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifndef CANDEV_LINUX_PARAMS_H
|
||||
#define CANDEV_LINUX_PARAMS_H
|
||||
|
||||
#include "candev_linux.h"
|
||||
#include "can/device.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Default parameters (device names)
|
||||
*/
|
||||
static candev_params_t candev_linux_params[] = {
|
||||
{ .name = "can0", },
|
||||
{ .name = "can1", },
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CANDEV_LINUX_PARAMS_H */
|
||||
/** @} */
|
@ -67,17 +67,23 @@ const char *_native_unix_socket_path = NULL;
|
||||
|
||||
netdev_tap_params_t netdev_tap_params[NETDEV_TAP_MAX];
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_MTD_NATIVE
|
||||
#include "board.h"
|
||||
#include "mtd_native.h"
|
||||
#endif
|
||||
#ifdef MODULE_CAN_LINUX
|
||||
#include "candev_linux.h"
|
||||
#endif
|
||||
|
||||
static const char short_opts[] = ":hi:s:deEoc:"
|
||||
#ifdef MODULE_MTD_NATIVE
|
||||
"m:"
|
||||
#endif
|
||||
#ifdef MODULE_CAN_LINUX
|
||||
"n:"
|
||||
#endif
|
||||
"";
|
||||
|
||||
static const struct option long_opts[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "id", required_argument, NULL, 'i' },
|
||||
@ -89,6 +95,9 @@ static const struct option long_opts[] = {
|
||||
{ "uart-tty", required_argument, NULL, 'c' },
|
||||
#ifdef MODULE_MTD_NATIVE
|
||||
{ "mtd", required_argument, NULL, 'm' },
|
||||
#endif
|
||||
#ifdef MODULE_CAN_LINUX
|
||||
{ "can", required_argument, NULL, 'n' },
|
||||
#endif
|
||||
{ NULL, 0, NULL, '\0' },
|
||||
};
|
||||
@ -249,6 +258,12 @@ void usage_exit(int status)
|
||||
real_printf(
|
||||
" -m <mtd>, --mtd=<mtd>\n"
|
||||
" specify the file name of mtd emulated device\n");
|
||||
#endif
|
||||
#if defined(MODULE_CAN_LINUX)
|
||||
real_printf(
|
||||
" -n <ifnum>:<ifname>, --can <ifnum>:<ifname>\n"
|
||||
" specify CAN interface <ifname> to use for CAN device #<ifnum>\n"
|
||||
" max number of CAN device: %d\n", CAN_DLL_NUMOF);
|
||||
#endif
|
||||
real_exit(status);
|
||||
}
|
||||
@ -325,6 +340,25 @@ __attribute__((constructor)) static void startup(int argc, char **argv, char **e
|
||||
case 'm':
|
||||
((mtd_native_dev_t *)mtd0)->fname = strndup(optarg, PATH_MAX - 1);
|
||||
break;
|
||||
#endif
|
||||
#if defined(MODULE_CAN_LINUX)
|
||||
case 'n':{
|
||||
int i;
|
||||
i = atol(optarg);
|
||||
if (i >= (int)CAN_DLL_NUMOF) {
|
||||
usage_exit(EXIT_FAILURE);
|
||||
}
|
||||
while ((*optarg != ':') && (*optarg != '\0')) {
|
||||
optarg++;
|
||||
}
|
||||
if (*optarg == '\0') {
|
||||
usage_exit(EXIT_FAILURE);
|
||||
}
|
||||
optarg++;
|
||||
strncpy(candev_linux_conf[i].interface_name, optarg,
|
||||
CAN_MAX_SIZE_INTERFACE_NAME);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
usage_exit(EXIT_FAILURE);
|
||||
|
1
drivers/can_trx/Makefile
Normal file
1
drivers/can_trx/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
53
drivers/can_trx/can_trx.c
Normal file
53
drivers/can_trx/can_trx.c
Normal 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;
|
||||
}
|
||||
}
|
108
drivers/include/can/can_trx.h
Normal file
108
drivers/include/can/can_trx.h
Normal 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 */
|
||||
/** @} */
|
182
drivers/include/can/candev.h
Normal file
182
drivers/include/can/candev.h
Normal 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 */
|
||||
/** @} */
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -365,4 +365,12 @@ void auto_init(void)
|
||||
#endif
|
||||
|
||||
#endif /* MODULE_AUTO_INIT_STORAGE */
|
||||
|
||||
#ifdef MODULE_AUTO_INIT_CAN
|
||||
DEBUG("auto_init CAN\n");
|
||||
|
||||
extern void auto_init_candev(void);
|
||||
auto_init_candev();
|
||||
|
||||
#endif /* MODULE_AUTO_INIT_CAN */
|
||||
}
|
||||
|
3
sys/auto_init/can/Makefile
Normal file
3
sys/auto_init/can/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
MODULE = auto_init_can
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
56
sys/auto_init/can/auto_init_can.c
Normal file
56
sys/auto_init/can/auto_init_can.c
Normal 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
|
||||
}
|
57
sys/auto_init/can/auto_init_can_native.c
Normal file
57
sys/auto_init/can/auto_init_can_native.c
Normal 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
10
sys/can/Makefile
Normal 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
3
sys/can/conn/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
MODULE = conn_can
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
419
sys/can/conn/isotp.c
Normal file
419
sys/can/conn/isotp.c
Normal 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
279
sys/can/conn/raw.c
Normal 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
590
sys/can/device.c
Normal 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
521
sys/can/dll.c
Normal 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
3
sys/can/isotp/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
MODULE = can_isotp
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
898
sys/can/isotp/isotp.c
Normal file
898
sys/can/isotp/isotp.c
Normal 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
163
sys/can/pkt.c
Normal 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
400
sys/can/router.c
Normal 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
149
sys/include/can/can.h
Normal 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
165
sys/include/can/common.h
Normal 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 */
|
||||
/** @} */
|
202
sys/include/can/conn/isotp.h
Normal file
202
sys/include/can/conn/isotp.h
Normal 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
138
sys/include/can/conn/raw.h
Normal 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
122
sys/include/can/device.h
Normal 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
110
sys/include/can/dll.h
Normal 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
23
sys/include/can/doc.txt
Normal 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
196
sys/include/can/isotp.h
Normal 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
152
sys/include/can/pkt.h
Normal 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
279
sys/include/can/raw.h
Normal 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
116
sys/include/can/router.h
Normal 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 */
|
||||
|
||||
/** @} */
|
@ -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
184
sys/shell/commands/sc_can.c
Normal 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;
|
||||
}
|
@ -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
24
tests/conn_can/Makefile
Normal 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
152
tests/conn_can/README.md
Normal 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
634
tests/conn_can/main.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user