mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-17 10:12:45 +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:
parent
30be4f7673
commit
5469ba1d49
10
Makefile.dep
10
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
|
||||
|
||||
@ -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)))
|
||||
|
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,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
1
sys/can/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
563
sys/can/device.c
Normal file
563
sys/can/device.c
Normal 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
504
sys/can/dll.c
Normal 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
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 */
|
||||
|
||||
/** @} */
|
162
sys/include/can/common.h
Normal file
162
sys/include/can/common.h
Normal 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
113
sys/include/can/device.h
Normal 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
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.
|
||||
*/
|
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 */
|
||||
|
||||
/** @} */
|
261
sys/include/can/raw.h
Normal file
261
sys/include/can/raw.h
Normal 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
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 */
|
||||
|
||||
/** @} */
|
Loading…
Reference in New Issue
Block a user