1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-17 05:12:57 +01:00

can: add CAN stack

The CAN stack support only raw CAN at this time.
It contains a device interface (drivers/include/can/candev.h) and the data link
layer, build around can/device.c can/pkt.c and can/router.c. can/dll.c contains
the upper level and lower level interface to send and receive CAN frames.
Upper layer interface is located in include/can/raw.h
This commit is contained in:
Vincent Dupont 2016-11-23 19:02:02 +01:00
parent 30be4f7673
commit 5469ba1d49
16 changed files with 2911 additions and 1 deletions

View File

@ -56,7 +56,7 @@ ifneq (,$(filter nordic_softdevice_ble,$(USEPKG)))
USEMODULE += gnrc_ipv6_netif
endif
ifneq (,$(filter gnrc_%,$(filter-out gnrc_netapi gnrc_netreg gnrc_netif% gnrc_pktbuf,$(USEMODULE))))
ifneq (,$(filter gnrc_%,$(filter-out gnrc_netapi gnrc_netreg gnrc_netif% gnrc_pkt%,$(USEMODULE))))
USEMODULE += gnrc
endif
@ -566,6 +566,14 @@ ifneq (,$(filter evtimer,$(USEMODULE)))
USEMODULE += xtimer
endif
ifneq (,$(filter can,$(USEMODULE)))
USEMODULE += can_raw
ifneq (,$(filter can_mbox,$(USEMODULE)))
USEMODULE += core_mbox
endif
USEMODULE += gnrc_pktbuf_static
endif
ifneq (,$(filter random,$(USEMODULE)))
# select default prng
ifeq (,$(filter prng_%,$(USEMODULE)))

View File

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

View File

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

1
sys/can/Makefile Normal file
View File

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

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

@ -0,0 +1,563 @@
/*
* 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"
#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");
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");
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
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_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;
}

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

@ -0,0 +1,504 @@
/*
* 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;
}
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];
}

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

@ -0,0 +1,163 @@
/*
* Copyright (C) 2016 OTA keys S.A.
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for more
* details.
*/
/**
* @file
* @brief CAN memory allocation module
*
* @author Vincent Dupont <vincent@otakeys.com>
* @author Toon Stegen <toon.stegen@altran.com>
*/
#include <string.h>
#include <limits.h>
#include <errno.h>
#include "net/gnrc/pktbuf.h"
#include "can/pkt.h"
#include "mutex.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
#define HANDLE_UNUSED 0
static int handle;
static mutex_t _mutex = MUTEX_INIT;
void can_pkt_init(void)
{
mutex_lock(&_mutex);
handle = 1;
mutex_unlock(&_mutex);
}
static can_pkt_t *_pkt_alloc(int ifnum, const struct can_frame *frame)
{
can_pkt_t *pkt;
gnrc_pktsnip_t *snip = gnrc_pktbuf_add(NULL, NULL, sizeof(*pkt), GNRC_NETTYPE_UNDEF);
if (!snip) {
DEBUG("can_pkt_alloc: out of memory\n");
return NULL;
}
pkt = snip->data;
pkt->entry.ifnum = ifnum;
pkt->frame = *frame;
pkt->snip = snip;
DEBUG("can_pkt_alloc: pkt allocated\n");
return pkt;
}
static void _init_pkt(can_pkt_t *pkt, int tx)
{
if (tx) {
mutex_lock(&_mutex);
pkt->handle = handle++;
if (handle == INT_MAX) {
handle = 1;
}
pkt->entry.next = NULL;
mutex_unlock(&_mutex);
}
else {
pkt->handle = 0;
atomic_store(&pkt->ref_count, 0);
}
}
can_pkt_t *can_pkt_alloc_tx(int ifnum, const struct can_frame *frame, kernel_pid_t tx_pid)
{
can_pkt_t *pkt = _pkt_alloc(ifnum, frame);
if (!pkt) {
return NULL;
}
_init_pkt(pkt, 1);
pkt->entry.target.pid = tx_pid;
#ifdef MODULE_CAN_MBOX
pkt->entry.type = CAN_TYPE_DEFAULT;
#endif
return pkt;
}
can_pkt_t *can_pkt_alloc_rx(int ifnum, const struct can_frame *frame)
{
can_pkt_t *pkt = _pkt_alloc(ifnum, frame);
if (!pkt) {
return NULL;
}
_init_pkt(pkt, 0);
return pkt;
}
#ifdef MODULE_CAN_MBOX
can_pkt_t *can_pkt_alloc_mbox_tx(int ifnum, const struct can_frame *frame, mbox_t *tx_mbox)
{
can_pkt_t *pkt = _pkt_alloc(ifnum, frame);
if (!pkt) {
return NULL;
}
_init_pkt(pkt, 1);
pkt->entry.target.mbox = tx_mbox;
pkt->entry.type = CAN_TYPE_MBOX;
return pkt;
}
#endif
void can_pkt_free(can_pkt_t *pkt)
{
if (!pkt) {
return;
}
DEBUG("can_pkt_free: free pkt=%p\n", (void*)pkt);
gnrc_pktbuf_release(pkt->snip);
}
can_rx_data_t *can_pkt_alloc_rx_data(void *data, size_t len, void *arg)
{
can_rx_data_t *rx;
gnrc_pktsnip_t *snip = gnrc_pktbuf_add(NULL, NULL, sizeof(*rx), GNRC_NETTYPE_UNDEF);
if (!snip) {
DEBUG("can_pkt_alloc_rx_data: out of memory\n");
return NULL;
}
rx = snip->data;
DEBUG("can_pkt_alloc_rx_data: rx=%p\n", (void *)rx);
rx->data.iov_base = data;
rx->data.iov_len = len;
rx->arg = arg;
rx->snip = snip;
return rx;
}
void can_pkt_free_rx_data(can_rx_data_t *data)
{
if (!data) {
return;
}
gnrc_pktbuf_release(data->snip);
}

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

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

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

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

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

@ -0,0 +1,162 @@
/*
* 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 */
/* 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 */
/** @} */

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

@ -0,0 +1,113 @@
/*
* 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
#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_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_PM) || defined(DOXYGEN)
uint32_t rx_inactivity_timeout; /**< Min timeout loaded when a frame is received */
uint32_t tx_wakeup_timeout; /**< Min timeout loaded when a frame is sent */
uint32_t last_pm_update; /**< time when the pm was updated */
uint32_t last_pm_value; /**< last pm timer value set */
xtimer_t pm_timer; /**< timer for power management */
#endif
} candev_dev_t;
/**
* @brief Initialize a CAN device thread
*
* This function sets up a CAN device thread
*
* @param[in] stack the device thread stack
* @param[in] stacksize the device thread stack size
* @param[in] priority the device thread priority
* @param[in] name the device thread name
* @param[in] params the parameters containing the device pointer and the ifnum
*
* @return the pid of the created thread
*/
kernel_pid_t can_device_init(char *stack, int stacksize, char priority,
const char *name, candev_dev_t *params);
/**
* @brief Fill in a @p bittiming structure from @p bittiming->bitrate and @p timing_const
*
* @param[in] clock the clock of the CAN controller
* @param[in] timing_const the timing parameter of the CAN controller
* @param[in,out] bittiming the calculated bittiming (bitrate field must be set)
*
* @return 0 on success
* @return < 0 on error
*/
int can_device_calc_bittiming(uint32_t clock, const struct can_bittiming_const *timing_const,
struct can_bittiming *bittiming);
#ifdef __cplusplus
}
#endif
#endif /* CAN_DEVICE_H */
/** @} */

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

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

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

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

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

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

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

@ -0,0 +1,261 @@
/*
* 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
/**
* @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);
#ifdef __cplusplus
}
#endif
#endif /* CAN_RAW_H */
/** @} */

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

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