From 5469ba1d49df7c8371022b11eb7f08f217638943 Mon Sep 17 00:00:00 2001 From: Vincent Dupont Date: Wed, 23 Nov 2016 19:02:02 +0100 Subject: [PATCH] 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 --- Makefile.dep | 10 +- drivers/include/can/candev.h | 182 +++++++++++ makefiles/pseudomodules.inc.mk | 3 + sys/can/Makefile | 1 + sys/can/device.c | 563 +++++++++++++++++++++++++++++++++ sys/can/dll.c | 504 +++++++++++++++++++++++++++++ sys/can/pkt.c | 163 ++++++++++ sys/can/router.c | 400 +++++++++++++++++++++++ sys/include/can/can.h | 149 +++++++++ sys/include/can/common.h | 162 ++++++++++ sys/include/can/device.h | 113 +++++++ sys/include/can/dll.h | 110 +++++++ sys/include/can/doc.txt | 23 ++ sys/include/can/pkt.h | 152 +++++++++ sys/include/can/raw.h | 261 +++++++++++++++ sys/include/can/router.h | 116 +++++++ 16 files changed, 2911 insertions(+), 1 deletion(-) create mode 100644 drivers/include/can/candev.h create mode 100644 sys/can/Makefile create mode 100644 sys/can/device.c create mode 100644 sys/can/dll.c create mode 100644 sys/can/pkt.c create mode 100644 sys/can/router.c create mode 100644 sys/include/can/can.h create mode 100644 sys/include/can/common.h create mode 100644 sys/include/can/device.h create mode 100644 sys/include/can/dll.h create mode 100644 sys/include/can/doc.txt create mode 100644 sys/include/can/pkt.h create mode 100644 sys/include/can/raw.h create mode 100644 sys/include/can/router.h diff --git a/Makefile.dep b/Makefile.dep index 949924ec72..a49ef30358 100644 --- a/Makefile.dep +++ b/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))) diff --git a/drivers/include/can/candev.h b/drivers/include/can/candev.h new file mode 100644 index 0000000000..5b6b66775c --- /dev/null +++ b/drivers/include/can/candev.h @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for + * more details. + */ + +/** + * @ingroup can + * @ingroup drivers + * @defgroup drivers_can CAN drivers + * @{ + * + * This is the CAN controller driver interface + * + * @file + * @brief Definitions low-level CAN driver interface + * + * @author Vincent Dupont + * @author Toon Stegen + */ + +#ifndef CAN_CANDEV_H +#define CAN_CANDEV_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#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 */ +/** @} */ diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 29cad6e389..003fad8198 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -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 diff --git a/sys/can/Makefile b/sys/can/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/sys/can/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/can/device.c b/sys/can/device.c new file mode 100644 index 0000000000..eab6b7a056 --- /dev/null +++ b/sys/can/device.c @@ -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 + * @author Vincent Dupont + * @author Aurelien Gonce + */ + +#include + +#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; +} diff --git a/sys/can/dll.c b/sys/can/dll.c new file mode 100644 index 0000000000..0287863d65 --- /dev/null +++ b/sys/can/dll.c @@ -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 + * @author Vincent Dupont + * @author Aurelien Gonce + */ + +#include +#include + +#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]; +} diff --git a/sys/can/pkt.c b/sys/can/pkt.c new file mode 100644 index 0000000000..cf032e81c1 --- /dev/null +++ b/sys/can/pkt.c @@ -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 + * @author Toon Stegen + */ + +#include +#include +#include + +#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); +} diff --git a/sys/can/router.c b/sys/can/router.c new file mode 100644 index 0000000000..2471c33b37 --- /dev/null +++ b/sys/can/router.c @@ -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 + * @author Vincent Dupont + */ + +#include +#include + +#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 +#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; +} diff --git a/sys/include/can/can.h b/sys/include/can/can.h new file mode 100644 index 0000000000..477aec7ed4 --- /dev/null +++ b/sys/include/can/can.h @@ -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 + * @author Toon Stegen + */ + +#ifndef CAN_CAN_H +#define CAN_CAN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#if defined(__linux__) + +#include +#include + +#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 */ + +/** @} */ diff --git a/sys/include/can/common.h b/sys/include/can/common.h new file mode 100644 index 0000000000..663d2a59fd --- /dev/null +++ b/sys/include/can/common.h @@ -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 + */ + +#ifndef CAN_COMMON_H +#define CAN_COMMON_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#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 */ +/** @} */ diff --git a/sys/include/can/device.h b/sys/include/can/device.h new file mode 100644 index 0000000000..f01dc44922 --- /dev/null +++ b/sys/include/can/device.h @@ -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 + * @author Toon Stegen + */ + +#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 */ + +/** @} */ diff --git a/sys/include/can/dll.h b/sys/include/can/dll.h new file mode 100644 index 0000000000..503e97fb98 --- /dev/null +++ b/sys/include/can/dll.h @@ -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 + * @author Toon Stegen + */ + +#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 */ + +/** @} */ diff --git a/sys/include/can/doc.txt b/sys/include/can/doc.txt new file mode 100644 index 0000000000..6b17fecc42 --- /dev/null +++ b/sys/include/can/doc.txt @@ -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. + */ diff --git a/sys/include/can/pkt.h b/sys/include/can/pkt.h new file mode 100644 index 0000000000..9744254dda --- /dev/null +++ b/sys/include/can/pkt.h @@ -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 + * @author Toon Stegen + */ + +#ifndef CAN_PKT_H +#define CAN_PKT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#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 */ + +/** @} */ diff --git a/sys/include/can/raw.h b/sys/include/can/raw.h new file mode 100644 index 0000000000..1316bf9663 --- /dev/null +++ b/sys/include/can/raw.h @@ -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 + * @author Toon Stegen + * @author Aurelien Gonce + */ + +#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 */ +/** @} */ diff --git a/sys/include/can/router.h b/sys/include/can/router.h new file mode 100644 index 0000000000..d5679a1df8 --- /dev/null +++ b/sys/include/can/router.h @@ -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 + * @author Vincent Dupont + */ + +#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 */ + +/** @} */